目录
Superprime
Reverse Meow_way
BabySSS
Secret
Superprime
这个题5层RSA加密,很简单就是要带条件爆破5组p,q,一开始看错了,对为5组是一一对应的,回头发现后边两对不是对应的。
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
def getSuperPrime(nbits):
while True:
p = getPrime(nbits)
pp = bytes_to_long(str(p).encode())
if isPrime(pp):
return p, pp
p1, q1 = getSuperPrime(512) #ok
p2, q2 = getSuperPrime(512)
p3, q3 = getSuperPrime(512)
p4, q4 = getSuperPrime(512)
p5, q5 = getSuperPrime(512)
n1 = p1 * q1
n2 = p2 * p3 #明显很小,不是 p2*q2是p2*q3
n3 = q2 * q3
n4 = p4 * q5
n5 = p5 * q4
e = 65537
c = bytes_to_long(open("flag.txt", "rb").read().strip())
for n in sorted([n1, n2, n3, n4, n5]):
c = pow(c, e, n)
print(f"{n1 = }")
print(f"{n2 = }")
print(f"{n3 = }")
print(f"{n4 = }")
print(f"{n5 = }")
print(f"{e = }")
print(f"{c = }")
对三种情况对应3种爆破姿势。
数据部分,方便哪们想复现的话可以用。hitcon的题目会放到googleapis上,但这个地址对国内极不稳定,明通明不通
n1 = 132240475872174910020944408159013384770525986234801028624784519134365862704105251340824787510945183963356820885920367304711310957365047441178683686926111455575911962257698539064559510444855775549001648292058855493337857073693460061212792795075263084221929517773754699732129582794061997056827998985829666251060653380798686353779510948629280009197715644814616657550158740378670095210797455828266323922570838468050733341304227070902756780052159113811360169205531739117518635829837403006788948761106892086004133969899267757339921
n2 = 95063555614573541810575593850289506856925979977609991181205616159125089261546784721154599484613262518469706157215924537125160406418217535531993036958388330505871763176297570429533467696205928686731756713059807727405313286020007347211892135226632724359291407913539632339885950358476265995466145680878334722001
n3 = 59077122528757446350604269162079270359932342538938986760275099248290981958441838384256597839386787448447136083450980256330743221155636885358548541847176342745397373638152767362253944731433134474562146358334422503033973244936835557423934491676324297243326613498319086748647812745223753746779568080090592100960499863677438403657325762852705171109382084916335379889394829715777901290096314487661584614712488781379507151355301063123233880909931925363322846957197537676660047824476129446066149857051131731840540157488590899311381370266592295206055792990886734933291304077440476730373491475852882163732120626849448728573574411786320772125534383707413572678316508826450778346723441956945169297689138799298561759843280317867927205551400065163199599457
n4 = 24589423756497058585126900932611669798817346035509889383925628660158156567930038333401661451846451875869437263666365776498658699865323180836374906288949824205543130261556051807217164348291174483234810669420041361857307271050079366739157441129916338485838528114129985080841445467007786565727910355311119650431197742495274527401569906785121880408809802492383216836691265423297722021017515667257863302820657924121913047547741420413553737917632809270380269758313556777803344394624408862183672919570479289614998783678080936272369083
n5 = 185885020243714550225131939334004568560534422416697599024696590344782893162219788079305752632863387855011040772104676488940707663157284194641170875157382507202789029285286466326803699701161968587945867047101502048926016515139728368809523009828247173096909917611001113266938209226483162533302629909322412013492978440863258135181226831155024690292336026753678424906151360739424148666951389956182136072508650529271179749569637083537783283360860102371562796635391549934474381821125255176073752645643893294533330184238070085333427
e = 65537
c = 44836759088389215801662306050375432910426695023654894661152471598197009644316944364461563733708795401026569460109604554622161444073404474265330567406370705019579756826106816505952084633979876247266812002057927154389274998399825703196810049647324831928277737068842860115202258059693760003397831075633707611377854322143735834890385706873765241863615950449707047454133596389612468366465634011925228326638487664313491916754929381088396403448356901628825906815317934440495749705736715790281606858736722493438754469493049523175471903946974639097168758949520143915621139415847104585816466890751841858540120267543411140490236193353524030168152197407408753865346510476692347085048554088639428645948051171829012753631844379643600528027854258899402371612
from Crypto.Util.number import getPrime, isPrime, bytes_to_long,long_to_bytes
from gmpy2 import invert, iroot
def getSuperPrime(nbits):
while True:
p = getPrime(nbits)
pp = bytes_to_long(str(p).encode())
if isPrime(pp):
return p, pp
第1种,最简单,因为q = bytes_to_long(str(p)),p,q是相关的可以直接爆破,原先经常用的是2进制的,现在这个是10进制的,就乘pow(10,i)
#1,
#n1 = p1 * q1
def get_sq(n, l):
p = 0
for i in range(l,-1,-1):
for j in range(9,-1,-1):
sp = p + j*pow(10, i)
sq = bytes_to_long(str(sp).encode())
if sp*sq<= n:
p = sp
print("", i, j)
break
print(p)
return p
p1 = get_sq(n1, 220)
#p1 = 8146548592442976266345996123132853490697005499246649457977706700220974227149533573761967281334961993159106889103915430835029497970085237349414587895387361
q1 = bytes_to_long(str(p1).encode())
对于n2,n3来说由于p,q并不对应,所以这里从后向前爆破,正好通过n3尾部判断。如果没有这人实际上只通过n2尾部的话是不能爆破的,有数不清的组合。
从后爆破的话还有一个问题,就是不足位时前被0,但如果是最后一位就不再被0,所以这里对小的做了能分解直接退出,避免无法判断的补0出现。
#2,
#n2 = p2 * p3
#n3 = q2 * q3
def get_tail(p2,p3, idx):
print(f"{idx}\n{p2=}\n{p3=}")
tn2 = p2*p3
if tn2>n2:
return False
if p2>1 and n2%p2 == 0:
print(f"OK\n{p2=}\n{n2//p2}")
exit()
for i in range(10):
for j in range(10):
tp2 = p2+i*pow(10,idx)
tp3 = p3+j*pow(10,idx)
if str(tp2*tp3)[-(idx+1):] == ('000'+str(n2))[-(idx+1):]:
if idx == 154:
print('x', str(tp2*tp3)[-(idx+1):], ('000'+str(n2))[-(idx+1):])
#爆到的 idx 字节 * 8 位 与n3 比较
sq2 = bytes_to_long(str(tp2).zfill(idx+1).encode())
sq3 = bytes_to_long(str(tp3).zfill(idx+1).encode()) #当不足时补0,当足时补0错
mask = (1<<((idx+1)*8))-1
if ((sq2*sq3)^n3) & mask == 0:
#print(hex(mask), tp2,tp3)
get_tail(tp2,tp3, idx+1)
get_tail(0,0,0)
p2 = 8896291584885417569104339023688932443326753617531842711599106401724528341937087331194574622599888534531753509912922572906401573640791655490141830263538581
q2 = bytes_to_long(str(p2).encode())
p3 = 10685750878049478986600454022422733804784834227531623991827538970867377925593354382775253050419846972347584519245766235538419501021140939003899401773087821
q3 = bytes_to_long(str(p3).encode())
第3种由于p4*q5没有尾部特征,只能从头开始爆破。需要判断上下边界,即后部全0大于n和后部全9小于n的情况剪掉。
#n4 = p4 * q5
#n5 = p5 * q4
def get_45(p4,p5,idx):
if idx<0:
return False
if idx<153 and (p4==0 or p5 == 0): #154-155位10进制
return False
if p4>1 and n4%p4 == 0:
print(f'OK\n{p4 = }\np5 = {long_to_bytes(n4//p4)}')
exit()
#print(idx,p4,p5)
q4 = bytes_to_long(str(p4).encode())
q5 = bytes_to_long(str(p5).encode())
if p4*q5 > n4 or p5*q4 > n5:
#print('>')
return False
if idx>0:
sp4 = str(p4).encode()[:-idx]+b'9'*(idx)
sp5 = str(p5).encode()[:-idx]+b'9'*(idx)
#if idx<2:
# print(idx,sp4,sp5)
rp4 = int(sp4)
rp5 = int(sp5)
rq4 = bytes_to_long(sp4)
rq5 = bytes_to_long(sp5)
if idx<154 and p4>0 and p5>0 and (rp4*rq5<n4 or rp5*rq4<n5):
#print(f"{idx} ,{rp4} ,{rp5} , {rq4}, {rq5}, {n4}, {n5}, {p4*rq5<n4}, {p5*rq4<n5}")
return False
for i in range(10):
for j in range(10):
#print('>>>>>',idx,i,j)
tp4 = p4+i*pow(10,idx-1)
tp5 = p5+j*pow(10,idx-1)
get_45(tp4,tp5, idx-1)
#get_45(0,0,155)
p4 = 6759224678814800913204473280361658486772650199941114505283409645622497866148765146601841353932633241398607040792961131806556756943022446601137737571037341
p5 = 11868750061342011267437975338788313730068511026231254554820987614699325962867540061083226545014725808842166519144882448332395484715124334891963558447955747
q4 = bytes_to_long(str(p4).encode())
q5 = bytes_to_long(str(p5).encode())
#print(n4%p4, n5%p5)
最后是5个已经分解n的RSA按从大到小顺序解密
d1 = invert(e, (p1-1)*(q1-1))
d2 = invert(e, (p2-1)*(p3-1))
d3 = invert(e, (q2-1)*(q3-1))
d4 = invert(e, (p4-1)*(q5-1))
d5 = invert(e, (p5-1)*(q4-1))
#加密顺序 2,1,5,4,3
m = pow(pow(pow(pow(pow(c,d3,n3),d4,n4),d5,n5),d1,n1),d2,n2)
print(long_to_bytes(m))
#b"hitcon{using_different_combinations_of_primes_still_doesn't_make_this_bad_prime_generation_method_any_safer...}"
Reverse Meow_way
这个题一看就用了复杂的东西。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+0h] [ebp-24h]
int v5; // [esp+0h] [ebp-24h]
int v6; // [esp+14h] [ebp-10h]
int v7[2]; // [esp+18h] [ebp-Ch] BYREF
v7[0] = -1;
v7[1] = -1;
if ( argc < 2 )
{
sub_401340("Usage: %s <flag>\n", (char)*argv);
exit(1);
}
if ( strlen(argv[1]) != 48 )
{
sub_401340("Wrong length\n", v4);
exit(1);
}
v6 = (int)argv[1];
dword_40544C(v6, v6 >> 31, v6, v6 >> 31, 196, 0, v7, (int)v7 >> 31);
++v6;
......
dword_4053F4(v6, v6 >> 31, v6, v6 >> 31, 152, 0, v7, (int)v7 >> 31);
dword_405438(v6 + 1, (v6 + 1) >> 31, v6 + 1, (v6 + 1) >> 31, 101, 0, v7, (int)v7 >> 31);
v5 = memcmp(&unk_405018, argv[1], 0x30u); // 将0x30依次修改为1,2,...仅判断1个字节,爆破得到flag
if ( v5 )
{
sub_401340("Wrong\n", v5);
exit(-1);
}
sub_401340("I know you know the flag!\n", 0);
return 0;
}
用了48个函数,和参数,每个函数又都是指针,还被隐藏了。
不过最后有个问题memcmp对全部进行比较,这里如果将0x30改为1,他就仅对第1个字符比较。这样通过修改比较的长度就可以实现爆破了。
这个字节在0x401bf5在文件里的偏移是0xff5
后来看了别人的帖子,这里用push 0x33;retf将32位转64位执行,可惜没有get到出题人的用心。
import subprocess
data = list(open('meow_way.exe', 'rb').read())
flag = ''
for i in range(1, 0x31):
fname = f'meow_{i}.exe'
data[0xff5] = i
open(fname, 'wb').write(bytes(data))
for v in range(0x21, 0x7f):
tmp = (flag+chr(v)).ljust(0x30, '0')
rc,out= subprocess.getstatusoutput(f"{fname} {tmp}")
if 'I know you know the flag!' in out:
flag += chr(v)
print(flag)
break
print(flag)
#hitcon{___7U5T_4_S1mpIE_xB6_M@G1C_4_mE0w_W@y___} 提交不正确,把两头的下划开线删掉
#hitcon{7U5T_4_S1mpIE_xB6_M@G1C_4_mE0w_W@y}
BabySSS
看到一个大牛博客 ,里边有3道,说是后两道以后上,关注中。
这个题我用的crt方法,他还提到格基约减法。
由于并没有取模,只是限制在64位,所以把右部单位阵的1部分改为2^-63
然后进行约减,结果中前8位为0的行对应的值再乘2**63就得到对应的一组v
#LLL法
V = matrix(QQ, 130,8+130)
for i in range(8):
for j in range(129):
V[j,i] = pow(a[i][0], j)
V[129,i] = -a[i][1]
for i in range(129):
V[i, 8+i] = pow(2, -63)
V[129,8+129] = 1
k = V.LLL()
#前8位全0的行
v = k[1][8:-1]*9223372036854775808 #2**63
Secret
看上去挺简单,就是个共模攻击,由于共模攻击时可以两边共同除其中一项m^(p+e1) / m^(p+e0) = m^(e1-e0) 所以这里的p可以解决掉。
麻烦的就是模未知。
“取一组核空间的基构成的矩阵,在这个矩阵上进行LLL算法即可得到符合条件的小系数 。因为我们不能求逆,把正负号分两边进行计算,即可得到n的整数倍 , 利用多组这样的倍数求一个最大公倍数即可恢复n。” 一时没理解,原样复制。
from output import es,cs
B = matrix(ZZ, 2, 64)
B[0] = es
B[1] = [1 for _ in range(64)]
M = B.right_kernel_matrix()
L = M.LLL()
#求n
def compute_kn(coff):
res_right = 1
res_left = 1
for i ,cof in enumerate(coff):
if cof > 0:
res_right = res_right * cs[i]**cof
else:
res_left = res_left * cs[i]**(-cof)
return res_left - res_right
n = compute_kn(L[0])
#再求公约数
for l in L[1:]:
n = gcd(compute_kn(l),n)
n = factor(n,limit = 2**20)[-1][0]
if n.nbits() <= 2048:
break
print(n)
得到n后先处理一下p,然后用共模攻击
n = 17724789252315807248927730667204930958297858773674832260928199237060866435185638955096592748220649030149566091217826522043129307162493793671996812004000118081710563332939308211259089195461643467445875873771237895923913260591027067630542357457387530104697423520079182068902045528622287770023563712446893601808377717276767453135950949329740598173138072819431625017048326434046147044619183254356138909174424066275565264916713884294982101291708384255124605118760943142140108951391604922691454403740373626767491041574402086547023530218679378259419245611411249759537391050751834703499864363713578006540759995141466969230839
from Crypto.Util.number import *
mpe1_inv = inverse(cs[0],n)
_es = [e-es[0] for e in es[1:]] # _c = m^(e1-e0)
_cs = [c*mpe1_inv%n for c in cs[1:]] #
#找两个互素的e进行共模攻击 find gcd(_ei,_ej) = 1
find = False
for i in range(len(_es)):
for j in range(len(_es)):
if gcd(_es[i],_es[j]) == 1:
find = True
break
if find:
break
print(i,j)
e1,e2 = _es[i] , _es[j]
c1,c2 = _cs[i] , _cs[j]
g, x1, x2 = xgcd(e1,e2)
m = pow(c1,x1,n)*pow(c2,x2,n) % n
print(long_to_bytes(int(m)))
#hitcon{K33p_ev3rythIn9_1nd3p3ndent!}