CNVService (Crypt/ACM)
$ nc cnvservice.acebear.site 1337
***************************CNVService*****************************
* Challenge created by CNV *
* My blog: https://chung96vn.blogspot.com *
***************************CNVService*****************************
********************Menu********************
* 1 - Register *
* 2 - Login *
********************************************
Your choice: 1
*****************************REGISTER*****************************
Name: 1
Username: 2
Cookie: PWg77PMNr9+uFv7AJVlwzob7DLRKNm7bpk7wY6l+JBBxnlH1lDq2HJXf53CIAgzXz70wj6nMNmgICMiq6l7CELxf80XHSkf5UsRnDr+CHOM=
***************************END REGISTER***************************
********************Menu********************
* 1 - Register *
* 2 - Login *
********************************************
Your choice:
ソースコードを見ると、以下のような処理になっている。
■1を選択
Nameを入力
Usernameを入力(rootはNG)
すると、暗号化結果を表示
◇Cookie.register(Name, Username)
name: Nameをpadding
iv: nameと特定文字列とのxor
cookie: CNVService*user=[username]*[ctime()]*[__SECRET__]
iv+cookieのAES暗号したものをBase64エンコードして返す。
◇AES暗号
cookieをpadding
ECB暗号モード利用
1ブロック目:ivとのxor --AES暗号-->
2ブロック目:1ブロック目暗号化文字列のmd5とのxor --AES暗号-->
■2を選択
クッキーを入力
◇Cookie.authentication(cookie)
cookieをBase64デコードする。
name: cookie前半16バイトと特定文字列とのxor
name: unpaddingする
AES復号する。
*区切りでチェック
1つ目:CNVService
最後:__SECRET__
2つ目の最初の5文字:user=
2つ目:=区切りで2つ
username=rootの暗号化文字列を割り出す必要があるが、当然rootで登録はできない。以下のようなことを念頭に暗号化文字列を作成することを考える。
"CNVService*user=" ^ iv --AES ECB--> 暗号1ブロック目
"root*Sat Jan 27 " ^ md5(暗号1ブロック目).digest() --AES ECB--> 暗号2ブロック目
"13:43:37 2018*SS" ^ md5(暗号2ブロック目).digest() --AES ECB--> 暗号3ブロック目
"SSSSSS??????????" ^ md5(暗号3ブロック目).digest() --AES ECB--> 暗号4ブロック目
いろいろ試したが、__HEDDEN__, __SECRET__の値を知らずに解くことはできそうにない。
まず md5(__HIDDEN__).digest()を求めてみる。
import socket
from hashlib import md5
from base64 import b64decode
from base64 import b64encode
def xor(dest, src):
if len(dest) == 0:
return src
elif len(src) == 0:
return dest
elif len(dest) >= len(src):
return ''.join(chr(ord(dest[i])^ord(src[i])) for i in range(len(src)))
else:
return ''.join(chr(ord(dest[i])^ord(src[i])) for i in range(len(dest)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('cnvservice.acebear.site', 1337))
data = s.recv(1024)
data += s.recv(1024)
data += '1'
print data
s.sendall('1\n')
data = s.recv(1024)
data += s.recv(1024)
print data
name = 'xxxxxxxxxxxxxxxx'
print name
s.sendall(name + '\n')
data = s.recv(1024)
print data
username = 'aaaa'
print username
s.sendall(username + '\n')
data = s.recv(1024)
data += s.recv(1024)
print data
cookie = data[8:].strip()
cookie = b64decode(cookie)
iv = cookie[:16]
cipher_text = cookie[16:]
HIDDEN = xor(iv, name)
print HIDDEN.encode('hex')
この結果、md5(__HIDDEN__).digest()の16進表記は以下の通りであることがわかる。
0c6734e3fc02a0d0a119f1cf2a567fc1
次に__SECRET__を求めてみる。最初は以下のコードで__SECRET__の長さを確認する。
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('cnvservice.acebear.site', 1337))
data = s.recv(1024)
data += s.recv(1024)
data += '1'
print data
s.sendall('1\n')
data = s.recv(1024)
data += s.recv(1024)
name = 'test'
print data + name
s.sendall(name + '\n')
data = s.recv(1024)
username = 'aaaaaaa'
print data + username
s.sendall(username + '\n')
data = s.recv(1024)
print data
cookie = data[8:].strip()
print len(cookie.decode('base64'))
・aのとき、80
・aaaaaaaのとき、80
・aaaaaaaaのとき、96
CNVService*user=
aaaaaaaa*Sat Jan
27 13:43:37 201
8*SSSSSSSSSSSSSS
\x16........\x16
この結果__SECRET__の長さは14。
次に__SECRET__の後ろから1文字ずつはみ出させ、usernameに同じデータを作るように指定し、BFで該当ブロックの暗号データが同じになるものを探す。最初の1文字は以下のようなイメージ。
CNVService*user=
aaaaaaaaa*Sat Ja
n 27 13:43:37 20
18*SSSSSSSSSSSSS
S\x15.......\x15
import socket
from hashlib import md5
from base64 import b64decode
from base64 import b64encode
HIDDEN = '0c6734e3fc02a0d0a119f1cf2a567fc1'.decode('hex')
P_HEAD = 'CNVService*user='
NAME = 'xxxxxxxxxxxxxxxx'
BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
def xor(dest, src):
if len(dest) == 0:
return src
elif len(src) == 0:
return dest
elif len(dest) >= len(src):
return ''.join(chr(ord(dest[i])^ord(src[i])) for i in range(len(src)))
else:
return ''.join(chr(ord(dest[i])^ord(src[i])) for i in range(len(dest)))
def right_enc(s, nth, plain, initFlg):
data = s.recv(1024)
if initFlg:
data += s.recv(1024)
data += '1'
print data
s.sendall('1\n')
data = s.recv(1024)
data += s.recv(1024)
print data + NAME
s.sendall(NAME + '\n')
data = s.recv(1024)
username = 'a' * (8 + nth)
print data + username
s.sendall(username + '\n')
data = s.recv(1024)
print data
cookie = data[8:].strip()
cookie = b64decode(cookie)
iv = cookie[:16]
cipher_text = cookie[16:]
enc_block4 = cipher_text[48:64]
enc_block5 = cipher_text[64:]
return xor(plain, md5(enc_block4).digest()), enc_block5
def getname(xor_val):
return xor(xor_val, xor(P_HEAD, HIDDEN))
def enc(s, name):
data = s.recv(1024)
data += '1'
print data
s.sendall('1\n')
data = s.recv(1024)
data += s.recv(1024)
print data + name
s.sendall(name + '\n')
data = s.recv(1024)
username = 'a'
print data + username
s.sendall(username + '\n')
data = s.recv(1024)
print data
cookie = data[8:].strip()
cookie = b64decode(cookie)
iv = cookie[:16]
cipher_text = cookie[16:]
enc_block1 = cipher_text[:16]
return enc_block1
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('cnvservice.acebear.site', 1337))
secret = ''
initFlg = True
for i in range(1, 15):
for code in range(32, 127):
print '[-] code =', code
try_secret = pad(chr(code) + secret)
p, c = right_enc(s, i, try_secret, initFlg)
initFlg = False
if enc(s, getname(p)) == c:
secret = chr(code) + secret
break
print '[-] secret =', secret
print '[+] secret =', secret
この結果、__SECRET__の値は以下の通りであることがわかる。
__SECRET__ = 'Thi5_i5_s3cr3t'
あとは適当なNameで1ブロック目から順に求めていく。
import socket
from hashlib import md5
from base64 import b64decode
from base64 import b64encode
BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
def xor(dest, src):
if len(dest) == 0:
return src
elif len(src) == 0:
return dest
elif len(dest) >= len(src):
return ''.join(chr(ord(dest[i])^ord(src[i])) for i in range(len(src)))
else:
return ''.join(chr(ord(dest[i])^ord(src[i])) for i in range(len(dest)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('cnvservice.acebear.site', 1337))
data = s.recv(1024)
data += s.recv(1024)
data += '1'
print data
s.sendall('1\n')
data = s.recv(1024)
data += s.recv(1024)
name = 'xxxxxxxxxxxxxxxx'
print data + name
s.sendall(name + '\n')
data = s.recv(1024)
username = 'aaaa'
print data + username
s.sendall(username + '\n')
data = s.recv(1024)
print data
cookie = b64decode(data[8:].strip())
iv = cookie[:16]
cipher_text = cookie[16:]
HIDDEN = xor(iv, name)
p = [
'CNVService*user=',
'root*Sat Jan 27 ',
'13:43:37 2018*Th',
pad('i5_i5_s3cr3t')]
c = [cipher_text[:16]]
for i in range(3):
data = s.recv(1024)
print data + '1'
s.sendall('1\n')
data = s.recv(1024)
data += s.recv(1024)
name2 = xor(xor(p[i+1], md5(c[i]).digest()), xor(p[0], HIDDEN))
print data + name2
s.sendall(name2 + '\n')
data = s.recv(1024)
username = 'a'
print data + username
s.sendall(username + '\n')
data = s.recv(1024)
print data
cookie = b64decode(data[8:].strip())
cipher_text = cookie[16:]
c.append(cipher_text[:16])
data = s.recv(1024)
print data + '2'
cookie = iv
for i in range(4):
cookie += c[i]
b64_cookie = b64encode(cookie)
s.sendall('2\n')
data = s.recv(1024)
data += s.recv(1024)
print data + b64_cookie
s.sendall(b64_cookie + '\n')
data = s.recv(1024)
print data
data = s.recv(1024)
print data
実行結果は以下の通り。
:
********************Menu********************
* 1 - Register *
* 2 - Login *
********************************************
Your choice: 2
*******************************LOGIN******************************
Cookie: dB9Mm4R62KjZYYm3Ui4HuX2M/X3aDOaMYiURc62G0iNH1X1MYq3cT/KLGBii3waxANcmeINSqA86qHKfYpL2fagUrLmrhK2qCQmiDLHHxxc=
**************************LOGIN SUCCESS***************************
Welcome CNV service: xxxxxxxxxxxxxxxx
Username: root
Time register: Sat Jan 27 13:43:37 2018
***************************Root Servive***************************
This is flag: AceBear{AES_CNV_is_s3cure_but_CNV_S3rvic3_i5_not_s3cure}
AceBear{AES_CNV_is_s3cure_but_CNV_S3rvic3_i5_not_s3cure}