ux## 0x00 前置
shellcode就是指攻击者要执行的代码,执行shellcode可以使攻击者获取某种权限。一般来说,只要启动了bin/sh,攻击者就能完全控制计算机。因此shellcode就是指一段很短小的,用于启动bin/sh的机器代码。
在开始了解shellcode之前,最好先弄清楚Linux用户权限//Linux用户权限传送门
本文实例环境是ubuntu16.04 64位


0x10 综述

先上一份示例代码-

运行结果如下:

看,通过运行这个程序,我们以普通用户的身份拿到了root权限的shell。

/*也许不少人会更熟悉system("/bin/sh");这样的写法。但是system函数本质上是fork出一个进程来调用了execve函数,所以个人感觉这样其实是更接近本质的shellcode。当然实际应用肯定还是system写起来方便,而且就这份代码而言,两者使用起来并没有什么差别。
上面的代码包含了很多底层的细节,如果你了解Linux用户权限的话应该会有所体会。不过本文的着重点并不在于此,故不赘述。*/

0x20 解析&构造

接下来我们用gdb来分析一下这份代码,我们首先看一下关键的execve函数部分

这里syscall是64位的系统调用(32位的系统调用是 int 0x80)。而前面的mov eax,0x3b是把0x3b(59)作为本次系统调用的编号。系统内核会根据不同的编号来实现不同的调用。
借此机会,我们简单的讲一下系统调用。


系统调用

在电脑中,系统调用(英语:system call),又称为系统呼叫,指运行在使用者空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供用户程序与操作系统之间的接口。

我们可以看看哪些操作是需要通过系统调用来实现的,在我的本机上系统调用的编号文件在/usr/include/asm/unistd_64.h里面
把64改成32就是32位的系统调用编号。我们可以看看里面的内容

可以看到很多我们比较熟悉的函数其实都是通过系统调用来实现的。
当我们要调用这些函数的时候,我们只需要把对应的编号装进EAX,再引发系统中断(int 0x80/syscall)就可以,也就是说,不考虑传参的话,我们调用write()在x64下就可以写成


接下来我们再看一下主程的反汇编结果

从0x4009fa开始是execve的传参,并在0x400a05处调用execve函数。
我们能够知道execve这里的三个参数

  • 参数1:rax(/bin/sh的地址)
  • 参数2:rcx(/bin/sh的地址以及内容为NULL的数组)
  • 参数3:NULL

根据这个我们能用pwntools写出shellcode

这里提一下记得在用asm函数是注意一下contextType,它存储了cpu类型以及操作系统,如果contextType不对的话你的asm函数可能会报错。

0x30 就是有这种操作

如果你实在不想写shellcode,pwntools有自带的shellcode生成器。
语法:shellcraft.arch.os.cmd()
比如你要生成在64位的Linux上执行/bin/sh的shellcode就可以使用shellcraft.amd64.linux.sh()

再搭配asm函数,你就能得到你想要的。