📌 Bài viết này thuộc chuỗi write-up quá trình tiếp cận và đáp án cho các bài lab từ Portswigger Web Academy mà mình đã làm trong thời gian thực tập tại NCSC.
Mình không thích lắm cái cách mấy idol viết write-up lab theo kiểu cứ như thể họ đã biết đáp án từ đầu – họ chẳng bao giờ đủ kiên nhẫn để giải thích cặn kẽ tại sao họ lại làm một cái gì đó. Vậy nên, write-up phong cách bạn Tiểu ra đời. Mình mong là bạn có được những câu trả lời thoả đáng qua loạt bài này. Những ý kiến góp ý, thảo luận, báo lỗi – luôn luôn được hoan nghênh dưới mục comment.
Đề bài
Ngữ cảnh: Tính năng thay đổi email của bài lab này có thể bị tấn công CSRF.
Mục tiêu: Viết HTML sử dụng CSRF để đổi email của nạn nhân, sử dụng Exploit server đã cho.
Thông tin sẵn có: Tài khoản wiener:peter
Phân tích
Lần đầu nhìn thấy bài lab này, tôi cảm thấy khá khó hiểu về cả những yêu cầu lẫn những tính năng mới trong lab.
Vì đây là bài lab đầu tiên nếu bạn đi theo mạch của hướng dẫn tấn công CSRF trên Portswigger, tôi sẽ giải thích từng thứ kỹ hơn một chút, để nhỡ đâu bạn có đặt dấu hỏi như tôi thì cũng có lời giải thích.
Khi vào bài lab, bạn sẽ thấy trên phần Lab Header có một nút mới là “Exploit server”. Bấm vào đây và bạn sẽ thấy một thứ tương tự như thế này:

Exploit server, hiểu nôm na là máy chủ mà lab cung cấp, máy chủ này sẽ lưu giữ (host) các file (chứa HTML bạn viết, mã độc, vân vân…) mà bạn viết trong cửa sổ này. Nó được dùng để minh hoạ cho mục đích thực hành trên Web Security Academy.
Trên thực tế, nếu bạn muốn thử các cuộc tấn công, bạn phải tự cài cắm và thiết lập máy chủ của riêng bạn.
File: Tên file mà bạn lưu mã của mình trên server. Tên mặc định là exploit như trong hình.
Head: Tương tự như HTTP header, đây chính là phần header của phản hồi sẽ trả về tới máy nạn nhân.
Body: Giống với HTTP Body, đây là phần thân của phản hồi.
Điều kiện để một cuộc tấn công CSRF khả thi
Không phải lúc nào có form là cũng CSRF được, mặc dù là trí tò mò luôn thúc đẩy chúng ta là “hãy bem thử cái này xem sao”. Bạn có thời gian thì cứ thử, nhưng nếu nguồn lực có hạn thì tôi thấy làm bài bản vẫn có cái lợi của nó.
Chuẩn theo sách giáo khoa Portswigger thì ta cần có 3 điều kiện như sau:
- Một hành động có liên quan. Có một hành động trong ứng dụng mà kẻ tấn công có lý do để thực hiện. Đây có thể là hành động đặc quyền (chẳng hạn như sửa đổi quyền cho người dùng khác) hoặc bất kỳ hành động nào đối với dữ liệu dành riêng cho người dùng (chẳng hạn như thay đổi mật khẩu của chính người dùng đó).
- Xử lý phiên dựa trên cookie. Việc thực hiện hành động bao gồm việc đưa ra một hoặc nhiều yêu cầu HTTP và ứng dụng chỉ dựa vào cookie phiên để xác định người dùng đã thực hiện yêu cầu. Không có cơ chế nào khác để theo dõi phiên hoặc xác thực yêu cầu của người dùng.
- Không có thông số yêu cầu không thể đoán trước. Các yêu cầu thực hiện hành động không chứa bất kỳ tham số nào mà kẻ tấn công không thể xác định hoặc đoán được giá trị.
Ví dụ: khi khiến người dùng thay đổi mật khẩu, chức năng này sẽ không dễ bị tấn công nếu kẻ tấn công cần biết giá trị của mật khẩu hiện có.
Lời giải
- Đăng nhập vào tài khoản được cho và thấy có tính năng “Change email”. Thử thay đổi email bằng tính năng mới này và thấy là đổi được thật.
- Nghiên cứu request và response trả về khi bạn làm thao tác trên. Nhận thấy tính năng trên kia là một cái form HTML đơn giản.
- Bắt đầu viết cái CSRF đầu tay. Bạn có thể viết thủ công hoặc dùng tool nó viết sẵn cho:
- Nếu bạn có Burp Suite Pro, bạn có thể chuột phải → Engagement Tool → Generate CSRF PoC
- Nếu bạn dùng phiên bản người nghèo (aka. Community) như tôi thì bạn phải tự động tay động chân. Viết tương tự như dưới đây:
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<form action="<https://0a43007b04952094834b34e500d10086.web-security-academy.net/my-account/change-email>" method="POST">
<input type="hidden" name="email" value="hi@gmail.com" />
<input type="submit" value="Submit request" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
</body>
</html>
Giải thích một chút về cái PoC:
- Phần
form
: Đây là một cái form HTML đơn giản, với hai trườnginput
:- Thứ nhất là
email
với giá trị ta muốn đổi thành. Cái này được ẩn đi để thằng người dùng nó không nhìn thấy là ta đang “hắc” nó. - Thứ hai là một cái nút “submit” vô thưởng vô phạt với tên “Submit request”, thứ mà thằng nạn nhân xấu số sẽ nhìn thấy. Cái này ở đó với mục đích trang trí thôi, vì form này sẽ tự nộp mà không cần tương tác — do ta setup phía dưới phần
script
.
- Thứ nhất là
- Phần
script
: Đoạn này thì mới đầu tôi cũng thấy hơi lạ, nhưng cơ bản là nó sẽ thực hiện việc tự submit cái form ở trên mà người dùng không phải làm gì cả.- Phương thức
history.pushState()
thêm một mục mới vào ngăn xếp lịch sử phiên của trình duyệt. - Lệnh
document.forms[0].submit()
gửi một biểu mẫu bằng JavaScript, có thể được thực hiện mà không cần người dùng nhập hoặc tự động thực hiện khi người dùng nhấp vào liên kết hoặc nút.forms[0]
chính là cái form duy nhất này. Nếu bạn thắc mắc tại sao có thể đọc thêm về Document Object Model (DOM).
- Phương thức
4. Copy paste thứ này vào phần Body trong Exploit server. Kiểm tra xem nó hoạt động hay chưa bằng nút “View exploit”.
5. Nếu như hành động “tự vả” thành công, hãy đảm bảo email trong PoC khác với email hiện tại của bạn. Đổi thành cái gì đó tuỳ ý. Chọn “Deliver exploit to victim”. Thế là xong.