1.只允许三次密码尝试;
2.在允许接下来的三次尝试之前需要经过15分钟的时间;
3.密码或密钥是一长串毫无意义的字母、数字和特殊符号。
字典攻击是一种入侵受密码保护的计算机、网络或其他IT资源的方法,它通过系统地尝试将字典中的每个单词作为密码输入来实现。此外,字典攻击还可用于查找解密加密消息或文档所需的密钥。字典攻击之所以有效,是因为许多用户和企业仍然坚持使用常见的单词作为密码。然而,对于使用多个单词组成的复杂密码或包含大小写字母和随机数字组合的密码,字典攻击通常不会成功。
在密码要求较高的系统中,暴力攻击有时可能是有效的。这种方法涉及测试字符和空格的每个可能组合,直到达到一定的最大长度。然而,暴力攻击可能需要很长时间才能成功。相比之下,强随机密码很难被预测,而且它们极不可能包含在预定的密码库中。由于字典攻击的猜测尝试仅限于预先选择的列表,因此基本上不可能破解不可预测的密码。
字典攻击的工作原理是利用预先选择的单词和短语库来猜测可能的密码。它的操作假设用户倾向于从基本的密码列表中选择密码,例如“password”、“123abc”和“123456”。这些列表可能会变得相当庞大,手动处理和测试所有这些密码是不切实际的。因此,通常需要使用密码字典或其他暴力攻击工具来加快该过程。
字典攻击的实施方式取决于攻击者登录的帐户、网络或设备是联机还是脱机。在联机攻击中,攻击者必须注意尝试的密码次数,以避免被站点管理员、帐户经理、用户或入侵检测系统检测到,或者受到密码尝试限制的影响。如果发生上述情况之一,系统可能会将攻击者锁定。对于离线攻击,黑客可以尝试的密码数量几乎没有限制,但是执行脱机攻击需要访问密码存储文件。
与字典攻击相比,暴力攻击的主要区别在于尝试的密码排列次数。
为了保护自己免受字典攻击,可以通过限制在给定时间段内允许的尝试次数,并明智地选择密码或密钥,将密码或解密密钥的攻击漏洞降至接近零。一种使系统免受字典攻击和暴力攻击的方法是满足以下三个条件:
...
/* 结构,包含线程的所有必要数据,在main中创建并作为参数传递给所有线程 */
struct thread_shared_data{
char** dict_ptr;
char** pass_ptr;
char** br_pass_ptr;
bool* is_cracked_ptr;
unsigned long dict_size;
unsigned long pass_size;
unsigned long br_pass_size;
unsigned int basic_thread_count;
unsigned int two_word_thread_count;
};
/* 退出应用程序的函数,也是SIGINT的处理程序(CTRL+C) */
void quit_program()
{
printf("nQuitingn");
running = false;
show_stats = true;
pthread_cond_broadcast(&pass_cracked_cv);
}
/* 使使用者线程打印统计信息的函数,也是SIGHUP的处理程序 */
void print_statistics()
{
show_stats = true;
pthread_mutex_lock(&cond_mutex);
pthread_cond_broadcast(&pass_cracked_cv);
pthread_mutex_unlock(&cond_mutex);
}
/* 函数,该函数使用MD5算法对给定字符串进行散列 */
void md5_hash(char * in_str, char ** out_str)
{
if(*out_str != NULL) free(*out_str);
*out_str = (char *)malloc(33*sizeof(char));
unsigned char digest[16];
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, in_str, strlen(in_str));
MD5_Final(digest, &ctx);
for(int n = 0; n < 16; ++n)
sprintf(&(*out_str)[n*2],"x", (unsigned int)digest[n]);
}
void *consumer_thread(void *arg);
void check_passwords(char * created_pass, struct thread_shared_data * my_tsd, unsigned int id);
void change_string_by_id(unsigned int id, char ** str);
void reset_thread(unsigned int id, unsigned long * num, bool * first_loop, unsigned long * i);
void *cracking_thread_basic(void *arg);
void *cracking_thread_two_words(void *arg);
void *cracking_thread_numbers(void *arg);
int read_file(char * file_name, char *** container_ptr, unsigned long * size);
...
int main(int argc, char * argv[])
{
...
/* 检查是否给出了参数 */
if(argc <= 1)
{
printf("main: passwords_file argument not givenn");
printf(USAGE);
exit(1);
}
/* 把字典读入存储器 */
/* 检查参数中是否提供了字典 */
if(argc >= 3)
{
if(read_file(argv[2], &tsd.dict_ptr, &tsd.dict_size) == -1)
{
printf("main: dictionary file: %s not foundn", argv[2]);
exit(1);
}
}
else /* 读取默认词典 */
if(read_file(DEFAULT_DICTIONARY, &tsd.dict_ptr, &tsd.dict_size) == -1)
{
printf("main: default dictionary file: %s not foundn", DEFAULT_DICTIONARY);
printf(USAGE);
exit(1);
}
/* 将散列密码读入内存 */
if(read_file(argv[1], &tsd.pass_ptr, &tsd.pass_size) == -1)
{
printf("main: passwords file: %s not foundn", argv[1]);
exit(1);
}
else /* allocate是密码破解的数组,并将所有位初始化为零==false */
tsd.is_cracked_ptr = (bool *)calloc(tsd.pass_size, sizeof(bool));
/* 信号处理 */
signal(SIGINT, quit_program);
signal(SIGHUP, print_statistics);
/* 创建线程并初始化互斥、cond值和读写锁 */
...
for(int i = 2; i < NUM_THREADS; i+=2)
{
pthread_create(&threads[i], NULL, cracking_thread_basic, (void *) &tsd);
pthread_create(&threads[i + 1], NULL, cracking_thread_two_words, (void *) &tsd);
}
...
while(running)
{
printf("main: waiting for input:n");
scanf("%s", input);
if(!strcmp(input, "exit")) quit_program();
else if(!strcmp(input, "stats")) print_statistics();
else
{
if(read_file(input, &tmp_ptr, &tmp_size) == -1)
printf("main: passwords file %s not foundn", input);
else
{
/* reset */
printf("main: new passwords file loadedn");
reset = true;
pthread_rwlock_wrlock(&tsd_rwlock);
printf("main: resetn");
print_statistics();
for(unsigned long i = 0; i < tsd.pass_size; i++) free(tsd.pass_ptr[i]);
free(tsd.pass_ptr);
for(int i = 0; i < tsd.br_pass_size; i++) free(tsd.br_pass_ptr[i]);
free(tsd.br_pass_ptr);
tsd.br_pass_ptr = NULL;
tsd.pass_ptr = tmp_ptr;
tsd.pass_size = tmp_size;
tmp_ptr = NULL;
free(tsd.is_cracked_ptr);
tsd.is_cracked_ptr = (bool *)calloc(tsd.pass_size, sizeof(bool));
tsd.br_pass_size = 0;
printf("main: startn");
reset = false;
pthread_rwlock_unlock(&tsd_rwlock);
}
}
}
free(input);
/* 清理程序 */
pthread_mutex_destroy(&count_mutex);
pthread_mutex_destroy(&cond_mutex);
pthread_cond_destroy(&pass_cracked_cv);
pthread_rwlock_destroy(&tsd_rwlock);
printf("main: freeing memoryn");
/* 免费字典数组 */
for(unsigned long i = 0; i < tsd.dict_size; i++)
free(tsd.dict_ptr[i]);
free(tsd.dict_ptr);
/* 免费密码数组 */
if(tsd.pass_ptr != NULL)
{
for(unsigned long i = 0; i < tsd.pass_size; i++)
free(tsd.pass_ptr[i]);
free(tsd.pass_ptr);
}
/* 免费破解密码数组 */
if(tsd.br_pass_ptr != NULL)
for(unsigned long i = 0; i < tsd.br_pass_size; i++)
free(tsd.br_pass_ptr[i]);
...
}
垃圾邮件发送者经常使用字典攻击的形式。他们会将邮件发送到由单词或名称组成的电子邮件地址,后跟@符号和特定域的名称。通常,一长串给定的名字,如zhangsan、xiaowu、lilei或lisi,再加上域名,都是成功的。
密码破解!字典攻击(C/C++代码实现)
应用程序的第一个参数是带有散列密码的文件,第二个参数是可选的字典文件。
编译并运行:
如果您需要完整的源代码,请添加微信号(c17865354792)。
该程序创建了8个线程,第一个线程(使用者)接收来自破解线程的破解密码,并将其打印出来。该线程通过条件值与其他线程进行通信。其他线程使用给定或默认字典生成密码,每个线程使用不同的方法生成密码。三个基本的破解线程根据不同的情况创建密码,并在生成的单词之前或之后添加数字。另外三个线程生成由“”、“2”和“4”分隔的两个单词密码,或者不生成任何内容,其修改方式与基本线程相同。最后一个线程生成数字密码。主循环读取用户输入。
总结起来,在网络攻击中,黑客会反复尝试以其他用户身份登录。如果黑客有一个可能的密码列表,字典攻击是最有效的方法。然而,由于这可能是一个耗时的过程,管理员或用户自己有可能在黑客企图破解代码之前检测到黑客。另一方面,离线攻击的特点是对密码的尝试次数没有网络限制。黑客的方法是从他们试图强制访问的系统中获取一个带有密码的文件。这种类型的字典攻击比在线方法更复杂,但一旦黑客获得正确的密码,他们就可以在没有被注意到的情况下登录。
欢迎关注微信公众号【程序猿编码】