AFCTF 2018 Writeup

NSSCTF Crypto刷题速通记录,目的是查漏补缺,有疑问欢迎交流ξ( ✿>◡❛)

BASE

给了一个很长的十六进制文件,导入CyberChef开始解码。说实话,感觉出题人有点无聊了。
下面是解码顺序:

1
hex - base64 - base64 - hex - base64 - base32 - base32 - base32 - hex - base32 - base32 - base32 - base32 - base32 - hex - base32 - base32 - base64 - base32 - base64 - base64 - base32 - base32 - hex - base32 - base64 - base64 - hex - base64 - base64

你能看出这是什么加密吗

题目:

1
2
3
4
5
6
7
p=0x928fb6aa9d813b6c3270131818a7c54edb18e3806942b88670106c1821e0326364194a8c49392849432b37632f0abe3f3c52e909b939c91c50e41a7b8cd00c67d6743b4f

q=0xec301417ccdffa679a8dcc4027dd0d75baf9d441625ed8930472165717f4732884c33f25d4ee6a6c9ae6c44aedad039b0b72cf42cab7f80d32b74061

e=0x10001

c=0x70c9133e1647e95c3cb99bd998a9028b5bf492929725a9e8e6d2e277fa0f37205580b196e5f121a2e83bc80a8204c99f5036a07c8cf6f96c420369b4161d2654a7eccbdaf583204b645e137b3bd15c5ce865298416fd5831cba0d947113ed5be5426b708b89451934d11f9aed9085b48b729449e461ff0863552149b965e22b6  

解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
p=0x928fb6aa9d813b6c3270131818a7c54edb18e3806942b88670106c1821e0326364194a8c49392849432b37632f0abe3f3c52e909b939c91c50e41a7b8cd00c67d6743b4f

q=0xec301417ccdffa679a8dcc4027dd0d75baf9d441625ed8930472165717f4732884c33f25d4ee6a6c9ae6c44aedad039b0b72cf42cab7f80d32b74061

e=0x10001

c=0x70c9133e1647e95c3cb99bd998a9028b5bf492929725a9e8e6d2e277fa0f37205580b196e5f121a2e83bc80a8204c99f5036a07c8cf6f96c420369b4161d2654a7eccbdaf583204b645e137b3bd15c5ce865298416fd5831cba0d947113ed5be5426b708b89451934d11f9aed9085b48b729449e461ff0863552149b965e22b6
phi = (p-1)*(q-1)
n = p*q
d = inverse(e,phi)
m = pow(c,d,n)
ans = long_to_bytes(m)
print(ans)

Vigenere

直接在线网站破解:
https://www.guballa.de/vigenere-solver

Morse

摩斯电码解码 -> 十六进制数转字符

Single

观察代码发现是单表替换加密,直接quipquip在线解密即可: https://quipqiup.com/#google_vignette

可怜的RSA

题目:

1
base64编码的密文 和 公钥PEM文件

分析:
在线解析公钥文件得到e和n
使用factordb分解n得到p和q
直接进行解密会发现不能出flag,这是因为加密过程中使用了OAEP填充
解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from base64 import b64decode
n = 79832181757332818552764610761349592984614744432279135328398999801627880283610900361281249973175805069916210179560506497075132524902086881120372213626641879468491936860976686933630869673826972619938321951599146744807653301076026577949579618331502776303983485566046485431039541708467141408260220098592761245010678592347501894176269580510459729633673468068467144199744563731826362102608811033400887813754780282628099443490170016087838606998017490456601315802448567772411623826281747245660954245413781519794295336197555688543537992197142258053220453757666537840276416475602759374950715283890232230741542737319569819793988431443
e = 65537
p = 3133337
q = 25478326064937419292200172136399497719081842914528228316455906211693118321971399936004729134841162974144246271486439695786036588117424611881955950996219646807378822278285638261582099108339438949573034101215141156156408742843820048066830863814362379885720395082318462850002901605689761876319151147352730090957556940842144299887394678743607766937828094478336401159449035878306853716216548374273462386508307367713112073004011383418967894930554067582453248981022011922883374442736848045920676341361871231787163441467533076890081721882179369168787287724769642665399992556052144845878600126283968890273067575342061776244939
c = b64decode(open('可怜的RSA/flag.enc', 'rb').read())
d = inverse(e, (p-1)*(q-1))
pk = RSA.construct((n,e,d,p,q))
cipher = PKCS1_OAEP.new(pk)
m = cipher.decrypt(c)
print(m)

One Secret, Two encryption

题目:

1
2
3
一份秘密发送给两个人不太好吧,那我各自加密一次好啦~~~  
素数生成好慢呀
偷个懒也……不会有问题的吧?

题目给了两组密文和公钥,解析后发现两组公钥的n不同,并结合题目所说,猜测两个n应该存在共同素数。
解答:

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Util.number import *
n1 = 4850297138162223468826481623082440249579136876798312652735204698689613969008632545220976699170308454082390834742570718247804202060929493571642074679428565168405877110681518105667301785653517697684490982375078989886040451115082120928982588380914609273008153977907950532498605486225883973643141516024058315360572988744607134110254489421516026937249163493982681336628726033489124705657217768229058487155865265080427488028921879608338898933540825564889012166181346177276639828346376362168934208822467295673761876965864573164529336885250577357767314256581019474130651412100897839606491189424373959244023695669653213498329
n2 = 2367536768672000959668181171787295271898789288397672997134843418932405959946739637368044420319861797856771490573443003520137149324080217971836780570522258661419034481514883068092752166752967879497095564732505614751532330408675056285275354250157955321457579006360393218327164804951384290041956551855334492796719901818165788902547584563455747941517296875697241841177219635024461395596117584194226134777078874543699117761893699634303571421106917894215078938885999963580586824497040073241055890328794310025879014294051230590716562942538031883965317397728271589759718376073414632026801806560862906691989093298478752580277
e = 65537
p = GCD(n1, n2)
q = n2 // p
c2 = bytes_to_long(open('One Secret, Two encryption/flag_encry2', 'rb').read())
d = inverse(65537, (p-1)*(q-1))
m = pow(c2, d, n2)
ans = long_to_bytes(m)
print(ans)

MagicNum

题目:

1
2
3
4
5
6
72065910510177138000000000000000.000000
71863209670811371000000.000000
18489682625412760000000000000000.000000
72723257588050687000000.000000
4674659167469766200000000.000000
19061698837499292000000000000000000000.000000

分析:
本题应该是MISC题,根据分析,题目应该是要转换成IEEE 754单精度浮点数小端序,然后再转换ASCII字符
解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import struct
from Crypto.Util.number import long_to_bytes
nums = [72065910510177138000000000000000.000000,
71863209670811371000000.000000,
18489682625412760000000000000000.000000,
72723257588050687000000.000000,
4674659167469766200000000.000000,
19061698837499292000000000000000000000.000000
]
a = ''
for i in nums:
i = float(i)
a += struct.pack('<f',i).hex() #IEEE 754单精度浮点数小端序十六进制
print(a)
print(long_to_bytes(int(a,16)))

MyOwnCBC

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

from Crypto.Cipher import AES
from Crypto.Random import random
from Crypto.Util.number import long_to_bytes

def MyOwnCBC(key, plain):
if len(key)!=32:
return "error!"
cipher_txt = b""
cipher_arr = []
cipher = AES.new(key, AES.MODE_ECB, "")
plain = [plain[i:i+32] for i in range(0, len(plain), 32)]
print(plain)
cipher_arr.append(cipher.encrypt(plain[0]))
cipher_txt += cipher_arr[0]
for i in range(1, len(plain)):
cipher = AES.new(cipher_arr[i-1], AES.MODE_ECB, "")
cipher_arr.append(cipher.encrypt(plain[i]))
cipher_txt += cipher_arr[i]
return cipher_txt

key = random.getrandbits(256)
key = long_to_bytes(key)

s = ""
with open("flag.txt","r") as f:
s = f.read()
f.close()

with open("flag_cipher","wb") as f:
f.write(MyOwnCBC(key, s))
f.close()

分析:
根据代码分析,可以发现题目对明文进行了AES分组加密,但是只有第一组是使用随机生成的密钥加密的,后面其他组都是使用前一组的密文作为密钥进行加密的,也就是说,除了第一组,其他所有组的密钥我们都知道,可以求出除了第一组外的所有明文。
解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Cipher import AES
with open("MyOwnCBC/flag_cipher","rb") as f:
c = f.read()
f.close()
#print(c)
def MyOwnCBC_decrypt(cipher_txt):
cipher_arr = [cipher_txt[i:i+32] for i in range(0, len(cipher_txt), 32)]
plain_txt = b''
#print(cipher_arr)
for i in range(len(cipher_arr)-1,1,-1):
plain = AES.new(cipher_arr[i-1], AES.MODE_ECB)
#print(cipher_arr[i-1])
plain_txt = plain.decrypt(cipher_arr[i]) + plain_txt
return plain_txt

m = MyOwnCBC_decrypt(c)
print(m)

Tiny LFSR

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import sys
from binascii import unhexlify

if(len(sys.argv)<4):
print("Usage: python Encrypt.py keyfile plaintext ciphername")
exit(1)

def lfsr(R, mask):
output = (R << 1) & 0xffffffffffffffff
i=(R&mask)&0xffffffffffffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)

R = 0
key = ""
with open(sys.argv[1],"r") as f:
key = f.read()
R = int(key,16)
f.close

mask = 0b1101100000000000000000000000000000000000000000000000000000000000

a = ''.join([chr(int(b, 16)) for b in [key[i:i+2] for i in range(0, len(key), 2)]])

f=open(sys.argv[2],"r")
ff = open(sys.argv[3],"wb")
s = f.read()
f.close()
lent = len(s)

for i in range(0, len(a)):
ff.write((ord(s[i])^ord(a[i])).to_bytes(1, byteorder='big'))

for i in range(len(a), lent):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
ff.write((tmp^ord(s[i])).to_bytes(1, byteorder='big'))
ff.close()

分析:
题目给出了一组对应的明密文文件,我们通过分析可以发现,通过明密文直接异或的前一部分便是key。
因此我们可以直接恢复出key,由于加解密都是异或操作,所以使用得到的密钥,结合题目文件的加密再对flag_encode.txt进行一次加密即可得到明文。
解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
plaintext = open("Tiny LFSR\\Plain.txt", "rb").read()
ciphertext = open("Tiny LFSR\\cipher.txt", "rb").read()

key_bytes = []
for i in range(min(len(plaintext), len(ciphertext))):
key_bytes.append(plaintext[i] ^ ciphertext[i])

key = ''.join(['%02x' % b for b in key_bytes])
print("Recovered key:", key)


def lfsr(R, mask):
output = (R << 1) & 0xffffffffffffffff
i=(R&mask)&0xffffffffffffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)

R = int(key,16)
mask = 0b1101100000000000000000000000000000000000000000000000000000000000

a = ''.join([chr(int(b, 16)) for b in [key[i:i+2] for i in range(0, len(key), 2)]])

f=open("Tiny LFSR\\flag_encode.txt","rb")
ff = open("Tiny LFSR\\flag.txt","wb")
s = f.read()
f.close()
lent = len(s)

for i in range(0, len(a)):
ff.write((s[i]^ord(a[i])).to_bytes(1, byteorder='big'))

for i in range(len(a), lent):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
ff.write((tmp^s[i]).to_bytes(1, byteorder='big'))
ff.close()

你听说过一次一密吗

貌似这题题目有些问题,而且之前在BUU上刷过,此次目的是查漏补缺,这里就不再多写了,直接上大佬讲解:Many-Time-Pad 攻击

花开藏宝地

题目附件有5个压缩包,根据提示分析,5个压缩包分别为数字爆破、小写字母爆破、大写字母爆破、伪加密、ntfs流隐写

最后获取到五组数据:

1
2
3
4
5
6
7
8
9
10
x1 = 305345133911395218573790903508296238659147802274031796643017539011648802808763162902335644195648525375518941848430114497150082025133000033835083076541927530829557051524161069423494451667848236452337271862085346869364976989047180532167560796470067549915390773271207901537847213882479997325575278672917648417868759077150999044891099206133296336190476413164240995177077671480352739572539631359
m1 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820813413
x2 = 152012681270682340051690627924586232702552460810030322267827401771304907469802591861912921281833890613186317787813611372838066924894691892444503039545946728621696590087591246339208248647926966446848123290344911662916758039134817404720512465817867255277476717353439505243247568126193361558042940352204093381260402400739429050280526212446967632582771424597203000629197487733610187359662268583
m2 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820818553
x3 = 40952412095267791829743119118333311932687870987919948671780408726886151430242690997238831410249436653299224291445012397813221016909468630372862610415470277301591535416193017906909638241212666990959976187895288689640250810487806568164431359887246760313154046201720715301307811951233077581047872827004824833876458687145628724339714212107812941785880896399800008924818580623979723496070665230
m3 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820819351
x4 = 100459779913520540098065407420629954816677926423356769524759072632219106155849450125185205557491138357760494272691949199099803239098119602186117878931534968435982565071570831032814288620974807498206233914826253433847572703407678712965098320122549759579566316372220959610814573945698083909575005303253205653244238542300266460559790606278310650849881421791081944960157781855164700773081375247
m4 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820820091
x5 = 230502064382947282343660159791611936696520807970361139469603458689311286041516767875903549263861950740778705012699983268093626403307298415066249636346303539570207577050391796770068203937723627361951969413683246596072925692670365490970847825269581004483964261491917680759091791653759514213188778401968676433284753781006738293752440186858616315727565803777032119737689210471541053061940547213
m5 = 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820822249

题目还给了一个数据:

1
80804238007977405688648566160504278593148666302626415149704905628622876270862865768337953835725801963142685182510812938072115996355782396318303927020705623120652014080032809421180400984242061592520733710243483947230962631945045134540159517488288781666622635328316972979183761952842010806304748313326215619695085380586052550443025074501971925005072999275628549710915357400946408857

原本没看懂是什么,后来搜了一下才知道是Asmuth-Bloom门限方案,参考:Secret Sharing - Web Encrypt
直接上脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import math
from Crypto.Util.number import long_to_bytes

remainders = [305345133911395218573790903508296238659147802274031796643017539011648802808763162902335644195648525375518941848430114497150082025133000033835083076541927530829557051524161069423494451667848236452337271862085346869364976989047180532167560796470067549915390773271207901537847213882479997325575278672917648417868759077150999044891099206133296336190476413164240995177077671480352739572539631359, 152012681270682340051690627924586232702552460810030322267827401771304907469802591861912921281833890613186317787813611372838066924894691892444503039545946728621696590087591246339208248647926966446848123290344911662916758039134817404720512465817867255277476717353439505243247568126193361558042940352204093381260402400739429050280526212446967632582771424597203000629197487733610187359662268583, 40952412095267791829743119118333311932687870987919948671780408726886151430242690997238831410249436653299224291445012397813221016909468630372862610415470277301591535416193017906909638241212666990959976187895288689640250810487806568164431359887246760313154046201720715301307811951233077581047872827004824833876458687145628724339714212107812941785880896399800008924818580623979723496070665230]
moduli = [347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820813413, 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820818553, 347051559622463144539669950096658163425646411435797691973701513725701575100810446175849424000000075855070430240507732735393411493866540572679626172742301366146501862670272443070970511943485865887494229487420503750457974262802053722093905126235340380261828593508455621667309946361705530667957484731929151875527489478449361198648310684702574627199321092927111137398333029697068474762820819351]
P = 80804238007977405688648566160504278593148666302626415149704905628622876270862865768337953835725801963142685182510812938072115996355782396318303927020705623120652014080032809421180400984242061592520733710243483947230962631945045134540159517488288781666622635328316972979183761952842010806304748313326215619695085380586052550443025074501971925005072999275628549710915357400946408857

def crt(rs, ms):
total = 0
prod = math.prod(ms)
for r, m in zip(rs, ms):
p = prod // m
total += r * pow(p, -1, m) * p
return total % prod

print(long_to_bytes(crt(remainders, moduli)%P))

一道有趣的题目

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#加密代码
def encrypt(plainText):
space = 10
cipherText = ""
for i in range(len(plainText)):
if i + space < len(plainText) - 1:
cipherText += chr(ord(plainText[i]) ^ ord(plainText[i + space]))
else:
cipherText += chr(ord(plainText[i]) ^ ord(plainText[space]))
if ord(plainText[i]) % 2 == 0:
space += 1
else:
space -= 1
return cipherText

# 密码
# 15120d1a0a0810010a031d3e31000d1d170d173b0d173b0c07060206

分析:
本题是一道算法逆向分析题,但是逆向求解时我们不知道space的具体值,也就没办法确定对应的计算方程。
但是我们可以发现密文的字节长度只有28,因此我们可以尝试爆破每一位明文的最后一位bit(总共约$2^{28}$种可能的序列),这样我们便能求出正确的space序列。
如何判断每个明文lastbit(即代码中的seq)序列是否正确呢?由于加密算法使用的是异或,因此可以利用加密算法来单独对明文的lastbit进行加密,再对比密文的lastbit是否一一对应,若确实一一对应,那大概率是正确的lastbit序列。
之后再通过我们已知的部分明文字节afctf{,便可以逆向求解了。
解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from tqdm import trange
cipher = bytes.fromhex("15120d1a0a0810010a031d3e31000d1d170d173b0d173b0c07060206")

def get_lastbit():
space = 10
for x in trange(2**28):
seq = bin(x)[2:].zfill(28)
space = 10
f = 1
for i in range(28):
if i + space < 28 - 1:
tx = int(seq[i]) ^ int(seq[i + space])
else:
tx = int(seq[i]) ^ int(seq[space])

if tx != cipher[i] % 2:
f = 0
break
if int(seq[i]) % 2 == 0:
space += 1
else:
space -= 1
if f == 1:
print(seq)
return seq

def decrypt(cipher_bytes, known_prefix=b'afctf{'):
plain = [0] * len(cipher_bytes)
space = 10
seq = get_lastbit()
#temp = 1010011010010101111111101001
print(seq)
seq_bits = [int(b) for b in str(seq)]
print(seq_bits)
space_list = []
for i in range(len(seq_bits)):
if seq_bits[i] % 2 == 0:
space += 1
else:
space -= 1
space_list.append(space)
print(space_list)
#部分已知前缀
for i in range(len(known_prefix)):
plain[i] = known_prefix[i]

#逆向逐字节恢复
for i in range(len(cipher_bytes)-1,0,-1):
if i + space_list[i-1] < len(cipher_bytes):
plain[i] = plain[i + space_list[i-1]] ^ cipher_bytes[i]
else:
plain[i] = plain[space_list[i-1]] ^ cipher_bytes[i]


return bytes(plain)

flag = decrypt(cipher)
print(flag)

AFCTF 2018 Writeup
http://ramoor.github.io/2025/07/31/AFCTF 2018 Writeup/
作者
Ramoor
发布于
2025年7月31日
许可协议