A new and obscure programming language, MimiLang, has surfaced. It runs on a peculiar interpreter, but something about it feels… off. Dive into its inner workings and figure out what’s really going on. Maybe you’ll uncover something unexpected.
Triage
The binary is stripped, but golang, and very large (because: golang). Since it’s golang we can just recover most of the information using a golang plugin (I use this one).
Initial Analysis
Upon restoring symbols we can just go to the main_main function and see some strings such as:
1 | f |
Mostly, these are program arguments, if we follow control flow we see the following functions (the github repo is not public as of doing the challenge):
1 | github_com_Lexterl33t_mimicompiler_compiler_Lifter_Compile |
This indicates that the .mimi file talked about in the string contains code that gets compiled to bytecode and interpreted by a VM.
The VM
Inside of “github_com_Lexterl33t_mimicompiler_vm_VM_Run” we see a large switch statement, within we see functions such as:
1 | github_com_Lexterl33t_mimicompiler_vm_VM_Push |
The most important being github_com_Lexterl33t_mimicompiler_vm_VM_VerifyProof
, since it clearly isn’t a standard instruction and its purpose will become pretty obvious when we take a look at it later.
Calling VerifyProof
Using the custom language, how do we call VerifyProof? We can see in Lifter_Compile
(prefix (github_com…) will be omitted from now on) that there is a function called Parser_Parse
, within that we see Parser_parse
, within that one we see Parser_GetStatement
, and within that function we see compiler_Parser_VerifyProofStatement
. In that function we see the string “verifyProof” so it can be assumed (and confirmed through testing) that we can call it like a standard function: verifyProof()
in the .mimi file.
VM_VerifyProof
In this function our suspicions are confirmed when we see the string Error decrypting flag:
. The only check that appears to be occurring is:
1 | if (r8 + rdx_4 != 0x4cb2f) { |
If we are to use reverse slicing, we first need to find values that satisfy r8 and rdx_4. We can do this with a C script:
1 |
|
This is very slow (takes 17 minutes, 57.836 seconds to run), here’s an optimized version:
1 |
|
And now we have the output (took 5 milliseconds):
1 | 107447 206712 |
Going back to our VerifyProof function we can see the following (note that only relevant lines are included):
1 | int64_t* var_10 = &var_80; |
Following this the above check function occurs. So, based off context, we can guess function arguments are placed on the stack and pop’d off and checked.
Solve
All we have to do is place the following in a .mimi file:
1 | verifyProof(206712, 107447); |
And run it with:
1 | ./mimicompiler -f source.mimi |
Oops, wrong one I guess!
1 | VerifyProof passed successfully. Flag unlocked! |
Let’s try our other value:
1 | verifyProof(123456, 190703); |
And…
1 | h@ada470e43019 /m/share> ./mimicompiler -f hi.mimi |