Task | Fastcalc |
---|---|
Competition | Teaser CONFidence CTF 2017 |
Location | Online |
Category | Software exploitation |
Platform | Windows x86 |
Scoring | 500 pts (hard) |
Number of solves | 11 out of 258 teams |
What would a CONFidence CTF be without a Windows-hosted exploitation challenge? :) This time you're facing a fast, greatly optimized calculator capable of concurrently evaluating multiple expressions. In fact it is so technically advanced that it will only run on CPUs supporting the cutting-edge SSE2 extension. Do you think you can hack it?
Click to expand
A regular 32-bit Windows binary with a typical set of mitigations (ASLR, DEP, /GS), but nothing out of the ordinary (no CFG, RFG etc.). It is a "multithreaded" evaluator of arithmetic expressions (e.g. (1+2*3)/4), which uses the Fibers Windows-specific mechanism to switch between calculations. It uses the XMM0-7 (SSE2) registers to perform the computation. It is statically linked and has a system
call to make exploitation easier.
There are two bugs:
- Semi-continuous stack buffer overflow in converting the infix representation of the expression to the Reverse Polish Notation one. It's in the stack frame of the
main
function. However, since the overflowing structures have large alignment holes, it is possible to leave the stack cookie intact, but overwrite the return address of the function. - The XMM context is not switched between executions of fibers. When the src and dst addresses of
memcpy
have the same 16-byte alignment, the function uses the XMM registers to transfer data in memory, and then doesn't clean them up. Whilestd::cout
zeros XMM0-XMM5, XMM6-7 keep the left-over values used bymemcpy
. These values can then be leaked through the calculation results of worker fibers, and may disclose information such as the image base address or stack address.
The exploitation process is as follows (see exploit.py for more details).
- Create a "++++++" expression, which will be later used to read the value of the XMM6 register and return it as the output (through XMM0).
- Create a "+"x256 expression. When its RPN representation is copied from stack to heap, and if the addresses align correctly,
memcpy
leaks a valid code pointer through XMM6. - Run the evaluation of both expressions, which will yield the leaked pointer as the output of the 1st one (or junk bytes, in which case we have to reconnect and try again).
- Create an expression of "0+"x131 followed by a 64-bit double-encoded ROP chain making up
system("cmd.exe")
. This will keep the original value of the stack cookie, but overwrite the return-address and one dword after it. - Call "quit", which will trigger the mini-ROP and invoke a shell.
Now we can type in type flag.txt
and the flag is ours. :)