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 수준이라 임계값을 넘지 못한다.

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 = 0for 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 += 1breed 는 픽셀 단위로 두 부모 값을 무작위 선택해 합성하고, 변이 확률에 맞춰 mutate 를 한 번 더 적용한다.
결과
variations/variant_*.png로 초기 개체군 생성.- 각 세대마다
attack()으로 similarity를 받아 정렬. - 6세대에서 0.812를 확인했고, 그 이미지를 재전송해 성공 응답과 플래그를 받았다.

Flag
fiesta{b2d39b22e51521403f8c04d353253636621f47a170df9bd6781231670ef81a3c}