0%

初探Sqlmap(一)

背景

​ 基于业务存在 Sql 注入危险 的现状,开始研究相关工具的使用一一 Sqlmap。

简介

Sqlmap 是一个开源的渗透测试工具,可以用来进行自动化检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。(Sqlmap源码

​ 本文会简单描述下 Sqlmap 的使用方式,然后详细介绍下 SqlmapAPI 的大致原理。

工具使用

一、 命令行方式

优点: 上手快,命令行输入/输出容易理解;问题定位方便。

​ 对于新手初次使用该工具时候,建议使用命令行模式先将整个流程跑通(如参考:Sql注入获取数据库信息,很详细的介绍了从Sql注入到破解数据库信息的整个过程),想了解更多命令行参数可以阅读源码目录 lib/parse/cmdline 文件获取更多参数的含义。

大致的命令行格式如下:

1
2
3
# 针对参数是直接拼接在链接后方的命令行
python3 sqlmap.py -u "http://ip:port/user/login?username=foo&passwd=bar"
python3 sqlmap.py -u "http://ip:port/user/login?username=foo&passwd=bar" --batch --dbs

我常用的命令行参数有:

1
2
3
4
5
6
--dbms='MySQL'		# 指定后台扫描数据库类型为 MYSQL
--risk 3 # 执行中的风险系数,1~3,级别越高,可能会使用'or'等等语句,默认1
--level 3 # 级别越高,发送的请求(payload)越多,默认1
--random-agent # Use randomly selected HTTP User-Agent header value
--keep-alive # Use persistent HTTP(s) connections
--batch # 自动选择默认的值,否则需要手动选择

⚠️ 注意: 针对实践中的一些特定的场景,如加 headers 或对 POST 方法注入的命令行如下:

1
2
3
4
5
6
7
8
# 如果需要自定义header,使用headers参数,注意:多个header之间用\n间隔
# 具体的data参数需要进行URL编码。
python3 sqlmap.py -u "https://xxx/test/user" --headers="xxx: xxx\nxbbb: 4\nxcc: 111\nx-token: xxx" --data='data=%7B%22id%22%3A%22111%22%2C%22new%22%3A%229%22%2C%20%22old%22%3A%221%22%7D' --dbms='MySQL ' --random-agent --batch

# 如上请求的POST参数具体格式如下
data={"id":"111","new":"9", "old":"1"}
# 在注入的时候,要对data中所有的字段进行注入,则需要将上述data修改如下,在每个字段的value值后新增一个*号:
data={"id":"111*","new":"9*", "old":"1*"}

注意⚠️: 对于data里面需要注入的参数带 * 号时候,转义后仍然需要是 * 号,不能直接转义,否则就导致最终仅仅是对 data最外层这个data进行了注入!!!

缺点:

  • 由于Sqlmap每检测一个站点都需要开启一个新的命令行窗口或者结束掉上一个检测任务。虽然 -m 参数可以批量扫描URL,但是模式也是一个结束扫描后才开始另一个扫描任务,执行效率较慢;
  • 对于想获取历史任务的日志和结果操作不友好。

实际使用场景:我大多数时候是用该方式进行参数调试。

二、API请求

优点: Sqlmap封装了较多的API接口,更方便操作扫描任务。

  • 通过 API接口下发扫描任务,无需每次执行扫描新开一个命令行窗口,通过Http接口即可执行扫描任务;

  • 可通过 API接口获取历史执行结果以及日志进行聚合和分析。

具体如何使用 SqlmapAPI不具体描述,可参考:细说Sqlmap,比较详细的说明了 SqlmapAPI的使用。

SqlmapAPI 原理

SqlmapAPI 服务器启动

SqlmapAPI的服务器启动逻辑还是很简单,下图为整个 sqlmapapi.py 脚本服务器启动流程。

sqlmapAPI.png

api.py

SqlmapAPI使用的是 sqlite3 数据库。在 server() 方法中主要是进行本地数据库的路径创建、连接以及创建必须的表结构。

1
2
3
4
5
6
7
8
# 生成一个随机地址, 用于存放数据库文件
_, Database.filepath = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.IPC, text=False)
...

# Initialize IPC database
DataStore.current_db = Database()
DataStore.current_db.connect()
DataStore.current_db.init()

使用API模式进行sql注入测试时候,主要使用如下几个API接口(具体如何调用可参考:细说Sqlmap

1
2
3
4
5
@get("/task/new")				# 创建一个新的扫描任务
@post("/scan/<taskid>/start") # 指定任务进行扫描
@get("/scan/<taskid>/status") # 查看指定任务的状态
@get("/scan/<taskid>/data") # 查看指定任务的执行结果
@get("/scan/<taskid>/log") # 查看指定任务的扫描执行日志

Task执行原理

此处仅介绍 /scan/<taskid>/start,主要通过 DataStore.tasks[taskid].engine_start() 进行执行指定任务执行扫描。

1
2
3
4
5
6
7
8
9
10
11
12
13
def engine_start(self):
handle, configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True)
os.close(handle)
saveConfig(self.options, configFile)

if os.path.exists("sqlmap.py"):
self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN)
elif os.path.exists(os.path.join(os.getcwd(), "sqlmap.py")):
self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.getcwd(), close_fds=not IS_WIN)
elif os.path.exists(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "sqlmap.py")):
self.process = Popen([sys.executable or "python", "sqlmap.py", "--api", "-c", configFile], shell=False, cwd=os.path.join(os.path.abspath(os.path.dirname(sys.argv[0]))), close_fds=not IS_WIN)
else:
self.process = Popen(["sqlmap", "--api", "-c", configFile], shell=False, close_fds=not IS_WIN)

从上面的代码来看,最终还是使用的 sqlmap.py 脚本来进行的任务扫描,也可以通过打印出源代码中 configFile 的路径,来查看每次任务的配置参数是什么。

总结

​ 至此,SqlmapAPI就介绍到此,可以看出来该脚本主要是启动一个本地数据库以及Web后台,来记录所有的任务相关信息。下一次将再继续分析 sqlmap.py 脚本中是如何进行sql注入的测试。

参考资料

sqlmap中文介绍
Sql注入获取数据库信息
Sqlmap源码
Sqlmap超详细攻略
细说Sqlmap

------------- 本 文 结 束 感 谢 您 的 阅 读 -------------