# Pagination A:\> dir /p # WideListFormat A:\> dir /w # Pipingtomore A:\> dir | more
cat.exe (type)
1 2 3 4 5 6 7 8 9 10 11 12 13
DOS <DIR> 12-15-255:07p COMMAND COM 54,64505-31-946:22a WINA20 3869,34905-31-946:22a CONFIG SYS 7112-15-255:07p AUTOEXEC BAT 7812-15-255:07p FLAG TXT 6004-02-265:07a 6 file(s) 64,203 bytes 10,428,416 bytes free C:\>typeflag.txt pwn.college{QMkbfHdYCkFD_y-FmBdS66_cyD2.******************} C:\>
ls.exe (dir)
DOS filenames were case-insensitive
DOS assumed that every file had a name of 8 characters or less and an
"extension" (after the one allowed . in the filename) of 3 characters or
less.
there is no permission or ownership information
As a side note, extensions were critical in DOS for similar reasons
that they remain important in Windows: figuring out the type of file.
Specifically for DOS, programs needed to be named with extensions of EXE
(normal DOS executable), COM (simple, older executables that were
basically just raw binary code), and BAT (a batch script containing
commands to run, analogous to a weird variant of a typical shell
script).
DOS assigned floppy drives the A: and B: drives, and "mounting"
happened automatically on access (or, more specifically, there was no
filesystem "mounting"; access just happened). The two letters were for
computers that had both a 5.25" and a 3.5" floppy drive
Any boot process includes not just the loading of the OS itself, but
its initialization. In DOS, this was elegantly handled by autoexec.bat,
a script that specified what commands would run when the system
booted.
you can access the various menu items using your Alt key (e.g., Alt-F
to open the File menu).
FUN FACT: doskey was introduced in MS-DOS 5.0 (1991) and was a
game-changer for productivity. Before doskey, if you made a typo in a
long command, you'd have to retype the whole thing!
edit and add doskey at top of autoexec.bat and quit relaunch
PKTCHK COM 85601-28-929:58p PKTMODE COM 1,72801-28-929:58p PKTMULTI COM 1,82201-28-929:59p PKTRING COM 2,42001-28-929:59p PKTSEND COM 2,34601-28-929:59p PKTSTAT COM 1,69301-28-929:59p PKTTRAF COM 6,01901-28-929:59p TERMIN COM 1,41901-28-929:58p TRACE COM 2,12401-28-929:58p WINPKT COM 3,61701-28-929:59p 18 file(s) 116,641 bytes 192,512 bytes free A:\PKTDRVR>pcntpkint=0x60 PacketdriverforanPCNTPK, version 03.10 Packetdriverskeletoncopyright 1988-92, CrynwrSoftware. Thisprogramisfreesoftware; seethefileCOPYINGfordetails. NOWARRANTY; seethefileCOPYINGfordetails. Packetdriverisatsegment 0BC8 Interruptnumber 0xB (11) I/Oport 0xC000 (49152) MyEthernetaddressis 52:54:00:12:34:56 A:\PKTDRVR>
An investigator recovered a Linux disk image from a manuscript ward
workstation in Varanasi. The drive appears normal, but operators suspect
hidden transfer records were concealed using layered steganography and
encryption workflows.
Initial Exploration
The provided disk image (kashi_ritual_ledger.img) is an
ext4 filesystem.
1 2 3 4 5 6 7 8 9
❯ file kashi_ritual_ledger.img kashi_ritual_ledger.img: Linux rev 1.0 ext4 filesystem data, UUID=f83285e2-bb1a-4a34-bdec-b901cf985c4e, volume name "KASHI_LEDGER" (extents) (64bit) (large files) (huge files)
# Mount the image sudomkdir -p /mnt/kashi_ledger sudo mount -o loop,ro kashi_ritual_ledger.img /mnt/kashi_ledger
# Search for hidden files find /mnt/kashi_ledger -type f -name ".*"
Exploring the filesystem reveals several key files: -
challenge_runtime.json: Metadata containing passphrases and
AES parameters. - /home/pandit_ved/: User home directory
with Pictures/ward_scans, Notes, and a hidden
.archive_payloads folder. -
/deleted_mail_pool/: Contains .eml files
discussing "passphrase doctrine."
Metadata Analysis
Reading challenge_runtime.json provided the following
stages: - Stage 1 Steg Passphrase:trishul-lantern-braid - Stage 2 Steg
Phrase:ghat-manjari-copper-owl
Notes in home/pandit_ved/Notes/ritual_index_notes.md
hinted that the "hidden ledger capsule is in one scan that does not open
with the standard (Stage 1) phrase."
Steganography Extraction
There were four BMP scans in the ward_scans directory.
Using steghide with the Stage 1 passphrase:
1 2 3
for f in /mnt/kashi_ledger/home/pandit_ved/Pictures/ward_scans/*.bmp; do steghide extract -sf "$f" -p "trishul-lantern-braid" done
While most contained decoy files,
scan_midnight_index.bmp failed, indicating it required the
Stage 2 passphrase:
1 2
steghide extract -sf scan_midnight_index.bmp -p "ghat-manjari-copper-owl" # Output: wrote extracted data to "stage2_ledger.enc"
Locating the Flag
The extracted stage2_ledger.enc matched a file in the
hidden .archive_payloads directory. A corresponding
.txt file was also present:
We sent the same announcement to three servers for redundancy. Each
server has its own RSA key. Intercept all three — maybe you can piece
something together.
Initial Analysis
The challenge provides an output.txt containing an
exponent \(e=3\), three moduli (\(n1, n2, n3\)), and three ciphertexts (\(c1, c2, c3\)). By examining the data, we
notice:
Low Exponent:\(e =
3\) is very small.
Identical Ciphertexts:\(c1 = c2 = c3\). This means the raw message
\(M\) was not padded differently for
each server.
Magnitude of \(c\): The ciphertext \(c\) is significantly smaller than any of
the moduli \(n\).
In RSA, the encryption process is \(c = M^e
\pmod n\). Usually, \(M^e\) is
much larger than \(n\). However, if
\(M^e < n\), then the modulo
operation has no effect, and \(c =
M^e\).
Solution
Since \(c < n_i\) and \(e=3\), the message \(M\) can be recovered simply by calculating
the integer cube root of \(c\): \[M = \sqrt[3]{c}\]
Implementation
Using Python and the gmpy2 library, we can solve for
\(M\):
1 2 3 4 5 6 7 8 9 10 11 12 13
import gmpy2 from binascii import unhexlify
# Intercepted ciphertext c = 475436441896018898725156479190091126537849994697426945980826369000641892902004477923335055269088235139492237640527487698088281484953901383579636883543216552932099156009006828723690550706326538736801225046068870773990108130474408522838234755277972911893744937243892927414355347438993698991261629557719442242861719577879055371620865465785392597257968132649494474946507819896785671106833645551504301840437212737125
# Calculate the cubic root m, exact = gmpy2.iroot(c, 3)
if exact: # Convert integer to hex, then to ASCII flag = unhexlify(hex(m)[2:]).decode() print(f"Flag: {flag}")
Note: This challenge is a simplified version of Håstad's
Broadcast Attack. While Håstad's attack typically uses the Chinese
Remainder Theorem (CRT) to solve for \(M^e\) when \(M^e
> n_i\), the small size of the message relative to the key
size here allowed for a direct cubic root calculation.
Join the RITSEC CTF Discord server to get the most up-to-date
information about the competition. The flag can be found in the topic of
the #announcements channel, or in the CTF kickoff announcement.
Initial Analysis
The challenge points to the RITSEC CTF Discord server as a source for
competition updates and a hidden flag.
Solution
By checking the topic of the #announcements channel, the
flag is readily available.
A secret message has been passed down through generations since the
time of the great Mahabharata war. Legend says that every 64 years, the
keepers of this secret would encode the message once more to protect it
from those who might seek to misuse its power. The message has traveled
through 3136 years of history, from the ancient battlefields of
Kurukshetra in 3136 BCE to the dawn of the Common Era.
Initial Analysis
The challenge provides two main clues:
Mathematical Clue: The message has existed for 3136
years and was re-encoded every 64 years. \[\frac{3136}{64} = 49\] This suggests the
message has been recursively encoded 49 times.
File Inspection: The provided file
secret_message.txt is large (~59MB) and starts with the
characters Vm0wd2Qy..., which is a classic signature for
multiple layers of Base64 encoding.
Extraction & Decoding
We can use a Python script to iteratively decode the file 49 times.
Each layer of decoding reduces the file size until the final plaintext
flag is revealed.
defsolve(): # Read the initial encoded data withopen('secret_message.txt', 'r') as f: data = f.read().strip()
# Iteratively decode 49 times print("[*] Starting iterative Base64 decoding...") for i inrange(49): try: data = base64.b64decode(data).decode('utf-8') except Exception as e: print(f"[-] Error at iteration {i+1}: {e}") break
The challenge hint "Kashi kings hate 184" suggests that the height of
the image (currently 150) has been tampered with and should likely be
184.
Extraction & Decoding
Netpbm (PPM) files use a plain-text header. We can repair the height
by editing the file header. Opening the file in a hex editor or a text
editor like vim (using :set binary or simply
editing the ASCII header) reveals:
1 2 3 4
P6 284 150 255 [Binary Data...]
Change the height value from 150 to
184:
1 2 3 4
P6 284 184 255 [Binary Data...]
After saving the change, the image can be opened with a standard
image viewer to reveal the flag.
Generating primes is expensive. I optimized my key generation to be
twice as fast. The modulus is 4096 bits — perfectly secure.
Initial Analysis
The hint "optimized my key generation to be twice as fast" suggests
that instead of generating two distinct large primes, the author might
have reused the same prime (\(n =
p^2\)) or chosen two primes that are extremely close to each
other. This makes the modulus vulnerable to Fermat's Factorization
Method or simply taking the square root.
Extraction & Decoding
Given a 4096-bit modulus \(n\), we
can check if it's a perfect square or if its factors are close to \(\sqrt{n}\) by starting from \(\lfloor\sqrt{n}\rfloor\) and searching
downwards. Once factored, we calculate \(\phi(n)\), derive the private key \(d\), and decrypt the AES key which was used
to encrypt the flag.
import math import base64 from Crypto.Util.number import long_to_bytes, bytes_to_long from Crypto.Util.Padding import unpad from Crypto.Cipher import AES
# Modulus and public exponent n = 0x752a94a112ba0ce096f47934dac094d3d07b8c036613938142c0d4c15fc82692eee38d2457dd8d16472c4ddbe8cb5e2a6331e0ca0351094fc9516559768ebfe44509154d64116fa1fe1daf698413d37c9fe3406555f3190e29d99bee0cdd663d531c8e818f2686c7ad24338b4e93c6bfbd1b5a6dc5161316b2cb9ac1ae05a4ac43fdeb3b024b2e00dfcd87069ea1645996d9ad16ac3a9697414c17279112303a1d21136a99dc47628e15a3d6e18779de7aec310331dff1a81871b03e214de09f56c0f3de02f9f399be4ebc094f34b578d311a8b48e9c6cf2fa2f4e321f1dab0a99e5b9d99464c19452d9cc21544ac8e32fb9f13d1b2990758de0876de465cbd3632f846ef49fd7b97abee2ce529cfbc75a0d0792df6cc8091198134e9f646cf7d33c85c4ddd2c4b9a248c2c470d7369ebc7245bcec049455da2ceb742b26058514418398149d03cd1ad74a997375d0462a43e73aa62fc1f7e0dcc67e8f1559073074b9b8d3c37edfcfce67fd1822227933c5a14425d76119fc0a25da4c059761c86bc3077c4d096d9b9f6ae2faf728dbf24d48fa74d99c8c0d8780d2963ca9eccef0dd847ce22fc5b13793981257a9d4dd1af965e9baa5bd9fc4e3321cf8c6fd9871e342e5ae0dff19ab6e9fd8e14b5cc766b92df3306ef63af248b019528928644007c17e31918f9fdf10daeadc1eb8abeb6297bdc8f8e9b27c591f12159479 e = 65537
# Factor n p = math.isqrt(n) while n % p != 0: p -= 1 q = n // p
# Calculate phi if p == q: phi = p * (p - 1) else: phi = (p - 1) * (q - 1)
# Decrypt Flag iv = base64.b64decode("XSCnpZLyN1Oin7F67hOKWQ==") flag_ct = base64.b64decode("n+H1n3ezKEm0ulyLMcp/ShxLZAddKX7y848o/Lf/56qDev/DPBz+IRcJ14yHWGOuodMaMwyLZi9er7slNa+QMw==")
cipher = AES.new(aes_key, AES.MODE_CBC, iv) flag = unpad(bytes(cipher.decrypt(flag_ct)), AES.block_size).decode()
Wait, you forgot to give me the Advent calendar!?!?! And what is that
supposed to mean, I should have opened the doors up until today?? Ugh,
okay, then I'll start: Door 1
Investigation
The hint suggests that we need to visit the "doors" of the Advent
calendar. The URL structure was identified as:
A network analysis challenge where data was exfiltrated via DNS
queries.
Challenge Description
A network capture was obtained from an internal monitoring system
after suspicious activity was detected. The traffic appears mostly
benign, but analysts believe data was covertly exfiltrated during normal
communication.
Initial Reconnaissance
Checking the protocol distribution of the capture.pcap
file using tshark:
1
tshark -r capture.pcap -q -z io,phs
The output confirms that 100% of the traffic is DNS,
indicating that DNS is being used as a tunnel for exfiltration.
DNS Query Analysis
Extracting the DNS query names reveals two distinct patterns: 1.
Repetitive queries for common domains like kashi.com and
amazon.com (likely noise). 2. High-entropy subdomains under
.exfil.internal.
A forensics challenge involving network traffic analysis and IP
Time-to-Live (TTL) steganography.
Challenge Description
A packet capture was collected from an internal network segment
during routine monitoring. No alerts were triggered at the time, and the
traffic appears largely normal. Your task is to analyze the capture and
determine whether any meaningful information can be recovered.
Initial Analysis
The provided file ttl_stego.pcap contains a series of
ICMP Echo (ping) requests. While the payloads appear standard, the IP
Time-to-Live (TTL) values fluctuate between 64 and
65, suggesting binary data is encoded in these
variations.
The TTL values can be mapped to binary bits: - 64\(\rightarrow\)0 -
65\(\rightarrow\)1
We can extract the full sequence of TTLs and decode them using a
Python script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import sys
# Extract TTLs using: tshark -r ttl_stego.pcap -T fields -e ip.ttl > ttls.txt withopen('ttls.txt', 'r') as f: ttls = [int(line.strip()) for line in f if line.strip()]
# Convert TTLs to bits bits = "".join(['0'if t == 64else'1'for t in ttls])
# Convert bits to characters (8 bits per byte) flag = "" for i inrange(0, len(bits), 8): byte = bits[i:i+8] iflen(byte) == 8: flag += chr(int(byte, 2))
print(f"Decoded message: {flag}")
Flag
kashiCTF{ttl_stego_is_evil}
+++
SYSTEM STATUS: ACTIVEENCRYPTED SECTOR 7PRTS_TERMINAL_V2.0PROTOCOL: 0x2AENCRYPTED DATA STREAMSYSTEM: ONLINE