The mimic defense is a fancy idea, I don’t know how it works in real-world environment, but definitely interesting when it goes to a pwn challenge.
Recon
The challenge designer provides three binary: mimic
, mimic_note_32
, mimic_note_64
. mimic
is the one which we can interact with, the core logic looks like this:
1 | for ( i = 0; i <= 1; ++i ) |
Frankly, I’m not that familiar with the pthread
and pipe
stuff, but I guess it is going to feed all the input to two child processes and check the outputs. To have a better understanding, I complied a “Hello World” program in both 32bit and 64bit. Then let this mimic
program talk to them. As expected, the mimic
program will redirect user’s input to two programs, and it will check whether the outputs are the same, if not, or either one of the programs dies, the interaction will be terminated immediately.
The mimic_note_64
and mimic_note_32
are quite simple, it contains a obvious off-by-null
in edit
function.
1 | int edit() |
Two program are compiled from the same source code, and the protections are in same configuration, and the libc version is 2.23.
1 | [*] '/Chanllage/De1CTF2019/mimic_note/mimic_note_32' |
Exploit plan
off-by-null
vulnerabilities are so normal in recent CTFs, so it is easy to exploit. Remember we DO NOT have RELRO and PIE protection here, so overwrite .bss
and GOT
is possible. Here are some questions I asked myself when thinking about the plan:
- How to make two programs have the same outputs until we get the flag?
- Admittedly, this mimic defense technique is really strong protection. It prevents the attacker from doing the following things:
- leak any address
- print out the flag
- Admittedly, this mimic defense technique is really strong protection. It prevents the attacker from doing the following things:
- Which target to choose as our target, 32 or 64?
- My idea is to exploit one of the programs to trigger a reverse shell, while letting the other behave as normal. 32-bit program looks better as the first glance, since lower entropy in ASLR can make things easier if we need to brute-force. But I chose 64-bit because it has a handy gadget in the binary like
pop rsp; ...; ret;
, which make it possible to perform stack pivot and jump into long rop chain. I’m glad to hear if you have any solution regarding 32-bit program as an attack target.
- My idea is to exploit one of the programs to trigger a reverse shell, while letting the other behave as normal. 32-bit program looks better as the first glance, since lower entropy in ASLR can make things easier if we need to brute-force. But I chose 64-bit because it has a handy gadget in the binary like
- What kind of exploit techniques should be applied?
- So it’s time to draw the exploit roadmap. First, we use
posion-null-byte
technique to create overlapping chunks. With the overlapping chunks, we can easily performfastbin attack
. Leaking addresses is not allowed and partial overwrite is also impossible (due to the off-by-one bug, any partial overwrite will end with\x00
), so direct GOT hijacking does not work. But withoutPIE
helps us tofastbin attack
the.bss
(we can freely create any fake size on it). By creating a pointer points to itself on.bss
, we can havearbitrary R/W
ability. Then I wroteatoi@got
to0x400c33
(pop rdi; ret), and pivot into the input buffer ofget_int
function. And put another rop chain in the input buffer with0x400c2d
(pop rsp; pop r13; pop r14; pop r15; ret;), so we can do stack pivot to.bss
. Next goal is ropping to callmprotect(0x602000, 0x1000, 7)
and jmp to reverse shellcode.
- So it’s time to draw the exploit roadmap. First, we use
Thoughts
After playing so many pwn challenges, some pattern can be concluded. Many exploit techniques are proposed to get shell, but they come with different flexibility. Quite often, we need to relay on those flexibilites to bypass some mitigation, such as seccomp or mimic.
1 | Techniques Corresponding Mitigation |
After playing so many pwn challenges, some pattern can be concluded. Many exploit techniques are proposed to get shell, but they come with different flexibility. Quite often, we need to rely on those flexibilities to bypass some mitigation, such as seccomp or mimic.
To gain enough flexibility to bypass strong mitigations, we usually at least need to get to ROP. But I consider that ROP has the most complexity, the major difficulty is finding proper gadgets and chain them together. For example, in this mimic challenge, our attack chain looks like: GOT hijacking
-> ROP
-> Shellcode
.
Exp
1 | from pwn import * |
sc.bin
is a raw shellcode file produced by Binary Ninja
‘s great shellcode compiler. Once we reach the shellcode, it reads ./flag
file and send it to our remote server.
1 | void main() |
The interesting part of exploiting it is how to guarantee the same outputs. So we need to mimic the normal behaviors when receive rop chain or overwriting bytes. We have to use rop to output “Invalid Choice”, otherwise both programs will be killed before we reach our shellcode. Also, the paritial overwrite have about 1/16 chance to success, so I setup nc -lvp 1337
on remote server and run the exp in loop on local machine. After a couple of tries, the flag is waitting for us on remote server.
Wrapup
Mimic defense is an interesting idea, for the pwn challenges with this settting, we should consider using ROP and then a reverse shellcode to get flag.