⚠️ SSRF에 대한 공격/우회방안/대응방안 등은 Cullinan 페이지를 통해 관리하고 있습니다. 더 자세한 정보를 얻고 싶으시다면 Cullinan > SSRF 페이지로 접근 부탁드려요. 참고로 Cullinan > SSRF 가 훨씬 많은 데이터와 최신화된 정보를 다룹니다.
SSRF 관련해서 테스트하던 중 Host name 검증 로직에 제대로 걸리게 되었습니다. 다만 조금 봐보니 IP는 따로 안보는 것 같아 DNS CNAME, A Recode를 이용해서 우회 가능할 것 같았고, 실제로 잘 되어서 글로 공유드려봅니다.
Common SSRF
서버가 직접 접근해서 데이터를 가져오는 ssrf.php 라는 페이지가 있다고 칩시다.
GET /ssrf.php?u=https://www.hahwul.com HTTP/1.1
이런 페이지에는 일반적으로 아래 형태로 SSRF가 가능합니다.
GET /ssrf.php?u=http://127.0.0.1/server-status
GET /ssrf.php?u=https://internal_domain
GET /ssrf.php?u=ssh://~~~
CNAME
CNAME(Canonical Name)은 서브 도메인에 할당한 별칭입니다. 도메인 소유주는 자게 도메인에 한해서 쉽게 추가하거나 삭제할 수 있습니다.
- abcd.test.com => test.com
- efgh.test.com => test.com
acde, efgh 모두 test.com의 이름 중 하나로 인식됩니다. abcd.test.com으로 접근시 test.com으로 붙은 것과 동일한 효과죠.
A Record
A record는 도메인과 이를 매핑하는 IP를 의미합니다. 우리가 a 도메인에 b ip 할당했다는 이야기가 바로 A Record를 설정했다는 이야기입니다.
- abcd.test.com => 192.168.0.2
- efgh.test.com => 192.168.0.3
Bypass SSRF
CNAME과 A Record는 각각 도메인에 매핑된 다른 도메인, IP를 가리키기 때문에 내부주소나 사설 대역 IP를 지정해서 SSRF의 방어로직을 우회하고 내부망으로 접근 시도를 할 수 있습니다. 제가 localhost.hahwul.com
이라고 하나 만들었습니다.
nslookup localhost.hahwul.com
.....
Non-authoritative answer:
localhost.hahwul.com canonical name = localhost.
Name: localhost
Address: 127.0.0.1
위와 같이 127.0.0.1을 바라보게 CNAME을 설정해두었습니다. SSRF 쿼리를 지정한 도메인으로 날려보면 localhost 를 읽어올 수 있습니다.
ssrf.php?u=http://localhost.hahwul.com:3000
한계점
단 이 방법의 한계점은 확실히 존재합니다. k8s가 많이 대중화되면서 많은 서비스들이 app 앞단에 ingress를 두기 시작했습니다. 그리고 ingress는 기본적으로 hostname 기반의 라우팅 정책을 펼치기 때문에 IP를 내부로 지정하더라도 해당 내부IP가 hostname으로 검증하는 경우 의미있는 접근을 만들기 어려워집니다. 다만 그래도 트래픽 자체를 내부로 넘기는데에는 문제가 없기 떄문에 충분히 좋은 공격 방법 중 하나입니다 :D
더 나아가서
A Record 말고도 IPv6를 의미하는 AAAA Record, 그리고 Glue Record 등 DNS에서 설정할 수 있는 도메인/IP의 종류는 여러가지입니다. 이러한 것들을 이용해서 SSRF에 충분히 활용할 수 있으니 한번 찾아보시면서 알아두면 언젠가는 꼭 도움될거라 생각됩니다.