This program的漏洞在哪里?(简单的缓冲区溢出)

huangapple go评论129阅读模式
英文:

Where is the vulnerability for this program? (simple buffer overflow)

问题

你好,以下是代码部分的翻译:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>

#define DEFAULT_GAME 0

unsigned int credits;

void game_1();
void game_2();
void set_credits();
void change_game();

void (*games[2]) () = {game_1, game_2};
void (*admin_functionality[2]) () = {change_game, set_credits};
int current_game = 0;

void read_string(unsigned char *buf, unsigned int len) {
    unsigned int i = 0;
    char c = 0;

    while (i < len && (c != '\n' || i == 0)) {
        c = getchar();
        if (c == EOF) {
            puts("SOMETHING WENT WRONG!");
            exit(0);
        } else if (c != '\n') {
            buf[i++] = c;
        }
    }
}

int read_int() {
    int input;

    fflush(stdin);
    if (scanf("%d", &input) != 1) {
        puts("SOMETHING WENT WRONG");
        exit(1);
    }
    return input;
}

int read_int_prompt(const char* prompt) {
    printf("%s", prompt);
    return read_int();
}

void initialize_seed(void) {
    unsigned int seed;
    int fd;

    fd = open("/dev/urandom", O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "Lost my wisdom, I have. Now will I rest, yes, forever sleep.\n");
        exit(1);
    }
    read(fd, &seed, sizeof(unsigned int));
    close(fd);
    srand(seed);
}

void read_flag() {
    unsigned char content[128];
    FILE *file;
    file = fopen("./flag", "r");
    if (file != NULL) {
        fscanf(file, "%s", content);
        fclose(file);
        printf("You seized the opportunity and stole some secrets: %s\n", content);
    } else {
        fprintf(stderr, "Error while opening the file.\n");
        exit(1);
    }
    return;
}

void print_banner() {
    printf("Credits: %d\n", credits);
}

void print_menu() {
    print_banner();

    puts("1) Play");
    puts("2) Administration");
    puts("3) Exit");
}

void set_credits() {
    printf("\n::::::: Set Credits :::::::\n");
    credits = read_int_prompt("(credits) >");
}

void change_game() {
    int opt = 0;
    do {
        opt = read_int_prompt("Select game: (1)Pick a Number or (2)No Match Dealer\n>");
    } while (opt < 3 && opt > 0);
}

void administration() {
    int opt = 0;
    void (*fptr)() = admin_functionality[DEFAULT_GAME];
    unsigned char correct_hash[20] = {
        0x33, 0x54, 0xcd, 0x14, 0x69, 0xa8, 0xfa, 0x4f, 0x9a, 0x92,
        0x53, 0xca, 0x62, 0x90, 0x56, 0xd7, 0xcd, 0xc1, 0x2f
    };

    unsigned char salt[8] = {0x6d, 0x62, 0x67, 0x59, 0x7a, 0x52, 0x72, 0x64};
    unsigned char password[20] = {0};
    unsigned char salted_password[28];

    printf("This is admin functionality.\nPassword: ");
    read_string(password, 54);
    memcpy(salted_password, salt, 8);
    memcpy(salted_password+8, password, 20);
    SHA1(salted_password, strlen((char *)salted_password), password);

    if (memcmp(password, correct_hash, 20) == 0) {
        do {
            opt = read_int_prompt("Select functionality: (1) change game or (2) set credits\n>");
        } while (opt > 2 && opt < 1);

        if (opt-1 != DEFAULT_GAME) {
            fptr = admin_functionality[opt-1];
        }

        fptr();

    } else {
        printf("Authentication failed!\n");
    }
}

void game_2() {
    int i, j;
    int numbers[16];
    int set_credits = -1;
    int match = -1;

    printf("\n::::::: No Match Dealer :::::::\n");
    printf("Wager up to all of your credits.\n");
    printf("The dealer will deal out 16 random numbers between 0 and 99.\n");
    printf("If there are no matches among them, you double your money! (stonks!)\n\n");

    if (credits == 0) {
        printf("You don't have any credits to wager!\n\n");
        return;
    }

    set_credits = read_int_prompt("Wager credits. If you guess the number we roll, you get back the double amount otherwise you lose them\n> ");
    if ((unsigned int)set_credits > credits) {
        printf("You cannot set more credits than you own!\n");
    } else {
        credits = credits - set_credits;
    }

    printf("\t\t::: Dealing out 16 random numbers :::\n");
    for (i = 0; i < 16; i++) {
        numbers[i] = rand() % 100; /* pick a number 0 to 99 */
        printf("%2d\t", numbers[i]);
        if (i % 8 == 7)  /* Print a line break every 8 numbers. */
            printf("\n");
    }
    for (i = 0; i < 15; i++) {  /* Loop looking for matches */
        j = i + 1;
        while (j < 16) {
            if (numbers[i] == numbers[j])
                match = numbers[i];
            j++;
        }
    }
    if (match != -1) {
        printf("The dealer matched the number %d!\n", match);
        printf("You lose %d credits.\n", set_credits);
        credits -= set_credits;
    } else {
        printf("There were no matches! You win %d credits!\n", set_credits);
        credits += set_credits;
    }
    return;
}

void game_1() {
    int set_credits;
    int guess;
    printf("\n::::::::::: Pick a Number :::::::::::\n");

    if (credits == 0) {
        printf("Thank you for playing. Unfortunately, you lost everything. Bye, see you the next time.\n");
        return;
    }
    printf("Wager credits. If you guess the number we roll, you get back the double amount otherwise you lose them\n");
    set_credits = read_int

<details>
<summary>英文:</summary>

I have got a program and was told to exploit a buffer overflow attack.
From what I know, buffer overflow occurs when we do no bound check when we move a variable to another variable, such as calling function like gets, strcmp. However, I have spent a few days and still cant figure out where is the vulnerability in my program. Can someone give me some hints to start or how can I look for a variable that is vulnerable getting buffer overflow attack? Many thanks.

Below is the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>

#define DEFAULT_GAME 0

unsigned int credits;

void game_1();
void game_2();
void set_credits();
void change_game();

void (*games[2]) () = {game_1, game_2};
void (*admin_functionality[2]) () = {change_game, set_credits};
int current_game = 0;

void read_string(unsigned char *buf, unsigned int len){
unsigned int i = 0;
char c = 0;

    while (i &lt; len &amp;&amp; (c != &#39;\n&#39; || i == 0)){
c = getchar();
if (c == EOF){
puts(&quot;SOMETHING WENT WRONG!&quot;);
exit(0);
} else if (c != &#39;\n&#39;) {
buf[i++] = c;
}
}

}

int read_int(){
int input;

    fflush(stdin);
if (scanf(&quot;%d&quot;, &amp;input) != 1) {
puts(&quot;SOMETHING WENT WRONG&quot;);
exit(1);
}
return input;

}

int read_int_prompt(const char* prompt){
printf("%s", prompt);
return read_int();
}

void
initialize_seed(void) {
unsigned int seed;
int fd;

    fd = open(&quot;/dev/urandom&quot;, O_RDONLY);
if (fd &lt; 0) {
fprintf(stderr, &quot;Lost my wisdom, I have. Now will I rest, yes, forever sleep.\n&quot;);
exit(1);
}
read(fd, &amp;seed, sizeof(unsigned int));
close(fd);
srand(seed);

}

void read_flag(){
unsigned char content[128];
FILE *file;
file = fopen("./flag", "r");
if (file != NULL) {
fscanf(file, "%s", content);
fclose(file);
printf("You seized the opportunity and stole some secrets: %s\n", content);
} else {
fprintf(stderr, "Error while opening the file.\n");
exit(1);
}
return;
}

void print_banner(){
printf("Credits: %d\n", credits);
}

void print_menu(){
print_banner();

puts(&quot;1) Play&quot;);
puts(&quot;2) Administration&quot;);
puts(&quot;3) Exit&quot;);

}

void set_credits() {
printf("\n::::::: Set Credits :::::::\n");
credits = read_int_prompt("(credits) >");
}

void change_game(){
int opt = 0;
do{
opt = read_int_prompt("Select game: (1)Pick a Number or (2)No Match Dealer\n>");
} while(opt < 3 && opt > 0);

}

void administration() {
int opt = 0;
void (*fptr)() = admin_functionality[DEFAULT_GAME];
unsigned char correct_hash[20] = {
0x33, 0x54, 0xcd, 0xb5, 0x14, 0x69, 0xa8, 0xfa, 0x4f, 0x9a,
0x92, 0x53, 0xca, 0x62, 0x90, 0x56, 0xd7, 0xcd, 0xc1, 0x2f
};

    unsigned char salt[8] = {0x6d, 0x62, 0x67, 0x59, 0x7a, 0x52, 0x72, 0x64};
unsigned char password[20] = {0};
unsigned char salted_password[28];
printf(&quot;This is admin functionality.\nPassword: &quot;);
read_string(password, 54);
memcpy(salted_password, salt, 8);
memcpy(salted_password+8, password, 20);
SHA1(salted_password, strlen((char *)salted_password), password);
if (memcmp(password, correct_hash, 20) == 0){
do{
opt = read_int_prompt(&quot;Select functionality: (1) change game or (2) set credits\n&gt;&quot;);
} while(opt &gt; 2 &amp;&amp; opt &lt; 1);
if (opt-1 != DEFAULT_GAME){
fptr = admin_functionality[opt-1];
}
fptr();
} else {
printf(&quot;Authentication failed!\n&quot;);
}

}

void game_2() {
int i, j;
int numbers[16];
int set_credits = -1;
int match = -1;

    printf(&quot;\n::::::: No Match Dealer :::::::\n&quot;);
printf(&quot;Wager up to all of your credits.\n&quot;);
printf(&quot;The dealer will deal out 16 random numbers between 0 and 99.\n&quot;);
printf(&quot;If there are no matches among them, you double your money! (stonks!)\n\n&quot;);
if (credits == 0) {
printf(&quot;You don&#39;t have any credits to wager!\n\n&quot;);
return;
}
set_credits = read_int_prompt(&quot;Wager credits. If you guess the number we roll, you get back the double amount otherwise you lose them\n&gt; &quot;);
if ((unsigned int)set_credits &gt; credits) {
printf(&quot;You cannot set more credits than you own!\n&quot;);
} else {
credits = credits - set_credits;
}
printf(&quot;\t\t::: Dealing out 16 random numbers :::\n&quot;);
for (i=0; i &lt; 16; i++) {
numbers[i] = rand() % 100; /* pick a number 0 to 99 */
printf(&quot;%2d\t&quot;, numbers[i]);
if (i%8 == 7)  /* Print a line break every 8 numbers. */
printf(&quot;\n&quot;);
}
for (i=0; i &lt; 15; i++) {  /* Loop looking for matches */
j = i + 1;
while (j &lt; 16) {
if (numbers[i] == numbers[j])
match = numbers[i];
j++;
}
}
if (match != -1) {
printf(&quot;The dealer matched the number %d!\n&quot;, match);
printf(&quot;You lose %d credits.\n&quot;, set_credits);
credits -= set_credits;
} else {
printf(&quot;There were no matches! You win %d credits!\n&quot;, set_credits);
credits += set_credits;
}
return;

}

void game_1() {
int set_credits;
int guess;
printf("\n::::::::::: Pick a Number :::::::::::\n");

    if (credits == 0) {
printf(&quot;Thank you for playing. Unfortunately, you lost everything. Bye, see you the next time.\n&quot;);
return;
}
printf(&quot;Wager credits. If you guess the number we roll, you get back the double amount otherwise you lose them\n&quot;);
set_credits = read_int_prompt(&quot;Credits to wager: &quot;);
if ((unsigned int)set_credits &gt; credits) {
printf(&quot;You cannot set more credits than you own!\n&quot;);
} else {
credits = credits - set_credits;
}
guess = read_int_prompt(&quot;Guess a number between 1 and 12 to win the game:\n&gt; &quot;);
if ((rand() % 12) +1 == guess) {
printf(&quot;You won %d credits\n&quot;, set_credits * 2);
credits = credits + set_credits * 2;
} else {
printf(&quot;You lost %d credits\n&quot;, set_credits);
}
}

void play(){
int opt = 0;

    while(1){
print_menu();
opt = read_int_prompt(&quot;&gt;&gt; &quot;);
switch (opt) {
case 1:
games[current_game]();
break;
case 2:
administration();
break;
case 3:
puts(&quot;Pleasure doing business with you!&quot;);
exit(0);
break;
default:
puts(&quot;Invalid option&quot;);
}
}

}

int main(void) {
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);

    initialize_seed();
credits = 10;
play();
return 0;

}


I have tried giving the program large input for every input, to see if there is any segmentation fault occurs. However, for all of the input I send, the program  just exit normally. I would like to know how can I find variable that is vulnerable for buffer overflow attack in gdb?
</details>
# 答案1
**得分**: 1
The buffer overflow is here:
read_string(password, 54);
`password` is only 20 bytes long. `read_string` is willing to write up to its second parameter's bytes.
Depending on your processor and compiler, this could allow you to overwrite `salt` and `correct_hash` with an over-long password.
<details>
<summary>英文:</summary>
The buffer overflow is here:
read_string(password, 54);
`password` is only 20 bytes long. `read_string` is willing to write up to its second parameter&#39;s bytes.
Depending on your processor and compiler, this could allow you to overwrite `salt` and `correct_hash` with an over-long password.
</details>

huangapple
  • 本文由 发表于 2023年4月10日 22:30:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75977989.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定