Puede extraer fácilmente la contraseña cifrada con awk. A continuación, debe extraer el prefijo $algorithm$salt$
(asumiendo que este sistema no está usando el DES tradicional, que está fuertemente obsoleto porque puede ser de fuerza bruta en estos días).
correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}
Para verificar la contraseña, la función C subyacente es crypt
, pero no hay un comando de shell estándar para acceder a él.
En la línea de comando, puede usar una sola línea de Perl para invocar crypt
en la contraseña.
supplied=$(echo "$password" |
perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …
Dado que esto no se puede hacer con herramientas de shell puras, si tiene Perl disponible, también puede hacerlo todo en Perl. (O Python, Ruby,... lo que sea que tengas disponible que pueda llamar al crypt
función.) Advertencia, código no probado.
#!/usr/bin/env perl
use warnings;
use strict;
my @pwent = getpwnam($ARGV[0]);
if ([email protected]) {die "Invalid username: $ARGV[0]\n";}
my $supplied = <STDIN>;
chomp($supplied);
if (crypt($supplied, $pwent[1]) eq $pwent[1]) {
exit(0);
} else {
print STDERR "Invalid password for $ARGV[0]\n";
exit(1);
}
En un sistema integrado sin Perl, usaría un programa C pequeño y dedicado. Advertencia, escrito directamente en el navegador, ni siquiera he intentado compilar. ¡Esto pretende ilustrar los pasos necesarios, no como una implementación robusta!
/* Usage: echo password | check_password username */
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <shadow.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char password[100];
struct spwd shadow_entry;
char *p, *correct, *supplied, *salt;
if (argc < 2) return 2;
/* Read the password from stdin */
p = fgets(password, sizeof(password), stdin);
if (p == NULL) return 2;
*p = 0;
/* Read the correct hash from the shadow entry */
shadow_entry = getspnam(username);
if (shadow_entry == NULL) return 1;
correct = shadow_entry->sp_pwdp;
/* Extract the salt. Remember to free the memory. */
salt = strdup(correct);
if (salt == NULL) return 2;
p = strchr(salt + 1, '$');
if (p == NULL) return 2;
p = strchr(p + 1, '$');
if (p == NULL) return 2;
p[1] = 0;
/*Encrypt the supplied password with the salt and compare the results*/
supplied = crypt(password, salt);
if (supplied == NULL) return 2;
return !!strcmp(supplied, correct);
}
Un enfoque diferente es usar un programa existente como su
o login
. De hecho, si puede, sería ideal hacer arreglos para que la aplicación web realice lo que necesite a través de su -c somecommand username
. La dificultad aquí es pasar la contraseña a su
; esto requiere una terminal. Se espera la herramienta habitual para emular una terminal, pero es una gran dependencia para un sistema embebido. Además, mientras su
está en BusyBox, a menudo se omite porque muchos de sus usos requieren que el binario BusyBox sea setuid root. Aún así, si puede hacerlo, este es el enfoque más sólido desde el punto de vista de la seguridad.
Echa un vistazo a man 5 shadow
y man 3 crypt
. De este último, puede aprender que la contraseña tiene un hash en /etc/shadow
tener la siguiente forma:
$id$salt$encrypted
donde id
define el tipo de cifrado y, leyendo más, puede ser uno de
ID | Method
---------------------------------------------------------
1 | MD5
2a | Blowfish (not in mainline glibc; added in some
| Linux distributions)
5 | SHA-256 (since glibc 2.7)
6 | SHA-512 (since glibc 2.7)
Según el tipo de hash, debe utilizar la función/herramienta adecuada para generar y verificar la contraseña "a mano". Si el sistema contiene mkpasswd
programa, puede usarlo como se sugiere aquí. (Tomas la sal del archivo shadow, si eso no fuera obvio). Por ejemplo, con md5
contraseñas:
mkpasswd -5 <the_salt> <the_password>
generará la cadena que debe coincidir con /etc/shadow
entrada.
Hubo una pregunta similar en Stack Overflow. cluelessCoder proporcionó un script usando expect, que puede o no tener en su sistema integrado.
#!/bin/bash
#
# login.sh $USERNAME $PASSWORD
#this script doesn't work if it is run as root, since then we don't have to specify a pw for 'su'
if [ $(id -u) -eq 0 ]; then
echo "This script can't be run as root." 1>&2
exit 1
fi
if [ ! $# -eq 2 ]; then
echo "Wrong Number of Arguments (expected 2, got $#)" 1>&2
exit 1
fi
USERNAME=$1
PASSWORD=$2
#since we use expect inside a bash-script, we have to escape tcl-$.
expect << EOF
spawn su $USERNAME -c "exit"
expect "Password:"
send "$PASSWORD\r"
#expect eof
set wait_result [wait]
# check if it is an OS error or a return code from our command
# index 2 should be -1 for OS erro, 0 for command return code
if {[lindex \$wait_result 2] == 0} {
exit [lindex \$wait_result 3]
}
else {
exit 1
}
EOF