背景
前面说过可以使用WinDivert+MITM搞一手流量透明,现在转发流量的项目已经有了,虽然只有第一部分(后面的代码分析可能会鸽了,代码已经看完了也大概知道了原理,但是不想写),然后需要知道https流量劫持怎么处理,于是在互联网的帮助下找到了这个,虽然他也是转发,但是原文已经找不到了,所以象征的贴一下,还有一个是模拟ssl握手的项目。
开整
这部分涉及到很多知识,可能会提及,也可能不会,不会的可能比较大。
下面是https代理的原理,如果你使用windows下面的internet setting设置,主机会发送一个connect请求,告诉代理服务器,我要请求的域名是什么。

前面的流量转发中是直接重定向了443端口的流量,而不是使用代理的模式,这里是不会有connect请求的,那mitm怎么知道我要访问的网站是什么?
来看看使用windows代理时的情况,首先是128给144发了一个connect请求,告诉144我要找百度,然后代理服务器知道它要找百度,于是问网关,可以告诉弟弟百度的地址吗?之后拿着这个地址去和百度建立连接。

数据传输的方式如下所示,不过原作者是burp suite,这里是mitm。

那直接转发端口的是如何知道访问域名的呢?其实tls中会携带要访问的域名信息,如下所示,所以在tls say hello的时候也同样可以获取到目的域名。

好了,前面说了一堆,主要想表达的是需要对tls的servername进行解析得到对应的域名,这里由于tls的结构没有现成的,所以很随便的获取到对应的域名,相当不规范。
- //获取访问的域名
- int size = recv(server_sock, buf, 0x100, MSG_PEEK);
- for (int i = 0x50; i < size; i++)//
- {
- int len = buf[i];
- if (len&& len < size - 0x50)
- {
- if (strlen(buf + i + 1) == len)
- {
- struct hostent* phost = gethostbyname(buf + i + 1);
- if (phost)
- {
- strcpy(hostName, buf + i + 1);
- int count = 0;
- int len = strlen(hostName) - 1;
- for (int j = len; j > 0; j--)
- {
- if (hostName[j] == '.')
- count++;
- if (count == 2)
- {
- pHostName = hostName + j + 1;
- break;
- }
- }
- if (count == 1)
- {
- pHostName = hostName;
- }
- SOCKADDR_IN client_sin;
- memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
- memcpy(&client_sin.sin_addr, phost->h_addr_list[0], phost->h_length);
- strcpy(target_host, inet_ntoa(client_sin.sin_addr));
- break;
- }
- }
- }
- }
- if (!pHostName || !pHostName[0] )
- {
- printf("anlyze domian failed !\n");
- return;
- }
证书的问题
这里需要注意的是,如果直接拿到这个代码去搞,会被chrome警告为不可信链接,需要在源主机安装自己的根证书,具体根证书和服务器证书看这。
好了,默认大家都知道根证书和服务器证书的关系,但是就算你安装根证书,还是会有问题,一样会被chrome警告。

那怎么玩?主要是chrome比别人要严格一点,它还会校验dns的域名信息,你好需要加上下面的拓展信息就行了
- keyUsage = nonRepudiation, digitalSignature, keyEncipherment
- extendedKeyUsage = serverAuth, clientAuth
- subjectAltName=@SubjectAlternativeName
-
- [ SubjectAlternativeName ]
- DNS.1=baidu.com
- DNS.2=www.baidu.com
openssl x509 。。。。。。。。最后加上这个 -extfile http.ext
- //对指定域名生成证书,就第一次需要生成,之后继续沿用就行了
- char tmp[0x100] = { 0 };
- char CERT_FILE[0x100] = { 0 };
- sprintf(CERT_FILE, "server%s.crt", pHostName);
- EnterCriticalSection(&g_critical);
- if (OpenFile(CERT_FILE, (LPOFSTRUCT)tmp, OF_EXIST) == -1)
- {
- LeaveCriticalSection(&g_critical);
- //生成证书文件,使用命令行的方式
- //首先写文件到http.ext里面
- char httpExtPath[0x100] = { 0 };
- sprintf(httpExtPath, "http%s.ext", pHostName);
- CopyFile("http.ext", httpExtPath, FALSE);
- FILE* fp = fopen(httpExtPath, "a+");
- fprintf(fp, "DNS.1=%s\n", pHostName);
- fprintf(fp, "DNS.2=*.%s", pHostName);
-
- fclose(fp);
-
- char ServerCrt[0x100] = { 0 };
- //openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile http.ext
- sprintf(ServerCrt, "openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server%s.crt -days 3650 -sha256 -extfile http%s.ext", pHostName, pHostName);
- WinExec(ServerCrt, FALSE);
- }
- else
- {
- LeaveCriticalSection(&g_critical);
- }
生成证书的方式,这里会给每一个域名发一个证书,考虑到访问同一个域名的问题,这里整了一个临界区。
- openssl genrsa -out server.key 1024
- openssl req -new -key server.key -out server.csr
- openssl genrsa -out ca.key 1024
- openssl req -new -key ca.key -out ca.csr
- openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
- openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile http.ext
结果
访问百度和QQ都不会提示证书有问题,但是会发现网站启动的很慢,而且这里生成的x509也不是在内存中生成,而是先写到文件在读取,效率会有问题,你问我为什么不使用openssl直接生成一个证书,还不是因为菜,所以只能效率低就低吧,能跑就行。


代码:
- #include
- #include
- #include
- #include
- #include
-
- #pragma comment(lib, "libeay32.lib")
- #pragma comment(lib, "ssleay32.lib")
- #pragma comment(lib, "Ws2_32.lib")
-
-
- #define KEY_FILE "server.key"
- #define MAX_USER 50 //最大鏈接量
-
- struct timeval timeout = { 0, 1000000 };
-
- CRITICAL_SECTION g_critical;
- SOCKET Server_Socket_Init()
- {
- WSADATA wsa;
-
- WSAStartup(0x0202, &wsa);
-
- SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
- if (INVALID_SOCKET == sock)
- {
- printf("Create socket as a server error");
- return INVALID_SOCKET;
- }
-
-
- int on = 1;
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)& on, sizeof(on)) < 0)
- {
- printf("reuseaddr error");
- }
-
- SOCKADDR_IN server_sin;
- memset((void*)& server_sin, 0, sizeof(SOCKADDR_IN));
- server_sin.sin_family = AF_INET;
- server_sin.sin_addr.S_un.S_addr = INADDR_ANY;
- server_sin.sin_port = htons(8080);
-
- int ret = bind(sock, (struct sockaddr*) & server_sin, sizeof(SOCKADDR_IN));
- if (SOCKET_ERROR == ret)
- {
- shutdown(sock, 2);
- printf("bind error: %d\n", GetLastError());
- return INVALID_SOCKET;
- }
-
- ret = listen(sock, MAX_USER);
- if (SOCKET_ERROR == ret)
- {
- printf("listen error.\n");
- return INVALID_SOCKET;
- }
-
- printf("Listening on all local ip address, waiting for connect...\n");
-
- return sock;
- }
-
- void SSL_Error(char* custom_string) {
- char error_buffer[256] = { 0 };
-
- printf("%s, ", custom_string);
- ERR_error_string(ERR_get_error(), error_buffer);
- printf("%s\n", error_buffer);
- }
-
- SSL* Server_SSL_Init(char * CERT_FILE) {
- // 加载SSL环境
- SSL_CTX* server_ctx = SSL_CTX_new(SSLv23_server_method());
- if (NULL == server_ctx)
- {
- SSL_Error("Init ssl ctx error");
- return NULL;
- }
-
- // 设置证书文件的口令
- //SSL_CTX_set_default_passwd_cb_userdata(server_ctx, "木有密碼");
-
- // 加载证书
- if (SSL_CTX_use_certificate_file(server_ctx, CERT_FILE, SSL_FILETYPE_PEM) <= 0)
- {
- SSL_Error("Load cert file error");
- return NULL;
- }
-
- // 加载私钥
- if (SSL_CTX_use_PrivateKey_file(server_ctx, KEY_FILE, SSL_FILETYPE_PEM) <= 0)
- {
- SSL_Error("Load cert file error");
- return NULL;
- }
-
- // 检查私钥和证书是否匹配
- if (!SSL_CTX_check_private_key(server_ctx))
- {
- printf("Private key does not match the certificate public key\n");
- return NULL;
- }
-
- SSL* ssl = SSL_new(server_ctx);
- if (NULL == ssl)
- {
- SSL_Error("Create ssl error");
- return NULL;
- }
-
- return ssl;
- }
-
- SOCKET Client_Socket_Init(char * target_host) {
- SOCKET client_sock = socket(AF_INET, SOCK_STREAM, 0);
-
- if (INVALID_SOCKET == client_sock)
- {
- printf("create socket as a client error.\n");
- return -1;
- }
-
- SOCKADDR_IN client_sin;
- memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
-
- client_sin.sin_family = AF_INET;
- client_sin.sin_addr.S_un.S_addr = inet_addr(target_host);
- client_sin.sin_port = htons(443);
-
- int ret = connect(client_sock, (struct sockaddr*) & client_sin, sizeof(SOCKADDR_IN));
- if (SOCKET_ERROR == ret)
- {
- printf("connect to real server %s error \n", target_host);
- return INVALID_SOCKET;
- }
-
- printf("connect to real server success!\n");
-
- return client_sock;
- }
-
- SSL* Client_SSL_Init() {
- SSL_CTX* client_ctx;
-
- client_ctx = SSL_CTX_new(SSLv23_client_method());
-
- if (NULL == client_ctx) {
- SSL_Error((char *)"Init ssl ctx error");
- return NULL;
- }
-
- SSL* ssl = SSL_new(client_ctx);
- if (NULL == ssl) {
- SSL_Error((char *)"Create ssl error");
- return NULL;
- }
-
- return ssl;
- }
-
- typedef struct _CONDATA
- {
- char target_host[20];
- SOCKET server_sock;
- SSL* server_ssl;
- }CONDATA,*PCONDATA;
- typedef struct _TRANSFER
- {
- SSL* client_ssl;
- SSL* server_ssl;
- }TRANSFER, * PTRANSFER;
-
- void transfer(LPVOID argument)
- {
- fd_set fd_read;
- timeval time_out;
- char buffer[4096] = { 0 };
- PTRANSFER pData = (PTRANSFER)argument;
- int socket_to_client = SSL_get_fd(pData->client_ssl);
- int socket_to_server = SSL_get_fd(pData->server_ssl);
- while (TRUE)
- {
- int max;
-
- FD_ZERO(&fd_read);
- FD_SET(socket_to_server, &fd_read);
- FD_SET(socket_to_client, &fd_read);
- max = socket_to_client > socket_to_server ? socket_to_client + 1
- : socket_to_server + 1;
-
- int ret = select(max, &fd_read, NULL, NULL, &timeout);
- if (ret < 0) {
- SSL_Error("Fail to select!");
- break;
- }
- else if (ret == 0) {
- continue;
- }
- if (FD_ISSET(socket_to_client, &fd_read)) {
- memset(buffer, 0, sizeof(buffer));
- ret = SSL_read(pData->client_ssl, buffer, sizeof(buffer));
- if (ret > 0) {
- if (ret != SSL_write(pData->server_ssl, buffer, ret)) {
- SSL_Error("Fail to write to server!");
- break;
- }
- else {
- //printf("%s\n", buffer);
- }
- }
- else {
- SSL_Error("Fail to read from client!");
- break;
- }
- }
- if (FD_ISSET(socket_to_server, &fd_read)) {
- memset(buffer, 0, sizeof(buffer));
- ret = SSL_read(pData->server_ssl, buffer, sizeof(buffer));
- if (ret > 0) {
- if (ret != SSL_write(pData->client_ssl, buffer, ret)) {
- SSL_Error("Fail to write to client!");
- break;
- }
- else {
- //printf("%s\n", buffer);
- }
- }
- else {
- SSL_Error("Fail to read from server!");
- break;
- }
- }
- }
-
- SSL_shutdown(pData->server_ssl);
- SSL_shutdown(pData->client_ssl);
-
- SSL_free(pData->server_ssl);
- SSL_free(pData->client_ssl);
-
- free(argument);
- }
-
- void WINAPI GetHttpsPacket(LPVOID argument) {
- char *pHostName = NULL;
- char buf[0x100] = { 0 };
- char hostName[0x100] = { 0 };
- char target_host[20] = { 0 };
-
- SOCKET server_sock = (SOCKET)(*(SOCKET*)argument);
- __try
- {
- //获取访问的域名
- int size = recv(server_sock, buf, 0x100, MSG_PEEK);
- for (int i = 0x50; i < size; i++)//
- {
- int len = buf[i];
- if (len&& len < size - 0x50)
- {
- if (strlen(buf + i + 1) == len)
- {
- struct hostent* phost = gethostbyname(buf + i + 1);
- if (phost)
- {
- strcpy(hostName, buf + i + 1);
- int count = 0;
- int len = strlen(hostName) - 1;
- for (int j = len; j > 0; j--)
- {
- if (hostName[j] == '.')
- count++;
- if (count == 2)
- {
- pHostName = hostName + j + 1;
- break;
- }
- }
- if (count == 1)
- {
- pHostName = hostName;
- }
- SOCKADDR_IN client_sin;
- memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
- memcpy(&client_sin.sin_addr, phost->h_addr_list[0], phost->h_length);
- strcpy(target_host, inet_ntoa(client_sin.sin_addr));
- break;
- }
- }
- }
- }
- if (!pHostName || !pHostName[0] )
- {
- printf("anlyze domian failed !\n");
- return;
- }
-
- //对指定域名生成证书,就第一次需要生成,之后继续沿用就行了
- char tmp[0x100] = { 0 };
- char CERT_FILE[0x100] = { 0 };
- sprintf(CERT_FILE, "server%s.crt", pHostName);
- EnterCriticalSection(&g_critical);
- if (OpenFile(CERT_FILE, (LPOFSTRUCT)tmp, OF_EXIST) == -1)
- {
- LeaveCriticalSection(&g_critical);
- //生成证书文件,使用命令行的方式
- //首先写文件到http.ext里面
- char httpExtPath[0x100] = { 0 };
- sprintf(httpExtPath, "http%s.ext", pHostName);
- CopyFile("http.ext", httpExtPath, FALSE);
- FILE* fp = fopen(httpExtPath, "a+");
- fprintf(fp, "DNS.1=%s\n", pHostName);
- fprintf(fp, "DNS.2=*.%s", pHostName);
-
- fclose(fp);
-
- char ServerCrt[0x100] = { 0 };
- //openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile http.ext
- sprintf(ServerCrt, "openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server%s.crt -days 3650 -sha256 -extfile http%s.ext", pHostName, pHostName);
- WinExec(ServerCrt, FALSE);
- }
- else
- {
- LeaveCriticalSection(&g_critical);
- }
-
- SSL* server_ssl = Server_SSL_Init(CERT_FILE);
- if (NULL == server_ssl)//初始化ssl失败
- {
- SSL_Error("server_ssl error\n");
- return;
- }
- SSL_set_fd(server_ssl, server_sock);
- int ret = SSL_accept(server_ssl);
- if (-1 == ret)//接受数据失败
- {
- SSL_Error("server_ssl SSL_accept error\n");
- return;
- }
-
-
- // 生成SSL,作为客户端
- SSL* client_ssl = Client_SSL_Init();
- if (NULL == client_ssl)
- {
- SSL_Error((char*)"client_ssl error");
- return;
- }
- //向真实主机发起请求
- SOCKET client_sock = Client_Socket_Init(target_host);
- if (INVALID_SOCKET == client_sock)
- {
- SSL_Error((char*)"client_sock error");
- return;
- }
- SSL_set_fd(client_ssl, client_sock);
- ret = SSL_connect(client_ssl);
- if (-1 == ret)
- {
- SSL_Error("SSL_connect error\n");
- return;
- }
-
- PTRANSFER data = (PTRANSFER)malloc(sizeof(TRANSFER));
- if (data)
- {
- data->client_ssl = client_ssl;
- data->server_ssl = server_ssl;
- }
- HANDLE h_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)transfer, data, 0, NULL);
- if (h_thread)
- CloseHandle(h_thread);
- }
- __except (EXCEPTION_EXECUTE_HANDLER) {}
-
- return;
- }
-
- int main()
- {
- InitializeCriticalSection(&g_critical);//初始化临界区
- SOCKET server_sock = Server_Socket_Init();//初始化一個監聽443端口套接字
- if (INVALID_SOCKET == server_sock)
- return -1;
-
- //初始化openssl
- SSL_library_init();
- SSL_load_error_strings();
-
-
- // 开始接受连接
- SOCKET client_sock; // 来自被攻击者的socket
- SOCKADDR_IN client_sin;
- int sin_len = sizeof(SOCKADDR_IN);
- memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
-
- while (TRUE) {
- client_sock = accept(server_sock, (struct sockaddr*) & client_sin, &sin_len);
-
- printf("connect from %s\n", inet_ntoa(client_sin.sin_addr));
-
- if (SOCKET_ERROR == client_sock)
- {
- printf("accept error.\n");
- continue;
- }
-
- DWORD thread_id = 0;
- HANDLE h_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GetHttpsPacket, (LPVOID)& client_sock, 0, &thread_id);
-
- if (h_thread)
- CloseHandle(h_thread);
-
-
- }
-
- closesocket(server_sock);
- WSACleanup();
-
- DeleteCriticalSection(&g_critical);
-
- return 0;
- }
