Hackro[n] CTF 2025 — Jerry_h4ck
Hackro[n] CTF 2025 既有趣又安全,获得了第9名,提供了令人兴奋的Web、密码学、取证、杂项和Pwn挑战组合。作为一名网络安全和CTF爱好者,我将带您了解我使用的工具以及每个挑战的关键要点。无论您是想提高自己的技能,还是只是对Hackro[n]竞技场感到好奇,这份记录都提供了大量实用见解。
类别格式如下:
- Misc
- Threat Intel L1
- Crypto
- OSINT
- Forensic
- Web
- Threat Intel L2
- PWN
MISC
1. The Easy Flag
我编写了一个小脚本,只需运行挑战文件并捕获它在屏幕上打印的任何内容。然后,它检查输出是否看起来像flag(SPL{…}内的内容)。如果它只打印内部部分,脚本会将其放回正确的flag格式。这样,脚本不是挖掘挑战内部的所有混乱代码,而是简单地运行它并直接获取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
|
#!/usr/bin/env python3
import subprocess, sys, re
if len(sys.argv) > 1:
path = sys.argv[1]
else:
path = "easychallenge.py"
try:
proc = subprocess.run([sys.executable, path],
capture_output=True, text=True, timeout=10)
except Exception as e:
print("Failed to run:", e); sys.exit(2)
out = (proc.stdout or "").strip()
err = (proc.stderr or "").strip()
m = re.search(r"SPL\{[^}]+\}", out)
if m:
print("Flag:", m.group(0))
else:
if re.fullmatch(r"[A-Za-z0-9_\-]{4,}", out):
print("Flag: SPL{" + out + "}")
elif out:
print("Program output (no SPL{...} found):", out)
else:
print("No output. Stderr:\n", err)
|
这最终揭示了flag:SPL{spl_is_cr4zy}
2. Sanity Check
这是一个被欺骗的挑战,描述说漏洞在ticket中,但flag显然就在我们面前。
3. Tv Buzz
下载给定的.wav文件并在频谱图上分析,这将给你flag。
SPL{h1dden-1n-sp3ctrum}
4. Mail-Mist
在这个挑战中,我们得到了实例,其中有一个邮件服务器,我们必须向管理员提交邮件。所以我这样发送了邮件(test@gmail.com.<ostify.com>)这是一个burpsuite collaborator请求,我发送到邮件服务器,然后我们将检查collaborator(SMTP)请求将在Headers中包含flag。
5. Time Capsule
分析给定的Zip,有一个文件名forum_logs.txt,其中"otherguy"在谈论"comment":
1
2
|
<dead_drop42> yo, leaving the time capsule at old_portal
<otherguy> noted. comment contains pubkey id: ABC123
|
然后,如果你在pubkeys.asc文件中搜索"comment",你会发现:
1
2
3
4
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: OpenPGP 1.0
Comment: FE5WUEBeqzXL3V4xYsrs4QUh607lZ54M0kQ0sNQUF8WgpUanxbUEeto+9jS7gTCDjXpZDXJElTUDult29e4UzeS3KWUjzwtRNtEZMhjbl5pDlqB0RiJihZnxihu5UCwGJx9cuaEYZclcQTI3lbs1+kq/BpHsvhag+K8tYTWJ2l+Kipym1G56g711FbgCuXk9g=
-----END PGP PUBLIC KEY BLOCK-----
|
在Base58中解码这个字符串后,我们将得到以下字符串:s3cr3t-pass-2025
现在,我们使用这个解码后的字符串作为密码,用隐写术分析drop.jpg文件,我们发现 ->
最后一步是借助这个字符串"ZipUp"作为密码提取flag.zip,最终得到flag.txt……
SPL{0001111time_capsule_of_lies]
因此挑战解决!!
Threat Intel L1
1. Copy Paste
我首先打开提供的服务器日志并扫描任何异常。大多数行是常规的Minecraft/Forge mod加载消息,但在噪音深处我发现了一个奇怪的条目:
com/client/bin cswitch student1 01110011 00110011 01100011 01110010 00110011 01110100 00100001
这立即引起了注意,因为:
- 它包含二进制值(0110xxxx),在游戏服务器日志中通常看不到
- 字符串student1看起来像是一个潜在的用户名
解码Payload,然后我复制二进制序列并将每个字节转换为文本:
1
2
3
4
5
6
7
|
01110011 → s
00110011 → 3
01100011 → c
01110010 → r
00110011 → 3
01110100 → t
00100001 → !
|
我将用户名和密码组合生成flag:SPL{student1:s3cr3t!}
2. Dns Sluth
为了追踪可疑子域,我首先使用WHOIS记录检查了主域secpen.org的注册详细信息,以确认它是一个真实域。由于攻击者通常创建虚假子域而不是直接使用主站点,真正的线索隐藏在被动DNS历史记录中,该记录保存了过去子域及其指向的服务器的记录。通过扫描此历史记录并搜索不寻常的单词"hackorn"(如网络钓鱼电子邮件中所述),我发现一个名为support-hackorn.secpen.org的子域在2025年5月短暂出现然后消失。这表明网络钓鱼电子邮件与一个短暂且可能是恶意的子域有关。该过程突显了调查域历史记录和搜索奇怪模式如何帮助发现在线诈骗中使用的临时设置。
揭示的flag:SPL{support-hackorn.secpen.org}
3. Mal Medium
我首先运行strings strings.txt | grep -i flag
查看是否有任何明显的内容隐藏,并迅速发现一个长的base64字符串:ZmxhZ3ttYWxpY2lvdXMtb3BzLnNlY3Blbi5uZXR9
。用echo 'ZmxhZ3ttYWxpY2lvdXMtb3BzLnNlY3Blbi5uZXR9' | base64 -d
解码给了我flag{malicious-ops.secpen.net}
。为了确认它不仅仅是一个诱饵,我使用grep -i "malicious-ops" network.log
搜索网络日志,并发现了重复的DNS查询到该确切域。最后,我使用VT_report进行了交叉检查,该报告将文档标记为使用PowerShell进行payload交付的宏投放器。这些组合步骤显示了感染路径 - 恶意文档投放了一个payload,然后向攻击者的命令和控制服务器发出信标 - 解码的字符串揭示了挑战flag。
反射的Flag:SPL{malicious-ops.secpen.net}
CRYPTO
1. Encty
凯撒移位 - 将每个字母字符向前移动2个位置(凯撒+2)将QNJ{…}转换为正确的格式。
1
2
3
4
5
6
7
8
9
10
11
12
|
hex_input = "51 4E 4A 7B 7A 39 32 39 39 64 31 79 30 31 38 35 32 39 36 31 61 7A 79 63 31 31 39 79 30 35 32 63 39 36 79 79 37 35 33 32 38 32 63 31 36 63 31 64 35 79 37 39 61 34 30 35 31 7A 63 30 32 32 30 39 38 38 38 33 7D"
s = bytes.fromhex(hex_input.replace(" ", "")).decode('ascii')
def shift_plus_two(ch):
if 'a' <= ch <= 'z':
return chr((ord(ch) - ord('a') + 2) % 26 + ord('a'))
if 'A' <= ch <= 'Z':
return chr((ord(ch) - ord('A') + 2) % 26 + ord('A'))
return ch
decoded = ''.join(shift_plus_two(c) for c in s)
print(decoded)
|
这个脚本将为你制作flag:SPL{b9299f1a01852961cbae119a052e96aa753282e16e1f5a79c4051be022098883}
2. Train Enc
在这个挑战中,我们被给予一个长的、混乱的字符串,看起来像是字母、数字和内部一小段{ }的混合。任务是找到格式为SPL{…}的隐藏flag。起初,文本看起来像随机的十六进制代码,但简单的解码不起作用。在测试了不同的经典密码和模式后,关键结果是一个栅栏置换 - 一种方法,其中消息以锯齿形模式跨多个"轨道"(行)写入,然后逐行读取以打乱它。通过用10个轨道反转这个过程,混乱的文本揭示了真正的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
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
|
#!/usr/bin/env python3
from typing import List
# --- Input (the ciphertext payload we were given) ---
ciphertext = "S96fP0226b9L7bcf40{64e5d403d74ad}946ac55fa316df5b0671c5495795b23596eb"
# --- Rail-fence decrypt implementation (inverse of zigzag encryption) ---
def rail_fence_decrypt(cipher: str, rails: int) -> str:
if rails <= 1:
return cipher
n = len(cipher)
# Build zigzag pattern (rail index for each plaintext position)
pattern: List[int] = []
rail = 0
direction = 1
for _ in range(n):
pattern.append(rail)
rail += direction
if rail == rails - 1 or rail == 0:
direction *= -1
# Count how many chars go to each rail
counts = [pattern.count(r) for r in range(rails)]
# Split ciphertext into the rail pieces (row-by-row)
parts: List[str] = []
idx = 0
for c in counts:
parts.append(cipher[idx: idx + c])
idx += c
# Reconstruct plaintext by taking characters following the pattern
positions = [0] * rails
out_chars: List[str] = []
for p in pattern:
out_chars.append(parts[p][positions[p]])
positions[p] += 1
return ''.join(out_chars)
# --- Try multiple rails and look for SPL{...} ---
def find_flags_by_rail(cipher: str, min_rails=2, max_rails=16):
hits = []
for r in range(min_rails, max_rails + 1):
dec = rail_fence_decrypt(cipher, r)
if "SPL{" in dec:
# Extract the first brace-enclosed substring starting at SPL{
i = dec.find("SPL{")
j = dec.find("}", i)
flag_excerpt = dec[i:j+1] if (i != -1 and j != -1) else dec[i:i+80]
hits.append((r, flag_excerpt, dec))
return hits
if __name__ == "__main__":
print("Ciphertext:", ciphertext)
hits = find_flags_by_rail(ciphertext, min_rails=2, max_rails=16)
if not hits:
print("No 'SPL{' found for rails 2..16.")
else:
print(f"Found {len(hits)} candidate(s):\n")
for r, frag, full in hits:
print(f"--- rails = {r} ---")
print("Extracted fragment:", frag)
print("Full decrypted text:\n", full)
print()
|
SPL{09a0597634367092b4d61796516a7ec266f54cdcbe25f5ad4bf904d5543b59bf}
3. SwArmy
这个Python脚本旨在破解一个谜题,其中秘密flag隐藏在用瑞典军事音标字母说的消息中。在这个系统中,每个字母和数字都被替换为瑞典名称或单词(如Adam代表A,Nolla代表0等)。脚本获取长的编码消息,将其分解为单词,并使用内置列表将每个单词匹配回其真实的字母或数字。然后它将所有内容拼凑在一起,并将其包装在CTF格式SPL{…}中以揭示最终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
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
|
#!/usr/bin/env python3
def solve_swedish_crypto_challenge():
swedish_alphabet = {
'Adam': 'A', 'Bertil': 'B', 'Caesar': 'C', 'David': 'D', 'Erik': 'E',
'Filip': 'F', 'Gustav': 'G', 'Helge': 'H', 'Ivar': 'I', 'Johan': 'J',
'Kalle': 'K', 'Ludvig': 'L', 'Martin': 'M', 'Niklas': 'N', 'Olof': 'O',
'Petter': 'P', 'Qvintus': 'Q', 'Rudolf': 'R', 'Sigurd': 'S', 'Tore': 'T',
'Urban': 'U', 'Viktor': 'V', 'Wilhelm': 'W', 'Xerxes': 'X', 'Yngve': 'Y',
'Zäta': 'Z', 'Åke': 'Å', 'Ärlig': 'Ä', 'Östen': 'Ö'
}
swedish_digits = {
'Nolla': '0', 'Ett': '1', 'Tvåa': '2', 'Trea': '3', 'Fyra': '4',
'Femma': '5', 'Sexa': '6', 'Sju': '7', 'Åtta': '8', 'Nia': '9'
}
swedish_mapping = {**swedish_alphabet, **swedish_digits}
cipher_text = "Sexa Trea Nolla Caesar Tvåa Filip Ett Caesar Nolla Erik Erik Ett Bertil Åtta David Sju David Adam Femma Sju Caesar Filip Åtta Nia Trea Sexa Adam Erik Sju Erik Sju Åtta Tvåa Sju Fyra Adam Bertil Adam Nolla Bertil Filip David Sju Sexa Femma Filip Erik Ett Nolla Adam Tvåa Nolla David Filip Erik Femma Åtta Nolla Filip Nia Erik Erik Caesar Caesar"
print("=== Swedish Phonetic Alphabet Crypto Challenge Solver ===")
print(f"Cipher text: {cipher_text}")
print()
words = cipher_text.split()
print(f"Total words to decode: {len(words)}")
print()
decoded = []
print("Decoding process:")
for i, word in enumerate(words, 1):
if word in swedish_mapping:
decoded_char = swedish_mapping[word]
decoded.append(decoded_char)
print(f"{i:2d}. {word:8s} -> {decoded_char}")
else:
print(f"{i:2d}. {word:8s} -> UNKNOWN")
decoded.append('?')
decoded_string = ''.join(decoded)
print()
print(f"Decoded string: {decoded_string}")
flag = f"SPL{{{decoded_string}}}"
print(f"Final flag: {flag}")
print()
return flag
def verify_flag(flag):
print("=== Verification ===")
print(f"Flag format correct: {'✓' if flag.startswith('SPL{') and flag.endswith('}') else '✗'}")
print(f"Flag length: {len(flag)} characters")
print(f"Content length: {len(flag) - 5} characters (excluding SPL{{...}})")
content = flag[4:-1] # Remove SPL{ and }
is_hex = all(c in '0123456789ABCDEFabcdef' for c in content)
print(f"Content is hexadecimal: {'✓' if is_hex else '✗'}")
if is_hex:
print(f"Hex length: {len(content)} characters ({len(content)//2} bytes)")
return is_hex and flag.startswith('SPL{') and flag.endswith('}')
if __name__ == "__main__":
flag = solve_swedish_crypto_challenge()
is_valid = verify_flag(flag)
print("=" * 50)
if is_valid:
print("🎉 SUCCESS! Flag is ready to submit:")
print(f" {flag}")
else:
print("❌ ERROR: Flag validation failed")
print("=" * 50)
|
SPL{630C2F1C0EE1B8D7DA57CF8936AE7E78274ABA0BFD765FE10A20DFE580F9EECC}
OSINT
1. Deterrence-01
所以,这里来了OG挑战,只有Luck/Gemini帮助我解决这些类型的挑战……
正如给定的问题 - 一篇泄露的2007年调查报告提到了一类用于防止未经授权武器引爆的技术"Permissive Action Links"。
任务:确认首字母缩略词并识别文章中提到的回忆录 -> 我寻求了Gemini的帮助,它告诉我"acronym"词是 -> PAL 而回忆录{1234} -> 很可能这是一个年份
制作的flag是:SPL{PAL_2006}
2. Bardeck
这个挑战给出了一个.pdf,其中有一个条形码、条形码编号和下面一些brainfuck编码的消息。
当我分析那个brainfuck消息时,它说"Rabbit Dbance over the Train",这是一个诱饵。然后从这个网站https://www.gs1.org/services/verified-by-gs1 分析了条形码编号,揭示了产品名称。
正确的flag制作是:SPL{CatchClubSoda}
3. The forgotten packet
从discord服务器找到了这个https://hackorn.secpen.org/the-forgotten-packet-21b41c7da72e,并在评论中找到了flag。
但这并不是完美的flag,然后在这里我使用了救生员(GPT),它解码了flag。
SPL{jack_n_j1ll}
Forensic
1. Photo Memories
将给定的.jpg文件提交到这个https://futureboy.us/stegano/decinput.html,它简单地反映了flag。
揭示的flag:SPL{2dfa898b8659508904d0c321d3139e4885a441442d7b587e938b31fa84e0d678}
2. FileNOM
挑战中给出了两个文件BSN.wav和BSO.wav,首先我分析了BSN.wav,有一个声音说(something…. do you know about base64),我认为这是一个让我们徘徊的把戏。然后,我分析了第二个BSO.wav,在那里我发现了一些可能是flag的东西。
SPL{991253426721BSN76}
3. Slxughter
挑战提供的ZIP存档包含两个文件:
part1.txt 揭示了一个不完整的字符串,类似于flag的开始:SPL{brok3n_h3arts_
分析第二个文件part2a.txt 显示为一串不可打印/编码的字节。
将原始字符转换为它们的字节值得到:1e 11 1b 20 19 0d 1e 18
用0x7f进行简单的XOR分析将这些字节转换为可读的ASCII。
解码的字符串是:and_frag
组合Part A和解码的Part B得到:SPL{brok3n_h3arts_and_frag
在这之后,我进行了一个非常大胆的猜测,并直接在谷歌上搜索,它给了我这个:
这是我设法解决挑战的方式,最终制作的flag是 - SPL{brok3n_h3arts_and_fragment}
WEB
1. Filter Fiasco
这也有一个棘手的描述,其中写道不使用暴力破解,但当我用基本的用户名:密码进行暴力破解时,它给了我200ok响应和flag:
用户名:admin
密码:admin123
SPL{27ff7dc84296b8bbec0bf9797df03a12}
2. Back Time
如描述中所述,默认凭据是admin:ad***,但在获取提示后:“默认凭据中的某些空格导致执行流程”
凭据如下:
用户名:admin(空格)
密码:admin
SPL{87b441720681ba85662b69df1bd41711}
3. Secrets in the Shadows
这个挑战在以下URL中存在LFI漏洞:http://64.227.171.211:8080/?page=../../../../../../../etc/passwd
然后,在尝试了一些根据CTF的常见LFI payloads后,我在../../../../tmp/flag.txt中找到了flag。
Threat Intel L2
1. Phantom Domain
在给定的.zip中,有五个文件给我们,正如描述所说"您的SOC拦截了一封指向https://secure-login[.]xyz的可疑网络钓鱼电子邮件。任务:关联它们以找到攻击者的隐藏子域,其中包含最终flag"
有一个文件given ctlogs.json,在这里我只是简单地搜索了字符串"flag",它给了我包含flag字符串的输出名称。
SPL{internal-login.flagcorp.net}
这可能是正确的flag,因此挑战解决!!
PWN
1. AD-DCjson
在这个挑战中,我使用了一个脚本,它获取flag.txt的数据,脚本是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#!/usr/bin/env python3
from impacket.smbconnection import SMBConnection
target = "13.71.126.207"
username = "developer"
password = "C|>@|#v863u)zl9G^&4}8kHl"
share = "flag"
remote_file = "\\flag.txt"
c = SMBConnection(target, target, sess_port=445)
try:
c.login(username, password)
with open("flag.txt", "wb") as out:
c.getFile(share, remote_file, out.write)
print("Saved flag.txt")
finally:
try: c.logoff()
except: pass
try: c.close()
except: pass
|
这个脚本给了我这些数据/凭据:
1
2
3
4
|
Hello Participant, Nice to See You here ;
Your Credentitals :
svc-flag:2<wN5cf4Zhu£mr!tP"5*,4v]
{Hint: PS Remoting can sometimes be useful for gaining an initial foothold.} @localh0ste
|
现在,借助用户svc-flag的这些凭据,我使用了一个名为evilwinrm的工具,我使用的命令是:evil-winrm -i <IP_or_Hostname> -u svc-flag -p '2<wN5cf4Zhu£mr!tP"5*,4v]'
-> 然后连接到用户(svc-flag)后,我发现有两个需要密码连接的管理员用户 -> 然后我在Searches目录中找到了一个文件,这给了我BSN — F用户的凭据 -> 然后,我找到了文件flag.txt,这是一个诱饵,在Desktop目录中找到文件名user.txt -> 但在进一步探索后,我在Favourite/important.txt中找到了管理员(localh0ste)的凭据 -> 然后连接到主管理员后,flag在Desktop目录中,因此挑战解决!!
(不幸的是无法拍摄截图,因为实例已关闭)
2. Manna Flow
— Credit-rohan0sh(first-blood)
基本枚举
-
访问了http://139.59.39.250/并在那里找到了静态内容……没什么特别的……除了页面末尾的html注释<! — @localh0ste > Keep Note :Email : vikram.pawar@bsidesnoida.in →
-
对顶部端口进行Nmap扫描,发现同一主机/ip上运行着端口3000的rocket chat。
Rocket Chat RCE
-
Rocket chat实例允许任何人注册账户,所以我注册了一个账户
a. 用户名:pew
b. 电子邮件:needadummymail@gmail.com
-
再次在#general频道看到关于管理员用户电子邮件地址的相同消息 - vikram.pawar@bsidesnoida.in
-
尝试查找其他用户、频道、机器人 - 但什么都没有……
-
由于挑战作者正在推动了解管理员用户电子邮件地址的重要性,我开始在Rocket Chat中寻找零日问题, mostly around privilege escalation when admin user’s email is known
-
找到了sonarsource研究和exploit-db上的一个漏洞利用
-
理解了泄漏用户详细信息的NoSQL注入漏洞利用链。exploit-db漏洞利用将不起作用,因为它有硬编码的约束,比如它认为管理员用户的用户名是admin
,但在我们的情况下是localh0ste
-
此外,exploit-db漏洞利用试图泄漏管理员用户的基于TOTP的秘密以绕过2FA。但是,我发现localh0ste用户有基于电子邮件的MFA - 在NoSQLi中使用了this.services.email2fa.enabled
小工具
-
忘记了vikram.pawar@bsidesnoida.in
的密码 -> 然后泄漏重置密码令牌。
-
更改了管理员用户的密码
-
最初无法绕过2FA,但找到了这个hackerone报告并绕过了MFA,获得了管理员用户的令牌
-
按照sonarsource博客中描述的相同步骤 - 关于利用启用了脚本的传入webhooks - 进行RCE。即使在这里,直接的exploit-db漏洞利用也不起作用(可能是沙盒保护或其他原因)。我编写了脚本并获得了RCE。
要清楚,在我完成后。我已经删除了由我创建的所有这些资源 - 在屏幕截图中可见 - 包括okok
频道、ohok
webhook集成等。
由Jerry_H4ck编写的记录,感谢阅读,稍后与更多CTF记录再见。