WHY2025 CTF | Full Forensics + Reversing Bonus

Forensics Index

forensics/Painted Black

Detecting Macro
We have given a Microsoft Word 2007+ document, as the extension is .docm being a standart of Microsoft Word Macro-Enabled. Knowing this we apply the tools suite from olevba, to be able to list all macros inside the file:
$> olevba case-2025-0412-public.docm -ac --deobf
olevba 0.60.2 on Python 3.11.2 - http://decalage.info/python/oletools
===============================================================================
FILE: case-2025-0412-public.docm
Type: OpenXML
WARNING For now, VBA stomping cannot be detected for files in memory
-------------------------------------------------------------------------------
VBA MACRO ThisDocument.cls
in file: word/vbaProject.bin - OLE stream: 'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sub PaintItBlack()
Dim selectedText As String
Dim encryptedText As String
Dim key As String
Dim xorChar, keyPos
selectedText = Selection.Text
key = Replace(LCase(Application.UserName), " ", "")
encryptedText = ""
For i = 1 To Len(selectedText)
keyPos = i Mod Len(key) + 1
xorChar = Asc(Mid(selectedText, i, 1)) Xor Asc(Mid(key, keyPos, 1)) Xor &H7B
encryptedText = encryptedText & Chr(xorChar)
Next i
Selection.Text = encryptedText
Selection.Shading.BackgroundPatternColor = wdColorBlack
Selection.Font.ColorIndex = wdBlack
End SubRetrieving encrypted content
At first glance, while reading the script, we realize that it is harmless and is simply decrypting certain parts of the document using the author’s name. To understood more the code, we open the file in a online docm viewer such as https://products.groupdocs.app/viewer/docm:

By doing hovering into the painted text, some xored data will be found, also it is possible to identify it if you toggle to dark mode the file content, function that can be done with extensions like Black Reader:

To obtain the raw content of the painted text, I used the command docx2txt:
$> docx2txt case-2025-0412-public.docm
$> cat case-2025-0412-public.docm.txt
Ravenbrook police
department
--------------------------------------------------------------------------------
Search and Seizure Warrant
Case No.: 2025-0412 Issued By: Swywy}wcm3U`}a{l2Hlpf`rm Date: 2025-05-01
Subject: Search and Seizure of Items Related to Unauthorized Cyber Intrusion
Suspect: A{nfu{>Yi}}j{ev Address: %#:2Iaqgdy~qdf-Sll25Zrlizu`b}q%>[P3.<#/
By order of the Ravenbrook Police Department, authorized officers have conducted a search of the above premises under suspicion of unlawful cyber activities, including but not limited to unauthorized access to protected systems.
The following items were seized as part of the investigation:
Computers (one desktop and one laptop)
An external harddrive labeled q~luaj*-:"#j!rs4v,k| <u-9'$wity8$9!.q
USB Storage Devices
Routers & Network Equipment
Handwritten Notes & Documents Pertaining to Cyber Intrusion
All confiscated materials will be held at the Ravenbrook Police Department Cybercrime Division for forensic examination. Failure to comply with this warrant or obstruction of law enforcement officers will result in legal consequences.
Signed: Detective X~ddsh>Gm}idu`, Ravenbrook Police DepartmentObtaining Application.UserName
From there you can extract some encrypted data. Paying attention in the VBA Script, we need the Application.UserName, for this task I thought into the metadata of the file, with exiftool we found the name: Olivia Renshaw:
$> exiftool case-2025-0412-public.docm
ExifTool Version Number : 12.57
File Name : case-2025-0412-public.docm
Directory : .
File Size : 1722 kB
File Modification Date/Time : 2025:05:09 01:47:38+02:00
File Access Date/Time : 2025:08:12 23:57:31+02:00
File Inode Change Date/Time : 2025:08:12 23:57:08+02:00
File Permissions : -rwxrwxrwx
File Type : DOCM
File Type Extension : docm
MIME Type : application/vnd.ms-word.document.macroEnabled.12
Zip Required Version : 20
Zip Bit Flag : 0x0006
Zip Compression : Deflated
Zip Modify Date : 1980:01:01 00:00:00
Zip CRC : 0x07b58c22
Zip Compressed Size : 426
Zip Uncompressed Size : 1764
Zip File Name : [Content_Types].xml
Title :
Subject :
Creator : Olivia Renshaw
Keywords :
Description :
Last Modified By : Olivia Renshaw
Revision Number : 2
Create Date : 2025:05:08 23:37:00Z
Modify Date : 2025:05:08 23:39:00Z
Template : Normal.dotm
Total Edit Time : 1 minute
Pages : 1
Words : 163
Characters : 933
Application : Microsoft Office Word
Doc Security : None
Lines : 7
Paragraphs : 2
Scale Crop : No
Company : Ravenbrook Police Department
Links Up To Date : No
Characters With Spaces : 1094
Shared Doc : No
Hyperlinks Changed : No
App Version : 16.0000Scripting
Knowing all of this, we create the following Python Script:
def decrypt(encrypted_text, key):
decrypted = ""
key_len = len(key)
for i in range(len(encrypted_text)):
key_index = (i + 1) % key_len
key_char = key[key_index]
original_char = chr(ord(encrypted_text[i]) ^ ord(key_char) ^ 123)
decrypted += original_char
return decrypted
key = "oliviarenshaw"
encrypted_strings = [
"Swywy}wcm3U`}a{l2Hlpf`rm",
"A{nfu{>Yi}}j{ev",
"%#:2Iaqgdy~qdf-Sll25Zrlizu`b}q%>[P3.<#/",
"q~luaj*-:\"#j!rs4v,k| <u-9'$wity8$9!.q",
"X~ddsh>Gm}idu`"
]
for enc in encrypted_strings:
print(decrypt(enc, key))To obtain the flag:
$> python3 solver.py
Detective Olivia Renshaw
Victor Langford
217 Shadowcrest Ave, Ravenbrook, NX 4078
flag{c48219f5ea9d6bb54f7533edfc1a1124}
Olivia RenshawCommon error
If you are trying by yourself and obtain a different output or the flag splitted, it may occur while copy and pasting the data, there is buggy byte: <0x7f>, I detected it with sublime text haha.
forensics/Bitlocked

Simple bitlocker bruteforce with an incompleted photo of the key recovery.

Mapping the device
As we have given an .001 file extension, we could use directly dislocker to the file but in that moment I colapsed and used: kpartx command to map the partitions from the disk image thus you can mount the device.
$> sudo kpartx -av SD_card.001
add map loop0p1 (254:0): 0 7854080 linear 7:0 8192PD: It will be located in
loop0p1if you are using UEFI mode, else it will be on asda1directory
Dislocker
Knowing the dislocker arguments to decrypt the device with the Recovery Key, we are able to create a python bruteforce script:
sudo dislocker -V <device.dd> -p<key> -- <Mount Point>Bruteforce Script
Once this, create the script.
import subprocess
import os
import shutil
keyBase = "718894-682847-228371-253055-328559-381458-030668-04"
file = "/dev/mapper/loop0p1"
mntPoint = "/tmp/dislocker_test"
os.makedirs(mntPoint, exist_ok=True)
for i in range(10000, -1,-1):
sufix = f"{i:04d}"
key = f"{keyBase}{sufix}"
print(f"Trying: {key}")
for f in os.listdir(mntPoint):
path = os.path.join(mntPoint, f)
try:
if os.path.isfile(path) or os.path.islink(path):
os.unlink(path)
elif os.path.isdir(path):
shutil.rmtree(path)
except Exception as e:
pass
result = subprocess.run(
["sudo", "dislocker", "-V", file, f"-p{key}", "--", mntPoint],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
if result.returncode == 0:
print(f"Key found!: {key}")
break
After some time bruteforcing from 049999 to 040000 we found the key was: 718894-682847-228371-253055-328559-381458-030668-047839
Opening autopsy
Finally with autopsy, or any related tool such as FTK Imager you will be able to locate flag.txt

forensics/ToShredYouSay

To be honest, I didn’t pwn this challenge it was my teammate tillamen from Caliphal Hounds. In the challenge they give you the following image:

And via the CTF logo you have to un-shred the image, so he printed the photo and do some manual art to obtain the following master piece:

Being the flag: flag{dd0755b73e4b7dfd0e06f927874e1511}. Congrats tillo!
forensics/The Wizard

File recognition
In this challenge a home directory is provided:
$> tar xvf thewizard.tgz
thewizard/
thewizard/.bashrc
thewizard/.bash_logout
thewizard/.pwfault
thewizard/.viminfo
thewizard/.profileThe only useful information was in .viminfo where it extract data from .pwfault to obtain the flag, anyways here the content of .viminfo:
# This viminfo file was generated by Vim 8.2.
# You may edit it if you're careful!
# Viminfo version
|1,4
# Value of 'encoding' when this file was written
*encoding=utf-8
# hlsearch on (H) or off (h):
~h
# Last Search Pattern:
~MSle0/?
# Last Substitute Search Pattern:
~MSle0~&.$
# Last Substitute String:
$}
# Command Line History (newest to oldest):
:q!
|2,0,1750169805,,"q!"
:%s/.$/}
|2,0,1750169794,,"%s/.$/}"
:%s/^...../flag{
|2,0,1750169789,,"%s/^...../flag{"
:s/dd/
|2,0,1750169783,,"s/dd/"
:%s/\n//
|2,0,1750169778,,"%s/\\n//"
:%s,[^a-f0-9],,g
|2,0,1750169774,,"%s,[^a-f0-9],,g"
::v/?/d
|2,0,1750169769,,":v/?/d"
# Search String History (newest to oldest):
? .$
|2,1,1750169794,,".$"
? ^.....
|2,1,1750169789,,"^....."
? dd
|2,1,1750169783,,"dd"
? \n
|2,1,1750169778,,"\\n"
? [^a-f0-9]
|2,1,1750169774,,"[^a-f0-9]"
? ?
|2,1,1750169769,,"?"
# Expression History (newest to oldest):
# Input Line History (newest to oldest):
# Debug Line History (newest to oldest):
# Registers:
""1 LINE 0
[=Pb7F0=GP2%G(zi!%h::,zLl&
|3,1,1,1,1,0,1750169770,"[=Pb7F0=GP2%G(zi!%h::,zLl&"
"2 LINE 0
wraC0,i0IkQN^,wIpKUgD:<g5#
|3,0,2,1,1,0,1750169770,"wraC0,i0IkQN^,wIpKUgD:<g5#"
"3 LINE 0
;0AHQQ8~<L{3^_z%/I/pkOdcSW
|3,0,3,1,1,0,1750169770,";0AHQQ8~<L{3^_z%/I/pkOdcSW"
"4 LINE 0
ml7@zu^fbae:OG7,{jPiiq[H3-
|3,0,4,1,1,0,1750169770,"ml7@zu^fbae:OG7,{jPiiq[H3-"
"5 LINE 0
IkW>#H>YXf>;\=S-Sa7aup=d3k
|3,0,5,1,1,0,1750169770,"IkW>#H>YXf>;\\=S-Sa7aup=d3k"
"6 LINE 0
y7q+01%Tk"V$#I_FK5ST;&Q$WE
|3,0,6,1,1,0,1750169770,"y7q+01%Tk\"V$#I_FK5ST;&Q$WE"
"7 LINE 0
J&<ou+;Q+5NCVI=i.mt^KRvvUE
|3,0,7,1,1,0,1750169770,"J&<ou+;Q+5NCVI=i.mt^KRvvUE"
"8 LINE 0
eE.u\Hg6"#Mem:4reCy[QB;:9K
|3,0,8,1,1,0,1750169770,"eE.u\\Hg6\"#Mem:4reCy[QB;:9K"
"9 LINE 0
Y{zM<!*32kpTPJ`R7bHUV~]3O$
|3,0,9,1,1,0,1750169770,"Y{zM<!*32kpTPJ`R7bHUV~]3O$"
# File marks:
'0 1 0 ~/.pwfault
|4,48,1,0,1750169805,"~/.pwfault"
# Jumplist (newest first):
-' 1 0 ~/.pwfault
|4,39,1,0,1750169805,"~/.pwfault"
# History of marks within files (newest to oldest):
> ~/.pwfault
* 1750169794 0
" 1 0
. 1 0
+ 2 0
+ 1 7
+ 2 0
+ 1 0Understanding .viminfo logic
The vim file follows this steps sequece, based in the command timestamps:
- :v/?/d -> delete lines NOT matching “?” (i.e., keep only lines containing ‘?’)
- :%s,[^a-f0-9],,g -> remove all characters not a-f or 0-9 (global)
- :%s/\n// -> remove all newlines (join lines)
- :s/dd/ -> remove the first occurrence of ‘dd’ on each line (here file is single line by now)
- :%s/^…../flag{ -> replace first 5 characters with ‘flag{’
- :%s/.$/} -> replace last character of the file with ‘}’
Scripting the logic
Thus create a Python Script:
import re
pwfault = open('.pwfault','r',encoding='utf-8', errors='ignore').read()
lines = pwfault.splitlines()
lines = [ln for ln in lines if '?' in ln]
step1 = "\n".join(lines)
step2 = re.sub(r'[^a-f0-9]', '', step1)
step3 = step2.replace("\n", "")
step4 = step3.replace("dd", "", 1)
if len(step4) >= 5:
step5 = 'flag{' + step4[5:]
else:
step5 = 'flag{'
if len(step5) >= 1:
final = step5[:-1] + '}'
else:
final = '}'
print("Lines kept (contain '?'):\n", step1, "\n")
print("After removing non-hex chars:\n", step2, "\n")
print("After joining lines (no newlines):\n", step3, "\n")
print("After removing first 'dd' occurrence:\n", step4, "\n")
print("After replacing first 5 chars with 'flag{':\n", step5, "\n")
print("Final result after replacing last char with '}':\n", final, "\n")
hex_candidate = re.fullmatch(r'[0-9a-f]+', final)
if hex_candidate:
try:
decoded = bytes.fromhex(final).decode('utf-8')
print("Decoded from hex ->", decoded)
except Exception as e:
print("Could not decode hex to UTF-8:", e)Having the following output:
$> python3 solver.py
Lines kept (contain '?'):
f)#-nA1~wex3z$Ix0*K^a0?ImR
.!Y}h.`:?u\GfM@6?d,]pZGdT>
jeZ!BS{Ur?pxt*q@$.?\S+jJKy
Vz#@^Fw?#Up|@LF8SrkL3UM[nX
OBk?"Ovlek;p1/lz$=iN(h./fI
P?1=C8Ud$Z-Ew;+z0Ap/shp4oc
W&k]&([X>tz*:1UTRZfs?Tg-sA
-CVwhU!L9C`}?K>W)!'-("Jmh"
XEjZ3V8M$3.vaFA30CB?DT7EDs
_5{1-%M:gl?^kO[%1vUI*WCBFV
6Q`$%_O/8wSHOewxq/p?|+~:9h
After removing non-hex chars:
f1e30a0f6dde83e1f18d04c1f9383a30751168e9
After joining lines (no newlines):
f1e30a0f6dde83e1f18d04c1f9383a30751168e9
After removing first 'dd' occurrence:
f1e30a0f6e83e1f18d04c1f9383a30751168e9
After replacing first 5 chars with 'flag{':
flag{a0f6e83e1f18d04c1f9383a30751168e9
Final result after replacing last char with '}':
flag{a0f6e83e1f18d04c1f9383a30751168e}Honestly a beautiful output for a second blood :)
forensics/IOT breach

Device recognition
Disk forensics! A DOS/MBR boot file with some ransomware attack. This time we will be using Autopsy to read the device content. At first glance, files directory is listed on the root system / with an index of .enc jpg images.

lighttpd access.log
As we didn’t find any suspicious file in /home and /root directories, and nothing relevant inside the bash history, I move on into services log, here the http service: lighttpd was interesting. In /var/log/lighttpd/access.log some base64 data was embedded in http requests.

Perl Script
With some regex filtering and cyberchef we obtain a base64 encrypyted payload:

From this perl code we obtain the cypher method and an IV
my $cipher = Crypt::Mode::CBC->new("AES");my $encrypted = $cipher->encrypt($data, $password, "R4ND0MivR4ND0Miv");Perl Execution | CBC key
Flexing my custom command we urldecode one http request with the $password used in the perl binary:
$> urldecode '10.0.0.1 10.5.5.246 - [11/Jun/2025:01:13:02 +0200] "GET /cgi-bin/ping.pl?hostname=a%3Bcd%20/files%20%26%26%20perl%20enc.pl%20L0s3%40llYourF1l3s HTTP/1.1" 200 1677 "-" "python-requests/2.21.0"'
10.0.0.1 10.5.5.246 - [11/Jun/2025:01:13:02 +0200] "GET /cgi-bin/ping.pl?hostname=a;cd /files && perl enc.pl L0s3@llYourF1l3s HTTP/1.1" 200 1677 "-" "python-requests/2.21.0"urldecode () {
echo -n $@ | python3 -c "import sys; from urllib.parse import unquote; print(unquote(sys.stdin.read()));"
}Being L0s3@llYourF1l3s the key from AES CBC.
Decrypting files
To conclude the challenge, you can create a fast bash script to decrypt all encoded files inside /files/ directory.
#!/bin/bash
PASS="L0s3@llYourF1l3s"
IV="R4ND0MivR4ND0Miv"
for i in $(seq -w 1 16); do
INFILE="kitten${i}.jpg.enc"
OUTFILE="kitten${i}.jpg"
if [[ -f "$INFILE" ]]; then
echo "[*] Decrypting $INFILE -> $OUTFILE"
openssl enc -d -aes-128-cbc \
-in "$INFILE" \
-out "$OUTFILE" \
-K $(echo -n "$PASS" | xxd -p) \
-iv $(echo -n "$IV" | xxd -p)
else
echo "[!] $INFILE does not exist, skipping..."
fi
doneThe file kitten08.jpg was the flag written.

Reversing Index

Sad ending, I still asking to organizers ctf for reversing files, but I don’t have any file from the category, only some solvers that I will share with you :). Anyways sorry for the bad writeups you will see forward, but I need to introduce reversing content in my web, at least on ctf blogs.
reversing/Lazy Code 1.0

From IDA we extracted the following loop-code and function:
Main code
for ( i = 1; i <= 1000; ++i )
{
printf("[+] Decrypting step %d/1000....\n", i);
xor_string(encrypted_flag, xors[i % 0x1BuLL]);
printf("[!] Yawn.... I'm tired... sleeping for %d seconds\n", sleeping_time);
sleep(sleeping_time);
}
printf("Pfff... I'm done, here is your flag: %s\n", encrypted_flag);
return 0;
}_BYTE *__fastcall xor_string(__int64 a1, char a2)
{
_BYTE *result; // rax
int i; // [rsp+Ch] [rbp-4h]
for ( i = 0; i <= 38; ++i )
{
result = (_BYTE *)(i + a1);
*result ^= a2;
}
return result;
}Data used in main code
Having the encrypted_flag value:
.data:0000000140003000 encrypted_flag db 15h, 1Fh, 12h, 14h, 8, 10h, 'J', 'E', 'C', 15h, 4 dup('F')
.data:0000000140003000 ; DATA XREF: main+7F↑o
.data:0000000140003000 ; main+C3↑o
.data:000000014000300E db 'G', 'K', 'D', 'B', 15h, 2 dup(16h), 2 dup(17h), 'D'
.data:0000000140003018 db 10h, 2 dup('@'), 12h, 'A', 'C', 15h, 11h, 'D', '@'
.data:0000000140003022 db 'J', 'B', 'J', 0Eh, 's', 19h dup(0)And xors value:
.data:0000000140003040 xors dd 12h, 2Bh, 47h, 76h, 66h, 4, 0Bh, 36h, 21h, 6Ch, 37h
.data:0000000140003040 ; DATA XREF: main+6D↑o
.data:000000014000306C dd 31h, 1Bh, 5Bh, 13h, 67h, 43h, 1Eh, 9, 27h, 8, 79h, 4Bh
.data:000000014000309C dd 0Dh, 3Dh, 52h, 5BhSolver written in C
We are able to create the following script to recreate the function functionality without having to wait any second..
#include <stdio.h>
unsigned char encrypted_flag[39] = {
0x15, 0x1F, 0x12, 0x14, 0x08, 0x10, 0x4A, 0x45, 0x43, 0x15,
0x46, 0x46, 0x46, 0x46, 0x47, 0x4B, 0x44, 0x42, 0x15, 0x16,
0x16, 0x17, 0x17, 0x44, 0x10, 0x40, 0x40, 0x12, 0x41, 0x43,
0x15, 0x11, 0x44, 0x40, 0x4A, 0x42, 0x4A, 0x0E, 0x73
};
char xors[27] = {
0x12, 0x2B, 0x47, 0x76, 0x66, 0x04, 0x0B, 0x36, 0x21, 0x6C,
0x37, 0x31, 0x1B, 0x5B, 0x13, 0x67, 0x43, 0x1E, 0x09, 0x27,
0x08, 0x79, 0x4B, 0x0D, 0x3D, 0x52, 0x5B
};
void xor_string(unsigned char *str, char key) {
for (int i = 0; i <= 38; ++i) {
str[i] ^= key;
}
}
int main() {
for (int i = 1; i <= 1000; ++i) {
xor_string(encrypted_flag, xors[i % 27]);
}
printf("Pfff... I'm done, here is your flag: %s\n", encrypted_flag);
return 0;
}reversing/Lazy Code 2.0

Same stuff than the first part but with different encrypted_flag value:
Main code
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+8h] [rbp-8h]
for ( i = 1; i <= 1000; ++i )
{
printf("[+] Decrypting step %d/1000....\n", i);
xor_string(encrypted_flag, xors[i % 0x1BuLL]);
printf("[!] Yawn.... I'm tired... sleeping for %d seconds\n", sleeping_time);
sleep(sleeping_time);
}
printf("Pfff... I'm done, here is your flag: %s\n", encrypted_flag);
return 0;
}_BYTE *__fastcall xor_string(__int64 a1, char a2)
{
_BYTE *result; // rax
int i; // [rsp+18h] [rbp-4h]
for ( i = 0; i <= 38; ++i )
{
result = (_BYTE *)(i + a1);
*result ^= a2;
}
return result;
}Encrypted Flag data
.data:0000000000004020 encrypted_flag db '* -+7.' ; DATA XREF: main+83↑o
.data:0000000000004020 ; main+CC↑o
.data:0000000000004026 db 7Fh ;
.data:0000000000004027 db 2Ah ; *
.data:0000000000004028 db 28h ; (
.data:0000000000004029 db 2Dh ; -
.data:000000000000402A db 78h ; x
.data:000000000000402B db 7Dh ; }
.data:000000000000402C db 7Ah ; z
.data:000000000000402D db 28h ; (
.data:000000000000402E db 2Dh ; -
.data:000000000000402F db 29h ; )
.data:0000000000004030 db 2Eh ; .
.data:0000000000004031 db 28h ; (
.data:0000000000004032 db 29h ; )
.data:0000000000004033 db 2Ah ; *
.data:0000000000004034 db 7Bh ; {
.data:0000000000004035 db 2Dh ; -
.data:0000000000004036 db 79h ; y
.data:0000000000004037 db 28h ; (
.data:0000000000004038 db 7Bh ; {
.data:0000000000004039 db 75h ; u
.data:000000000000403A db 28h ; (
.data:000000000000403B db 29h ; )
.data:000000000000403C db 2Eh ; .
.data:000000000000403D db 7Ch ; |
.data:000000000000403E db 7Bh ; {
.data:000000000000403F db 2Fh ; /
.data:0000000000004040 db 78h ; x
.data:0000000000004041 db 7Fh ;
.data:0000000000004042 db 7Fh ;
.data:0000000000004043 db 7Bh ; {
.data:0000000000004044 db 79h ; y
.data:0000000000004045 db 31h ; 1
.data:0000000000004046 db 4Ch ; L
.data:0000000000004047 db 0
.data:0000000000004048 db 0
.data:0000000000004049 db 0
.data:000000000000404A db 0
.data:000000000000404B db 0
.data:000000000000404C db 0
.data:000000000000404D db 0
.data:000000000000404E db 0
.data:000000000000404F db 0
.data:0000000000004050 db 0
.data:0000000000004051 db 0
.data:0000000000004052 db 0
.data:0000000000004053 db 0
.data:0000000000004054 db 0
.data:0000000000004055 db 0
.data:0000000000004056 db 0
.data:0000000000004057 db 0
.data:0000000000004058 db 0
.data:0000000000004059 db 0
.data:000000000000405A db 0
.data:000000000000405B db 0
.data:000000000000405C db 0
.data:000000000000405D db 0
.data:000000000000405E db 0
.data:000000000000405F db 0Xor keys data
.data:0000000000004060 public xors
.data:0000000000004060 ; unsigned int xors[27]
.data:0000000000004060 xors dd 17h, 2Bh, 57h, 6Fh, 7Ah, 0, 21h, 2Dh, 1Dh, 67h, 2Bh
.data:0000000000004060 ; DATA XREF: main+71↑o
.data:000000000000408C dd 56h, 16h, 63h, 5Bh, 67h, 38h, 1Dh, 0Bh, 0Ah, 3, 70h
.data:00000000000040B8 dd 47h, 12h, 36h, 0Ch, 63hSolver written in C
#include <stdio.h>
unsigned char encrypted_flag[39] = {
'*', ' ', '-', '+', '7', '.', 0x7F, '*', '(', '-', 'x', '}', 'z', '(', '-', ')', '.', '(', ')', '*', '{', '-', 'y', '(', '{', 'u', '(', ')', '.', '|', '{', '/', 'x', 0x7F, 0x7F, '{', 'y', '1', 'L'
};
char xors[27] = {
0x17, 0x2B, 0x57, 0x6F, 0x7A, 0x00, 0x21, 0x2D, 0x1D, 0x67, 0x2B, 0x56, 0x16, 0x63, 0x5B, 0x67, 0x38, 0x1D, 0x0B, 0x0A, 0x03, 0x70, 0x47, 0x12, 0x36, 0x0C, 0x63
};
void xor_string(unsigned char *str, char key) {
for (int i = 0; i <= 38; ++i) {
str[i] ^= key;
}
}
int main() {
for (int i = 1; i <= 1000; ++i) {
xor_string(encrypted_flag, xors[i % 27]);
}
printf("Pfff... I'm done, here is your flag: %s\n", encrypted_flag);
return 0;
}reversing/3 Ball Mark

Those who lie to others deceive themselves. I completed this challenge doing a manual bruteforce I tried 2 times to guess a number between 1 and 3, 10 times in a row, and obtained the flag, because it was always the same pattern. The solver of this challenge was literaly a bruteforce of the digits.
The 100 easiest points of my career
Conclusion
I hope you liked and enjoyed the writeups, see you in others CTFs playing with Caliphal Hounds! (and who knows maybe with Lil L3ak…)