Getting Started with Damn Vulnerable Router Firmware (DVRF) v0.1
Posted by Elvis Collado
The goal of the DVRF project is to simulate a real-world environment to help people learn about other CPU architectures outside of the x86_64 space. The project will also help people get into discovering new things about hardware. As of now this DVRF firmware is tailored for the Linksys E1550 Device. If you do not have one don’t worry! Ready to get a jump start on learning aspects of embedded device hacking for exploit development? If so, this project is for you.
Now that v0.1 has been released for DVRF, I wanted to make this post to help people get started with DVRF even if they don't have an E1550 in hand. For this post we'll be going over how to use DVRF with QEMU.
To get started you'll need to do the following:
$ sudo apt-get install qemu-user-static Reading package lists… Done Building dependency tree Reading state information… Done The following NEW packages will be installed: qemu-user-static 0 upgraded, 1 newly installed, 0 to remove and 25 not upgraded. Need to get 0 B/7,795 kB of archives. After this operation, 79.4 MB of additional disk space will be used. Selecting previously unselected package qemu-user-static. (Reading database ... 173955 files and directories currently installed.) Preparing to unpack .../qemu-user-static_2.0.0+dfsg-2ubuntu1.21_amd64.deb ... Unpacking qemu-user-static (2.0.0+dfsg-2ubuntu1.21) ... Processing triggers for man-db (184.108.40.206-1ubuntu1) ... Setting up qemu-user-static (2.0.0+dfsg-2ubuntu1.21) ...
Once you have qemu-user-static installed you'll next want to install Binwalk. Make sure to follow the instructions so you have all of the dependancies needed to extract the squash-fs within the DVRF binary. Once Binwalk is installed you will also want to download the uClibc Buildroot tar file. This is crucial since this is going to be your bread and butter for cross compiling and also debugging, especially if you don't have IDA. To get started do the following:
$ wget https://buildroot.org/downloads/buildroot-2015.11.1.tar.gz --2016-01-20 22:53:34-- https://buildroot.org/downloads/buildroot-2015.11.1.tar.gz Resolving buildroot.org (buildroot.org)... 220.127.116.11 Connecting to buildroot.org (buildroot.org)|18.104.22.168|:443… connected. HTTP request sent, awaiting response… 200 OK Length: 5460407 (5.2M) [application/x-gzip] Saving to: ‘buildroot-2015.11.1.tar.gz’ 100%[======================================>] 5,460,407 87.5KB/s in 57s 2016-01-20 22:54:33 (92.7 KB/s) - ‘buildroot-2015.11.1.tar.gz’ saved [5460407/5460407] $ tar xzf buildroot-2015.11.1.tar.gz $ cd buildroot-2015.11.1/ b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1 $ ls arch build Config.in.legacy docs Makefile README toolchain board CHANGES configs fs Makefile.legacy support boot Config.in COPYING linux package system
Now you're going to type make menuconfig and when it's done you're going to see the following:
Under Target you're going to select MIPS little endian, ELF, and mips32. Soft float can be enabled since for now it doesn't matter for the exercises I have under /pwnables.
Under Toolkit you'll want to set the C Library to uClibc since the binary is compiled with this library and most devices you'll come across will be using this C library.
Also under Toolkit you'll want to enable "Build cross gdb for the host." This will create a gdb binary that will run on your host (e.g. x86_64) but will support your target Architecture (e.g. MIPS). This is helpful for debugging applications when using the -g argument in Qemu.
Make sure to save your configuration changes so that your toolkit will compile for the right Architecture we've chosen.
Now feel free to either explore what other options the toolkit can provide or exit the menu and type "make" but be warned that this process does take a while so it might be a good time to go grab a cup of coffee and also make sure you have an internet connection since this process will download tar files that are needed for compiling.
b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1 $ make menuconfig *** End of the configuration. *** Execute 'make' to start the build or try 'make help'. b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1 $ make /usr/bin/make -j1 HOSTCC="/usr/bin/gcc" HOSTCXX="/usr/bin/g++" silentoldconfig make: Entering directory /home/b1ack0wl/DVRF/buildroot-2015.11.1' mkdir -p /home/b1ack0wl/DVRF/buildroot-2015.11.1/output/build/buildroot-config/lxdialog [...Truncated…]
Once the toolkit has finished compiling you'll see a new folder called output. This is where the toolkit compiled, but we'll only care about what's been compiled into the host folder within output.
b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1 $ ls arch build Config.in.legacy dl linux output support board CHANGES configs docs Makefile package system boot Config.in COPYING fs Makefile.legacy README toolchain b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1 $ cd output/ b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1/output $ ls build host images staging target b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1/output $ cd host/usr/ b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1/output/host/usr $ ls bin include lib libexec mipsel-buildroot-linux-uclibc share x86_64-unknown-linux-gnu b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1/output/host/usr $ cd bin/ b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1/output/host/usr/bin $ ls *-build* mipsel-buildroot-linux-uclibc-addr2line mipsel-buildroot-linux-uclibc-gcov mipsel-buildroot-linux-uclibc-ar mipsel-buildroot-linux-uclibc-gdb mipsel-buildroot-linux-uclibc-as mipsel-buildroot-linux-uclibc-gprof mipsel-buildroot-linux-uclibc-cc mipsel-buildroot-linux-uclibc-ld mipsel-buildroot-linux-uclibc-cc.br_real mipsel-buildroot-linux-uclibc-ld.bfd mipsel-buildroot-linux-uclibc-c++filt mipsel-buildroot-linux-uclibc-ldconfig mipsel-buildroot-linux-uclibc-cpp mipsel-buildroot-linux-uclibc-ldd mipsel-buildroot-linux-uclibc-cpp.br_real mipsel-buildroot-linux-uclibc-nm mipsel-buildroot-linux-uclibc-elfedit mipsel-buildroot-linux-uclibc-objcopy mipsel-buildroot-linux-uclibc-gcc mipsel-buildroot-linux-uclibc-objdump mipsel-buildroot-linux-uclibc-gcc-4.9.3 mipsel-buildroot-linux-uclibc-ranlib mipsel-buildroot-linux-uclibc-gcc-4.9.3.br_real mipsel-buildroot-linux-uclibc-readelf mipsel-buildroot-linux-uclibc-gcc-ar mipsel-buildroot-linux-uclibc-size mipsel-buildroot-linux-uclibc-gcc.br_real mipsel-buildroot-linux-uclibc-strings mipsel-buildroot-linux-uclibc-gcc-nm mipsel-buildroot-linux-uclibc-strip mipsel-buildroot-linux-uclibc-gcc-ranlib
You can see that we have cross compilers and an instance of gdb. We will use this for crafting our exploits. Now that we have binwalk, buildroot, and qemu-user-static installed we can get started on the first pwnable within the Intro folder. First we need to extract the contents within the binary file. We will use binwalk to help us extract the binary with options -e for Extract and -M for Matryoshka which will extract recursively for up to 8 layers deep.
$ binwalk -eM ./DVRF_v01.bin Scan Time: 2016-01-21 19:44:52 Target File: /home/b1ack0wl/DVRF/DVRF_v01.bin MD5 Checksum: 34dced9038b2d1e205b6c0f68991ccfe Signatures: 343 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 BIN-Header, board ID: 1550, hardware version: 4702, firmware version: 1.0.0, build date: 2012-02-08 32 0x20 TRX firmware header, little endian, image size: 7753728 bytes, CRC32: 0x97096BA6, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x192704, rootfs offset: 0x0 60 0x3C gzip compressed data, maximum compression, has original file name: "piggy", from Unix, last modified: 2015-12-31 10:44:22 1648420 0x192724 Squashfs filesystem, little endian, non-standard signature, version 3.0, size: 6099526 bytes, 447 inodes, blocksize: 65536 bytes, created: 2016-01-19 01:47:20 [...Truncated…]
You will now have a folder that should start with _DVRF which contains all of the necessary files for these exercises. So go ahead and change directory to DVRFv01.bin.extracted/squashfs-root/ and perform the following.
b1ack0wl@b1ack0wl-VM ~/DVRF/_DVRF_v01.bin.extracted/squashfs-root $ cp `which qemu-mipsel-static` ./ b1ack0wl@b1ack0wl-VM ~/DVRF/_DVRF_v01.bin.extracted/squashfs-root $ ls bin etc media proc qemu-mipsel-static sys usr www dev lib mnt pwnable sbin tmp var b1ack0wl@b1ack0wl-VM ~/DVRF/_DVRF_v01.bin.extracted/squashfs-root $
We need to have the statically built Qemu instance on our squash-fs root directory since we are going to chroot the environment when emulating the binaries. So we're pretty much ready to get started! Let's go ahead and test out our environment by performing the following command: sudo chroot <current directory> <qemu-mipsel-static> <path to binary to emulate> argv
b1ack0wl@b1ack0wl-VM ~/DVRF/_DVRF_v01.bin.extracted/squashfs-root $ sudo chroot . ./qemu-mipsel-static ./pwnable/Intro/stack_bof_01 test123 Welcome to the first BoF exercise! You entered test123 Try Again
Awesome!! We can execute the binary without any issues! Now for the last step we need to debug the application and we will use qemu again but we will feed it the -g argument which will attach a gdbserver instance to it but won't execute the binary until an attached gdb instance initiates the continue instruction.
First get two terminals open and in one terminal type in the following command: sudo chroot . ./qemu-mipsel-static -g 1234 ./pwnable/Intro/stackbof01 test123 Remember -g 1234 means that gdbserver is going to start listening on port 1234. Now in the other terminal window you'll want to use the crossed compiled gdb that's located within the buildroot output folder.
You should now be at this step:
Just for fun you can even disassemble the file using the objdump we compiled with option -D
b1ack0wl@b1ack0wl-VM ~/DVRF/buildroot-2015.11.1/output/host/usr/bin $ ./mipsel-buildroot-linux-uclibc-objdump -D ~/DVRF/_DVRF_v01.bin.extracted/squashfs-root/pwnable/Intro/stack_bof_01 | more /home/b1ack0wl/DVRF/_DVRF_v01.bin.extracted/squashfs-root/pwnable/Intro/stack_bof_01: file f ormat elf32-tradlittlemips Disassembly of section .interp: 004000f4 <.interp>: 4000f4: 62696c2f 0x62696c2f 4000f8: 2d646c2f sltiu a0,t3,27695 4000fc: 696c4375 0x696c4375 400100: 732e6362 0x732e6362 400104: 00302e6f 0x302e6f Disassembly of section .reginfo: 00400108 <.reginfo>: 400108: b20001f6 0xb20001f6 ... 40011c: 00448ce0 0x448ce0 Disassembly of section .dynamic: 00400120 <_DYNAMIC>: 400120: 00000001 movf zero,zero,$fcc0 400124: 00000083 sra zero,zero,0x2 400128: 00000001 movf zero,zero,$fcc0 40012c: 000000ac 0xac 400130: 0000000c syscall 400134: 0040059c 0x40059c [...Truncated…] 004007e0 <main>: 4007e0: 3c1c0005 lui gp,0x5 4007e4: 279c8500 addiu gp,gp,-31488 4007e8: 0399e021 addu gp,gp,t9 4007ec: 27bdff18 addiu sp,sp,-232 4007f0: afbf00e4 sw ra,228(sp) 4007f4: afbe00e0 sw s8,224(sp) 4007f8: 03a0f021 move s8,sp 4007fc: afbc0010 sw gp,16(sp) 400800: afc400e8 sw a0,232(s8) 400804: afc500ec sw a1,236(s8) 400808: 8f82801c lw v0,-32740(gp) 40080c: 00000000 nop 400810: 94420b98 lhu v0,2968(v0) 400814: 00000000 nop 400818: a7c20018 sh v0,24(s8) 40081c: 27c2001a addiu v0,s8,26 400820: 240300c6 li v1,198 400824: 00402021 move a0,v0 400828: 00002821 move a1,zero 40082c: 00603021 move a2,v1 400830: 8f998040 lw t9,-32704(gp) 400834: 00000000 nop 400838: 0320f809 jalr t9 40083c: 00000000 nop 400840: 8fdc0010 lw gp,16(s8) 400844: 8fc200e8 lw v0,232(s8) [...Truncated…] 00400950 <dat_shell>: 400950: 3c1c0005 lui gp,0x5 400954: 279c8390 addiu gp,gp,-31856 400958: 0399e021 addu gp,gp,t9 40095c: 27bdffe0 addiu sp,sp,-32 400960: afbf001c sw ra,28(sp) 400964: afbe0018 sw s8,24(sp) 400968: 03a0f021 move s8,sp 40096c: afbc0010 sw gp,16(sp) 400970: 8f82801c lw v0,-32740(gp) 400974: 00000000 nop 400978: 24440c60 addiu a0,v0,3168 40097c: 8f998050 lw t9,-32688(gp) 400980: 00000000 nop 400984: 0320f809 jalr t9 400988: 00000000 nop 40098c: 8fdc0010 lw gp,16(s8) 400990: 00000000 nop 400994: 8f82801c lw v0,-32740(gp) 400998: 00000000 nop 40099c: 24440c94 addiu a0,v0,3220 [...Truncated…]
So all we need to do to solve this is change the return pointer to function <dat_shell> which is located at 0x00400950 remember this is little endian and we cannot use NULL bytes but once you get the picture you should just about have the following:
b1ack0wl@b1ack0wl-VM ~/DVRF/_DVRF_v01.bin.extracted/squashfs-root $ sudo chroot . ./qemu-mipsel-static ./pwnable/Intro/stack_bof_01 "[REDACTED]" Welcome to the first BoF exercise! You entered [REDACTED] Try Again Congrats! I will now execute /bin/sh - b1ack0wl qemu: uncaught target signal 11 (Segmentation fault) - core dumped
You can see the Segmentation Fault this is due to a pointer within the stack being corrupted during the overflow. Your goal is to make this program not segfault so use the tools I provided and make sure to use breakpoints! To get you started here is a proof of concept:
I hope this has helped you get started into your journey into embedded device exploitation. If you have any questions feel free to find me on twitter at @b1ack0wl