addition-quiz

A_dawn·2024년 8월 16일
0

misc

목록 보기
1/1
post-thumbnail

addition-quiz

문제 주소: https://dreamhack.io/wargame/challenges/1114

Dreamhack의 새싹 난이도의 misc 문제이다.

문제


문제는 다음과 같다.

랜덤한 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() { //alarm(1)에 의해 signal을 거친후 alarm_handler가 호출되는데 화면에 TIME OUT을 출력하고 프로그램을 종료함.
    puts("TIME OUT");
    exit(1);
}

void initialize() {  //비버퍼링 모드로 전환하여 사용자와 상호작용을 더 빠르게 해줌
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler); // signal(SIGALRM, alarm_handler);은 특정신호에 대해 alarm_handler를 호출하는 역할을 함
}

int main(void) {
    int fd;
    char *flag;

    initialize();
    srand(time(NULL)); 

    flag = (char *)malloc(FLAG_SIZE); // flag라는 값을 FLAG_SIZE만큼 동적으로 할당하고 flag 파일을 읽기 전용으로 읽어와서 FLAG_SIZE만큼 flag 변수에 저장함.
    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); //1초후 alarm이 실행되며 SIGALRM으로 신호가 전달되어 alarm_handler의 내용을 실행함.
        
        num1 = rand() % 10000; //num1과 num2 랜덤한 값으로 생성
        num2 = rand() % 10000;
        printf("%d+%d=?\n", num1, num2);
        scanf("%d", &inpt);

        if(inpt != num1 + num2){ //사용자에게 inpt값을 받아와서 num1+num2값과 비교하고 만약 틀리면 Wrong을 반환하고 틀릴 경우 종료 , 맞을 경우 for문을 탈출할 시 nice를 띄우며 flag를 출력해준다.
            printf("Wrong...\n");
            return 0;
        }
    } 
    
    puts("Nice!");
    puts(flag);

    return 0;
}

문제와 프로그램 실행 결과,소스코드를 참고해봤을 떄 결론은 프로그램에서 띄워주는 랜덤한 2개의 값을 더해서 프로그램으로 반환하는 일을 50번하면 flag을 얻을 수 있다라는 것이다.

홈페이지의 hint를 보면 다음과 같다.

pwntools를 사용하라는 걸 알 수 있는데 pwntools는 간단하게 시스템 해킹에 주로 사용하는 Python 모듈이다.

프로그램이 1초라는 시간 안에 답을 입력하지 않으면 닫히기 떄문에 pwntools를 이용하여 해결하고자 한다.

Exploit


Full Exploit은 다음과 같다.

from pwn import * //pwntools를 사용

p = remote(' host3.dreamhack.games',14299) // 본인의 포트를 입력



for i in range(50): //아래 내용을 50번 반복
    number = p.recvuntil(b'=')
    numbers = number.split(b'+')
    number1 = int(numbers[0])
    number2 = int(numbers[1].split(b'=')[0])

    p.recvline()
    p.sendline(str(number1 + number2))

p.interactive()

Exploit 분석


해당 코드를 분석해보면 다음과 같다.

p = remote(' host3.dreamhack.games',14299)는('호스트',포트) 형태로 입력하면 되는데 해당 호스트에 해당 포트로 원격 접속한다는 의미이다. 이해를 돕기 위해 예를 들어 설명하겠다.


예를 들어

1234 + 5678 = ?

가 출력되었다고 가정해보자

number = p.recvuntil(b'=')는 =까지의 내용을 받아오는 코드인데 위의 예시를 들면 1234 + 5678 = 까지 가져온다.
그럼 현재 number에 1234 + 5678 = 가 저장되어 있는 상태이다.

numbers = number.split(b'+') 같은 경우엔 number의 값이 split(b'+')를 기준으로 나뉘어져 저장되는데 이는 리스트 형태로 저장되게 된다.

numbers = [b'1234', b'5678=']

number1 = int(numbers[0])는 numbers[0]인 b'1234'를 number1에 int형으로 저장하고
number2 = int(numbers[1].split(b'=')[0])는 numbers[1]인 b'5678='를 b'='을 기준으로 나눠 그중 0번 인덱스 즉 b'5678'을 number2에 int형으로 저장한다는 의미이다.


이렇게 되면 number1에는 int형 1234 , number2에는 int형 5678이 저장되게 되는데
revuntil()은 괄호안의 값까지 읽어오는 역할을 하는데 우리는 b'='까지 읽어왔었기 떄문에
전체문장이 1234 + 5678 = ? \n 이지만 1234 + 5678 = 만 읽어온 것이다.


그럼 그 뒷부분을 마저 실행해줘야 다음으로 넘어가기 떄문에 p.recvline() 를 사용하여 넘어가준다.
이후 p.sendline(str(number1 + number2))을 이용하여 number1과 number2를 합한 값을 프로그램에 전달해준다.


이 과정들을 for문을 통해 50번 실행하고 끝나게 되면
p.interactive()를 통해 결과값 확인 및 쉘을 얻을 수 있다.

profile
https://adawn0106.tistory.com/ <- 이쪽으로 옮겨요! / EN blog: https://adawn0106.github.io/

0개의 댓글

관련 채용 정보