리눅스로 비밀번호를 확인하는 방법?


22

주어진 일반 텍스트 비밀번호가 / 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 one-liner를 사용 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. 이것은 터미널이 필요합니다. 터미널을 에뮬레이트하는 일반적인 도구는 expect 이지만 임베디드 시스템의 경우 큰 의존성입니다. 또한 suBusyBox에 있지만 BusyBox 바이너리를 setuid 루트로 사용하려면 많은 용도로 사용되기 때문에 종종 생략됩니다. 여전히 가능하다면 보안 관점에서 가장 강력한 접근 방법입니다.


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프로그램이 포함 된 경우 여기에 제안 된대로 사용할 수 있습니다 . ( 분명하지 않은 경우 섀도 파일에서 솔트 를 가져옵니다 .) 예를 들어, md5passwords :

 mkpasswd -5 <the_salt> <the_password>

/etc/shadow항목 과 일치하는 문자열을 생성합니다 .


1
데비안 wheezy에서 나는 명령을 사용하기 위해 완전히 다른 문법을 mkpasswd사용했다 apt-get install whois. 섀도우 라인의 명령 줄 <user>:$6$<salt>$<pwd>:은 다음과 같습니다mkpasswd -msha-512 <password> <salt>
— Daniel Alder

1

Stack Overflow 에서 비슷한 질문 이있었습니다 . cluelessCoder는 임베디드 시스템에있을 수도 있고 없을 수도있는 expect를 사용하여 스크립트를 제공했습니다 .

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

시스템이 올바르게 구성되었다고 가정하면 프로그램을 루트로 실행해야합니다.

섀도 파일을 직접 읽고 crypt 주위에 자신의 코드를 작성하는 것보다 낫는 해결책은 pam 바인딩을 사용하는 것입니다.

오징어 인수를 사용에 적응하기 위해 너무 간단 - - 타르는 표준 입출력을 사용하여 사용자 이름 / 암호를 확인하기위한 간단한 CLI 도구를 제공하는 데 사용 나는 해킹 버전이 이전에 거의 구조화 프로그래밍을위한 핀 - 업 포스터 없었다하지만. 빠른 Google과 최신 버전 이 크게 정리되었지만 여전히 몇 가지 '고토'가 있는 것처럼 보입니다 .


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 여야하며 사용자 이름과 비밀번호를 모두 인수로 표시해야합니다. 나는 그것이 작동하는지 테스트했습니다.
— Alberto Salvia Novella
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.