Created
June 14, 2020 23:26
-
-
Save iordic/e62631da9bab829dc87e2d8df4da15e4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Cracker sencillo para ataques mediante diccionario contra ficheros 'shadow' | |
* Copyright (C) 2020 @iordic | |
* | |
* Licencia (GPLv3): | |
* ================= | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |
* | |
* | |
* Descripción: | |
* ============ | |
* se hace uso de la función crypt(3) de la API de linux (del wrapper glibc). Creado con | |
* fines educativos, para ataques de este tipo existen por ahí hay cosas infinitamente | |
* mejores. | |
* | |
* Compilación: | |
* ------------ | |
* gcc -Wall -o <ejecutable a crear> <este fichero>.c -lcrypt | |
* -Wall: opcional, pero nunca viene mal ver los avisos. | |
* -lcrypt: hace falta enlazar esta biblioteca para que funcione. | |
* | |
* Aquí viene toda la información: https://www.man7.org/linux/man-pages/man3/crypt.3.html | |
* | |
* Uso: | |
* ---- | |
* ./<ejecutable> <fichero shadow> <fichero diccionario> | |
* | |
* - Fichero shadow: la copia del fichero /etc/shadow del sistema a crackear. | |
* - Fichero diccionario: un fichero de texto plano (txt) con una contraseña por línea. | |
*/ | |
#define _XOPEN_SOURCE // Viene en la documentación: | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <string.h> | |
#define LINE_MAX_SIZE 256 | |
struct usuario { | |
char nombre[32]; | |
char algoritmo[4]; // Podría ser int, pero una de las posibles opciones es "2a" | |
char sal[20]; | |
char hash[120]; // Completo para comparar salida (con nº de algoritmo y sal) | |
char contrasena[32]; // Algunos algoritmos no tienen límite... | |
}; | |
/* Definición de funciones */ | |
void uso(char *elf_name); | |
int get_number_users(char *fichero); | |
void shadow_parser(char *fichero, struct usuario *usuarios); | |
int main(int argc, char *argv[]) { | |
char *shadow_file = argv[1]; | |
char *dic_path = argv[2]; | |
char str_hash[32]; // Buffer para formatear el hash | |
char *calc_hash; // Hash calculado para la contraseña | |
char contrasena[LINE_MAX_SIZE]; | |
int pass_size; | |
int pass_found; | |
if (argc < 3) { // argumentos insuficientes | |
printf("Faltan argumentos\n"); | |
uso(argv[0]); | |
return -1; | |
} | |
FILE *dic_file = fopen(dic_path, "r"); | |
if (dic_file == NULL) { | |
printf("El fichero %s no existe\n", dic_path); | |
return -1; // El fichero no existe | |
} | |
int n_users = get_number_users(shadow_file); | |
struct usuario *usuarios = malloc(sizeof(struct usuario) * n_users); | |
shadow_parser(shadow_file, usuarios); | |
int i; | |
for (i = 0; i < n_users; i++) { | |
pass_found = 0; | |
// Crea un string formateado para pasarlo como parámetro ($n_algoritmo$sal$) | |
if (sprintf(str_hash, "$%s$%s$", usuarios[i].algoritmo, usuarios[i].sal) <= 0) { | |
printf("Error: no se ha podido obtener hash para: '%s'\n", usuarios[i].nombre); | |
continue; | |
} | |
fseek(dic_file, 0, SEEK_SET); // Para cada usuario posiciona al principio del fichero | |
while (fgets(contrasena, sizeof(contrasena), dic_file)) { | |
pass_size = strlen(contrasena); | |
// Quita el '\n' para que no falle | |
if (pass_size > 0 && contrasena[pass_size - 1] == '\n') contrasena[pass_size - 1] = '\0'; | |
/* Esta es la función de la API de linux para generar los hashes: */ | |
calc_hash = crypt(contrasena, str_hash); | |
if (calc_hash != NULL && !strcmp(calc_hash, usuarios[i].hash)) { | |
printf("Encontrada la contraseña de '%s': '%s'.\n", usuarios[i].nombre, contrasena); | |
// Se guarda en el struct por si se quiere implementar algo más complejo algún día | |
strncpy(usuarios[i].contrasena, contrasena, pass_size); | |
pass_found = 1; | |
break; | |
} | |
} | |
if (!pass_found) | |
printf("No se ha podido encontrar la contraseña para '%s'.\n", usuarios[i].nombre); | |
} | |
fclose(dic_file); | |
free(usuarios); | |
return 0; | |
} | |
void uso(char *elf_name) { | |
printf("Uso: %s <fichero shadow> <fichero diccionario>\n", elf_name); | |
} | |
/* Interpreta el número de usuarios válidos que contiene el fichero tipo shadow */ | |
int get_number_users(char *fichero) { | |
int n = 0; | |
char line[LINE_MAX_SIZE]; | |
FILE *file = fopen(fichero, "r"); | |
if (file == NULL) { | |
return -1; // El fichero no existe | |
} | |
while (fgets(line, sizeof(line), file)) { | |
// Si hay alguno de los dos caracteres en la línea no existe hash, la ignoramos | |
// ("*" para servicios, "!" para usuarios bloqueados) | |
if (strstr(line, "*") != NULL || strstr(line, "!") != NULL) | |
continue; | |
n++; | |
} | |
fclose(file); | |
return n; | |
} | |
/* Lee un fichero de tipo shadow y carga los usuarios válidos a un array de estructuras | |
* válido. | |
*/ | |
void shadow_parser(char *fichero, struct usuario *usuarios) { | |
int index = 0; | |
int attr_pos; | |
int tok_size; | |
char line[LINE_MAX_SIZE]; | |
char *token; | |
char hash[120]; | |
const char sep1[2] = ":"; | |
const char sep2[2] = "$"; | |
FILE *file = fopen(fichero, "r"); | |
if (file == NULL) { | |
printf("El fichero %s no existe\n", fichero); | |
return; // El fichero no existe | |
} | |
while (fgets(line, sizeof(line), file)) { | |
if (strstr(line, "*") != NULL || strstr(line, "!") != NULL) | |
continue; | |
/* Separa primer y segundo campo: nombre y hash completo */ | |
attr_pos = 0; | |
token = strtok(line, sep1); // Primer token | |
while (token != NULL) { | |
// Primer atributo: nombre de usuario | |
if (attr_pos == 0) { | |
tok_size = strlen(token); | |
strncpy(usuarios[index].nombre, token, tok_size); | |
usuarios[index].nombre[tok_size] = '\0'; | |
} | |
// Segundo atributo: hash completo con id de algoritmo y sal | |
else if (attr_pos == 1) { | |
tok_size = strlen(token); | |
strncpy(usuarios[index].hash, token, tok_size); | |
usuarios[index].hash[tok_size] = '\0'; | |
} | |
token = strtok(NULL, sep1); | |
attr_pos++; | |
} | |
/* Separa los campos del hash */ | |
strcpy(hash, usuarios[index].hash); // Necesario para no cargarnos el string original | |
attr_pos = 0; | |
token = strtok(hash, sep2); // Primer token | |
while (token != NULL) { | |
// Primer atributo: id del algoritmo | |
if (attr_pos == 0) { | |
tok_size = strlen(token); | |
strncpy(usuarios[index].algoritmo, token, tok_size); | |
usuarios[index].algoritmo[tok_size] = '\0'; | |
} | |
// Segundo atributo: la sal | |
else if (attr_pos == 1) { | |
tok_size = strlen(token); | |
strncpy(usuarios[index].sal, token, tok_size); | |
usuarios[index].sal[tok_size] = '\0'; | |
} | |
token = strtok(NULL, sep2); | |
attr_pos++; | |
} | |
index++; | |
} | |
fclose(file); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment