之前两篇有整理了通道构建、主机发现、端口探测和权限提升之类的。一般情况下,不一定需要纯靠漏洞进行下一步操作,有时在一台内网的机器上能否继续扩大利用面,取决于能不能找到一些可复用的凭证。

内网里有一个很现实的问题,就是很多系统之间不是完全隔离的。Web 应用要连数据库,定时任务要调内部接口,CI/CD 要拉代码、推镜像,运维脚本要登录服务器,容器要读取环境变量,Kubernetes 要挂载 Secret。只要这些凭据管理得不够好,一台机器上的线索就可能变成访问另一套系统的入口。

0. 凭据在内网里的价值

0.1 为什么内网很多时候靠凭据继续走

外网打点时,漏洞往往是入口。但进入内网以后,凭据的价值会明显变高。归纳起来主要是两类原因:

  1. 横向调用关系密集:内网系统之间相互依赖,Web 调数据库,应用调中间件,CI/CD 调发布系统,几乎每条调用链都需要认证。
  2. 凭据生命周期管理差:开发为了方便把账号写在配置里,运维为了省事在多套环境里复用密码,老的备份和脚本里的凭据没人轮换,本地管理员、API Token、AccessKey 经常被多个系统共用。

因此尝试获取凭证也是能够让内网中的攻击面扩大的一个很好的方式。

0.2 凭据和凭据线索的区别

这里要先区分两个概念:凭据凭据线索

凭据是可以直接用于认证的东西,例如:

  1. username + password
  2. SSH private key
  3. 数据库账号密码
  4. Bearer Token
  5. Cookie
  6. AccessKey ID + Secret
  7. Git Token
  8. CI/CD Token

凭据线索则不一定能直接登录,但能帮助判断认证关系,例如:

  1. 数据库地址
  2. Redis 地址
  3. 内部 API 地址
  4. 用户名
  5. 租户 ID
  6. AK ID
  7. Token 前缀
  8. 配置项名称
  9. 服务名
  10. 数据库名
  11. 环境名

比如如果在 log 里看到下面这种内容:

1
2
3
spring.datasource.url=jdbc:mysql://10.10.5.20:3306/order_center
spring.datasource.username=order_readonly
spring.datasource.password=********

这里 usernamepassword 是凭据,10.10.5.203306order_center 是凭据线索。线索本身不能直接认证,但它能告诉你这个凭据应该去哪里验证、可能属于哪个业务、权限范围大概是什么。

这一点很重要。因为很多情况下如果没有判断作用范围,拿到一个密码就到处试,结果触发账号锁定、登录告警,甚至影响业务系统。

0.3 不同类型凭据的区别

内网里常见凭据不能混在一起看,它们的验证方式、风险和复用路径都不一样。

类型 常见形式 主要用途 风险点
密码 username/password 系统登录、后台登录、数据库登录 容易被复用,容易触发失败锁定
Hash NTLM Hash、密码摘要 Windows 认证、历史泄露分析 在 NTLM 认证场景下等同于密码可直接复用(PtH),跨协议时需还原明文
Token Bearer Token、API Token、Git Token 调用 API、访问代码仓库、CI/CD 可能不需要密码和 MFA
Cookie JSESSIONIDSESSION Web 会话认证 有时可直接代表登录态
私钥 SSH private key、证书私钥 SSH 登录、服务间认证 一旦无口令或口令泄露,影响很大
连接串 JDBC、Mongo URI、Redis URI 数据库或中间件连接 往往同时包含地址、账号、密码
AccessKey AK/SK、Secret Key 云平台、对象存储、消息队列 权限边界取决于云 IAM 策略
Kerberos 票据 TGT (kirbi/ccache)、Service Ticket Windows 域认证 可用于 PtT,部分票据可离线爆破
配置型 Secret Kubernetes Secret、CI/CD Secret 容器、流水线、服务启动 容易影响整条发布链路

1. 常见凭据来源

1.1 Web 应用配置文件

Web 的配置文件是凭据活着的一个来源之一,常见的一些文件名:

1
2
3
4
5
6
7
8
9
.env
application.yml
application.properties
bootstrap.yml
config.php
database.php
settings.py
jdbc.properties
config.json

在这些配置文件中,通常都可能会存在一些敏感可利用的凭据,类似一些可以重点关注的字段:

1
2
3
4
5
6
7
8
9
10
11
DB_HOST
DB_PORT
DB_USER
DB_PASSWORD
MYSQL_PASSWORD
REDIS_PASSWORD
MONGO_URI
JWT_SECRET
SECRET_KEY
ACCESS_KEY
SECRET_ACCESS_KEY

但是通常也得注意这些凭据找到的路径啥的,防止是测试环境配置或者是示例配置,通常可结合上下文进行判断:

  1. 文件路径是不是生产目录
  2. 配置里连接的是不是内网真实地址
  3. 数据库名是否像真实业务库
  4. 账号名是否像业务账号
  5. 文件修改时间是否接近当前
  6. 同目录是否存在启动脚本或部署脚本

防守视角:配置文件里的明文凭据是最常见的低级问题。检测上可以通过定期扫描代码仓库和服务器上的配置文件(trufflehog、gitleaks、自研规则),关注 password=secret=、AK/SK 模式的字符串。治理上推荐使用集中式 Secret 管理(Vault、KMS、配置中心 + 加密),让应用启动时动态拉取,而不是把明文写死在文件里。

1.2 数据库连接串

数据库连接的价值一般会比较高,因为它其中可能一次性就涵盖了很多有价值的信息,比如:协议、地址、端口、数据库名、用户名、密码、连接参数等,常见形式:

1
2
3
4
5
jdbc:mysql://db.internal:3306/order?useSSL=false
jdbc:postgresql://10.10.5.30:5432/report
mongodb://user:[email protected]:27017/admin
redis://:[email protected]:6379/0
sqlserver://10.10.5.60:1433;databaseName=erp

防守视角:连接串的特征非常明显,可以基于正则在日志、Git 提交、聊天记录、工单系统里做扫描。同时数据库账号要按最小权限分配(业务账号只读 / 只写指定表),避免一把账号能动整个库。

1.3 运维脚本和部署脚本

在内网环境里,开发人员如果存在不规范的行为,可能会为了方便,直接存一些凭据在系统里或者运维脚本里。

常见的:

  1. 备份数据库脚本
  2. 定时同步脚本
  3. 发布脚本
  4. 日志清理脚本
  5. 监控上报脚本
  6. 批量 SSH 脚本
  7. 数据库巡检脚本

脚本里常见的凭据形式:

1
2
3
4
mysql -h 10.10.5.20 -u backup -p<password>
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=xxx
curl -H "Authorization: Bearer xxx" http://api.internal

通常从脚本执行的行为,就可以看出来该脚本的用处和承担的角色。比如一个备份脚本如果出现数据库账号,则这个脚本可能至少有数据库的读取权限,可以尝试利用。如果一个发布脚本里出现 CI/CD Token,则其可能有拉代码、构建镜像、推送镜像、部署服务之类的权限。

一般来说如果脚本的权限越靠近发布链路,则其价值通常就更高。

防守视角:cron、systemd timer、/etc/rc.local/opt/scripts//home/*/bin/ 这类目录是脚本残留的高发区,应纳入定期巡检。命令行参数里出现 -p<password> 是非常容易在 ps aux、auditd、bash history 里被读到的,要禁止这种写法,改用配置文件或环境变量并设置严格权限。

1.4 备份文件

备份文件里的凭据经常比线上文件更危险,因为备份往往缺少权限控制,且运维人员可能对其中存在的一些凭据泄密更加松懈。

常见的备份形式:

1
2
3
4
5
6
7
www.zip
backup.tar.gz
web.bak
config.bak
application.yml.bak
db_backup.sql
sql_dump.sql

备份里可能包含:

1
2
3
4
5
6
7
旧配置文件
历史数据库连接串
管理员初始化密码
用户表 Hash
API Token
内部接口地址
测试环境账号

这里有一个容易忽略的点,旧凭据不一定没用。一个比较典型的场景是:公司轮换了应用账号密码,但忘了改 cron 里的备份脚本或者历史发布包,结果备份脚本里的”旧密码”反而是最后一个还能用的;或者运维只改了业务代码里的连接配置,没有同步轮换数据库密码、Token 或云密钥,导致历史备份里的凭证在当前环境里依然有效。

除了显式的 .bak,还要注意编辑器和 IDE 残留:

1
2
3
4
5
6
.swp / .swo            # vim 临时文件
*~ # 编辑器备份
.idea/dataSources.xml # IntelliJ / DataGrip 保存的数据库连接
.idea/workspace.xml
.vscode/settings.json
nohup.out # 启动日志可能 dump 了完整环境变量

特别是 IntelliJ/DataGrip 的 dataSources.local.xml,里面保存的数据库密码用的是可逆加密,运维机或开发机上经常能直接拿到。

防守视角:备份不应该和业务代码放在同一个 web 可访问目录里。检测上可以扫 web 根目录下的 .bak.zip.sql.swp.idea/.vscode/.git/ 等敏感后缀和目录。备份文件应该走独立存储 + 独立权限 + 加密保存。

1.5 SSH 私钥和证书文件

SSH 的私钥泄密绝对是重点中的重点,一旦获取到就是高价值的凭据。

常见的特征:

1
2
3
4
-----BEGIN OPENSSH PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
-----BEGIN DSA PRIVATE KEY-----
-----BEGIN EC PRIVATE KEY-----

判断一把 SSH 私钥的价值,主要看下面六个维度:

判断维度 关注点 常用命令
私钥归属用户 文件路径、属主、UID、所属组、sudo -l、登录活跃度 ls -lstatidgroupssudo -llastlastlog
是否有 passphrase 直接导出公钥能否成功;passphrase 是否被脚本/agent 覆盖 ssh-keygen -y -f id_rsassh-add -l
文件权限 是否为 600,是否其他用户可读 ls -lstat
同目录公钥 是否有 .pub 文件,公钥注释中的用户/主机/邮箱 cat id_rsa.pub
目标主机线索 部署脚本、CI/CD inventory、~/.ssh/configknown_hosts 中的目标 cat ~/.ssh/configcat ~/.ssh/known_hosts
部署密钥 vs 个人密钥 文件名、用户名、所在目录是否带 deploy/jenkins/runner/ci 等关键字

下面对每一项展开说明。

(1) 私钥属于哪个用户

判断私钥价值,第一步是判断它大概率属于哪个用户。一般可以从文件路径、文件属主、文件名和上下文一起看。比如用户名是 deploy:

1
2
ls -l /home/deploy/.ssh/id_rsa
stat /home/deploy/.ssh/id_rsa

如果私钥在 /home/deploy/.ssh/ 下,文件属主也是 deploy,那它大概率是 deploy 用户的密钥。如果出现在 /root/.ssh/ 下,价值通常更高,因为它可能对应 root 的远程登录权限。不过也要注意,有些私钥是被复制到临时目录、Web 目录、备份目录里的,这时文件属主不一定等于真正的使用者,需要结合路径和上下文判断。

确定用户以后,要看这个用户本身的价值。可以看 /etc/passwd 里的 UID、shell 和 home 目录:

1
2
3
4
5
6
grep '^deploy:' /etc/passwd
id deploy
groups deploy
sudo -l
last deploy
lastlog -u deploy

可以重点关注:UID 是否为 0、shell 是否是 /bin/bash /bin/sh 这类可登录 shell、home 目录是否正常、是否在 dockerlxdsudowheel 等组里、sudo -l 是否有 NOPASSWD 项、最近是否活跃登录。docker 组在很多环境里接近 root 等价,因为能操作 Docker socket 的用户可能通过容器影响宿主机。

(2) 是否有 passphrase

有 passphrase 的私钥,即使文件泄露,也不一定能直接使用。判断方法是尝试从私钥导出公钥:

1
ssh-keygen -y -f id_rsa

如果直接输出公钥,说明这把私钥大概率没有 passphrase。如果提示输入 passphrase,说明私钥本身有一层保护。

不过有 passphrase 不能简单理解为”有就安全”,还要看几个上下文:(1) passphrase 是否被写在脚本里;(2) 是否被保存在 ssh-agent 里;(3) 是否和系统登录密码相同;(4) 是否能从同目录、配置文件、CI/CD 变量里找到。CI/CD 场景里有些私钥虽然设置了 passphrase,但 passphrase 也被写进了流水线变量或部署脚本里,这种保护意义就弱很多。还有一种情况是私钥本身有 passphrase,但当前机器上 ssh-agent 已经加载了这把 key,攻击者不一定需要知道 passphrase,也可能借用当前用户的 agent 去访问目标。

(3) 文件权限是否异常宽松

正常情况下,SSH 私钥应该是 -rw------- (600)。如果看到 644、664、666 之类的权限,就说明私钥权限过宽,其他用户也可能读取这个文件。比如一台 Web 服务器上,deploy 用户的私钥权限是 644,而 Web 进程用户 www-data 能读到这个文件,那么 Web 权限一旦被拿到,就可能进一步拿到部署账号的私钥。

(4) 同目录是否有对应 public key

公钥本身不是敏感凭据,但它能帮助判断私钥的身份和用途。公钥末尾经常会有注释,比如 deploy@app01jenkins@gitlab-runnersunny@macbook。但这个注释不一定绝对可信,因为它可以随便改,通常仅能提供线索。

(5) 是否能从上下文判断目标主机

私钥本身只是认证材料,上下文决定它的使用范围。能从部署脚本、CI/CD、inventory、~/.ssh/config~/.ssh/known_hosts、文件名里定位到目标主机的私钥,价值远高于孤立出现的一把 key。

(6) 是否是部署密钥还是个人登录密钥

类型 常见特征 价值特点
部署密钥 文件名带 deploy/runner/jenkins/gitlab/ci/backup;出现在 CI/CD 目录、自动化运维目录 稳定、长期、自动化,可能批量覆盖生产机器或控制发布链路
个人登录密钥 文件名是 id_rsa / id_ed25519;公钥注释里是个人用户名、主机名、邮箱;出现在个人 home 目录 范围不确定但可能很广,运维个人 key 可能能登堡垒机、生产、数据库、K8s master

防守视角:私钥文件权限不是 600 是非常明显的信号,可以做主机基线扫描。~/.ssh/authorized_keys 的新增也应纳入文件完整性监控(FIM)。部署密钥应使用专用账号、最小权限、并定期轮换;CI/CD 中的私钥优先使用平台原生 Secret 管理而不是落盘。可以监控异常源 IP 的 SSH 登录行为,特别是部署账号在非工作时间、非跳板机来源的登录。

1.6 内存凭据(LSASS、进程参数、core dump)

很多凭据在磁盘上找不到,但运行时存在于内存里,这部分往往是渗透中价值最高的来源之一。

Windows: LSASS 内存

LSASS(lsass.exe)进程里保存了当前登录会话的凭据材料,包括 NTLM Hash、Kerberos 票据,部分场景下还能拿到明文密码(取决于 WDigest 配置和系统版本)。常见获取方式:

1
2
3
4
mimikatz: sekurlsa::logonpasswords
procdump64.exe -ma lsass.exe lsass.dmp # 离线用 pypykatz 解析
comsvcs.dll MiniDump 方式 dump
任务管理器手动 dump(管理员权限)

防守侧通常会启用 LSASS 保护(RunAsPPL)、Credential Guard、关闭 WDigest,并且 EDR 对 LSASS 句柄打开会重点告警。

Linux: 进程命令行和环境变量

Linux 上没有 LSASS 这种集中存储,但有几个非常容易被忽略的地方:

1
2
3
4
5
6
7
8
9
10
# 进程命令行:mysql -uroot -p123456 这种写法直接暴露
ps -ef
ps auxwwe # 带环境变量

# 进程环境变量(需要权限)
cat /proc/<pid>/environ | tr '\0' '\n'

# core dump
ls /var/crash/ /var/lib/systemd/coredump/
coredumpctl list

特别是 /proc/<pid>/environ,很多容器化的进程把数据库密码、Redis 密码、JWT secret 直接通过环境变量注入,进程跑起来以后这些值就一直在内存里。如果当前用户能读到目标进程的 /proc/<pid>/environ(同 UID 或 root),就能直接拿到。

core dump / 内存 dump

应用崩溃产生的 core 文件、gcore 主动生成的 dump、JVM 的 heap dump(jmap -dump)里都可能包含运行时的密码、Token、Session。Java 应用的 heap dump 用 MAT 或 strings 翻一下经常有惊喜。

防守视角:禁止把密码放在命令行参数里(用配置文件或 stdin);限制 core dump 生成路径和权限;Windows 启用 LSASS 保护和 Credential Guard;EDR 关注 MiniDumpWriteDump 调用、procdump 工具落地、对 lsass.exe 的异常句柄打开。

1.7 Shell history、环境配置和云/容器客户端凭据

用户 home 目录下有一批文件几乎是凭据”重灾区”,单独拎出来说:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
~/.bash_history / ~/.zsh_history    # 命令历史,常有 mysql -p、curl -H Authorization
~/.python_history
~/.mysql_history / ~/.psql_history # 数据库客户端历史
~/.netrc # FTP/HTTP 凭据
~/.aws/credentials # AWS AK/SK
~/.aws/config
~/.azure/ # Azure CLI 凭据
~/.config/gcloud/ # GCP 凭据
~/.docker/config.json # registry 认证(base64,可直接还原)
~/.kube/config # K8s API 凭据,价值非常高
~/.npmrc # npm registry token
~/.pypirc # pypi token
~/.git-credentials # Git 明文凭据
~/.gitconfig # 可能引用 credential helper
~/.m2/settings.xml # Maven 仓库凭据
~/.gradle/gradle.properties

这里面 ~/.aws/credentials~/.kube/config~/.docker/config.json 是最高频的高价值点。.kube/config 拿到就基本等于拿到对应集群的 API 访问权限(取决于绑定的 ServiceAccount/User 权限),可以直接用 kubectl 操作集群。

bash history 里很容易翻到的写法:

1
2
3
4
5
mysql -h 10.10.5.20 -uroot -pP@ssw0rd
curl -H "Authorization: Bearer eyJhbGc..." http://api.internal
sshpass -p 'xxx' ssh user@host
export AWS_SECRET_ACCESS_KEY=xxx
git clone https://user:[email protected]/...

防守视角.bash_history 和各种客户端历史可以做基线扫描(grep 高危关键字)。教育层面引导使用 HISTIGNORE、不在命令行里带密码、用凭据管理工具(aws-vault、git-credential-manager、kubectl 的 exec auth provider)。~/.aws/credentials 优先用 STS 临时凭据 + IAM Role 替代长期 AK/SK。

1.8 Windows 凭据管理器和 RDP 保存凭据

Windows 上很多远程登录凭据并不是直接明文保存在配置文件里,而是通过系统凭据管理器或 DPAPI(Windows Data Protection API)机制保护。比如用户在远程桌面、VPN、部分客户端工具中选择”保存密码”后,系统可能会在当前用户上下文中保存对应凭据。

常见位置:

1
2
3
4
5
%APPDATA%\Microsoft\Credentials\
%LOCALAPPDATA%\Microsoft\Credentials\
%APPDATA%\Microsoft\Protect\<SID>\ # MasterKey
%USERPROFILE%\Documents\Default.rdp
*.rdg # RDCMan 配置

DPAPI 加密的凭据本质上由用户登录密码(或域 Backup Key)保护——拿到当前用户的登录态、用户密码 hash,或域控的 DPAPI Backup Key,都能解密。所以 Windows 运维机、跳板机、开发机的价值通常高于普通业务服务器。这些机器上可能保存了大量到服务器、数据库、VPN、堡垒机、远程桌面的历史连接信息。

常用工具:mimikatz 的 dpapi::* 模块、SharpDPAPI、LaZagne。

防守视角:跳板机/运维机不应该作为日常办公终端,且应启用 Credential Guard。重要的远程访问应强制走堡垒机 + 一次性密码 / MFA,而不是依赖客户端”记住密码”。监控对 %APPDATA%\Microsoft\Credentials\ 和 MasterKey 目录的异常读取。

1.9 运维开发客户端配置

内网中,运维人员和开发人员的终端里通常会存在很多有价值的可利用的工具,例如:

1
2
3
4
5
6
7
8
9
10
11
XShell
FinalShell
SecureCRT
MobaXterm
WinSCP
FileZilla
Navicat
DBeaver
DataGrip
SVN / Git 客户端
VPN / 堡垒机客户端

这些工具通常都会保存很多信息,比如连接目标(服务器 IP、端口、数据库地址、分组名称)、身份线索(用户名、登录方式、密钥名称)、凭据材料(保存的密码、私钥路径、加密后的密码、Token)等。

这里有一个比较关键的点:很多客户端的”加密保存”实际上是可逆加密(密钥硬编码或基于固定算法),社区有现成的解密工具。Navicat、XShell、SecureCRT、FileZilla、WinSCP 都有对应的解密脚本。所以拿到这些客户端的配置文件几乎等于拿到明文。

防守视角:运维终端应纳入资产管理并强化基线(禁用客户端”保存密码”功能、强制走堡垒机)。对 Navicat、XShell 等工具的配置目录做监控,异常拷贝/外传应告警。

1.10 远程控制工具配置

一些办公终端或运维终端上,可能会安装 TeamViewer、ToDesk、向日葵等远程控制工具。这类工具保存的信息不一定是传统意义上的账号密码,也可能是:

1
2
3
4
5
6
设备 ID
固定验证码
安全密码
历史连接记录
授权设备列表
连接过的主机名

从凭据角度看,它们的风险在于:

1
2
3
4
可能绕过正常 SSH / RDP / VPN 访问链路
可能直接获得图形化交互权限
可能保存历史连接过的高价值主机
可能被当作备用运维通道长期存在

防守视角:办公网/生产网应通过软件白名单或 EDR 限制远控工具安装。对 TeamViewer、向日葵、ToDesk 等的进程和网络行为做审计,特别关注非工作时间的远控连接。

1.11 浏览器保存密码、Cookie 和本地 Token

浏览器里常见的凭据不只有保存密码,可能包括:保存的账号密码、Cookie/Session、LocalStorage/SessionStorage/IndexedDB 中的 Token。

Chromium 系浏览器的 Login Data、Cookies 文件由 DPAPI(Windows)或 keyring(Linux/macOS)保护,拿到当前用户上下文以后通常能解出来。HackBrowserData、SharpChrome 等工具是常见利用方式。

内网场景里浏览器价值很高的几类目标:内部 OA、Confluence/Wiki、Jira、Jenkins、GitLab、Grafana、Kibana、云控制台、堡垒机 Web UI——这些 Cookie 拿到通常能直接代表登录态绕过 MFA。

防守视角:关键内部系统应缩短 Session 有效期、绑定 IP 或设备指纹、敏感操作二次验证。监控对浏览器 Login Data / Cookies 文件的异常读取。

1.12 代码仓库和 CI/CD 配置

代码仓库和 CI/CD 连接了开发、构建、镜像、部署和生产环境。

常见位置:

1
2
3
4
5
6
7
8
9
10
.git/config
.gitlab-ci.yml
.github/workflows/
Jenkinsfile
Dockerfile
docker-compose.yml
helm values
kustomization
terraform variables
ansible inventory

几个特别要注意的点:

  1. **Web 根目录下暴露的 .git/**:在内网 Web 应用里很常见,直接 git-dumper 或者手动拉 .git/ 就能还原整个仓库代码和历史。
  2. Git 历史里被”删除”的 secret:开发提交了密码后用 git rm 或者新提交覆盖,但历史里仍然存在,git log -p 或者 git log --all -- <file> 能翻出来。trufflehog 跑全 git 历史是标准操作。
  3. .gitignore 没生效的兜底.gitignore 只对未追踪文件生效,已经被 add 的文件加进 .gitignore 是没用的。
  4. CI/CD 变量泄露:流水线日志里 set -xecho $SECRET、构建产物里残留的环境变量都是常见的泄露方式。

防守视角:禁止 .git/ 部署到 web 可访问目录(Nginx/Apache 配置 deny)。代码仓库启用 push-time secret 扫描(GitLab/GitHub 自带或 trufflehog/gitleaks pre-receive hook)。CI/CD 平台的 Secret 必须设置为 masked + protected,并且禁止流水线中打印环境变量。

1.13 中间件和组件配置

中间件本身也可能保存凭据,尤其是:

1
2
3
4
5
6
7
8
9
10
Tomcat
ActiveMQ
WebLogic
JBoss
Jenkins
Harbor
Nexus
SonarQube
Confluence
vCenter

常见可获取的凭据可能包括管理控制台账号密码、数据库连接账号、消息队列认证信息、LDAP / SSO 对接账号、集群节点通信密钥、插件或 Webhook Token。

例如 Tomcat 项目中常见的:db.propertiesjdbc.propertiesapplication.propertiescontext.xml 中就可能保存了数据库的连接信息。

ActiveMQ 这类消息中间件可能保存控制台认证、消息队列认证信息。Jenkins、Harbor、Nexus 这类组件还可能保存制品库、镜像仓库、Git、部署系统相关的凭据。Jenkins 的 credentials.xml + master.key + hudson.util.Secret 三件套可以离线解密所有保存的凭据,是非常经典的横向跳板。

防守视角:中间件管理控制台不应该暴露在大网段,应只允许特定运维网段访问。Jenkins 这类系统应启用 RBAC、审计日志,凭据使用 Credentials Plugin 而不是写在 Job 配置里。

1.14 Docker 镜像和环境变量

容器环境里,凭据经常通过环境变量注入。

环境变量中常见字段:

1
2
3
4
5
6
DB_PASSWORD
REDIS_PASSWORD
SPRING_DATASOURCE_PASSWORD
JWT_SECRET
S3_ACCESS_KEY
S3_SECRET_KEY

而 Docker 镜像里可能会残留构建时写入的 .env、历史 layer 中的配置文件、镜像构建参数、源码目录、临时配置。即使 Dockerfile 里用了 RUN rm xxx,前一层的文件依然存在。常见利用方式:

1
2
3
docker history --no-trunc <image>           # 看构建命令,ARG/ENV 经常有泄露
docker save <image> -o img.tar # 拿到镜像 tar,逐层解开翻配置和源码
dive <image> # 交互式翻 layer

容器为了启动应用,必须在运行时拿到数据库、缓存、消息队列、对象存储等依赖的认证信息。如果 Secret 管理不好,这些信息就会出现在镜像、环境变量、启动命令或编排配置里。

防守视角:镜像构建避免 ARG/ENV 传敏感值(用 --secret 或运行时注入)。镜像仓库做扫描(Trivy、Clair)发现明文 secret。生产容器禁用 docker inspect 之类的能读环境变量的接口对外暴露。

1.15 Kubernetes Secret 和 ServiceAccount Token

Kubernetes Secret 是云原生环境里很常见的凭据载体。

常见类型:

1
2
3
4
Opaque
kubernetes.io/dockerconfigjson
kubernetes.io/tls
kubernetes.io/service-account-token

Secret 里可能保存数据库密码、镜像仓库认证、TLS 私钥、服务账号 Token、第三方 API Token、云平台密钥。

需要注意的是 Kubernetes 的 Secret 默认只是 base64 编码而不是加密,etcd 层的加密需要单独配置(EncryptionConfiguration)。一旦能 list secret,基本等于明文获取。

容器内通常可以直接读到挂载的 ServiceAccount Token:

1
2
3
ls /var/run/secrets/kubernetes.io/serviceaccount/
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

拿到 Token 以后,结合 API Server 地址(通常是 kubernetes.default.svc)就可以用 kubectl --token=... 或者直接 curl API。能做什么取决于这个 SA 绑定的 Role/ClusterRole——如果绑了 cluster-admin 那就是直接控制集群。

如果某个 Pod、ServiceAccount 或运维账号能读取大量 Secret,它本身就可能成为横向扩散入口。

防守视角:开启 etcd 加密;ServiceAccount 严格按最小权限分配 Role;禁用默认 SA Token 自动挂载(automountServiceAccountToken: false);用 OPA/Kyverno 做 Secret 访问的策略管控;监控 kubectl get secretget configmap 的异常调用。

1.16 日志里的 Authorization、Cookie、Token

日志是非常容易泄露凭据的地方,尤其是 API 网关、反向代理、调试日志、错误日志。

常见敏感字段:

1
2
3
4
5
6
7
Authorization: Bearer <token>
Cookie: SESSION=<value>
X-API-Key: <key>
access_token=
refresh_token=
client_secret=
password=

日志里的 Token 有几个特点:

  1. 可能是短期 Token,也可能是长期 Token;
  2. 可能已经过期,也可能仍然有效;
  3. 可能只对某个服务有效;
  4. 可能对应真实用户身份;
  5. 可能包含权限范围、过期时间和签发方信息。

常见的日志路径:/var/log/nginx/、应用 logs/ 目录、stdout/stderr 重定向文件、ELK/Kafka 上的全量日志、APM(SkyWalking/Pinpoint)里的请求详情。

防守视角:日志侧做敏感字段脱敏(Authorization、Cookie、password、token 类字段统一打码),SDK 层和网关层都做一遍。日志查询平台启用细粒度权限——能搜全量日志的人本质上等同能拿到大量凭据。

1.17 Windows 域相关凭据

域环境里有几类凭据特别值得单独列出来,因为它们的获取方式和复用模式都比较固定:

凭据类型 获取位置 / 方式 典型利用
SAM / SYSTEM %SystemRoot%\System32\config\ 或注册表导出 离线提取本地账号 NTLM Hash
LSASS 内存 mimikatz / procdump + pypykatz 当前会话 Hash、明文、Kerberos 票据
NTDS.dit 域控上 %SystemRoot%\NTDS\NTDS.dit + SYSTEM hive 域内全量账号 Hash(DCSync 的离线版本)
DCSync 拥有 DS-Replication-Get-Changes 权限的账号 在线拉取任意域账号 Hash
Kerberos TGT 内存 dump、ccache 文件、kirbi 文件 Pass-the-Ticket 横向
Kerberoasting 任意域用户向有 SPN 的账号请求 ST 离线爆破服务账号密码
AS-REP Roasting 不要求 Kerberos 预认证的账号 离线爆破账号密码
缓存的域凭据 (mscash) 注册表 HKLM\SECURITY\Cache 离线爆破最近登录过域账号的密码
GPP cpassword SYSVOL 共享中的 Groups.xml 固定 AES Key 解密本地管理员密码(老问题)
DPAPI Master Key 用户目录 + 域 Backup Key 解密 DPAPI 保护的所有凭据

这部分内容深度比较大,每一项都能展开很多,这里只是给一个清单作为索引,具体技术细节会在后续单独的 AD 篇里展开。

防守视角:域环境的核心防线是 DC 的强化(Tier 0 隔离、不允许低权限主机登录 DC、禁用不必要的 SPN)、关闭 WDigest、强制 Kerberos AES、监控 4624/4625/4769(异常 ST 请求)/4662(DCSync 模式)等关键事件 ID、对 SYSVOL 的写入和 Groups.xml 类敏感文件做监控。

2. 凭据复用路径

凭据复用的核心是根据凭据来源、服务关系和权限边界,推导下一步最可能的路径。

2.1 Web 配置 → 数据库

最常见的一条路径:Webshell -> Web 配置文件 -> 数据库连接串 -> 数据库连接验证 -> 读取业务配置 / 用户表 / Token

很多业务系统会把第三方配置放在数据库里,例如:

(1) 短信平台 AK/SK (2) 邮件 SMTP 密码 (3) 对象存储 Key (4) 支付平台密钥 (5) 内部 API Token (6) Webhook Secret

2.2 数据库账号 → 更多业务配置 / 系统命令

拿到数据库访问以后,重点不是只看业务数据,而是找能继续说明系统关系的配置。

常见重要的表名:

1
2
3
4
5
6
7
8
9
10
sys_config
system_config
app_config
settings
properties
oauth_client
api_key
third_party_config
datasource
job_config

常见字段:

1
2
3
4
5
6
7
8
9
access_key
secret_key
client_secret
private_key
token
callback_url
endpoint
username
password

另外数据库本身也是横向的跳板:

1
2
3
4
5
MySQL: UDF 提权 / into outfile 写文件
MSSQL: xp_cmdshell / sp_OACreate / Agent Job 命令执行
PostgreSQL: COPY FROM PROGRAM / 扩展加载
Redis: 写公钥 / 写 cron / 主从复制 RCE
MongoDB: 业务数据里的 Token / Session

也就是说一个数据库账号的复用方向不只是”读更多数据”,还可能是”在数据库所在的宿主机上执行命令”。

2.3 SSH 私钥 → Linux 服务器

SSH 私钥复用通常有两种场景。

第一种是部署密钥:

1
2
3
4
5
Jenkins / GitLab Runner / 发布机

SSH 私钥

应用服务器

第二种是运维人员个人密钥:

1
2
3
4
5
运维机

SSH 私钥

多台 Linux 服务器

2.4 运维客户端 → 服务器 / 数据库 / 堡垒机

运维客户端配置本质上是一份人为维护过的连接关系表。例如:

1
2
3
4
5
XShell / MobaXterm → SSH 服务器
Navicat / DBeaver → 数据库
WinSCP / FileZilla → FTP / SFTP 服务器
堡垒机客户端 → 核心服务器
VPN 客户端 → 内网入口

这类路径的特点就是目标通常更准,因为这些连接不像是之前说的信息搜集时扫出来的,而是运维人员日常真实使用过的。配置里的分组、备注、用户名、连接地址都能帮助判断系统价值。

2.5 本地管理员密码复用 → Windows 横向

Windows 内网里,本地管理员密码复用是非常经典的问题。多台 Windows 主机使用了相同的本地 Administrator 密码。只要拿到其中一台机器的本地管理员凭据,就可能登录其他机器。

常见的横向工具:

1
2
3
crackmapexec / nxc smb <range> -u Administrator -H <hash>
impacket-psexec / smbexec / wmiexec
evil-winrm

但是这类验证风险很高,因为涉及 Windows 远程登录、SMB、服务管理等行为,很容易触发检测(4624 logon type 3、Service Control Manager 事件、远程进程创建)。喷洒前应该先做小范围验证,避免触发账号锁定。

防守侧更推荐用 LAPS(Local Administrator Password Solution)让每台机器的本地管理员密码不同,避免一台机器失陷影响一批机器。

2.6 NTLM Hash → Pass-the-Hash / 跨主机横向

拿到 NTLM Hash 之后不需要解密就可以直接用于横向,这是 Windows 认证机制本身决定的:

1
2
3
4
5
NTLM Hash
↓ Pass-the-Hash
SMB / WMI / WinRM / RDP (受限) / MSSQL (Windows 认证)

其他主机 / 数据库 / 域服务

常用工具:

1
2
3
crackmapexec smb <target> -u <user> -H <NTLM>
impacket-psexec <domain>/<user>@<target> -hashes :<NTLM>
evil-winrm -i <target> -u <user> -H <NTLM>

2.7 Kerberos 票据 → Pass-the-Ticket / 跨服务

Kerberos 场景下复用方式更多样:

1
2
3
4
5
6
7
8
9
10
11
TGT (kirbi/ccache)
↓ Pass-the-Ticket(注入到当前会话)
访问任意服务(SMB / LDAP / CIFS / HTTP / MSSQL ...)

服务账号 ST
↓ Kerberoasting 离线爆破
拿到服务账号明文密码

普通域账号
↓ Silver Ticket / Golden Ticket(已拿到对应 hash 时)
伪造票据持久化

Token / 票据类的复用最危险的地方在于:它通常不需要密码、不触发常规登录失败日志、不触发 MFA,对常规检测体系是比较大的盲点。

2.8 域账号密码 → SMB / RDP / WinRM

常见路径:

1
2
3
4
5
6
7
8
9
域账号

SMB 文件共享

RDP 远程桌面

WinRM 远程管理

MSSQL Windows 认证

但是通常这个步骤,需要先判断账号类型然后再验证。例如一个出现在数据库连接里的域账号,可能只是 MSSQL 服务账号,不应该直接拿去 RDP 登录办公终端——这种行为很容易触发异常登录告警。

2.9 Token → 内部 API / CI/CD / 云平台

一些对应的 Token 的匹配:

1
2
3
4
5
6
Bearer Token → 内部 API
Git Token → 代码仓库
CI Token → 流水线
Registry Token → 镜像仓库
AK/SK → 云资源
ServiceAccount Token → Kubernetes API

Token 的危险在于它往往不需要密码,容易被当作正常行为,也不一定触发传统登录日志。

2.10 K8s ServiceAccount Token → 集群横向

容器内拿到 SA Token 后的典型路径:

1
2
3
4
5
6
7
8
9
10
11
Pod 内 SA Token
↓ kubectl auth can-i --list
判断 Token 权限

list secrets / exec into pods / create pod
↓ 创建特权 Pod 或挂载 hostPath
逃逸到 Node

Node 上的 kubelet 凭据 / 其他 Pod 的 Secret

集群范围横向

如果 Token 权限够,直接 kubectl get secret -A 就可能拿到整个集群所有命名空间的凭据,包括其他业务的数据库密码、镜像仓库认证、第三方 API Key 等。

2.11 云 AK/SK → 云资源 → 实例横向

云上的复用路径相对独立但威力很大:

1
2
3
4
5
6
7
8
9
AK/SK(来自 ~/.aws/credentials、环境变量、CI/CD 变量)
↓ 枚举权限(aws sts get-caller-identity / iam:SimulatePrincipalPolicy)
根据 IAM 策略选择路径

S3 bucket → 拿到更多凭据 / 业务数据
EC2 → 创建实例 / 修改安全组 / 读取 user-data
SSM → 直接在已有实例上执行命令
KMS → 解密历史加密数据
RAM/IAM → 创建后门用户做持久化

另一条很关键的路径是实例元数据服务(IMDS):在云主机上拿到 webshell 后,访问 IMDS(AWS 是 169.254.169.254、阿里云类似)可以拿到该实例绑定的 Role 临时凭据,这个凭据的权限取决于 Role 策略,往往比想象中大。IMDSv1 没有任何鉴权,IMDSv2 需要 PUT 拿 token 但限制不算强。

2.12 远控工具 → 图形化入口

远控工具凭据和传统系统凭据不太一样,它可能是:设备 ID + 固定密码、授权设备关系、历史连接记录、本机识别码、安全密码。

这类路径的风险在于,它可能绕过企业正常的远程访问链路,比如正常情况下访问服务器需要:

1
VPN → 堡垒机 → 审批 → RDP / SSH

但如果某台运维机上保存了远控工具的固定密码,攻击者可能通过远控工具直接进入图形化桌面。

3. 防守侧的整体思路

把前面零散的防守视角整理成一个整体框架,主要分四层:

(1) 凭据生命周期管理

  • 集中化:用 Vault / KMS / 配置中心管理 Secret,禁止散落在配置文件、代码、脚本里。
  • 短周期:长期 AK/SK 替换为 STS / IAM Role / OIDC Federation;Token 设置合理过期时间。
  • 强轮换:建立轮换机制,特别是数据库账号、CI/CD Token、备份脚本里用到的凭据。
  • 最小权限:账号、ServiceAccount、IAM Role 都按职责最小化,避免”一把钥匙开所有门”。

(2) 落地侧检测

  • 主机基线:扫描 ~/.ssh/~/.aws/~/.kube/.bash_history、配置文件中的高危关键字。
  • 文件完整性监控:authorized_keys/etc/sudoers.d/、Jenkins credentials、K8s admission config 等关键文件。
  • 仓库扫描:trufflehog、gitleaks 在 push 阶段拦截;定期对历史做全量扫描。
  • 镜像扫描:CI 阶段做 secret 扫描,禁止带明文凭据的镜像入库。

(3) 行为侧检测

  • 异常登录:非工作时间、非常用源 IP、跨地域、跨时区、短时间多账号尝试。
  • 横向行为:SMB/WMI/WinRM 异常调用、psexec 类工具落地、4624 logon type 3 异常账号组合。
  • 域内异常:4769 异常 SPN 请求(Kerberoasting)、4662 DCSync 模式、SYSVOL 异常写入。
  • API 异常:K8s get secret -A、云 IAM ListAccessKeys / CreateUser、Git clone --mirror 等。

(4) 横向阻断

  • 网络分段:办公网、生产网、运维网、Tier 0 互相隔离,必须经过堡垒机/跳板。
  • 强制 MFA:所有关键系统(VPN、堡垒机、云控制台、代码仓库、K8s Dashboard)开 MFA。
  • LAPS:Windows 本地管理员密码每机不同。
  • 禁用 NTLM / 启用 Credential Guard / 关闭 WDigest / 启用 LSASS 保护。

凭据这块的攻防本质上是个”管理问题 + 工程问题”的叠加:单点凭据泄露很难完全避免,但通过最小化作用范围 + 缩短有效期 + 强制审计,可以让单个凭据的失陷不至于扩散成整条链路的失陷。后续会再单独写一篇专门讲自动化收集和 AD 域内凭据攻击。