#!/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)
문제 전체 코드이다.
문제 사이트도 들어가 보자.
image Viewer에 들어가서 url 안에 있는 경로를 view 버튼을 눌렀더니 png 가 나온다.
뭐 할거 없으니 중요 코드를 보러 가자
url = request.form.get("url", "")
urlp = urlparse(url)
url에 대해서 POST 요청으로 처리가 된다.
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)
if 문에서는 URL에 대해서 상대경로인지 확인한다.
만약 상대경로이면 절대경로인 "http://localhost:8000"으로 바꾼다.
elif에서는 urlp.netloc을 사용하여 "localhost" , "127.0.0.1"을 확인한다.
조건에 맞다면 error.png를 불러온다.
error.png 이미지를 가져올 때 base64 인코딩을 해서 표시한다.
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)
다르게 URL 이 로컬 호스트(127.0.0.1 , localhost)가 아닐 경우, error.png를 출력한다.
똑같이 base64 인코딩해서 표시한다.
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)
local_host는 127.0.0.1로 설정
port 은 1500~1800 사이의 랜덤포트 번호이다.
local_server는 HTTP 서버를 설정한다.
서버에 들어가 /flag.txt를 출력하려면
http:로컬호스트:{랜덤포트번호}를 만족시켜야 한다.
로컬호스트는 "localhost" , "127.0.0.1" 으로만 막아둔 상태이다.
그러면 로컬호스트는 "Localhost" , "LOCALHOST" , "0.0.0.0" 으로 우회가 가능하다.
나머지 포트번호를 찾기 위해서는 1500~1800 에다가 브루트 포스 연산을 넣어야하는데
조건을 찾아야한다.
다시 돌아와
정상적으로 url 에 dream.png 를 불러오면 image Viewer 가 잘 불러와준다.
우리는 아까 소스코드에서 image 를 불러올때 base64 로 표시가 된다는걸 확인했다.
프록시로 보면...
확인해보면 정상적으로 url 를 보냈을때 image 가 base64 인코딩된걸 확인할수있다.
이것은 error.png 도 마찬가지로 base64 로 인코딩되서 나온다.
decode 를 해보면....
error.png , dream.png 전부 decode 할때 동일하게 PNG 가 decode 된걸 확인할 수 있다.
그렇다면 저 PNG 부분만 걸러낸다면 포트번호를 확인할 수 있을거 같다.
왜냐하면
포트번호가 실패한다면 error.png 가 출력되면서 PNG 가 나오기 때문이다.
그러면 종합적으로 보면 localhost 우회하고 포트번호를 찾을수 있는 조건을 알아낸것이다.
PNG 만 걸러내도록 하자.
나 같은 경우는 1582 포트 가 열려있었다.
localhost 우회, 찾은 포트 를 입력하니 flag.txt 를 가져왔다.
flag.txt 를 여전히 image 에서 가져오기 때문에 base64 인코딩이 되어 있는 상태이다.
'DreamHack > WEB' 카테고리의 다른 글
proxy-1 (0) | 2024.10.16 |
---|---|
web-misconf-1 (1) | 2024.10.10 |
blind-command (0) | 2024.10.10 |
session (1) | 2024.10.08 |
pathtraversal (0) | 2024.09.11 |