Flask-SSTI限制长度绕过
前置知识
利用config绕过payload长度限制
Flask 框架中存在**config全局对象**,用来保存配置信息
config 对象实质上是一个字典的子类,可以像字典一样操作
因此要更新字典,我们可以使用 Python 中的 update() 方法
用 update() 方法 + 关键字参数更新字典:
1 | |

Jinja 模板中存在 set 语句,用来设置模板中的变量,可以在模板中创建新的变量:
1 | |

因此,我们可以使用 Jinja 模板的 set 语句配合字典的 update() 方法来更新 config 全局对象
下面是利用{% set ... %}语句新定义一个变量x,其值为 config.update(s='string') 的返回值,config.update(s='string') 会尝试将键 's' 和值 'string' 添加到 config 字典中
1 | |
下图是原本的config

下图是经过修改的

可以看到在最后多出了's':'string',这就是我们新添加的
接下来我们就可以使用这个方法在 config 全局对象中更新值
做题实操
题目来源:ctfshow–ezzz_ssti
根据题目可知是SSTI,注入点就是登录的地方


bp抓包fuzz,看看过滤了什么

发现似乎没什么过滤的,直接打SSTI,但是发现存在长度限制

经过测试后发现限制payload长度不能超过40,因为没有过滤,所以可以利用config进行绕过
思路就是利用set和update将想要的模块或函数写入到全局变量中,依次获取到lipsum函数的全局命名空间__globals__(包含所有内置函数和变量),然后从全局命名空间中获取到os模块,最后就是获取到os模块中的popen函数,从而可以执行命令
以下是原本的config

利用set和update在config全局变量中新创建一个a变量,并赋其键值为update
1 | |


可以看到成功将键值为update的变量a写入全局变量中,之后可以直接使用变量a来代替update,可以在之后使用更短的payload
然后就是利用相同的方法,将__globals__、os、popen写入到全局变量中
1 | |


1 | |


1 | |


最后就可以直接使用全局变量中的popen函数执行命令


完整payload
1 | |