修改返回地址,让其指向内存中已有的某个函数
要完成的任务包括:在内存中确定某个函数的地址,并用其覆盖掉返回地址。由于 libc 动态链接库中的函数被广泛使用,所以有可能可以在内存中找到该动态库。同时由于该库包含了一些系统级的函数(例如 system()等),所以通常使用这些系统级函数来获得当前进程的控制权。鉴于要执行的函数可能需要参数,比如调用 system() 函数打开 shell的完整形式为system(“/bin/sh”) ,所以溢出数据也要包括必要的参数。
下面就以执行 system(“/bin/sh”)为例,先写出溢出数据的组成,再把对应的各部分填充进去。
payload:padding1 + address of system() + padding2 + address of “/bin/sh”
根据上面的构造,我们要解决几个问题。
1. 返回地址之前的填充数据(padding1)应该多长?
解决方法和 shellcode 中提到的答案一样。
2. system() 函数地址应该是多少?
要回答这个问题,就要看看程序是如何调用动态链接库中的函数的。当函数被动态链接至程序中,程序在运行时首先确定动态链接库在内存的起始地址,再加上函数在动态库中的相对偏移量,最终得到函数在内存的绝对地址。
说到确定动态库的内存地址,就要回顾一下shellcode 中提到的内存布局随机化(ASLR),这项技术也会将动态库加载的起始地址做随机化处理。所以,如果操作系统打开了ASLR,程序每次运行时动态库的起始地址都会变化,也就无从确定库内函数的绝对地址。
在 ASLR被关闭的前提下,我们可以通过调试工具在运行程序过程中直接查看 system()的地址,也可以查看动态库在内存的起始地址,再在动态库内查看函数的相对偏移位置,通过计算得到函数的绝对地址。
最后,“/bin/sh” 的地址在哪里?
可以在动态库里搜索这个字符串,如果存在,就可以按照动态库起始地址+相对偏移来确定其绝对地址。如果在动态库里找不到,可以将这个字符串加到环境变量里,再通过 getenv() 等函数来确定地址。
解决完上述问题,我们就可以拼接出溢出数据,输入至程序来通过 system() 打开 shell 了。需要指出的是,这种方法需要操作系统关闭内存布局随机化(ASLR)
最后声明一下 此文章仅为摘抄笔记不是原创