Як перевірити пароль за допомогою Linux?


22

Я хочу перевірити в командному рядку Linux, чи вказаний пароль прозорого тексту той самий зашифрований пароль в / etc / shadow

(Мені потрібно це для автентифікації веб-користувачів. Я використовую вбудований Linux.)

У мене є доступ до самого файлу / etc / shadow.


Увійти як користувач із паролем?
Kusalananda

Тест потрібно робити автоматично, я не можу вручну ввести пароль з веб-сервера
michelemarcon

Відповіді:


17

Ви можете легко дістати зашифрований пароль з awk. Потім потрібно витягнути префікс $algorithm$salt$(якщо припустити, що ця система не використовує традиційну DES, яка сильно застаріла, оскільки може бути жорстокою формою в наші дні).

correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}

Для перевірки пароля основна функція C є crypt, але немає стандартної команди оболонки для доступу до неї.

У командному рядку ви можете використовувати однолінійку Perl для виклику cryptпароля.

supplied=$(echo "$password" |
           perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then 

Оскільки цього не можна зробити в інструментах чистої оболонки, якщо у вас є Perl, ви можете також зробити все це в Perl. (Або Python, Ruby,… все, що у вас є, що може викликати cryptфункцію.) Попередження, неперевірений код.

#!/usr/bin/env perl
use warnings;
use strict;
my @pwent = getpwnam($ARGV[0]);
if (!@pwent) {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);
}

У вбудованій системі без Perl я використовував би невелику спеціалізовану програму C. Попередження, набране безпосередньо в браузер, я навіть не намагався компілювати. Це покликане проілюструвати необхідні кроки, а не як надійну реалізацію!

/* 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);
}

Інший підхід полягає у використанні існуючої програми, такої як suабо login. Насправді, якщо ви можете, було б ідеально домовитись, щоб веб-додаток виконував все, що потрібно через su -c somecommand username. Складність тут полягає в подачі пароля su; для цього потрібен термінал. Звичайний інструмент для емуляції терміналу очікується , але це велика залежність для вбудованої системи. Крім того, хоча він suзнаходиться в BusyBox, його часто опускають, оскільки для багатьох його використання потрібен файл бінарного файлу BusyBox для встановлення root. І все-таки, якщо ви можете це зробити, це найбільш надійний підхід з точки зору безпеки.


1
Мені подобається suпідхід.
Benjohn

6

Погляньте man 5 shadowі man 3 crypt. З останнього ви можете дізнатися, що хеші паролів /etc/shadowмають такий вигляд:

 $id$salt$encrypted

де idвизначається тип шифрування і, читаючи далі, може бути одним із

          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)

Залежно від типу хешу, вам потрібно використовувати відповідну функцію / інструмент для генерації та перевірки пароля "вручну". Якщо система містить mkpasswdпрограму, ви можете використовувати її як запропоновано тут . (Ви берете сіль з тіньового файлу, якщо це не було очевидно.) Наприклад, із md5паролями:

 mkpasswd -5 <the_salt> <the_password>

створить рядок, який повинен відповідати /etc/shadowзапису.


1
На моїй хрипці Debian у мене був зовсім інший синтаксис команди mkpasswd, який мені довелося встановити за допомогою apt-get install whois. Командний рядок для тіньової лінії <user>:$6$<salt>$<pwd>:бувmkpasswd -msha-512 <password> <salt>
Даніель Алдер

1

Був подібне питання , поставлене на переповнення стека . cluelessCoder надав сценарій, використовуючи очікування , які у вас може бути або не бути у вашій вбудованій системі.

#!/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

0

Майте на увазі, що, якщо система правильно налаштована, програму потрібно запустити як root.

Кращим рішенням, ніж читати тіньовий файл безпосередньо та писати власний код навколо крипти, буде просто використовувати прив’язки пам.

Кальмар тарбол приходив з простим інструментом CLI для перевірки імен користувачів / паролів з використанням STDIO - так просто адаптуватися до використання аргументів - хоча версія , яку я зламав раніше була ледь чарівною плакат для структурного програмування. Швидкий google, і схоже, що новіші версії були значно очищені, але все ще є кілька 'goto's там.


0
#! /bin/bash
#  (GPL3+) Alberto Salvia Novella (es20490446e)


passwordHash () {
    password=${1}
    salt=${2}
    encryption=${3}

    hashes=$(echo ${password} | openssl passwd -${encryption} -salt ${salt} -stdin)
    echo $(substring ${hashes} "$" "3")
}


passwordIsValid () {
    user=${1}
    password=${2}

    encryption=$(secret "encryption" ${user})
    salt=$(secret "salt" ${user})
    salted=$(secret "salted" ${user})
    hash=$(passwordHash ${password} ${salt} ${encryption})

    [ ${salted} = ${hash} ] && echo "true" || echo "false"
}


secret () {
    secret=${1}
    user=${2}
    shadow=$(shadow ${user})

    if [ ${secret} = "encryption" ]; then
        position=1
    elif [ ${secret} = "salt" ]; then
        position=2
    elif [ ${secret} = "salted" ]; then
        position=3
    fi

    echo $(substring ${shadow} "$" ${position})
}


shadow () {
    user=${1}
    shadow=$(cat /etc/shadow | grep ${user})
    shadow=$(substring ${shadow} ":" "1")
    echo ${shadow}
}


substring () {
    string=${1}
    separator=${2}
    position=${3}

    substring=${string//"${separator}"/$'\2'}
    IFS=$'\2' read -a substring <<< "${substring}"
    echo ${substring[${position}]}
}


passwordIsValid ${@}

line 61: :: syntax error: operand expected (error token is ":")
Показує

Емулятором терміналу повинен бути Bash 5, і ви повинні вказати як ім'я користувача, так і пароль як аргументи. Я перевірив це працює.
Альберто Сальвія Новелла
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.