MISC
FindYourWindows
题目拿到两个文件:密钥文件和磁盘镜像文件
使用磁盘解密的常用工具VeraCrypt
进行镜像的挂载,并且使用指定keyfile
进行解密,挂载到任意空闲的驱动器号中:
挂载成功后,就能像正常访问Windows文件管理器一样,进行简单的文件翻找,没有发现flag
使用WinHex打开磁盘驱动器,可以看到有关磁盘的相关信息
考虑到flag字符串的格式,直接在Winhex
中搜索字符串,得到flag
题目分析:
这是一道很标准的磁盘取证题,关键就是要注意到文件的大小比较规整,为2048KB
,结合另一个附件的文件名为key,就能知道这是一个被Keyfile
密钥文件加密的文件,进而开始磁盘的取证分析,主要有以下做法:
- 磁盘文件的解密和挂载
- 磁盘文件格式(FAT32,NTFS)的分析
- 磁盘文件扇区的恢复,找回删除的文件
- 对磁盘内文件的分析
什么?来玩玩心算吧
题目交互环境是一个简单的python计算器
计算24点并不能获得flag,那么需要命令执行,确定题目类型为pyjail
逃逸
注意到交互中的所有字母都被WAF
,似乎是无字母RCE,简单Fuzz一下只有括号,数字,运算符可以通过。
想到使用Unicode
字符绕过,使用如下的宽体字,可以成功通过检验得到执行:
eval(input())
题目分析:
python引入了对unicode的支持,这使得很多基于黑名单危险函数和字母的WAF均可以被简单绕过,总的来说,这个"无字母" pyjail并没有像PHP无字母RCE那样困难。
此外,如果使用八进制,如\160\162\151\156\164\50\70\51
同样可以完成无字母绕过,但是此题限制了斜杠,不适用
四妹还是萍萍
使用十六进制编辑器打开附件图片,发现了异常的IDAT块:
在倒数第二个IDAT处发现了类似压缩包的文件结构,包内存在文件passwrod...
提取出压缩包,利用公众号得到的密码解压得到,文本文件,发现图片的BASE64标识 iVBOR
很容易还原图片:
不难发现这是宽度修改不当导致的色块分布错误,需要根据CRC32值爆破宽高进行修复。
- 宽为: 00000311 高为: 00000616
得到flagtu'pian
我太喜欢亢金星君了
附件得到gif,很显然是要分析帧图片,使用工具分离一共258帧图片:
根据题目提示,需要转成摩斯码,而黑色图片似乎间隔出现,不做译码
将白色图片作为分隔符,编写脚本转换摩斯码(使用md5哈希标识每张图片)
import os
import numpy as np
from PIL import Image
from hashlib import md5
files = os.listdir("./gif")
files = sorted(files, key=lambda x: int(x.lstrip("output.gif_").rstrip(".jpg")))
dic = {}
res = ""
tags = ["-", "", ".", "/"]
for img in files:
img = Image.open("./gif/" + img)
arrimg = np.array(img).flatten()
imghash = md5(arrimg).hexdigest()
if imghash not in dic:
dic[imghash] = tags[len(dic)]
res += dic[imghash]
print(res)
对得到的摩斯码进行解密就能得到flag.
Black&White
黑白图片已经给出,显然需要先将其转换为01的二进制信息:
import os
from PIL import Image
fl = [i for i in os.listdir(".") if i.endswith(".jpg")]
fl = sorted(fl, key=lambda x: int(x.split(".")[0]))
res = ""
for i in fl:
im = Image.open(i).convert("L")
color = im.getcolors(256)[0][1]
if color == 255:
res += "1"
else:
res += "0"
print(res)
得到了01串,发现大小恰好为33*33
,将其转换黑白的二维码,扫码得到密文3I8XEDHUCJTARQFOEDX7D+08AC80T8N08Y6948DF2C43C9B6Z2
,发现只有大写字符和数字,尝试Base45解密得到flag
题目分析:
本题和上一题一样,都需要手动编写脚本批量处理图片文件,不同于隐写题,图片处理需要对图片内容拥有敏锐的感知,如RGB色值转ASCII码等处理方式,都需要至少熟悉一种图像处理的库,才能做起来得心应手。
CRYPTO
Ju5t_d3c0de_1t!
进制、编码和RSA
e = 'VTJGc2RHVmtYMThUWGplSkN2YzJwazJ2KzJieQ=='
c = 10110011010010110101101101011110100001010100101110111011101101111101101001000010111010010011101111001111111000000001111000000010101
p = 1100010000110101100011011010101011111101001101010011001010110111100011110110101111000101100001011011001100110010010111111100110000001111010111111101111001100111100011100110110011101011111011100111010011000100011001101101111011010110000011011100101010010101110001011011010111001010101101100101011111101000110010111000011010111001101001
q = 11000010010010011110101110100011011100101101100100011011100010111000011110111000000011111100010100100000101101010101000101101101101011011100001101111010001010010011001010110010110101001100100011010110100011110011101110101110111110100000011110101010011011111010111100011000011111001000010101100100011110010000010001110010101100100111001
key = 592924741013363689040199750462798275514934297277010275281372369969899775117892551575873706970423924419480394766364097497072075403342004187895966953143489192628648965081601335846012859223829286606349019
# use m minus key to get the final flag!
题目分析:
RSA本身并不难,但是需要进行解码,其中,e需要在base64之后进行Rabbit
解密得到65537
已知p,q,解密的过程并不难,根据提示需要最后减去key再转为字符串。
ezzzzz_RSA
题目涉及两个关于RSA因子的问题:
n = p * q
n1 = q * r
n2 = p * s
c = pow(m, e, n)
h0 = pow(2023 * p + 2024, s, n2)
h1 = pow(2024 * r + 2023 * q, 113, n1)
h2 = pow(2023 * r + 2024 * q, 629, n1)
题目分析:
- 首先是利用
h0
分解n2
,在(2023 * p + 2024)^s
中只有一个不含p的常数项2024^s
,利用2024^n2 % n2
做差就能消除,然后和 n2 就拥有了共同的因子p - 然后利用
h1,h2
分解n1
,在模n1下,r和q的共同项都会被消去,根据二项式定理就知道,h1和h2只含r和q的最高次项。
e = 65537
n1 = 19957426023169626195602761840035904096149402534966487535713447987366768645542881124782551268978342063458430846877824210659778126281705984711061190351636497944943321988950188171159903717348936556346198638311950016136865425015037098270040031872702873264144372191898253134939805153141701819590164140250130420280491966786900651186941317959556066730959744279963976065565436153399679475410040773637142677936926894677919242351610457296203864806991539480593546084449323017670431590012312526757477514457145686070196978477495658962519391041011847512041022828710693830661412217389320600888361578917153088073678587422269955710471
n2 = 11933661747067216317642315621042074566046499785197709817779978157416906347669444374234313329064859622960743743511735672614999566264025648698589886185056758071718319964262619819143757922916624196354313322456534266520150543008117888101349920396737532937616502689667208207329048979872222563877933742673021891249520999021187404065706388700711208445628041386956459398271230236018476964839399245143666534359113777846535151773174701732284280083586580489995666306373839417946648196140879978268472361473557375951972193618245984950374326806423407152520541682571610372434453778172497925696535270204943842467472100237854318244291
c = 20080676122944896238797522372441559951736929534371084097400233944319893926800196694449564534150770085554349952433141815637324753386484549616573636001763815852095984830828952020047938406909274311785306299061021662484544371813739713520361343350959698642021322243662988875917088108399877176033404097457939417134483333264562602633853694382014472747500159100723626314928476484037666519857604568300967071868151508142784271042600815406853978696857309760951105852288354603503207383899902135741426285551161292195639862478256231538619968275273876467583013024899054710124331145912185471501398910765579441956531091561893256832468
h0 = 2996726009726260695732821166504040344731102637047682432884058857493935625094258046641569918904978173116793673563730117949606727933902262668880339210084101176866383602543966179840353633735507442926707342258391362245904850297416642271123328980812931025677857373199540129280097315832907023777052101133649877194495480543646472133854655383755313968952550827443970931104462445312146328606862802196901953935238972759852435882720786570965542286278549107402918041194008845717507735786897968734831064393337773557817839343449001368565856921138408039931608804233595980497557733714560035682416265029819340316734845279080134432704
h1 = 19843160604742228074331688651361052208481287636527838615063387670722213224954610448720065937378201545177278841575633697012434074186046556843292068835752113384756149944114298949115412819730843598288637259467085268861201775723817790428386595559040938133481222229290199923979132846871398172318539492741755408720073350962388138453341677009547616238262211176727424067946020683742262782319735286357465817786446238528187722959357444676512705451504136333336415880020502524009647940182721264953084120705872870651891290569527156804993340563927419561415555818468261824287933683736509372616293569615247228388443284457740072850735
h2 = 15147052684674827267989051566164167603473413362261253296001082161136918959833294463185335416662127368473980239667918561600741667513285708843081475074688239507330230558331408877583246661862040918410036936505307437329914363201630212163952357444441705663871720438955166472073576526814546767805314463827075388036712200327696168965762177567346966479399896578190111819130000991594490932388132188241726654756368698998232826340969288082645860324404980143489489946490266439447342461483490582149239131554246756547000945718737195930407251232848166108751122870333559461452459416252942341423373918245090162970624108991537972775066
a = pow(h1, 629, n1)
b = pow(h2, 113, n1)
t = 629 * 113
A = pow(2024, t, n1) * a % n1
B = pow(2023, t, n1) * b % n1
r = GCD(n1, A - B)
q = n1 // r
p = GCD(pow(2024, n2, n2) - h0, n2)
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, p * q)
print(libnum.n2s(m))
gift
题目涉及p+q
高位已知的问题:
def get_gift(p, q):
noise = getPrime(40)
p, q = p + 2 * noise + 1, q - pow(noise, 2)
gift = 2024 * (p + q)
return gift
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 0x10001
m = bytes_to_long(flag)
题目分析:
先可以把式子变形为(p+q)-(r-1)**2+2
,p,q为512位,noise的位数为40,显然gift泄露了p+q的高位
可以多次尝试发现,除去低86位,gift高位等于p+q的高位
造Copper解决:
#sage
import gmpy2
from Crypto.Util.number import *
gift = 46635322848619790584491725916282901439691751328335921415278638528896063068132242718070261114525516272650970256270551306096774004921902972838212903368063625872
c=101383046356447336426623798470530695448361708798731382238747567108067236241251384089401506320741815081024352908156466877907424203888923965647318146770258139921360377246187637085549628797640957048672797430217647039035455011311505942632107576730906489223641894279483592789523228409885925263914621255862261546919
n=131097719698687108485813302886652389604731026998272796315024695395496199386497660846418712521921387496051077394308820230360184411431376692252923609505060476542577219656866593501271690536991944882324175509626138475159461332403161471880082192150081456601522403673111515117219716055561941951891570977025178643791
gift=(gift//2024)-2
x1=(gift>>86)<<86
e = 0x10001
PR.<x> = PolynomialRing(RealField(1000))
f = x*(x1-x) - n
phigh = int(f.roots()[0][0])
print(phigh,phigh.bit_length())
PR.<x> = PolynomialRing(Zmod(n))
f = phigh + x
res = f.small_roots(X=2**90, beta=0.4,epsilon=0.01)[0]
p=int(res+phigh)
q=n//p
assert p*q==n
d=gmpy2.invert(e,(p-1)*(q-1))
m=int(pow(c,d,n))
print(long_to_bytes(m))
girls_band_cry_pto
def getprime(kbit, FLAG):
a = getPrime(kbit)
b = getPrime(kbit)
N = getPrime(kbit + 5)
seed = getPrime(kbit)
t = seed
list_t = []
for i in range(10):
t = (a * t + b) % N
list_t.append(t)
if FLAG:
print(list_t)
return seed
p = getprime(512, 1)
q = getprime(512, 0)
flag = b"..."
flag = bytes_to_long(flag)
n = p * q
e = 1384626
assert flag.bit_length() < n.bit_length() // 2
c = pow(flag, e, n)
print("c=", c)
关键在于利用LCG的多个连续项,还原LCG的a,b和seed参数从而得到p.
- 同时,题目给出了提示
flag.bit_length() < n.bit_length() // 2
,这意味者我们可以只用p解RSA当发现e和phi不互质时,可以先把看成一个关于X=[] c = 51846448616255629242918159354807752786692784645460532308823434086479848425723111371477823327980874708898952566998637230358105087254392989515438172155717708590176244736140994735777168368143405720703501031813936741444894000217727880068767785957507824708838189619286341612305393812568642372035793481458142583420 diffs = T = [x1 - x0 for x0, x1 in zip(X[:-1], X[1:])] zeros = [t2 * t0 - t1 * t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])] N = abs(GCD(*zeros)) print(N, N.bit_length()) n = 2 A = (T[n] * pow(T[n - 1], -1, N)) % N B = (X[2] - X[1] * A) % N print(A.bit_length(), B.bit_length()) p = (X[0] - B) * inverse(A, N) % N pphi = p - 1 e = 1384626 c = c % p print(GCD(e // 6, pphi)) d = inverse(e // 6, pphi) m = pow(c, d, p) print(f"{p=}") print(f"{m=}")
m**6
的RSA,此时e//6和p-1互质
最后利用模下开方得到m:#sage from Crypto.Util.number import * p=11406146503880823399297963629845559494461007497449751823413253129053497454943898251983630826960583790125622063400069720120655389759003474122064810099132057 c=4627418782344342494068293700017423506328601823965912183563375693738732971994620371812616987659520185367722780188594554060692021622964832980886716122288540 R.<x> = Zmod(p)[] f=x^6-c res=f.roots() print([long_to_bytes(int(i[0])) for i in f.roots()])
mitm
顾名思义为中间相遇攻击,题目使用4个随机的密钥进行了4次AES加密
flag = b""
note = b"Crypt_AES*42$@" * 2
r = 4
keys = []
def enc():
for i in range(r):
key = bytes(choices(note, k=3))
print(key)
print(sha256(key).digest())
keys.append(sha256(key).digest())
print(keys)
leak = b"Hello_BuildCTF!!"
cipher = leak
for i in range(r):
cipher = AES.new(keys[i], AES.MODE_ECB).encrypt(cipher)
enc_key = sha256(b"".join(keys)).digest()
enc_flag = AES.new(enc_key, AES.MODE_ECB).encrypt(pad(flag, AES.block_size))
print(f"cipher = {cipher}")
print(f"enc_flag = {enc_flag}")
- 爆破四个密钥进行四层加密/解密,复杂度为
(A3/14) ^ 4
无法穷举 - 进行中间相遇攻击,使用空间换时间,分别进行两层爆破解密和两层爆破加密,对中间的密文结果取交集
from Crypto.Util.number import *
from Crypto.Util.Padding import *
from hashlib import sha256
from Crypto.Cipher import AES
from random import *
from itertools import permutations
from tqdm import tqdm
note = b"Crypt_AES*42$@"
r = 4
A, B = set(), set()
leak = b"Hello_BuildCTF!!"
dic = set()
for i in permutations(note * 3, 3):
dic.add(bytes(i))
for a in tqdm(dic):
a = sha256(bytes(a)).digest()
c1 = AES.new(a, AES.MODE_ECB).encrypt(leak)
for b in dic:
b = sha256(bytes(b)).digest()
c2 = AES.new(b, AES.MODE_ECB).encrypt(c1)
A.add(c2)
cipher = b"\xb9q\x04\xa3<\xf0\x11-\xe9\xfbo:\x9aQn\x81"
for d in tqdm(dic):
d = sha256(bytes(d)).digest()
c3 = AES.new(d, AES.MODE_ECB).decrypt(cipher)
for c in dic:
c = sha256(bytes(c)).digest()
c2 = AES.new(c, AES.MODE_ECB).decrypt(c3)
B.add(c2)
print(A & B)
发现加密过程的中间密文为b"\xd0\xe8]\x1dIQ\x93S\x7f\xe1\xda\x90\xe5\x19\xe6\xb8"
由此可以解得flag
ominous
from Crypto.Util.number import *
from secret import flag
import random
import string
Ominous_dic = ['啊', '米', '诺', '斯']
flag_word = (string.ascii_letters + string.digits + '{}@_!').encode()
assert all(char in flag_word for char in flag)
msg = bytes_to_long(flag)
random.shuffle(Ominous_dic)
def Ominous_enc(msg):
res = 0
for idx, word in enumerate(Ominous_dic):
res += random.randint(0, 200) * ord(word) * (2 ** (50 * (idx + 1)))
return res + msg
cipher = Ominous_enc(msg)
print(f'cipher = {cipher}')
# cipher = 11174132013050242293373893046306047184706656363469879247040688497021
200以内的随机数乘word并不能超过2**50
,这意味着可以每50个比特去穷举而不用连续穷举4层深度:
from Crypto.Util.number import *
import string
cipher = 11174132013050242293373893046306047184706656363469879247040688497021
print(cipher.bit_length())
dic = ["诺", "斯"]
for idx, word in enumerate(dic):
print(idx, word, ord(word))
assert 200 * ord(word) < 2**44
flag_word = string.ascii_letters + string.digits + "{}@_!"
c1 = cipher % (2**48)
print(long_to_bytes(c1).decode())
c2 = cipher % (2**96)
for i in dic:
for j in range(201):
x = j * ord(i) * 2**50
c11 = (c2 - x) >> 48
try:
m = long_to_bytes(c11).decode()
assert all(char in flag_word for char in m) and "}" not in m
print(m, len(m), i)
# break
except:
pass
print()
c3 = cipher % (2**144)
for i in dic:
for j in range(201):
x = j * ord(i) * 2**100
c11 = (c3 - x) >> 96
try:
m = long_to_bytes(c11).decode()
assert all(char in flag_word for char in m) and "}" not in m
print(m, len(m), i)
# break
except:
pass
print()
# BuildCTF{Wh@t_i5_0m1nous!!!}
where is my n
flag = "..."
e = 65537
p = getPrime(512)
q = gmpy2.next_prime(p)
n = p * q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
c = pow(flag, e, n)
print("c=", c)
print("e=", e)
print("d=", d)
给出了e和d,要求还原n,需要先还原phi,可以先简单计算一下,phi在1023~1024个比特位
e*d=k*phi+1
在1039个bit,所以可以穷举得到phi,然后分解p,q:
from Crypto.Util.number import *
from gmpy2 import *
from sympy import nextprime, prevprime
c = 107973408658512316248795675829719026556281556876279221462095299771897472835817102507431099132436173117611783572607408542140665445616624626408781699266046553444252772105867617770124779786841928535661872891635303381758336724610931502145965143374870804147444436791292512235485326451051756451904673491759905663466
e = 65537
d = 62036379179617188220635702722848631787124203142048526951004487465970915306760341332025319712290841316288636152355908585406155087541334717529113872233640624205650204907669681116401961584897042519881342485819364897891612540596760113597723865477121348794797592568686540283535491492936074500143092361821406613969
kphi = e * d - 1
print(kphi.bit_length())
for i in range(2**14, 2**16):
# print((kphi // i).bit_length())
if kphi % i == 0:
phi = kphi // i
temp = isqrt(phi)
p = nextprime(temp)
q = prevprime(temp)
n = p * q
if d == inverse(e, (p - 1) * (q - 1)):
m = pow(c, d, p * q)
print(long_to_bytes(m))
break
我这辈子就是被古典给害了
先进行类似凯撒的换表加密,然后是AES加密,编写相应解密函数即可,由于flag头已知,密钥的长度为8,就能直接得到key
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES
def minus(a, b):
assert len(a) == len(b)
res = ""
for i in range(len(a)):
x = (dict1[a[i]] - dict1[b[i]]) % 26
res += dict2[x]
return res
def AES_dec(key, value):
key = (key * 2).encode()
cipher = AES.new(key, AES.MODE_ECB)
decrypted_text = cipher.decrypt(value)
decrypted_text = unpad(decrypted_text, AES.block_size)
print("AES Decrypted Text =", decrypted_text.decode())
Text = "HLMPWKGLYSWFACEWBYRSUKYFAXZFXDKOTZHHSLFCXNICAHPGRIFUF"
AESText = b'\x92T{\x1f\x0f"\xbd\xbb\xfa|O\x11\x83\xa0\xec.\x15]\x9f\x9a\xe5\x85Z\x9f@yUm\xbb\xdc\x93\x08\xe5\x8b\xd5\x98\x84\xfa\x91\xe8\xde\x1b}\xcd\x9056\xa3\xbf\xdb\x85J\xcc\xec\x812T\x11\xa7Tl\x15\xf6"'
print(len(Text))
a = "BUILDCTF"
b = "HLMPWKGL"
key_ = minus(b, a)
print(key_, len(key_))
print(AES_dec(key_, AESText))