검사 코드 찾기
제공 파일은 15 KB 실행 파일입니다. 플래그를 입력받고 Correct! 또는 Incorrect!을 출력합니다. 에이전트에게 간단한 요청을 해보세요.
REA로 ms_flag_checker를 분석하고 플래그를 찾으세요. 검사 동작을 설명한 뒤 원래 프로그램으로 답을 검증하세요.
문제 파일을 로컬에 다운로드하고 네이티브 분석 도구를 연결한 상태에서 사용하는 예제 프롬프트입니다.
-
입력 프롬프트에서 해당 함수로 추적
에이전트 → REAopen_binary {"path": "ms_flag_checker"} search_strings {"pattern": "flag|correct|wrong|input", "mode": "regex", "case_sensitive": false} xrefs {"address": "0x102004"} resolve_containing_procedure {"address": "0x10126d"}REA → 에이전트 · 결과 발췌0x102004 "What is the flag? " 0x10201c "Incorrect!" 0x102027 "Correct!" Prompt referenced at: 0x10126d Containing function: 0x10124d원래 함수 이름이 제거된 실행 파일에서도 이 참조를 통해 조사를 시작할 위치를 찾을 수 있습니다.
-
검사기와 보조 함수 두 개 읽기
에이전트 → REAanalyze_function {"procedure": "0x10124d"} analyze_function {"procedure": "0x101189"} analyze_function {"procedure": "0x101217"}REA → 에이전트 · 선택한 명령어0x1012c1: CMP EDI, 0x24 0x1012c6: LEA RBX, [0x1040e0] 0x1012cd: LEA RBP, [0x104060] 0x1012d4: LEA R13, [RBX + 0x270] 0x1012e1: CALL 0x00101189 0x1012ec: CALL 0x00101217 0x1012f1: CMP dword ptr [RBP], EAX 0x1012f4: JNZ 0x00101335 0x1012f6: ADD RBX, 0x18첫 루프는 문자 코드 36개를 복사합니다. 검사 루프는 마스크를 해석해 선택한 코드를 더하고 저장된 수와 비교합니다.
0x270바이트 범위를0x18바이트씩 이동하므로 마스크는 26개입니다.
새로 수행한 REA 분석에서 일부 결과를 골랐습니다. 로컬 대상 경로는 표시용으로 줄였고, 주소는 REA가 가져온 이미지를 기준으로 합니다.
하나의 검사로 문자 하나를 알아냅니다
검사기는 입력을 6×6 격자로 배치합니다. 마스크는 합산할 칸을 선택합니다. 압축된 인코딩에서 음수는 칸 건너뛰기, 양수는 칸 선택을 뜻합니다.
바이너리에서 마스크와 목표값 읽기
{"address": "0x1040e0", "length": 624}
{"address": "0x104060", "length": 144}
Mask at 0x104170:
eb 01 f2 00 → -21, +1, -14, stop
Target at 0x104078:
37 00 00 00 → 55
명령어 MOVSX은 마스크 바이트를 부호 있는 값으로 읽습니다. 그래서 eb은 −21을 뜻합니다. 목표값은 리틀 엔디언 정수입니다. 이 바이트들로 code[21] = 55임을 알 수 있으므로 해당 문자는 7입니다.
명령어에서 읽기 쉬운 검사 코드로
단계를 선택해 합산 보조 함수와 호출 함수가 C 코드의 같은 규칙에 어떻게 대응하는지 살펴보세요.
0x10121c: MOV ECX, 0x0
0x10122c: CMP dword ptr [RSI + RAX*0x1], 0x0
0x101230: JZ 0x00101223
0x101232: ADD ECX, dword ptr [RDI + RAX*0x1]
0x101223: ADD RAX, 0x4
0x1012f1: CMP dword ptr [RBP], EAX
0x1012f4: JNZ 0x00101335
int check_one_mask(const int codes[36],
const int mask[36],
int target) {
int total = 0;
for (int i = 0; i < 36; ++i) {
if (mask[i]) {
total += codes[i];
}
}
return total == target;
}
01 · 0에서 시작합니다. ECX에는 누적 합이 저장됩니다.
02 · 마스크 셀을 확인합니다. 0 값은 문자를 건너뛰고 0 이 아닌 값은 문자를 포함합니다.
03 · 문자 코드를 추가합니다. 같은 오프셋에 있는 입력의 정수가 합에 더해집니다.
04 · 다음 셀로 이동합니다. 원래 루프는 정수당 4 바이트를 앞당기고 6 개의 셀의 6 개의 행을 방문합니다.
05 · 저장된 목표값과 비교합니다. 호출 함수는 EAX로 합을 전달받습니다. 값이 일치하지 않으면 Incorrect!로 분기합니다.
명령어는 합산 보조 함수와 호출 함수에서 가져왔습니다. C 코드는 설명용 요약으로, 의미 있는 이름과 36개 칸을 한 번에 순회하는 루프를 사용했습니다.
방정식 26개 풀기
대부분의 마스크는 여러 칸을 선택합니다. 첫 검사는 문자 코드 16개를 더해 합이 1441인지 확인합니다. 선택 영역들이 겹치므로 작은 Python 스크립트로 함께 풀 수 있는 연립방정식이 됩니다.
from z3 import Int, Or, Solver, Sum, sat
codes = [Int(f"code_{i}") for i in range(36)]
solver = Solver()
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}"
for code in codes:
solver.add(Or(*[code == ord(c) for c in alphabet]))
for code, char in zip(codes, "DUCTF{"):
solver.add(code == ord(char))
solver.add(codes[-1] == ord("}"))
for selected, target in checks:
solver.add(Sum([codes[i] for i in selected]) == target)
assert solver.check() == sat
model = solver.model()
print("".join(chr(model.eval(code).as_long()) for code in codes))
Z3는 이 방정식들을 만족하는 값을 찾는 솔버입니다. 발췌 코드는 추출한 검사를 사용하며, 영문자·숫자·밑줄·중괄호로 이루어진 DUCTF{…} 플래그를 가정합니다. 전체 다운로드에는 마스크 26개와 목표값이 모두 들어 있습니다.
원본 프로그램으로 정답 검증
REA의 프로세스 캡처로 원본 실행 파일이 구한 플래그를 받아들이는 것을 기록했습니다. 선택된 문자 하나를 z에서 y로 바꾸면 첫 합계가 1441에서 1440이 되어 프로그램이 거부합니다.
| 입력 | 관찰된 출력 | 종료 코드 |
|---|---|---|
| 솔버가 구한 플래그 | Correct! |
0 |
| 한 문자 변경 | Incorrect! |
255 |
복구된 플래그 및 캡처된 출력 표시
What is the flag? DUCTF{ezzzpzzz_07bcda7bfe81faf43caa}
Correct!
복구한 값은 주최 측이 공개한 문제 메타데이터의 플래그와도 일치합니다. 이 대조는 바이너리를 푼 뒤에 수행했습니다.
출처, 대상 식별 정보, 분석 세부 사항
2026년 10월 8일 Linux x64에서 REA 4.1.0과 Ghidra 12.1.4를 사용해 새로 분석하고 프로세스 실행을 두 차례 캡처했습니다. 바이너리는 심볼 정보가 제거된 x86-64 ELF이며, 크기는 15,248바이트입니다.
dcd3bec4f608e11f3a12ce461100aaf5f621851ee63a1f870d7c90d4e3dd51ae
주최 측의 소스나 풀이를 읽기 전에 문자열 참조, 함수, 상수를 분석했습니다. Python 풀이 코드와 그림은 이 REA 결과로 만들었습니다.
직접 해보기
원본 문제와 풀이 코드를 한 폴더에 다운로드하세요. 위 프롬프트로 에이전트와 함께 조사하거나, 제공된 스크립트를 실행하세요.
python3 -m venv .venv
.venv/bin/python -m pip install z3-solver
.venv/bin/python solve.py
chmod +x ms_flag_checker
./ms_flag_checker
스크립트가 출력하는 후보 플래그를 프로그램 프롬프트에 붙여넣으세요. 풀이에는 Python과 z3-solver가 필요합니다. 원본 실행 파일은 Linux x86-64에서 실행해야 합니다.