From 32704f28094bcb40059ec8550df64112513bbee8 Mon Sep 17 00:00:00 2001 From: cato447 Date: Sat, 28 Feb 2026 21:36:17 +0100 Subject: [PATCH] srdnlen2026 quals started --- 2026/srdnlen_quals/forensic/chapter1/fc.wcm | 140 ++++++++++++ 2026/srdnlen_quals/forensic/chapter1/img.qcow | 3 + 2026/srdnlen_quals/forensic/chapter1/solve.py | 18 ++ .../forensic/chapter1/writeup.md | 212 ++++++++++++++++++ 4 files changed, 373 insertions(+) create mode 100644 2026/srdnlen_quals/forensic/chapter1/fc.wcm create mode 100644 2026/srdnlen_quals/forensic/chapter1/img.qcow create mode 100644 2026/srdnlen_quals/forensic/chapter1/solve.py create mode 100644 2026/srdnlen_quals/forensic/chapter1/writeup.md diff --git a/2026/srdnlen_quals/forensic/chapter1/fc.wcm b/2026/srdnlen_quals/forensic/chapter1/fc.wcm new file mode 100644 index 0000000..a2f0af2 --- /dev/null +++ b/2026/srdnlen_quals/forensic/chapter1/fc.wcm @@ -0,0 +1,140 @@ +Application (WP; "WordPerfect"; Default; "EN") + +v0 := 0 +v1 := 1 +v3 := 128 +v4 := 7 +v5 := 13 + +k1 := v3 - v4 - 51 +k2 := k1 - v5 + v4 + v1 +k3 := k1 + v4 - 2 +k4 := k2 + v4 - 3 + +doclen := 38 +docbodyLen := 38 +Declare docbody[38] +docbody[1] := 206 +docbody[2] := 56 +docbody[3] := 8 +docbody[4] := 128 +docbody[5] := 209 +docbody[6] := 47 +docbody[7] := 2 +docbody[8] := 149 +docbody[9] := 202 +docbody[10] := 34 +docbody[11] := 95 +docbody[12] := 128 +docbody[13] := 226 +docbody[14] := 41 +docbody[15] := 92 +docbody[16] := 156 +docbody[17] := 142 +docbody[18] := 38 +docbody[19] := 51 +docbody[20] := 153 +docbody[21] := 137 +docbody[22] := 57 +docbody[23] := 51 +docbody[24] := 218 +docbody[25] := 211 +docbody[26] := 21 +docbody[27] := 88 +docbody[28] := 130 +docbody[29] := 201 +docbody[30] := 121 +docbody[31] := 30 +docbody[32] := 128 +docbody[33] := 137 +docbody[34] := 62 +docbody[35] := 93 +docbody[36] := 152 +docbody[37] := 142 +docbody[38] := 55 + +rhLen := 24 +Declare rh[24] +rh[1] := 80 +rh[2] := 113 +rh[3] := 171 +rh[4] := 195 +rh[5] := 17 +rh[6] := 89 +rh[7] := 222 +rh[8] := 175 +rh[9] := 32 +rh[10] := 97 +rh[11] := 153 +rh[12] := 187 +rh[13] := 68 +rh[14] := 2 +rh[15] := 204 +rh[16] := 127 +rh[17] := 136 +rh[18] := 19 +rh[19] := 85 +rh[20] := 224 +rh[21] := 250 +rh[22] := 60 +rh[23] := 109 +rh[24] := 144 + +doB64 := v0 +If (doB64 = v1) + Type (NToC(k3 + v5 + v4 + v1 + v1)) + Type (NToC(v3 - v4)) + Type (NToC(v3 - v5 - v4)) + Type (NToC(v3 - v5 - v4 - v1)) + Type (NToC(v3 - v5 - v4)) + Type (NToC(v3 - v5 - v5 + v1 + v1)) + Type (NToC(v3 - v5 - v5 + v1 + v1)) + Type (NToC(v5 + v5 + v4 - v1)) +EndIf + +Declare kArr[4] +kArr[1] := k1 +kArr[2] := k2 +kArr[3] := k3 +kArr[4] := k4 + +Type (NToC(k1 + v5 + v1)) +Type (NToC(v3 - v5 - v5 + v1 + v1)) +Type (NToC(v3 - v5 - v4 - v4)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(v3 - v5 - v4 - v1)) +Type (NToC(v3 - v5 - v4 - v4)) +Type (NToC(v3 - v4)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(k2 + k2 - v5 - v5 + v1)) +Type (NToC(v3 - v5)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(k2 + k2 - v5 - v5 + v1)) +Type (NToC(v3 - v5 - v4 + v1 + v1)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(v3 - v4 - v1 - v1)) +Type (NToC(v3 - v5 - v5 + v1 + v1)) +Type (NToC(k3 + v5 + v4 + v1 + v1)) +Type (NToC(v3 - v5 + v1)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(k2 + k2 - v5 - v5 + v1)) +Type (NToC(v3 - v5)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(v3 - v5 - v4)) +Type (NToC(v3 - v5 - v4 - v4)) +Type (NToC(k1 + v5 + v5 + v4 - v1)) +Type (NToC(v3 - v5 + v1)) +Type (NToC(13)) + +ForNext (ii; v1; doclen; v1) + bb := docbody[ii] + kb := kArr[(ii - v1) % 4 + v1] + Type (NToC((bb + kb) - 2 * (bb & kb))) +EndFor + +buf := "" +ForNext (jj; v1; rhLen; v1) + kb := kArr[(jj - v1) % 4 + v1] + rb := rh[jj] + buf := buf + NToC((rb + kb) - 2 * (rb & kb)) +EndFor \ No newline at end of file diff --git a/2026/srdnlen_quals/forensic/chapter1/img.qcow b/2026/srdnlen_quals/forensic/chapter1/img.qcow new file mode 100644 index 0000000..ec073bc --- /dev/null +++ b/2026/srdnlen_quals/forensic/chapter1/img.qcow @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b17566ea4baf5060e5bfc7813a70397d9dc8de852c71827ebb20e986ed5b4550 +size 1029701632 diff --git a/2026/srdnlen_quals/forensic/chapter1/solve.py b/2026/srdnlen_quals/forensic/chapter1/solve.py new file mode 100644 index 0000000..c7d7032 --- /dev/null +++ b/2026/srdnlen_quals/forensic/chapter1/solve.py @@ -0,0 +1,18 @@ +docbody = [206,56,8,128,209,47,2,149,202,34,95,128,226,41,92,156, + 142,38,51,153,137,57,51,218,211,21,88,130,201,121,30,128, + 137,62,93,152,142,55] + +# Known prefix "srdnlen{" +prefix = "srdnlen{" + +# Derive key bytes from known prefix +key = [] +for i, c in enumerate(prefix): + key.append(docbody[i] ^ ord(c)) + +print(f"Derived key bytes: {key}") +print(f"Derived key chars: {''.join(chr(k) for k in key)}") + +# Decode full docbody with cycling key +result = ''.join(chr(docbody[i] ^ key[i % len(key)]) for i in range(len(docbody))) +print(f"\nDecoded flag: {result}") diff --git a/2026/srdnlen_quals/forensic/chapter1/writeup.md b/2026/srdnlen_quals/forensic/chapter1/writeup.md new file mode 100644 index 0000000..c92bc8c --- /dev/null +++ b/2026/srdnlen_quals/forensic/chapter1/writeup.md @@ -0,0 +1,212 @@ +I mounted the filesytem via: +```bash +sudo qemu-nbd --connect=/dev/nbd1 img.qcow +sudo mount /dev/nbd1 /mnt/chall1 +``` + +Then I tried to look for interesting stuff. + + +``` +total 140K +drwxr-xr-x 20 root root 4,0K 27. Feb 04:11 . +drwxr-xr-x 6 root root 4,0K 28. Feb 19:34 .. +drwxr-xr-x 3 root root 4,0K 27. Feb 04:11 bin +drwxr-xr-x 2 root root 4,0K 27. Feb 04:10 boot +drwxr-xr-x 4 root root 4,0K 26. Feb 20:07 .cdl_amnt +drwxr-xr-x 5 root root 20K 27. Feb 05:20 dev +drwxr-xr-x 2 root root 4,0K 26. Feb 20:06 disks +drwxr-xr-x 56 root root 4,0K 27. Feb 05:20 etc +drwxr-sr-x 3 root games 4,0K 26. Feb 20:07 home +drwxr-xr-x 2 root root 4,0K 26. Feb 20:06 initrd +drwxr-xr-x 5 root root 4,0K 27. Feb 04:11 lib +drwxr-xr-x 2 root root 48K 26. Feb 20:05 lost+found +drwxr-xr-x 6 root root 4,0K 27. Feb 05:19 mnt +drwxr-xr-x 3 500 500 4,0K 27. Feb 04:11 opt +drwxr-xr-x 2 root root 4,0K 26. Feb 20:06 proc +drwx---r-x 4 root root 4,0K 27. Feb 05:19 root +drwxr-xr-x 3 root root 4,0K 27. Feb 04:11 sbin +drwxrwxrwt 5 root root 4,0K 27. Feb 05:20 tmp +drwxr-xr-x 16 root root 4,0K 26. Feb 20:06 usr +drwxr-xr-x 15 root root 4,0K 27. Feb 04:10 var +lrwxrwxrwx 1 root root 20 26. Feb 20:06 vmlinuz -> /boot/vmlinuz-2.2.16 +lrwxrwxrwx 1 root root 19 26. Feb 20:06 vmlinuz-debug -> boot/vmlinuz-2.2.16 +``` +In the initial phase I tried the easy stuff +- checking etc/passwd, etc/issue, the home directory of user davezero +- running `fd flag` hoping for an easy win +- tried strings img.qcow | rg srdnl +- reading bash_history of `root` and `davezero` with no special findings + +Then I turned to log analysis +- syslog files +- auth files +- apache logs +I found out that only root ever logged in. This explains the empty bash_history of davezero + +I found nothing interesting in the log files so I snooped around the file system. +The challenge stated that the hacker hid their secrets in old systems. I looked for anything that I did not recognize in modern Linux. +- `.cdl_amnt/` is a directory special to corel but had nothing interesting in it +- `/opt/smartmove` is a program special to corel to migrate settings from windows but seemed untouched +- I found `/var/lib/dwww/quickfind.dat` which seemed interesting but i then found out that this is standard debian stuff for manuals +- The `var/www` directory also did not have anything special +The rest of the filesystem looked pretty normal to me. + +At this point I was a bit confused what to do. I returned to the log files to see if I missed anything. + +Here I discovered the key to the challenge. I found the file /var/log/fc.wcm. +I thought of this as strange since `.wcm` is not a file ending i recognize. + +Opening the file revealed this: + +```bash +❯ cat fc.wcm +Application (WP; "WordPerfect"; Default; "EN") + +v0 := 0 +v1 := 1 +v3 := 128 +v4 := 7 +v5 := 13 + +k1 := v3 - v4 - 51 +k2 := k1 - v5 + v4 + v1 +k3 := k1 + v4 - 2 +k4 := k2 + v4 - 3 + +doclen := 38 +docbodyLen := 38 +Declare docbody[38] +docbody[1] := 206 +docbody[2] := 56 +docbody[3] := 8 +docbody[4] := 128 +docbody[5] := 209 +docbody[6] := 47 +docbody[7] := 2 +docbody[8] := 149 +docbody[9] := 202 +docbody[10] := 34 +docbody[11] := 95 +docbody[12] := 128 +docbody[13] := 226 +docbody[14] := 41 +docbody[15] := 92 +docbody[16] := 156 +docbody[17] := 142 +docbody[18] := 38 +docbody[19] := 51 +docbody[20] := 153 +docbody[21] := 137 +docbody[22] := 57 +docbody[23] := 51 +docbody[24] := 218 +docbody[25] := 211 +docbody[26] := 21 +docbody[27] := 88 +docbody[28] := 130 +docbody[29] := 201 +docbody[30] := 121 +docbody[31] := 30 +docbody[32] := 128 +docbody[33] := 137 +docbody[34] := 62 +docbody[35] := 93 +docbody[36] := 152 +docbody[37] := 142 +docbody[38] := 55 + +rhLen := 24 +Declare rh[24] +rh[1] := 80 +rh[2] := 113 +rh[3] := 171 +rh[4] := 195 +rh[5] := 17 +rh[6] := 89 +rh[7] := 222 +rh[8] := 175 +rh[9] := 32 +rh[10] := 97 +rh[11] := 153 +rh[12] := 187 +rh[13] := 68 +rh[14] := 2 +rh[15] := 204 +rh[16] := 127 +rh[17] := 136 +rh[18] := 19 +rh[19] := 85 +rh[20] := 224 +rh[21] := 250 +rh[22] := 60 +rh[23] := 109 +rh[24] := 144 + +doB64 := v0 +If (doB64 = v1) + Type (NToC(k3 + v5 + v4 + v1 + v1)) + Type (NToC(v3 - v4)) + Type (NToC(v3 - v5 - v4)) + Type (NToC(v3 - v5 - v4 - v1)) + Type (NToC(v3 - v5 - v4)) + Type (NToC(v3 - v5 - v5 + v1 + v1)) + Type (NToC(v3 - v5 - v5 + v1 + v1)) + Type (NToC(v5 + v5 + v4 - v1)) +EndIf + +Declare kArr[4] +kArr[1] := k1 +kArr[2] := k2 +kArr[3] := k3 +kArr[4] := k4 + +Type (NToC(k1 + v5 + v1)) +Type (NToC(v3 - v5 - v5 + v1 + v1)) +Type (NToC(v3 - v5 - v4 - v4)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(v3 - v5 - v4 - v1)) +Type (NToC(v3 - v5 - v4 - v4)) +Type (NToC(v3 - v4)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(k2 + k2 - v5 - v5 + v1)) +Type (NToC(v3 - v5)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(k2 + k2 - v5 - v5 + v1)) +Type (NToC(v3 - v5 - v4 + v1 + v1)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(v3 - v4 - v1 - v1)) +Type (NToC(v3 - v5 - v5 + v1 + v1)) +Type (NToC(k3 + v5 + v4 + v1 + v1)) +Type (NToC(v3 - v5 + v1)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(k2 + k2 - v5 - v5 + v1)) +Type (NToC(v3 - v5)) +Type (NToC(v5 + v5 + v4 - v1)) +Type (NToC(v3 - v5 - v4)) +Type (NToC(v3 - v5 - v4 - v4)) +Type (NToC(k1 + v5 + v5 + v4 - v1)) +Type (NToC(v3 - v5 + v1)) +Type (NToC(13)) + +ForNext (ii; v1; doclen; v1) + bb := docbody[ii] + kb := kArr[(ii - v1) % 4 + v1] + Type (NToC((bb + kb) - 2 * (bb & kb))) +EndFor + +buf := "" +ForNext (jj; v1; rhLen; v1) + kb := kArr[(jj - v1) % 4 + v1] + rb := rh[jj] + buf := buf + NToC((rb + kb) - 2 * (rb & kb)) +EndFor +``` + +This looks incredibly suspicious. To get started with things I chucked the file into Claude and asked it if it can decode it. +Prompt: `I found this strange looking file in a forensics CTF. Can you decode its contents? The flag format is srdnlen{*}` +Sadly Claude solved it in one go generating a simple script that bruteforces the key from the known `srdnlen{` flag prefix. + +Flag: `srdnlen{wh3n_c0r3l_w4s_4n_4lt3rn4t1v3}` + +Using AI in CTFs is a double edged sword because it can save you a lot of time but can take out the fun out of solving puzzles and hinders learning.