ตรวจสอบรหัสผ่านด้วย Linux ได้อย่างไร


22

ฉันต้องการตรวจสอบจากบรรทัดคำสั่ง linux หากรหัสผ่าน cleartext ที่กำหนดนั้นเหมือนกันกับรหัสผ่านที่เข้ารหัสบน / 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; สิ่งนี้ต้องใช้เทอร์มินัล คาดว่าเครื่องมือทั่วไปในการเลียนแบบเครื่องเทอร์มินัลแต่มันเป็นระบบใหญ่ที่ต้องพึ่งพา นอกจากนี้ในขณะที่suอยู่ใน BusyBox ก็มักจะถูกละไว้เนื่องจากการใช้งานจำนวนมากต้องการไบนารี 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โปรแกรมที่คุณสามารถใช้มันเป็นข้อเสนอแนะที่นี่ (คุณนำเกลือมาจากไฟล์เงาหากยังไม่ชัดเจน) ตัวอย่างเช่นด้วย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>
Daniel Alder

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

พึงระลึกไว้ว่าหากระบบได้รับการกำหนดค่าอย่างเหมาะสมโปรแกรมจะต้องทำงานเป็นรูท

ทางออกที่ดีกว่าการอ่านไฟล์ shadow โดยตรงและการเขียนโค้ดของคุณรอบ ๆ crypt นั้นก็คือการใช้ pam bindings

ปลาหมึก tarball ใช้ในการมาพร้อมกับเครื่องมือ CLI ง่ายสำหรับการตรวจสอบชื่อผู้ใช้ / รหัสผ่านที่ใช้ stdio - เพื่อให้ง่ายต่อการปรับให้เข้ากับการใช้ข้อโต้แย้ง - แม้ว่ารุ่นที่ผมแฮ็กก่อนหน้านี้แทบจะไม่โปสเตอร์ขาขึ้นสำหรับการเขียนโปรแกรมโครงสร้าง google อย่างรวดเร็วและดูเหมือนว่าเวอร์ชันล่าสุดได้รับการล้างอย่างมีนัยสำคัญ แต่ก็ยังมี 'goto' อยู่ในนั้น


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 ":")
hoefling

เทอร์มินัลอีมูเลเตอร์ควรเป็น Bash 5 และคุณต้องระบุทั้งชื่อผู้ใช้และรหัสผ่านว่าเป็นอาร์กิวเมนต์ ฉันได้ทดสอบมันใช้งานได้
Alberto Salvia Novella
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.