## 創建Metasploit框架 LoginScanners模塊
那么,你想在Metasploit中創建一個Login Scanner模塊,呃?在開始之前,你需要知道幾件事情。本文將試圖說明創建一個有效的暴力/登錄掃描器模塊所涉及的所有部分。
[TOC]
### 憑據對象
Metasploit::Framework::Credential (lib/metasploit/framework/credential.rb)
這些對象代表了我們現在如何思考憑證的最基本概念。
* Public:證書的公共部分是指可以公開的部分。在幾乎所有情況下,這是用戶名。
* Private:證書的私人部分,這是應該是一個秘密的部分。這目前代表:密碼,SSH密鑰,NTLM哈希等
* Private Type:這個定義了上面定義了什么類型的私人證件
* Realm:這表示證書有效的認證區域。這是身份驗證過程的一個重要部分。例子包括:活動目錄域,Postgres數據庫等
* Realm key:這定義了領域屬性代表什么類型的領域
* Paired:此屬性是一個布爾值,用于設置憑據是否必須同時具有公有和私有兩種屬性
所有LoginScanner都使用Credential對象作為其嘗試登錄的基礎。
### Result Objects
Metasploit::Framework::LoginScanner::Result (lib/metasploit/framework/login_scanner/result.rb)
這些是由掃描產生的對象! 方法在每個LoginScanner上。 他們包含:
* Access Level: 可選的.訪問級別,可以描述登錄嘗試授予的訪問級別.
* Credential : 實現這結果的Credential 對象
* spoof: 一個可選的證明字符串。顯示為什么我們認為結果是有效的
* Status: 登錄嘗試的狀態。 這些值來自于
` Metasploit::model::Login::Status`
示例 "Incorrect", "Unable to Connect", "Untried"
### CredentialCollection
Metasploit::Framework::CredentialCollection(lib/metasploit/ramework /credential_collection.rb)
這個類用于從模塊獲取數據存儲選項和從每個方法中生成Credential對象。它需要wordlist文件,以及直接的用戶名和密碼選項。它也需要是否嘗試將用戶名作為密碼和空白密碼選項。
它可以作為LoginScanner上的cred_details傳入,并響應#each并生成已制作的憑證。
示例(modules/auxiliary/scanner/ftp/ftp_login.rb):
~~~
cred_collection = Metasploit::Framework::CredentialCollection.new(
blank_passwords: datastore['BLANK_PASSWORDS'],
pass_file: datastore['PASS_FILE'],
password: datastore['PASSWORD'],
user_file: datastore['USER_FILE'],
userpass_file: datastore['USERPASS_FILE'],
username: datastore['USERNAME'],
user_as_pass: datastore['USER_AS_PASS'],
prepended_creds: anonymous_creds
)
~~~
### LoginScanner 基礎
Metasploit::Framework::LoginScanner::Base (lib/metasploit/framework/login_scanner/base.rb)
這是一個Ruby模塊,包含所有LoginScanners的所有基本行為。所有的LoginScanner類都應該包含這個模塊。
此行為的規范保存在共享示例組中。 您的LoginScanner規范應使用以下語法來包含這些測試:
~~~
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: false, has_default_realm: false
~~~
其中has_realm_key和has_default_realm應該根據您的LoginScanner是否有來設置。
LoginScanner總是收集Crednetials來嘗試登錄一個主機和端口。 所以每個LoginScanner對象都只會嘗試登錄到一個特定的服務。
#### 屬性
* connection_timeout: 一個連接等待多長時間超時
* cred_details: 一個在each中生成credentials的對象 (像一個 credentialCollection 或者一個數組)
* host: 目標主機地址
* port: 目標服務端口號
* proxies: 在連接中使用的任何代理(一些掃描可能不支持這個)
* stop_on_success: 是否在嘗試成功登錄后停止
#### 方法
* each_credential :你不用擔心這個方法,請注意它在那里。 它遍歷cred_details中的任何內容,進行一些規范化操作,并嘗試確保每個Credential被正確設置以供給定的LoginScanner使用。 它在一個塊中產生每個憑證。
~~~
def each_credential
cred_details.each do |raw_cred|
# This could be a Credential object, or a Credential Core, or an Attempt object
# so make sure that whatever it is, we end up with a Credential.
credential = raw_cred.to_credential
if credential.realm.present? && self.class::REALM_KEY.present?
credential.realm_key = self.class::REALM_KEY
yield credential
elsif credential.realm.blank? && self.class::REALM_KEY.present? && self.class::DEFAULT_REALM.present?
credential.realm_key = self.class::REALM_KEY
credential.realm = self.class::DEFAULT_REALM
yield credential
elsif credential.realm.present? && self.class::REALM_KEY.blank?
second_cred = credential.dup
# Strip the realm off here, as we don't want it
credential.realm = nil
credential.realm_key = nil
yield credential
# Some services can take a domain in the username like this even though
# they do not explicitly take a domain as part of the protocol.
second_cred.public = "#{second_cred.realm}\\#{second_cred.public}"
second_cred.realm = nil
second_cred.realm_key = nil
yield second_cred
else
yield credential
end
end
end
~~~
* set_sane_defaults: 這個方法將被每個特定的Loginscanner覆蓋。 這是在初始化程序的結束調用的,并為它們具有并且在初始化程序中沒有給定具體的值的屬性設置理想的默認值,
~~~
# This is a placeholder method. Each LoginScanner class
# will override this with any sane defaults specific to
# its own behaviour.
# @abstract
# @return [void]
def set_sane_defaults
self.connection_timeout = 30 if self.connection_timeout.nil?
end
~~~
* attempt_login:這個方法只是Base mixin中的一個存根。 在每個LoginScanner類中將覆蓋,以包含采用一個Credential對象的邏輯,并使用它來針對目標服務進行登錄嘗試。它返回一個::Metasploit::Framework::LoginScanner::Result 對象, 包含有關該嘗試結果的所有信息。 舉一個例子,讓我們看一下來自Metasploit::Framework::LoginScanner::FTP (lib/metasploit/framework/login_scanner/ftp.rb)的attempt_login方法
~~~
# (see Base#attempt_login)
def attempt_login(credential)
result_options = {
credential: credential
}
begin
success = connect_login(credential.public, credential.private)
rescue ::EOFError, Rex::AddressInUse, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
success = false
end
if success
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
elsif !(result_options.has_key? :status)
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
end
::Metasploit::Framework::LoginScanner::Result.new(result_options)
end
~~~
* scan! :這個方法是你將要關心的主要方法。 這個方法做了幾件事。
1. 它調用有效! 它將檢查類的所有驗證,如果發生任何失敗。并拋出一個Metasploit::Framework::LoginScanner::Invalid。此錯誤將包含任何失敗驗證的全部錯誤消息
2. 它跟蹤連接錯誤計數,并救援。如果我們有太多的連接錯誤或連續太多連接錯誤
3. 它通過在一個塊調用each_credential來使用所有憑證
4. 在這個塊中,它將每個憑證傳遞給#attempt_login
5. 它會生成Result對象放入傳遞的塊中
6. 如果設置了stop_on_success,如果結果是成功的,它也會提前退出
~~~
# Attempt to login with every {Credential credential} in
# {#cred_details}, by calling {#attempt_login} once for each.
#
# If a successful login is found for a user, no more attempts
# will be made for that user.
#
# @yieldparam result [Result] The {Result} object for each attempt
# @yieldreturn [void]
# @return [void]
def scan!
valid!
# Keep track of connection errors.
# If we encounter too many, we will stop.
consecutive_error_count = 0
total_error_count = 0
successful_users = Set.new
each_credential do |credential|
next if successful_users.include?(credential.public)
result = attempt_login(credential)
result.freeze
yield result if block_given?
if result.success?
consecutive_error_count = 0
break if stop_on_success
successful_users << credential.public
else
if result.status == Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
consecutive_error_count += 1
total_error_count += 1
break if consecutive_error_count >= 3
break if total_error_count >= 10
end
end
end
nil
end
~~~
#### 常量
雖然沒有在Base上定義,但是每個LoginScanner都有一系列可以在其上面定義的常量來幫助處理關鍵行為。
* DEFAULT_PORT:DEFAULT_PORT是一個與set_sane_defaults一起使用的簡單常量。如果端口沒有被用戶設置,它將使用DEFAULT_PORT。這被放在一個常量,所以它可以從掃描儀外部快速引用。
LoginScanner命名空間方法classes_for_services使用這兩個常量。這個被調用的方法Metasploit::Framework::LoginScanner.classes_for_service(<Mdm::service>)實際上會返回一個LoginScanner類的數組,這對于針對這個特定的Service可能是有用的。
* LIKELY_PORTS:這個常量保存了n個端口號,這對于使用這個掃描器來說可能是有用的。
* LIKELY_SERVICE_NAMES:如上所述,服務名稱的字符串而不是端口號。
* PRIVATE_TYPES:這包含表示它所支持的不同私有證書類型的符號數組。它應該總是匹配私人類的demodulize結果,即password ntlm_hash ssh_key
這些常量是必須處理域名(如AD域或數據庫名稱)的LoginScanners
* REALM_KEY: 這個掃描器希望處理的領域的類型.應始終是來自metasploit::Model::Login::Status的常量
* DEFAULT_REALM:一些掃描儀有一個默認的領域(比如AD域名的WORKSTATION).如果將憑證給需要領域的掃描器,但憑證沒有領域,則將該值作為領域的值添加到證書中。
* CAN_GET_SESSION: 這應該是TURE或者FALES的,我們是否希望我們能夠以某種方式獲得從這個掃描儀發現的憑證會話。
示例1( Metasploit::Framework::LoginScanner::FTP)
~~~
DEFAULT_PORT = 21
LIKELY_PORTS = [ DEFAULT_PORT, 2121 ]
LIKELY_SERVICE_NAMES = [ 'ftp' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
~~~
示例2( Metasploit::Framework::LoginScanner::SMB)
~~~
CAN_GET_SESSION = true
DEFAULT_REALM = 'WORKSTATION'
LIKELY_PORTS = [ 139, 445 ]
LIKELY_SERVICE_NAMES = [ "smb" ]
PRIVATE_TYPES = [ :password, :ntlm_hash ]
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
~~~
### 把它們放在一個模塊中
所以,現在你希望有從所有移動部分創建一個LoginScanner的好想法。下一步是在實際模塊中使用全新的LoginScanner。
我們來看看ftp_login模塊:
`def run_host(ip)`
每個Bruteforce/Login模塊都應該是一個掃描器,并且應該使用每個RHOST調用一次的run_host方法。
#### 憑證收集
~~~
cred_collection = Metasploit::Framework::CredentialCollection.new(
blank_passwords: datastore['BLANK_PASSWORDS'],
pass_file: datastore['PASS_FILE'],
password: datastore['PASSWORD'],
user_file: datastore['USER_FILE'],
userpass_file: datastore['USERPASS_FILE'],
username: datastore['USERNAME'],
user_as_pass: datastore['USER_AS_PASS'],
prepended_creds: anonymous_creds
)
~~~
所以在這里我們看到使用數據存儲選項創建CredentialCollection。 我們傳遞了憑證創建的選項,例如密碼表,原始用戶名和密碼,是否嘗試將用戶名作為密碼,以及是否嘗試空白密碼。
你也會注意到這里有個選項prepended_creds。 FTP只是是使用這個的模塊之一,但它通常通過CredentialCollection可以使用。這個選項是一個Metasploit::Framework::Credential 的數組。在使用其他憑證對象前會被使用。FTP使用這個來處理匿名FTP訪問的測試。
#### 初始化掃描
~~~
scanner = Metasploit::Framework::LoginScanner::FTP.new(
host: ip,
port: rport,
proxies: datastore['PROXIES'],
cred_details: cred_collection,
stop_on_success: datastore['STOP_ON_SUCCESS'],
connection_timeout: 30
)
~~~
這里我們實際上創建了我們的Scanner對象。 我們根據模塊已知的數據設置IP和端口。 我們可以從數據存儲中提取任何用戶提供的代理數據。 我們也從數據存儲中取出stop_on_success。 信用詳情對象由我們的cred_collection填充,這將無形地處理我們所有的憑證生成。
這將給我們一個scaner對象 一切準備好了。讓我們開始
#### 掃描代碼塊
~~~
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(
module_fullname: self.fullname,
workspace_id: myworkspace_id
)
if result.success?
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
else
invalidate_login(credential_data)
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
end
end
~~~
這是這件事的真正核心。我們調用掃描!
在我們的掃描儀,并通過一個代碼塊。正如我們之前提到的那樣,掃描器將每個嘗試的Result對象放到該塊中。我們檢查結果的狀態,看看它是否成功。
result對象現在作為一個.to_h方法,它返回一個與我們的憑證創建方法兼容的哈希。我們把這個哈希合并到我們模塊的特定信息和工作區ID中。
在成功的情況下,我們建立一些信息散列并調用create_credential。
這是在metasploit-credential gem下lib / metasploit / credential / creation.rb中找到的一種方法調用Metasploit::Credential::Creation
mixin包含在Report mixin中,所以如果你的模塊包含了mixin,你可以免費獲得這些方法。
create_credential創建一個Metasploit::Credential::Core。
然后,我們把這個核心,服務數據,并與一些額外的數據合并。
這些附加數據包括訪問級別,當前時間(更新在Metasploit::Credential::Login的last_attempted_at),狀態。
完成。對于一個成功 我們把結果輸出到控制臺
對于錯誤的情況,我們調用invalidate_login 方法。這個方法也是來自Creation mixin
這個方法查看credential:service pair.是否有一個login對象存在這個。如果是,我們將它的狀態更新到我們從scaner返回得到的狀態。
這主要是為了具有未嘗試狀態的Post模塊創建的Login對象。
### ftp_login最終圖
綜合起來,我們得到一個新的ftp_login模塊,看起來像這樣:
~~~
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'metasploit/framework/credential_collection'
require 'metasploit/framework/login_scanner/ftp'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Ftp
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
def proto
'ftp'
end
def initialize
super(
'Name' => 'FTP Authentication Scanner',
'Description' => %q{
This module will test FTP logins on a range of machines and
report successful logins. If you have loaded a database plugin
and connected to a database this module will record successful
logins and hosts so you can track your access.
},'Author' => 'todb',
'References' =>
[
[ 'CVE', '1999-0502'] # Weak password
],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(21),
OptBool.new('RECORD_GUEST', [ false, "Record anonymous/guest logins to the database", false])
], self.class)
register_advanced_options(
[
OptBool.new('SINGLE_SESSION', [ false, 'Disconnect after every login attempt', false])
]
)
deregister_options('FTPUSER','FTPPASS') # Can use these, but should use 'username' and 'password'
@accepts_all_logins = {}
end
def run_host(ip)
print_status("#{ip}:#{rport} - Starting FTP login sweep")
cred_collection = Metasploit::Framework::CredentialCollection.new(
blank_passwords: datastore['BLANK_PASSWORDS'],
pass_file: datastore['PASS_FILE'],
password: datastore['PASSWORD'],
user_file: datastore['USER_FILE'],
userpass_file: datastore['USERPASS_FILE'],
username: datastore['USERNAME'],
user_as_pass: datastore['USER_AS_PASS'],
prepended_creds: anonymous_creds
)
scanner = Metasploit::Framework::LoginScanner::FTP.new(
host: ip,
port: rport,
proxies: datastore['PROXIES'],
cred_details: cred_collection,
stop_on_success: datastore['STOP_ON_SUCCESS'],
connection_timeout: 30
)
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(
module_fullname: self.fullname,
workspace_id: myworkspace_id
)
if result.success?
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
else
invalidate_login(credential_data)
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
end
end
end
# Always check for anonymous access by pretending to be a browser.
def anonymous_creds
anon_creds = [ ]
if datastore['RECORD_GUEST']
['IEUser@', 'User@', 'mozilla@example.com', 'chrome@example.com' ].each do |password|
anon_creds << Metasploit::Framework::Credential.new(public: 'anonymous', private: password)
end
end
anon_creds
end
def test_ftp_access(user,scanner)
dir = Rex::Text.rand_text_alpha(8)
write_check = scanner.send_cmd(['MKD', dir], true)
if write_check and write_check =~ /^2/
scanner.send_cmd(['RMD',dir], true)
print_status("#{rhost}:#{rport} - User '#{user}' has READ/WRITE access")
return 'Read/Write'
else
print_status("#{rhost}:#{rport} - User '#{user}' has READ access")
return 'Read-only'
end
end
end
~~~
- Home
- 開始使用
- 安裝metasploit開發環境
- 使用metasploit
- 使用git
- 報告一個bug
- 貢獻代碼
- 貢獻給metasploit
- 創建一個loginscans Metasploit模塊
- 接受模塊和增強功能的指導
- 常見的Metasploit模塊代碼錯誤
- 樣式提示
- metasploit提交者
- metasploit開發
- 為什么是ruby
- 樣式提示
- 如何開始寫一個exploit
- 如何開始寫一個輔助模塊
- 如何開始寫一個post模塊
- 如何開始寫一個Meterpreter腳本
- 載入外部模塊
- exploit rank
- Metasploit模塊引用標識符
- 怎么在你的exploit中確認window補丁程序級別
- 如何使用filedropper清理文件
- 如何棄用metasploit模塊
- 如何在模塊開發中報告或儲存數據
- 在metasploit如何使用日志
- 如何在metasploit對JavaScript進行混淆
- 如何解析一個http響應
- 如何使用HTTPClient發送HTTP請求
- 如何使用命令階段
- 如何使用數據儲存選項
- 如何在window后期開發中使用railgun
- 如何在exploit中使用powershell
- 如何使用PhpEXE來利用任意文件上傳漏洞
- 如何使用FILEFORMAT mixin創建一個文件格式exploit
- 如何使用BrowserExploitServer編寫一個瀏覽器exploit
- 如何使用HttpServer編寫瀏覽器exploit
- 如何編寫一個check()方法
- 如何使用Seh mixin來利用異常處理程序
- 如何在Windows上使用WbemExec進行寫入權限攻擊
- 如何使用httpserver和httpclient編寫一個模塊
- payloads如何工作
- 如何免殺
- 如何正確使用metasploit模塊
