字典攻击是一种通过系统地将字典中的每个单词作为密码输入,从而侵入受密码保护的计算机、网络或其他IT资源的方法。字典攻击也可以用于查找解密加密消息或文档所需的密钥。
字典攻击之所以有效,是因为许多计算机用户和企业坚持使用普通单词作为密码。这些攻击通常针对使用多字密码的系统不成功,也经常针对由大小写字母和随机组合的数字组成的密码不成功。
在密码要求很高的系统中,暴力攻击方法有时是有效的,在这种方法中,字符和空格的每一个可能组合都要测试到一定的最大长度。然而,暴力攻击可能需要很长时间才能产生结果。
强随机密码无法轻易预测,而且它们极不可能包含在预定的密码库中。由于字典攻击的猜测尝试仅限于预先选择的列表,因此基本上不可能破解不可预测的密码。
字典攻击是如何工作的?
字典攻击使用预先选择的单词和短语库来猜测可能的密码。它的操作假设用户倾向于从基本的密码列表中提取,如“password”、“123abc”和“123456”
这些列表不像其他暴力攻击那样广泛,但它们可能会变得相当大。手动处理和测试所有这些密码不是一种实用的方法。因此,通常需要额外的技术来加快该过程。攻击者使用支持程序,如密码字典或其他暴力攻击工具。
字典攻击的实施方式取决于攻击者登录的帐户、网络或设备是联机还是脱机。在在线攻击中,攻击者必须注意他们可以用来猜测正确密码的尝试次数。经过一定次数的尝试后,站点管理员、帐户经理、用户或入侵检测系统可能会检测到攻击,或者密码尝试限制可能会发挥作用。如果发生上述任何一种情况,系统都可以将攻击者锁定。
使用较短优先级的可能密码列表的字典攻击可能更成功。老练的黑客还可以禁用检测功能或密码尝试限制。
对于离线攻击,黑客可以尝试的密码数量几乎没有限制。但是,执行脱机攻击需要从系统访问密码存储文件。只有这样,才能在离线环境中发起字典攻击。
暴力攻击与字典攻击
暴力攻击和字典攻击的主要区别在于尝试的密码排列次数。
如何保护自己免受字典攻击
通过限制给定时间段内允许的尝试次数并明智地选择密码或密钥,可以将密码或解密密钥攻击的漏洞降至接近零。一种使系统免受字典攻击并实际上免受暴力攻击的方法需要以下三个条件:
1.只允许三次密码尝试;
2.在允许接下来的三次尝试之前需要经过15分钟的时间;
3.密码或密钥是一长串毫无意义的字母、数字和特殊符号。
垃圾邮件发送者经常使用字典攻击的形式。邮件会发送到由单词或名称组成的电子邮件地址,后跟@符号和特定域的名称。一长串给定的名字,如zhangsan、xiaowu、lilei或lisi再加上域名,通常都是成功的。
密码破解!字典攻击(C/C++代码实现)
应用程序将带有散列密码的文件作为第一个参数和可选的第二个参数字典文件。
...
/* 结构,包含线程的所有必要数据,在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("\nQuiting\n");
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],"%02x", (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 given\n");
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 found\n", argv[2]);
exit(1);
}
}
else /* 读取默认词典 */
if(read_file(DEFAULT_DICTIONARY, &tsd.dict_ptr, &tsd.dict_size) == -1)
{
printf("main: default dictionary file: %s not found\n", DEFAULT_DICTIONARY);
printf(USAGE);
exit(1);
}
/* 将散列密码读入内存 */
if(read_file(argv[1], &tsd.pass_ptr, &tsd.pass_size) == -1)
{
printf("main: passwords file: %s not found\n", 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 found\n", input);
else
{
/* reset */
printf("main: new passwords file loaded\n");
reset = true;
pthread_rwlock_wrlock(&tsd_rwlock);
printf("main: reset\n");
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: start\n");
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 memory\n");
/* 免费字典数组 */
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]);
...
}
编译运行:
If you need the complete source code, please add the WeChat number (c17865354792)
它总共创建了8个线程,第一个线程(使用者)接收来自破解线程的破解密码,然后打印它,这个线程通过条件值与其他线程通信。其他线程使用给定或默认字典生成密码,每个线程使用不同的方法生成密码。
三个基本的破解线程根据后一种不同的情况创建密码,并在生成的单词之前或之后添加数字。另外三个线程生成由“”、“2”、“4”分隔的两个单词密码,或者什么都不生成,单词的修改与基本线程中的相同。最后一个线程生成数字密码。主循环正在读取用户输入。
总结
在网络攻击中,黑客会反复尝试以其他用户身份登录。如果黑客有一个可能的密码列表,这种方法效果最好。然而,由于这可能是一个耗时的过程,黑客有可能在代码被破解之前被管理员或用户自己检测到黑客企图。
另一方面,离线攻击的特点是,对密码的尝试次数没有网络限制。黑客的方法是从他们试图强制访问的系统中获取一个带有密码的文件。这样,这是一种比在线方法更复杂的字典攻击类型。但一旦他们有了正确的密码,他们就可以在没有人注意到的情况下登录。
Welcome to follow WeChat official account【程序猿编码】