입력 한 비밀번호를 확인하는 방법은이 사용자에게 유효한 비밀번호입니까? 한 비밀번호가

시나리오 :

bash 스크립트에서 사용자가 제공 한 비밀번호가 유효한 사용자 비밀번호인지 확인해야합니다.

즉, 암호 PA가있는 사용자 A가 있다고 가정합니다. 스크립트에서 사용자 A에게 암호를 입력하도록 요청 했으므로 입력 한 문자열이 실제로 암호인지 확인하는 방법은 무엇입니까? …



답변

쉘 스크립트에서이 작업을 수행하려고하므로 Linux에서 비밀번호를 확인하는 방법에 몇 가지 기여가 있습니까? ( AB 에서 제안한 Unix.SE )는 특히 관련이 있습니다.

문자열이 실제로 사용자의 암호인지 수동으로 확인하려면 사용자의 섀도 항목과 동일한 해시 알고리즘으로 사용자의 섀도 항목 과 동일한 소금 으로 문자열을 해시해야합니다 . 그런 다음 저장된 암호 해시와 비교할 수 있습니다.

이 작업을 수행하는 방법을 보여주는 완전하고 작동하는 스크립트를 작성했습니다.

  • 이름을 지정하면 chkpass실행할 수 있으며 표준 입력에서 한 줄을 읽고 암호 인지 확인 합니다.chkpass useruser
  • 이 스크립트가 종속 된 유틸리티 를 얻으려면 whois 후이즈 설치 패키지를 설치하십시오 mkpasswd.
  • 성공하려면이 스크립트를 루트로 실행해야합니다.
  • 이 스크립트 또는 그 일부를 사용하여 실제 작업을 수행하기 전에 아래의 보안 정보 를 참조하십시오 .
#!/usr/bin/env bash

xcorrect=0 xwrong=1 enouser=2 enodata=3 esyntax=4 ehash=5  IFS=$
die() {
    printf '%s: %s\n' "$0" "$2" >&2
    exit $1
}
report() {
    if (($1 == xcorrect))
        then echo 'Correct password.'
        else echo 'Wrong password.'
    fi
    exit $1
}

(($# == 1)) || die $esyntax "Usage: $(basename "$0") <username>"
case "$(getent passwd "$1" | awk -F: '{print $2}')" in
    x)  ;;
    '') die $enouser "error: user '$1' not found";;
    *)  die $enodata "error: $1's password appears unshadowed!";;
esac

if [ -t 0 ]; then
    IFS= read -rsp "[$(basename "$0")] password for $1: " pass
    printf '\n'
else
    IFS= read -r pass
fi

set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
case "${ent[1]}" in
    1) hashtype=md5;;   5) hashtype=sha-256;;   6) hashtype=sha-512;;
    '') case "${ent[0]}" in
            \*|!)   report $xwrong;;
            '')     die $enodata "error: no shadow entry (are you root?)";;
            *)      die $enodata 'error: failure parsing shadow entry';;
        esac;;
    *)  die $ehash "error: password hash type is unsupported";;
esac

if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
    then report $xcorrect
    else report $xwrong
fi

보안 정보

올바른 접근법이 아닐 수도 있습니다.

이와 같은 접근 방식이 안전하고 적절한 것으로 간주되어야하는지 여부는 제공하지 않은 사용 사례에 대한 세부 정보에 따라 다릅니다 (이 문서 작성 시점 기준).

감사되지 않았습니다.

이 스크립트를 작성하는 동안주의를 기울이려고했지만 보안 취약점에 대해 제대로 감사하지 않았습니다 . 데모 용으로 제작되었으며 프로젝트의 일부로 출시 된 경우 “알파”소프트웨어가됩니다. 더욱이…

“보고있는”다른 사용자가 사용자의 소금 을 발견 할 수 있습니다 .

mkpasswd솔트 데이터를 받아들이 는 방식의 한계로 인해이 스크립트 에는 알려진 보안 결함포함되어 있으며 , 사용 사례에 따라 허용 가능한 것으로 간주 될 수도 있고 그렇지 않을 수도 있습니다. 기본적으로 Ubuntu 및 대부분의 다른 GNU / Linux 시스템 사용자는 명령 줄 인수를 포함하여 다른 사용자 (루트 포함)가 실행하는 프로세스에 대한 정보를 볼 수 있습니다. 사용자의 입력 또는 저장된 암호 해시는 명령 줄 인수로 외부 유틸리티에 전달되지 않습니다. 그러나 데이터베이스 에서 추출 된 salt 에 대한 명령 행 인수 로 제공됩니다. 유틸리티가 salt를 입력으로 받아들이는 유일한 방법이기 때문입니다.shadowmkpasswd

만약

  • 시스템의 다른 사용자 또는
  • 사용자 계정을 만들 수있는 사람 (예 www-data:)이 코드를 실행 하거나
  • 달리 실행중인 프로세스에 대한 정보를 볼 수있는 사람 (에서 수동으로 항목 검사 포함 /proc)

mkpasswd이 스크립트에 의해 실행될 때 명령 행 인수를 확인할 수 있으며, shadow데이터베이스 에서 사용자의 소금 사본을 얻을 수 있습니다 . 그들은 추측 할 수있을 그 명령을 실행하지만 때때로 달성 할 수있다.

salt를 가진 공격자는 salt 와 hash를 가진 공격자만큼 나쁘지는 않지만 이상적이지는 않습니다. 소금은 누군가가 귀하의 비밀번호를 발견 할 수있는 충분한 정보를 제공하지 않습니다. 그러나 누군가가 해당 시스템에서 해당 사용자에 대한 레인보우 테이블 또는 사전 계산 된 사전 해시 를 생성 할 수 있습니다. 처음에는 가치가 없지만 나중에 보안이 손상 되어 전체 해시 를 얻는 경우 사용자 암호를 변경하기 전에 사용자 암호를 얻기 위해 더 빨리 해독 될 수 있습니다.

따라서이 보안 결함은 완전히 악용 가능한 취약점이 아니라보다 복잡한 공격 시나리오의 악화 요인입니다. 그리고 당신은 위의 상황을 멀리 가져온 것으로 생각할 수 있습니다. 그러나 비공개 데이터를 루트가 아닌 사용자 에게 유출 하는 일반적인 실제 사용 방법을 권장하지 않습니다 /etc/shadow.

다음을 통해이 문제점을 완전히 피할 수 있습니다.

  • 당신이로 C 함수를 호출 펄 또는 수 있습니다 다른 언어로 스크립트의 일부를 작성 질의 대답에 표시관련 Unix.SE 질문 , 또는
  • bash를 사용하지 않고 전체 언어로 스크립트 / 프로그램을 작성하십시오. (질문에 태그를 지정한 방식에 따라 bash를 선호하는 것으로 보입니다.)

이 스크립트를 어떻게 호출하는지주의하십시오.

신뢰할 수없는 사용자가이 스크립트를 루트로 실행하거나이 스크립트를 호출하는 프로세스를 루트로 실행하도록 허용하는 경우 주의하십시오 . 환경을 변경하면이 스크립트 또는 루트로 실행되는 모든 스크립트가 무엇이든 수행 할 있습니다. 이를 방지 할 수 없다면 쉘 스크립트를 실행할 수있는 권한을 사용자에게 허용해서는 안됩니다.

10.4를 참조하십시오 . 이에 대한 자세한 내용 은 David A. Wheeler의 Linux 및 Unix HOWTO보안 프로그래밍셸 스크립팅 언어 (sh 및 csh 파생) 입니다 . 그의 프레젠테이션은 setuid 스크립트에 중점을 두지 만, 다른 메커니즘이 환경을 올바르게 위생 처리하지 않으면 동일한 문제 중 일부에 노출 될 수 있습니다.

기타 사항

shadow데이터베이스 에서 해시 읽기만 지원합니다 .

이 스크립트가 작동하려면 비밀번호를 음영 처리해야합니다 (즉, 해시는 /etc/shadow루트가 아닌 읽을 수 있는 별도의 파일 에 있어야 함 /etc/passwd).

우분투에서는 항상 그렇습니다. 어쨌든, 필요한 경우 스크립트를 간단하게 확장하여 passwd에서뿐만 아니라 암호 해시를 읽을 수 있습니다 shadow.

IFS이 스크립트를 수정할 때 명심 하십시오 .

IFS=$섀도 항목의 해시 필드에있는 세 개의 데이터가로 구분되므로 처음에 설정 했습니다 $.

  • 또한 선도적 인이 $해시 유형과 소금이 이유입니다, "${ent[1]}"그리고 "${ent[2]}"보다는 "${ent[0]}""${ent[1]}"각각.

이 스크립트에서 $IFS 단어를 나누 거나 결합 하는 방법을 결정하는 유일한 위치

  • 이러한 데이터가 인용되지 않은 $( )명령 대체에서 초기화되어 배열로 분할되는 경우 :

    set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
  • 에서 전체 필드와 비교하기 위해 배열을 문자열로 재구성 shadow하면 "${ent[*]}"식은 다음과 같습니다.

    if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]

다른 상황에서 스크립트를 수정하고 단어 분리 (또는 단어 결합)를 수행하는 경우 IFS다른 명령이나 스크립트의 다른 부분에 대해 다른 값 으로 설정해야합니다 .

이것을 명심하지 않고 $IFS일반적인 공백 ( $' \t\n')으로 설정되어 있다고 가정 하면 스크립트가 이상한 모양으로 보일 수 있습니다.


답변

sudo이것을 오용 할 수 있습니다 . sudo-l사용자가 가지고있는 sudo는 권한을 테스트하고, 옵션, -S표준 입력에서 암호에서 읽을 수 있습니다. 그러나 사용자의 권한 수준에 관계없이 성공적으로 인증되면 sudo종료 상태 0으로 돌아갑니다. 따라서 인증이 작동하지 않았다는 표시로 다른 종료 상태를 취할 수 있습니다 ( sudo자체에 아무런 문제가 없다고 가정). 권한 오류 또는 잘못된 sudoers구성).

다음과 같은 것 :

#! /bin/bash
IFS= read -rs PASSWD
sudo -k
if sudo -lS &> /dev/null << EOF
$PASSWD
EOF
then
    echo 'Correct password.'
else
    echo 'Wrong password.'
fi

이 스크립트는 sudoers구성 에 따라 약간 다릅니다 . 기본 설정을 가정했습니다. 실패 할 수있는 것들 :

  • targetpw또는 runaspw설정
  • listpw 이다 never
  • 기타

다른 문제는 다음과 같습니다 (엘리아에게 감사합니다).

  • 잘못된 시도가 로그인됩니다 /var/log/auth.log
  • sudo인증하는 사용자로 실행해야합니다. 을 sudo실행할 수 있는 권한이 없으면 sudo -u foo sudo -lS스크립트를 대상 사용자로 실행해야합니다.

자, 내가 여기 문서를 사용한 이유는 도청을 방해하기 위해서입니다. 커맨드 라인의 일부로서 사용되는 변수는 더 쉽게하여 노출 top또는 ps프로세스를 검사하는 다른 도구 또는.


답변

또 다른 방법 (아마도 실제 적용보다 이론적 내용에 더 흥미 롭습니다).

사용자 비밀번호는에 저장됩니다 /etc/shadow.

여기에 저장된 비밀번호는를 사용하여 최신 Ubuntu 릴리스에서 암호화됩니다 SHA-512.

특히 암호를 만들 때 일반 텍스트의 암호는을 통해 소금에 절이고 암호화됩니다 SHA-512.

한 가지 해결책은 주어진 암호를 소금 / 암호화하고 주어진 사용자의 /etc/shadow항목에 저장된 암호화 된 사용자 암호와 일치시키는 것 입니다.

암호는 각 사용자에 저장하는 방법에 대한 간단한 고장 제공하려면 /etc/shadow항목을 여기에 샘플의 /etc/shadow사용자에 대한 항목 foo암호는 bar:

foo:$6$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/:16566:0:99999:7:::
  • foo: 사용자 이름
  • 6: 비밀번호의 암호화 유형
  • lWS1oJnmDlaXrx1F: 비밀번호의 암호화 솔트
  • h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/: SHA-512소금에 절인 암호

bar주어진 사용자에 대해 주어진 암호를 일치시키기 위해 foo가장 먼저 할 일은 소금을 얻는 것입니다.

$ sudo getent shadow foo | cut -d$ -f3
lWS1oJnmDlaXrx1F

그런 다음 완전히 소금에 절인 / 암호화 된 암호를 가져옵니다.

$ sudo getent shadow foo | cut -d: -f2
$6$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/

그런 다음 주어진 암호를 소금에 절인 / 암호화하고 /etc/shadow다음에 저장된 소금에 절인 / 암호화 된 사용자의 비밀번호와 일치시킬 수 있습니다 .

$ python -c 'import crypt; print crypt.crypt("bar", "$6$lWS1oJnmDlaXrx1F")'
$6$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/

일치합니다! bash스크립트에 넣은 모든 것 :

#!/bin/bash

read -p "Username >" username
IFS= read -p "Password >" password
salt=$(sudo getent shadow $username | cut -d$ -f3)
epassword=$(sudo getent shadow $username | cut -d: -f2)
match=$(python -c 'import crypt; print crypt.crypt("'"${password}"'", "$6$'${salt}'")')
[ ${match} == ${epassword} ] && echo "Password matches" || echo "Password doesn't match"

산출:

$ ./script.sh
Username >foo
Password >bar
Password matches
$ ./script.sh
Username >foo
Password >bar1
Password doesn't match


답변