freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

《深入研究注入工具SqlMap内部结构和原理及其运行机制》系列(三):sqlmapapi的核心运行机制
R4be1 2022-03-06 00:19:08 206954
所属地 江西省

本文主要知识点:

Bollte单文件模块框架

用sqlmapapi启动扫描的核心引擎engine_start()

在sqlmap/lib/utils/api.py的58行和60行处,我们可以发现从第三方插件thirdparty库中导入了发送get和post请求的库

from thirdparty.bottle.bottle import get
from thirdparty.bottle.bottle import post

image-20220206105220663.png

第三方插件bottle是Python的一个快速,简单和轻巧的WSGI微Web框架。它是个单文件模块bottle.py且不依赖其他Python标准库,它是构建静态和动态HTTP请求的关键所在,这个框架还是相对比较冷门的,不过够经典,在一些应用程序规模比较小的情况下可以实现快速简洁地开发。可惜相对于那些大型的web应用,Bottle还是显得有些无力,与Pylons(类似于Django和TurboGears)这类框架相比Bottle的优势就很难体现出来。

我们可以看到在sqlmap/lib/utils/api.py中,有相当多基于HTTP协议的接口,另外利用装饰器给各种函数提供了一个可靠的交互接口。

image-20220206142045166.png

先用一段代码来快速地介绍这个单文件模块框架到底有多方便快捷。

from bottle import route, run, template

@route('/test/<name>')
def index(name):
    return template('<b>各位大佬好,name接口参数为: {{name}}</b>!', name=name)
run(host='127.0.0.1', port=8888)

保存执行后即可看到效果,非常简单。

我们可以再用Bollte来实现简单登录业务。

@get('/login') #这里其实可以用 @route('/login')
def login():
	html='''
        <form action="/login" method="post">
            Username: <input name="username" type="text" />
            Password: <input name="password" type="password" />
            <input value="Login" type="submit" />
        </form>
    '''
    return html
@post('/login') #或者用 @route('/login', method='POST')
def do_login():
    username = request.forms.get('username')
    password = request.forms.get('password')
    if check_login(username, password):#check_login()是一个用于检查密码正确性的自定义函数
        return "<p>Your login information was correct.</p>"
    else:
        return "<p>Login failed.</p>"

可以发现整个代码非常的简洁就实现了登录界面,它的大致流程是**/login绑定了两个回调函数,一个回调函数响应GET请求,一个回调函数响应POST请求。如果浏览器使用GET请求访问/login,则调用login_form()函数来返回登录页面,浏览器使用POST方法提交表单后,调用login_submit()函数来检查用户有效性,并返回登录结果。**

我们根据sqlmapapi提供的http交互接口可以知道用于启动扫描的接口是第492行的/scan//start,这个接口它绑定了一个回调函数scan_start()用于启动引擎来扫描注入。大概流程就是 user发送包含url等参数的post请求包给/scan//start接口(是从sqlmapapi中第369行@get("/task/new")的想拥抱中获取的),该接口调用scan_start(taskid)来启动扫描。

从上面我们不难得知:sqlmapapi发起扫描的关键就在于这个scan_start函数。

@post("/scan/<taskid>/start")
def scan_start(taskid):
    """
    Launch a scan
    """

    if taskid not in DataStore.tasks:
        logger.warning("[%s] Invalid task ID provided to scan_start()" % taskid)
        return jsonize({"success": False, "message": "Invalid task ID"})

    if request.json is None:
        logger.warning("[%s] Invalid JSON options provided to scan_start()" % taskid)
        return jsonize({"success": False, "message": "Invalid JSON options"})

    for key in request.json:
        if key in RESTAPI_UNSUPPORTED_OPTIONS:
            logger.warning("[%s] Unsupported option '%s' provided to scan_start()" % (taskid, key))
            return jsonize({"success": False, "message": "Unsupported option '%s'" % key})

    # Initialize sqlmap engine's options with user's provided options, if any
    for option, value in request.json.items():
        DataStore.tasks[taskid].set_option(option, value)

    # Launch sqlmap engine in a separate process
    DataStore.tasks[taskid].engine_start()

    logger.debug("(%s) Started scan" % taskid)
    return jsonize({"success": True, "engineid": DataStore.tasks[taskid].engine_get_id()})

jsonize是从第34行from lib.core.convert import jsonize过来的,追到sqlmap/lib/core/convert.py中看看

image-20220206194035894.png

有点编程常识的一眼都能看出来,就是把data的数据当作json数据导出。

整个流程我翻译给各位讲一遍:

  1. 将接口中含有的(从get请求/task/new接口获取的)taskid作为scan_start函数的参数传入。
  2. 如果taskid不存在于DataStore.tasks这个字典里,输出警告日志“[taskid] 提供给 scan_start() 的任务 ID 无效”,并返回json数据{"success": False, "message": "Invalid task ID"}
  3. 如果请求包里的json数据为空,输出警告日志“[taskid] 提供给 scan_start() 的 JSON 选项无效”,并返回json数据{"success": False, "message": "Invalid JSON options"}
  4. 遍历请求包中的key,如果key在RESTAPI_UNSUPPORTED_OPTIONS(第52行 from lib.core.settings import RESTAPI_UNSUPPORTED_OPTIONS)中,则输出警告日志“"[taskid] 不支持的选项 key 提供给 scan_start()”,并返回json数据{"success": False, "message": "Unsupported option '%s'" % key}
  5. 遍历请求包中的键值,将参数作为选项存储到DataStore类中的字典tasks中。
  6. 将DataStore类中的字典tasks中存储的taskid用engine_start()方法启动
  7. 日志输出taskid扫描启动
  8. 返回json数据{"success": True, "engineid": DataStore.tasks[taskid].engine_get_id()}

不难看处,第5步之前一堆的if,在正确传入参数的情况下是不用考虑的

而在第6步可以明显的一个类方法engine_start(),启动引擎,应该就是整个sqlmapapi的核心了。

在166行处发现了engine_start,是作为类的方法运作的

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)

不难看出,其实整个sqlmapapi的核心运行机制还是通过调用系统命令行去调用sqlmap发起注入扫描

用三句话总结sqlmapapi对整体结构的处理机制:有条不紊,有条不紊,还是tmd有条不紊。

真的从哪都看不出sqlmapapi的开发者对于整个环境有特别着急处理的地方,反而运用了如Bottle单文件模块框架,环境判断机制,交叉引用,json响应包等来优化整体的兼容性和移动性以及合理性,并没有说像一些项目开发者毛毛躁躁地处理各类环境以及运行环节,这对开发者的要求还是比较高的。

sqlmapapi的分析差不多就到这,值得一说的是,分析sqlmap的时候还会略带分析一下sqlmapapi。

# SqlMap # web安全 # 工具分析 # 自动化渗透工具
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 R4be1 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
待看文章
R4be1 LV.2
这家伙太懒了,还未填写个人描述!
  • 3 文章数
  • 10 关注者
《深入研究注入工具SqlMap内部结构和原理及其运行机制》系列(二):循序渐进——从sqlmapapi.py开始研究
2022-03-06
《深入研究注入工具SqlMap内部结构和原理及其运行机制》系列(一):SqlMap文件结构介绍
2022-02-10