finding command injections on a router using strace

this was my first router pentest.  it was a battery-powered portable mobile/wifi router which was to be sold on a foreign market. i learned so many things!  this is just one of them…

to perform command injection discovery on the web gui, i first used a browser and burp to meddle with parameters.  however, meddling with every field for every page in burp quickly became tiresome.  furthermore, it felt a bit too ‘blind’, since for any given field there might have been an injection point, but it was possible i just wasn’t attacking it correctly

i already had root telnet access into the router, thanks to a different security problem, so i had the router execute ‘ps’ with verbose output as fast as it could, dumping the results to file.  i then browsed through the web gui’s pages, and did a sort/uniq (and other filtering stuff) on the ps output to get a list of unique commands spawned by the web gui as it went about its business.  hunting through the output, i managed to find a command injection point.  but… i knew i had been lucky – ‘ps’ had just happened to see this command as it was being executed

what i really wanted was something listening to the web gui’s execve syscalls (i.e. when it forks a child process and then gets that child to do something), and in linux that’s what strace is for.  i hadn’t used strace before, but i knew about it enough to know it was what i needed! unfortunately strace was not on this router, and i had no experience whatsoever of cross-compiling utils for router chipsets/architetures (RALINK/MIPS in this case)

a much more experienced colleague pointed me in the direction of a helpful blog post.  i could download openwrt, which supports a lot of router chipsets/architectures, configure and build it for the correct target, then snaffle the strace binary from it.  nice!

sudo apt-get install libncurses5-dev gawk flex build-essential qemu-user
git clone git://
cd openwrt
make menuconfig
[select the correct chipset and utils required]

about an hour later (on a 4 core i7 laptop!) it had built, hooray!  i transferred strace to a usb stick and then executed it on the router.  no dice.  why? i compared a binary from the device to my binary to see:

$ file date
date: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/, stripped
$ file strace
strace: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1, dynamically linked, interpreter /lib/, not stripped

durrrr 🙂 after some floundering around with the rats nest of makefiles in openwrt, i got it to link statically

diff -ur -x .git openwrt/package/devel/strace/Makefile openwrt-static/package/devel/strace/Makefile
--- openwrt/package/devel/strace/Makefile 2017-05-09 01:14:13.112386648 +0300
+++ openwrt-static/package/devel/strace/Makefile 2017-05-09 01:27:45.108752248 +0300
@@ -28,8 +28,11 @@
include $(INCLUDE_DIR)/

HOST_CFLAGS += -I$(LINUX_DIR)/user_headers/include
+TARGET_CFLAGS += -static
+TARGET_LDFLAGS += -static

+ LDFLAGS="-static" \

rebuild it:

make package/devel/strace/{clean,compile,install}

my initial aim was to build ALL the util binaries statically, but after some considerable effort i decided quite conclusively that openwrt just doesn’t support this. static compilation has to be enabled (in a bespoke manner) on a per-package basis. i managed to do it for the utils i really wanted, like ‘ss’, ‘tcpdump’ and a handful of others, but for others i tried it just wasn’t happening. oh well!

now i had a static strace, i ran it against the web-gui process:

./strace -f -p 31722 -e execve 2>&1 | grep execve

but ran into a problem:

[pid 16478] execve("/bin/sh", ["sh", "-c", "echo 4 > /var/pcontrol_0/cpin"], [/* 7 vars */]) = 0

ooer, strace is truncating stuff. not good. the man page suggested the ‘-s’ option could stop the truncation, but it didn’t

google and stackoverflow to the rescue! it turned out i could recompile strace with output truncation disabled

the way the openwrt build works is that it downloads the compressed source of a package, unpacks it, applies patches if necessary, then compiles it.  so i could either create a patch or meddle with the downloaded source directly.  the proper approach would have been the former, but i chose the latter out of laziness :-p

cd dl
mkdir unpack
cp strace-4.11.tar.xz unpack
cd unpack
xz -d strace-4.11.tar.xz
tar -xvf strace-4.11.tar
rm strace-4.11.tar
cd strace-4.11/
gedit defs.h

[change the abbrev(tcp) value to be 0]
#define abbrev(tcp) 0

cd ..
tar -cvf strace-4.11.tar strace-4.11/
xz strace-4.11.tar
mv ./strace-4.11.tar.xz ..
cd ..
rm -rf unpack

rebuild it again:

make package/devel/strace/{clean,compile,install}

ok, great, now it dumps full output:

[pid 4342] execve("/bin/sh", ["sh", "-c", "echo 4 > /var/pcontrol_0/cpin"], ["USER=root", "LD_LIBRARY_PATH=/ram/lib:/lib:/lib/iptables", "HOME=/", "TERM=vt102", "PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/bin/scripts", "SHELL=/bin/sh", "PWD=/ram/tmp/www3"]) = 0

now i just browsed through the web gui with a browser, submitting forms with entirely normal/expected data. it didn’t take long to find a new candidate injection:

[pid 1654] execve("/bin/sh", ["sh", "-c", "mac-control fixed 1 00:22:31:26:47:81"], ["USER=root", "LD_LIBRARY_PATH=/ram/lib:/lib:/lib/iptables", "HOME=/", "TERM=vt102", "PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/bin/scripts", "SHELL=/bin/sh", "PWD=/"]) = 0

i sent the request to burp’s repeater, set the mac parameter to ’00:22:31:26:47:81;reboot;’, and voila! of course ‘reboot’ is a stupidly crass injection! adding a superuser with a known password, opening up telnet to wan, followed by a ‘check in’ call to an attacker-controlled domain would be the proper thing to do, for an appropriately shocking client demo

tracking execve calls may not be the only way to find command injections, and it won’t get them all, but it’s a nice trick to have in my book from now on!

Leave a Reply

Your email address will not be published. Required fields are marked *