创意电子

标题: 一个简单的小程序入门信息安全 [打印本页]

作者: hualuo    时间: 2020-11-3 02:31
标题: 一个简单的小程序入门信息安全
文章目录

本文通过一个简单的密码验证的小程序来演示在输入密码错误的情况下仍然可以通过验证的方法。其中用到了缓冲区溢出的原理,通过该文章的学习,即可成功入门信息安全。
本文需要用到一些基础的汇编知识。
首先看程序的源码
程序源码

#include #include int main(void){    char buff[15];    int pass = 0 ;               printf("n please input the passwordn");    gets(buff);    if(strcmp(buff,"hello")){        printf("npassword wrongn");    }         else{        printf("npassword rightn");        pass = 1;    }         if(pass){        printf("ncongratulationsn");    }         return 0;}该代码首先分配了15个字节的buff和4个字节的pass。
接着使用gets函数接受输入存在buff中。(
gets函数是不安全的,工作中使用一定要慎重哦!!)
接着该代码使用strcmp进行字符串比较判断输入的密码是否正确,如果正确则打印出password right,并打印出congratulations,如果错误,则打印出password wrong。

而我们的目的是在密码输入错误的情况下,让程序也能打印出congratulations。
接下来,我们就开始分析该程序。
汇编分析

通过反汇编,我们可以得到如下的代码:
gdb-peda$ pdisass mainDump of assembler code for function main():   0x000000000040067d :        push   rbp   0x000000000040067e :        mov    rbp,rsp   0x0000000000400681 :        sub    rsp,0x20   0x0000000000400685 :        mov    DWORD PTR [rbp-0x4],0x0   0x000000000040068c :        mov    edi,0x400780   0x0000000000400691 :        call   0x400550    0x0000000000400696 :        lea    rax,[rbp-0x20]   0x000000000040069a :        mov    rdi,rax   0x000000000040069d :        call   0x400570    0x00000000004006a2 :        lea    rax,[rbp-0x20]   0x00000000004006a6 :        mov    esi,0x40079c   0x00000000004006ab :        mov    rdi,rax   0x00000000004006ae :        call   0x400580    0x00000000004006b3 :        test   eax,eax   0x00000000004006b5 :        je     0x4006c3    0x00000000004006b7 :        mov    edi,0x4007a2   0x00000000004006bc :        call   0x400550    0x00000000004006c1 :        jmp    0x4006d4    0x00000000004006c3 :        mov    edi,0x4007b2   0x00000000004006c8 :        call   0x400550    0x00000000004006cd :        mov    DWORD PTR [rbp-0x4],0x1   0x00000000004006d4 :        cmp    DWORD PTR [rbp-0x4],0x0   0x00000000004006d8 :        je     0x4006e4    0x00000000004006da :        mov    edi,0x4007c2   0x00000000004006df :        call   0x400550    0x00000000004006e4 :        mov    eax,0x0   0x00000000004006e9 :        leave     0x00000000004006ea :        ret    End of assembler dump.首先看下面这行:
sub    rsp,0x20将rsp-0x20,相当于给栈区分配了32个字节的空间。0x20=32(十进制)。
我们的代码中,实际上只有15+4=19个字节的大小,但是因为内存对齐,实际上编译器分配了32个字节的空间。
char buff[15];int pass = 0 ; 接下来
mov    DWORD PTR [rbp-0x4],0x0这一行实际上就是给pass进行赋值,pass=0。
接着往下可以看
   0x0000000000400696 :        lea    rax,[rbp-0x20]   0x000000000040069a :        mov    rdi,rax   0x000000000040069d :        call   0x400570 在get函数之前,有这么一句rbp-0x20,这个实际上就是buff的地址。
看到这里我们基本就对buff和pass存储的位置有了印象。
由于程序中使用的是gets函数,而gets函数是可以进行缓冲区溢出的,那么如果我们一次性将32个字节的内存区域全部填满,那么就可以将pass赋值为别的值,那么就有可能跳过密码检查。
根据猜想,我们输入:

aaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBBBBBB如下所示:
# root @ localhost in /home/code/test [11:05:32] $ ./test please input the passwordaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBBBBBBpassword wrongcongratulations发现虽然打印出password wrong 但是还是打印出了congratulations。
程序调试

进入gdb 中,我们通过调试来验证我们的想法:
gdb-peda$ pdisass mainDump of assembler code for function main():   0x000000000040067d :        push   rbp   0x000000000040067e :        mov    rbp,rsp   0x0000000000400681 :        sub    rsp,0x20   0x0000000000400685 :        mov    DWORD PTR [rbp-0x4],0x0   0x000000000040068c :        mov    edi,0x400780   0x0000000000400691 :        call   0x400550    0x0000000000400696 :        lea    rax,[rbp-0x20]   0x000000000040069a :        mov    rdi,rax   0x000000000040069d :        call   0x400570    0x00000000004006a2 :        lea    rax,[rbp-0x20]   0x00000000004006a6 :        mov    esi,0x40079c   0x00000000004006ab :        mov    rdi,rax   0x00000000004006ae :        call   0x400580    0x00000000004006b3 :        test   eax,eax   0x00000000004006b5 :        je     0x4006c3    0x00000000004006b7 :        mov    edi,0x4007a2   0x00000000004006bc :        call   0x400550    0x00000000004006c1 :        jmp    0x4006d4    0x00000000004006c3 :        mov    edi,0x4007b2   0x00000000004006c8 :        call   0x400550    0x00000000004006cd :        mov    DWORD PTR [rbp-0x4],0x1   0x00000000004006d4 :        cmp    DWORD PTR [rbp-0x4],0x0   0x00000000004006d8 :        je     0x4006e4    0x00000000004006da :        mov    edi,0x4007c2   0x00000000004006df :        call   0x400550    0x00000000004006e4 :        mov    eax,0x0   0x00000000004006e9 :        leave     0x00000000004006ea :        ret    End of assembler dump.12345678910111213141516171819202122232425262728293031下断点,输入密码:
gdb-peda$ b *main+49Breakpoint 1 at 0x4006ae: file test.cpp, line 12.gdb-peda$ RStarting program: /home/code/test/./test  please input the passwordaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBBBBBB结果如下
[----------------------------------registers-----------------------------------]RAX: 0x7fffffffe2e0 ('a' , 'B' )RBX: 0x0 RCX: 0xfbad2288 RDX: 0x7ffff75b7a10 --> 0x0 RSI: 0x40079c --> 0x700a006f6c6c6568 ('hello')RDI: 0x7fffffffe2e0 ('a' , 'B' )RBP: 0x7fffffffe300 --> 0x42424242 ('BBBB')RSP: 0x7fffffffe2e0 ('a' , 'B' )RIP: 0x4006ae (:        call   0x400580 )R8 : 0x7ffff7ff7025 --> 0x0 R9 : 0x0 R10: 0x24 ('$')R11: 0x246 R12: 0x400590 (:        xor    ebp,ebp)R13: 0x7fffffffe3e0 --> 0x1 R14: 0x0 R15: 0x0EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)[-------------------------------------code-------------------------------------]   0x4006a2 :        lea    rax,[rbp-0x20]   0x4006a6 :        mov    esi,0x40079c   0x4006ab :        mov    rdi,rax=> 0x4006ae :        call   0x400580    0x4006b3 :        test   eax,eax   0x4006b5 :        je     0x4006c3    0x4006b7 :        mov    edi,0x4007a2   0x4006bc :        call   0x400550 Guessed arguments:arg[0]: 0x7fffffffe2e0 ('a' , 'B' )arg[1]: 0x40079c --> 0x700a006f6c6c6568 ('hello')[------------------------------------stack-------------------------------------]0000| 0x7fffffffe2e0 ('a' , 'B' )0008| 0x7fffffffe2e8 ("aaaaaaa", 'B' )0016| 0x7fffffffe2f0 ('B' )0024| 0x7fffffffe2f8 ('B' )0032| 0x7fffffffe300 --> 0x42424242 ('BBBB')0040| 0x7fffffffe308 --> 0x7ffff7210555 (:        mov    edi,eax)0048| 0x7fffffffe310 --> 0x0 0056| 0x7fffffffe318 --> 0x7fffffffe3e8 --> 0x7fffffffe68b ("/home/code/test/./test")[------------------------------------------------------------------------------]Legend: code, data, rodata, valueBreakpoint 1, 0x00000000004006ae in main () at test.cpp:1212            if(strcmp(buff,"hello")){12345678910111213141516171819202122232425262728293031323334353637383940414243444546通过gdb 打印出rbp pass和buff的地址,
gdb-peda$ p $rbp$4 = (void *) 0x7fffffffe300gdb-peda$ p &pass$5 = (int *) 0x7fffffffe2fcgdb-peda$ p &buff$6 = (char (*)[15]) 0x7fffffffe2e0rbp pass和buff 在内存地址的分配如下图所示:

                               
登录/注册后可看大图
栈区存储位置图


打印rsp地址向上32字节中的内容,我们发现已经将pass所在的位置替换成了
0x42 0x42 0x42 0x42,如下图所示:


                               
登录/注册后可看大图
栈区存储的数据


敲击c(continue),成功跳过了验证。
gdb-peda$ cContinuing.password wrongcongratulations[Inferior 1 (process 19009) exited normally]Warning: not running




欢迎光临 创意电子 (https://wxcydz.cc/) Powered by Discuz! X3.4