文章目錄
  1. 1. 测试环境
  2. 2. ida反汇编
  3. 3. gdb调试
  4. 4. 参考资料

https://github.com/bkerler/exploit_me 是github上一个学习ARM架构下二进制漏洞的项目。目前包括13个不同类型的漏洞,包括

Level 1: Integer overflow

Level 2: Stack overflow

Level 3: Array overflow

Level 4: Off by one

Level 5: Stack cookie

Level 6: Format string

Level 7: Heap overflow

Level 8: Structure redirection / Type confusion

Level 9: Zero pointers

Level 10: Command injection

Level 11: Path Traversal

Level 12: Basic ROP

Level 13: Use-after-free

本系列文章会一一介绍每个漏洞的详细原理和常见的利用方式和联系真实世界的物联网设备漏洞做分析。

测试环境

exploit_me提供了一个vagrant环境用作测试,不过下载vagrant速度非常慢,建议自己编译源码,在自己的arm环境中调试。也可以直接下载我编译好的可执行文件。https://github.com/stayliv3/blog_material/blob/master/exploit4

ida反汇编

第一关的密码试hello。先尝试运行。

armint1.png

降低难度,可以先看一下源码:

/ LEVEL 1 /

int int_overflow(char* value)

{

unsigned short number;

int i = atoi(value);

    unsigned int h=i;

if (h<=0)

{

    printf("Value less or equal 0 is not allowed.\n");

    exit(0);

}

number=i;

if (number!=0)

{

    printf("Value %d defined.\n",number);

    exit(0);

}

if (i<0 || number==0) // Two ways of overflow possible ... int AND short :)

{

    printf("Level 2 Password: \"%s\"\n",passwds[1]);

}

return 0;

}

unsigned short number;

int i = atoi(value);

    unsigned int h=i;

if (h<=0)

{

    printf("Value less or equal 0 is not allowed.\n");

    exit(0);

} 

将输入字符串使用atoi转换成int类型,然后转换成无符号int,如果结果小于0则退出,这里显然输入一个正数就可以不退出。

1
if (number!=0)

    {

        printf("Value %d defined.\n",number);

        exit(0);

    }

    if (i<0 || number==0) // Two ways of overflow possible ... int AND short :)

    {

        printf("Level 2 Password: \"%s\"\n",passwds[1]);

    }

number是short型,将int赋值给short会造成整数的截断问题。然后判断number是否等于0,如果不等于0则退出,如果小于0或者等于0 则打印出下一关的password。显然,我们的输入需要满足这个条件,进入这个分支逻辑。

根据程序输入,在ida中定位相关的函数:armint2.png

gdb调试

设置断点和程序运行的参数:

gef> b int_overflowBreakpoint 1 at 0x11164

gef> set args hello 2222

第一个可能出现问题的语句是

int i = atoi(value);

unsigned int h=i;

有符号数转换成无符号数,由于有符号数和无符号数的表示范围不一致,在转换过程中容易出现问题。查看对应的汇编代码:当输入 -1时:

armint3.png

可以看到对应的汇编代码为:

str r0,[r11,#-8]

ldr r3,[r11,#-8]

str r3,[r11,#-12]

ldr r3,[r11,#-12]

进行了2次内存的读写,但是依然是r0的值没有变化,其实在汇编语言层面是没有有符号数和无符号数的区别的。是否有符号是更上一层的语言的解释效果。可以参考附录中关于符号数,补码的原理。对底层来说只有二进制加法而已。所以有符号数和无符号数的转换,本身变量的二进制是不变的,变的只是上一层语言对其的解释。

第二个容易出现问题的点: number=i;将整型赋值给short int。会造成截断。

armint4.png

对应的汇编代码为:

1
0x111a4 <int_overflow(char*)+64> ldr    r3,  [r11,  #-8]
0x111a8 <int_overflow(char*)+68> strh   r3,  [r11,  #-14]
0x111ac <int_overflow(char*)+72> ldrh   r3,  [r11,  #-14]

strh 加了h标记表示half word。取低位的2个字节。

所以保证低位2个字节是全零就可以绕过这个判断。

比如0x10000,10进制是65536,利用整数的阶段问题。也可以使用负数-2147483648,利用了有符号数转无符号数的问题。

armint6.png
armint6.png

参考资料

http://phrack.org/issues/60/10.html#article

http://roo0.me/2017/11/06/%E6%95%B4%E6%95%B0%E6%BA%A2%E5%87%BA/

https://www.zhihu.com/question/20159860

文章目錄
  1. 1. 测试环境
  2. 2. ida反汇编
  3. 3. gdb调试
  4. 4. 参考资料