1 2 3 | 操作系统 Windows XP SP3
开发环境 VC + + 6.0
调试器 Ollydbg
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | int verify_password (char * password)
{
int authenticated;
char buffer [ 8 ];
authenticated = strcmp(password,PASSWORD);
strcpy( buffer ,password);
return authenticated;
}
main()
{
int valid_flag = 0 ;
char password[ 1024 ];
while ( 1 )
{
printf( "please input password: " );
scanf( "%s" ,password);
valid_flag = verify_password(password);
if (valid_flag)
{
printf( "incorrect password!nn" );
}
else
{
printf( "Congratulation! You have passed the verification!n" );
break ;
}
}
}
|
1 | 程序未对输入的密码进行长度检测,接收密码的缓冲区只有 8 ,而输入的密码最长可以输入 1024 。判断密码是否正确的变量authenticated存储在栈中,当输入的密码长度大于 8 时,输入的字符串将冲破缓冲区,淹没authenticated所处的位置。当密码错误时authenticated的值是 1 ,正确的时候authenticated的值是 0. 这就意味着我们可以构造一个合适的输入字符串来改变判断结果。
|
1 | 我们的重点不是逆向工程,而是漏洞分析,故此不再详诉诸如寻找main函数等逆向知识。
|
1.在jmp mai下断点,方便后续反复调试

2.单步步入main函数分析程序

3.分析main函数逻辑,可以看出主要问题出在密码比对函数00401005
3.1输出引导字符串后,要求用户输入密码。

3.2通过00401005处的函数进行密码比对

3.3判断比对结果是不是0,如果是0则输出成功字符串,如果是1则输出失败字符串。

4.单步执行程序,随便输入一个密码,然后单步步入00401005函数,分析这个函数的内容。

4.1可以看出这个00401005是个跳转,直接单步进入函数真实位置。

4.2进入到函数内部后可以看到真正的密码是1234567,如果我们是在逆向破解这个程序,那么到了这一步,就已经算是成功了。但是我们的目的是分析漏洞,所以我们现在进一步分析这个函数。

4.2.1可以看出,在strcmp之后,ebp-4的位置上就有了密码比对的结果
4.2.1.1strcmp比对密码,将比对结果存入ebp-4的位置上

4.2.1.2栈中的密码比对结果

4.2.2如果是正常的程序,这个时候就应该返回了,但是因为是实验代码,所以下面还有一个strcpy的拷贝函数,将输入的密码字符串拷贝入一个长度为8的缓冲区中。

4.2.3当执行完strcpy的时候我们看一下堆栈区,可以看到字符串缓冲区的位置就在密码字符串比对结果旁边,并且strcpy没有对拷贝入的字符串进行长度判断。因此我们可以判断,我们在构造一个合适的字符串传入的情况下,是可以覆盖密码字符串比对结果的。这也意味着我们可以传入一个合适的字符串来冲破密码验证。

5.我们重新加载这个程序,并且传入一个特定的字符串“qqqqqqqq”

6.运行到密码比对函数进行分析
6.1可以看出strcmp函数没有任何问题的执行成功并且返回了1,代表密码错误。并且把存储在eax中的返回值存储到ebp-4的位置上。
6.1.1函数执行

6.1.2返回值存储在eax中

6.1.3 eax中的值mov到了ebp-4的位置上

6.2下面到了引起溢出错误的strcpy函数,详细分析该函数溢出的过程。
6.2.1首先记录一下strcpy没有执行前堆栈的情况

6.2.2 执行strcpy函数

6.2.3 可以看到,堆栈中原本保存着密码比对结果的位置ebp-4,由于传入字符串超长,已经被覆盖成了0。这样一来,原本比对失败的结果就变成了比对成功。

7.运行至返回,成功输出密码比对成功的信息

7.1控制台成功信息

8.那么是任何长于1234567的字符串都可以成功覆盖比对结果吗,尝试一下,输入9个q,可以看到,对比结果的位置上并没有被覆盖成00,而是71,而只有对比结果等于0才可以成功验证。这说明不是任意长度字符串都可以。

9.那么有什么结果可以覆盖成整好是0呢,那么答案是长度为8的字符串,长度为8的字符串实际长度为9,因为还有用来标记字符串结束的00,我们就是要使用结尾处的00来覆盖对比结果,使其数值为0。

参考材料:《0day安全:软件漏洞分析技术》