SQLMap 源码阅读

0x01 前言

还是代码功底太差,所以想尝试阅读 sqlmap 源码一下,并且自己用 golang 重构,到后面会进行 ysoserial 的改写;以及 xray 的重构,当然那个应该会很多参考 cel-go 项目

0x02 环境准备

sqlmap 的项目地址:https://github.com/sqlmapproject/sqlmap
用 pycharm 打断点调试,因为 vscode 用来调试比较麻烦。

因为要动调,所以需要一个 sql 注入的靶场,这里直接选用的是 sql-labs,用 docker 起

docker pull acgpiano/sqli-labs
docker run -dt --name sqli-lab -p [PORT]:80 acgpiano/sqli-labs:latest

最后还需要重新配置一下数据库,然后才能以 sqli-labs 为靶场进行测试。

这里也挂一下 sqlmap 对应的一些基础操作 ———— https://www.cnblogs.com/hongfei/p/3872156.html

直接在 pycharm 的 Debug 下进行调试,设置参数如下,开始调试

-u "http://81.68.120.14:3333/Less-1/?id=1" -technique=E --dbs

0x03 sqlmap 源码阅读

在开始之前我们有必要确认一下 sqlmap 运行的流程图,很重要!这样有助于我们进一步分析源码。

1. 初始化

sqlmap.py 的 main 函数下断点,开始调试

在没有对 URL 进行发包/探测的时候 sqlmap 会先对一些环境、依赖、变量来做一些初始化的处理

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

往下,通过 cmdLineParser() 获取参数,cmdLineParser() 通过 argparse 库进行 CLI 的打印与获取,类似的一个小项目我之前也有接触过 https://github.com/Drun1baby/EasyScan

往下 initOptions(cmdLineOptions) 解析命令行参数

init 函数: 初始化

init() 函数中通过调用各种函数进行参数的设置、payload 的加载等,有兴趣的师傅可以点进去阅读一下。

其中这三个相对比较重要,是用来加载 payload 的 ———— loadBoundaries()loadPayloads()_loadQueries()

loadBoundaries()  // 加载闭合符集合
loadPayloads()    // 加载 payload 集合
_loadQueries()    // 加载查询语句,在检测到注入点之后后续进行数据库库名字段名爆破会用到的语句

下个断先点调试一下 loadBoundaries() 函数

首先,会去加载 paths.BOUNDARIES_XML,也就是 data/xml/boundaries.xml

接着进入解析 XML 文件的部分,跟进 parseXmlNode(root)

最终添加到 conf 对象的 tests 属性里

  • loadPayloads() 函数与 _loadQueries() 函数大体上也是如此,都是做了解析 xml 文件的工作,再将内容保存到 conf 对象的 tests 属性里。像 loadPayloads() 函数,最后在 conf.tests 里面可以很清晰的看到 payloads

此时我们还可以看一下 conf 是什么

conf 属性中主要存储了一些目标的相关信息(hostname、path、请求参数等等)以及一些配置信息,init 加载的 payload、请求头 header、cookie 等

init() 函数执行完毕后,就会来到 start() 函数进行项目的正式运行。

初始化功能点小结

简单概括一下初始化部分的代码做了什么事

  • 获取命令行参数并处理
  • 初始化全局变量 conf 以及 kb
  • 获取并解析几个 xml 文件,完成闭合工作、payloads 加载工作
  • 设置 HTTP 相关配置,如 HTTP Header,UA,Session 等

2. URL 处理

f8 下来,先到的是 threadData = getCurrentThreadData(),继续往下走,到 result = f(*args, **kwargs) 代码块,跟进一下

代码逻辑此时来到了 /lib/controller/controller.py 下,往下走,是不会进到 conf.directconf.hashFile 中的,会直接进入到 kb.targets.add() 的代码逻辑里面。

此处的 kb 变量的作用是共享一些对象,其实本质上是保存了注入时的一些参数。kb.targets 添加了我们输入的参数,如图

往下看,大体上是做了一些类似类似打印日志、赋值、添加 HTTP Header 等工作,这一部分代码我们就不看了,直接看最关键的这一部分代码 parseTargetUrl()

跟进

一开始先进行了这一判断

if re.search(r"://\[.+\]", conf.url) and not socket.has_ipv6

判断 http:// 的开头形式是否正确,以及 socket 是否为 ipv6 协议,如果为 ipv6 协议,那么 sqlmap 并不支持。

接着判断

if not re.search(r"^(http|ws)s?://", conf.url, re.I):

判断是 http 开头还是 https 开头,又或者是否是 ws/wss 开头,如果没有这些开头,则就从端口判断,这里我认为或许可以加上 80 与 8080 端口。

继续往下看,进行了 url 的拆分、host 的拆分,并将这些内容保存到 conf 里面的对应属性,后续也是一些基础的判断与赋值,这里不再赘述。

总而言之是在对 URL 进行剖析与拆解,最后这些东西都是放到 conf 里面的

3. 如果这个网站已经被注入过,生成注入检测的payload

核心代码在 controller.py 的第 434 行,需跟进;此处我们可以设置对 kb.injections 的变量监测。先跟进 setupTargetEnv() 函数

  • setupTargetEnv() 函数调用了如下图所示的七个函数

我们跟进最主要的 _resumeHashDBValues() 函数,首先调用了 hashDBRetrieve() 函数,设置检索

出来,到第 476 行,这一次又调用了 hashDBRetrieve() 函数,传参是 HASHDB_KEYS.KB_INJECTIONS,意思就是以 KB_INJECTIONS 作为 KEY 进行检索。跟进发现函数先将需要注入的 URL 信息放到了 _
这个变量中,并将基础信息用 | 符号隔开。

跟进 retrieve() 函数,这个函数做了生成 payload 的工作,具体是怎么生成的我们继续往下看

第 95 行,这里很重要,执行了 SQL 语句,并通过 Hash 加密,加密方式是 base64Pickle 序列化

最终反序列化解密 Payload,说实话这里没看懂是怎么生成的,看上去仅仅是执行了一个 SQL 语句,后面看其他师傅的文章的时候并没有把这一段单独拉出来说,payloads 其实都放在 xml 当中。

接着再循环一次,生成一个 payload

在生成完所有 payload 之后会先对目标进行一次探测,如果 Connection refused 则返回 False

这里生成的 payload 只是很基础的一部分,并非是

4. WAF 检测

解析完 URL 之后对目标进行探测,往下看,位置是 controller.py 的第 439 行,第 448 行有 checkWaf() 的函数,很明显就是要做 WAF 检测的功能。

先会判断这一目标是否存在 WAF,如果存在 WAF 的话,会进行字符的相关 fuzz,当然此处建议对一个存在 WAF 的目标进行测试。值得注意的是,如果这个目标你已经探测过存在 waf,且已知 waf 归属厂商的情况下,就不会走到 payload 那一段代码逻辑当中去,相关的业务代码在 hashDBRetrieve() 下,此处不再展开,比较容易。

如果存在 WAF,则会生成用于 fuzz 的 payload,这个 payload 是基于这个 NMAP 的 http-waf-detect.nse ———— https://seclists.org/nmap-dev/2011/q2/att-1005/http-waf-detect.nse

设置 payload 类似于 "9283 AND 1=1 UNION ALL SELECT 1,NULL,'<script>alert("XSS")</script>',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#",如果没有 WAF,页面不会变化,如果有 WAF,因为 payload 中有很多敏感字符,大多数时候页面都会发生改变。

接下来的 conf.identifyWaf 代表 sqlmap 的参数 --identify-waf,如果指定了此参数,就会进入 identifyWaf() 函数,主要检测的 waf 都在 sqlmap 的 waf 目录下。不过新版的 sqlmap 已经将这一参数的功能自动放到里面了,无需再指定参数

  • 这里的 payload 先经过处理后赋值给 value,再将 value 作为参数传入 queryPage() 请求中,跟进

在经过很长一段的数据处理与判断代码后,我们到第 1531 行,如图,跟进;getPage() 函数的作用是获取界面的一些信息,如 url,ua,host 等,通过输出比对 payload,为判断 waf 类型提供信息。

  • 获取基本信息

这些基础信息最后都会保存在 response 系列的 message 当中

getPage() 函数中调用了 processResponse() 函数做响应结果的处理,跟进

往下看,到 401 行开始,后续的代码进行了 Waf 的识别

跟进 identYwaf.non_blind_check(),是通过正则表达式来对页面进行匹配,对应的规则在 thirdparty/identywaf/data.json

同时 sqlmap 不光通过规则库来进行判断,也会通过页面相似度来判断是否存在 waf/ips

如果相似度小于设定的 0.5 那么就判定为有 waf 拦截

WAF 注入总结

总结一下就是两点,一种方法是通过正则匹配的检测,另外一种方法是根据页面相似度来检测,我自己应该很难写出来 waf 检测的东西;届时再做尝试。

5. 注入检测之启发式注入

checkWaf() 函数里面出来,先到第 457 行,检测网站是否稳定(因为有些网站一测试可能就炸了)对应此 info

[INFO] testing if the target URL content is stable

继续往下走到第 471 行,会先判断参数是否可以注入,这里与命令的参数 —— --level 挂钩

在前文环境准备的时候我们采用的方式是报错注入,如果不这么做,直接指定参数 --dbs,无法进入到启发式注入里面。我们接着看代码,往下直到第 581 行,调用的 heuristicCheckSqlInjection() 函数,意思是启发性注入。

  • 启发式注入做了哪些工作

1、数据库版本的识别
2、绝对路径获取
3、XSS 的测试

数据库版本的识别

首先会从 HEURISTIC_CHECK_ALPHABET 中随机抽取10个字符出现构造 Payload,当然里面的都不是些普通的字符,而且些特殊字符,当我们进行 SQL 注入测试的时候会很习惯的在参数后面加个分号啊什么的,又或者是其他一些特殊的字符,出现运气好的话有可能会暴出数据的相关错误信息,而那个时候我们就可以根据所暴出的相关错误信息去猜测当前目标的数据库是什么。

并且最后生成的这个 payload 是能够闭合的

实际找个网站测试,如图,这就是报出的 SQL 数据库错误

判断在 lib/request/connect.py 的 1532 行

接着跟进 processResponse() 函数,这里和 waf 对比用的同一种方式,不再详细说明

其中 processResponse() 会调用到 ./lib/parse/html.py 中的 htmlParser() 函数,这一个函数就是根据不同的数据库指纹去识别当前的数据库究竟是什么。

最终实现这一功能的其实是 HTMLHandler 这个类,errors.xml 文件内容如图

这一配置文件的比较简单,其实也就是一些对应数据库的正则。sqlmap 在解析 errors.xml 的时候,然后根据 regexp 中的正则去匹配当前的页面信息然后去确定当前的数据库。这一步和 WAF 比对类似。

到此 sqlmap 就可以确定数据的版本了,从而选择对应的测试 Payload,后续我们会看到这是根据莫索引将 payloads 排序,然后选取对应数据库信息的 payloads 进行测试。减少 sqlmap 的扫描时间。

  • 最后这个 DBMS 探测对应的是这一段信息

获取绝对路径与 XSS 探测

相比指纹识别,获取绝对路径的功能模块相对简单,利用正则匹配寻找出绝对路径。

XSS 的探测也比较简单,这里就不作代码分析了

6. 注入检测之正式注入

从启发式注入里面出来,到第 592 行,进行正式的注入检测,跟进

到第 130 行,获取所有的 payload,后续会根据数据库的信息构建索引,将符合索引的 payload 拿去攻击

往下走,先判断有没有做数据库信息的获取,如果有则跳过,如果没有就先进行上一步的启发式注入

接着根据通过报错得到的数据库信息建立索引,将对应最有效的 payload 拿出来。这些 payloads 会进行 while 循环

第 370 行,通过 cleanupPayload() 函数对 payload 进行处理,主要功能其实是做了 payload 的标签替换

最后替换过的 payload 长这样

"AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('qbpxq',(SELECT (ELT(9125=9125,1))),'qxkvq','x'))s), 8446744073709551610, 8446744073709551610)))"

在 sqlmap 中将payload 分为了三部分,上面生成的 fstpayload 就是中间那部分

prefix + payload + suffix 

prefix 和 suffix 就是对应的,闭合前面的结合以及注释后面的结构,这两个属性主要是从 boundary 中进行获取的,boundary 就是前面加载的 boundaries.xml 配置文件,用来闭合的,所以这里作为了 prefix 和 suffix

最后的拼接

并分别对 prefix 和 suffix 进行 clean,然后进行组合,组合之后的 payload 就是 reqPayload,然后进行请求

  • 发出请求最终还是通过 request.queryPage() 来实现的

请求完毕的结果经过 queryPage() 函数来获取界面,但是页面结果是由 kb.chars.startkb.chars.stop 包裹着的

当第一次的注入不成功的时候,会不断变更 prefix,suffix,当 prefix 和 suffix 都变更完毕但还是无法注入时,才会变更 payload,取出另一个 payload 出来,直至 injectable 变量为 true,同时 output=1

并且 injectable=true

7. 爆数据库等操作

经过上一步正式注入的判断,得到的 injectable=true 参数,才能进行下一步的爆数据库操作.

爆库阶段主要是先经过四个函数处理数据后,再调用 action() 函数,跟进。

这里已爆库为例,先看 --dbs 参数有关的这一块,核心函数是 getDbs()

先根据后台数据库信息,输出日志

第 133 行,queries 就是存放之前初始化 queries.xml 的变量

首先通过 count(schema_name) 来获取数据库的个数,然后再通过 limit num,1 来依次获取数据库名,从 queries 变量中获取语句之后就会传递到 getValue 函数

跟进,前面做了一些基础的设置和 payload 的处理与赋值,比如第 401 行的 cleanQuery() 函数,将语句转换为大写,这里我就不跟进了。直接看关键语句,第 451 行,errorUse() 函数

errorUse() 中首先通过正则将 payload 中的各个部分都进行了获取 ,保存到了对应的 field 当中,最终经过一系列处理,取出了 payload 中的 schema_name

跳出 getFields() 函数,往下,将 expression 的值经过 replace 操作,赋值给了 countedExpression,最终得到的值是 'SELECT COUNT(schema_name) FROM INFORMATION_SCHEMA.SCHEMATA'

第 337 行,跟进 _oneShotErrorUse() 函数,在这一个函数中,sqlmap 对目标网站发包,使用的 payload 为 countedExpression,目的是探测数据库个数(count)

具体业务发包在这里

最后将结果传入 extractRegexResult() 函数中进行正则提取

多线程的方式进行注入,而 runThreads() 函数调用了 errorThread() 函数,最终的注入业务还是由 errorThread() 函数来完成的

跟进一下 _errorFields() 函数,将每一个表进行 while 循环操作,再通过 limitQuery() 函数设置最后的 Limit 语句

最后成功 --dbs

sqlmap 流程分析结束

0x04 小结

sqlmap 的流程分析需要非常重视这张图,当感觉代码看不下去的时候看一下这张图可以事半功倍。

在审计开始之前也可以看一下 utils 文件夹下的 python 文件,总体来说流程并不难,看正则的时候其实挺吃力的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/680.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习java——②面向对象的三大特征

目录 面向对象的三大基本特征 封装 封装demo 继承 继承demo 多态 面向对象的三大基本特征 我们说面向对象的开发范式&#xff0c;其实是对现实世界的理解和抽象的方法&#xff0c;那么&#xff0c;具体如何将现实世界抽象成代码呢&#xff1f;这就需要运用到面向对象的三大…

从ChatGPT与New Bing看程序员为什么要学习算法?

文章目录为什么要学习数据结构和算法&#xff1f;ChatGPT与NEW Bing 的回答想要通关大厂面试&#xff0c;就不能让数据结构和算法拖了后腿业务开发工程师&#xff0c;你真的愿意做一辈子CRUD boy吗&#xff1f;对编程还有追求&#xff1f;不想被行业淘汰&#xff1f;那就不要只…

2023年网络安全比赛--CMS网站渗透中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 1.使用渗透机对服务器信息收集,并将服务器中网站服务端口号作为flag提交; 2.使用渗透机对服务器信息收集,将网站的名称作为flag提交; 3.使用渗透机对服务器渗透,将可渗透页面的名称作为flag提交; 4.使用渗透机对服务器渗透,…

一个Bug让人类科技倒退几十年?

大家好&#xff0c;我是良许。 前几天在直播的时候&#xff0c;问了直播间的小伙伴有没人知道「千年虫」这种神奇的「生物」的&#xff0c;居然没有一人能够答得上来的。 所以&#xff0c;今天就跟大家科普一下这个人类历史上最大的 Bug 。 1. 全世界的恐慌 一个Bug会让人类…

jQuery《一篇搞定》

今日内容 一、JQuery 零、 复习昨日 1 写出至少15个标签 2 写出至少7个css属性font-size,color,font-familytext-algin,background-color,background-image,background-sizewidth,heighttop,bottom ,left ,rightpositionfloatbordermarginpadding 3 写出input标签的type的不…

flex布局左边宽度固定,右边宽度动态扩展问题

我们希望在一个固定宽度的容器中&#xff0c;分左右两边&#xff0c;左边宽度固定大小&#xff0c;右边占满&#xff0c;使用flex布局时&#xff0c;如下&#xff1a; 对应代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta…

LeetCode - 198 打家劫舍

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装…

【华为机试真题 Python实现】2023年1、2月高频机试题

文章目录2023年1季度最新机试题机考注意事项1. 建议提前刷题2. 关于考试设备3. 关于语言环境3.1. 编译器信息3.2. ACM 模式使用sys使用input&#xff08;推荐&#xff09;3. 关于题目分值及得分计算方式4. 关于做题流程5. 关于作弊2023年1季度最新机试题 两个专栏现在有200博文…

2023还有人不知道kubernetes?| 初步理解kubernetes

文章目录Kubernetes(K8s)一、Openstack&VM1、**认识虚拟化****1.1**、什么是虚拟化**1.2、虚拟化分类**2、OpenStack与KVM、VMWare2.1、OpenStack2.2、KVM2.3、VMWare二、容器&编排技术1、容器发展史1.1、Chroot1.2、FreeBSD Jails1.3、Solaris Zones1.4、LXC1.5、Dock…

深度学习 Day26——使用Pytorch实现猴痘病识别

深度学习 Day26——使用Pytorch实现猴痘病识别 文章目录深度学习 Day26——使用Pytorch实现猴痘病识别一、前言二、我的环境三、前期工作1、设置GPU导入依赖项2、导入猴痘病数据集3、划分数据集四、构建CNN网络五、训练模型1、设置超参数2、编写训练函数3、编写测试函数4、正式…

【LeetCode】1544. 整理字符串、LCP 44. 开幕式焰火

作者&#xff1a;小卢 专栏&#xff1a;《Leetcode》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 1544. 整理字符串 LCP 44. 开幕式焰火 1544. 整理字符串 1544. 整理字符串 题目描述…

金三银四、金九银十 面试宝典 Spring、MyBatis、SpringMVC面试题 超级无敌全的面试题汇总(超万字的面试题,让你的SSM框架无可挑剔)

Spring、MyBatis、SpringMVC 框架 - 面试宝典 又到了 金三银四、金九银十 的时候了&#xff0c;是时候收藏一波面试题了&#xff0c;面试题可以不学&#xff0c;但不能没有&#xff01;&#x1f941;&#x1f941;&#x1f941; 一个合格的 计算机打工人 &#xff0c;收藏夹里…

正则表达式高阶技巧之匹配模式(使用python实现)

匹配模式介绍不区分大小写模式模式的指定方式应用单行模式多行模式注释模式其它模式修饰符的作用范围介绍 我们在正则中所说得匹配模式&#xff08;match mode&#xff09;&#xff0c;指的是匹配时使用的规则。设置特定的匹配模式&#xff0c;可能会改变对正则表达式的识别&a…

业内人士真心话,软件测试是没有前途的,我慌了......

我在测试行业爬模滚打7年&#xff0c;从点点点的功能测试到现在成为高级测试&#xff0c;工资也翻了几倍。个人觉得&#xff0c;测试的前景并不差&#xff0c;只要自己肯努力。 我刚出来的时候是在鹅厂做外包的功能测试&#xff0c;天天点点点&#xff0c;很悠闲&#xff0c;点…

Spark SQL支持DataFrame操作的数据源

DataFrame提供统一接口加载和保存数据源中的数据&#xff0c;包括&#xff1a;结构化数据、Parquet文件、JSON文件、Hive表&#xff0c;以及通过JDBC连接外部数据源。一个DataFrame可以作为普通的RDD操作&#xff0c;也可以通过&#xff08;registerTempTable&#xff09;注册成…

嵌入式安防监控项目——实现真实数据的上传

目录 一、相关驱动开发 二、A9主框架 三、脚本及数据上传实验 https://www.yuque.com/uh1h8r/dqrma0/tx0fq08mw1ar1sor?singleDoc# 《常见问题》 上个笔记的相关问题 一、相关驱动开发 /* mpu6050六轴传感器 */ i2c138B0000 { /* #address-cells <1>…

web实现太极八卦图、旋转动画、定位、角度、坐标、html、css、JavaScript、animation

文章目录前言1、html部分2、css部分3、JavaScript部分4、微信小程序演示前言 哈哈 1、html部分 <div class"great_ultimate_eight_diagrams_box"><div class"eight_diagrams_box"><div class"eight_diagrams"><div class&…

SpringBoot-实用开发篇

SpringBoot开发实用篇开发实用篇中因为牵扯到SpringBoot整合各种各样的技术&#xff0c;所以在整合每一个技术之前&#xff0c;都会做一个快速的普及&#xff0c;这样的话内容整个开发实用篇所包含的内容就会比较多。在学习的时候&#xff0c;如果对某一个技术不是很清楚&#…

硬刚ChatGPT!文心一言能否为百度止颓?中国版ChatGPT“狂飙”的机会在哪儿?

文章目录目录产品背景发展历程科技简介主要功能合作伙伴结语文心一言 &#xff08;英文名&#xff1a;ERNIE Bot&#xff09; *是百度基于文心大模型技术推出的生成式对话产品&#xff0c;被外界誉为“中国版ChatGPT”&#xff0c;将于2023年3月份面向公众开放。 [40] 百度在人…

python自动化办公(二)

上接python自动化办公&#xff08;一&#xff09; 文章目录文件和目录操作使用shutil库文件查找globfnmatchhashlib文件和目录操作 使用shutil库 shutil库也是Python标准库&#xff0c;它可以处理文件、文件夹、压缩包&#xff0c;能实现文件复制、移动、压缩、解压缩等功能。…