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
- https://en.wikipedia.org/wiki/Reduced_instruction_set_computer
- https://en.wikipedia.org/wiki/Load/store_architecture
- https://en.wikipedia.org/wiki/Calling_convention#ARM_.28A32.29
- https://en.wikipedia.org/wiki/Link_register
- https://www.youtube.com/watch?v=7LqPJGnBPMM
- http://ioarm.netgarage.org/arm1/
- http://infocenter.arm.com/help/index.jsp
- https://community.arm.com/processors/b/blog/posts/useful-assembler-directives-and-macros-for-the-gnu-assembler
- https://azeria-labs.com/writing-arm-assembly-part-1/
- http://thinkingeek.com/arm-assembler-raspberry-pi/