GitHub - lwzSoviet/CVE-2021-3281
django.utils.archive.py의 TarArchive 클래스, 171번 라인에서 발생하는 Path Traversal 취약성이다.
이 함수는 os.path.join(to_path, name)를 사용하며, 'name' 파라미터에 대한 검증을 실시하지 않는다.
사용자가 이 함수를 윈도우 환경에서 사용할 경우 Directory Traversal 취약성이 발생할 수 있다.
POC는 아래와 같다.
from django.utils import archive
archive.extract('test.tar','.')
# The test.tar include file named "d:game.exe",and the poc will create a file named "game.exe" in D://game.exe rather than "."
# It looks like the Django core didn't use this util,but I still think it's a risk,maybe someone will use this util in webapp to archive somethings.``and there is another scene:``"djangoadmin startapp --template" command will use archive.py,see in https://docs.djangoproject.com/en/3.1/ref/django-admin/#s-startapp. POC is:
django-admin.exe startapp vulapp --template="C:/my_templates/test.tar"
It'll create a file named "game.exe" in D://game.exe rather than "vulapp/", It also accept URLs like "django-admin.exe startapp vulapp --template=https://xxx.com/evil.tar"
환경 구축 & 취약성 재현
Python 설치(Windows)
- 취약환경을 그대로 구현하기 위해 취약한 버전의 장고를 설치해야한다.버전 관리를 위해 pyenv+virtualenv를 이용한다.(https://github.com/pyenv-win/pyenv-win)
- 취약성 발생 환경이 윈도우 환경이므로 윈도우 운영체제에서 진행한다.
pyenv update
pyenv install -l
pip install virtualenv
cd py368/Scripts > activate
virtualenv --python=C:\\Users\\ADMIN\\.pyenv\\pyenv-win\\versions\\3.6.8\\python.exe py368
# 가상환경 이름pyenv install 3.6.8
Django 설치(3.1.5)
- Django 3.1.6버전에서 패치된 취약성이므로 하위버전인 3.1.5버전 설치
- 이렇게 하면 python 가상 환경 내에서만 동작하는 장고 환경을 구축할 수 있다.
pip install Django==3.1.5
취약성 재현
- tar 파일을 언아카이빙(압축해제)할 때 from django.utils import archive를 통해 모듈을 로드한 후 archive.extract('압축해제할 파일', '압축해제할 경로')의 형태로 명령을 실행한다.
- 공격자는 '압축해제할 파일'의 test.tar 파일 내부에 다른 경로로 이동하도록 하는 파일 명을 삽입(d:game.exeee)하여 다른 경로에 압축이 해제되도록 한다.
test.tar파일의 구조는 다음과 같다.
3. PoC대로 명령을 실행시켜 취약성이 동일하게 발생하는지 확인한다.
from django.utils import archive
archive.extract('test.tar', '.')
실행 전,후 D 드라이브의 파일 내용이다.
명령실행 후 D:\ 경로에 game.exeee 파일이 생성된 것을 확인할 수 있다.
원인 분석
https://github.com/django/django/commit/05413afa8c18cdb978fcdf470e09f7a12b234a23
취약성이 발생하는 위치인 django/utils/archive.py 파일의 TarArchive클래스 코드라인을 분석한다.
class TarArchive(BaseArchive):
def __init__(self, file):
self._archive = tarfile.open(file)
def list(self, *args, **kwargs):
self._archive.list(*args, **kwargs)
def extract(self, to_path):
members = self._archive.getmembers()
leading = self.has_leading_dir(x.name for x in members)
for member in members:
name = member.name
if leading:
name = self.split_leading_dir(name)[1]
filename = os.path.join(to_path, name) # 취약성 발생 위치
if member.isdir():
if filename:
os.makedirs(filename, exist_ok=True)
...
- os.path모듈을 사용하여 filename을 정의하고 있다.
- join 함수는 경로명 조작을 처리하고 있는 함수이며 인수에 전달된 2개의 문자열을 조합하여 1개의 경로로 정의할 수 있다.
-
import os os.path.join("/A/B/C", "file.py") # /A/B/C/file.py
하지만 결합하려는 경로명에 디렉터리 구분 문자("\") 등이 포함되어 있는 경우 다음과 같이 동작한다.
import os
os.path.join('dirA', 'dirB', '\dirC') # \dirC
os.path.join('dirA', '\dirB', '\dirC') # \dirB\dirC
os.path.join('\dirA', 'dirB', 'dirC') # \dirA\dirB\dirC
join() 의 인자에 디렉터리 구분 문자가 있으면, 그것을 root로 보는 성질이 있다.
Django에서는 이 함수를 tar 파일 내 여러 파일들을 상위 경로와 연결하여 압축해제하기 위한 로직에 사용하고 있다.
filename = os.path.join(to_path, name) # 취약성 발생 위치
test.tar의 입력 시 다음과 같이 동작한다.
import os
os.path.join('ccc', 'D:\game.exeee') # D:\game.exeee
이 취약성은 파이썬 TarFile 모듈의 extractall 함수에도 동일하게 발생한다.
tarfile - Read and write tar archive files - Python 3.9.6 documentation
보안조치
Django에서는 취약성의 해결을 위해 파일명 인자를 검증하는 함수를 추가하였다.
def target_filename(self, to_path, name):
target_path = os.path.abspath(to_path)
filename = os.path.abspath(os.path.join(target_path, name))
if not filename.startswith(target_path):
raise SuspiciousOperation("Archive contains invalid path: '%s'" % name)
return filename
...
filename = self.target_filename(to_path, name)
'Security > WEB' 카테고리의 다른 글
[웹해킹] SQL Injection 총정리 (2) | 2022.08.05 |
---|---|
[Pentest] 배너 그래빙(Banner Grabbing) 도구 (0) | 2022.03.28 |
[모의해킹 실습] 데이터 평문전송 취약 페이지 구현 및 공격 실습 (0) | 2021.08.06 |
[모의해킹 실습] 정보누출 취약 페이지 구현 및 공격 실습 (0) | 2021.08.02 |
[Angular]설치 및 시작하기 (0) | 2021.07.31 |