Recently, I was given the opportunity to work with an embedded Linux OS that was locked down to prevent unauthorized access. I was able to obtain a shell fairly quickly, but then I ran into a number of security mechanisms. Fortunately, I found creative ways to overcome each of them.
Here's the list of the biggest problems I encountered, in the order that I overcame them:
- The user account couldn't 'ls' most folders due to lack of privileges
- Process management tools (like ps) didn't work (thanks to the missing 'ls')
- The user account could only write to designated areas, in spite of file permissions
- Architecture was PowerPC, which I have no experience with
- netstat, ifconfig, arp, and other tools were disabled
I can't talk about how I actually obtained a shell, unfortunately, because the nature of the device would be too obvious. But I will say this: despite all their lockdowns, they accidentally left netcat installed. Oops :)
If you've been in similar situations and found some other tricks, I'd like to hear about them!
Unfortunately, I was only able to obtain user access, not root. Despite permissions to the contrary, I couldn't run 'ls' against any system folders:
$ cd / $ ls /bin/ls: cannot open directory .: Permission denied $ cd /bin $ ls /bin/ls: cannot open directory .: Permission denied $ find / / $ find . .
And so on. I could, however, run ls on /home/user, /tmp, and subfolders thereof.
As a side effect, I couldn't run the 'ps' command because it didn't have permission to read /proc:
$ ps Error: can not access /proc.
But I'll get to that later.
After struggling a little, I was happy to discover that the 'which' command was enabled!
$ which ls /usr/bin/ls $ which ps /usr/bin/ps
Great luck! I wrote a script on my personal laptop that would find every executable:
# find / -perm /0111 -type f | # Find all executable files grep -v '^/home' | # Remove files stored on /home grep -v '\.so$' | # Remove libraries grep -v '\.a$' | # Remove libraries grep -v '\.so\.' | # Remove libraries sed 's|^.*/||' # Remove the path
And redirected the output from this script to a file. Then, I uploaded the file to the device using netcat and, after adding the sbin folders to the $PATH, I ran the following command:
$ export PATH=/sbin:/usr/sbin:/usr/local/sbin:$PATH $ cat my-programs.txt | xargs which | sort | uniq > installed-programs.txt
Which returned a list that looked like:
$ head installed-programs.txt bin/arch /bin/bzip2recover /bin/cpio /bin/dmesg /bin/fusermount /bin/hostname /bin/ipmask /bin/kill /bin/killall /bin/login
And finally, if you want more information:
$ cat installed-programs.txt | xargs ls -l > installed-programs-full.txt
Which, of course, gives you this type of output:
$ head installed-programs-full -rwxr-xr-x 1 root root 2896 2008-03-31 16:56 /bin/arch -rwxr-xr-x 1 root root 7696 2008-04-07 00:42 /bin/bzip2recover -rwxr-xr-x 1 root root 52800 2007-04-07 12:04 /bin/cpio -rwxr-xr-x 1 root root 4504 2008-03-31 16:56 /bin/dmesg -rwsr-xr-x 1 root root 19836 2008-03-07 19:52 /bin/fusermount -rwxr-xr-x 1 root root 9148 2008-03-31 23:10 /bin/hostname -rwxr-xr-x 1 root root 3580 2008-03-31 23:10 /bin/ipmask -rwxr-xr-x 1 root root 8480 2008-03-31 16:56 /bin/kill -rwxr-xr-x 1 root root 14424 2006-12-19 18:07 /bin/killall -rwxr-xr-x 1 root root 44692 2008-03-24 15:11 /bin/login
Success! Now I have a pretty good idea of which programs are installed. I could collect samples from a wider variety of machines than just my laptop, potentially turning up more interesting applications, but I found that just the output from a single Linux system was actually a good enough sample to work with.
Remember, with the full 'ls -l' output, keep your eye out for 's' in the permissions. ;)
As I mentioned earlier, the ps command fails spectacularly when you can't ls folders:
$ ps Error: can not access /proc.
The first thing I tried was an experimental 'cat', which worked nicely:
$ cat /proc/1/status Name: init State: S (sleeping) [...]
Which tells me that the /proc filesystem is there, and that I have access to their accounting information. The only reason I can't list them is because 'ls /proc' (or the equivalent thereof) is failing. An investigation also told me that /proc/cpuinfo and /proc/meminfo also exist, which were helpful. So, I threw together a quick script to bruteforce the list:
for i in `seq 1 100000`; do # Take the first 100,000 PIDs #(experimentally determined) if [ -f /proc/$i/status ]; then # Check if the status file exists CMDLINE=`cat /proc/$i/cmdline | # Read the commandline sed 's/|//g' | # Remove any pipes (will break things) sed "s/\x00/ /g"`; # Replace null with space cat /proc/$i/status | # Get the process details grep 'Name:' | # We only want the name cut -b7- | # Remove the prefix "Name: " sed "s|$| ($CMDLINE)|"; # Add the commandline to the end fi; done
The output for this will look like:
init (init  ) kthreadd () [...] udevd (/sbin/udevd --daemon ) syslogd (/usr/sbin/syslogd ) klogd (/usr/sbin/klogd -c 3 -x )
So now I have a pretty good list of the running processes. Win!
Another option would be to write a patch for procps that implements a bruteforce listing, but that was beyond what I really wanted to do.
Writing to protected areas
This one, I want to be careful with. The reason is, I don't understand what was happening, or why.
In any case, in spite of permissions, I couldn't write to most folders, including /home/user. How they locked it down, I don't know, but I can't touch, cat, grep, etc them.
After some poking, though, I discovered that I could rm files and read/write them using redirection. So, oddly, it would look like this:
$ touch test touch: cannot touch `test': Permission denied $ echo "TEST DATA" > test $ cat test cat: test: Permission denied $ cat < test TEST DATA $ mv test test2 mv: cannot move `test' to `test2': Permission denied $ cat < test > test2 $ rm test
That's all I can really say about that one. This bug let me write to some sensitive folders and modify settings I shouldn't have been able to.
The architecture of this device turned out to be PowerPC, which presented an interesting challenge. I've never done any cross compilation before, and I didn't even know where to start. So, I was going to skip it altogether.
Then, this past weekend, my friend brought over a device called WD HD Live. After installing Linux on it, I discovered that, like our old friend WRT54g, it had a MIPS core. So I took a couple hours out and learned how to cross compile for MIPS.
By Monday, I knew
everything one or two things about cross compiliation, and was ready to get started! I downloaded Hobbit's Netcat from Debian and compiled it with the crosstool commands (note: I have *no* idea whether or not this is the right way to cross compile; all I know is, it worked :) ):
$ export PATH=/opt/crosstool/gcc-4.1.0-glibc-2.3.6/powerpc-860-linux-gnu/powerpc-860-linux-gnu/bin:$PATH $ wget http://ftp.de.debian.org/debian/pool/main/n/netcat/netcat_1.10.orig.tar.gz $ wget http://ftp.de.debian.org/debian/pool/main/n/netcat/netcat_1.10-38.diff.gz $ tar -xvvzf netcat_1.10.orig.tar.gz $ gunzip -v netcat_1.10-38.diff.gz $ patch -p0 < netcat_1.10-38.diff $ patch -p0 < netcat-1.10.orig/debian/patches/glibc-resolv-h.patch $ cd netcat-1.10.orig $ make linux CC=gcc
I successfully copied the new netcat to the device and ran it, to prove that the cross compile worked.
Obviously, using netcat to copy netcat to the device makes very little sense. But the point was to prove that cross compilation works, not that I could do something interesting with it.
No networking tools
Finally, I was dismayed to find out that netstat, ifconfig, arp, and others all returned a "Permission denied" error when I tried to run them. How am I supposed to figure out the system state without them?
Fortunately, none of them require setuid to run, so I downloaded the latest net-tools package, compiled it with the PowerPC toolchain, uploaded them with netcat, and tried them out:
$ ./netstat-ron -an Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 192.168.155.11:39002 192.168.155.105:3306 TIME_WAIT tcp 0 0 192.168.155.11:41992 192.168.155.105:3306 ESTABLISHED tcp 0 0 192.168.155.11:37288 192.168.155.105:3306 ESTABLISHED tcp 0 0 192.168.155.11:38736 192.168.155.105:3306 ESTABLISHED tcp 0 0 192.168.155.11:38652 192.168.155.105:3306 ESTABLISHED
$ ./ifconfig-ron lo lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:1285090 errors:0 dropped:0 overruns:0 frame:0 TX packets:1285090 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:130762797 (124.7 MiB) TX bytes:130762797 (124.7 MiB)
$ ./arp-ron Address HWtype HWaddress Flags Mask Iface 192.168.155.1 ether 00:0C:29:7E:21:63 C eth0 192.168.155.105 ether 00:50:56:C0:00:00 C eth0 192.168.155.144 ether 00:0C:29:42:B7:1B C eth0
So, I managed to overcome the lockdown on the embedded device. Once I had shell I could do pretty anything I could normally do, in spite of the attempted lockdown. Therefore, I concluded that, in its current state, the lockdown was nearly useless.
I plan to work with the vendor, of course, to help them resolve these issues.
Now, your turn! Have you ever had to use makeshift tools on a locked down system? Any interesting stories?