Information
ID | gunjyo |
---|---|
Date | 2021/05/22 09:30 - 17:30 |
Rank | 11/282 |
Score | 1362 |
Prologue
這是我初次參加(好像也只能參加一次) MyFirstCTF,為期 8 小時。
題目會與接續的 AIS3 Pre-exam 些微重疊(因為 AIS3 Pre-exam 的第一天是 MFC~)
我的排名是第 11 名 ,雖然解的題目不多,對當時的我來說是個還滿滿意的成績~
因為是 PR90 以上所以當時有領到獎狀和 AIS3 的帽子 😍
事不遲疑,直接進到 write-up 吧
Misc
Cat Slayer ᶠᵃᵏᵉ | Nekogoroshi
BGM: 亜咲花「I believe what you said」
TERM=xterm-256color ssh -p 5566 h173@quiz.ais3.org
Author: splitline feat. Hojo Satoko
Welcome 題
TERM=xterm-256color ssh -p 5566 h173@quiz.ais3.org
連線後會跳轉到輸入數字密碼的介面,只要按錯一個就會跳 Locked,要重新連線
暴力嘗試即可
密碼 2025830455298
flag
AIS3{H1n4m1z4w4_Sh0k0gun}
HINT 附上了影片
[祝賀] 新垣結衣新婚快樂 發錢囉
下載後觀察到是 png , foremost
可以看到裡面有 zip檔案
需要密碼 但 zip cracker 沒辦法找到
猜測是 zip 偽加密,下載 editor 更改後就可以得到 flag 的圖片
左邊為原檔 右邊為更改後
Crypto
Microchip
#include "python.h"
def track(name, id):
if (len(name) % 4 == 0) :
padded = name + "4444"
elif (len(name) % 4 == 1) :
padded = name + "333"
elif (len(name) % 4 == 2) :
padded = name + "22"
elif (len(name) % 4 == 3) :
padded = name + "1"
keys = list()
temp = id
for i in range(4):
keys.append(temp % 96)
temp = int(temp / 96)
result = ""
for i in range(0, len(padded), 4) : #10 round
nums = list()
for j in range(4) :
num = ord(padded[i + j]) - 32
num = (num + keys[j]) % 96
nums.append(num + 32)
result += chr(nums[3])
result += chr(nums[2])
result += chr(nums[1])
result += chr(nums[0])
return result #len -> 40
name = open("flag.txt", "r").read().strip()
id = int(input("key = "))
print("result is:", track(name, id))
output: =Js&;*A``odZHi\'>D=Js&#i-DYf>Uy\'yuyfyu<)Gu
把 FLAG 每四個字就翻轉,接著加上 key 後 mod 96,再加上 32 後取字元值,就會得到 output
只要有 key 就可以用 result 逆推回 flag
由於我們知道 flag format = AIS3{printable}
,因此利用AIS3
就可以輕易逆推 key
4 個而已慢慢用手推即可
把 result
推回陣列(如下script),取前四個數字(6,83,42,29)
ord('A') - 32 = 33
( 33 + key[0] ) % 96 = 6 -> key[0] = 69
ord('I') - 32 = 41
( 41 + key[1] ) % 96 = 83 -> key[1] = 42
ord('S') - 32 = 51
( 51 + key[2] ) % 96 = 42 -> key[2] = 87
ord('3') - 32 = 19
( 19 + key[3] ) % 96 = 29 -> key[3] = 10
key={69,42,87,10}
接著把 result
依據 key 逆推回去即可
script
result = '=Js&;*A`odZHi\'>D=Js&#i-DYf>Uy\'yuyfyu<)Gu'
n = list()
for i in range(0,40,4):
for j in range(4):
n.append(ord(result[i+j])-32)
print(n)
#n = [29, 42, 83, 6, 27, 10, 33, 64, 79, 68, 58, 40, 73, 7, 30, 36, 29, 42, 83, 6, 3, 73, 13, 36, 57, 70, 30, 53, 89, 7, 89, 85, 89, 70, 89, 85, 28, 9, 39, 85]
for i in range(0,40,4):
n[i],n[i+1],n[i+2],n[i+3] = n[i+3],n[i+2],n[i+1],n[i]
print(n)
#n = [6, 83, 42, 29, 64, 33, 10, 27, 40, 58, 68, 79, 36, 30, 7, 73, 6, 83, 42, 29, 36, 13, 73, 3, 53, 30, 70, 57, 85, 89, 7, 89, 85, 89, 70, 89, 85, 39, 9, 28]
key = [69,42,87,10]
for i in range(40):
print(chr(((n[i] + 96 - key[i%4]) % 96) + 32) , end='')
執行 script 後得到AIS3{w31c0me_t0_AIS3_cryptoO0O0o0Ooo0}22
可以看到 padding 為 22
flag
AIS3{w31c0me_t0_AIS3_cryptoO0O0o0Ooo0}
Judgement
from hashlib import sha256
import string
flag = 'AIS3{THIS_IS_A_FAKE_FLAG}'
cand = string.ascii_letters + string.digits + '_{}'
charset = string.printable[:93]
print(cand)
enc = ''
for c in flag:
assert(c in cand)
enc += charset[int(sha256(c.encode()).hexdigest(), 16) % len(charset)]
print(enc)
output:)g;Fk@>2g;2V2J?d5G3_8V2<dR2i5GZ@<?2)g\j_2V&?2;@[F@ek2_3"=k&;2)\F2J9LL4g[W2"[2<)RZ23@<?2elFZ?2=@jZ23@=F2Yi52;lL5Vj2J?2J8\e@eW23e2lF330
很直覺的想法是把所有cand
裡面的字元都跑一次+比對,但因為有多個字元會跑出同一個結果,所以我把每個結果的可能性都print
出來用人工比對
script
from hashlib import sha256
import string
enc = ')g;Fk@>2g;2V2J?d5G3_8V2<dR2i5GZ@<?2)g\j_2V&?2;@[F@ek2_3"=k&;2)\F2J9LL4g[W2"[2<)RZ23@<?2elFZ?2=@jZ23@=F2Yi52;lL5Vj2J?2J8\e@eW23e2lF330'
for i in enc:
print(i,end = ' ')
for j in cand:
c = charset[int(sha256(j.encode()).hexdigest(), 16) % len(charset)]
if(i==c):
print(j,end = ' ')
print()
第一個字元為題目的enc
後面的是可能的字元
) A 8
g I
; S
F v G 3
k x E 9 {
@ i
> T X
2 _
g I
; S
2 _
V L M 4
2 _
J B
? e
d a
5 u Z
G t 2
3 1
_ q F H 5
8 U
V L M 4
2 _
< c d K
d a
R y
2 _
i 0
5 u Z
G t 2
Z s
@ i
< c d K
? e
2 _
) A 8
g I
\ r
j D
_ q F H 5
2 _
V L M 4
& R
? e
2 _
; S
@ i
[ N
F v G 3
@ i
e n 7
k x E 9 {
2 _
_ q F H 5
3 1
" o
= k w Q
k x E 9 {
& R
; S
2 _
) A 8
\ r
F v G 3
2 _
J B
9 l
L O
L O
4 m
g I
[ N
W 6
2 _
" o
[ N
2 _
< c d K
) A 8
R y
Z s
2 _
3 1
@ i
< c d K
? e
2 _
e n 7
l h
F v G 3
Z s
? e
2 _
= k w Q
@ i
j D
Z s
2 _
3 1
@ i
= k w Q
F v G 3
2 _
Y Y
i 0
5 u Z
2 _
; S
l h
L O
5 u Z
V L M 4
j D
2 _
J B
? e
2 _
J B
8 U
\ r
e n 7
@ i
e n 7
W 6
2 _
3 1
e n 7
2 _
l h
F v G 3
3 1
3 1
0 }
按照一般英文邏輯 + leet 語法去比對,但發現有幾個不太明確的,因此把已經推出來的丟到網路上便發現 flag 其實是來自sans undertale 😂
flag
AIS3{iT_IS_4_Beaut1FUL_day_0utside_8IrD5_4Re_SiNGin9_F1owERS_Ar3_BlOOmIN6_oN_dAys_1iKe_7h3se_
kiDs_1ik3_Y0u_ShOuLD_Be_BUrnin6_1n_h311}
ReSident evil villAge
註:此題用了非正規解
import socketserver
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
from binascii import unhexlify
class Task(socketserver.BaseRequestHandler):
def recv(self):
return self.request.recv(1024).strip()
def send(self, msg):
self.request.sendall(msg + b'\n')
def handle(self):
privkey = RSA.generate(1024)
n = privkey.n
e = privkey.e
self.send(b'Welcome to ReSident evil villAge, sign the name "Ethan Winters" to get the flag.')
self.send(b'n = ' + str(n).encode())
self.send(b'e = ' + str(e).encode())
while True:
self.request.sendall(b'1) sign\n2) verify\n3) exit\n')
option = self.recv()
if option == b'1':
self.request.sendall(b'Name (in hex): ')
msg = unhexlify(self.recv())
if msg == b'Ethan Winters' or bytes_to_long(msg) >= n: # msg+k*n not allowed
self.send(b'Nice try!')
else:
sig = pow(bytes_to_long(msg), privkey.d, n) # TODO: Apply hashing first to prevent forgery
self.send(b'Signature: ' + str(sig).encode())
elif option == b'2':
self.request.sendall(b'Signature: ')
sig = int(self.recv())
verified = (pow(sig, e, n) == bytes_to_long(b'Ethan Winters'))
if verified:
self.send(b'AIS3{THIS_IS_A_FAKE_FLAG}')
else:
self.send(b'Well done!')
else:
break
class ForkingServer(socketserver.ForkingTCPServer, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 42069
print(HOST, PORT)
server = ForkingServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()
題目是 RSA 簽證,目標是把 signature 送進去以達到 pow(sig, e, n) == bytes_to_long(b'Ethan Winters')
原則上只要把 Ethan Winters 轉成 hex 丟進去讓它跑出 signature 之後,再丟回去讓他 verify 就可以了。
但是從 if msg == b'Ethan Winters' or bytes_to_long(msg) >= n: # msg+k*n not allowed
這行可以看到 check msg == Ethan Winters
會被擋掉。
由於送進去的是字串,於是要讓數字不變但是字串改變,就去嘗試 Bypass,發現前面加上 00
可以繞過,所以傳 00
加上 Ethan Winters
轉成 hex
就可以了
直接上圖。
Epilogue
記得在早上的時候可能是太緊張,好像只解出一兩題(?)左右
一開始就看到有一題 RSA,因為前幾天一直在 CryptoHack 練 RSA 然後又有兩個人很快就解了就果斷看那題,然後就看了爆炸久,大概在一個半小時的時候就很崩潰,因為大家的分數都蹭蹭蹭的上去(賽前一周有專門陪我練的學長,所以有種要考好的壓力(#))
後來吃完午餐冷靜下來之後解題就比較順利了
真的是親身體會比賽緊張會影響發揮這句話~~~
有任何問題歡迎和我討論~~