Pwn学习随笔(11)

puts函数打印库函数的地址 , 减掉该库函数与libc加载基地址的偏移 , 就可以计算libc的基地址 。然后可以利用libc中的gadget构造可以执行 " /bin/sh" 的ROP , ROPgadget使用ROPgadget检查程序中是否存在/bin/sh字符串或某条指令:ROPgadget --binary 文件名 --string="/bin/sh"ROPgadget --binary 文件名 --sting '/bin/sh'命令: ROPgadget --binary 文件名 --sting 'sh'命令: ROPgadget --binary 文件名 --sting 'cat flag'命令: ROPgadget --binary 文件名 --sting 'cat flag.txt'ROPgadget --binary 文件名 --only="pop|ret"用ROPgadget找一下程序里有没有可以用的改变rdi寄存器的值的gadgetsROPgadget --binary 文件名 --only "pop|ret" | grep rdi栈溢出栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数 , 因而导致与其相邻的栈中的变量的值被改变 。这种问题是一种特定的缓冲区溢出漏洞 , 类似的还有堆溢出 , bss 段溢出等溢出方式 。栈溢出漏洞轻则可以使程序崩溃 , 重则可以使攻击者控制程序执行流程 。此外 , 我们也不难发现 , 发生栈溢出的基本前提是

  • 程序必须向栈上写入数据 。
  • 写入的数据大小没有被良好地控制 。
最典型的栈溢出利用是覆盖程序的返回地址为攻击者所控制的地址 , 当然需要确保这个地址所在的段具有可执行权限 。下面 , 我们举一个简单的例子:
#include <stdio.h>#include <string.h>void success() { puts("You Hava already controlled it."); }void vulnerable() {char s[12];gets(s);puts(s);return;}int main(int argc, char **argv) {vulnerable();return 0;}这个程序的主要目的读取一个字符串 , 并将其输出 。我们希望可以控制程序执行 success 函数 。
输入gcc -m32 -fno-stack-protector stack1.c -o stack1 -no-pie -z execstack进行编译 , 可以看见报错了(但仍会生成可执行文件) , 可以看出 gets 本身是一个危险函数 。它从不检查输入字符串的长度 , 而是以回车来判断输入是否结束 , 所以很容易可以导致栈 溢出 , 
Pwn学习随笔

文章插图
gcc 编译指令中 , -m32 指的是生成 32 位程序; -fno-stack-protector 指的是不开启堆栈溢出保护 , 即不生成 canary 。-z execstack表示开启栈的可执行权限 , 在gcc4.1中默认禁用 ,  此外 , 为了更加方便地介绍栈溢出的基本利用方式 , 这里还需要关闭 PIE(Position Independent Executable) , 避免加载基址被打乱 。不同 gcc 版本对于 PIE 的默认配置不同 , 我们可以使用命令gcc -v查看 gcc 默认的开关情况 。如果含有--enable-default-pie参数则代表 PIE 默认已开启 , 需要在编译指令中添加参数-no-pie
编译成功后 , 可以使用 checksec 工具检查编译出的文件:
Pwn学习随笔

文章插图
提到编译时的 PIE 保护 , Linux 平台下还有地址空间分布随机化(ASLR)的机制 。简单来说即使可执行文件开启了 PIE 保护 , 还需要系统开启 ASLR 才会真正打乱基址 , 否则程序运行时依旧会在加载一个固定的基址上(不过和 No PIE 时基址不同) 。我们可以通过修改 /proc/sys/kernel/randomize_va_space 来控制 ASLR 启动与否 , 具体的选项有

经验总结扩展阅读