谈谈Django的RCE
字数 1991 2025-08-18 17:33:25
Django RCE漏洞分析与防御指南
0x00 前言
Django作为一款高安全性框架,历史上很少出现无条件直接利用的RCE漏洞。本文详细分析两种因配置不当导致的Django RCE漏洞:CVE-2014-0472历史漏洞和FileBasedCache新型RCE利用方法。
0x01 CVE-2014-0472漏洞分析
漏洞描述
2014年发现的漏洞,位于django/core/urlresolvers.py(现为django/url/resolvers.py)中的reverse函数过滤不当,导致可以任意获取内部函数,当攻击者知道代码内部存在恶意模块时,可导致任意代码执行。
漏洞原理
reverse函数用于URL反向解析,根据视图名称查找对应URL- 漏洞点在于
get_callable函数中未对传入的函数名进行充分过滤 - 关键危险代码:
lookup_view = getattr(import_module(mod_name), func_name) - 当传入不可调用函数名时,会直接通过
getattr调用该函数
漏洞利用条件
- 存在使用
redirect或reverse函数且参数可控的视图 - 攻击者知道项目内部存在的恶意引用
典型漏洞代码示例
def redirect_test(request):
page = request.GET.get('page', None)
if page is not None:
redirect(page) # 危险:直接使用用户输入作为redirect参数
return HttpResponse("OK")
漏洞复现步骤
- 构造恶意请求:
http://example.com/example?page=os.system - Django会尝试调用
os.system函数 - 如果知道项目内部使用的恶意模块,可进一步利用
官方修复
- 增加了对传入
reverse函数内部功能的检查 - 检查方法是否存在于URL路由表中
- 补丁地址:https://github.com/django/django/commit/8b93b31487d6d3b0fcbbd0498991ea0db9088054
0x02 FileBasedCache RCE漏洞
漏洞描述
Django的缓存配置错误可能引发RCE,特别是使用FileBasedCache时,由于直接使用pickle反序列化缓存文件,攻击者可构造恶意序列化数据实现代码执行。
漏洞原理
- FileBasedCache使用pickle进行序列化/反序列化
- 关键危险代码:
pickle.loads(zlib.decompress(f.read()))(get方法)pickle.load(f)(_is_expired方法)
- 无任何过滤直接反序列化用户可控的缓存文件
漏洞利用条件
- 知道缓存文件存储位置(通常为
/var/tmp/django_cache或c:/foo/bar) - 知道缓存文件命名规则
- 有权限写入缓存目录(通过其他服务或文件上传)
缓存文件命名规则
- GET方式:
md5("<prefix>:version:views.decorators.cache.cache_page.<prefix>.GET.<全局url的md5>.d41d8cd98f00b204e9800998ecf8427e.<时间区域>") + ".djcache" - HEAD方式:
md5("<prefix>:version:views.decorators.cache.cache_page.<prefix>.HEAD.<全局url的md5>.d41d8cd98f00b204e9800998ecf8427e.<时间区域>") + ".djcache"
利用场景
-
多服务共享临时目录:
- Django默认缓存权限700
- 但若nginx/apache运行多个服务,其他服务可能有权写入
-
错误配置缓存位置:
- 将CACHE_LOCATION配置在MEDIA_ROOT子目录
- 通过文件上传覆盖缓存文件
- 注意:FileField不会覆盖已有文件,但可上传HEAD类型缓存
-
敏感信息泄露:
- 过期缓存未被删除
- 可能泄露之前视图的响应内容
漏洞复现步骤
- 确定缓存位置(检查settings.py中的CACHE_LOCATION)
- 根据URL计算缓存文件名
- 构造恶意pickle序列化数据写入缓存文件
- 访问对应URL触发反序列化
缓存内容读取示例代码
import pickle
import zlib
import sys
def readcachecontent(filename):
f = open(filename, "rb")
pickle.load(f)
previous_value = pickle.loads(zlib.decompress(f.read()))
f.close()
print("Content:")
print(previous_value.content)
if __name__ == '__main__':
filename = sys.argv[1]
readcachecontent(filename)
0x03 防御措施
针对CVE-2014-0472
- 升级到已修复版本
- 避免直接使用用户输入作为
redirect或reverse参数 - 对传入参数进行严格过滤
针对FileBasedCache RCE
-
官方修复:
- 在manage.py的check方法中增加对MEDIA_ROOT、STATIC_ROOT和STATICFILES_DIRS的检查
- 警告CACHE_LOCATION不应位于这三个路径下
- 补丁地址:https://github.com/django/django/commit/c36075ac1dddfa986340b1a5e15fe48833322372
-
配置建议:
- 使用
key_prefix参数混淆缓存文件名:@cache_page(60 * 60, key_prefix="obfuscating_cache") - 设置严格的缓存目录权限
- 避免使用默认缓存位置
- 使用
-
替代方案:
- 考虑使用其他缓存后端如Memcached或Redis
- 定期清理过期缓存文件
0x04 总结
- Django虽然安全性高,但配置不当仍可能导致严重漏洞
- 历史漏洞CVE-2014-0472展示了URL反向解析的风险
- FileBasedCache的pickle反序列化是新型RCE利用点
- 防御关键在于严格过滤输入和合理配置缓存
- 遵循最小权限原则和安全配置指南可有效降低风险