CTF4b 2018 writeup
約1ヶ月前に新しく研究室に加わった後輩達の勧めでCTFを始めました。 その後彼らの管理しているCTFサイトの問題を解いたりしていたのですが、今回初心者向けのCTFとしてSECCON Beginners CTFが開かれるとのことで後輩達とチームを組んで出場する運びとなりました。
いわく、「解いた問題のwriteupを公開するまでがCTF」らしいのでここにwriteupを記します。
[Warmup] plain mail
与えられたパケットを調べるとパスワード付きのzipファイルとパスワードが見つかるのでzipファイルを解凍するとフラグが得られる。
ctf4b{email_with_encrypted_file}
RSA is Power
与えられた$N$を素因数分解して、$N=p\times q$ となるような $p, q$ を求めます。
p = 299681192390656691733849646142066664329
q = 324144336644773773047359441106332937713
あとは $de \equiv 1\bmod (p-1)(q-1)$ を満す $d$ を拡張ユークリッド互除法を用いて求めてやれば、 Wikipedaに載っているとおりに $C^{d} \bmod N$ とすれば 復号できます。
d = 88509020092584531671107468782943602124921999287671161687233461555074737950465
C^d mod N = 0x63746634627b35696d706c655f7273345f31735f336173795f6630725f757d
得られるフラグは16進数表記なので適当に文字列に変換します。
ctf4b{5imple_rs4_1s_3asy_f0r_u}
Streaming
フラグの先頭がctf4bであることが分かっているのでそれを使って初期seedを求め、そこから次々にseedを求めながら複合していけばフラグを得られます。
こんな感じのコードを書きました。
def next(seed):
A = 37423
B = 61781
C = 34607
return (A*seed+B)%C
f = open('encrypted', 'rb')
encrypted = f.read()
decrypted = ""
d = int(hex(ord(encrypted[0])), 16) + int(hex(ord(encrypted[0+1])), 16) * 256
#init seed
seed = d ^ 25460
decrypted += chr(int(hex(d^seed)[2:4], 16)) + chr(int(hex(d^seed)[4:6], 16))
print(hex(d^seed))
for i in range(1, len(encrypted)//2):
d = int(hex(ord(encrypted[2*i])), 16) + int(hex(ord(encrypted[2*i+1])), 16) * 256
seed = next(seed)
d = d^seed
print(hex(d))
if len(hex(d)) <= 4:
decrypted += chr(int(hex(d)[2:4], 16))
else:
decrypted += chr(int(hex(d)[2:4], 16)) + chr(int(hex(d)[4:6], 16))
print(decrypted)
ctf4b{lcg-is-easily-predictable}
てけいさんえくすとりーむず
サーバにアクセスすると四則演算を解かされます。 自分はえくすとりーむなてけいさんのプロではないのでプログラムにやらせました。
import socket
import re
class Netcat:
""" Python 'netcat like' module """
def __init__(self, ip, port):
self.buff = ""
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((ip, port))
def read(self, length = 1024):
""" Read 1024 bytes off the socket """
return self.socket.recv(length)
def read_until(self, data):
""" Read data into the buffer until we have data """
while not data in self.buff:
self.buff += self.socket.recv(1024)
pos = self.buff.find(data)
rval = self.buff[:pos + len(data)]
self.buff = self.buff[pos + len(data):]
return rval
def write(self, data):
self.socket.send(data)
def close(self):
self.socket.close()
nc = Netcat('133.242.234.140', 8690)
nc.read_until('Stage.1)')
for i in range(1, 101):
print(nc.read_until('Stage.'+str(i)+')'))
expr = nc.read_until('=')
expr = expr.strip()
m = re.match(r'(\d+)\s*(\+|-|\*|/)\s*(\d+).*', expr)
term1 = int(m.group(1))
op = m.group(2)
term2 = int(m.group(3))
ans = ""
if op == "+":
ans = term1 + term2
elif op == "-":
ans = term1 - term2
elif op == "*":
ans = term1 * term2
elif op == "/":
ans = term1 / term2
print(expr),
print(ans)
nc.write(str(ans) + '\n')
print(nc.read())
Netcatクラスの部分のコードはここのやつをそのまんま使ってます。
これを実行すると
(Stage.1)
908 * 509 = 462172
(Stage.2)
925 * 757 = 700225
(Stage.3)
940 - 850 = 90
(Stage.4)
618 + 921 = 1539
(Stage.5)
625 - 536 = 89
.
.
.
(Stage.100)
860 + 823 = 1683
Congrats.
Flag is: "ctf4b{ekusutori-mu>tekeisann>bigina-zu>2018}"
ctf4b{ekusutori-mu>tekeisann>bigina-zu>2018}
感想
初心者向けということで簡単な問題だけかと思ってたんですが、結構難しい問題もあったなという印象でした。 でも、Cryptoは結構解けたので良かったです。Pwn, Reversingはゼンゼンワカランカッタ。