はじめに
Metasploitで、davtestのようなモジュールを作成します。
davtestでurlだけ指定して実行した場合と同様にフォルダを作成して、各種ファイル形式をアップロードして、実行可能か、テストします。
modules/auxiliary/scanner/http/http_put.rbを元に作成しました。
テストは、php,txt,html,shtmlしかしていません。
Msf::Exploit::Remote::HttpClient
send_request_cgi
このメッソドを使用して、httpリクエストを送信します。
res = send_request_cgi(opts = {}, timeout = 20, disconnect = true)
Options Hash (opts):
- Content-Type ('ctype' => String)
- ヘッダー値(POSTリクエストのデフォルト値):application/x-www-form-urlencoded
- URI ('encode_params' => Bool)
- GETまたはPOST変数(名前と値)をエンコードします。デフォルト:true
- GET ('vars_get' => Hash)
- クエリ文字列に変換されるハッシュとしての変数
- POST ('vars_post' => Hash)
- POSTデータに変換されるハッシュとしての変数
- POST ('vars_form_data' => Hash)
- POSTデータに変換されるハッシュとしての変数
- User-Agent ('agent' => String)
- ヘッダー値
- Connection ('connection' => String)
- ヘッダー値
- Cookie ('cookie' => String)
- ヘッダー値
- HTTP ('data' => String)
- データ(一部のメソッドでのみ有用。RFC2616を参照)
- URI ('encode' => Bool)
- 指定されたURIをエンコードします。デフォルト: false
- HTTP ('headers' => Hash)
- ヘッダーをハッシュで指定する、例: { "X-MyHeader" => "value" }
- HTTP ('method' => String)
- リクエストで使用するメソッド。RFC2616で定義されている標準メソッドに限定されない。デフォルト:GET
- protocol, ('proto' => String)
- デフォルト: HTTP
- raw ('query' => String)
- クエリ文字列をそのまま指定する。
- Raw ('raw_headers' => String)
- HTTPヘッダーをそのまま指定する。改行コード(\r\n)も必要
- the ('uri' => String)
- リクエストするURI
- version ('version' => String)
- プロトコルのデフォルト値: 1.1
- Host ('vhost' => String)
- ヘッダー値
HTTPメソッド
PROPFIND
指定したURIが示す資源の属性を取得します。具体的には、要求する属性をクライアントがWebサーバに送信すると、サーバはそれに対応した属性値を返します。また、その資源の属性全てを取得することも出来ます。
davtest.plでは、GETを送る前にPROPFINDを送っているようです。
最初、PROPFINDで、フォルダやファイルの存在を確認していましたが、nginxのWebDavでは、デフォルトでは、PROPFINDに対応していないようですので、GETで確認するようにしました。
以下のヘッダーを、raw_headerで追加しています。
Depth: 0
データとして
<?xml version="1.0" encoding="utf-8"?>
<D:propfind xmlns:D="DAV:">
<D:allprop/>
</D:propfind>
が必要です。
多分、すべてのプロパティを取得するということだと思います。
MKCOL
指定したURIの場所に新たな資源を作成します。
ファイルをアップロードするためのフォルダを作成します。
GET
通常のGETコマンド。
アップロードフォルダの存在確認とアップロードしたアプリの動作確認をします。
PUT
Webサーバー上のファイルをHTTPのPUTメソッドを用いて、ローカルのファイルで上書き更新、または新規作成します。
dataとして、ファイルの内容を引き渡すことで、path名のファイルを作成します。
プログラム
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'securerandom'
require 'date'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::WmapScanDir
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize
super(
'Name' => 'HTTP Writable Path PUT File Testing',
'Description' => %q{
This module tests whether the web server's Path supports WebDAV via PUT and
GET HTTP requests.
Please configure the target host and Path.
},
'Author' => [
'tsuko5963 [at] gmail.com'
],
'License' => MSF_LICENSE
)
register_options(
[
OptString.new('PATH', [true, "Path to test", '/'])
]
)
end
#
# Do a PROFIND request to the server. Function returns the HTTP response.
#
def propaty_find(path, data, ip)
begin
res = send_request_cgi(
{
'uri' => path,
'method' => 'PROPFIND',
'ctype' => 'text/xml',
'raw_headers' => "Depth: 0\r\n",
'data' => data,
}, 20
)
rescue ::Exception => e
print_error("#{ip}: Error: #{e.to_s}")
return nil
end
return res
end
#
# Do a MKCOL request to the server. Function returns the HTTP response.
#
def make_collection(path, ip)
begin
res = send_request_cgi(
{
'uri' => path,
'method' => 'MKCOL',
}, 20
)
rescue ::Exception => e
print_error("#{ip}: Error: #{e.to_s}")
return nil
end
return res
end
#
# Send a normal HTTP GET request. Function returns the HTTP response.
#
def do_get(path, ip)
begin
res = send_request_cgi(
{
'uri' => path,
'method' => 'GET',
}, 20
)
rescue ::Exception => e
print_error("#{ip}: Error: #{e.to_s}")
return nil
end
return res
end
#
# Do a PUT request to the server. Function returns the HTTP response.
#
def do_put(path, data, ip)
begin
res = send_request_cgi(
{
'uri' => normalize_uri(path),
'method' => 'PUT',
'ctype' => 'text/plain',
'data' => data,
}, 20
)
rescue ::Exception => e
print_error("#{ip}: Error: #{e.to_s}")
return nil
end
return res
end
#
# Main function for the module.
#
def run_host(ip)
test_txt = {
php: {content: '<?php print 7.8 * 6.4;?>', execmatch: '49.92', put: false, exec: false},
txt: {content: 'TXT put via davtest', execmatch: 'TXT put via davtest', put: false, exec: false},
html: {content: 'HTML put via davtest<br />', execmatch: 'HTML put via davtest', put: false, exec: false},
asp: {content: '<html><body><% response.write (7.8 * 6.4) %>', execmatch: '49.92', put: false, exec: false},
aspx: {content: '<html><body><% response.write (7.8 * 6.4) %>', execmatch: '49.92', put: false, exec: false},
cfm: {content: '<cfscript>WriteOutput(7.8*6.4);</cfscript>', execmatch: '49.92', put: false, exec: false},
cgi: {content: "#!/usr/bin/perl\nprint 'Content-Type: text/html\n\r\n\r' . 7.8 * 6.4;", execmatch: '49.92', put: false, exec: false},
jhtml: {content: '<%= System.out.println(7.8 * 6.4); %>', execmatch: '49.92', put: false, exec: false},
jsp: {content: '<%= System.out.println(7.8 * 6.4); %>', execmatch: '49.92', put: false, exec: false},
pl: {content: "#!/usr/bin/perl\nprint 'Content-Type: text/html\n\r\n\r' . 7.8 * 6.4;", execmatch: '49.92', put: false, exec: false},
shtml: {content: '<p><!--#config timefmt="%Y-%m-%d" --><!--#echo var="DATE_LOCAL" --></p>', execmatch: Date.today.to_s, put: false, exec: false},
}
dir_create = false
path = datastore['PATH']
#Add a leading '/' if one is not already present.
if path[0, 1] != '/'
path = '/' + path
end
#Add a '/' if there is no '/' at the end of the path.
if path[-1, 1] != '/'
path += '/'
end
puts "********************************************************\n Testing DAV connection"
# Find Property of the upload directory
# data = '<?xml version="1.0" encoding="utf-8"?><D:propfind xmlns:D="DAV:"><D:allprop/></D:propfind>'
# res = propaty_find(path, data, ip)
data = ""
res = do_get(path, ip)
if res && res.code.to_s == '200'
print_good("OPEN\t\tSUCCEED:\t\t#{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{path}")
sid = [*('a'..'z'), *('A'..'Z'), *('0'..'9'), '_'].sample(rand(5..15)).join
newdir = "DavTestDir_#{sid}"
new_url = "#{path}#{newdir}"
puts "********************************************************\nNOTE\tRandom string for this session: #{newdir}"
puts "********************************************************\n Creating directory"
data = ""
res = make_collection(new_url, ip)
if res && res.code.to_s == '201'
print_good("MKCOL\t\tSUCCEED:\t\tCreated #{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{new_url}")
dir_create = true
puts "********************************************************\n Sending test files"
test_txt.each do |k, v|
# Upload file
res = do_put("#{new_url}/davtest_#{sid}.#{k}", v[:content], ip)
if res && res.code.to_s == '201'
print_good("PUT\t#{k}\tSUCCEED:\t#{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{new_url}/davtest_#{sid}.#{k}")
v[:put] = 'true'
else
print_err("PUT\t#{k}\tFAIL")
end
end
puts "********************************************************\n Checking for test files execution"
test_txt.each do |k, v|
# Execute file
res = do_get("#{new_url}/davtest_#{sid}.#{k}", ip)
if res && res.code.to_s == '200' && res.body.include?(v[:execmatch])
print_good("EXEC\t#{k}\tSUCCEED:\t#{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{new_url}/davtest_#{sid}.#{k}")
v[:exec] = 'true'
else
print_error("EXEC\t#{k}\tFAIL")
end
end
else
print_error("MKCOL\t\tFAIL")
end
else
print_error("OPEN\t\tFAIL:\t\t#{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{path}")
end
puts "********************************************************\n Summary:"
if dir_create
puts "Created: #{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{new_url}"
end
test_txt.each do |k, v|
if v[:put]
puts "PUT File: #{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{new_url}/davtest_#{sid}.#{k}"
end
end
test_txt.each do |k, v|
if v[:exec]
puts "Executes: #{(ssl ? 'https' : 'http')}://#{ip}:#{rport}#{new_url}/davtest_#{sid}.#{k}"
end
end
end
end
インストール
~/.msf4/modules/auxiliary/scanner/httpにdavtest.rbで保存します。
実行する
Metasploitable3のuploadsフォルダをテストします。
Metasploitを起動します。
モジュールを検索します。
[msf](Jobs:0 Agents:0) >> search davtest
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 auxiliary/scanner/http/davtest . normal No HTTP Writable Path PUT File Testing
Interact with a module by name or index. For example info 0, use 0 or use auxiliary/scanner/http/davtest
モジュールを選択します。
[msf](Jobs:0 Agents:0) >> use 0
オプションを確認します。
[msf](Jobs:0 Agents:0) auxiliary(scanner/http/davtest) >> show options
Module options (auxiliary/scanner/http/davtest):
Name Current Setting Required Description
---- --------------- -------- -----------
PATH / yes Path to test
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: socks4, socks5, socks5h, http, sapni
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
RPORT 80 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
THREADS 1 yes The number of concurrent threads (max one per host)
VHOST no HTTP server virtual host
View the full module info with the info, or info -d command.
オプションを設定します。
[msf](Jobs:0 Agents:0) auxiliary(scanner/http/davtest) >> set RHOSTS 192.168.56.200
RHOSTS => 192.168.56.200
[msf](Jobs:0 Agents:0) auxiliary(scanner/http/davtest) >> set PATH uploads
PATH => uploads
実行します。
[msf](Jobs:0 Agents:0) auxiliary(scanner/http/davtest) >> run
********************************************************
Testing DAV connection
[+] OPEN SUCCEED: http://192.168.56.200:80/uploads/
********************************************************
NOTE Random string for this session: DavTestDir_8h0UM
********************************************************
Creating directory
[+] MKCOL SUCCEED: Created http://192.168.56.200:80/uploads/DavTestDir_8h0UM
********************************************************
Sending test files
[+] PUT php SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.php
[+] PUT txt SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.txt
[+] PUT html SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.html
[+] PUT asp SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.asp
[+] PUT aspx SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.aspx
[+] PUT cfm SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.cfm
[+] PUT cgi SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.cgi
[+] PUT jhtml SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.jhtml
[+] PUT jsp SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.jsp
[+] PUT pl SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.pl
[+] PUT shtml SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.shtml
********************************************************
Checking for test files execution
[+] EXEC php SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.php
[+] EXEC txt SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.txt
[+] EXEC html SUCCEED: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.html
[-] EXEC asp FAIL
[-] EXEC aspx FAIL
[-] EXEC cfm FAIL
[-] EXEC cgi FAIL
[-] EXEC jhtml FAIL
[-] EXEC jsp FAIL
[-] EXEC pl FAIL
[-] EXEC shtml FAIL
********************************************************
Summary:
Created: http://192.168.56.200:80/uploads/DavTestDir_8h0UM
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.php
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.txt
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.html
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.asp
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.aspx
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.cfm
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.cgi
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.jhtml
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.jsp
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.pl
PUT File: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.shtml
Executes: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.php
Executes: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.txt
Executes: http://192.168.56.200:80/uploads/DavTestDir_8h0UM/davtest_8h0UM.html
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed