Logo
Overview
[FIESTA 2025] Model To Deep writeup

[FIESTA 2025] Model To Deep writeup

September 29, 2025
1 min read

TL;DR

  • http://54.180.253.212/verify-face 는 cosine similarity 0.8 이상일 때만 통과.
  • 제공 자료는 baseline 얼굴 이미지 한 장과 API 엔드포인트뿐.
  • 밝기·대비·색감·회전·스케일을 살짝 변형시키는 GA로 6세대 만에 0.812 점수를 만들었다.

Analysis

서비스 요약

  • 입력: PNG 업로드 → 응답: {"similarity": float, "success": bool}.
  • 인증 토큰/세션 없음, 반복 요청 제한도 없다.
  • 제공된 source.png 는 similarity 0.71 수준이라 임계값을 넘지 못한다.

verify-face response

Exploit

GA 설정값

  • 세대 10, 개체군 20, 부모 5, 엘리트 2, 변이 확률 0.3, 목표 0.8.

변이 함수

def mutate(img):
adj = lambda enh: enh.enhance(random.uniform(0.9, 1.1))
img = adj(ImageEnhance.Brightness(img))
img = adj(ImageEnhance.Contrast(img))
img = adj(ImageEnhance.Color(img))
img = img.rotate(random.uniform(-5, 5), resample=Image.BICUBIC)
w, h = img.size
scale = random.uniform(0.95, 1.05)
resized = img.resize((int(w * scale), int(h * scale)), Image.BICUBIC)
return ImageOps.fit(resized, (w, h), method=Image.BICUBIC, centering=(0.5, 0.5))

세대 갱신

parents = [r["image"] for r in results[:NUM_PARENTS]]
next_idx = 0
for keep in parents[:ELITE_COUNT]:
shutil.copy(keep, new_dir / f"variant_{next_idx}.png")
next_idx += 1
while next_idx < POPULATION_SIZE:
p1, p2 = random.sample(parents, 2)
breed(p1, p2, new_dir / f"variant_{next_idx}.png")
next_idx += 1

breed 는 픽셀 단위로 두 부모 값을 무작위 선택해 합성하고, 변이 확률에 맞춰 mutate 를 한 번 더 적용한다.

결과

  1. variations/variant_*.png 로 초기 개체군 생성.
  2. 각 세대마다 attack() 으로 similarity를 받아 정렬.
  3. 6세대에서 0.812를 확인했고, 그 이미지를 재전송해 성공 응답과 플래그를 받았다.

high-score variant

Flag

fiesta{b2d39b22e51521403f8c04d353253636621f47a170df9bd6781231670ef81a3c}