云安全渗透(一)云环境信息收集与 AK/SK 泄露挖掘

一、云渗透和传统渗透不是同一套方法论

先说结论:云上渗透的起点通常是一组凭证,而不是一个 IP 段。

做过 Web 渗透的人第一次做云测评,最容易犯的错是打开 nmap 扫 C 段。扫完发现全是 443 和 80,返回的还是云厂商的负载均衡,看起来毫无破绽,然后就卡住了。原因是云环境的攻击面根本不在端口上:

维度传统 IDC 渗透云环境渗透
入口IP 段、开放端口、Web 漏洞凭证泄露、配置错误、身份信任链
主要动作扫描、爆破、EXPAPI 调用、权限枚举、模拟身份
权限模型系统用户 / 数据库用户IAM / RAM / CAM / ServiceAccount
横向方式内网跳板、凭据抓取角色扮演、凭证复用、共享存储
关键资产服务器、数据库对象存储、镜像仓库、CI 变量、K8s 集群
拿权限的标志拿到 root / system拿到 AdministratorAccess / cluster-admin
痕迹在哪系统日志、流量云审计日志、K8s audit log、CI 日志

所以云渗透的第一步不是扫描,是回答三个问题:

  1. 目标用的是哪家云(决定后面用哪套 API 和工具)。
  2. 目标有哪些云资产暴露在外面(存储桶、镜像仓库、K8s API)。
  3. 目标的凭证可能泄露在哪里(代码仓库、CI 日志、配置文件、前端 JS)。

这一篇就按这三个问题展开。整条链路的结果是一张资产清单加若干组待验证的凭证,交给第二篇和第三篇继续。

二、判断目标用的是哪家云

2.1 从 DNS 和响应头看

最省事的办法是看 CNAME 和 HTTP 响应头,因为云厂商在这两处都会留下很明显的指纹。国内常见的对应关系:

CNAME 后缀对应服务
*.amazonaws.comAWS(S3、ELB、API Gateway)
*.cloudfront.netAWS CloudFront
*.aliyuncs.com阿里云(OSS、SLB、FC)
*.oss-cn-*.aliyuncs.com阿里云 OSS
*.myqcloud.com腾讯云 COS / CDN
*.cos.ap-*.myqcloud.com腾讯云 COS
*.huaweicloud.com / *.obs.cn-*.myhuaweicloud.com华为云
*.azureedge.net / *.blob.core.windows.netAzure
*.googleusercontent.com / *.storage.googleapis.comGCP

查询命令:

# 一次性看 A、CNAME、MX、TXT
dig +noall +answer example.com A
dig +noall +answer www.example.com CNAME
dig +noall +answer example.com MX
dig +noall +answer example.com TXT

# 用公共 DNS 避免被本地解析污染
dig @8.8.8.8 +short cdn.example.com CNAME
dig @223.5.5.5 +short static.example.com CNAME

TXT 记录里的 SPF 经常直接暴露云厂商,比如:

"v=spf1 include:spf.protection.outlook.com include:amazonses.com -all"

看到 amazonses.com 就知道对方用 SES 发信,邮箱系统在 AWS。

响应头特征更直接:

curl -sI https://example.com/ | grep -iE 'server|x-amz|x-oss|x-cos|x-ms|x-goog'
响应头判断
Server: AmazonS3 / x-amz-request-idAWS S3
Server: AliyunOSS / x-oss-request-id阿里云 OSS
x-cos-request-id腾讯云 COS
x-obs-request-id华为云 OBS
x-ms-request-id / x-ms-versionAzure
x-goog-generation / x-guploader-uploadidGCP

有一个坑:CDN 会把响应头改掉,Server 可能被重写成 nginx 之类。这时候要去看错误页。故意请求一个不存在的路径,很多云厂商的错误页会带自己的特征,比如 S3 返回 XML:

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message>...

阿里云 OSS 的错误页也长这样,但错误码和 EC 字段不同。错误页比正常页更难伪装。

2.2 证书透明日志

证书透明(Certificate Transparency)日志是个被低估的信息源。所有公开 CA 签发的证书都会进 CT 日志,而企业在给内部系统签证书时,往往不会想到这些域名会因此暴露。

crt.sh 的查询方式:

# 网页查询
https://crt.sh/?q=%25.example.com

# JSON API,写脚本时用这个
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -u

真正有价值的不是主域名,是这些看起来像内部系统的子域:

gitlab.example.com
jenkins.example.com
harbor.example.com
kibana.example.com
oss-test.example.com
k8s-dashboard.example.com

我看到过的真实案例里,harbor.example.com 这种私有镜像仓库通过 CT 日志暴露的比例相当高,而镜像仓库一旦能匿名 pull,里面的 Dockerfile 和源码基本等于把内网架构图送出来。

2.3 favicon hash 定位同源代码

同一个系统部署在多台机器上时,favicon 的 hash 是一样的。FOFA 和 Shodan 都支持用这个 hash 反查,是找"同一套系统还部署在哪"最快的手段。

# 计算 favicon 的 mmh3 值
python3 -c "
import mmh3, requests, codecs
r = requests.get('https://example.com/favicon.ico')
print(mmh3.hash(codecs.lookup('base64').encode(r.content)[0]))
"

拿到值之后:

# FOFA
icon_hash="-247388890"

# Shodan
http.favicon.hash:-247388890

这个技巧在找测试环境时特别好用。生产环境上了 WAF,但测试环境往往用的还是同一套前端,favicon 一致,搜出来的 IP 直接就是没防护的那一台。

2.4 云厂商 IP 段反查

知道用了哪家云之后,反过来可以用云厂商公开的 IP 段去圈定资产。AWS 提供了机器可读的 IP 段列表:

curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq -r '.prefixes[] | select(.region=="ap-northeast-1") | .ip_prefix' | head

# 只取 EC2 的段
curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | jq -r '.prefixes[] | select(.service=="EC2") | .ip_prefix'

这个文件里的每个网段都带 service 字段(S3、EC2、CLOUDFRONT),可以用来判断某个 IP 属于什么服务。

阿里云和腾讯云没有同等质量的单一官方 JSON,实操里走 APNIC 的 ASN 导出。阿里云主要 AS 是 AS45102 和 AS37963,腾讯云是 AS132203 和 AS45090:

# 导出某个 AS 的全部网段
whois -h whois.apnic.net -i origin AS45102 | grep -E '^inetnum|^route'

# 或者用 whois 里的组织信息直接判断
whois 47.98.1.1 | grep -iE 'org|netname|descr'

阿里云杭州节点 whois 会带 Alibaba (China) Technology Co., Ltd. 这类信息,AWS 会带 Amazon Technologies Inc.。ASN 导出法得到的网段不如 AWS 官方 JSON 精确,用之前要抽样验证,这条标为待验证。

2.5 测绘语法

FOFA、Quake、Hunter、Shodan 这类测绘平台是云资产发现的主力。常用的语法组合:

# 按云存储特征找
body="AliyunOSS"
body="<Error><Code>NoSuchBucket</Code>"
header="x-oss-request-id"

# 按证书找
cert="*.aliyuncs.com"
cert="example.com"

# 按 favicon 找同名系统
icon_hash="-247388890"

# 按域名收敛
domain="example.com"
domain="example.com" && port="6443"

最后一条特别有用。port="6443" 是 K8s API Server 的默认端口,port="2379" 是 etcd,port="10250" 是 kubelet,port="8080" 可能是旧版 API Server。把 domain="example.com" 和这些端口组合,经常能直接把集群暴露面捞出来:

domain="example.com" && (port="6443" || port="2379" || port="10250")

这部分的技术细节在第五篇展开,这里只要先知道怎么发现。

三、找真实 IP,绕过云 WAF

云 WAF 的本质是 DNS 解析到 WAF 的节点,流量经 WAF 转发回源站。所以只要找到源站真实 IP,就能直连绕过 WAF 的所有规则。这是第一篇里性价比最高的一个动作。

按优先级排列的几种办法:

历史 DNS 记录。 域名上 CDN 之前的解析记录会被各种历史 DNS 数据库留存。

# 常见的历史解析查询平台,注意只能查已公开的被动数据
# https://securitytrails.com/  https://dnsdumpster.com/  https://viewdns.info/iphistory/

证书透明日志找未上 CDN 的子域。 主站上了 WAF,但 mail.example.com、oa.example.com、test.example.com 这类子域往往还在源站。用 2.2 节的 crt.sh 查询列出所有子域,逐个解析,看哪个 IP 不在云 WAF 的网段里。

MX 和 SPF 记录。 邮件服务器几乎不可能放在 CDN 后面,而且通常和主站同一个机房。

dig +short example.com MX
# 拿到 mail.example.com 再解析
dig +short mail.example.com A

直接扫全网段找特征。 如果知道源站大概在哪个云厂商哪个地域,可以用 favicon hash 或页面特征在测绘平台里反查,这个方法对小型站点特别有效。

IPv6 和非常规端口。 很多企业只给 IPv4 上了 WAF,AAAA 记录直接指向源站。

dig +short AAAA www.example.com

找到疑似源站后,验证方法是用 Host 头直连:

curl -sk -H "Host: www.example.com" https://1.2.3.4/ -I

如果返回的页面和走 WAF 的一致,基本可以确认是源站。这一步在授权测试里要写进报告,因为"WAF 可被绕过"本身就是一个独立的高危发现。

四、代码仓库里的凭证

结论前置:GitHub 是云凭证泄露的第一大来源,没有之一。

开发者本地调试时把 AK/SK 写进配置文件,提交时忘了加 .gitignore,这是最常见的场景。更麻烦的是,即使后来删掉了,历史提交里还在。

4.1 GitHub 搜索语法

GitHub 的代码搜索支持限定符,组合起来威力很大:

# 按组织搜配置文件
org:example-company filename:.env
org:example-company filename:config.json
org:example-company path:.aws

# 按文件类型搜
org:example-company extension:yml "accessKey"
org:example-company extension:tf "access_key"

# 按内容搜
org:example-company "AKIA"
org:example-company "LTAI"
org:example-company "SecretAccessKey"

# 按路径搜
path:.git-credentials
path:config.json "password"

# 一起用
org:example-company in:file "access_key_id"

几个实战经验:

  • 搜公司名不一定要用准确的 GitHub 组织名,用公司拼音、英文简称、邮箱后缀(@example.com)往往能捞到员工个人仓库。
  • in:file 是搜文件内容,in:path 是搜路径,in:readme 只在 README 里搜。默认是全文搜索,容易漏。
  • 中文项目的配置文件命名习惯不同,多试 application-prod.yml、config-dev.properties、settings.py。
  • GitHub Code Search 只索引默认分支。 被删除的分支、只存在于历史提交里的凭证,用网页搜索是搜不到的,必须克隆下来本地扫。
  • 网页版 Code Search 支持正则,但 REST API 和 gh search code 不支持。想批量拉只能用 gh api 拼查询,或者直接克隆仓库。

4.2 高价值文件名清单

按实际泄露频率排序,这个清单可以直接当字典用:

.env
.env.production
.env.local
config.json
config.yaml
config.yml
application.yml
application-prod.yml
application-dev.yml
settings.py
local_settings.py
database.yml
docker-compose.yml
Dockerfile
terraform.tfstate
terraform.tfstate.backup
*.tfvars
.git-credentials
.npmrc
.pypirc
.docker/config.json
id_rsa
*.pem
*.p12
kubeconfig
config
credentials

搜索时按内容关键词组合:

org:example-company filename:.env DB_PASSWORD
org:example-company extension:json secret
org:example-company path:config access_key
org:example-company language:yaml secret_key
org:example-company in:file "aws_secret_access_key"

terraform.tfstate 值得单独说,因为它的危害最直接。

4.3 terraform.tfstate 为什么是重灾区

Terraform 用 state 文件记录资源的真实状态,为了保证 apply 时能对比差异,它会把资源创建时的属性原样存进去,包括数据库密码、AK/SK、连接串。这是一份明文文件。

一个真实的 state 片段长这样:

{
  "version": 4,
  "terraform_version": "1.5.7",
  "resources": [
    {
      "mode": "managed",
      "type": "alicloud_db_instance",
      "name": "prod",
      "instances": [
        {
          "attributes": {
            "instance_name": "prod-rds",
            "connection_string": "rm-xxxx.mysql.rds.aliyuncs.com",
            "account_name": "app_user",
            "password": "Prod%2023!Pass",
            "port": "3306"
          }
        }
      ]
    },
    {
      "mode": "managed",
      "type": "alicloud_ram_access_key",
      "name": "deploy",
      "instances": [
        {
          "attributes": {
            "access_key_id": "LTAI5tXXXXXXXXXXXXXX",
            "secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "status": "Active"
          }
        }
      ]
    }
  ]
}

拿到这份文件,等于同时拿到了 RDS 的账号密码和一组可用的 RAM AK/SK。而且 state 文件经常被顺手传到对象存储里当备份,或者提交到代码仓库。

搜索时专门盯这几个文件名:

terraform.tfstate
terraform.tfstate.backup
*.tfstate
*.tfvars

4.4 工具

trufflehog 做仓库全量扫描,包括历史提交:

# 扫 GitHub 组织
trufflehog github --org=example-company --only-verified

# 扫单个仓库
trufflehog git https://github.com/example-company/app.git --only-verified

# 扫本地目录(包括 .git 历史)
trufflehog filesystem ./app-repo --only-verified

--only-verified 很关键。加上它之后 trufflehog 会真的去调用云 API 验证凭证是否有效,能过滤掉大量误报和已经失效的密钥。不加的话输出里可能几百条都是假的。

gitleaks 更适合接进 CI 或者扫本地:

# 扫当前目录的 git 历史
gitleaks detect --source . -v

# 从生成到现在的完整历史
gitleaks detect --source . --log-opts="--all" -v

# 只扫工作区不扫历史
gitleaks protect --source . -v

# 输出 JSON 报告
gitleaks detect --source . -f json -r report.json

gitrob 用 GitHub API 遍历组织内所有仓库的提交历史,适合做组织级排查,但项目已归档,速度慢且容易触发 API 限速。

ggshield 兼顾扫描和 Git hook 拦截:

ggshield secret scan repo https://github.com/example-company/app.git
ggshield secret scan path -r ./src

GitDorker 把 dorks 和 GitHub Token 池结合起来批量跑,适合在大批量目标上做初筛:

python GitDorker.py -tf tokens.txt -q example.com -d dorks/alldorksv3

gsil 的关键词和正则写在 config.py 里,改完直接跑:

python gsil.py --github --tokens tokens.txt

如果只是想知道某个仓库里有什么,先克隆下来再本地扫,比在线搜更快也更全:

git clone --mirror https://github.com/example-company/app.git
cd app.git
git log --all --oneline | head -50

# 在全部历史里搜内容(-S 是"增删了这个字符串的提交")
git log -p --all -S 'AKIA'

# 直接在所有对象里 grep
git rev-list --all | xargs git grep -E 'AKIA[0-9A-Z]{16}'

# 看所有分支里曾经出现过的敏感文件
git log --all --full-history --name-only -- '*.tfstate*' | sort -u
git log --all --diff-filter=D --name-only | grep -iE '\.env|credential|key|secret' | sort -u

--mirror 会把所有分支和标签都拉下来,很多泄露只存在于已被删除的分支里。

对 terraform.tfstate 可以直接用 jq 把关键字段捞出来:

curl -s https://example.com/terraform.tfstate | \
  jq '.resources[].instances[].attributes | {password, access_key, secret_key, private_key, connection_string}'

4.5 CI 日志泄露

CI 的构建日志是另一个高发区。原因有两个:一是 CI 环境里本来就注入了部署用的云凭证;二是很多脚本会 echo 出环境变量做调试,忘了删。

GitLab CI 的例子:

deploy:
  script:
    - echo "Deploying with $ALIYUN_AK"
    - aliyun oss cp ./dist oss://prod-bucket/ --access-key-id $ALIYUN_AK --access-key-secret $ALIYUN_SK

如果这个 job 的日志是公开的,$ALIYUN_AK 会展开成明文。GitLab 对标记为 masked 的变量做脱敏,但脱敏规则是基于固定字符串匹配的,只要变量被拼接、截断、base64 编码,脱敏就失效:

# 这几种写法都能绕过 masked 变量脱敏
echo $ALIYUN_AK | base64
echo "prefix_${ALIYUN_AK}_suffix"
echo $ALIYUN_AK | rev

对应的检查路径:

https://gitlab.example.com/example-group/example-project/-/jobs
https://github.com/example-company/app/actions

GitHub Actions 的日志同理,echo ${{ secrets.AWS_SECRET_ACCESS_KEY }} 是典型的自杀式写法,GitHub 会尝试脱敏,但同样可以被编码绕过。

批量拉 Actions 日志:

gh run list -R example-company/app -L 100 --json databaseId \
  | jq -r '.[].databaseId' \
  | xargs -I{} gh run view {} -R example-company/app --log

配合正则直接过滤凭证:

gh run list -R example-company/app -L 100 --json databaseId \
  | jq -r '.[].databaseId' \
  | xargs -I{} gh run view {} -R example-company/app --log \
  | grep -E 'LTAI|AKIA|AKID|password|token' | sort -u

五、AK/SK 的识别与有效性验证

5.1 各厂商凭证格式

厂商AccessKeyId 前缀总长度字段名备注
AWSAKIA(长期)、ASIA(临时)20AccessKeyId / SecretAccessKeyASIA 是 STS 临时凭证,必须带 SessionToken
阿里云LTAI24AccessKeyId / AccessKeySecret主账号 AK 和 RAM 用户 AK 同格式
腾讯云AKID36SecretId / SecretKey
华为云无固定前缀20AK / SK
GCP无–Service Account JSON整个 JSON 文件,含 private_key

5.2 提取正则

写脚本抓泄露时可以直接用这几条。AWS 那两条来自公开的 gitleaks 规则集:

# AWS Access Key ID
(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}

# AWS Secret Access Key
(?i)aws(.{0,20})?(?-i)['\"][0-9a-zA-Z/+]{40}['\"]

# 阿里云 AccessKeyId
LTAI[A-Za-z0-9]{12,20}

# 阿里云 AccessKeySecret(32位字母数字,需要上下文限定,否则误报极高)
(?i)access[_-]?key[_-]?secret['\"]?\s*[:=]\s*['\"]?([A-Za-z0-9]{30})['\"]?

# 腾讯云 SecretId
AKID[A-Za-z0-9]{13,32}

# GCP Service Account
"type":\s*"service_account"

# 通用私钥
-----BEGIN (RSA|EC|OPENSSH|PGP) PRIVATE KEY-----

实践提醒:阿里云和腾讯云的 Secret 本身没有固定前缀,纯靠正则误报率极高。正确做法是先定位 AccessKeyId,然后取它上下 5 行去找同名的 secret 字段,而不是全网正则撒网。这一点在写自研扫描脚本时特别重要,否则一半时间都在处理误报。

5.3 验证凭证

拿到疑似凭证后,第一件事是验证有效性,而且只做只读调用。这一步的原则是:先证明凭证有效并摸清权限范围,绝不直接执行任何写操作。

AWS:

export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="ap-northeast-1"

# 临时凭证才需要这一行
# export AWS_SESSION_TOKEN="..."

# 第一条命令:我是谁
aws sts get-caller-identity

返回:

{
    "UserId": "AIDAXXXXXXXXXXXXXXXXX",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/deploy-bot"
}

从 ARN 就能判断这是 IAM 用户还是角色(user/ vs assumed-role/),以及账号 ID 和用户名。账号 ID 很重要,后面所有资源 ARN 都带它。

阿里云:

aliyun configure --mode AK \
  --access-key-id LTAI5tXXXXXXXXXXXXXX \
  --access-key-secret xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
  --region cn-hangzhou

aliyun sts GetCallerIdentity

腾讯云:

tccli configure set secretId AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
tccli configure set secretKey xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
tccli configure set region ap-guangzhou

tccli sts GetCallerIdentity

华为云:

hcloud configure set --access-key=XXXXXXXX --secret-key=XXXXXXXX
hcloud IAM KeystoneListAuthProjects

如果凭证是 STS 临时凭证,注意看有效期。ECS 元数据拿到的临时凭证默认 6 小时轮换,K8s IRSA 的默认 1 小时(可配到 12 小时)。这意味着你必须在一个时间窗口内完成所有枚举和利用,超时后凭证失效,得回去重新取。

六、配置文件与备份文件泄露

除了代码仓库,Web 站点本身的配置文件泄露也是高频入口。

常见路径字典:

/.env
/.env.bak
/.git/config
/.git/HEAD
/.svn/entries
/config.json
/config.js
/appsettings.json
/application.yml
/application.properties
/config/database.php
/WEB-INF/web.xml
/backup.zip
/www.zip
/db.sql
/dump.sql
/phpinfo.php

Spring Boot 应用的 Actuator 端点尤其值得盯,因为它经常把整个环境变量打出来:

# 列出所有可用端点
curl -s http://target.com/actuator | jq

# env 端点,环境变量全在里面,包括云凭证
curl -s http://target.com/actuator/env

# 高价值端点
curl -s http://target.com/actuator/configprops
curl -s http://target.com/actuator/heapdump -o heapdump.bin

/actuator/heapdump 是个内存快照,里面的字符串可以直接捞:

# 用 strings 捞凭证特征
strings heapdump.bin | grep -E 'LTAI|AKID|AKIA|password|secret' | sort -u

Git 源码泄露的恢复:

# 用 git-dumper
pip install git-dumper
git-dumper http://target.com/.git/ ./target-src

# 或者 GitHack
python2 GitHack.py http://target.com/.git/

.git/config 经常还带着凭证:

[remote "origin"]
    url = https://deploy:ghp_xxxxxxxxxxxx@github.com/example-company/app.git

这里的 ghp_ 是 GitHub Personal Access Token,能直接访问该组织的私有仓库。

七、对象存储攻击

对象存储是云上最常见的错误配置载体,也是黑盒下最容易确认的东西。

7.1 权限错误怎么判断

对象存储的权限问题归结为三种:匿名 List、匿名 Get、匿名 Put。危害依次递增,Put 意味着可以投毒或者挂马。

命名规则要按目标习惯构造。S3 桶名有硬性约束:3 到 63 个字符,只能用小写字母、数字和连字符,不能用下划线,全局唯一。所以爆破字典按 {公司名}-{环境}-{用途} 这个模式生成命中率最高。常见的组合:公司名、公司名 + 环境(prod/test/dev)、公司名 + 用途(static/upload/backup/log)、公司名 + 区域。

响应码的含义要记清楚:

响应含义
200 + XML 列表桶存在且允许匿名 List,最严重
403 AccessDenied桶存在,但不允许匿名 List(可以继续试 GetObject)
404 NoSuchBucket桶不存在,如果 CNAME 指向它就可以接管
301 PermanentRedirect桶存在但在别的 region

用 HEAD 请求快速判断:

curl -sI https://example-company-backup.s3.amazonaws.com | head -3

7.2 逐条命令

AWS S3 用 --no-sign-request 做匿名访问测试:

# 列出桶(需要 ListBucket 权限开放)
aws s3 ls s3://example-company-backup --no-sign-request

# 递归列出所有对象,含大小和时间
aws s3 ls s3://example-company-backup --recursive --no-sign-request --human-readable

# 下载单个文件
aws s3 cp s3://example-company-backup/db/dump.sql.gz . --no-sign-request

# 异步全量拉取
aws s3 sync s3://example-company-backup ./dump --no-sign-request

# 测试写权限(授权测试中这一步要先确认,写操作会产生真实数据)
aws s3 cp ./test.txt s3://example-company-backup/test.txt --no-sign-request

阿里云 OSS 用 ossutil:

# 配置一个匿名 profile
ossutil config -e oss-cn-hangzhou.aliyuncs.com

# 列桶
ossutil ls oss://example-company --limited-num 100

# 递归列出并下载
ossutil ls oss://example-company-backup/ -r
ossutil cp -r oss://example-company-backup/ ./dump

# 测试写权限
ossutil cp ./test.txt oss://example-company-backup/test.txt

也可以用 s3cmd 或 aws cli 打 OSS,因为 OSS 兼容 S3 协议:

aws s3 ls s3://example-company-backup --endpoint-url https://oss-cn-hangzhou.aliyuncs.com --no-sign-request

不想装客户端时,直接发 HTTP 请求更省事。OSS 支持 V2 风格的 list-type 参数:

curl -s "https://example-company.oss-cn-hangzhou.aliyuncs.com/?list-type=2&max-keys=20"

返回的是 XML,用 xmllint 或 python 解析出 Key 列表即可。

腾讯云 COS 用 coscmd:

coscmd config -e cos.ap-guangzhou.myqcloud.com
coscmd list -r
coscmd download -r / ./

批量爆破桶名的工具:

# s3scanner:扫 S3 权限
s3scanner scan --bucket-file buckets.txt
s3scanner scan --bucket-file buckets.txt --enumerate

# cloud_enum:一次扫 AWS、Azure、GCP
python3 cloud_enum.py -k example -k example-company -l out.txt

# OSS 没有现成好用的枚举器,用 ffuf 自己搭
ffuf -u https://FUZZ.oss-cn-hangzhou.aliyuncs.com -w names.txt -mc 200,403

云函数 FC 的临时桶也值得看,阿里云 FC 会给每个函数分配一个 *-fc-* 命名的临时 OSS 桶,有时候权限配得更松。

镜像仓库是同一类问题里最容易被忽略的一个。私有 registry 只要暴露在公网且没开认证,可以直接列出全部仓库:

# Docker Registry v2 API,未授权时直接返回仓库列表
curl -s http://1.2.3.4:5000/v2/_catalog

# 列出某个仓库的 tag
curl -s http://1.2.3.4:5000/v2/example-app/tags/list

# 拉取 manifest 和层,接着可以用 dive 或直接解压层去找源码
docker pull 1.2.3.4:5000/example-app:latest

云厂商的托管仓库也有对应的枚举命令:

# AWS ECR
aws ecr list-repositories
aws ecr describe-images --repository-name example-app

# Azure ACR
az acr repository list -n exampleregistry

镜像层里能翻出源码、.env、构建时的凭证,甚至 CI 的 token。这一部分在第六篇会展开讲怎么从镜像反推 CI 配置。

7.3 Bucket 接管

原理是这样:如果站点有个子域 static.example.com 通过 CNAME 指向 static-example.s3.amazonaws.com,而对应的桶已经被删除,那么这个桶名就空出来了,任何人都可以注册。攻击者注册同名桶后,就能控制这个子域返回的内容。

确认流程:

# 第一步:看 CNAME
dig +short static.example.com CNAME
# 返回 static-example.s3.us-east-1.amazonaws.com.

# 第二步:确认桶不存在
aws s3 ls s3://static-example --no-sign-request
# 返回 NoSuchBucket 说明桶名可用

# 或者直接 curl,看错误码
curl -sI https://static.example.com/ | head -5
<Error><Code>NoSuchBucket</Code><Message>The specified bucket does not exist</Message>

在授权测试里,这里的正确做法是证明可注册性,但不实际注册。报告里写清楚:桶名可用、CNAME 仍指向该桶、一旦被第三方注册即可控制 static.example.com 的内容,可用于 XSS、钓鱼、绕过 CSP。实际注册属于超出授权范围的行为。

7.4 桶里应该翻什么

按价值排序,这是我实际翻桶时优先看的东西:

目标说明
*.sql / *.sql.gz / *.bak数据库备份,直接等于全量数据
*.zip / *.tar.gz源码包,里面通常带 .env 和配置文件
config* / *credentials*直接就是凭证
.git/ 目录能还原完整源码和提交历史
application*.yml / .env数据库、Redis、云凭证
日志文件可能有内网 IP、接口路径、Token
用户上传文件身份证、合同、头像,涉及个人信息合规
terraform.tfstate云凭证 + 全部资源清单

一个实用技巧:桶里文件太多时,先按大小和时间排序,通常最新的备份文件和最大的压缩包价值最高。

aws s3 ls s3://example-company-backup --recursive --no-sign-request --human-readable \
  | sort -k3 -hr | head -20

八、把流程串成一条线

回顾一下这一篇的完整链路。假设目标是 example.com:

# 1. 判断云厂商
dig +short www.example.com CNAME
curl -sI https://example.com/ | grep -iE 'x-amz|x-oss|x-cos'

# 2. 拉子域
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r '.[].name_value' | sort -u

# 3. 找源站
for sub in mail oa test dev gitlab jenkins harbor; do
  dig +short $sub.example.com A
done

# 4. 代码仓库狩猎
trufflehog github --org=example-company --only-verified

# 5. 配置文件探测
for p in .env .git/config actuator/env config.json; do
  curl -s -o /dev/null -w "%{http_code} $p\n" https://example.com/$p
done

# 6. 对象存储
aws s3 ls s3://example-company-backup --recursive --no-sign-request

# 7. 验证拿到的凭证
aws sts get-caller-identity
aliyun sts GetCallerIdentity

每一步的输出都是一张表的输入,最终收敛成这样的资产清单:

资产类型归属云暴露面风险等级
static.example.comS3/OSS 桶AWS匿名 List高
gitlab.example.comGitLab自建CT 日志暴露中
AKIA...IAM 用户密钥AWSGitHub 泄露严重
terraform.tfstate状态文件阿里云对象存储可读严重

这张表就是第二篇和第三篇的输入。


下一篇讲第二篇:从 SSRF 到云凭证。会逐个给出 AWS、阿里云、腾讯云、华为云、GCP、Azure 的元数据服务地址、必需请求头、凭证获取路径,以及 SSRF 打元数据时遇到的各种限制和绕过方式。核心问题是:一个看起来只能读内网页面的 SSRF,怎么变成云账号里的第一组凭证。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇