CTF spoilers ahead. Last Light was an Insane Sherlock challenge from Hack The Box's Holmes CTF 2026: The Reichenbach Directive. All identities, systems, addresses, and events described here belong to the fictional challenge environment.
| Event | Hack The Box Holmes CTF 2026 |
| Challenge | Last Light |
| Category | Sherlock |
| Difficulty | Insane |
| Team | CIAT Cybersecurity Club |
| Recognition | Team MVP |
I was already working on Silent Passenger, a Hard challenge, when one part of it brought the investigation to a crawl. I opened Last Light instead. Its rating? Insane. Just my type.
Sometimes crazy high difficulty challenges have a more distinct path. Albeit, it's a steep one. Last Light was technically nuanced. Categorizing the evidence into three sources helped the clear path emerge.
The C2 traffic, forged Kerberos ticket, and stolen access token were discovered in process memory.
The AD database identified a planted machine account. Windows event records then correlated the aforementioned account to resource based constrained delegation and a two-session password attack.
Isolating each question's meaning required the most brain RAM. Wrong answers were frequently supported by evidence. Hashes changed depending on where I placed the end of a recovered file. Different libraries converted the same Kerberos ticket into different ccache files. There was even a Windows token with several identifiers appearing to be the correct LogonID. Women's intuition and artifact correlation led the way.
The ten Last Light questions that appeared after most of the investigation was already complete.
Initial Memory Analysis
After preserving the evidence, memory triage commenced. Volatility identified a Windows Server 2019 domain controller captured on September 9, 2026 at 22:40:38 UTC.
One of these process tree processes was not like the others:
svc_bkup.exe PID 3376 Parent: services.exe
It was running as a Windows service from an administrative share:
\Device\Mup\dc02\ADMIN$\svc_bkup
Just because a service launches through an administrative share doesn't mean it's malicious, but the network evidence indicated this process as suspicious. PID 3376 maintained an outbound HTTP connection from the domain controller:
192.168.56.11:54162 -> 192.168.56.1:8818
Both addresses belong to the isolated challenge network. The connection between the service process and the remote listener was a key finding.
Recovering Command and Control Evidence
I used malfind to check the process, but it didn't return executable memory pages commonly associated with code injection. I instead moved to the full process dump and searched for network and application data.
The 443 MB dump contained the following HTTP paths:
/api/v1/status
/content.html
/updates/check.php
It also showed a fake Firefox 20 user agent and a repeating custom header:
X-Beacon-Id
Pro tip: X-Beacon-ID is a good one to search for in beacon and C2 traffic.
One request linked the process, network endpoint, custom header, and fake browser ID. Although malfind found no injected code, the process dump still exposed the beacon's HTTP traffic.
Decrypting the Agent Profile and Task Data
Strings etched the shape of the HTTP traffic. Profile decryption unveiled the process behavior.
In the dump, we found an Adaptix style profile, encrypted metadata, task frames, and command results. The recovered data detailed the compromised domain controller, account, process, callback paths, and server config. It also held BOF activity and LDAP enumeration information, including the default naming context and number of AD groups.
Instead of relying on the product name, identification was based on the protocol structure and behavior. The service running from ADMIN$ maintained an HTTP control channel, accepted tasks, returned results, and queried Active Directory.
Disclaimer: The public writeup is comprised of short, structural excerpts. The profile keys, task payloads, and reconstructed malware will remain private.
Finding the Planted Machine Account
The SYSTEM hive was used to analyze the ntds.dit file. One particular directory object used the same name as the suspicious service:
svc_bkup$
The machine account's SID ended in -1140. It lived in the default Computers container and carried the unassuming WORKSTATION_TRUST_ACCOUNT flag with no meaningful service principal name or logon history.
At this point, WORKSTATION_TRUST_ACCOUNT was the strongest connection. I kept Domain Computers in the evidence set until the event records declared its innocence.
Recovering the Correct Service Agent Hash
Challenge Question #1 asked for the SHA256 hash of the service agent. Several viable hashes failed.
Volatility recovered multiple memory versions of the executable. The DataSection dump was 106,496 bytes. The ImageSection dump was 107,520 bytes. Both were valid PE32+ files, but neither matched the original file.
I parsed the PE sections and unearthed that the raw executable was 103,936 bytes. The lightbulb turned on. The remaining bytes in both dumps were zeros added during memory reconstruction.
The challenge expected the raw PE hash. I'd been hashing the full dump.
I trimmed the DataSection artifact at the PE endpoint, then calculated the winning SHA256:
1e44c63950dd4fe1ffcc955c08d509132bcaddf87bbb08f5ea622d0533625274
The rejected hashes weren't lost causes. I had the right executable with the wrong file boundary.
Analyzing the Forged Kerberos Ticket
The Airbus volatility-kerberos plugin recovered an Administrator ticket here:
0x227473721b0
It was a TGT for krbtgt/CORE.DIOGENES.HTB. It used RC4 HMAC and a suspicious ten year lifetime, making it a strong golden ticket candidate.
The decrypted PAC contained an Extra SID ending in RID 519, representative of Enterprise Admins in the root domain:
S-1-5-21-3066635835-107521988-671693545-519
Removing RID 519 resulted in the root domain SID that made the challenge happy.
The ticket inspection also displayed two similar checksum fields. The correct KDC checksum:
b63742148ab83e91d38dd632b5b1fb99
Next up, the ServerChecksum. It was, of course, a different value. This was one more instance where, you guessed it, a technically valid value didn't correctly answer the question.
Reproducing the Accepted Ccache MD5
Question #2 asked for the memory address of the forged ticket along with the MD5 of its ccache. The address stayed the same. The ccache MD5 changed depending on which tool was used to write the file.
I converted the recovered ticket with Impacket 0.14 development code, minikerberos, and a manual correction. Even though the conversions produced what looked like valid ccache files, all of them had different MD5s. The lightbulb moment here? Thank you to source review. It showed the memory plugin read a 64 bit flag value but output this malformed 33 bit result:
0x150e00000
Each converter handled the value differently. Impacket 0.14 development code, minikerberos, and a manual correction provided seemingly valid hashes, but HTB rejected all three.
I phoned a friend aka requested an independent AI assisted review of the misbehaving converter. That review found the compatibility issue, compared serialization paths, and saved the day. One conversion repeat with Impacket 0.13.1 later, flag 0xa8700000, a 1,302 byte ccache file, and the correct MD5 were born:
dcf56965eee82fff4d381c6127834d72
To be fair, the Kerberos ticket wasn't invalid. The discordance between the flag metadata and the way each library wrote the ccache file was the root cause.
Identifying the Stolen Token and Thread
The focus shifted to Windows access tokens.
MemProcFS found thread 4968 inside PID 3376, which carried an impersonation token here:
ffffca8ddc130830
Since the challenge required truncated addresses, we had a flag-winning answer:
0xca8ddc130830
The LogonID required yet another fix. luid.txt in MemProcFS was a seemingly fitting value. Closer _TOKEN structure inspection revealed that it was the token's TokenId, not its AuthenticationId.
The key fields:
TokenId: 0x2fb4ef
AuthenticationId: 0xa616a
TokenType: 2
ImpersonationLevel: 2
OriginatingLogonSession: 0x3e7
What was the accepted answer, you ask?
2:0xa616a
My earlier submission used a real field from the right token, but mixed it with the wrong identifier. You live, you learn.
Correlating the RBCD Modification
We still needed an event showing the usage of the machine account from ntds.dit.
Raw EVTX file searches came up inconclusive. MemProcFS thankfully also recovered normalized event data in forensic/csv/eventlog.csv, and in that data, this DC02 attribute was modified:
msDS-AllowedToActOnBehalfOfOtherIdentity
The subject session, 0x3f213b, had an adjacent record identifying LogonType 3 for the same session.
This connected the planted svc_bkup$ machine account to resource based constrained delegation on the domain controller.
Glad I documented a failed raw EVTX search. One parser's empty result is another person's treasure, and those normalized memory records unveiled missed evidence.
Reconstructing the Password Attack
Finally on the last question, which described a password attack performed across two sessions.
There were several seemingly relevant Administrator password resets, but alas, they were unrelated.
The correct events: afenwick at 20:46:48 UTC. Event 4738 showed afenwick temporarily assigning jreed as its UPN, then shadily removing it during session 0x17c514. Event 4723 showed a new afenwick session changing jreed's password with reset session 0x17c569.
The accepted submission:
0x17c514:0x17c569
That netted our final flag.
Technical Takeaways
Last Light required equally careful reading of the evidence and question formatting. A memory dump of an executable might include padding not included in the original file. When libraries serialize metadata differently, the same Kerberos ticket can produce different ccache hashes. A valid token ID and authentication ID from the same Windows object can represent different things.
Event visibility is also contingent on the parser and evidence source being searched.
To add icing to the cake, I'd already completed most of that work before realizing the unfamiliar HTB CTF interface hid ten questions under an almost invisible dropdown.
I'm so glad I document like a lunatic.
Unlike my usual notes, these were controlled chaos. They preserved enough evidence (because I preserve all) to blaze through all ten questions once I realized they existed.
Accepted Answer Appendix
The accepted submissions are below for reference.
| Question | Accepted answer | Evidence |
|---|---|---|
| Q1 | 1e44c63950dd4fe1ffcc955c08d509132bcaddf87bbb08f5ea622d0533625274 |
Raw PE trimmed at its 103,936 byte endpoint. |
| Q2 | 0x227473721b0:dcf56965eee82fff4d381c6127834d72 |
Ticket address and Impacket 0.13.1 ccache serialization. |
| Q3 | b63742148ab83e91d38dd632b5b1fb99 |
PAC KDC checksum signature. |
| Q4 | 0xca8ddc130830 |
Truncated stolen _TOKEN address. |
| Q5 | 2:0xa616a |
ImpersonationLevel and _TOKEN.AuthenticationId. |
| Q6 | 4968 |
Thread carrying the stolen token. |
| Q7 | svc_bkup$:S-1-5-21-2253468260-689643353-167204612-1140 |
NTDS machine account record. |
| Q8 | S-1-5-21-3066635835-107521988-671693545 |
Root domain SID derived from the PAC Extra SID. |
| Q9 | 0x3f213b:3 |
RBCD modification session and LogonType. |
| Q10 | 0x17c514:0x17c569 |
Password preparation and reset sessions. |