6.1.23 pwn BCTF2016 bcloud

下载文件

题目复现

$ file bcloud
bcloud: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=96a3843007b1e982e7fa82fbd2e1f2cc598ee04e, stripped
$ checksec -f bcloud
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   Yes     0               4       bcloud
$ strings libc-2.19.so | grep "GNU C"
GNU C Library (Ubuntu EGLIBC 2.19-0ubuntu6.7) stable release version 2.19, by Roland McGrath et al.
Compiled by GNU CC version 4.8.2.

32 位程序,开启了 Canary 和 NX,默认开启 ASLR。

在 Ubuntu-14.04 上玩一下:

$ ./bcloud
Input your name:
AAAA
Hey AAAA! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!
Now let's set synchronization options.
Org:
1234
Host:
4321
OKay! Enjoy:)
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
1
Input the length of the note content:
10
Input the content:
BBBB
Create success, the id is 0
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
2
WTF? Something strange happened.
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
3
Input the id:
0
Input the new content:
CCCC
Edit success.
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
4
Input the id:
0
Delete success.
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
5
Syncing...
Synchronization success.

典型的堆利用程序,实现了添加、修改、删除的功能,显示功能未实现。另外还有个同步,不知道做什么用。在打印菜单之前,程序读入 name 并打印了出来,这个很有意思,可能是为了信息泄漏故意设置的。

题目解析

init

在 main 函数打印菜单之前,有一个初始化函数 fcn.0804899c,这个函数又依次调用了函数 sub.memset_7a1 和函数 sub.memset_84e

所以该函数所读入的字符串是先放在栈上,然后复制到堆。最后调用一个函数打印出了堆上的字符串。

来看一下读入字符串的函数 sub.read_68d

乍看之下似乎没有问题,在读入字符串末尾也加上了截断 \x00

但是,注意观察读入字符串和 malloc 返回地址在栈上的位置关系。字符串其实地址 local_5ch,最多 0x40 个字节,返回地址位于 local_5ch + 0x40,所以如果我们正好读入 0x40 字节,则 \x00 会被放到 local_5ch + 0x41 的位置,然后正好被返回地址给覆盖掉了。由于函数 strcpy() 是以 \x00 来决定字符串结尾的,所以字符串连上返回地址会被一起复制到堆上。然后又被一起打印出来。于是我们就得到了堆地址。

继续看函数 sub.memset_84e

同样的,Host 的返回地址放在 local_9ch + 0x88 的位置,而字符串最多到 local_9ch + 0x44 + 0x40,中间还间隔了 0x4 字节,所以不存在漏洞。但是 Org 的返回地址放在 local_9ch + 0x40,正好位于字符串的后面,所以存在漏洞。同时 Host 的字符串又正好位于 Org 返回地址的后面,所以 strcpy 会将 Org 字符串,返回地址和 Host 字符串全都复制到 Org 的堆上,造成堆溢出。利用这个堆溢出我们可以修改 top chunk 的 size,即 house-of-force。

当然这种漏洞有一定的几率不会成功,比如返回地址的低位本来就是 \x00 的时候,就恰好截断了。

New note

我们可以得到下面的数据结构:

三个数组都是通过指标 i 来对应的,分别存放 note 地址,length 及是否同步。

Edit note

该函数在修改 note 时,先将 syns[i] 清空,然后读入 lengths[i] 长度的内容到 notes[i]。

Delete note

该函数首先判断 notes[i] 是否存在,如果存在则释放 notes[i] 并将 notes[i] 和 lengths[i] 都置 0。不存在悬指针等漏洞。

至于 Syn 功能,就是将 syns[i] 都置 1,对漏洞利用没有影响。

漏洞利用

所以这题的利用思路就是 house-of-force,步骤如下:

  • 泄漏 heap 地址

  • 利用溢出修改 top chunk 的 size

  • 分配一个 chunk,将 top chunk 转移到 lengths 数组前面

  • 再次分配 chunk,即可覆盖 notes,并利用 Edit 修改其内容

  • 修改 free@got.pltputs@got.plt,泄漏 libc

  • 修改 atoi@got.pltsystem@got.plt,得到 shell

leak heap

可以看到对指针被复制到了堆中,只要将其打印出来即可。

house-of-force

接下来是 house-of-force,通过溢出修改 top chunk 的 size,可以在下次 malloc 时将 top chunk 转移到任意地址,之后的 chunk 也将依据转移后的 top chunk 来分配。

溢出:

转移 top chunk:

再次 malloc,将其后的 .bss 段变为可写,然后放上 GOT 表指针:

leak libc

接下来就可以利用 Edit 功能修改 GOT 表,泄漏 libc 地址了。

pwn

开启 ASLR,Bingo!!!

exploit

完整的 exp 如下:

参考资料

Last updated

Was this helpful?