https://dreamhack.io/wargame/challenges/75
web-ssrf
flask로 작성된 image viewer 서비스 입니다. SSRF 취약점을 이용해 플래그를 획득하세요. 플래그는 /app/flag.txt에 있습니다. 문제 수정 내역 2023.07.17 css, html 제공 Reference Server-side Basic
dreamhack.io
SSRF 취약점 문제이다
내가 스스로 풀 수 있을리가 없으니 풀이를 보고 천천히 따라해 보았다 *^^*
일단 SSRF는 클라이언트 측 요청을 변조시키는 것이 아니라 서버측 자체의 요청을 변조하여 공격자가 원하는 형태의 악성 행위를 서버에 던져주면 서버가 검증 없이 그대로 받아 그에 따른 행위/응답을 해주는 공격을 말한다.
주로 사용자가 입력을 받아 서버가 직접 다른 웹이나 포트에 접근해서 데이터를 가져오는 기능에서 발생하며,
외부가 아닌 내부의 공격을 수행하게 되므로 접근제어 정책을 우회할 수 있는 공격이다.


일단 View를 눌러보았다.

오호.. 이런 드림핵 로고 이미지가 나타난다.
일단 소스코드를 봐보자.
#!/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()
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)
url에 입력된 값의 첫번째가 /이면 url=" "http://localhost:8000" + url가 되고
입력된 값에 "localhost" 또는 "127.0.01"이 포함되어 있으면 error.png를 반환하고 함수를 종료한다.
궁금하니까 해봐야겠다.

생각보다 더 큰 이미지가 나와서 놀랐다...
다시 소스코드를 보자.
함수가 종료되지 않으면, data값에 localhost에 위치한 url에 입력한 값에 해당하는 경로의 값을 가져오고
해당 값을 base64로 디코딩해 이미지를 출력한다.
local port는 1500-1800의 랜덤 값이다.
따라서 flag.txt는 http://127.0.0.1:{랜덤포트값}/flag.txt로 접근해야한다고 한다..
하지만 또 127.0.0.1이 필터링되기 때문에 우회를 해야한다.
- 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 port 번호를 알아내기 위해서 Burp Suite를 이용해보자

저 부분에 다가 1500부터 1800을 대입해서 local port 값을 찾아내보자

300개를 다 해야 나오려나 생각하던 참에
다른 값들과 다른게 튀어나왔다!!
local port는 1664라는 것을 알 수 있다.

이제 경로를 /flag.txt로 설정해서 Request를 보내면
저런 base64로 인코딩된 flag값이 나온다

디코딩을 하니 flag가 나왔다!
'ETC > EVI$ION' 카테고리의 다른 글
| EVI$ION 정규 세션 과제 - #6 (0) | 2024.05.23 |
|---|---|
| EVI$ION 정규 세션 과제 - #5 (0) | 2024.05.16 |
| EVI$ION 정규 세션 과제 - #4 (0) | 2024.05.09 |
| EVI$ION 러닝 세션 과제 - #3 (5/4) (0) | 2024.05.04 |
| EVI$ION 러닝 세션 과제 - #2 (4/6) (1) | 2024.04.06 |