使用concolic解决crash时的输入约束问题

使用concolic解决crash时的输入约束问题
    当产生了crash,能够找到导致crash的数据与输入数据的关系,对进行漏洞利用有着重要的意义。通过污点追踪的方式,例如temu,已经可以定位到导致crash的数据是由哪些输入所导致的。但简单的污点追踪无法给出导致crash数据与输入数据的具体约束关系。通过具体的约束关系,可以找出导致crash的数据的输入数据范围,从而可以使用多个输入来触发漏洞。(还可以用来干嘛?)
    解决该问题主要有两种思路:
1、    使用trace之后的指令和数据进行离线符号执行;这种方式在vine中应该有初步使用,或者FuzzBall。可以进一步研究。
2、    使用符号执行中的concolic模式,将导致crash的输入作为初始的输入,这时符号执行引擎会按照输入的样本数据来优先执行路径,从而可以到达crash的地方。同时,符号执行引擎仍然开启了约束收集的功能,因此,在执行到crash的时候,路径的约束条件,以及相关变量的约束条件也会被记录。


对于第二种思路,使用S2E符号执行引擎就可以完全实现,几乎不需要改动代码。

我们实现了两个例子来验证,包括一个简单的示例程序来验证和一个真实漏洞。

例子一:代码如下:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include "s2e.h"
  4
  5 int main(void)
  6 {
  7   char str[3];
  8    
  9    
 10   
 11   str[0] = 'a';
 12   str[1] = 'a';
 13
 14   s2e_disable_forking();
 15  
 16   s2e_make_concolic(str, 2, "str");
 17
 18   if(str[0] == '\n' || str[1] == '\n') {
 19     printf("Not enough characters\n");
 20   } else {
 21     if(str[0] >= 'a' && str[0] <= 'z')
 22       printf("First char is lowercase\n");
 23     else
 24       printf("First char is not lowercase\n");
 25
 26     if(str[0] >= '0' && str[0] <= '9')
 27       printf("First char is a digit\n");
 28     else
 29       printf("First char is not a digit\n");
 30
 31     if(str[0] == str[1])
 32       printf("assume a bug here\n");
 33     else
 34       printf("First and second chars are not the same\n");
 35   }
 36
 37  
 38
 39   s2e_get_example(str, 2);
 40   printf("'%c%c' %02x %02x\n", str[0], str[1],
 41          (unsigned char) str[0], (unsigned char) str[1]);
 42
 43   s2e_kill_state(0, "kill state ");
 44   return 0;
 45 }
 46


例子中,我们给出了输入一个初始数据,即str[0]=’a’,str[1]=’a’,通过该输入,可以到达32行,并触发漏洞。(这里的漏洞是已知的,在此假设)。那么我们要得到的是到达此处时的与输入的关系问题。这个例子中只体现了路径约束问题,而没有涉及变量的约束问题。即只有可达问题,不涉及受控问题。
经过测试,我们得到到达该处的路径约束关系如下:
Constraint: (Eq false
    (Eq (w32 0x0)
        (And w32 (Add w32 (w32 0xfffffff6)
                          (ZExt w32 (Read w8 0x0 v0_str_0)))
                 (w32 0xff))))
Constraint: (Eq false
    (Eq (w32 0x0)
        (And w32 (Add w32 (w32 0xfffffff6)
                          (ZExt w32 (Read w8 0x1 v0_str_0)))
                 (w32 0xff))))
Constraint: (Eq false
    (Slt (Read w8 0x0 v0_str_0) (w8 0x61)))
Constraint: (Eq false
    (Slt (w8 0x7a) (Read w8 0x0 v0_str_0)))
Constraint: (Eq false
    (Slt (Read w8 0x0 v0_str_0) (w8 0x30)))
Constraint: (Slt (w8 0x39) (Read w8 0x0 v0_str_0))
Constraint: (Eq (w32 0x0)
    (And w32 (Sub w32 (ZExt w32 (Read w8 0x0 v0_str_0))
                      (ZExt w32 (Read w8 0x1 v0_str_0)))
             (w32 0xff)))

通过整理,可以得到关系如下:
    str0 != 0x10 &&
str1 != 0x10 &&
str0 >= 0x61 &&
str0 <= 0x7a &&
str0 >= 0x30 &&
str0 > 0x39 &&
str0 == str1
即只要满足上述约束关系,就可以达到该路径。通过验证可以满足。

其实使用符号执行就可以完成上述工作。为什么需要concolic模式?主要原因是对于上述的小例子,符号执行时可以完成。但对于大的程序,符号执行所遍历的路径太多,难以()使得符号执行到达crash的地方。为使得符号执行能够达到crash的地方,需要有较好的路径选择算法,能使其达到程序深处。而concolic模式则是一种以原始输入为指导的简单的路径选择算法。依据该方式,符号执行引擎可以较快的到达crash的地方。
一个以bind平台为漏洞的例子。Bind是一个开53端口的dns服务器软件。目前可以通过一个数据包将bind所运行的named进程crash掉。
这里crash的原理很简单,畸形的输入使得程序中的lib/dns/db.c的REQUIRE(type != dns_rdatatype_any);出现错误。这里的REQUIRE中其实是一个assert,而当type !=  dns_rdatatype_any时(dns_rdatatype_any = 255), 则可以使得assert出错。从而导致程序crash。
其实这里需要知道的是此时的type与输入数据有着怎样的对应关系。以及达到该路径有哪些约束。

当前实现的仅是将路径约束输出,而没有将type输出。Type可以通过在s2e_assert中加入s2e_print_expression来输出。
Zj的s2e.h中修改的代码如下,
static inline void _s2e_assert(int b, const char *expression )
{
    S2e_enable_forking();
if (!b) {
    s2e_message(“Hit s2e assert\n”);
s2e_kill_state(0, expression);
return 0
}else {
    S2e_disable_forking();
    Return 1;
}
}
之所以加入if , else,目的是增加一个分支,来逼上绝路。但我认为写成如下代码是一样的。这里的if (!b)本来就有。
static inline void _s2e_assert(int b, const char *expression )
{
    S2e_enable_forking();
if (!b) {
s2e_kill_state(0, expression);
return 0;
}
S2e_disable_forking();
Return 1;
}


这里的关键我倒认为是对b的求解,b是一个约束表达式,而这里一个简单的if (!b),是通过求解之后的结果吗?
我认为此处并没有进行求解,而是实际值。因此,如果实际值没有导致crash则是无法检测crash的。但已足够给出其约束表达式。

进一步,如果是在此处进行求解,判断,那么则可以实现符号执行的检测功能。从而可以达到只要走到该路径,不需初始输入时crash的值,也可以检测出bug。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值