[PWN] 栈溢出初见

0x1. 前置条件

此案例来源于 《0day安全: 软件漏洞分析技术》, 章节 -> 第2章.
程序源码如下:

#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"

int verify_password(char *password) {
    int flag = -1;
    char buffer[44];
    flag = strcmp(password, PASSWORD);
    strcpy(buffer, password);  // over flowed here
    return flag;
}

void main() {
    int flag = 0;
    char password[1024];
    FILE * fp;
    LoadLibrary("user32.dll");
    if(!(fp = fopen("password.txt", "r"))) {
        exit(0);
    }
    fscanf(fp, "%s", password);
    flag = verify_password(password);
    if( flag ) {
        printf("fail\n");
    } else {
        printf("succss\n");
    }
}

编译环境: Windows XP SP3 + VC 6.0
调试环境: Windows XP SP3 + Ollydbg + UltraEdit / Win10 + IDA

0x2 栈溢出初见

IDA 打开程序后, F5 看一下源码和汇编代码走向.
主程序流程
可以看到 main 函数中并不对数据进行校验, 主要校验函数在 verify_password() 里.
main 函数将数据从 password.txt 中取出来然后以参数的形式传递给 verify_password().
数据校验函数
verify_password 函数主要做了两步操作, 第一步是 strcmp 比较函数;
第二步是 strcpy 复制函数, 而 strcpy 的执行并没有对参数进行任何的校验或者限制.
这里存在着溢出点. 我们看到这里用以 strcmp 比较的内置字符串为 "1234567", 在这下个断点.
001-bp-1234567.PNG
首先用字符串搜索插件搜索字符串, 然后双击字符串到达领空, F2 下断点, F9 执行程序到达断点处。
在 Ollydbg 的界面中, 可以看到 EBP/ESP/EIP 的各项数值. 其中:
EBP => 0x0012FB20, 栈基址
ESP => 0x0012FAA4, 栈顶
EIP => 0x0040103F, 下一条语句的地址
在界面的中间下部栈空间区域, 可以看到当前栈基址 0x0012FB20 保存着上一个栈的栈基址为 0x0012FF80.
在它的下面, 0x0012FB24 则保存着返回地址 0x00401118, 通过左下窗口的数据查看器可以看到,
0x00401118 为 main 函数的代码段, 此地址对应的代码为执行了 call test003.00401005 后的下一条语句.
可以确定的认为 test003.00401005 为 verify_password 函数地址.
本次栈溢出的目的主要通过 strcmp 函数, 覆盖返回地址, 劫持 EIP 的指向, 来达到我们控制程序以执行任意代码目的.

0x3 细节

002-exec-strcmp.png
F8 单步执行, 从断点处执行到 strcmp 函数执行完毕, strcmp 函数的返回值一般保存在 EAX,
003-rtn-strcmp.png
再次单步执行 2 次, 可以看到 verify_password 函数的内置变量 flag 的值变为了 1.
004-push-payload-01.png
接着往下走, strcmp 执行完后, 到了 strcpy 函数.
首先可以通过代码窗口看到, 先将 arg.1 的地址入栈.
其中 arg.1 的地址为 0x0012FB7C, 这里保存着 arg.1 的数据. 它的长度为 0x12FB7C ~ 0x12FBD4, 共 88D 的长度.
004-push-payload-02.png
往下走, 程序已经将 local.12 的地址入栈.
local.12 的地址为 0x0012FAF0, 它的长度为 0x12FAF0 ~ 0x12FB18. 共 40D 的长度.
显而易见, arg.1 的长度是 local.12 长度的 2 倍多, 将会导致将 local.12 以及后面的空间给覆盖掉.
004-push-payload-03.png
F8 单步执行, strcmp 执行完毕后, 可以看到栈空间已经完全变了样.
flag 和 EBP 被覆盖成了 0x90909090, 而最重要的返回地址, 则被覆盖成了 0x77D8692C.
005-PAYLOAD-EXEC.png
0x77D8692C 这个地址有一条关键指令. 叫做 JMP ESP.
当程序执行到 retn 时, retn 等价于 POP EIP, 把当前栈中, ESP 指向地址的值出栈, 赋给 EIP.
当我们利用 strcpy 将原本的返回地址 0x00401118 给覆盖为 0x77D8692C 时.
我们就完成了将程序进行劫持的步骤, 可以控制程序执行任意代码.
007-FINAL.png

标签: none

添加新评论