Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2021-35042 — CVE-2021-35942에 대한 기본 분석. Django의 SQL 인젝션. | Kitploit
도구/GitHubGitHub/zer0qs/cve-2021-35042
Vulnerability AnalysisExploitationWeb SecurityPapers & ResearchLearning & Education
GitHubzer0qs/cve-2021-35042

CVE-2021-35042

CVE-2021-35942에 대한 기본 분석. Django의 SQL 인젝션.

저장소 보기
24년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2021-35042: Django SQL 인젝션 취약점

I. 개요

Django는 Python으로 작성된 오픈소스 웹 애플리케이션 프레임워크로, MVC(Model - View - Controller) 모델을 기반으로 구축되었습니다. 원래는 Lawrence 출판 그룹이 소유한 뉴스 콘텐츠 웹사이트를 관리하기 위해 구축되었으며, CMS(Content Management System) 소프트웨어입니다.

Django 버전 3.1.x ~ 3.1.13 및 버전 3.2.x ~ 3.2.5에는 SQL 인젝션 취약점이 존재합니다.

이 취약점의 원인은 QuerySet.order_by()에서 사용자가 제어하는 입력 데이터를 필터링하는 기능이 SQL 인젝션 공격을 방지하기에 충분하지 않기 때문입니다. 이 취약점은 공격자가 민감한 데이터 유출로 이어지는 무단 작업을 수행할 수 있도록 악용될 수 있습니다.

CVE - IDCVE-2021-35042
심각도9.8 - CRITICAL
CWE - IDCWE-89: SQL 명령에 사용되는 특수 요소의 부적절한 중화('SQL 인젝션')
취약점 공개일2021년 7월 1일
영향받는 소프트웨어3.1.x < 3.1.13, 3.2.x < 3.2.5
인증 필요필요 없음

II. 개요 x2

0x01. Django의 Model

Django에서 데이터베이스 테이블 생성 및 필드 정의는 models.py 파일에서 모델 클래스를 선언하여 수행됩니다. 이 예제에서는 Wolf라는 이름의 테이블과 name이라는 필드를 선언합니다.

0x02. Django의 QuerySet 및 Order_by()

Django에 내장된 ORM 프레임워크는 데이터베이스와 상호작용하는 데 사용되며, 쿼리 결과는 집합이며 이 집합을 QuerySet이라고 합니다.

order_by(fields) 기본적으로 order_by()는 Model의 Meta에 있는 ordering 옵션에 지정된 순서로 정렬된 QuerySet을 반환합니다. 각 쿼리에서 order_by() 메서드를 사용하여 order_by 조건을 재정의할 수 있습니다.

예시

wolves = Wolf.objects.order_by('-name', 'id')

위 쿼리의 결과는 name 필드를 기준으로 내림차순으로 정렬된 다음 id를 기준으로 오름차순으로 정렬됩니다. name 필드 이름 앞의 음수 부호는 결과가 내림차순으로 정렬됨을 나타냅니다.

다음 예시는 사용자로부터 받은 필드를 기준으로 반환 결과를 정렬하며, 전달된 값이 없으면 id 필드를 기준으로 정렬합니다.

결과

버전 3.1 및 3.2에서 Django는 쿼리 메서드와 테이블 이름을 order_by 쿼리에 결합할 수 있도록 허용합니다. 이것이 이 취약점의 주요 원인입니다.

테이블 이름을 전달하면 일반적인 필드 이름을 전달하는 것과 동일한 결과를 얻을 수 있습니다.

cve202135042_wolf는 테이블 이름입니다.

먼저 애플리케이션은 order_by() 함수를 직접 호출하며, order_by() 함수를 처리하는 코드는 다음 위치에 정의되어 있습니다: django/db/models/query.py

order_by() 함수는 두 가지 작업을 수행합니다.

  1. order_by()에 의해 현재 호출되는 모든 메서드를 삭제하고 order_by가 다른 값을 받을 때 전달되는 기본 매개변수를 삭제합니다.
  1. 매개변수를 order_by에 전달합니다. add_ordering() 함수가 이 작업을 수행합니다.
root@kitploit:~
def add_ordering(self, *ordering):
        """
        Add items from the 'ordering' sequence to the query's "order by"
        clause. These items are either field names (not column names) --
        possibly with a direction prefix ('-' or '?') -- or OrderBy
        expressions.

        If 'ordering' is empty, clear all ordering from the query.
        """
        errors = []
        for item in ordering:
            if isinstance(item, str):
                if '.' in item:
                    warnings.warn(
                        'Passing column raw column aliases to order_by() is '
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().' % item,
                        category=RemovedInDjango40Warning,
                        stacklevel=3,
                    )
                    continue
                if item == '?':
                    continue
                if item.startswith('-'):
                    item = item[1:]
                if item in self.annotations:
                    continue
                if self.extra and item in self.extra:
                    continue
                # names_to_path() validates the lookup. A descriptive
                # FieldError will be raise if it's not.
                self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
            elif not hasattr(item, 'resolve_expression'):
                errors.append(item)
            if getattr(item, 'contains_aggregate', False):
                raise FieldError(
                    'Using an aggregate in order_by() without also including '
                    'it in annotate() is not allowed: %s' % item
                )
        if errors:
            raise FieldError('Invalid order_by arguments: %s' % errors)
        if ordering:
            self.order_by += ordering
        else:
            self.default_ordering = False
            

add_ordering()에 전달되는 매개변수는 배열입니다.

예를 들어 매개변수가 다음과 같이 전달되는 경우: wolves = Wolf.objects.order_by( 'name' , 'id' ) 그러면 애플리케이션은 데이터베이스에서 다음과 같은 쿼리로 변환합니다:

root@kitploit:~
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC

전달되면 add_ordering 함수는 배열의 각 요소를 검사하며, string인 경우 다음 5가지 경우를 검사합니다:

  1. if '.' in item: SQL 문에 지정된 테이블 이름이 있는 열 이름을 가진 쿼리인지 확인합니다. 그렇다면 경고를 표시하고 continue합니다.
  2. if item == '?': 요소 값이 '?' 기호인 경우 출력 결과는 무작위로 정렬되며 continue합니다.
  3. if item.startswith('-'): 항목이 '-' 문자로 시작하면 쿼리 결과는 DESC(내림차순)로 정렬됩니다.
  4. if item in self.annotations: 주석이 포함되어 있는지 확인하고, 포함되어 있으면 continue합니다.
  5. if self.extra and item in self.extra: 추가 항목이 있는지 확인하고 있으면 continue합니다.

5번의 검사 후, 매개변수는 self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) 함수에 계속 전달되어 유효한 열 이름인지 계속 확인한 다음, 유효하면 Query 클래스의 self.ordering에 추가되어 계속 처리됩니다.

III. Django의 SQL 인젝션 취약점 분석

0x31. 원인

Django의 ORM은 쿼리에 입력되는 데이터를 매우 엄격하게 필터링하지만, 이번 소스 코드 변경으로 인한 SQL 인젝션은 작성자가 열 이름이 UUID(Universal Unique Identifier) 열인 경우 order_by 쿼리를 수행할 수 없다는 가정을 세웠기 때문입니다.

즉, 입력 데이터가 xxx-xxx-xxx-xxx(UUID 형식)인 경우 쿼리를 수행할 수 없습니다.

변경 전 코드

root@kitploit:~
# django/db/models/sql/constants.py 
ORDER_PATTERN  =  _lazy_re_compile ( r '\?|[-+]?[.\w]+$' )

# django/db/models/sql/query.py 
def  add_ordering ( self ,  * ordering ): 
        errors  =  [] 
        for  item  in  ordering : 
            if  isinstance ( item ,  str )  and  ORDER_PATTERN . match ( item ): 
                if  '.'  in  item : 
                    warnings . warn ( 
                        'Passing column raw column aliases to order_by() is ' 
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().'  %  item , 
                        category = RemovedInDjango40Warning , 
                        stacklevel = 3 , 
                    ) 
            elif  not  hasattr ( item ,  'resolve_expression' ): 
                errors . append ( item ) 
            if  getattr ( item ,  'contains_aggregate' ,  False ): 
                raise  FieldError ( 
                    'Using an aggregate in order_by() without also including ' 
                    'it in annotate() is not allowed: %s ' %  item 
                ) 
        if  errors : 
            raise  FieldError ( 'Invalid order_by arguments: %s '  %  errors ) 
        if  ordering : 
            self . order_by  +=  ordering 
        else : 
            self . default_ordering  =  False

위 코드에서 매개변수가 ?와 일치하거나 -로 시작하고 그 뒤에 일반 문자나 . 기호가 오는 경우에만 쿼리가 실행된다는 것을 알 수 있습니다.

따라서 열 이름이 UUID인 경우 유효하지 않은 값이 되어 order_by에 포함될 수 없습니다. 이 처리 부분의 코드 변경이 승인되었으며 다음과 같이 변경되었습니다: https://github.com/charettes/django/commit/513948735b799239f3ef8c89397592445e1a0cd5

self.name_to_path 함수를 사용하여 입력 데이터를 검증했습니다.

하지만 항목에 .이 있는지 확인한 후 테이블 이름이 있는 쿼리로 간주하고 continue 명령이 실행되어 self.name_to_path 함수를 사용한 데이터 유효성 검사를 직접 건너뛰게 됩니다.

get_order_by 함수에서 .을 처리하는 코드는 다음과 같습니다. django/db/models/sql/compiler.py

root@kitploit:~
if  '.'  in  field : 
    table ,  col  =  col . split ( '.' ,  1 ) 
    order_by . append (( 
            OrderBy ( 
                RawSQL ( ' %s . %s '  %  ( 
                self . quote_name_unless_alias ( table ),  col ),  [ ]), 
                descending = descending 
            ),  False )) 
    continue

self.quote_name_unless_alias 함수는 테이블 이름을 처리하고 유효한 테이블 이름을 필터링하며 열 이름 필터링을 건너뛰므로 SQL 인젝션 문을 삽입할 수 있습니다.

0x32. 패치

현재 Django 4.0 버전에서는 .을 사용한 테이블 이름 쿼리가 제거되었으며 더 이상 지원되지 않습니다. 패치는 3.1 및 3.2 버전에 제공되었습니다. 3.2 ~ 3.2.4 및 3.1 ~ 3.1.12 버전이 영향을 받습니다. 3.2.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

3.1.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

수정은 매우 간단하며, 이전 ReGex를 사용한 데이터 검사가 다시 도입되었습니다.

0x33. 해결 방법

Django를 영향을 받지 않는 버전으로 업데이트하세요.

IV. 데모

0x41. 환경

Docker & Docker-compose

0x42. 설정

  1. git clone https://github.com/WynSon/CVE-2021-35042.git
  2. 초기 설정을 위해 ./setup.sh 실행
  3. sudo docker-compose up --build
  4. sudo docker exec -it cve-2021-35042_web_1 python manage.py makemigrations cve202135042
  5. sudo docker exec -it cve-2021-35042_web_1 python manage.py migrate
  6. 샘플 데이터를 로드하려면 http://localhost:8000/load_example_data에 접속하세요:
  7. 취약한 매개변수가 포함된 경로: http://localhost:8000/wolves/ http://localhost:8000/wolves/?order_by=name

설치 완료 후 화면

0x43. 악용

조건: 악용하려면 어떤 방식으로든 테이블 이름을 알아야 합니다 :))

문을 주입할 때 테이블 이름을 알아야 SQLi 문을 실행할 수 있습니다.

잘못된 테이블 이름을 입력한 경우

올바른 테이블 이름을 입력하면 orderby 쿼리가 정상적으로 실행됩니다.

이때 문은 다음과 같습니다. SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name") ASC

이때 앞의 order_by 문을 종료하고 SQL 문을 삽입하여 악용할 수 있습니다.

SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name"); SELECT * from cve202135042_wolf where id =1; --) ASC

V. 참고 자료

https://www.djangoproject.com/weblog/2021/jul/01/security-releases/ https://xz.aliyun.com/t/9834 https://www.bugxss.com/vulnerability-report/3095.html https://blankheart.top/2022/04/07/cve-2021-35042/ https://itcn.blog/p/1648921763575859.html https://github.com/YouGina/CVE-2021-35042

도구 다운로드