title: Sqlmap速查表与Python进行功能移植
copyright: true
top: 0
date: 2021-02-27 11:59:00
tags: [扫描注入,注入,sqlmap]
categories: 安全开发
permalink:
password:
keywords:
description: Sqlmap速查,对其WAF识别功能移植,对其Sql注入的payload进行移植,完成Python批量检测SQL注入。
今天的剑桥对我而言只是一百年前那个剑桥的幻影,但我还会不由自主地、一次又一 次地回那里去。站在那里我仍会觉得温暖,隐约闻到一百年前的气息,记忆中的白绸长 裙和牛津式白底高跟鞋又鲜明起来。
阅读提要
全文约9K字,大致阅读完约10分钟,包含主要知识点:Sqlmap常用命令、python调用SqlmapApi进行批量扫描、Python移植Sqlmap的WAF识别功能并拓展、Python完成Sql注入漏洞扫描、Python移植Sqlmap的Payload分析,其中关键部位文字使用橙色重点标注,网址使用绿色重点标注。
目录:
- Sqlmap常用命令
- SqlmapApi常用方法
- 移植Sqlmap的WAF识别功能与拓展
- 批量Sql注入识别之Python检测报错型
- 批量Sql注入识别之sqlmap命令检测
- 批量Sql注入识别之封装整个sqlmap验证
- 批量Sql注入识别之移植sqlmap的payoad识别
SQLMAP常用命令
使用Sqli-Labs搭建SQL注入靶场进行练习,靶场练习下载地址,手工注入练习地址
基础检测语法
python sqlmap.py -u http://127.0.0.1/sqli/Less-1/?id=1
批量检测
将动态链接保存在文本中,然后使用sqlmap的命令进行批量的扫描
- target.txt
python sqlmap.py -m target.txt
注意target.txt跟sqlmap在同一个目录下,或者额外指定路径也可以,另外批量检测需要手动确认,这个时候添加–batch命令即可自动确认。
站点爬取
python sqlmap.py -u http://www.langzi.fun --batch --crawl=3
使用sqlmap自带的爬虫功能对网址进行爬行后,自动判断注入,但是sqlmap的爬虫不是很值得信赖…当然知道这条命令肯定没错的啦
使用hex避免字符编码导致数据丢失
有些时候因为数据库或者穿输编码的问题,有些结果无法显示出来,这个时候可以进行编码后查看数据
python sqlmap.py -u "http://127.0.0.1/sqli/Less-1/?id=1" --banner –hex -v 3 –parse-errors
模拟测试手机环境站点
某些时候服务器会对请求的数据请求头判断,只会接受来自手机移动端的数据,这个时候就可以使用命令模拟收集请求
python sqlmap.py -u "http://127.0.0.1/sqli/Less-1/?id=1" –mobile
智能判断测试
自动智能测试,常用在批量识别注入的地方
python sqlmap.py -u "http://127.0.0.1/sqli/Less-1/?id=1" -–batch –smart
python sqlmap.py -m target.txt -–batch –smart
结合burpsuite进行注入
有些注入点可能存在请求头中,比如浏览器头信息,链接的IP地址等等,这个时候对请求抓包后,保存在文本中,使用sqlmap对其进行注入检测。
python sqlmap.py -r 数据包.txt
sqlmap 自动填写表单注入
如果网页由输入框,登录框等,可以使用自带的自动填写表单测试注入
python sqlmap.py -u URL –forms
延时注入
python sqlmap.py -u "http://127.0.0.1/sqli/Less-1/?id=1" --delay 0.5 --dbs
延迟0.5秒后发起请求
可以看到请求时间延长
sqlmap版本小坑
在批量扫验证的时候,发现不同版本号扫描的结果不一样,1.2.11.6版本可以扫出来数据,但是1.3.4版本就没办法,在网上有师傅指出问题出在:
经过分析,两坑如下:
(1)v1.2.11(/v1.2.10/v1.2.9/master)的boundaries.xml没有了针对模糊查询(%)的测试,而v1.2(/v1.1.12/1.1.4/1.2.2)则有。
(2)v1.2.11(/v1.2.10/1.2.9/master)必须手动设置json的某个参数为*才能对这个参数进行注入,否则payload直接跟在json后导致无法注入,而v1.2(/v1.1.12)则可以默认回车(y)即可对json的某个参数注入。
Python调用SqlmapApi进行自动化批量扫描
sqlmapapi使用的是bottle web框架,与flask类似,但是更加精简。
首先开启sqlmapapi服务
python sqlmapapi.py -s
输出结果大致如下:
C:\Users\Administrator\Desktop>sqlmapapi.py -s
[15:42:37] [INFO] Running REST-JSON API server at '127.0.0.1:8775'..
[15:42:37] [INFO] Admin ID: 1624e1c613cf56c1afe4241b28487b4d
[15:42:37] [DEBUG] IPC database: 'c:\users\administrator\appdata\local\temp\sqlmapipc-fh0y03'
[15:42:37] [DEBUG] REST-JSON API server connected to IPC database
[15:42:37] [DEBUG] Using adapter 'wsgiref' to run bottle
[15:42:41] [DEBUG] Created new task: '4ebbb121a16a47ab'
根据输出结果,可以明白bottle开启的服务地址为
http://127.0.0.1:8775
Admin ID 是用来管理 task 所用的,每次开启 sqlmapapi 都会改变,可以通过修改源码的方式将其固定,或是写入文件供其他程序读取。
这里只是开启了服务,进一步需要创建任务,每个注入点对应不同的id。
创建任务方法如下:
r = requests.get(url='http://127.0.0.1:8775/task/new')
print(r.json())
此时会返回一个json
{u'success': True, u'taskid': u'4ebbb121a16a47ab'}
代表创建任务成功,后面的id值就是创建任务的时候该任务对应的唯一id值。
注意:每次发送请求返回的id值都是不一样的。
然后发送一个 链接过去 ,sqlmapapi自动判断是否存在注入。
我这里先用phpstudy搭建apache环境,安装好了sqli labs
使用的链接是
url = 'http://127.0.0.1/sqli/Less-1/?id=1'
代码实现如下:
# -*- coding:utf-8 -*-
import requests, json, time
def scan_sql(url):
r = requests.get(url='http://127.0.0.1:8775/task/new')
task_id = r.json()['taskid']
sqlmap_set = 'http://127.0.0.1:8775/option/%s/set' % task_id
sqlmap_url = 'http://127.0.0.1:8775/scan/%s/start' % task_id
sqlmap_status = 'http://127.0.0.1:8775/scan/%s/status' % task_id
sqlmap_result = 'http://127.0.0.1:8775/scan/%s/data' % task_id
set = requests.post(url=sqlmap_set, data=json.dumps({'url': url}), headers={'Content-Type': 'application/json'})
scans = requests.post(url=sqlmap_url, data=json.dumps({'url': url}), headers={'Content-Type': 'application/json'})
r = requests.get(sqlmap_status).json()['status']
print('当前运行状态:{}'.format(r))
while 1:
if requests.get(sqlmap_status).json()['status'] == 'running':
# 当前表示正在跑数据
time.sleep(10)
# 每十秒钟请求一次扫描状态
else:
print(requests.get(sqlmap_status).json()['status'])
if 'terminated'==requests.get(sqlmap_status).json()['status']:
re = requests.get(url=sqlmap_result)
print('当前网址扫描完毕')
print(re.json())
return re.json()
scan_sql('http://127.0.0.1/sqli/Less-1/?id=1')
Python移植Sqlmap的WAF识别功能并拓展
在扫描器开发过程中,对传入的网址进行waf检测识别是很重要的,参考sqlmap源码后,直接移植其中的waf识别功能。
- 优点:直接移植,简单方便
- 缺点:sqlmap中大多数waf都是国外的
- 补充: 移植代码后,可拓展性非常高,阅读完此文小学生坐在马桶上都会
代码阅读
在sqlmap的waf目录下,有45个py文件,除了一个初始化文件其他的都是waf检测插件,随便打开几个看看。
分析代码
可以发现所有的函数都传入一个值get_page,然后定义retval为假。
上面这两个比较好理解,即传入一个网页,获取这个网页的内容和头部信息,然后retval这个值是用作判断是否存在这个waf,如果retval为真就说明存在此waf,如何才能让retval为真呢?自然是在网页的内容和头部信息中检测了,检测确认存在该waf的判别方式。
移植思路
在sqlmap中检测waf的方式是传入一个网址,获取网址内容与头部信息,然后检测是否存在该waf的特征值,如果存在,就让retval为真并且返回这个值。因为一个waf的检测方法有好几种,比如在网页中匹配特征码,或者在网页的头部信息中匹配特征码,那么对应的waf字典数据结构应该是这样的。
{'360':[
'retval = re.search(r"wangzhan\.360\.cn", headers_get, re.I)',
'retval = "/wzws-waf-cgi/" in (page_get)'
],
'airlock':[
'retval = re.search(r"\AAL[_-]?(SESS|LB)=",headers_get, re.I)'
],
'anquanbao':[
'retval = re.search(r"MISS", headers_get, re.I)',
'retval = "/aqb_cc/error/" in (page_get)'
],
'armor':[
'retval = "This request has been blocked by website protection from Armor" in (page_get)'
]}
即在字典中,waf的名字是键,对应的检测方法为值,并且把检测方法的结果赋值给retval,如果检测waf存在,那么retval就为真。
完成代码
其实看完waf检测的字典就清楚我的思路是什么,循环迭代键值,如果返回的值(retval)为真,就说明存在改waf,这个时候在返回字典的键也就是waf名字。
其中headers_get是传入网页的头部信息,page_get是传入网页的内容。
正常的页面中一般不可能出现waf关键词的,但是让页面报错的话,就能检测出waf的关键词,这就好比打开一个网站,随便输入一些错误的字符串,在返回的body或者headers会反馈waf信息。
比如在一个正常的网址后面加上一个很明显的payload
/list.php?k=1?aspx?id=1?"download.asp=manage.mdb" and 1=1 union select user from admin%23<script>alert(1)</script>
比如下面的案例中存在加速乐的防火墙,是根据关键词识别
对应指纹中:
一一对应则确认存在的防火墙与防火墙类型。
以及会在请求头中出现关键词
对应指纹库中的关键词对比,则判断出存在的防火墙
提及一下稍微有一个小知识点,python的exec与eval,都是把字符串当代码执行,但是前者可以进行一些深度的运算,比如计算数值加减,正则匹配等等,后者只能进行打印,即前者的权限比较大,什么都可以执行,后者只能执行一些普通的操作。
详细代码如下,如果要套进扫描器的话。可以把代码封装在一个函数里面,只接受一个参数(正常的网址)即可。
在poc-T找到部分waf的相关返回结果,对代码重新整理一下。
# -*- coding:utf-8 -*-
# __author__:langzi
# __blog__:www.langzi.fun
import requests
import re
def scan_waf(uul):
urls = uul + '/list.php?k=1?aspx?id=1?"download.asp=manage.mdb" and 1=1 union select user from admin%23<script>alert(1)</script>' if uul.startswith(
'http') else 'http://' + uul + '/list.php?k=1" manage.mdb" and 1=1 union select user from admin%23<script>alert(1)</script>'
print('检测WAF:{}'.format(urls))
try:
r = requests.get(url=urls, timeout=5)
# encoding = requests.utils.get_encodings_from_content(r.text)[0]
# page_get = r.content.decode(encoding, 'replace')
page_get = r.content
headers_get = str(r.headers)
except Exception as e:
print(e)
waf_dic = {'360': [
're.search(b"wangzhan\.360\.cn", headers_get, re.I)',
'"/wzws-waf-cgi/" in (page_get)',
'"360.cn" in (page_get)',
'"360.cn" in headers_get'
],
'airlock': [
're.search(b"\AAL[_-]?(SESS|LB)=",headers_get, re.I)'
],
'anquanbao': [
're.search(b"MISS", headers_get, re.I)',
'"/aqb_cc/error/" in (page_get)'
],
'armor': [
'"This request has been blocked by website protection from Armo" in (page_get)'
],
'aws': [
're.search(b"\bAWS", headers_get,re.I)'
],
'baidu': [
're.search(b"fhl", headers_get, re.I)',
're.search(b"yunjiasu-nginx", headers_get,re.I)'
],
'barracuda': [
're.search(b"\Abarra_counter_session=",headers_get, re.I)',
're.search(b"(\A|\b)barracuda_",headers_get, re.I)'
],
'bigip': [
're.search(b"\ATS\w{4,}=",headers_get, re.I)',
're.search(b"BigIP|BIGipServe",headers_get, re.I)',
're.search(b"BigIP|BIGipServe", headers_get,re.I)',
're.search(b"\AF5\Z", headers_get,re.I)'
],
'binarysec': [
're.search(b"BinarySec", headers_get,re.I)'
],
'blockdos': [
're.search(b"BlockDos\.net", headers_get,re.I)'
],
'ciscoacexml': [
're.search(b"ACE XML Gateway", headers_get,re.I)'
],
'cloudflare': [
're.search(b"cloudflare-nginx", headers_get,re.I)',
're.search(b"\A__cfduid=",headers_get, re.I)',
're.search(b"CloudFlare Ray ID:|var CloudFlare=", page_get)'
],
'cloudfront': [
're.search(b"cloudfront", headers_get,re.I)',
're.search(b"cloudfront", headers_get,re.I)'
],
'comodo': [
're.search(b"Protected by COMODO WAF", headers_get,re.I)'
],
'datapower': [
're.search(b"\A(OK|FAIL)", headers_get, re.I)'
],
'denyall': [
're.search(b"\Asessioncookie=",headers_get, re.I)',
're.search(b"\ACondition Intercepted", page_get, re.I)'
],
'dotdefender': [
'"dotDefender Blocked Your Request" in (page_get)'
],
'edgecast': [
're.search(b"\AECDF", headers_get,re.I)'
],
'expressionengine': [
'"Invalid GET Data" in (page_get)'
],
'fortiweb': [
're.search(b"\AFORTIWAFSID=",headers_get, re.I)'
],
'hyperguard': [
're.search(b"\AODSESSION=",headers_get, re.I)'
],
'incapsula': [
're.search(b"incap_ses|visid_incap",headers_get, re.I)',
're.search(b"Incapsula", headers_get, re.I)',
'"Incapsula incident ID" in (page_get)'
],
'isaserver': [
'"The server denied the specified Uniform Resource Locator (URL). Contact the server administrator." in (page_get)',
'"The ISA Server denied the specified Uniform Resource Locator (URL)" in (page_get)'
],
'jiasule': [
're.search(b"jiasule-WAF", headers_get,re.I)',
're.search(b"__jsluid=",headers_get, re.I)',
're.search(b"jsl_tracking",headers_get, re.I)',
're.search(b"static\.jiasule\.com/static/js/http_error\.js", page_get, re.I)',
'"notice-jiasule" in (page_get)'
],
'kona': [
're.search(b"Reference #[0-9a-f.]+", page_get, re.I)',
're.search(b"AkamaiGHost", headers_get,re.I)'
],
'modsecurity': [
're.search(b"Mod_Security|NOYB", headers_get,re.I)',
'"This error was generated by Mod_Security" in (page_get)'
],
'netcontinuum': [
're.search(b"\ANCI__SessionId=",headers_get, re.I)'
],
'netscaler': [
're.search(b"\Aclose", headers_get,re.I)',
're.search(b"\A(ns_af=|citrix_ns_id|NSC_)",headers_get, re.I)',
're.search(b"\ANS-CACHE",headers_get,re.I)'
],
'newdefend': [
're.search(b"newdefend", headers_get,re.I)'
],
'nsfocus': [
're.search(b"NSFocus", headers_get,re.I)'
],
'paloalto': [
're.search(b"Access[^<]+has been blocked in accordance with company policy", page_get, re.I)'
],
'profense': [
're.search(b"\APLBSID=",headers_get, re.I)',
're.search(b"Profense", headers_get,re.I)'
],
'radware': [
're.search(b"Unauthorized Activity Has Been Detected.+Case Number:", page_get, re.I | re.S)'
],
'requestvalidationmode': [
'"ASP.NET has detected data in the request that is potentially dangerous" in (page_get)',
'"Request Validation has detected a potentially dangerous client input value" in (page_get)'
],
'safe3': [
're.search(b"Safe3WAF",headers_get, re.I)',
're.search(b"Safe3 Web Firewall", headers_get,re.I)'
],
'safedog': [
're.search(b"WAF/2\.0",headers_get, re.I)',
're.search(b"Safedog", headers_get,re.I)',
're.search(b"safedog",page_get, re.I)',
'"safedog.cn" in (page_get)'
],
'secureiis': [
're.search(b"SecureIIS[^<]+Web Server Protection", page_get)',
'"http://www.eeye.com/SecureIIS/" in (page_get)',
're.search(b"\?subject=[^>]*SecureIIS Erro", page_get)'
],
'senginx&