/ Web安全

Sqlmap源码阅读-1

0x01 读源码

不得不说,安全从业者的编码能力有限,但是大牛们的编码能力都不差,想想自己也没有完成过什么像样子的项目,还是希望自己在代码能力上能够有一定的提升,最近在研究bugscan的架构,没有开源,根据他的文档和开发SDK,猜测这个架构的设计一定很牛逼,支持主机/web漏洞扫描,扩展性好。最后我找到了知道创宇的Pocsuite,一款轻量级的Poc集成框架,在我研究了大概一个多小时后惊奇的发现他的代码和Sqlmap是如此的相似,甚至部分源码是直接Copy的。上次在t00ls论坛看到有人分享sqlmap源码阅读的文章,当时只是草草的看了看,现在打算仔细研究一遍,学习一下相关的编码风格和框架特性。

0x02 说干就干

Sqlmap目录结构

Sqlmap目录结构

sqlmap.py是入口文件,我们在使用的时候也是python sqlmap.py开始
sqlmap.conf是一个调用配置文件,我们一般使用的是命令行参数的方式,其实可以通过这个文件配置sqlmap的探测目标,对于sqlmap命令不熟悉的时候可以使用这个文件,里面相关说明比较详细。

目录 说明
doc/ 该目录是sqlmap文档
extra/ 一些额外的组件,比如有linux下发出声音、icmp shell、windows/linux下的shell程序,还有涉及编码转换的东西
lib/ 核心库,涉及到探测注入等等都在这个库里面,是在看sqlmap源码主要关注的部分
plugins/ 插件库,比如使用sqlmap连接数据库时需要用到这里面的一些不同数据库的python连接类
procs/ 存放了一些sql语句,主要涉及dns带外传输数据的一些数据库命令
shell/ 存放一些shell文件,比如我们发现存在sql注入时尝试写webshell,就会用到这里面的shell文件
tamper/ 不用多说,用过都知道
thirdparty/ 第三方库,比如命令输出带颜色、bs、chardet等等第三方库
txt/ 存放一些表名、列名字典
udf/ udf提权用的dll文件
waf/ 探测waf的,可以看到有360、nsfocus、knowsec等等的waf探测代码
xml/ 字典,比如根据页面返回错误信息判断数据库类型的正则数据,xml格式

等lib库看个大概再对这个目录下的目录再做一个详细说明

0x03 入口

入口文件python.py

前面70行左右,主要是引入一些库文件,并对python版本进行了检测,忽略了一些warning提示,引入模块出错的报错处理等等,这部分简单看看就行。

继续往下77~113行,定义了两个函数,modulePath()和checkEnvironment(),这两个函数也是在main函数中最先使用到的函数,第一个是获取脚本的路径,第二个是检测sqlmap的环境是否正常。

checkEnvironment()  
setPaths(modulePath())
banner()

main函数调用了这两个函数,并用banner函数打印sqlmap的banner信息。setPath是设置系统中的相关路径,比如模块加载路径,字典路径等等

cmdLineOptions.update(cmdLineParser().__dict__)
initOptions(cmdLineOptions)

解析参数,并将这些参数初始化到变量中去,方便后面使用。
跟进cmdLineParser -> from lib.parse.cmdline import cmdLineParser
这里大概猜到lib.parse目录下写的是解析参数相关的脚本
调用了分割命令的python默认库shlex以及处理命令行参数的optparse库

在cmdLineParser函数里面,有几百行的add_option操作,,,这么多命令参数哇!
仔细看会发发现,sqlmap将不同的参数类型进行了分类,有parser/target/request/optimization/injection/enumeration/techniques等等几个类型,顾名思义可以想到各种类型的参数的作用,这部分可以使用-help来查看各种参数的作用。其中enumeration是在存在注入后我们使用的获取数据库名、列名、dump数据时的那些参数
这部分还是值得仔细过一遍,以对sqlmap的功能有一个更加全面的了解,这里我不在赘述记录。

Sqlmap解析参数保存在全局变量中

简单看一下cmdLineParser()的返回值。其中cmdLineOptions是一个sqlmap单独实现的AttribDict类,为了方便字典的操作,含有深拷贝等等。简单的理解就是现在我们输入的注入参数现在放在了cmdLineOptions中了。

然后使用initOptions进行初始化
跟进initOptions -> from lib.core.option import initOptions

def initOptions(inputOptions=AttribDict(), overrideOptions=False):
    _setConfAttributes()
    _setKnowledgeBaseAttributes()
    _mergeOptions(inputOptions, overrideOptions)

0x04 初始化参数

上面我跟进到参数初始化,这一部分主要是根据我们输入的参数,设置属性和其他选项
_setConfAttributes()函数初始化conf配置为None

def _setConfAttributes():
    """
    This function set some needed attributes into the configuration
    singleton.
    """

    debugMsg = "initializing the configuration"
    logger.debug(debugMsg)

    conf.authUsername = None
    conf.authPassword = None
    conf.boundaries = []
    conf.cj = None
    conf.dbmsConnector = None
    conf.dbmsHandler = None
    conf.dnsServer = None
    conf.dumpPath = None
    conf.hashDB = None
    conf.hashDBFile = None
    conf.httpCollector = None
    conf.httpHeaders = []
    conf.hostname = None
    conf.ipv6 = False
    conf.multipleTargets = False
    conf.outputPath = None
    conf.paramDict = {}
    conf.parameters = {}
    conf.path = None
    conf.port = None
    conf.proxyList = None
    conf.resultsFilename = None
    conf.resultsFP = None
    conf.scheme = None
    conf.tests = []
    conf.trafficFP = None
    conf.HARCollectorFactory = None
    conf.wFileType = None

其中conf来自from lib.core.data import conf。也是一个AttribDict。

接下去_setKnowledgeBaseAttributes(),设置了一大堆叫the knowledge base的东西,官方的解释是This function set some needed attributes into the knowledge base singleton.我不是很清楚这个kb是个什么,但是看到一些参数名,感觉是和运行环境相关,看到有页面编码什么的。接下来__mergeOptions(inputOptions, overrideOptions),把我们命令行的参数赋值到了conf和kb里面去。修改代码如下,调试发现,我们在命令行输入的参数目标url、cookie、ua等会被放进conf这个字典中。

_setConfAttributes()
_setKnowledgeBaseAttributes()
_mergeOptions(inputOptions, overrideOptions)
print(conf)
print(kb)
exit()

Sqlmap解析参数保存在全局变量中

0x05 总结

这一部分的分析主要涉及的sqlmap运行环境和参数解析的部分,继续向下分析就会看到调用init()函数,我觉得几个值得学习的地方:

  • 参数类型分类,不同类型的参数分类
  • 初始化参数的过程
  • AttribDict