Format String Vulnerability

printf("%s", user_input)
printf(user_input)
  • 读取栈上的任意欸容
  • 写任意内存

printf 如何解析格式化字符串

  • 遇到普通字符:原样打印
  • 遇到 %:需要从栈上提取一个参数
%d
%x
%p
%s
%n

AAAA %7$p

位置选择

%1$x
%2$x
%10$p
%14$s

Arbitrary Read

payload = <8字节地址> + %7$s (截断)
  • 泄露 stack canary
  • 泄露 libc 地址 -> 立刻可以ROP
  • 泄露 GOT 表内容
  • 泄露指针 -> 推算ASLR偏移

Arbitrary Write

  • %n 会把已打印字符数量写入指定地址
AAAA %n <addr bytes>
  • hhn / hn

典型攻击场景

  • GOT entries
  • 函数指针
  • vtable
  • dtors
  • libc hooks (_malloc_hook, _free_hook)
  • atexit handlers
  • 全局变量中的函数指针

标准解题步骤

  1. 找漏洞点
printf(user_input)
fprint(log, user_input)
syslog(user_input)
sprintf(buffer, user_input)
  1. 找buffer在第几个参数的位置
AAAABBBB %1$p
AAAABBBB %2$p
AAAABBBB %3$p
...
  1. Arbitrary Read
%<index>$s
%<index>$p
  • canary
  • return address
  • libc address
  • PIE base
  • GOT 内容
  1. 计算目标地址(system, win, GOT)

  2. Arbitrary Write

%<width>x%<index>$hhn
  1. 重定向控制流
overwrite exit@got  ->  win
overwrite printf@got  ->  system

Return Oriented Programming

不用写任何shellcode,只使用程序已有的代码片段(gadgets)实现任意i计算。

- NX
- 现代ASLR配合信息泄露
- 无法放shellcode的情况(过滤输入字符)

为什么要ROP

  • NX
  • PIE / ASLR
  • RELRO

ROP 的基本单位: gadget

pop rdi; ret
pop rsi; ret
pop rdx; ret
syscall; ret

ROP chain (*)

system("/bin/sh")

[padding]
[pop rdi; ret]
["/bin/sh" 地址]
[system 地址]
[exit 地址]

Bypass ASLR

  • 某个libc函数的运行时地址 (puts / printf / gets)
puts(puts@got)


libc_base = leaked_puts - offset_puts
system = libc_base + offset_system
binsh = libc_base + offset_binsh

system("/bin/sh")

System unavailable -> Syscall ROP

用syscall gadget 手写 Linux系统调用,例如 execve

控制 rax (系统调用号)
控制 rdi (argv[0])
控制 rsi
控制 rdx
控制 rcx ...

exevce("/bin/sh", NULL, NULL)

Stack Pivot

  • xchg rsp, rax
  • add rsp, 0x80
  • 覆盖rbp 让 ret 到其他地方

让栈指向更大的buffer

解题方法

  1. 找出溢出点,找可以覆盖 return addr 的位置
  2. 检查保护(checksec)
  • NX on -> 必考 ROP
  • PIE on -> 必须信息泄露
  • RELRO full -> 不能改GOT
  1. 泄露地址(information leak)
puts(puts@got)
printf(%7$p)

得到libc地址
  1. 计算libc基址,找到system和“/bin/sh”
  2. 构造ROP chain
  • stack alignment
  1. 执行ret2libc / ROP 获取 shell

ROP 失败的常见原因

  • Stack Alignment
  • 没有 control rdi(pop rdi; ret)
pop rdi; pop rsi; ret
pop rdi; pop r15; ret
mov
add
syscall ROP -> system
  • libc version
  • 参数搞反了
  • 距离算错
  • slide
  • /bin/sh 指针
  • bad characters
0x0a (换行)
0x00 (字符串结束符)
0x20 (空格)

payload提前截断