Tôi muốn kiểm tra, từ dòng lệnh linux, nếu mật khẩu Cleartext đã cho giống với mật khẩu được mã hóa trên / etc / bóng
(Tôi cần điều này để xác thực người dùng web. Tôi đang chạy một linux nhúng.)
Tôi có quyền truy cập vào tập tin / etc / bóng.
Tôi muốn kiểm tra, từ dòng lệnh linux, nếu mật khẩu Cleartext đã cho giống với mật khẩu được mã hóa trên / etc / bóng
(Tôi cần điều này để xác thực người dùng web. Tôi đang chạy một linux nhúng.)
Tôi có quyền truy cập vào tập tin / etc / bóng.
Câu trả lời:
Bạn có thể dễ dàng trích xuất mật khẩu được mã hóa bằng awk. Sau đó, bạn cần trích xuất tiền tố $algorithm$salt$
(giả sử rằng hệ thống này không sử dụng DES truyền thống, vốn không được dùng nữa vì nó có thể bị ép buộc trong những ngày này).
correct=$(</etc/shadow awk -v user=bob -F : 'user == $1 {print $2}')
prefix=${correct%"${correct#\$*\$*\$}"}
Để kiểm tra mật khẩu, hàm C bên dưới là crypt
, nhưng không có lệnh shell tiêu chuẩn để truy cập nó.
Trên dòng lệnh, bạn có thể sử dụng Perl one-liner để gọi crypt
mật khẩu.
supplied=$(echo "$password" |
perl -e '$_ = <STDIN>; chomp; print crypt($_, $ARGV[0])' "$prefix")
if [ "$supplied" = "$correct" ]; then …
Vì điều này không thể được thực hiện trong các công cụ shell thuần túy, nếu bạn có sẵn Perl, bạn cũng có thể làm tất cả trong Perl. (Hoặc Python, Ruby, xông bất cứ thứ gì bạn có sẵn có thể gọi crypt
hàm.) Cảnh báo, mã chưa được kiểm tra.
#!/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);
}
Trên một hệ thống nhúng không có Perl, tôi sẽ sử dụng một chương trình C nhỏ, chuyên dụng. Cảnh báo, được gõ trực tiếp vào trình duyệt, tôi thậm chí chưa thử biên dịch. Điều này có nghĩa là để minh họa các bước cần thiết, không phải là một triển khai mạnh mẽ!
/* 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);
}
Một cách tiếp cận khác là sử dụng một chương trình hiện có như su
hoặc login
. Trong thực tế, nếu bạn có thể, sẽ rất lý tưởng khi sắp xếp cho ứng dụng web thực hiện bất cứ điều gì nó cần thông qua su -c somecommand username
. Khó khăn ở đây là cung cấp mật khẩu cho su
; điều này đòi hỏi một thiết bị đầu cuối. Công cụ thông thường để mô phỏng thiết bị đầu cuối là mong đợi , nhưng đó là một sự phụ thuộc lớn cho một hệ thống nhúng. Ngoài ra, trong khi su
ở BusyBox, nó thường bị bỏ qua vì nhiều cách sử dụng của nó yêu cầu nhị phân BusyBox phải là root setuid. Tuy nhiên, nếu bạn có thể làm điều đó, đây là cách tiếp cận mạnh mẽ nhất từ quan điểm bảo mật.
su
cách tiếp cận.
Có một cái nhìn man 5 shadow
và man 3 crypt
. Từ cái sau, bạn có thể biết rằng băm mật khẩu /etc/shadow
có dạng sau:
$id$salt$encrypted
trong đó id
xác định loại mã hóa và, đọc thêm, có thể là một trong
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)
Tùy thuộc vào loại băm, bạn cần sử dụng chức năng / công cụ thích hợp để tạo và xác minh mật khẩu "bằng tay". Nếu hệ thống chứa mkpasswd
chương trình, bạn có thể sử dụng nó như được đề xuất ở đây . (Bạn lấy muối từ tệp bóng, nếu điều đó không rõ ràng.) Ví dụ: với md5
mật khẩu:
mkpasswd -5 <the_salt> <the_password>
sẽ tạo ra chuỗi phù hợp với /etc/shadow
mục nhập.
mkpasswd
mà tôi phải cài đặt bằng cách sử dụng apt-get install whois
. Dòng lệnh cho dòng bóng <user>:$6$<salt>$<pwd>:
làmkpasswd -msha-512 <password> <salt>
Có một câu hỏi tương tự được hỏi trên Stack Overflow . clulessCoder đã cung cấp một tập lệnh sử dụng kỳ vọng mà bạn có thể có hoặc không có trên hệ thống nhúng của mình.
#!/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
Hãy nhớ rằng, giả sử hệ thống được cấu hình đúng, chương trình sẽ cần được chạy dưới quyền root.
Một giải pháp tốt hơn là đọc tệp bóng trực tiếp và viết mã của riêng bạn xung quanh tiền điện tử sẽ chỉ là sử dụng các ràng buộc pam.
Các mực tarball sử dụng để đi kèm với một công cụ CLI đơn giản cho việc xác minh tên người dùng / mật khẩu sử dụng stdio - đơn giản như vậy để thích ứng với sử dụng đối số - mặc dù các phiên bản hack tôi trước đây là hầu như không một poster pin-up cho lập trình có cấu trúc. Google nhanh chóng và có vẻ như các phiên bản gần đây đã được dọn sạch đáng kể nhưng vẫn còn một vài 'goto trong đó.
#! /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 ":")