오늘은 취약점이 존재하는 image_processor.py 코드 중 모르는 문법을 찾아보았다
본 게시물은 모르는 문법을 찾아 코드 해석을 가능하게 한 후, 다음 게시물에서 취약점에 관한 내용을 다룰 것이다
import os
import uuid
from PIL import Image, ImageFilter, ImageOps, ImageMath, ImageEnhance
from app.utils import get_processed_images
import (모듈 가져오기)
- 모듈
- 함수나 변수 또는 클래스를 모아 놓은 파이썬 파일
PIL(Pillow)
- 이미지 처리 모듈
app.utils
- app 패키지 안에 있는 utils 모듈 기능들을 사용함
- app. 패키지명
- 프로젝트의 메인 애플리케이션 코드를 담고 있는 최상위 패키지 또는 디렉터리
- utils
- 해당 패키지 내의 유용한 함수들을 모아놓은 모듈(또는 디렉터리)
def apply_transform(transform_name, filenames, user_uuid, options=None):
"""Apply a specific transformation to images and return PIL Image objects."""
images = get_processed_images(filenames, user_uuid)
if not images:
return []
if options is None:
options = {}
- 처리된 이미지 받아오기
#rotate(회전)
if transform_name == 'rotate':
angle = options.get('angle', 90)
return [img.rotate(angle, expand=True) for img in images]
options.get(’angle’, 90)
- options.get(key, default_value)
- 딕셔너리 키 값을 이용해 값을 가져오고 만약 키가 없을 경우, 기본값을 반환
return [img.rotate(angle, expand=True) for img in images]
- img.rotate(각도, expand)
- 이미지 객체를 회전시킴
- expand = True → 회전된 이미지가 잘리지 않고 전체가 보이도록 이미지 크기를 조절
- expand = False → 원본 이미지 크기를 유지, 회전하는 동안 이미지의 일부가 잘릴 수 있음
#composite(합성)
elif transform_name == 'composite':
if len(images) != 2:
raise ValueError("이미지 합성은 반드시 2개의 이미지를 선택해야 합니다.")
img1 = images[0]
img2 = images[1].resize(img1.size)
return [Image.blend(img1, img2, alpha=0.5)]
- composite(합성)을 선택할 때 이미지 개수 확인
img2=images [1]. resize(img1.size)
- img2를 img1의 사이즈만큼 이미지 크기 변경
image.blend(img1, img2, alpha=0.5)
- Pillow 라이브러리에서 두 이미지를 알파 값을 사용해 선형 보간을 통해 섞는 합수
- 쉽게 말해 알파 값에 따라 혼합되는 비율이 달라짐
- 0일 경우, img1이 완전히 보이고 1일 경우, img2가 완전히 보임
- 0.5일 경우, 두 이미지가 일정 비율로 섞임
#append(추가)
elif transform_name == 'append':
num_images = len(images)
if num_images == 0:
raise ValueError("이미지를 선택해주세요.")
elif num_images > 4:
raise ValueError("이미지 조합은 최대 4장까지만 지원합니다.")
- append(추가)를 선택했을 경우, 이미지 개수 확인
if num_images == 1:
return images
elif num_images <= 3:
min_width = min(img.width for img in images)
resized_images = [img.resize((min_width, int(img.height * min_width / img.width))) for img in images]
total_height = sum(img.height for img in resized_images)
dst = Image.new('RGB', (min_width, total_height))
min(img.width for img in images)
- images 안에 있는 img 중 넓이가 최소인 img의 값을 반환
- min(x)
- 인수로 받은 자료형 내에서 최솟값을 찾아서 반환하는 함수
resized_images = [img.resize((min_width, int(img.height * min_width / img.width))) for img in images]
- 넓이는 최소 넓이로 맞추고 높이는 비율을 계산하여 이미지 크기 변경
dst=img.new(’RGB’, (min_width, total_height))
- 이 이미지는 RGB 방식으로 표현되며 넓이는 최소 넓이로 맞추고, 높이는 전체 사진의 합
- image.new(mode, size, color=0)
- : Pillow 라이브러리에서 새로 이미지를 생성하는 함수
y_offset = 0
for img in resized_images:
dst.paste(img, (0, y_offset))
y_offset += img.height
return [dst]
dst.paste(img, (0, y_offset))
- 0, y_offset 위치에 img를 dst에 붙여 넣기
- paste(img, (x, y))
- img : 붙여 넣을 이미지 객체
- x : 왼쪽 상단 모서리의 가로 위치
- y : 세로 위치
elif num_images == 4:
min_size = min(img.width for img in images), min(img.height for img in images)
resized_images = [img.resize(min_size) for img in images]
dst = Image.new('RGB', (min_size[0] * 2, min_size[1] * 2))
dst.paste(resized_images[0], (0, 0))
dst.paste(resized_images[1], (min_size[0], 0))
dst.paste(resized_images[2], (0, min_size[1]))
dst.paste(resized_images[3], (min_size[0], min_size[1]))
return [dst]
- 이미지가 4개일 때 처리 방법
- min_size [0] == min(img.width for img in images)
- min_size [1] == min(img.height for img in images)
최종 사진 위치) 3번째 사진 4번째 사진
1번째 사진 2번째 사진
elif transform_name == 'contour':
return [img.filter(ImageFilter.CONTOUR) for img in images]
elif transform_name == 'solarize':
threshold = options.get('threshold', 128)
return [ImageOps.solarize(img, threshold=threshold) for img in images]
return [img.filter(ImageFilter.CONTOUR) form img in images]
- contour (윤곽)을 이미지에 적용
- img.filter()
- Pillow 라이브러리의 Image 객체에 다양한 필터를 적용하는 데 사용
threshold = options.get(’threshold’,128)
- threshold(임계값)을 키값을 이용해 가져오고 없다면 기본 값이 128로 설정
- options.get
- 특정 기능을 수행하는 다른 객체에서 키를 사용하여 값을 가져옴
return [ImageOps.solarize(img, threshold=threshold) for img in images]
- threshold(=128)을 가져와 임계값 이상일 경우 픽셀 반전
- ImageOps.solarize(img, threshold)
- 특정 임계값(threshold) 이상일 경우, 이미지의 픽셀 값을 반전시킴(solarize)
픽셀 반전
: 픽셀의 색상 값을 최댓값에서 뺀 값으로 변환 (최대 픽셀 값 - 현재 픽셀 값)
elif transform_name == 'brightness':
factor = options.get('factor', 1.5)
return [ImageEnhance.Brightness(img).enhance(factor) for img in images]
elif transform_name == 'grayscale':
return [img.convert('L') for img in images]
elif transform_name == ‘brightness’
- brightness(명도)를 이미지에 적용
factor = option.get(’factor’, 1.5)
- 키 값을 이용하여 factor값을 가져오고 만약 없다면 기본 값인 1.5로 설정
return [ImageEnhance.Brightness(img). enhance(factor) for img in images]
- 이미지의 밝기를 1.5배 증가 시킴
- ImageEnhance.Brightness(image)
- Pillow 라이브러리에서 이미지의 밝기를 조절하는 데 사용
return [img.convert(’L’) for img in images]
- img.convert()
- 이미지의 색상 모드를 변화할 때 사용
- img.convert(’L’)
- 이미지를 흑백이미지(grayscale)로 변환
elif transform_name == 'sepia':
sepia_matrix = [
0.393, 0.769, 0.189, 0,
0.349, 0.686, 0.168, 0,
0.272, 0.534, 0.131, 0
]
return [img.convert("RGB", sepia_matrix) for img in images]
return [img.convert(”RGB”, sepia_matrix) for img in images]
- 이미지에 세피아 톤 적용
elif transform_name == 'custom_formula':
exp = options.get('expression')
if not exp:
raise ValueError("Custom formula requires an 'expression'.")
env = { fname: img for fname, img in zip(filenames, images) }
try:
result = ImageMath.eval(exp, env)
return [result]
except Exception as e:
return [None]
else:
raise ValueError(f"Unknown transformation: {transform_name}")
env = { fname : img for fname, img in zip(filenames, images) }
- filenames와 images에서 차례대로 한 개씩 짝지어 fname:img 형태의 딕셔너리를 만듦
- zip()
- 두 그룹의 데이터를 서로 엮어 튜플 형태로 만듦
///ex)
number = [1,2,3]
letters = ["A", "B", "C"]
for pair in zip(numbers, letters):
print(pair)
......
(1, 'A')
(2, 'B')
(3, 'C')
result = ImageMath.eval(exp, env)
- ImageMath.eval
- 이미지에 대한 수학적 표현식을 문자열 형태로 입력받아 계산, 그 결과를 이미지나 숫자로 반환
///ex)
#이미지 픽셀 값을 2배로 늘리는 연산
result_img=imageMath.eval("a * 2", a=img)'CTF > 2025 CCE Photo Editing' 카테고리의 다른 글
| [CCE] utils.py (0) | 2025.09.10 |
|---|