랜덤한 2개의 숫자를 더한 결과가 입력 값과 일치하는지 확인하는 과정을 50번 반복하는 프로그램입니다. 모두 일치하면 flag 파일에 있는 플래그를 출력합니다. 알맞은 값을 입력하여 플래그를 획득하세요.
플래그 형식은 DH{...} 입니다.
소스파일
// Name: chall.c
// Compile Option: gcc chall.c -o chall -fno-stack-protector
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#define FLAG_SIZE 0x45
void alarm_handler() {
puts("TIME OUT");
exit(1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
}
int main(void) {
int fd;
char *flag;
initialize();
srand(time(NULL));
flag = (char *)malloc(FLAG_SIZE);
fd = open("./flag", O_RDONLY);
read(fd, flag, FLAG_SIZE);
close(fd);
int num1 = 0;
int num2 = 0;
int inpt = 0;
for (int i = 0; i < 50; i++){
alarm(1);
num1 = rand() % 10000;
num2 = rand() % 10000;
printf("%d+%d=?\n", num1, num2);
scanf("%d", &inpt);
if(inpt != num1 + num2){
printf("Wrong...\n");
return 0;
}
}
puts("Nice!");
puts(flag);
return 0;
}
소스코드 분석
void alarm_handler() {
puts("TIME OUT");
exit(1);
}
main 반복문에 alarm(1) → 1초 내에 입력하지 않으면 이 함수를 실행하고 종료
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
}
initialize() 함수는 C에서 입출력 처리 속도와 시그널 처리를 조절하는 초기 설정 함수
setvbuf(stdin, NULL, _IONBF, 0);
→ 표준 입력 버퍼링 제거
setvbuf(stdout, NULL, _IONBF, 0);
→ 표준 출력 버퍼링 제거
⇒ 입출력 내용이 바로바로 화면에 보이게 된다.
- c의 입출력 함수 (scanf, getchar 등)은 내부적으로 버퍼를 사용
- stdin(표준입력)은 줄 단위로 버퍼링됨 = 사용자가 enter를 눌러야 입력이 전달
|
_IOFBF
|
완전한 버퍼링(Full Buffering)
|
|
|
_IOLBF
|
행 버퍼링(Line Buffering)
|
버퍼가 채워지거나 개행문자가 입력되었다면 데이터가 버퍼에서 출력됨. 입력시에는 버퍼가 개행 문자를 만날 때까지 버퍼를 채우게 됨
|
|
_IONBF
|
버퍼링 사용 안함(No Buffering)
|
버퍼를 사용하지 않고 요청 즉시 진행
|
signal(SIGALRM, alarm_handler);
→ SIGALRM 시그널이 발생하면 alarm_handler() 실행
⇒ alarm()함수로 예약된 1초의 시간이 지나면 alarm_handler()를 실행
- SIGALRM은 alarm() 함수로 예약된 타이머가 끝났을 때 발생하는 시그널
flag = (char *)malloc(FLAG_SIZE);
fd = open("./flag", O_RDONLY);
read(fd, flag, FLAG_SIZE);
close(fd);
- ./flag 파일을 열고 내용을 flag 포인터에 저장함
- O_RDONLY : 파일을 읽기 전용으로 open
for (int i = 0; i < 50; i++){
alarm(1); // 1초 타이머 시작
num1 = rand() % 10000;
num2 = rand() % 10000;
printf("%d+%d=?\n", num1, num2);
scanf("%d", &inpt);
if(inpt != num1 + num2){
printf("Wrong...\n");
return 0;
}
}
puts("Nice!");
puts(flag);
- 50번 반복하는 반복문임
- 0~9999 사이의 숫자 2개 랜덤 생성
- 입력된 값이 랜덤 숫자 2개의 합과 다르면 바로 Wrong 출력 후 종료
- 총 50번 전부 맞으면 Nice 출력 후 flag 출력
⇒ 반복문이 50번 실행될 때마다 랜덤 숫자 2개의 합을 1초 안에 입력해야 flag를 얻을 수 있음
풀이
원격 서버 접속


덧셈 숫자 수신
덧셈을 수행할 두 숫자를 recv()로 받아와야한다
첫 번째 숫자를 얻기 위해서 “+”까지 출력을 받은 뒤
두 번째 숫자를 얻기 위해서 “=”까지 출력을 받는다.

first_number = int(r.recvuntil(b'+', drop=True))
서버가 printf("%d+%d=?\n", num1, num2); 과 같은 수식을 출력하기 때문에
- recvuntil(b’+’) → + 기호가 나올 때까지 수신
- drop=True → +는 버리고 첫 번째 숫자만 받는다
- int()로 정수로 변환한다.
second_number = int(r.recvuntil(b'=?\n', drop=True))
- recvuntil(b’=?\n’) → =?\n가 나올 때까지 수신
- drop = True → =?\n은 버리고 두 번째 숫자만 받는다
- int()로 정수로 변환
덧셈 결과 전송

context.log_level = 'debug'
- 전송된 것이 정답으로 처리되는지 여부를 확인하기 위해서 데이터 송수신 내역을 확인
r.sendline(str(first_number + second_number).encode())
- 두 수를 더한 후 문자열로 바꾸고 .encode()해서 바이트로 전송
플래그 얻기
- 앞 과정을 50번 반복하는 반복문 구현 추가



50번 계산된 로그가 나온 뒤 flag가 출력됨
'Cryptography > Cryptography CTF' 카테고리의 다른 글
| [Dreamhack] No shift please! (0) | 2025.11.07 |
|---|---|
| [Dreamhack] No sub please! (0) | 2025.11.06 |
| [Dreamhack] chinese what? (0) | 2025.10.09 |
| [Dreamhack] Exploit Tech: Meet-in-the-middle Attack (0) | 2025.10.09 |
| [Dreamhack] flag-shop (0) | 2025.10.09 |