6.1.25 pwn HCTF2017 babyprintf

下载文件

题目复现

$ file babyprintf
babyprintf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5652f65b98094d8ab456eb0a54d37d9b09b4f3f6, stripped
$ checksec -f babyprintf
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   Yes     1               2       babyprintf
$ strings libc-2.24.so | grep "GNU C"
GNU C Library (Ubuntu GLIBC 2.24-9ubuntu2.2) stable release version 2.24, by Roland McGrath et al.
Compiled by GNU CC version 6.3.0 20170406.

64 位程序,开启了 canary 和 NX,默认开启 ASLR。

在 Ubuntu16.10 上玩一下:

./babyprintf
size: 0
string: AAAA
result: AAAAsize: 10
string: %p.%p.%p.%p
result: 0x7ffff7dd4720.(nil).0x7ffff7fb7500.0x7ffff7dd4720size: -1
too long

真是个神奇的 "printf" 实现。首先 size 的值对 string 的输入似乎并没有什么影响;然后似乎是直接打印 string,而没有考虑格式化字符串的问题;最后程序应该是对 size 做了大小上的检查,而且是无符号数。

题目解析

main

整个程序非常简单,首先分配 size 大小的空间,然后在这里读入字符串,由于使用 gets() 函数,可能会导致堆溢出。然后直接调用 __printf_chk() 打印这个字符串,可能会导致栈信息泄露。

这里需要注意的是 __printf_chk() 函数,由于程序开启了 FORTIFY 机制,所以程序在编译时所有的 printf() 都被 __printf_chk() 替换掉了。区别有两点:

  • 不能使用 %x$n 不连续地打印,也就是说如果要使用 %3$n,则必须同时使用 %1$n%2$n

  • 在使用 %n 的时候会做一些检查。

漏洞利用

所以这题应该不止是利用格式化字符串,其实是 house-of-orange 的升级版。由于 libc-2.24 中加入了对 vtable 指针的检查,原先的 house-of-arange 已经不可用了。然后新的利用技术又出现了,即一个叫做 _IO_str_jumps 的 vtable 里的 _IO_str_overflow 虚表函数(参考章节 4.13)。

overwrite top chunk

为了能将 top chunk 释放到 unrosted bin 中,首先覆写 top chunk 的 size 字段:

leak libc

然后利用格式化字符串来泄露 libc 的地址,此时的 top chunk 也已经放到 unsorted bin 中了:

house of orange

改进版的 house-of-orange,详细你已经看了参考章节,这里就不再重复了,内存布局如下:

pwn

最后触发异常处理,malloc_printerr -> __libc_message -> __GI_abort -> _IO_flush_all_lockp -> __GI__IO_str_overflow,获得 shell。

开启 ASLR,Bingo!!!

exploit

完整 exp 如下:

参考资料

Last updated

Was this helpful?