正式比赛一道都没写出来,看了第一名大佬发的wp,赶紧来复现一下,学习并记录。
A1natas 2024 古剑山 WriteUp 这里是大佬的wp链接
一、jpg
附件是个.jpg文件,
binwalk分离出来一个压缩包,打开是这三个文件,不过需要密码
破解压缩包就那么几个思路
压缩包思路
1.通过其他文件找到密码
2.伪加密
3.已知明文爆破 //文件大字节
4.强爆破
5.crc32爆破 //文件小字节
这里看的出来是伪加密,010改完之后发现只有flag.pdf可以打开,其他仍然需要密码
那么这密码显然就是要在这个flag.pdf里找了。
我当时就卡在这里,看师傅的wp发现可以去strings一下可打印字符,这里是个好方法
(虽然就是把010里的字符串打印了一下,不过更加直观了)
这里文件尾发现了photoshop的字样,有可能需要用ps打开(这是人能想出来的?)
这里ps打开一下(我没有ps,借用一下A1natas师傅的wp)
扫描二维码得到
67f480eff11781617044bd47fb9535cfb0e4b6a09e51daff2107c536e9d4eebb3d517cfea6e3f176d4e0d37a9f3658845f3d1917cfce30a4f44ffa2e0af58485
是sha512的值,结合之前的文件sha512.txt文件,推测是明文攻击。
已知明文攻击
简单来说,就是你有一个压缩包需要解密,但同时你也有这个压缩包里面的一个文件。然后因为同一个压缩包的密钥都是一样的,只要确保他们的crc值相同(压缩方式),就可以把密码攻击出来。
然后这里之所以不能使用flag.pdf作为已知的明文,是因为flag.pdf是伪加密,并没有通过压缩包真正的加密。
我们新建一个sha512.txt,然后把他压缩,不用加密。
这里可以看到循环冗余检测码是一样的(在查看那里可以打开查看循环冗余检测码)
额,不知道是不是版本问题,我爆破一晚上都没爆出来,师傅两分钟就爆出来了。
换了版本之后是这样的,但是最后还是有惊无险,能打开了
这里估计是我伪加密更改的时候出错了。有没有师傅可以帮我看看QAQ
二、蓝书包
附件是一大堆压缩包(又是)
爆破一下试试
试了试别的,发现密码和名字有关,写个脚本批量爆了
(我不会写,借鉴一下大佬的)
(我运行居然也出错,要好好学一下了)
然后第一个文件发现了png的头,那么把每个文件的字符连在一起,就能获得png图片啦
又要写脚本,再借鉴一下大佬的(之前的都运行不了,这里我就带过一下)
之后就能得到一张图片了,然后图片隐写大致有这些思路
图片思路
1.文件后缀隐藏文字或文件
2.详细信息
3.lsb
4.盲水印
这里因为题目名为蓝书包,大概与lsb有关,师傅说StegSolver不能直接打开!!又要用脚本爆破(如果是师傅自己写的,那我估计不用写这道题目了QAQ)
import threading
from queue import Queue
from Crypto.Cipher import AES
from lsb import assemble
from PIL import Image
import hashlib
from Crypto import Random
from Crypto.Util.number import long_to_bytes
class AESCipher:
def __init__(self, key):
self.bs = 32 # Block size
self.key = hashlib.sha256(key.encode()).digest() # 32 bit digest
def encrypt(self, raw):
raw = self.pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(raw)
def decrypt(self, enc):
# if len(enc) % 16 != 0:
# enc = enc[:len(enc) - len(enc)%16]
# print(len(enc))
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
message = cipher.decrypt(enc[AES.block_size:])
return message
# return self.unpad()
def pad(self, s):
return s + (self.bs - len(s) % self.bs) * long_to_bytes(self.bs - len(s) % self.bs)
def unpad(self, s):
return s[:-ord(s[len(s)-1:])]
def aes_brute_force(key, ciphertext):
try:
# print("testing", key)
cipher = AESCipher(key)
data_dec = cipher.decrypt(ciphertext).decode()
if "flag" in data_dec:
print(f"正确密钥: {key}, 明文: {data_dec}")
return True
except Exception:
return False
def worker(ciphertext, queue):
while not queue.empty():
key = queue.get()
if aes_brute_force(key, ciphertext):
with queue.mutex:
queue.queue.clear() # 停止其他线程
break
queue.task_done()
def main():
# read data
img = Image.open("576X1024_6_4_8.png")
(width, height) = img.size
conv = img.convert("RGBA").getdata()
print("[+] Image size: %dx%d pixels." % (width, height))
# Extract LSBs
v = []
for h in range(height):
for w in range(width):
(r, g, b, a) = conv.getpixel((w, h))
v.append(r & 1)
v.append(g & 1)
v.append(b & 1)
data_out = assemble(v)
dic = "rockyou.txt"
keys = open(dic, "r", encoding="utf-8").read().split("\n")
ciphertext = data_out
queue = Queue()
for key in keys:
queue.put(key)
num_threads = 8
threads = []
for _ in range(num_threads):
thread = threading.Thread(target=worker, args=(ciphertext, queue))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
if __name__ == "__main__":
main()
运行之后得到flag
三、小结
根据师傅的wp过了一遍,主要其实是想记录下来,不过就算只是过一遍也没有完全成功。看来网安之路任重而道远。