SQLMap 源码分析:Part 1

0x00 前言

SQLMAP是一款测试SQL注入的神器,在大概一年前就有了想法,但是那时候静态看的一脸懵逼再加上个人的懒惰所以导致我没有继续看下去。

那么为什么我又会来看呢?主要有几个原因

  1. 想知道 SQLMap 高效测试 SQL 注入的一个逻辑
  2. 学习 SQLMap 的工程结构,学习其中处理的比较好的点
  3. 学习 SQLMap 中的一些我个人比较感兴趣的功能点:启发式扫描、页面相似度、时间盲注的检测、结果缓存的处理等
  4. 最近想做一些自动化 Fuzz 的东西并深知自己代码能力的薄弱,所以在做之前先看一下 SQLMAP 源码学习一下

但是光看不做点东西总感觉和没看一样,正好最近在学习 Go,所以也尝试用 Go 来仿制一个 SQLMAP ,不求很好用只求弄明白 SQLMAP 中的一些核心功能并提炼出来以此在别的场景能用到

本文只会针对一些我个人比较感兴趣的功能点进行介绍(毕竟sqlmap本身也比较复杂),所以不可能面面俱到,还请海涵

0x01 前期准备

SQLMAP 如果不结合动态调试的话是会很头大的,尤其是其中的 kb、conf 这两个值

靶场准备:SQLi-labs - Docker

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

Pycharm:直接debug 配置界面添加参数就可以了

image-20220209174623839

0x02 正文

在实际阅读源码并且通过 go 仿写的过程中发现 sqlmap 远比我想象中要复杂,所以我先从最简单的报错注入检测到爆库进行了阅读,先大致过一下sqlmap的一个整体流程

https://github.com/KpLi0rn/Gosqlmap-Beta

0x03 初始化

在还没解析 URL 发请求之前 SQLMAP 首先会对一些环境、变量来做一些初始化的处理

image-20220210211505479

initOptions : 解析我们的命令行参数

init: 初始化函数

在 init 函数中通过调用各种函数进行参数的设置、payload 的加载等

image-20220210211623759

我个人比较关注的是 payload 加载的部分,我红框框出来的这几个函数就是 sqlmap 加载 payload 的主要函数

image-20220210212907496

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

挑选一个函数来看,发现逻辑就是从 data 下的 xml 中加载对应的 xml 文件,调用 parseXmlNode 函数进行解析

image-20220210211931427

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

image-20220210212036764

动态调试一下可以很清晰的看到各种 payload

在 sqlmap 中可以经常看到 conf 和 kb 这两个变量

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

image-20220210212123099

xml 中自然就是一些 payload 相关的信息

image-20220210212206439

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

image-20220210213003843

如果觉得 sqlmap 代码太多也可以参考我go写的简化版的代码,init 地址为:https://github.com/KpLi0rn/Gosqlmap-Beta/blob/main/lib/core/option.go

0x04 URL

在初始化之后进行 start 函数之后,会先对我们输入的 URL 进行解析,即调用 parseTargetUrl 函数

image-20220210213325015

简单看一下 sqlmap 中是如何处理我们输入的 url 的

首先如果我们传入的 url 并不是协议开头,正则没有匹配到的情况下就会先进行端口匹配如果发现 :443 就判定为 https 反之为 http

image-20220210213648253

然后对传入 url 进行冒号分离,分别获取协议、路径、hostname这些

image-20220210213814543

然后将各部分组合起来赋给 url

image-20220210214746570

解析完URL之后会先对目标进行一次链接检测,如果发现无法连接就返回 False

image-20220210214903502

然后就是 waf 检测

0x05 WAF检测

代码还是动态看比较容易看懂,正好我的博客上了阿里云waf,所以就来看一下 sqlmap 是如何做检测的吧

首先 sqlmap 生成了百分百会被 waf 拦截的 payload 例如 :<script>alert(1)</script> 这种

image-20220211151506160

然后将payload作为参数传入 queryPage 来发起请求

image-20220211152316628

在 getPage 中会调用 processResponse 来处理响应结果

image-20220211152350684

在函数中可以看到会对response做一个检查

image-20220211152431650

跟进发现通过正则表达式来对页面进行一个匹配

image-20220211152507804

正则对应的规则在 thirdparty/identywaf/data.json 中,可以看到有正则和签名来进行判断

image-20220211152554529

image-20220211152638161

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

image-20220211153127399

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

image-20220211153143102

具体关于相似度的代码分析我打算另开一篇来好好看看放在本文的话篇幅就有点过长了

0x06 注入检测

waf 检测结束之后就开始进入到核心部分,SQL 注入的检测了

sqlmap 这里主要有两个检测,第一个红框处是启发式检测,对目标参数 sql 注入做一个初步的判断,第二个是注入检测

image-20220210215817697

启发式检测

首先会随机生成会造成sql闭合错误的payload

image-20220211154300903

并发送请求,通过是否报错来进行快速判断

image-20220211154335007

image-20220211155253513

注入检测

报错注入检测

启发式检测是初步判断,那么现在就是正式开始注入检测了

image-20220210220844843

首先是将 pyload 全部加载到 tests 变量中,然后 while 进行遍历

首先会根据前面的启发式检测的结果来初步确定数据库

image-20220210223219911

然后就是从 tests 中弹出的 payload 进入 cleanupPayload函数对payload 进行清理

image-20220210223332671

在 cleanupPayload 函数中对针对 payload 中的各种标签进行替换

image-20220210223400989

替换后大致张这样

AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('qpbvq',(SELECT (ELT(5688=5688,1))),'qpkqq','x'))s), 8446744073709551610, 8446744073709551610)))

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

prefix + payload + suffix 

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

image-20220210223941924

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

image-20220210224151072

通过 queryPage 请求页面,然后对目标页面进行正则提取,页面结果是为 kb.chars.start 和 kb.chars.stop 包裹着的

这里 kb.chars.start -> qpbvq kb.chars.stop-> qpkqq

image-20220210224739195

所以去掉包裹的话如果发现为 1 就说明注入存在,并且设置 injectable 为 True

image-20220210224808932

0x07 爆数据库

在 checkSqlInjection 函数中判断了sql注入是否存在,如果存在的话就会输出 payload 以及对应的信息,这里输出的 Title 和 Type 都是在 xml 中 payload 中对应着的

image-20220211143916653

image-20220211144028987

输出信息之后就是要对数据库名等后续信息进行获取,主要是在 action 函数中,我们这里只介绍如何获取 dbs 所以这里就来看爆库这块

核心函数是 getDbs

image-20220211144137051

来到 getDbs ,首先是判断是否存在缓存,如果存在缓存就直接返回缓存的结果,如果没有就从 queries 中获取payload语句

queries 就是存放之前初始化 queries.xml 的变量

image-20220211144346046

sqlmap 爆库的逻辑也和常规的一样,首先通过 count(schema_name) 来获取数据库的个数,然后再通过 limit num,1 来依次获取数据库名

从queries 变量中获取语句之后就会传递到 getValue 函数

image-20220211144737451

在 getValue 函数中前面主要是一些基础设置和一些对payload 的处理这里我们不用去关注,直接来看关键位置 errorUse

image-20220211145231389

在 errorUse 中首先通过正则将 payload 中的各个部分都进行了获取 ,这里指返回了 payload 中的 schema_name

image-20220211145344707

对 schema_name 部分进行了处理,将 schema_name -> count(schema_name) 然后传递给了 _oneShotErrorUse 函数调用

image-20220211145612121

在 _oneShotErrorUse 函数中主要是处理形成最终 payload 并发送请求通过正则获取结果

sqlmap 会把我们前面的 payload 放到 [QUERY] 中进行替换,然后通过 queryPage 发送请求

image-20220211150100178

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

image-20220211150207059

ps: 这里提一嘴,sqlmap 这里是采用的多线程

image-20220211150328271

image-20220211150452588

在上面 sqlmap 知道了数据库数量之后就会 while 依次调用获取数据库名了,另外 sqlmap 中各种类型的都在 lib/techniques 文件夹中

image-20220211150735480

如果觉得sqlmap不清晰也可以看一下我用go写的 https://github.com/KpLi0rn/Gosqlmap-Beta/blob/main/lib/techniques/error/use.go 应该能帮助到您

0x08 总结

当然一篇文章是根本不可能把 sqlmap 介绍清楚的,所以本文只是 part 1,sqlmap 能做到如此高效的检测/利用在代码中其实有着很多细节处理,像一些装饰器、缓存、判断动态参数、误报等代码我都没有在文中涉及而这些也都是让sqlmap变的可靠/高效的一部分,本文主要是简单的介绍了 sqlmap 从初始化到报错注入利用的一个流程

评论

  1. June
    3月前
    2022-2-16 14:16:16

    木神最后一行字打错了,是高效打成搞笑了

    • KpLi0rn 博主
      3月前
      2022-2-23 1:37:04

      这就改过来

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇