Linux에서 비밀번호를 확인하는 방법은 무엇입니까?

Linux에서 비밀번호를 확인하는 방법은 무엇입니까?

주어진 일반 텍스트 비밀번호가 /etc/shadow의 암호화된 비밀번호와 동일한지 Linux 명령줄에서 확인하고 싶습니다.

(네트워크 사용자를 인증하려면 이것이 필요합니다. 임베디드 Linux를 실행하고 있습니다.)

/etc/shadow 파일 자체에 액세스할 수 있습니다.

답변1

awk를 사용하면 암호화된 비밀번호를 쉽게 추출할 수 있습니다. 그런 다음 접두사를 추출해야 합니다 $algorithm$salt$(시스템이 레거시 DES를 사용하지 않는다고 가정합니다. 레거시 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);
}

또 다른 접근 방식 sulogin. 실제로 가능하다면 웹 애플리케이션이 를 통과하도록 준비하는 것이 더 좋습니다 su -c somecommand username. 여기서 어려운 점은 에 비밀번호를 제공하는 것입니다 su. 터미널을 시뮬레이션하는 일반적인 도구는 다음과 같습니다.예상되는, 그러나 이는 임베디드 시스템에 대한 큰 종속성입니다. 또한 suBusyBox에서는 많은 용도로 BusyBox 바이너리가 setuid 루트여야 하기 때문에 생략되는 경우가 많습니다. 그러나 그렇게 할 수 있다면 보안 측면에서 가장 안정적인 방법입니다.

답변2

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.

답변3

하나 있다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

답변4

내가 답장한 사람의 C 코드에 오류가 있었습니다. 항상 버그가 있기 때문에 코드가 작동하는지 먼저 확인하지 않고 사람들이 여기에 코드를 게시하는 이유를 결코 이해하지 마십시오. 내 코드를 확인할 필요가 없다는 것은 아닙니다. (구문) 오류 없이 만들려고 시도한 가장 큰 파일은 1000줄 길이였는데, 그것은 30년의 경험의 결과입니다.

KDE Neon에서 이것을 테스트했는데 /etc/shadow를 읽으려면 높은 권한이 필요하거나 이를 호출하는 사용자가 "shadow" 그룹(예: /etc/shadow의 그룹)에 속해야 하기 때문에 루트로 실행해야 합니다. )에 속합니다. 사용자 이름을 매개변수로 사용합니다.

컴파일: gcc <source_file.c> -lcrypt

#define _GNU_SOURCE  

#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <shadow.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#define DBG()                             \
do {                                      \
  char buf[100];                          \
  sprintf (buf, "error: %d\n", __LINE__); \
  perror (buf);                           \
} while (0)

void chomp (char *str)
{
  while (*str != '\0' && *str != '\n')
  {
    str++;
  }
  *str = '\0';
}

int main(int argc, char *argv[])
{
  char password[100];
  struct spwd *shadow_entry;
  char *p, *correct, *supplied, *salt;
  if (argc < 2)
  {
    DBG ();
    return 2;
  }
  /* Read the password from stdin */
  p = fgets(password, sizeof(password), stdin);
  if (p == NULL)
  {
    DBG ();
    return 2;
  }
  //*p = 0; - this was a pretty obvious error
  chomp (p); // this is what was intended above
  printf ("password = %s\n", p);
  /* Read the correct hash from the shadow entry */
  shadow_entry = getspnam( argv[1] );
  if (shadow_entry == NULL)
  {
    DBG ();
    return 1;
  }
  correct = shadow_entry->sp_pwdp;
  /* Extract the salt. Remember to free the memory. */
  salt = strdup(correct);
  if (salt == NULL)
  {
    DBG ();
    return 2;
  }
  p = strchr(salt + 1, '$');
  if (p == NULL)
  {
    DBG ();
    return 2;
  }
  p = strchr(p + 1, '$');
  if (p == NULL)
  {
    DBG ();
    return 2;
  }
  p[1] = 0;
  /*Encrypt the supplied password with the salt and compare the results*/
  supplied = crypt(password, salt);
  if (supplied == NULL)
  {
    DBG ();
    return 2;
  }
  if (strcmp(supplied, correct) == 0)
  {
    printf ("pass\n %s\n %s\n", supplied, correct);
    return (0);
  }
  else
  {
    printf ("fail\n %s\n %s\n", supplied, correct);
    return (1);
  }
}

printf 함수를 제거하고 DBG()에 대한 호출을 제거할 수 있지만 프로그램이 올바르게 작동하는지 확인할 때까지는 둘 다 유용합니다. 나는 그것들을 추가하고 그것이 어떻게 그리고 어디서 실패하는지 확인해야 할 것입니다. 각 오류 종료도 다른 번호여야 하지만 이는 단지 내 의견일 뿐입니다.

관련 정보