DEV Community

Cover image for 💣picoCTF: flag_shop
tRavOndAtrACk
tRavOndAtrACk

Posted on

💣picoCTF: flag_shop

Mục Nội dung
Tên bài flag_shop
Thể loại General Skills
Link https://play.picoctf.org/practice/challenge/49?originalEvent=1&page=3

Image description


📚 Lý thuyết

Khái niệm Giải thích
Integer Overflow Khi một biến số nguyên vượt quá giới hạn của kiểu dữ liệu, nó sẽ “quay vòng” thành số âm (trong two's complement).
Two’s Complement Cách biểu diễn số âm phổ biến trong máy tính. Số lớn vượt ngưỡng sẽ bị hiểu nhầm là số âm.
Vấn đề ở đây Biến số dư balance có thể bị overflow nếu cộng thêm số lớn, khiến ta vượt qua kiểm tra balance >= FLAG_PRICE.
Hậu quả Người dùng có thể “mua” flag mà không đủ tiền, nhờ lỗi logic từ overflow.

🛡️ Thực hành

Source code:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    setbuf(stdout, NULL);
    int con;
    con = 0;
    int account_balance = 1100;
    while(con == 0){

        printf("Welcome to the flag exchange\n");
        printf("We sell flags\n");

        printf("\n1. Check Account Balance\n");
        printf("\n2. Buy Flags\n");
        printf("\n3. Exit\n");
        int menu;
        printf("\n Enter a menu selection\n");
        fflush(stdin);
        scanf("%d", &menu);
        if(menu == 1){
            printf("\n\n\n Balance: %d \n\n\n", account_balance);
        }
        else if(menu == 2){
            printf("Currently for sale\n");
            printf("1. Defintely not the flag Flag\n");
            printf("2. 1337 Flag\n");
            int auction_choice;
            fflush(stdin);
            scanf("%d", &auction_choice);
            if(auction_choice == 1){
                printf("These knockoff Flags cost 900 each, enter desired quantity\n");

                int number_flags = 0;
                fflush(stdin);
                scanf("%d", &number_flags);
                if(number_flags > 0){
                    int total_cost = 0;
                    total_cost = 900*number_flags;
                    printf("\nThe final cost is: %d\n", total_cost);
                    if(total_cost <= account_balance){
                        account_balance = account_balance - total_cost;
                        printf("\nYour current balance after transaction: %d\n\n", account_balance);
                    }
                    else{
                        printf("Not enough funds to complete purchase\n");
                    }


                }
            }
            else if(auction_choice == 2){
                printf("1337 flags cost 100000 dollars, and we only have 1 in stock\n");
                printf("Enter 1 to buy one");
                int bid = 0;
                fflush(stdin);
                scanf("%d", &bid);

                if(bid == 1){

                    if(account_balance > 100000){
                        FILE *f = fopen("flag.txt", "r");
                        if(f == NULL){

                            printf("flag not found: please run this on the server\n");
                            exit(0);
                        }
                        char buf[64];
                        fgets(buf, 63, f);
                        printf("YOUR FLAG IS: %s\n", buf);
                        }

                    else{
                        printf("\nNot enough funds for transaction\n\n\n");
                    }}
            }
        }
        else{
            con = 1;
        }
    }
    return 0;
}

Enter fullscreen mode Exit fullscreen mode

🔎 Phân tích

Ban đầu người dùng có số dư 1100. Flag "xịn" (1337 Flag) có giá 100000, nên về logic bình thường thì không thể mua được.

💡 Ý tưởng

  • Lổ hổng: int total_cost = 900 * number_flags;
  • Biến total_cost là kiểu int. Nếu number_flags lớn, total_cost sẽ bị overflow ==> có thể biến total_cost thành số âm → bypass điều kiện if(total_cost <= account_balance)

Điều này làm cho account_balance sau khi trừ tiền có thể trở thành số rất lớn, dù người dùng nhập số cực lớn. Sau đó quay lại mua 1337 Flag, kiểm tra account_balance > 100000 được thoả mãn!

🧪 Bước 2: Thực hiện

Image description

Image description

==> DONEEEE!


Cần kiểm tra cẩn thận input và dùng kiểu dữ liệu an toàn (long long, unsigned long,...) nếu cần.


✍️ Write-up by tRavOndAtrACk – Happy hacking!

Top comments (0)