1089 단어
5 분
[Reversing] simultaneous
문제 정보
- 출처: 정보보안 콘테스트를 위한 CTF 문제집
- 분야: Reversing
- 풀이 날짜: 2026. 06. 22
문제
- 프로그램은 실행 시 12개의 정수를 입력받는다.
- 입력된 값은 check() 함수에서 검증되며, 모든 조건을 만족하면 입력한 정수들을 ASCII 문자로 출력하여 Flag를 생성한다.
- 즉, check() 함수의 검증 로직을 분석하여 조건을 만족하는 입력값을 찾는 것이 목표이다.
분석 및 Exploit
프로그램 분석
main() 함수 분석
int __cdecl __noreturn main(int argc, const char **argv, const char **envp){ int i; // [esp+0h] [ebp-2Ch] int j; // [esp+4h] [ebp-28h] _DWORD *v5; // [esp+8h] [ebp-24h] int status; // [esp+10h] [ebp-1Ch]
v5 = calloc(4u, argc - 1); if ( argc != 13 ) { puts("Usage: ./equation_revenge x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12"); exit(-1); } for ( i = 0; i < 12; ++i ) v5[i] = atoi(argv[i + 1]); status = check(v5); printf("FLAG{"); for ( j = 0; argc - 1 > j; ++j ) putchar(v5[j]); putchar(125); exit(status);}IDA에서 main() 함수를 분석한 결과 다음과 같은 흐름을 확인하였다.
- 입력 인자는 총 12개
- atoi()를 이용하여 문자열을 정수 배열로 변환
- 변환된 배열을 check() 함수로 전달
- 검증 성공 시 입력값을 문자로 출력하여 Flag 생성
check() 함수 분석
int __cdecl check(int a1){ int i; // [esp+Ch] [ebp-4h]
for ( i = 0; i <= 11; ++i ) { if ( sum(a1, i) != b[i] ) return 1; } return 0;}check() 함수를 분석한 결과 전역 변수 int coefficients[144]와 int b[12] 를 이용하여 입력값을 검증하고 있었다.
또한 반복문이 총 12번 수행되며, 조건문에 sum() 함수를 호출한다.
sum() 함수 분석
int __cdecl sum(int a1, int a2){ int v3; // [esp+8h] [ebp-8h] int i; // [esp+Ch] [ebp-4h]
v3 = 0; for ( i = 0; i <= 11; ++i ) // v3 += a1[i] * coefficients[12 * a2 + i] v3 += *(_DWORD *)(4 * i + a1) * cofficients[12 * a2 + i]; return v3;}a1은 사용자가 입력한 12개의 정수가 저장된 배열의 시작 주소이고, a2는 사용할 계수 행렬의 행 번호이다.
sum() 함수는 입력 배열의 각 원소 a1[i]와 coefficients[12 * a2 + i]를 곱한 뒤 모두 더한다. 즉, 입력 벡터와 coefficients의 a2번째 행을 내적한 결과를 반환한다.
이 반환값은 check() 함수에서 b[a2]와 비교되므로, 하나의 선형방정식이 성립하는지를 검사하는 구조이다.
분석 결과
전역 배열의 크기가 12 × 12 = 144 인 점을 보고 12개의 미지수를 갖는 연립방정식의 계수 행렬이라고 판단하였다.
수식으로 표현하면 다음과 같은 형태이다.
총 12개의 식으로 구성된 연립방정식을 만족하는 해를 구해야 한다.
Exploit
계수 추출
IDA Free에서는 전역 배열 전체를 한 번에 확인하기 어려웠기 때문에 GDB를 이용하여 메모리에서 계수를 직접 추출하였다.
cofficients[144] 전역 배열 데이터 추출
gdb ./equation_revenge(gdb) x/144dw &cofficients
b[12] 전역 배열 데이터 추출
gdb ./equation_revenge(gdb) x/12dw &b
연립방정식 풀이
NumPy의 numpy.linalg.solve()를 이용하여 미지수 12개를 계산하였다.
import numpy as np
A = np.array([ [-72, -74, 91, 59, 53, -95, -32, -39, 93, 76, -31, 22], [78, -84, -96, 69, -21, -72, 89, -26, 21, 65, 3, 49], [-46, 11, -39, 54, 57, -14, 59, -10, 77, -34, 0, 99], [27, 4, 52, 23, -1, 43, -41, 13, 9, -70, -16, 91], [60, -92, 84, 58, -8, -6, 91, 8, -30, -11, -5, -96], [-91, -16, -96, 51, -56, -85, -52, 46, -78, 87, 96, -83], [-32, -80, -80, 54, -28, -85, -38, -75, 5, 32, -80, -72], [5, -18, 6, 74, -9, -64, 30, -44, -26, -6, -22, 13], [30, 61, -100, 63, -19, -92, 68, -38, -11, -96, 44, -50], [59, 4, 99, -62, -34, -89, -52, 87, 22, 38, 86, 15], [-75, -92, -21, 62, -77, -31, -10, 90, 83, 89, 66, -17], [-5, 23, -29, 16, 25, 50, 95, 65, -57, 35, 4, 22]], dtype=np.int64)
b = np.array([ 4885, 10656, 14249, 7244, 17152, -22439, -41829, -7222, -12530, 18797, 13201, 22623], dtype=np.int64)
x = np.linalg.solve(A, b)
print("Solution:")for i, value in enumerate(x, start=1): print(f"x{i} = {value}")
print("\nRounded:")print(np.round(x).astype(int))코드 실행 결과:

결과
Flag
파이썬 코드로 구한 연립방정식들의 해를 매개변수로 입력하면 Flag를 얻을 수 있다.

FLAG{z3_beginner!}