• 利用Openssl写一个简陋的https劫持


    背景

    前面说过可以使用WinDivert+MITM搞一手流量透明,现在转发流量的项目已经有了,虽然只有第一部分(后面的代码分析可能会鸽了,代码已经看完了也大概知道了原理,但是不想写),然后需要知道https流量劫持怎么处理,于是在互联网的帮助下找到了这个,虽然他也是转发,但是原文已经找不到了,所以象征的贴一下,还有一个是模拟ssl握手的项目

    开整

    这部分涉及到很多知识,可能会提及,也可能不会,不会的可能比较大。

    下面是https代理的原理,如果你使用windows下面的internet setting设置,主机会发送一个connect请求,告诉代理服务器,我要请求的域名是什么。

     前面的流量转发中是直接重定向了443端口的流量,而不是使用代理的模式,这里是不会有connect请求的,那mitm怎么知道我要访问的网站是什么?

    来看看使用windows代理时的情况,首先是128给144发了一个connect请求,告诉144我要找百度,然后代理服务器知道它要找百度,于是问网关,可以告诉弟弟百度的地址吗?之后拿着这个地址去和百度建立连接。

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

    http://p8.qhimg.com/t01c852271242287cd3.png

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

    好了,前面说了一堆,主要想表达的是需要对tls的servername进行解析得到对应的域名,这里由于tls的结构没有现成的,所以很随便的获取到对应的域名,相当不规范。

    1. //获取访问的域名
    2. int size = recv(server_sock, buf, 0x100, MSG_PEEK);
    3. for (int i = 0x50; i < size; i++)//
    4. {
    5. int len = buf[i];
    6. if (len&& len < size - 0x50)
    7. {
    8. if (strlen(buf + i + 1) == len)
    9. {
    10. struct hostent* phost = gethostbyname(buf + i + 1);
    11. if (phost)
    12. {
    13. strcpy(hostName, buf + i + 1);
    14. int count = 0;
    15. int len = strlen(hostName) - 1;
    16. for (int j = len; j > 0; j--)
    17. {
    18. if (hostName[j] == '.')
    19. count++;
    20. if (count == 2)
    21. {
    22. pHostName = hostName + j + 1;
    23. break;
    24. }
    25. }
    26. if (count == 1)
    27. {
    28. pHostName = hostName;
    29. }
    30. SOCKADDR_IN client_sin;
    31. memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
    32. memcpy(&client_sin.sin_addr, phost->h_addr_list[0], phost->h_length);
    33. strcpy(target_host, inet_ntoa(client_sin.sin_addr));
    34. break;
    35. }
    36. }
    37. }
    38. }
    39. if (!pHostName || !pHostName[0] )
    40. {
    41. printf("anlyze domian failed !\n");
    42. return;
    43. }

    证书的问题

    这里需要注意的是,如果直接拿到这个代码去搞,会被chrome警告为不可信链接,需要在源主机安装自己的根证书,具体根证书和服务器证书看这

    好了,默认大家都知道根证书和服务器证书的关系,但是就算你安装根证书,还是会有问题,一样会被chrome警告。

     那怎么玩?主要是chrome比别人要严格一点,它还会校验dns的域名信息,你好需要加上下面的拓展信息就行了

    1. keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    2. extendedKeyUsage = serverAuth, clientAuth
    3. subjectAltName=@SubjectAlternativeName
    4. [ SubjectAlternativeName ]
    5. DNS.1=baidu.com
    6. DNS.2=www.baidu.com

    openssl x509 。。。。。。。。最后加上这个 -extfile http.ext

    1. //对指定域名生成证书,就第一次需要生成,之后继续沿用就行了
    2. char tmp[0x100] = { 0 };
    3. char CERT_FILE[0x100] = { 0 };
    4. sprintf(CERT_FILE, "server%s.crt", pHostName);
    5. EnterCriticalSection(&g_critical);
    6. if (OpenFile(CERT_FILE, (LPOFSTRUCT)tmp, OF_EXIST) == -1)
    7. {
    8. LeaveCriticalSection(&g_critical);
    9. //生成证书文件,使用命令行的方式
    10. //首先写文件到http.ext里面
    11. char httpExtPath[0x100] = { 0 };
    12. sprintf(httpExtPath, "http%s.ext", pHostName);
    13. CopyFile("http.ext", httpExtPath, FALSE);
    14. FILE* fp = fopen(httpExtPath, "a+");
    15. fprintf(fp, "DNS.1=%s\n", pHostName);
    16. fprintf(fp, "DNS.2=*.%s", pHostName);
    17. fclose(fp);
    18. char ServerCrt[0x100] = { 0 };
    19. //openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile http.ext
    20. 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);
    21. WinExec(ServerCrt, FALSE);
    22. }
    23. else
    24. {
    25. LeaveCriticalSection(&g_critical);
    26. }

    生成证书的方式,这里会给每一个域名发一个证书,考虑到访问同一个域名的问题,这里整了一个临界区。

    1. openssl genrsa -out server.key 1024
    2. openssl req -new -key server.key -out server.csr
    3. openssl genrsa -out ca.key 1024
    4. openssl req -new -key ca.key -out ca.csr
    5. openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt
    6. 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直接生成一个证书,还不是因为菜,所以只能效率低就低吧,能跑就行。

    代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #pragma comment(lib, "libeay32.lib")
    7. #pragma comment(lib, "ssleay32.lib")
    8. #pragma comment(lib, "Ws2_32.lib")
    9. #define KEY_FILE "server.key"
    10. #define MAX_USER 50 //最大鏈接量
    11. struct timeval timeout = { 0, 1000000 };
    12. CRITICAL_SECTION g_critical;
    13. SOCKET Server_Socket_Init()
    14. {
    15. WSADATA wsa;
    16. WSAStartup(0x0202, &wsa);
    17. SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    18. if (INVALID_SOCKET == sock)
    19. {
    20. printf("Create socket as a server error");
    21. return INVALID_SOCKET;
    22. }
    23. int on = 1;
    24. if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)& on, sizeof(on)) < 0)
    25. {
    26. printf("reuseaddr error");
    27. }
    28. SOCKADDR_IN server_sin;
    29. memset((void*)& server_sin, 0, sizeof(SOCKADDR_IN));
    30. server_sin.sin_family = AF_INET;
    31. server_sin.sin_addr.S_un.S_addr = INADDR_ANY;
    32. server_sin.sin_port = htons(8080);
    33. int ret = bind(sock, (struct sockaddr*) & server_sin, sizeof(SOCKADDR_IN));
    34. if (SOCKET_ERROR == ret)
    35. {
    36. shutdown(sock, 2);
    37. printf("bind error: %d\n", GetLastError());
    38. return INVALID_SOCKET;
    39. }
    40. ret = listen(sock, MAX_USER);
    41. if (SOCKET_ERROR == ret)
    42. {
    43. printf("listen error.\n");
    44. return INVALID_SOCKET;
    45. }
    46. printf("Listening on all local ip address, waiting for connect...\n");
    47. return sock;
    48. }
    49. void SSL_Error(char* custom_string) {
    50. char error_buffer[256] = { 0 };
    51. printf("%s, ", custom_string);
    52. ERR_error_string(ERR_get_error(), error_buffer);
    53. printf("%s\n", error_buffer);
    54. }
    55. SSL* Server_SSL_Init(char * CERT_FILE) {
    56. // 加载SSL环境
    57. SSL_CTX* server_ctx = SSL_CTX_new(SSLv23_server_method());
    58. if (NULL == server_ctx)
    59. {
    60. SSL_Error("Init ssl ctx error");
    61. return NULL;
    62. }
    63. // 设置证书文件的口令
    64. //SSL_CTX_set_default_passwd_cb_userdata(server_ctx, "木有密碼");
    65. // 加载证书
    66. if (SSL_CTX_use_certificate_file(server_ctx, CERT_FILE, SSL_FILETYPE_PEM) <= 0)
    67. {
    68. SSL_Error("Load cert file error");
    69. return NULL;
    70. }
    71. // 加载私钥
    72. if (SSL_CTX_use_PrivateKey_file(server_ctx, KEY_FILE, SSL_FILETYPE_PEM) <= 0)
    73. {
    74. SSL_Error("Load cert file error");
    75. return NULL;
    76. }
    77. // 检查私钥和证书是否匹配
    78. if (!SSL_CTX_check_private_key(server_ctx))
    79. {
    80. printf("Private key does not match the certificate public key\n");
    81. return NULL;
    82. }
    83. SSL* ssl = SSL_new(server_ctx);
    84. if (NULL == ssl)
    85. {
    86. SSL_Error("Create ssl error");
    87. return NULL;
    88. }
    89. return ssl;
    90. }
    91. SOCKET Client_Socket_Init(char * target_host) {
    92. SOCKET client_sock = socket(AF_INET, SOCK_STREAM, 0);
    93. if (INVALID_SOCKET == client_sock)
    94. {
    95. printf("create socket as a client error.\n");
    96. return -1;
    97. }
    98. SOCKADDR_IN client_sin;
    99. memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
    100. client_sin.sin_family = AF_INET;
    101. client_sin.sin_addr.S_un.S_addr = inet_addr(target_host);
    102. client_sin.sin_port = htons(443);
    103. int ret = connect(client_sock, (struct sockaddr*) & client_sin, sizeof(SOCKADDR_IN));
    104. if (SOCKET_ERROR == ret)
    105. {
    106. printf("connect to real server %s error \n", target_host);
    107. return INVALID_SOCKET;
    108. }
    109. printf("connect to real server success!\n");
    110. return client_sock;
    111. }
    112. SSL* Client_SSL_Init() {
    113. SSL_CTX* client_ctx;
    114. client_ctx = SSL_CTX_new(SSLv23_client_method());
    115. if (NULL == client_ctx) {
    116. SSL_Error((char *)"Init ssl ctx error");
    117. return NULL;
    118. }
    119. SSL* ssl = SSL_new(client_ctx);
    120. if (NULL == ssl) {
    121. SSL_Error((char *)"Create ssl error");
    122. return NULL;
    123. }
    124. return ssl;
    125. }
    126. typedef struct _CONDATA
    127. {
    128. char target_host[20];
    129. SOCKET server_sock;
    130. SSL* server_ssl;
    131. }CONDATA,*PCONDATA;
    132. typedef struct _TRANSFER
    133. {
    134. SSL* client_ssl;
    135. SSL* server_ssl;
    136. }TRANSFER, * PTRANSFER;
    137. void transfer(LPVOID argument)
    138. {
    139. fd_set fd_read;
    140. timeval time_out;
    141. char buffer[4096] = { 0 };
    142. PTRANSFER pData = (PTRANSFER)argument;
    143. int socket_to_client = SSL_get_fd(pData->client_ssl);
    144. int socket_to_server = SSL_get_fd(pData->server_ssl);
    145. while (TRUE)
    146. {
    147. int max;
    148. FD_ZERO(&fd_read);
    149. FD_SET(socket_to_server, &fd_read);
    150. FD_SET(socket_to_client, &fd_read);
    151. max = socket_to_client > socket_to_server ? socket_to_client + 1
    152. : socket_to_server + 1;
    153. int ret = select(max, &fd_read, NULL, NULL, &timeout);
    154. if (ret < 0) {
    155. SSL_Error("Fail to select!");
    156. break;
    157. }
    158. else if (ret == 0) {
    159. continue;
    160. }
    161. if (FD_ISSET(socket_to_client, &fd_read)) {
    162. memset(buffer, 0, sizeof(buffer));
    163. ret = SSL_read(pData->client_ssl, buffer, sizeof(buffer));
    164. if (ret > 0) {
    165. if (ret != SSL_write(pData->server_ssl, buffer, ret)) {
    166. SSL_Error("Fail to write to server!");
    167. break;
    168. }
    169. else {
    170. //printf("%s\n", buffer);
    171. }
    172. }
    173. else {
    174. SSL_Error("Fail to read from client!");
    175. break;
    176. }
    177. }
    178. if (FD_ISSET(socket_to_server, &fd_read)) {
    179. memset(buffer, 0, sizeof(buffer));
    180. ret = SSL_read(pData->server_ssl, buffer, sizeof(buffer));
    181. if (ret > 0) {
    182. if (ret != SSL_write(pData->client_ssl, buffer, ret)) {
    183. SSL_Error("Fail to write to client!");
    184. break;
    185. }
    186. else {
    187. //printf("%s\n", buffer);
    188. }
    189. }
    190. else {
    191. SSL_Error("Fail to read from server!");
    192. break;
    193. }
    194. }
    195. }
    196. SSL_shutdown(pData->server_ssl);
    197. SSL_shutdown(pData->client_ssl);
    198. SSL_free(pData->server_ssl);
    199. SSL_free(pData->client_ssl);
    200. free(argument);
    201. }
    202. void WINAPI GetHttpsPacket(LPVOID argument) {
    203. char *pHostName = NULL;
    204. char buf[0x100] = { 0 };
    205. char hostName[0x100] = { 0 };
    206. char target_host[20] = { 0 };
    207. SOCKET server_sock = (SOCKET)(*(SOCKET*)argument);
    208. __try
    209. {
    210. //获取访问的域名
    211. int size = recv(server_sock, buf, 0x100, MSG_PEEK);
    212. for (int i = 0x50; i < size; i++)//
    213. {
    214. int len = buf[i];
    215. if (len&& len < size - 0x50)
    216. {
    217. if (strlen(buf + i + 1) == len)
    218. {
    219. struct hostent* phost = gethostbyname(buf + i + 1);
    220. if (phost)
    221. {
    222. strcpy(hostName, buf + i + 1);
    223. int count = 0;
    224. int len = strlen(hostName) - 1;
    225. for (int j = len; j > 0; j--)
    226. {
    227. if (hostName[j] == '.')
    228. count++;
    229. if (count == 2)
    230. {
    231. pHostName = hostName + j + 1;
    232. break;
    233. }
    234. }
    235. if (count == 1)
    236. {
    237. pHostName = hostName;
    238. }
    239. SOCKADDR_IN client_sin;
    240. memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
    241. memcpy(&client_sin.sin_addr, phost->h_addr_list[0], phost->h_length);
    242. strcpy(target_host, inet_ntoa(client_sin.sin_addr));
    243. break;
    244. }
    245. }
    246. }
    247. }
    248. if (!pHostName || !pHostName[0] )
    249. {
    250. printf("anlyze domian failed !\n");
    251. return;
    252. }
    253. //对指定域名生成证书,就第一次需要生成,之后继续沿用就行了
    254. char tmp[0x100] = { 0 };
    255. char CERT_FILE[0x100] = { 0 };
    256. sprintf(CERT_FILE, "server%s.crt", pHostName);
    257. EnterCriticalSection(&g_critical);
    258. if (OpenFile(CERT_FILE, (LPOFSTRUCT)tmp, OF_EXIST) == -1)
    259. {
    260. LeaveCriticalSection(&g_critical);
    261. //生成证书文件,使用命令行的方式
    262. //首先写文件到http.ext里面
    263. char httpExtPath[0x100] = { 0 };
    264. sprintf(httpExtPath, "http%s.ext", pHostName);
    265. CopyFile("http.ext", httpExtPath, FALSE);
    266. FILE* fp = fopen(httpExtPath, "a+");
    267. fprintf(fp, "DNS.1=%s\n", pHostName);
    268. fprintf(fp, "DNS.2=*.%s", pHostName);
    269. fclose(fp);
    270. char ServerCrt[0x100] = { 0 };
    271. //openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile http.ext
    272. 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);
    273. WinExec(ServerCrt, FALSE);
    274. }
    275. else
    276. {
    277. LeaveCriticalSection(&g_critical);
    278. }
    279. SSL* server_ssl = Server_SSL_Init(CERT_FILE);
    280. if (NULL == server_ssl)//初始化ssl失败
    281. {
    282. SSL_Error("server_ssl error\n");
    283. return;
    284. }
    285. SSL_set_fd(server_ssl, server_sock);
    286. int ret = SSL_accept(server_ssl);
    287. if (-1 == ret)//接受数据失败
    288. {
    289. SSL_Error("server_ssl SSL_accept error\n");
    290. return;
    291. }
    292. // 生成SSL,作为客户端
    293. SSL* client_ssl = Client_SSL_Init();
    294. if (NULL == client_ssl)
    295. {
    296. SSL_Error((char*)"client_ssl error");
    297. return;
    298. }
    299. //向真实主机发起请求
    300. SOCKET client_sock = Client_Socket_Init(target_host);
    301. if (INVALID_SOCKET == client_sock)
    302. {
    303. SSL_Error((char*)"client_sock error");
    304. return;
    305. }
    306. SSL_set_fd(client_ssl, client_sock);
    307. ret = SSL_connect(client_ssl);
    308. if (-1 == ret)
    309. {
    310. SSL_Error("SSL_connect error\n");
    311. return;
    312. }
    313. PTRANSFER data = (PTRANSFER)malloc(sizeof(TRANSFER));
    314. if (data)
    315. {
    316. data->client_ssl = client_ssl;
    317. data->server_ssl = server_ssl;
    318. }
    319. HANDLE h_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)transfer, data, 0, NULL);
    320. if (h_thread)
    321. CloseHandle(h_thread);
    322. }
    323. __except (EXCEPTION_EXECUTE_HANDLER) {}
    324. return;
    325. }
    326. int main()
    327. {
    328. InitializeCriticalSection(&g_critical);//初始化临界区
    329. SOCKET server_sock = Server_Socket_Init();//初始化一個監聽443端口套接字
    330. if (INVALID_SOCKET == server_sock)
    331. return -1;
    332. //初始化openssl
    333. SSL_library_init();
    334. SSL_load_error_strings();
    335. // 开始接受连接
    336. SOCKET client_sock; // 来自被攻击者的socket
    337. SOCKADDR_IN client_sin;
    338. int sin_len = sizeof(SOCKADDR_IN);
    339. memset((void*)& client_sin, 0, sizeof(SOCKADDR_IN));
    340. while (TRUE) {
    341. client_sock = accept(server_sock, (struct sockaddr*) & client_sin, &sin_len);
    342. printf("connect from %s\n", inet_ntoa(client_sin.sin_addr));
    343. if (SOCKET_ERROR == client_sock)
    344. {
    345. printf("accept error.\n");
    346. continue;
    347. }
    348. DWORD thread_id = 0;
    349. HANDLE h_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GetHttpsPacket, (LPVOID)& client_sock, 0, &thread_id);
    350. if (h_thread)
    351. CloseHandle(h_thread);
    352. }
    353. closesocket(server_sock);
    354. WSACleanup();
    355. DeleteCriticalSection(&g_critical);
    356. return 0;
    357. }
  • 相关阅读:
    three.js学习笔记(二十二)——混合HTML和WebGL
    统计字符串中单词的个数--空格间隔
    uniapp搜索框防抖、自动获取焦点
    Meta携手亚马逊共建PyTorch ,抵抗Tensorflow
    网络程序设计——VC的多线程编程(线程与进程)
    [激光器原理与应用-9]: 开关电源主要指标
    Java中的this关键字
    Java中Servlet的生命周期
    使用ceph-deploy部署Ceph集群
    【Solution】一文学会微信扫码登录
  • 原文地址:https://blog.csdn.net/lyshark_lyshark/article/details/126792461