shellcode 的基本原理
shellcode 通常使用机器语言编写,是一段用于利用软件漏洞而执行的代码,因其目的常常是让攻击者获得目标机器的命令行 shell 而得名。
shellcode 可分为本地和远程两种类型。本地 shellcode 通常用于提权,攻击者攻击高权限程序中的漏洞,获得与目标进程相同的权限。远程 shellcode 则用于攻击网络上的另一台机器,通过 TCP/IP 套接字为共计则提供 shell 访问。根据连接方式的不同,可分为反向shell、绑定shell 和 套接字重用shell。
有时,攻击者注入目标进程中的字节数是被限制的,因此可以将 shellcode 分阶段执行,由前一阶段比较简短的 shellcode 将后一阶段复杂的 shellcode 下载并执行,这是恶意程序常见的一种操作。
简短的 shell 程序
1 | /* |
问题:
- shellcode 允许输入普遍较短只有几十个字节。
- 无法调用系统函数
案例解析
执行之后,可以看到程序拿到了 shell
使用 gdb 动态调试 shell
“n” 指令执行下一步,可以看到 system 函数是使用动态链接库,函数定位使用的是 <main+0x11>
。在不同的计算机环境中,这个地址都是不同的。
因此我们写的shellcode,无法直接调用 system 函数去获得 shell 。
解决方法
代码 system("/bin/sh")
底层调用的真实函数是 execve("/bin/sh",0,0)
,以下是 system 函数的片段源码。
使用软中断 int 0x80
或 syscall 指令调用 execve 函数进行系统调用,可使用这个地址查看:Linux System Call Table。
-
32位
-
64位
syscall 和 int 0x80 的使用
-
int 0x80调用execve函数是一种传统的Linux系统调用方法,它依赖于中断门来进行系统调用,这种方法在32位Linux系统中较为常见。
-
在64位系统中,系统调用的机制已经发生了变化,采用了更现代的系统调用接口。通常通过syscall指令来实现。syscall指令是x86-64架构中专门用于系统调用的指令。
32 位 shellcode
32 位 shellcode 需要完成如下操作:
1 | eax = 0xb |
代码:
1 | ;;nasm -f elf32 i386.asm |
0x2f62696e2f7368 就是字符串 “/bin/sh” 的十六进制值。
64 位 shellcode
64 位 shellcode 需要完成如下操作:
1 | rax = 0x3b |
代码:
1 | ;;nasm -f elf64 amd64.asm |
pwntools 生成 shellcode
使用 pwntools 快速生成对应架构的 shellcode:
- 设置目标架构
- 生成shellcode
32位
1 | from pwn import* |
64位
1 | from pwn import* |
案例一:mrctf2020_shellcode_revenge
第一步:IDA静态分析程序
read 函数
read函数是通过文件描述符来读取数据的。它的原型如下:
1 |
|
fd是文件描述符,buf是接收数据的缓冲区地址,count表示期望读取的字节数。read函数会从指定的文件中读取count个字节到buf中,并返回实际读取到的字节数。在读取过程中,文件指针会根据读取的字节数偏移。
fd参数的值可以是以下之一:
1 | 0: 标准输入(stdin) |
第二步:查看程序开启了哪些安全模块
前面可以看到,我们输入的数据是存到堆栈中的,而NX功能没开启,说明我们有权限去执行堆栈中的数据。
Rech
程序架构信息,判断是64位还是32位,exp编写的时候是p64还是p32
RELRO
Relocation Read-Onl(RELRO)此项技术主要针对GOT改写的攻击方式,它分成两种,Partial RELRO和FULL RELRO
Partial (部分)RELRO容易受到攻击,例如攻击者可以atoi.got为system.plt进而输入/bin/sh\x00获得shell,完全RELRO使整个GOT只读,从而无法被覆盖,但这样会大大增加程序的启动时间,因为程序在启动之前需要解析所有的符号。
1 | gcc -o hello test.c //默认情况下,是Partial RELRO |
Stack-canary
栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞是,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行,当启用栈保护后,函数开始执行的时候先会往栈里插入类似cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行,攻击者在覆盖返回地址的时候往往会将cookie信息给覆盖掉,导致栈保护检车失败而阻止shellcode的执行,在linux中我们将cookie信息称为canary。
1 | gcc -fno-stack-protector -o hello test.c //禁用栈保护 |
NX
NX enabled如果这个保护开启就是意味着栈中数据没有执行权限,如此一来,当攻击者在堆栈上部署自己的shellcode并触发时,就会直接造成程序的崩溃,但是可以利用rop这种方法绕过
1 | gcc -o hello test.c // 默认情况下,开启NX保护 |
PIE
PTE(Position-Independent Executable,位置无关可执行文件)技术与ASLR技术类似,ASLR将程序运行时的堆栈以及共享库的加载地址随机化,而PIE及时则在编译时将程序编译为位置无关,即程序运行时各个段(如代码但等)加载的虚拟地址也是在装载时才确定,这就意味着。在PIE和ASLR同时开启的情况下,攻击者将对程序的内存布局一无所知,传统改写GOT表项也难以进行,因为攻击者不能获得程序的.got段的虚地址。
1 | gcc -o hello test.c // 默认情况下,不开启PIE |
第三步:执行 shellcode,连接程序
本地验证
使用 pwntools 生成 shellcode
1 | # use pwntools generate shellcode |
使用手写shellcode
1 | # use pwntools generate shellcode |
远程验证(buuctf)
使用 pwntools 生成 shellcode
1 | # use pwntools generate shellcode |
使用手写shellcode
1 | # use pwntools generate shellcode |
参考
- 本文标题:PWN基础入门 - shellcode
- 本文作者:9unk
- 创建时间:2024-04-07 10:00:00
- 本文链接:https://9unkk.github.io/2024/04/07/pwn-ji-chu-ru-men-shellcode/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!