首页
社区
课程
招聘
[原创]Windbg和IDA脚本辅助分析
2016-4-23 23:00 7022

[原创]Windbg和IDA脚本辅助分析

2016-4-23 23:00
7022
本文尝试解决另个问题:内核漏洞快速定位漏洞触发点 && Windbg执行流在IDA中高亮标示
内核漏洞快速定位漏洞触发点:
拿到一个内核漏洞的POC,首先我们需要定位触发漏洞,提权的地方。直接看反汇编代码往往不容易看出来

内核漏洞常见的目标是提权,一般会替换本进程的token为System的token,这种办法简单粗暴,成功率极高,所以往往为各黑客所爱。

既然要提权token,那么必须要对token所在的地址有写入操作,本文的思路就是对token所在地址下写入断点,一但触发,便是来到替换token,也就是提权的地方,再栈回溯,就能找到触发漏洞的函数了。下面我们来演示一下,我写了个windbg脚本:
$$注意一:凡是寄存器操作需加上r
$$注意二:打印字符串用%ma
$$此脚本用到了一个硬编码token偏移 0xf8,请根据你的实际系统修正它。

r @$t0=@$proc;
r @$t1=@$proc;

.while(1)
{
  
  .if ((poi(@$t1+0xb8)-0xb8) != @$t0 ) 
  {
    
    $$比较是否为test进程
    .if ( poi(@$t1+0x16c)==0x74736574 )   
    {
      .printf "进程名:%ma\t",@$t1+0x16c;
      .printf "进程EPROCESS:%x\t",@$t1;
      .printf "进程TOKEN:%x\n",poi(@$t1+0xf8);
      .printf "请输入:\n";
      .printf "ba w1 %x+f8 \".if (wo(%x+f8+2)!=0x%x){} .else{gc;}\"",@$t1,@$t1,wo(@$t1+0xf8+2);
    }
    r @$t1=poi(@$t1+0xb8)-0xb8;
  }
  .else
  {
    .break;
  }
}

代码便不会解释,如有不明,烦请百度。我用CVE-2014-4113这个漏洞演示,把win32.exe复制到虚拟机改名为test.exe。然后

回到物理机,把前面的代码保存为js.txt,然后

执行提示的那条命令,结果如下

然后物理机g跑起来,虚拟机也g跑起来,稍等一会,断在这里,ring3地址

再看看此是test.exe进程token

这个token恰好是System的token,请看

找到了提权的地方后,就可以对test.exe反汇编代码进行进一步分析了。

Windbg执行流在IDA中高亮标示:
前面得到了提权的地方,但是往往漏洞触发条件很复杂,需要经过很多函数,我们需要逐步跟踪这个函数,理解漏洞的成功,而有些函数及其庞大,分支又多,我们需要确定走哪些分析导致漏洞触发。前面得到了关键点,打印此时的堆栈
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
99a80a24 96d094f3 fffffffb 000001ed 0192fac4 test+0x186d
99a80a64 96d095c5 fffffffb 000001ed 0192fac4 win32k!xxxSendMessageTimeout+0x1ac
99a80a8c 96d892fb fffffffb 000001ed 0192fac4 win32k!xxxSendMessage+0x28
99a80aec 96d88c1f 99a80b0c 00000000 0192fac4 win32k!xxxHandleMenuMessages+0x582
99a80b38 96d8f8f1 ffb89bb0 96e6f580 00000000 win32k!xxxMNLoop+0x2c6
99a80ba0 96d8f9dc 0000001c 00000000 ffffd8f0 win32k!xxxTrackPopupMenuEx+0x5cd
99a80c14 83e501ea 0009017d 00000000 ffffd8f0 win32k!NtUserTrackPopupMenuEx+0xc3
99a80c14 772570b4 0009017d 00000000 ffffd8f0 nt!KiFastCallEntry+0x12a
0192fad8 76b6483e 76b52243 0009017d 00000000 ntdll!KiFastSystemCallRet
0192fadc 76b52243 0009017d 00000000 ffffd8f0 USER32!NtUserTrackPopupMenuEx+0xc
0192fafc 013c1604 0009017d 00000000 ffffd8f0 USER32!TrackPopupMenu+0x1b
0192fb8c 75d93c45 00000000 0192fbd8 772737f5 test+0x1604
0192fb98 772737f5 00000000 76bae6a7 00000000 kernel32!BaseThreadInitThunk+0xe
0192fbd8 772737c8 013c1526 00000000 00000000 ntdll!__RtlUserThreadStart+0x70
0192fbf0 00000000 013c1526 00000000 00000000 ntdll!_RtlUserThreadStart+0x1b

PS:这是CVE-2015-1701,也是用前面的windbg脚本定位到关键点,上面是断下来后的堆栈。win32k!xxxSendMessageTimeout是最直接调用了提权函数,我们首先想到这这个函数下断,然后分析。但是win32k!xxxSendMessageTimeout频繁被系统调用,所以不好确定什么时候调用了提权函数。我选择在USER32!NtUserTrackPopupMenuEx下断。来到win32k!xxxSendMessageTimeout后
.logopen "C:\Users\Netfairy\Desktop\test.txt"

把指令流记录下来,然后一直按p,其实按一次p,接着按住回车键就可以。一会之后,windbg记录的指令流如下:

接着使用这个IDA脚本:
//
//IDC脚本,用于配合windbg动态调试得到的指令流,自动化用高亮绿色在IDA中标示出来,方便静态分析
//By www.netfairy.net
//

#include <idc.idc>

extern ida_image_base_addr,windbg_image_base_addr,offset;  //全局变量

//主函数
static main(void)
{
  auto i,temp;
  auto FileName,FileLength,hFile,str,Target="66666666";

  //
  //获取输入Windbg和IDA中模块的基址
  //
  temp=AskStr("96c50000","Please input Windbg's Win32k.sys imageBase,For example:");
  windbg_image_base_addr=xtol(temp); 
  temp=AskStr("bf800000","Please input IDA's Win32k.sys imageBase,For example:"); 
  ida_image_base_addr=xtol(temp); 

  FileName=AskFile(0,"*.*","Please choose Windbg Instruction stream file");  //选择文件
  hFile=fopen(FileName,"r+");  //读写方式打开文件
  FileLength=filelength(hFile);  //读取文件长度

  //
  //跳到指令流开头
  //
  for(i=0;i<FileLength;i++)
  {
  //往后读取八个字符
  Target[0]=fgetc(hFile);
  Target[1]=fgetc(hFile);
  Target[2]=fgetc(hFile);
  Target[3]=fgetc(hFile);
  Target[4]=fgetc(hFile);
  Target[5]=fgetc(hFile);
  Target[6]=fgetc(hFile);
  Target[7]=fgetc(hFile);

  temp=xtol(Target); // 转换字符为十六进制地址
  
  if( temp > windbg_image_base_addr && temp < 0xffffffff )  //判断地址范围
  {
     temp=temp-windbg_image_base_addr+ida_image_base_addr;  //转换为IDA地址
     str=fgetc(hFile);  //因为Windbg得到的指令流有(地址)情况,需要排除它
     fseek(hFile,-1,1);
     if(str=='\x29')  //0x29是 ')'的十六进制
     {

     }
     else
     {
       Jump(temp);  //首次跳转到指令流开头
       fseek(hFile,0,0);  //修正文件指针到文件开头
       break;
       
     }
  }
  else
  {
    fseek(hFile,-7,1);
  }
  
  }
  
 

  //
  //功能代码
  //
  for(i=0;i<FileLength;i++)
  {
  //往后读取八个字符
  Target[0]=fgetc(hFile);
  Target[1]=fgetc(hFile);
  Target[2]=fgetc(hFile);
  Target[3]=fgetc(hFile);
  Target[4]=fgetc(hFile);
  Target[5]=fgetc(hFile);
  Target[6]=fgetc(hFile);
  Target[7]=fgetc(hFile);

  temp=xtol(Target); // 转换字符为十六进制地址
  
  if( temp > windbg_image_base_addr && temp < 0xffffffff )  //判断地址范围
  {
     temp=temp-windbg_image_base_addr+ida_image_base_addr;  //转换为IDA地址
     str=fgetc(hFile);  //因为Windbg得到的指令流有(地址)情况,需要排除它
     fseek(hFile,-1,1);
     if(str=='\x29')  //0x29是 ')'的十六进制
     {

     }
     else
     {
       SetColor(temp,1,0x98fb98);  //染高亮绿色
       
     }
  }
  else
  {
    fseek(hFile,-7,1);
  }
  
  }

}

根据提示输入相应参数,一会之后


图一

图二
下面就可以进行进一步的分析了,还是看IDA舒服!!!

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (5)
雪    币: 606
活跃值: (608)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
Morgion 1 2016-4-24 03:25
2
0
这个跟我分析缓冲区溢出的时候的思路一样。。。
雪    币: 3842
活跃值: (1830)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
音货得福 1 2016-4-24 11:36
3
0
学习了,我还有好多路要走啊.T-T
雪    币: 94
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
川美 2016-4-24 12:46
4
0
学习了,感觉好多问题需要解决
雪    币: 82
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
星际 2016-4-25 08:20
5
0
超赞。顶起。
雪    币: 51
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
M二锅头 2016-6-25 08:33
6
0
学习了,非常有用!
游客
登录 | 注册 方可回帖
返回