SSRF
在之前学习文件包含的时候大概看过SSRF的攻击原理,主要是一种攻击方式的表示,请求资源的时候直接去获取内网资源。因此在理解这一块的过程中,我重新理了一下IP,主机,端口,域名以及内网的存在方式和关联方式
SSRF的原理和概念
SSRF是Server-side Request Forge的缩写,中文翻译为服务端请求伪造。产生的原因是由于服务端提供了从其他服务器应用获取数据的功能且没有对地址和协议等做过滤和限制。
SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,文档等等。SSRF漏洞通过篡改获取资源的请求发送给服务器(服务器并没有检测这个请求是否合法的),然后服务器以他的身份来访问服务器的其他资源。SSRF利用存在缺陷的Web应用作为代理攻击远程和本地的服务器。
攻击者通过构造一个请求,让服务器去访问内网资源,然后把结果返回给攻击者,从而实现了对内网的访问。
那么,为什么需要绕过呢?因为开发者可能会意识到SSRF的风险,所以对用户输入的URL做了一些限制(比如过滤内网IP、限制协议等)。攻击者为了突破这些限制,就需要使用一些技巧来绕过这些防护措施。
PHP中下面函数的使用不当会导致SSRF:
1 | file_get_contents():将整个文件或一个url所指向的文件读入一个字符串中。 |
伪协议
- file协议: 在有回显的情况下,利用 file 协议可以读取任意文件的内容 (就是用来读passwd的方法)
- dict协议:泄露安装软件版本信息,查看端口,操作内网redis服务等(用来对内网的服务进行查询)
- gopher协议:gopher支持发出GET、POST请求。可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。可用于反弹shell (像是一个万能胶 可以模仿出POST和GET协议可以打很多服务例如Mysql,FastCGI,redis)
- http/s协议:探测内网主机存活(探测存活主机)
挖掘SSRF漏洞:能够对外发起网络请求的地方,就可能存在SSRF漏洞。
SSRF的可利用情景
在具体利用场景和利用方式中提到了部分需要带一下的概念:
DNS(Domain Name System,域名系统)是互联网的一项核心服务,被称为“互联网的电话簿”。它的作用是将人类可读的域名(如 example.com)转换为计算机能识别的数字IP地址(如 192.0.2.1),从而实现通过域名访问网站。DNS 采用分布式数据库技术,确保了网络访问的快速和稳定。
DNS 的核心细节与工作原理功能: 解决IP地址难记的问题。用户在浏览器输入网址时,DNS 负责在幕后将域名翻译成正确的 IP 地址。
工作机制(解析过程): 当用户输入域名时,系统会向 DNS 服务器发起查询,依次通过根域名服务器、顶级域名服务器、权威域名服务器找到对应记录,最后返回 IP 地址给浏览器。
组成部分:
DNS 记录(Records): 存储域名与 IP 映射的文本记录,如 A 记录(域名对IP)。
DNS 服务器: 专门负责回答 DNS 查询的机器。
关键特性: 采用分布式系统,通过缓存(Cache)技术提高解析速度。
API(Application Programming Interface,应用程序编程接口)是一组规则、协议和工具,允许不同的软件程序相互通信、交换数据和调用功能。它作为不同组件或系统之间的中介,使开发者无需从头开发,即可整合现有功能,提高开发效率并促进系统集成。
核心概览:
功能与作用:API 允许系统安全、快速地分享数据和功能(如天气查询、登录认证),常用于连接前端和后端、将不同微服务连接起来。
工作原理:当一个应用程序(客户端)向 API 服务器发送请求(Request)时,服务器处理请求并返回响应(Response),通常采用 JSON 或 XML 格式。
常见类型:包括
RESTful API(最常用)、SOAP、GraphQL、gRPC和WebSocket API。典型应用:例如使用地图API将百度/谷歌地图集成到自建网站、使用支付API处理线上交易、利用社交媒体API实现第三方登录。
API 与 SDK 的区别:
API:是接口定义,仅提供访问服务的方法。
SDK(Software Development Kit):软件开发工具包,通常包含 API 以及相关的文档、工具和代码示例,是更完整的开发集成包。
实际可能出现SSRF的场景:
从远程服务器请求资源(
Upload from URL,Import &Export RSS feed)数据库内置功能(
Oracle[UTL_HTTP]、MongoDB、MSSQL、Postgres、CouchDB)Webmail 收取其它邮箱邮件(
POP3/IMAP/SMTP)文件处理,编码处理,属性信息处理(
ffpmg,ImageMaic,DOCX,PDF,XML处理器)
利用条件:被攻击的服务等没有开启加密传输与鉴权。
利用技巧:
端口扫描(扫描内网)
攻击内网存在漏洞的服务
攻击Web应用进行指纹识别及其中的漏洞
如果PHP安装了expect扩展,可以通过expect协议执行系统命令
dos攻击
file 协议暴力枚举敏感文件
Redis未授权漏洞的6种利用方法:
保存文件到
www目录,形成webshell创建
authorized_keys文件,利用ssh私钥登录服务器写计划任务(
/var/spool/cron/&/etc/cron.d/)slave of 8.8.8.8主从模式利用写入到
/etc/profile.d/用户环境变量修改开启AOF持久化纯文本记录
appendfilename
具体利用
其他表示方式
- 十进制IP:
127.0.0.1→2130706433 - 八进制IP:
127.0.0.1→0177.0.0.1 - 十六进制:
0x7f000001 - IPv6:
127.0.0.1→[::1]或localhost→ip6-localhost
利用域名解析:
- 注册域名
attacker.com,将其A记录指向192.168.1.1 - 提交
http://attacker.com,防御代码看到的是域名,不是IP
使用指向本地的域名:
127.0.0.1.nip.io(自动解析到127.0.0.1),这个地方xip.io也可以localhost.example.com(在自己的DNS服务器上解析到127.0.0.1)- 大小写变换:
LocalHost、LOCALHOST - 添加端口:
localhost:80vslocalhost - 使用Unicode字符:
locålhost
重定向:
当服务端对用户输入的URL进行校验时,可能会先检查域名或IP是否合法(例如是否属于内网)。但校验往往只针对初始URL,而忽略了后续可能发生的跳转。攻击者可以搭建一个外部服务器,该服务器返回一个HTTP 302重定向响应,将请求指向内网地址。服务端在收到重定向后,会跟随跳转并最终访问内网资源,从而绕过初始的校验。
- 攻击者提交一个看似合法的URL(例如
http://attacker.com/redirect)。 - 服务端校验该URL的域名
attacker.com为外网地址,通过检查。 - 服务端请求
attacker.com/redirect,该页面返回302状态码,并设置Location: http://192.168.1.1/admin。 - 服务端跟随跳转,最终请求了内网的
192.168.1.1/admin,获取敏感信息或执行操作。
补充理解
- 跳转类型:除了302,还有301永久重定向、307临时重定向等,均可利用。甚至可以利用一些开放在公网的跳转服务(如某些URL短链接服务)作为跳板。
- 局限性:某些服务端库可能默认不允许跟随跳转(如PHP的
allow_url_fopen默认不跟随),或可以配置最大跳转次数。但大多数HTTP客户端(如curl、Java的HttpURLConnection)默认会跟随重定向。 - 防御:禁用跟随重定向,或者对最终请求的URL再次进行校验。如果必须支持重定向,可以设置白名单或限制重定向的目标地址范围。
- 攻击者提交一个看似合法的URL(例如
存在协议限制
- 使用其他危险协议:
file://协议直接读取本地文件:file:///etc/passwddict://协议探测端口服务:dict://127.0.0.1:3306/infogopher://协议(功能强大的旧协议,可以构造各种请求)ftp://协议等
- 协议拼接:
http://127.0.0.1:80@evil.com(利用@符号)http://localhost\\@evil.com(利用解析差异)
URL解析器的差异
1 | # 防御代码的解析(可能使用Python的urllib.parse) |
常见解析差异:
http://foo@127.0.0.1:80@example.com/- 不同库解析出不同的主机http://127.0.0.1:80\\@example.com/- 反斜杠处理差异- URL编码差异:
http://%6c%6f%63%61%6c%68%6f%73%74= localhost
DNS重绑定利用解析时间差
原理:DNS Rebinding利用了DNS解析的时间差。服务端通常会先对用户提供的域名进行DNS解析,获取IP地址并校验是否为内网IP。如果校验通过,服务端才会发起实际的HTTP请求(此时会再次解析域名)。攻击者控制一个域名,并设置非常短的TTL(生存时间),使得第一次解析时返回一个公网IP(通过校验),第二次解析时返回内网IP。由于两次解析的结果不同,最终请求被发送到内网,而校验阶段却认为域名是安全的。
- 攻击者提交域名
evil.com,其DNS记录TTL设为0。 - 服务端第一次解析
evil.com,得到IP1.2.3.4(公网),校验通过。 - 服务端准备发起HTTP请求,此时再次解析
evil.com(因为TTL过期),得到内网IP192.168.1.1。 - 服务端向
192.168.1.1发送请求,成功访问内网。
- TTL的作用:TTL决定了DNS记录在本地缓存的时间。攻击者设置极短的TTL(如0),确保每次查询都重新解析,从而实现两次解析返回不同IP。
- 客户端缓存差异:你提供的TIPS非常关键:“JAVA请求DNS成功会缓存30s,PHP默认没有缓存,linux不会缓存,windows和mac会”。这意味着DNS重绑定攻击的成功率取决于服务端所在环境以及编程语言/库的DNS缓存行为。
- Java:JVM默认会对DNS查询结果进行缓存(
networkaddress.cache.ttl默认30秒,成功解析缓存30秒;负缓存10秒)。如果第一次解析结果被缓存,第二次请求直接使用缓存,就不会重新查询,攻击失败。但攻击者可以通过等待缓存过期或使用极短TTL迫使JVM重新查询(JVM是否遵守TTL取决于实现,通常忽略TTL而使用自己的缓存策略)。 - PHP:默认不缓存DNS结果,每次请求都会重新解析,因此容易受DNS Rebinding攻击。
- 操作系统层面:Linux一般不会缓存DNS(除非启用了nscd等),而Windows和macOS有DNS缓存服务。不过,大多数编程语言有自己的DNS缓存机制或使用系统库,情况较复杂。
- Java:JVM默认会对DNS查询结果进行缓存(
- 绕过缓存的方法:如果存在缓存,攻击者可以尝试使用不同的域名、子域名或IP,或者等待缓存超时。此外,某些技术如“DNS Rebinding with multiple A records”也可尝试。
防御
- 去除URL中的特殊字符
- 防止内网的其他表达方式,防止攻击者利用URL编码、换行、空格、Unicode字符等绕过正则表达式或黑名单过滤。例如,
192.168.1.1可能被写成192。168。1。1(中文句号)或192.168.1。1,通过特殊字符干扰IP格式检测。
- 判断是否属于内网IP,直接拒绝请求内网IP地址。
阻止对私有IP范围(如
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、127.0.0.0/8、0.0.0.0/8等)以及本地链路地址的访问。- IP地址可能有多种表示形式(点分十进制、十六进制、八进制、整数等),需要先规范化为标准点分十进制后再进行判断。
- 同时要考虑IPv6的私有地址范围(如
fc00::/7、::1等)。 - 如果业务需要访问某些内网资源(如内部API),应使用白名单而非单纯禁止。
- 如果是域名,将URL中的域名改为IP
- 提前解析域名得到IP,并用IP替换域名,后续请求直接使用IP,避免在请求阶段再次解析域名。
- 针对DNS Rebinding:因为DNS Rebinding利用的是两次解析(校验时和请求时)的时间差,而这里只解析一次并固化IP,彻底消除了时间差漏洞。
- 同时也可以防止域名指向内网IP的简单绕过(因为解析后会直接发现IP属于内网,在步骤2就被拦截)。
- 请求的URL为3中返回的URL
- 确保实际请求的地址是基于步骤3解析出的IP构造的,而不是原始域名。
- 防止后续任何依赖域名的攻击(如Host头注入、基于域名的访问控制绕过等)。
- 请求时设置Host Header为IP
将HTTP请求的
Host头设置为IP地址(而非原始域名)。防止攻击者通过域名触发服务端基于
Host头的路由(例如某些内网服务根据Host头来区分虚拟主机),从而可能将请求导向预期外的内网应用。也能避免某些利用
Host头进行的缓存投毒或权限绕过攻击。更安全的做法是在请求前校验
Host头是否与解析后的IP匹配,或限制只允许特定Host值。
- 不跟随30x跳转(若需跟随则从步骤1重新检测)
- 禁止自动跟随HTTP重定向,或者如果必须支持重定向,则对重定向后的URL重新执行整套检测流程。
- 递归检测时要防止无限循环(如跳转链过长或循环跳转),需设置最大跳转次数。
- 有些重定向可能使用其他状态码(301、307等),都需要处理。
