ImaginaryCTF 2023
好比赛!!是我最喜欢的maple大佬出题,但今年难度比去年高了很多,也学到不少。
rsa
私钥都给了,就考的是ssl证书格式的读取。
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
cipher_text = open(r"flag.enc", "rb").read()
c = bytes_to_long(cipher_text)
key1 = RSA.importKey(open(r"private.pem").read())
key2 = RSA.importKey(open(r"public.pem").read())
print(long_to_bytes(pow(c,key1.d,key2.n)))
emoticons
题目是一个单表替换,利用flag头ictf{的hex形式为696374667b,可以确定ictf{在密文中出现的位置,并且确定好几个字符和emoji的对应关系,其余的对应关系再利用全排列爆破即可。
from itertools import permutations
from binascii import unhexlify,hexlify
from tqdm import tqdm
m = open('out.txt','r',encoding='utf-8').read()
print(m)
print(hexlify(b'ictf{frequency'))
# 696374667b6672657175656e6379
# 🎈🐳🎈🌸🍕🎉🎈🎈🍕🎸🎈🎈🍕🎁🎈🌼🍕🦋🍕🌼🎈🌼🎈⚡🎈🌸🍕🐳
dic = {'🎈':'6','🐳':'9','🌸':'3','🍕':'7','🎉':'4','🎸':'b','🎁':2,'🌼':'5','🦋':'1'}
emojis = [n for n in r"🍔🚀🌞🍦🐶🌺⚡️"]
print(emojis)
ee = emojis[:7]
print(ee)
for i in tqdm(permutations(ee,7)):
dic = {'🎈': '6', '🐳': '9', '🌸': '3', '🍕': '7', '🎉': '4', '🎸': 'b', '🎁': '2', '🌼': '5', '🦋': '1'}
for e, c in zip(i, "08acdef"):
dic[e] = c
mm = ''
for j in m:
mm = mm + dic[j]
flag = unhexlify(mm)
try:
if 'ictf{frequency' in flag.decode() :
print(flag)
except:
continue
#b'Emoticons, also known as smileys, are graphical representations of facial expressions used to convey emotions or tone in written communication. They have become an integral part of online messaging, social media platforms, and email correspondence. Emoticons are formed using a combination of keyboard characters and symbols, allowing users to express their feelings and add nuance to their text-based conversations. ictf{frequency_analysis_is_really_fun_right} The primary purpose of emoticons is to enhance digital communication by bridging the gap between written text and face-to-face interactions. They provide a way to convey emotions, such as happiness, sadness, surprise, or humor, which can be challenging to express solely through words. For example, a simple smiley face :) can denote happiness or friendliness, while a frowning face :( can indicate sadness or disappointment. Emoticons offer a visual shorthand that helps clarify the intended emotional context of a message, reducing the chances of miscommunication or misunderstandings. Moreover, emoticons also contribute to the creation of a more personalized and relatable online environment. By using emoticons, individuals can infuse their written messages with personality, humor, or sarcasm. This adds depth and richness to conversations, making them more engaging and enjoyable. Emoticons serve as a form of nonverbal communication in the digital realm, providing a way to convey subtle cues and emotional nuances that would typically be expressed through facial expressions, gestures, or tone of voice in face-to-face interactions. In summary, emoticons are graphical representations of facial expressions that have revolutionized online communication. They allow individuals to express emotions and add context to their written messages, improving understanding and reducing the risk of miscommunication. By incorporating emoticons into digital conversations, people can infuse their texts with personality and create a more vibrant and relatable online environment.\n'
signer
目标message的crc值是已知的,只需要把这个crc值分解成几个值的乘积,再找到这几个值对应的原字符串发过去就行了,这个reverse的过程需要用到github上的一些工具。
from pwn import *
io = remote('signer.chal.imaginaryctf.org',1337)
io.recvuntil(b'n = ')
n = io.recvuntil(b'-')[:-1].replace(b'\n',b'')
n = int(n)
print(n)
print(io.recvuntil(b'flag\n'))
io.sendline(b'1')
io.recvline()
io.sendline(b"0yh6zd")
s1 = int(io.recvline()[11:])
print(io.recvline())
print(io.recvline())
io.sendline(b'1')
print(io.recvline())
io.sendline(b'5VsRF')
s2 = int(io.recvline()[11:])
s = s1*s2 % n
print(io.recvline())
print(io.recvline())
io.sendline(b'2')
print(io.recvline())
io.sendline(str(s).encode())
io.interactive()
# ictf{m4ybe_crc32_wasnt_that_secure_after_all_1ab93213}
Wasteful
参考地大学弟的做法复现一下,已知信息:\(\small len(e_f)=1024,e_s=Aphi+e_f,len(A)=2048//3,len(n)=2048\) 那么A可以求出来,\(\small A=e_s//n+1\)。然后\(\small e_s//A=phi+e_f//A\),后边部分太小了直接省掉,就得到一个\(\small n-p-q+1\)的近似值,利用这个和n就能求出p或q的近似值,也就是p的高位,后边来个p高位攻击恢复就好了。
from gmpy2 import *
from Crypto.Util.number import *
pub = (13922840822259331816327061476419085409003512519528232469661604921423111313933598569871108546042864180459201549111178812056125732062953204575105829153985440916358190133642640001059768522480269302460718708597523393249631888987925111597599833550446912844403320258488415701628616610291446078064678789122886626285576964983117608034972639529880315364530395658464093479171544352975507100901794069628879853771787404835325718642690170628943504486705897282590391844395048189949020318348859452241379637822658476872641393738097989171169214581791719140741349554748016829670973465416125341357284757048506380319235703137899963208097, 277280553454648256942834244379334472844168843473349557354045626945887508188843789771287658693103957086560979814144224475018152887866998026452048010146805033029525535857427508882228600591838528622357772029813779190474001956391284442790327418967008241712383374750163158501752035607501218757269377329115862935993211034408859090869371053922670217394364484435238584857739198196990517123089953446738914179515819217861804510588560805960014326909359351025582398395246863003472699126965102937701727970546875134667199496557865907051073237849103589853403297723219825816214258909806838856777367332603146213417602038287539276024671910161582026600672105045278321812944891449538504092047724097803562730455612681768718159973804451251572816656604960574129566605495957657313103014678987914865218770999608008584731005908772508291497450420251)
c = 3829822460565637897613746990073994763941184171034737019836828390881076647859928468862763977005814025279072259387758773244151849201650347661717265009919615554395232777636705330792559486048004076300537706604078026741727734943374711480077394341022197696120902084993991738576670236710557122356593411988288326114827264936321401579532665129353852827951354347055111972043781820183887400365339563091888343224947709711699474542183525971689156145628294192147447087789859929759168015997297169275809161690938900939934830602427271340102001253593076013316161869089892572516396348368906753304918487695981844135741146949836634949417
n,es = pub
A = es//n + 1
# p+q的近似值
t = n - es//A
p_q = iroot(t^2 - 4*n,2)[0]
ph = (t + p_q)//2
PR.<x> = PolynomialRing(Zmod(n))
f = ZZ(ph) + x
f.small_roots(X=2^400,beta=0.5)
p = ph + 23404084453800851210852260916105348863638485055901451524821885751026557174568191624636664556181441903932
q = n//p
assert p*q == n
fi = (p-1)*(q-1)
print(long_to_bytes(ZZ(pow(c,invert(es,fi),n))))
# ctf{just_an_obligatory_coppersmith_challenge}
Tan
给了\(\small x=tan(flag)\)的值,要求flag。首先tan函数是以\(\small \pi\)为周期的,因此有\(\small arctan(x)=k\pi+flag\),利用这个关系可以构造格,详情见exp,有几个需要注意的点。第一列需要乘上系数优先规约,并且由于精度问题,规约之后得到的向量的第一维是不为0的,而第二维是flag,第三维为了平衡也需要得到约\(\small 2^{320}\)大小的值,为了规约成功需要细调第一维的系数。
RR = RealField(1024)
x = RR(-0.7578486465144361653056740883647981074157721568235263947812770328593706155446273431983003083023944193451634501133844062222318380912228469321984711771640337084400211818130699382144693337133198331117688092846455855532799303682791981067718891947573941091671581719597626862194794682042719495503282817868258547714)
ax = atan(x)
pi_ = RR(pi)
M = Matrix(ZZ, [
[(ax*2^800).round(), 0, 2**320],
[-2^800, 1, 0 ],
[(pi_*2^800).round(), 0, 0],
])
for r in M.LLL().rows():
print(r)
print(int(abs(r[1])).to_bytes(60,'big'))
Sus
非常需要代数技巧的一个题,这里放一下出题人的思路。
Pick a random polynomial \(\small f(x)=x^3+ax^2+bx+c\), and pick a random element \(\small a\) in \(\small \mathbb{R}=\mathbb{Z}_n[x]/f(x)\). If \(\small f(x)\) is irreducible in \(\mathbb{F}_p[x]\) then \(\small \mathbb{K}=\mathbb{F}_p[x]/f(x)=\mathbb{F}_{p^3}\) will be a field with order \(\small p^3-1=(p-1)(p^2+p+1)\).
We raise \(a\) to the power of \(\small n=pqr=p(p^2+p+1)r\) then \(a^n\) would probably be of order \(\small p-1\), which implies it will be in the form of \(\small u+0x+0x^2\) in \(\small \mathbb{K}\). This means we can take the degree 1 or 2 coefficient of \(a^n\) in \(\mathbb{R}\) and gcd it with \(n\) to get \(\small p\), then we can fully factor \(\small n\) to decrypt the flag.
This is basically the same idea as Pollard's p-1 or Williams' p+1 factorization algorithm, but we are doing it in a field with higher degree.
from Crypto.Util.number import *
n = 1125214074953003550338693571791155006090796212726975350140792193817691133917160305053542782925680862373280169090301712046464465620409850385467397784321453675396878680853302837289474127359729865584385059201707775238870232263306676727868754652536541637937452062469058451096996211856806586253080405693761350527787379604466148473842686716964601958192702845072731564672276539223958840687948377362736246683236421110649264777630992389514349446404208015326249112846962181797559349629761850980006919766121844428696162839329082145670839314341690501211334703611464066066160436143122967781441535203415038656670887399283874866947000313980542931425158634358276922283935422468847585940180566157146439137197351555650475378438394062212134921921469936079889107953354092227029819250669352732749370070996858744765757449980454966317942024199049138183043402199967786003097786359894608611331234652313102498596516590920508269648305903583314189707679
e = 65537
c = 27126515219921985451218320201366564737456358918573497792847882486241545924393718080635287342203823068993908455514036540227753141033250259348250042460824265354495259080135197893797181975792914836927018186710682244471711855070708553557141164725366015684184788037988219652565179002870519189669615988416860357430127767472945833762628172190228773085208896682176968903038031026206396635685564975604545616032008575709303331751883115339943537730056794403071865003610653521994963115230275035006559472462643936356750299150351321395319301955415098283861947785178475071537482868994223452727527403307442567556712365701010481560424826125138571692894677625407372483041209738234171713326474989489802947311918341152810838321622423351767716922856007838074781678539986694993211304216853828611394630793531337147512240710591162375077547224679647786450310708451590432452046103209161611561606066902404405369379357958777252744114825323084960942810
k = 3
R = Zmod(n)["x"]
while True:
Q = R.quo(R.random_element(k))
pp = gcd(ZZ(list(Q.random_element() ^ n)[1]), n)
if pp != 1:
qq = sum([pp**i for i in range(k)])
rr = n // (pp * qq)
assert n == pp * qq * rr
break
phi = (pp - 1) * (qq - 1) * (rr - 1)
d = pow(e, -1, phi)
m = pow(c, d, n)
print(long_to_bytes(m))