argv[0] leak - Stack Canary实现

Stack Canary

  • gcc编译后,stack上存在一个canary
  • 进入函数时,canary被随机赋值
  • 退出函数时(return前),会检查canary是否被修改

简单测试

自己写一段简单代码,产生溢出,查看结果

  • gcc加入了一个叫做SSP(ProPolice)的机制
  • 溢出导致canary被修改,检测到之后产生这样的错误信息

栈的结构及溢出情况

大概是这样,当产生溢出时,一定会覆盖到canary

stack canary的检查机制

  • 进入函数时,canary值从TLS区域(fs:0x28)到rax,再加载到stack上,之后清空rax
  • 正常执行函数代码
  • 退出函数前检查stack上canary与TLS区域的canary是否相等,检测到被修改则调用__stack_chk_fail()

argv[0] leak - __stack_chk_fail()实现

__stack_chk_fail()内部实现

__libc_argv

  • 在stack上大概是这样

argv[0] leak - 运行测试

能否通过溢出修改栈上的argv[0]?

测试代码

  • 定语一个测试字符串,确定它的地址,然后溢出写入很多这个地址
  • 在检查canary时断点,检查状态,这时候栈上的argv[0]应该已经被修改为定义的字符串
  • 继续运行,因为canary被修改,调用__stack_chk_fail(),错误信息显示的argv[0]也已经是定义的字符串
  • 通过这种方式,就能够泄漏任意内存
  • 但是,程序在此之后会结束,所以即使多次运行也只能泄漏”不变的字符串/数据”

这就是通过argv[0]进行内存泄漏的技术

  1. 假设在存在stack canary的函数中产生溢出
  2. stack canary被检测到修改
  3. 调用__stack_chk_fail()
  4. 产生错误信息,结束
  5. 错误信息中包含__libc_argv[0]
  6. __libc_argv[0]是存在于stack上的argv[0]
  7. 这个argv[0]也可以通过溢出修改
  • 如果有足够的溢出空间,那么就能够同时做到”canary破坏”和”argv[0]重写”
  • 泄漏目标可以是地址固定区域的值,例如.data或者.bss
    • flag自身(如果在内存里)
    • 秘密信息,例如密码,密钥信息
    • func@GOT
    • 根据低12比特位识别libc版本
  • https://qiita.com/sei0o/items/55db337b0829367a2052

补充 - xinetd与socat的默认行为差异

xinetd环境下

  • 会因为/dev/tty打开失败输出到stderr中
  • 在xinetd中,stderr绑定到socket
  • 因此argv[0]泄漏大体上是能成功的

socat环境下

  • 能成功打开/dev/tty,输出到/dev/tty中
  • /dev/tty表示终端屏幕,因此无法连接到socket
  • 因此,不能使用argv[0]泄漏
  • 在本地执行的情况下,如果想要socat的行为类似于xinetd,则需要手动添加stderr,setsid选项
  • 如果远程环境是socat,并且没有stderr,setsid,伪造环境变量LIBC_FATAL_STDERR_=1也可以