Baking a Pwn and Berry Pi
Jul 7, 2017
7 minute read

tl;dr

Setting up a Raspberry Pi 3 for ARM Linux binary pwning.

Intro

It’s time to dust off my Raspberry Pi 3 (rpi3)! I briefly used it to play ioarm’s level1 about a year ago, and this is as far as my ARM exploitation journey has gone. But I’m well decided to improve my pwning-fu for embedded devices. So let’s see how to turn this neglected rpi3 turned into a lab for ARM Linux binary exploitation.

Note: this blog entry does not deal with PwnBerryPi, PwnPi or RasPwn OS.

Many thanks to Robin Verton for his help with setting up the rpi3, and for testing the steps on a rpi2 and checking that everything works exactly the same.

Failed attempts

Jump to Sec. Successful attempts if you’re not interested in failures 😉, but note that trying to install pwntools is useless.

NOOBS

First attempt was to go for the easy solution that NOOBS is. At the time of installation, this was NOOBS

NOOBS Version: 2.4.2, Release date: 2017-07-05

From NOOBS’ menu, I installed Raspian. It all worked fine except for a very annoying bug in gdb.

pi@raspberry:~ $ cat hello.c
#include <stdio.h>
int main() { printf("Hello world!\n"); }
pi@raspberry:~ $ gcc -o hello hello.c
pi@raspberry:~ $ gdb ./hello
GNU gdb (Raspbian 7.7.1+dfsg-5+rpi1) 7.7.1
<...snip...>
Reading symbols from ./hello...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
   0x0001041c <+0>:	push	{r11, lr}
   0x00010420 <+4>:	add	r11, sp, #4
   0x00010424 <+8>:	ldr	r0, [pc, #8]	; 0x10434 <main+24>
   0x00010428 <+12>:	bl	0x102c4
   0x0001042c <+16>:	mov	r0, r3
   0x00010430 <+20>:	pop	{r11, pc}
   0x00010434 <+24>:	andeq	r0, r1, r12, lsr #9
End of assembler dump.

As you can see, the instruction at address 0x00010428 lacks the usual function name resolution, since the gdb output should be

  0x00010428 <+12>:	bl	0x102c4 <puts@plt>

Note: As usual, the compiler has replaced printf with the lighter puts since the string to be printed contains no format specifier.

The missing information is really crucial and I just can’t pwn in these conditions 😞. Another gdb version is needed.

It is possible to stick to NOOBS and follow the steps below in Sec. Successful attempts, but before finding the proper fix, I tried a few other things on my way. Let’s review them even if they failed.

Upgrade to testing

In a desperate attempt, I tried to upgrade Jessie to testing (simply replacing jessie by testing in /etc/apt/sources.list then running apt-get update && apt-get dist-upgrade). This took a full hour and reminded me of my first computer, a Sinclair ZX Spectrum 48KB ❤️.

The bug was fixed by the upgrade… but other things, some of which being discussed in Sec. Successful attempts went wrong, namely pwndbg was ultra slow, and my beloved colored root prompt was as broken as 💔.

Ubuntu

Since at this point I had had enough of Raspbian, I gave Ubuntu a shot.

ssh worked out of the box but apt update && apt upgrade failed to upgrade linux-firmware. I then opted for the famous: “have you tried turning it off and on again” from The IT Crowd… the system would no longer boot. This was short lived! And I was (almost) back to square one 😁

Pwntools

No need to try and install pwntools since trying to use it will result in the following message

Pwntools does not support 32-bit Python.  Use a 64-bit release.

Radare2

radare2’s debugger, completely failed at the time of writing.

pi@raspberrypi:~ $ r2 -v
radare2 1.6.0-git 15212 @ linux-arm-32 git.1.5.0-264-g5a0551f
commit: 5a0551f2b755825d5bca872258304f8806f44472 build: 2017-07-07__10:34:56

Successful attempts

Fixing the gdb bug

As said above in Sec. NOOBS, you can can stick to NOOBS and follow the intructions below. But since I tried many things on the way, I thought I’d go for a clean system, namely Raspbian Lite. At the time of installation, this was Raspbian Jessie Lite

Version: July 2017, Release date: 2017-07-05, Kernel version: 4.9

Note: Follow Section 3 of this guide for headless setup.

An apt update && apt upgrade was useless since the image I used was freshly baked, but it’s a good idea to do it.

The simplest fix to the gdb bug is to install the backports version of gdb, and for this I followed this guide, and did (as root)

apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7638D0442B90D010
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 8B48AD6246925553
echo 'deb http://httpredir.debian.org/debian jessie-backports main contrib non-free' | sudo tee -a /etc/apt/sources.list.d/jessie-backports.list
apt update
apt -t jessie-backports install gdb

Note: If you try to follow the steps mentionned in this post, at some point in what follows, you may see that some installation process issues some warnings about locales. To fix this, edit /etc/locale.gen, uncomment the line about the problematic locale, and run locale-gen.

Enhancing gdb with pwndbg

A tool I really like in order to have some colored output and feel like it’s 21st century when using gdb is pwndbg. I followed the installation instructions in the link and did

sudo su
cd /usr/local/src/
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh

I edited /root/.gdbinit to add the line set show-retaddr-reg on, (see this link) in order to see the value of the link register (LR) in context. I copied this file to /home/pi, and changed its user and group to pi.

Note: If at some point you want plain vanilla gdb because pwndbg messes something up, just launch gdb with the -nx option.

Installing checksec

Once in gdb, if pwntools is installed, one can launch checksec thanks to pwndbg. But since pwntools can’t be installed on a 32 bit release, I used this script that I copied to /usr/local/bin/checksec and made it executable. A simple test gives

pi@raspberrypi:~ $ checksec -f /bin/ls
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	FORTIFY	Fortified Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   Error: libc not found.

To fix the error (but see Note below), I modified a few lines. Here is a patch (to be used with patch checksec checksec_rpi.patch)

pi@raspberrypi:/usr/local/bin $ cat checksec.patch 
330a331,332
>   elif [ -e /lib/arm-linux-gnueabihf/libc.so.6 ] ; then
>     FS_libc=/lib/arm-linux-gnueabihf/libc.so.6
1476a1479,1480
>     elif [ -e /lib/arm-linux-gnueabihf/libc.so.6 ] ; then
>       FS_libc=/lib/arm-linux-gnueabihf/libc.so.6
1527a1532,1533
>       elif [ -e /lib/arm-linux-gnueabihf/libc.so.6 ] ; then
>   FS_libc=/lib/arm-linux-gnueabihf/libc.so.6

Note: even though the error message for FORTIFY is now gone, this script does not seem to distinguish between a fortified binary and a binary with canaries. Furthemore, the compile option -D_FORTIFY_SOURCE=2 seems to have no effect on Raspbian, which is confirmed with man gcc not containing the word FORTIFY.

Radare2

Even if at the time of writing, radare2’s debugger is totally bugged, the rest of the r2 suite is still extremely useful. I followed the installation instructions in this link and did

sudo su
cd /usr/local/src/
git clone https://github.com/radare/radare2
cd radare2
sys/install.sh   # just run this script to update r2 from git

I also installed r2pipe with pip install --upgrade r2pipe

Socat and Xinetd

Since pwntools can’t be installed on Raspbian, the exploits will have to be launched from a x64 system. This can be done via ssh in pwntools, and so, as if the exploit was launched locally. But in case I’d want to pwn a remote service running on the rpi3, I installed socat and xinetd via apt install.

Feeling home

Here are a few simple tweaks for those of you who would want to do the same but don’t know how to.

Colors are ❤️

Apart from the usual editing of /home/pi/.bashrc to get a color prompt and colored ouput, I copied everything from # Change the window title of X terminals in this script at the end of /etc/bash.bashrc, to have a nice root colored prompt.

Host name

I edited /etc/hostname and changed raspberrypi to the short and cozy raspwn, did the same with /etc/hosts on the 127.0.0.1 line. Changes are taken into account after a reboot.

No more password

On the machine I’m ssh’ing from, I generated a pair of keys with ssh-keygen -t rsa, set a passphrase, copied the generated id_rsa.pub to the rpi3 in /home/pi/.ssh/authorized_keys (after creating the by default non-existent .ssh directory). I furthermore created the following config file on my main machine:

pwner@pwnbox:~$ cat .ssh/config
Host pi
	Hostname rpi.ip.ad.dr
	Port 22
	User pi

where, obviously, my rpi3’s ip address has been redacted. Now after an initial ssh-add, ssh pi logs me to the rpi3, and a scp file pi:path copies a file to the rpi3.

Conclusion

As simple as it may seem, this all took some time because of trial and error. Now that it’s all working, let da pwn begin! 😄

Note: Before this, I will first have to learn more about the ARM architecture (and RISC in general) and its assembly. Maybe so do you. So here’s an unordered list of useful links to start with