etc

Zed Attack Proxy (ZAP) ์‚ฌ์šฉ ๊ฐ€์ด๋“œ : [Dreamhack] web-ssrf ํ’€์ด

minnggyuu 2024. 12. 24. 15:29

๐Ÿš€  ZAP์˜ ์‚ฌ์šฉ๋ฒ•์€ ๋’ค์— ๋‚˜์˜ค๋‹ˆ ๋‚ด๋ ค๊ฐ€์ฃผ์„ธ์š” ๐Ÿš€

web-ssrf

 

web-ssrf ๋ผ๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

 

๐Ÿ’ก SSRF ๋ž€ ? 
  Server-side Request Forgery(SSRF)๋Š” ์›น ์„œ๋น„์Šค์˜ ์š”์ฒญ์„ ๋ณ€์กฐํ•˜๋Š” ์ทจ์•ฝ์ ์œผ๋กœ, 
  ์›น์„œ๋ฒ„์— ์œ„์กฐ๋œ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด, ํ•ด๋‹น ์›น์„œ๋ฒ„๊ฐ€ ๋‚ด๋ถ€ ์„œ๋ฒ„์™€ ํ†ต์‹ ์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 
  ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋Š” ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ๋‚ด๋ถ€ ์ž์›์— ๋Œ€ํ•ด ๊ฐ„์ ‘์ ์œผ๋กœ ๊ณต๊ฒฉ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ถœ์ฒ˜ : ๋“œ๋ฆผํ•ต

 


๐Ÿ”Ž ์†Œ์Šค์ฝ”๋“œ ๋ถ„์„

๐Ÿ–‹๏ธ ์ „์ฒด ์ฝ”๋“œ

๋”๋ณด๊ธฐ
#!/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)



๐Ÿ”Ž /img_viewer

@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)

 

/img_viewer ์—์„œ๋Š” POST ์š”์ฒญ ์‹œ url ์ด๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ์ถ”์ถœ, urlparse ํ•ด์„œ urlp์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ urlp์˜ ํ˜•ํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

โœ… urlp์˜ ํ˜•ํƒœ

ParseResult(
    scheme='https',        # URL ์Šคํ‚ค๋งˆ (http, https ๋“ฑ)
    netloc='example.com',  # ๋„คํŠธ์›Œํฌ ์œ„์น˜ (๋„๋ฉ”์ธ ๋ฐ ํฌํŠธ)
    path='/path',          # URL ๊ฒฝ๋กœ
    params='',             # ๊ฒฝ๋กœ ๋‚ด ๋งค๊ฐœ๋ณ€์ˆ˜ (์ž˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ)
    query='query=123',     # ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด
    fragment='fragment'    # ํ”„๋ž˜๊ทธ๋จผํŠธ (# ๋’ค์˜ ๊ฐ’)
)

 

url์ด "/" ์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉด, "http://localhost:8000" + url ํ˜•ํƒœ๋กœ url์— ๋ถ™์—ฌ์„œ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  data ๋ณ€์ˆ˜์— ์ €์žฅ, 

data๋Š” base64๋กœ ์ธ์ฝ”๋”ฉ ํ›„ utf-8๋กœ ๋””์ฝ”๋”ฉ ํ•ด img ๋ณ€์ˆ˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ img๋ฅผ returnํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

 

ํ•˜์ง€๋งŒ "/" ์œผ๋กœ ์‹œ์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด urlp.netloc์ด localhost ํ˜น์€ 127.0.0.1 ์ผ ์‹œ error.png๋ฅผ data์— ์ €์žฅํ•˜๊ณ , 

data๋ฅผ base64๋กœ ์ธ์ฝ”๋”ฉ, utf-8๋กœ ๋””์ฝ”๋”ฉํ•ด returnํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

 

๐Ÿ”Ž local_host

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 ์™€ local_port๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ด ํฌํ•จ๋ผ ์žˆ์Šต๋‹ˆ๋‹ค.
local_host ๋Š” 127.0.0.1 ๋กœ ์„œ๋ฒ„ ์ž๊ธฐ ์ž์‹ ์œผ๋กœ ์ง€์ •ํ•˜๊ณ , local_port ๋Š” 1500 ~ 1800 ์‚ฌ์ด์˜ ๊ฐ’์œผ๋กœ ๋žœ๋คํ•˜๊ฒŒ ์ง€์ •ํ•˜๋Š” ๋ชจ์Šต์ž…๋‹ˆ๋‹ค.

 

์•„๋งˆ FLAG ๋Š” ๋‚ด๋ถ€ ์„œ๋ฒ„์— ์œ„์น˜ํ•  ๊ฒƒ์œผ๋กœ ์ถ”์ •๋ฉ๋‹ˆ๋‹ค. (SSRF ๋ฌธ์ œ๋‹ˆ๊นŒ)


๐Ÿ”Ž ๋‚ด๋ถ€ PORT ์ฐพ๊ธฐ

 

์šฐ์„  localhost ์™€ 127.0.0.1 ์ด๋ผ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ด ๋‘ ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ๊ฐ™์€ ์˜๋ฏธ๋ฅผ ์ง€๋‹Œ ํ‚ค์›Œ๋“œ๋ฅผ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ Localhost๋กœ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๊ณ , 16์ง„์ˆ˜๋กœ ๋ณ€ํ™˜ํ•ด 7F.00.00.01 ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ 0.0.0.0 ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

์ €๋Š” ์ด๋ฒˆ์— 0.0.0.0์„ ์‚ฌ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์—ด๋ ค์žˆ๋Š” ํฌํŠธ์ผ ๊ฒฝ์šฐ IMG๊ฐ€ ํ‘œ์‹œ๋˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ NOT FOUND๋ผ๋Š” IMG๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ Response Length๋ฅผ ๋น„๊ตํ•ด์„œ ์—ด๋ ค์žˆ๋Š” ํฌํŠธ๋ฅผ ์ฐพ์•„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


โšก๏ธ Zed Attack Proxy (ZAP) ์‚ฌ์šฉ

 

ํŒŒ์ด์ฌ์œผ๋กœ requests ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•ด ์—ด๋ ค์žˆ๋Š” ํฌํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜๋„ ์žˆ๊ณ , Burp Suite์˜ Intruder ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋А๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ZAP์„ ์ด์šฉํ•ด ์—ด๋ ค ์žˆ๋Š” ํฌํŠธ๋ฅผ ์ฐพ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 

๐Ÿš€ OWASP ZAP ์ด๋ž€ ?

-> Zed Attack Proxy์˜ ์•ฝ์ž๋กœ, ์˜คํ”ˆ ์†Œ์Šค ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ ์Šค์บ๋„ˆ์ž…๋‹ˆ๋‹ค. ํฌํŠธ ์Šค์บ”, ์ทจ์•ฝ์  ๋ถ„์„, ๋ณด๊ณ ์„œ ์ž‘์„ฑ, ์›น ํ”„๋ก์‹œ ๋“ฑ ๋งŽ์€ ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

https://www.zaproxy.org/

 

The ZAP Homepage

Welcome to ZAP!

www.zaproxy.org


 

์ €๋Š” ํŒŒ์ด์–ดํญ์Šค์— ํ”„๋ก์‹œ ์„ค์ •์„ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. 


Break ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € ํ”„๋ก์‹œ ์„ค์ •์ด ๋˜์–ด์žˆ์–ด์•ผ ํ•˜๋Š”๋ฐ,

 

ํŒŒ์ด์–ดํญ์Šค ๋‚ด์—์„œ ์„ค์ • -> ๋„คํŠธ์›Œํฌ ์„ค์ • -> ์ˆ˜๋™ ํ”„๋ก์‹œ ์„ค์ •  - 127.0.0.1, ํฌํŠธ 8081 ์„ค์ •ํ•ด์ฃผ๊ณ ,

ZAP Options -> Network - Server Cerificates์—์„œ ์ธ์ฆ์„œ ์ €์žฅ ํ›„

ํŒŒ์ด์–ดํญ์Šค ์„ค์ • -> ๊ฐœ์ธ ์ •๋ณด ๋ฐ ๋ณด์•ˆ -> ์ธ์ฆ์„œ -> ์ธ์ฆ์„œ ๋ณด๊ธฐ -> ๊ฐ€์ ธ์˜ค๊ธฐ ์—์„œ zap_root_ca.cer์„ ๋“ฑ๋กํ•ด ์ค๋‹ˆ๋‹ค.

 

์„ค์ •์„ ๋๋งˆ์น˜๊ณ 

 

ZAP

 

ZAP์˜ ํ™”๋ฉด ์ž…๋‹ˆ๋‹ค. ์ƒ๋‹จ์— ๋ณด์‹œ๋ฉด

 

์ด๋Ÿฐ ์ƒ๋‹จ๋ฐ” ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ฒซ ๋…น์ƒ‰ ๋ฒ„ํŠผ์ด Break ๋ฒ„ํŠผ์ž…๋‹ˆ๋‹ค. ํด๋ฆญํ•˜๊ณ  ํŒŒ์ด์–ดํญ์Šค๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๋ฉด

 

 

์ด๋ ‡๊ฒŒ ์š”์ฒญ์ด ์žกํžˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ €๊ธฐ์„œ ์š”์ฒญ์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

๋ฌธ์ œ input์— http://0.0.0.0:1500์„ ๋„ฃ์–ด ์š”์ฒญํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค. Response Length๊ฐ€ 65121์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ ์šฐํด๋ฆญ - Fuzz ํ•˜๋ฉด

์šฐํด๋ฆญ - Fuzz

 

Fuzzer ์ฐฝ์ด ๋œฐ ํ…๋ฐ 

 

์—ฌ๊ธฐ์„œ ์ฆ๊ฐ€ํ•˜๋Š” ์นด์šดํ„ฐ ๋ถ€๋ถ„์„ ๋“œ๋ž˜๊ทธ ํ•˜๊ณ  (์—ฌ๊ธฐ์„œ๋Š” 1500) -> Payloads -> Add ํด๋ฆญ

 

Payloads

 

Type์„ Numberzz๋กœ ์„ ํƒํ•˜๊ณ  ์‹œ์ž‘์ ๊ณผ ์ถœ๋ฐœ์ ์„ ์ง€์ •ํ•ด์ฃผ๊ณ  Start Fuzzer ๋ฅผ ํด๋ฆญํ•ด์ฃผ๋ฉด

 

 

๊ต‰์žฅํžˆ ๋น ๋ฅด๊ฒŒ request๋ฅผ ๋ณด๋‚ด๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. response length๊ฐ€ ํŠน๋ณ„ํžˆ ์งง์€ 1649๊ฐ€ ๋žœ๋คํ•˜๊ฒŒ ์—ด๋ฆฐ ํฌํŠธ๊ฒ ๋„ค์š”.

 

response

response ์ž…๋‹ˆ๋‹ค. base64๋กœ ์ธ์ฝ”๋”ฉ๋˜์–ด ์žˆ๋Š”๋ฐ ์ด๋ฅผ ๋ณ€ํ™˜ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="app.py">app.py</a></li>
<li><a href="error.png">error.png</a></li>
<li><a href="flag.txt">flag.txt</a></li>
<li><a href="requirements.txt">requirements.txt</a></li>
<li><a href="static/">static/</a></li>
<li><a href="templates/">templates/</a></li>
</ul>
<hr>
</body>
</html>

 

flag.txt ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ http://0.0.0.0:1649/flag.txt ์— ์ ‘๊ทผํ•ด๋ณด๋ฉด?

 

 

Base64๋กœ ์ธ์ฝ”๋”ฉ๋œ flag๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.