티스토리 뷰

Pwnable에는 많은 기법이 있는데, 그중에서 자주 쓰이는 기법들 중 Int Overflow라는 것이 있다.

그대로 말하면 정수타입에서 일어나는 Overflow취약점이다. 


가령 아래와 같은 코드가 있다. (shell-storm.org에서 받을 수 있음)


fire-me.c


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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
 
/*
Version "secured" of the challenge
*/
 
void rules_explanation(void)
{
    printf("==========================================\n");
    printf("+> The rules are very simple:           <+\n");
    printf("+> The one that take the last match win <+\n");
    printf("==========================================\n");
}
 
int check_decision(char decision)   // no cheat :(
{
    return (decision >= 1 && decision <= 3);
}
 
int human_turn(int *nb_matches) 
{
    unsigned int decision = 0;
    char buffer[10];
 
    printf("How many matches do you want to take ? (1-3)\n");
    fgets(buffer, sizeof(buffer), stdin);
    
    decision = strtoul(buffer, NULL, 10); // string to unsigned long
 
    if (!check_decision(decision)) 
    {
        printf("Are you trying to cheat ??\n");
        return 0;
    }
 
    *nb_matches -= decision;
    printf("You've taken %i match(es)\n", decision);
 
    return 1;
}
 
void computer_turn(int *nb_matches) 
{
    unsigned int decision = 0;
 
    if (*nb_matches == 4
    {
        decision = 4;
        *nb_matches = 0;
 
        printf("HUMMM\n");
        sleep(1);
 
        printf("HUMMMMMMMMMMM\n");
        sleep(1);
 
        printf("Wait, I got this !\n");
        sleep(1);
    }
    else if (*nb_matches % 4 == 0
    {
        decision = 1;
        *nb_matches -= decision;
    }
    else 
    {
        decision = (*nb_matches % 4);
        *nb_matches -= decision;
    }
 
    printf("The computer has taken %i match(es)\n", \
                                            decision);
}
 
int main(void)
{
    // We set the number of matches
    srand(time(NULL)); 
    char pass[80];                   
    FILE *fd;                         
    int nb_matches = 10 + rand() % 40// randValue : 10 ~ 50
    int turn = 0;
    
    // Game settings
    rules_explanation();
    printf("There are %u match(es)\n", nb_matches);
    
    // Now we begin the game
    while (nb_matches > 0)
    {
        if (turn == 0
        {
            if (human_turn(&nb_matches))
            {
                turn = 1;
            }
            else
            {
                turn = 0;
            }
        }
        else 
        {
            computer_turn(&nb_matches);
            turn = 0;
        }
 
        printf("there are %i match(es) remaining\n",\
                                         nb_matches);
    }
    
    if (!turn) // turn must be TRUE
    {
        printf("You lose !\n");
    }
    else    // Game win! get flag.
    {
        printf("How did you do this ?\n");
        printf("password is : \n");
 
        fd = fopen(".password""r");
        fgets(pass, sizeof(pass), fd);
 
        printf("%s", pass);
        printf("You win !\n");
    }
 
    return EXIT_SUCCESS;
}



cs


이 게임은 우리가 흔히 아는 베스킨 라빈스 31게임과 매우 흡사하다.

차이점은 성냥이란 것과 성냥이 10~50중 랜덤하게 나온다는것, 

그리고 우리가 정확하게 0개를 만들어야 이긴다는 점이다.


그런데 베스킨 라빈스 31게임은 남은 개수중 4로 나눠서 나머지 값을 계속해서 부르면 

무조건 이기게 돼있다.


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
~> ./fire_me 
==========================================
+> The rules are very simple:           <+
+> The one that take the last match win <+
==========================================
There are 15 match(es)
How many matches do you want to take ?(1-3)
3
You've taken 3 match(es)
there are 12 match(es) remaining
The computer has taken 1 match(es)
there are 11 match(es) remaining
How many matches do you want to take ?(1-3)
3
You've taken 3 match(es)
there are 8 match(es) remaining
The computer has taken 1 match(es)
there are 7 match(es) remaining
How many matches do you want to take ?(1-3)
3
You've taken 3 match(es)
there are 4 match(es) remaining
HUMMM
HUMMMMMMMMMMM
Wait, I got this !
The computer has taken 4 match(es)
there are 0 match(es) remaining
You lose !
cs


그런데 컴퓨터가 그걸 놔 둘리 없음


1
2
3
4
5
6
7
8
9
10
11
12
13
14
    if (*nb_matches == 4
    {
        decision = 4;
        *nb_matches = 0;
 
        printf("HUMMM\n");
        sleep(1);
 
        printf("HUMMMMMMMMMMM\n");
        sleep(1);
 
        printf("Wait, I got this !\n");
        sleep(1);
    }
cs


computer_turn() 함수에 이런 구문이 있음.

즉 4가 되면 무조건 유저가 이겨버리니 컴퓨터가 4개를 모두

가져가 버림; 


그런데 우리는 check_decision() 함수 때문에 1 ~ 3까지 밖에 선택을 못함.

여기서는 절대로 게임을 이길 수 없게 만들어 놓았음.


1
2
3
4
int check_decision(char decision)  // no cheat :(
{
    return (decision >= 1 && decision <= 3);
}
cs


그러면 우리는 어떻게든 check_decision()함수를 Bypass해서 게임을

이겨야 함.


그런데 여기서 한가지 이상한 점을 발견할 수 있음.


1
unsigned int decision;
cs


원래 decision변수는 Unsigned Int형임. 즉 0 ~ 2^32까지 포함 할 수 있음.

(signed int형은 -2^31 ~ 2^31, 4byte, 32bit)


그런데 check_decision(char decision)함수를 보면 desicion변수를

char형으로 받아버림. char형은 0 ~ 256까지임 (1byte, 8bit)


코드에서는 fgets()함수로 buffer에 10개의 문자열을 받아서

strtoul(string to unsigned long)함수를 통해 정수형으로 바꿔줌 

(마이너스 값은 입력 불가)


우리는 10e9 - 1 (9999999999) 만큼 값을 입력할 수 있음.


그렇다면 우리는 char형으로 인식했을 때 1~3이여야 하며

unsigned int형으로 인식했을 때 무조건 50보다 큰 값을 입력해야 함.


2진수로 바꿔보면 매우 쉽게 풀 수 있음.


 0000 0000 0000 0000 0000 0001 0000 0001

4         3         2         1         (byte)


이 값을 10진수로 바꾸면 257임 char의 최대 값보다 1높은 값인데, 

왜 이값을 char형으로 바꿧을 때 1로 인식하는지 알아야 함.


Char형은 1바이트까지 담아둘 수 있는 변수임. 즉 저기서 1바이트는


                               0000 0001


0000 0001 이며 0000 0001까지 밖에 인식을 못하기 때문에 char형으로 

보았을 때 1로 인식해 버리는 것. 


1은 check_decision() 함수 조건에 만족하는 값이기 때문에

Are You trying to cheat? 문자열이 뜨지 않을 것이고

우리는 게임에서 승리하여 Flag를 획득 할 수 있음.


1
2
3
4
5
6
7
8
9
10
11
12
==========================================
+> The rules are very simple:           <+
+> The one that take the last match win <+
==========================================
There are 38 match(es)
How many matches do you want to take ?(1-3)
257
You've taken 257 match(es)
there are -219 match(es) remaining
How did you do this ?
password is : Integer Overflow :)
You win !
cs



1
Integer Overflow :)
cs



댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
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
글 보관함