xss-labs WriteUp
2025-01-04 07:14:49

xss-labs

level 1:

没有任何的编码和过滤,用户的输入直接输出到heml页面了。
所以payload:

1
<script>alert(1)</script>

level 2:

经过测试,本关有两个输出点。
当我尝试输入<script>时,页面中两个输出点:

1
2
3
<h2 align=center>没有找到和&lt;script&gt;相关的结果.</h2><center>

<input name=keyword value="<script>">

我们可以发现
<h2>标签中的尖括号被过滤了,而<input>标签中的属性值没有被过滤,所以利用这一漏洞点我们即可构造payload如下:

1
" onclick="alert(1)

我们得到的html页面中拼接好的<input>标签如下:

1
<input name=keyword  value="" onclick="alert(1)">

所以当我们点击输入框时即可触发弹框。

level2-1
level2-2

注意<h2>处的过滤是通过htmlspecialchars()函数过滤的,后面的过滤也会用到这个函数。

level 3:

经过level2跳转过来,给的默认参数是level3.php?writing=wait,但是向writing传入值并没有在页面中发现输出点,所以此参数没有被输出到页面中来。
通过看前端的代码我们可以在<input>标签中发现一个名为keyword的参数名,向它传值,发现可以在前端中输出。
经过测试,可以发现两个输出点中过滤了尖括号、双引号,单引号没有被编码,由此可以推断出是后端是通过htmlspecialchars函数进行过滤的,因为该函数默认情况下仅编码双引号,而恰好<input>标签中的属性值的闭合符号是单引号,所以我们可以构建闭合,
payload:

1
' onclick='alert(1)


同level2,点击输入框即可触发弹窗。

level 4:

测试发现两个输出点,
第一个输出点编码过滤了尖括号、双引号;
第二个输出点过滤了尖括号。
所以这里我们可以使用双引号进行属性的闭合来进行弹窗的注入。
payload:

1
" onclick="alert(1)

level 5:

经过测试:
第一个输出点,过滤了尖括号、双引号、没有过滤掉单引号;推测使用htmlspecialchars函数。
第二个输出点,过滤了<scripton;推测是识别指定字符串然后进行替换过滤。
由此可以看出第二个输出点的余地是比较大的,毕竟没有把尖括号和引号完全过滤,我们可以使用>来闭合标签的方法,来插入新的标签。
payload:

1
2
3
"> <a href="javascript:alert(1)">colickme</a>

"> <iframe src="javascript:alert()"></iframe>

注意:第二个输出点这里过滤使用的函数是:str_replace()

level 6:

测试发现被过滤的字段如下:
<script on href src
也是有两处输出点,第一处是htmlspecialchars函数过滤,第二处是过滤指定字符串。
由于html大小写不敏感,所以我们可以尝试大小写绕过,
payload:

1
2
3
4
5
" Onclick="alert(1)

"> <img Src=1 Onerror=alert(1)>

其他被过滤的字符串同理...

level 7:

这关是将敏感字符串置为空值,这类过滤方法我们可以使用双写来绕过。
payload:

1
" oonnmouseover="alert(1)

level 8:

什么是HTML实体编码?
由经验可以推测出<input>标签中的输出点是通过htmlspecialchars()函数过滤的
第二个输出点是通过str_replace()函数过滤的,将指定的敏感字符过滤掉。
之前的绕过方法都不可行,所以这里我们可以试着尝试进行html实体编码绕过
我们要利用的是第二个输出点中的标签的属性值,对要输入的字符串进行html实体编码处理,来绕过过滤
payload:

1
2
3
javascript:alert(1)
编码后:
&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;

html编码工具
属性值被html编码后还可以正常运行,但是标签一旦被html编码后就无法执行了

level 9:

这部分黑盒没出来,所以审计代码可知,如果要得到href参数的值,字符串中必须有http://的值,字符串过滤与level8一样,通过html实体编码可以绕过对关键字的过滤

payload:

1
java&#115;&#99;&#114;&#105;&#112;&#116;:alert('http://')

level 10:

在url keyword变量输出参数查看返回的结果。
打开网页端源码进行检查,网页端源码表单并没有keyword,但是多了三个隐藏的<input>标签,我们可以依次传值,查看输出点。如下:

我们可以发现,只有t_sort值被输出,所以我们在这个点进行注入,并且闭合掉后面的隐藏属性:
payload:

1
t_sort=" onfocus="alert(1)" type="text

level 11:

这部分我们查看网页端源码可以发现,在表单中有一串url地址:

这是我们上一个页面的url地址,可以判断出这是http头中的REFERER值,所以我们可以抓包修改http请求头中的REFERER中的值构建payload,在http头尝试构建payload,发现只过滤了尖括号,所以我们可以构建payload如下:

1
" onfocus="alert(1)" type="text

level 12:

首先我们查看网页端源码发现表单中t_ua中的值是User-Agent的值,

所以这里与level11同理,后端也是只过滤了尖括号,所以我们抓包修改User-Agent的值即可,payload同上。

level 13:

首先查看网页端代码,

抓包发现这次是注入点是cookie值,查看后端代码发现也是只过滤了尖括号,所以payload同level11

level 14:

exif xss漏洞
由于特殊原因链接中的站点已经无法访问了


<iframe>标签规定了一个内联框架,一个内联框架用来在当前HTML文档中嵌入另一个文档。
由于以上链接中的网站无法访问了,我们来修复这部分功能:
修改网站中level14.php的源码,替换目标链接到本地路径:

创建exifviewer.php文件,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<?php
/**
* website2.php
* 用途:提供一个上传图片并展示 EXIF 信息的示例后端页面
* 说明:此示例将 HTML 和 PHP 写到一起,可直接在服务器上部署使用
*/

// 如果表单提交了文件
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 检查是否有文件上传
if (isset($_FILES['upload_image']) && $_FILES['upload_image']['error'] === UPLOAD_ERR_OK) {
$fileTmpPath = $_FILES['upload_image']['tmp_name']; // 临时文件路径
$fileName = $_FILES['upload_image']['name']; // 原文件名
$fileSize = $_FILES['upload_image']['size']; // 文件大小
$fileType = $_FILES['upload_image']['type']; // MIME 类型

// 可以根据需要进一步判断文件扩展名,如:jpg, jpeg, png, gif等
// 这里只示例判断是否是常见图片 MIME 类型
$allowedMimeTypes = [
'image/jpeg',
'image/png',
'image/gif'
];

if (in_array($fileType, $allowedMimeTypes)) {
// 尝试读取 EXIF 信息(仅对 JPEG 有效,PNG/GIF 通常没有 EXIF)
$exifData = @exif_read_data($fileTmpPath, 0, true);

// 将输出缓存在缓冲区,稍后一起存到变量中
ob_start();
if (!empty($exifData)) {
echo "<div style='border:1px solid #ccc; padding:10px; margin-bottom:20px;'>";
echo "<strong>[" . $fileName . "] 的 EXIF 信息:</strong><br>";
foreach ($exifData as $section => $data) {
foreach ($data as $key => $val) {
echo $section . "." . $key . ": " . $val . "<br>";
}
}
echo "</div>";
} else {
// 若没有 EXIF 信息或该格式不支持
echo "<div style='border:1px solid #ccc; padding:10px; margin-bottom:20px;'>";
echo "<strong>[" . $fileName . "] 没有可读的 EXIF 信息。</strong>";
echo "</div>";
}

// 将展示 EXIF 的内容存到变量里,便于后面在 HTML 中直接输出
$exifOutput = ob_get_clean();
} else {
// 非法文件格式
$errorMsg = "错误:非法的文件格式。";
}
} else {
// 上传错误或没选择文件
$errorMsg = "错误:未选择文件或上传失败。";
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>上传图片查看 EXIF 信息</title>
</head>
<body>
<?php
// 如果有错误信息,先输出错误
if (!empty($errorMsg)) {
echo "<div style='color:red; margin-bottom:20px;'>" . $errorMsg . "</div>";
}

// 如果成功解析到了 EXIF 信息,则输出
if (!empty($exifOutput)) {
echo $exifOutput;
}
?>
<!-- 上传表单 -->
<form action="" method="post" enctype="multipart/form-data">
<label for="upload_image">请上传一张图片:</label>
<input type="file" name="upload_image" id="upload_image">
<input type="submit" value="提交">
</form>
</body>
</html>

刷新页面,如下:

上传照片文件,从页面信息中我们可以得知,后端程序是读取照片中的exif信息输出到网页端中的,根据xss漏洞的原理,当exif信息过滤不严谨,我们构建的payload被输出到了网页端,就会造成xss攻击。
什么是EXIF?:

1
2
3
4
可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的文件格式,可以记录数码照片的属性信息和拍摄数据。  
Exif可以附加于JPEG、TIFF、RIFF等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。
Windows具备对Exif的原生支持,通过鼠标右键点击图片打开菜单,点击属性并切换到详细信息标签下即可直接查看Exif信息。
Exif信息是可以被任意编辑的,因此只有参考的功能。

我们在Exif中写入payload:

注入成功:

level 15:

ng-include指令注入
查看页面源代码,经过测试url中src的值是注入点,输出点在<span>标签中

又看到ng-include指令可以利用,所以我们可以利用ng-include指令引入外部html来构建payload
注意ng-include,如果单纯指定地址,必须要加引号

payload:

1
2
3
?src='level1.php?name=<img src=1 onclick=alert(1)>'

?src='level1.php?name=<img src=1 onerror=alert(1)>'

参考
文章中的ng-include注意事项非常值得参考
ng-include官方文档

level 16:

首先看网页端的源码,发现我们传入的值显示在标签的外部,像这种位置,想要触发xss攻击,似乎只能构建新的标签来触发了。
经过测试,后端程序过滤替换的字符如下:

被替换的字符 替换后的字符
script &nbsp;
(空格,即 " " &nbsp;
/ &nbsp;
" "(5个空格) &nbsp;

script字符被过滤了,我们只能构建其他的标签了,程序并没有阻止我们构建标签,看来主要是目标是将空格替换为&nbsp,来使得我们的标签无法触发。
现在我们要找能够替换空格的编码了。
可以替换空格的编码:
在 ASCII 编码及许多文本处理、HTML/CSS 渲染规范中,换行符 (LF, %0A)、回车符 (CR, %0D)、换页符 (FF, %0C)、制表符 (TAB, %09) 等都被视为“空白字符 (whitespace characters)” 的一部分,它们在很多场景下会被等同于空格或直接折叠成空格。这就是为什么在某些情况下 %0A、%0C、%0D 解码后表现得像空格一样。
所以我们可以构建payload如下:

1
<input%0Conclick="alert(1)">

level 17-20都是flash xss下一期再一块写这部分

Prev
2025-01-04 07:14:49