第十一届全国大学生信息安全竞赛实践技能赛预赛writeup



过了这么久终于完成了博客添加图片的更新, 其实也就加上了图片外链, 本来是打算图片放到到自己服务器上的, 不过发现外链实在是太方便了=-=.不过这个markdown展示出来没有那么好看T_T...

第十一届全国大学生信息安全竞赛实践技能赛预赛writeup

下面是小菜鸡没做几题的writeup

Crypto

flag_in_your_hand

分析网页源代码发现点击调用了getFlag函数

发现当ic值为true的时候会返回得到了flag, 接着去js文件里面看bm函数到底发生了什么, 看了很久发现很多的代码其实看起来很复杂,但是都是不用管的, 真正有用的就是那个ck函数

发现在这里ic值可以变成true, 只需要s是一个和a长度相等也就等于8的字符串, 并且s中每个字符的ASCII码比a中的相对应的数字小3, 于是可以构造出字符串security-xyf

SM

(老感觉题目名字怪怪的?)

前面东西很多的样子, 就看加密flag那附近, 发现他把加密后的flag给我们了, 还给了一堆其他的数据, 而key是有MD5计算之后得到的, 感觉是之后把choose算出来才有希望了。仔细看, 他算了一个对加密来说并没有什么用的 r 值, 还存下来给我们了, 同时存下来的还有ps数组, 看起来似乎需要用着两个数据去计算出choose值, 这样就可以得到flag了

r=0
bchoose = "0"*(512-len(bchoose))+bchoose
for i in range(512):
    if bchoose[i]=='1':
        r=r^ps[i]

看这段得到r的代码, 他由有ps数组的其中一些数据异或而来的, 而选取数据的依据就是choose值中的二进制位是否为1, 这就是r和choose的关联.

但是512位想要爆破也是不现实的, 接下里就分析gen512num的代码了, 毕竟也是给了源码, 写着没用也不太好(但其实是瞎几把相了半天之后, 觉得ps数据不能是乱给的, 不然解不出来啊, 应该在ps里面找找规律)

def gen512num():
    order=[]
    while len(order)!=512:
        tmp=randint(1,512)
        if tmp not in order:
            order.append(tmp)
    ps=[]
    for i in range(512):
        p=getPrime(512-order[i]+10)
        pre=bin(p)[2:][0:(512-order[i])]+"1"
        ps.append(int(pre+"0"*(512-len(pre)),2))
    return ps

order的话就是一个随机的长度512没用重复的数组. 下一个循环中, 每次生成的p的二进制位数也是不一样的(似乎没卵用?), 重点是pre, 居然每次截取了不一样呢的长度之后还在最后面加了一个1, 最后加到ps数组里面的数据的特征就很明显了, 512个数, 每个数的二进制位的最末尾一个1后面的0的个数都是不一样的, 并且都是唯一的. 这样就可以根据r的二进制位从低位开始找, r是由ps数组中的数据异或而来的, r的二进制的低位只和ps数组中那个低位数据相关, 也就是这个ps数组中的数是否参与了异或, 参与了 , 就记录下这个数在数组中的位置, 并且与r异或, 没有异或就继续, 这样就可以逐位的恢复出choose的值了, 下面是具体代码

#!/usr/bin/env python3

from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long
from Crypto.Cipher import AES
import hashlib, base64
from random import randint


def gen512num():
    order=[]
    while len(order)!=512:
        tmp=randint(1,512)
        if tmp not in order:
            order.append(tmp)
    ps=[]
    for i in range(512):
        p=getPrime(512-order[i]+10)
        pre=bin(p)[2:][0:(512-order[i])]+"1"
        ps.append(int(pre+"0"*(512-len(pre)),2))
    return ps

# def run():
#     choose=getPrime(512)
#     ps=gen512num()
#     print("gen over")
#     bchoose=bin(choose)[2:]
#     r=0
#     bchoose = "0"*(512-len(bchoose))+bchoose
#     for i in range(512):
#         if bchoose[i]=='1':
#             r=r^ps[i]
#     # flag=open("flag","r").read()
#     flag = 'ciscn{}'

#     key=long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(),16))
#     aes_obj = AES.new(key, AES.MODE_ECB)
#     ef=aes_obj.encrypt(flag).encode("base64")

#     open("r", "w").write(str(r))
#     open("ef","w").write(ef)
#     gg=""
#     for p in ps:
#         gg+=str(p)+"\n"
#     open("ps","w").write(gg)


# fi函数查找ps数组中每个数的二进制位最末尾1后面0的个数
def fi(n):
    for i in range(512):
        if (n & ((2<<i)-1)) != 0:
            return i

    return None


# 从文件读取ps数组
def readps():
    lp = open('ps').readlines()
    ps = list(map(lambda x:int(x.strip()), lp))
    return ps


# 从文件读入r数据
def readr():
    fr = open('r').read()
    r = int(fr.strip())
    return r


# 从文件读入ef数据
def readef():
    fef = open('ef').read().strip()
    ef = base64.b64decode(fef.encode())
    return ef


# 用于测试恢复出来的choose值是否能够得到r
def testchoose(tchoose):
    bchoose=bin(tchoose)[2:]
    rr=0
    bchoose = "0"*(512-len(bchoose))+bchoose
    for i in range(512):
        if bchoose[i]=='1':
            rr=rr^ps[i]
    return rr == readr()


ps = readps()
r = readr()
a = []
for i in ps:
    a.append(fi(i))

# res数组存放choose二进制位哪些位是1
res = []
for i in range(512):
    index = a.index(i)
    # print(bin(1<<i))
    if (r & ((2<<i)-1)) != 0:
        r = r ^ ps[index]
        # print(bin(r))
        res.append(index)

choose = 0
# for i in res:
#     choose = choose + (1<<i)
# 从二进制位恢复数据, 注意二进制位是高位还是低位, 之前搞错了差点怀疑人生
s = ''
for i in range(512):
    if i in res: s += '1'
    else: s += '0'

choose = int(s, 2)
assert testchoose(choose)

# 然后就照着加密的方法进行解密
key=long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(),16))
aes_obj = AES.new(key, AES.MODE_ECB)
ef = readef()
flag = aes_obj.decrypt(ef)
# 得到flag
print(flag)

Misc

Picture

先binwalk走一波

发现图片其实jpg的, 并不是png(当然这里已经改过来了), 并且后面有zlib数据

用binwalk的-e 参数解一下, 得到了解码后的冗余数据

二进制编辑器打开, 发现似乎有base64编码, 用python解码了一下

似乎是一个文件, 但是不清楚, 保存下来, 再用二进制编辑器打开

发现下面有几个PK, 但是最前面居然是KP, 就想着可能是个压缩包吧, 试着修改了一下数据, 发现可以解压, 但是需要密码, 看见提示写着密码从图片中找, 找了半天没发现什么隐写啊, 又试了图片中的那些字符, 还是不对. 再看压缩包不是有写着吗, password, ZeroDivisionError的报错信息, 因为我平时用的很多的是python3, 所以就直接上来就用python3去产生了一个这样的错误信息, 发现不对, 再仔细看这个明明是python2的shell, 于是密码integer division or modulo by zero

解压文件得到flag