Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

LordElph

By: a guest on Oct 25th, 2008  |  syntax: PHP  |  size: 3.74 KB  |  views: 844  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. <?php
  2. /**
  3. * PHP Soap Digest Authentication
  4. *
  5. * This script simulates the implementation of Digest
  6. * authentication present in the php5 soap module to
  7. * demonstate the flaw which prevents authorisation
  8. * against Microsoft SQL Server
  9. *
  10. */
  11.  
  12. //point this script at a SOAP service on MSSQL Server
  13. $user='username';
  14. $pass='password';
  15. $host="1.2.3.4";
  16. $uri="/ept/cv?wsdl";
  17.  
  18. //demonstrate fix
  19. $fix_buggy_behaviour=true;
  20.  
  21. //make initial request
  22. $headers=getHttpResponseHeaders($host, $uri);
  23.  
  24. //ensure resource requires authentication
  25. if ($headers['_statuscode']!=401)
  26.         die("$host does not require authorization");
  27. if (!isset($headers['WWW-Authenticate']))
  28.         die("No WWW-Authenticate header");
  29.        
  30. //parse the WWW-Authenticate header
  31. $auth=array();
  32. list($type,$params)=explode(' ', $headers['WWW-Authenticate']);
  33. $params=explode(',',$params);
  34. foreach($params as $param)
  35. {
  36.         list($name,$value)=explode('=',$param);
  37.        
  38.         $value=preg_replace('/^"/', '', $value);
  39.         $value=preg_replace('/"$/', '', $value);
  40.        
  41.         $auth[$name]=$value;
  42. }
  43.  
  44.  
  45. //check the header is worth testing further
  46. if ($type!='Digest')
  47.         die('Host is not configured for Digest authentication');
  48. if ($auth['algorithm']!='MD5-sess')
  49.         die('Bug only occurs with server requesting MD5-sess algorithm');
  50.  
  51. //define our client side values
  52. $nc="00000001";
  53. $cnonce=substr(md5(rand()),0,8);
  54.  
  55. //calc HA1
  56. $ha1=md5($user.':'.$auth['realm'].':'.$pass);
  57.  
  58. //refine HA1 for MD5-sess
  59. if ($auth['algorithm']=='MD5-sess')
  60.         $ha1=md5($ha1.':'.$auth['nonce'].':'.$cnonce);
  61.  
  62. //calc HA2
  63. $ha2=md5('GET:'.$uri);
  64.  
  65. //build response hash
  66. $rhash=md5($ha1.':'.$auth['nonce'].':'.$nc.':'.$cnonce.':'.$auth['qop'].':'.$ha2);
  67.  
  68. //assemble what we want in our response
  69. $response=array();
  70. $response['username']=$user;
  71. $response['realm']=$auth['realm'];
  72. $response['nonce']=$auth['nonce'];
  73. $response['uri']=$uri;
  74. $response['cnonce']=$cnonce;
  75. $response['nc']=$nc;
  76. $response['qop']=$auth['qop'];
  77. $response['response']=$rhash;
  78.  
  79. if ($fix_buggy_behaviour)
  80. {
  81.         //the PHP soap module does not include this header
  82.         //when the md5-sess algorithm has been applied 
  83.         $response['algorithm']=$auth['algorithm'];
  84. }
  85.  
  86.  
  87. //construct the header line
  88. $authorization="Digest";
  89. $sep="";
  90. foreach($response as $name=>$value)
  91. {
  92.         $authorization.=$sep." {$name}=\"".$value."\"";
  93.         $sep=",";
  94. }
  95.  
  96.  
  97.  
  98. //make the request again
  99. $headers=getHttpResponseHeaders($host, $uri, array('Authorization'=>$authorization));
  100.  
  101. if ($headers['_statuscode']==200)
  102. {
  103.         echo "Request succeeded - digest authentication worked<br>";
  104. }
  105. else
  106. {
  107.         echo "Request failed - digest authentication broken<br>";
  108.         var_dump($headers);
  109.        
  110. }
  111.  
  112.  
  113.  
  114. /**
  115.  * Makes HTTP request with optional headers and returns
  116.  * array of response headers
  117.  */            
  118. function getHttpResponseHeaders($host, $uri='/', $headers=array())
  119. {
  120.         $fp = fsockopen($host, 80, $errno, $errstr, 30);
  121.         if (!$fp) {
  122.            die("$errstr ($errno)");
  123.         } else {
  124.                 $http="GET $uri HTTP/1.1\r\n";
  125.                
  126.                 foreach($headers as $name=>$value){
  127.                         $http.="$name:$value\r\n";
  128.                 }
  129.                 $http.="Host:$host\r\n";
  130.                 $http.="Connection: Close:$host\r\n";
  131.                 $http.="\r\n";
  132.      
  133.         fwrite($fp, $http);
  134.        
  135.                 $lines=0;
  136.                 while (!feof($fp)) {
  137.                 $lines++;
  138.                 $header=trim(fgets($fp, 8192));
  139.                 if (empty($header))
  140.                         break;
  141.                
  142.                 if ($lines>1)
  143.                 {
  144.                         list($name,$value)=explode(':',$header);
  145.                         $headers[$name]=trim($value);
  146.                 }
  147.                 else
  148.                 {
  149.                         list($protocol, $status, $msg)=explode(' ',$header,3);
  150.                         $headers['_protocol']=$protocol;
  151.                         $headers['_statuscode']=$status;
  152.                         $headers['_statusmsgmsg']=$msg;
  153.                 }
  154.                 }
  155.                 fclose($fp);
  156.         }
  157.         return $headers;
  158. }
  159.  
  160. ?>
clone this paste RAW Paste Data