Ponieważ chcesz to zrobić w skrypcie powłoki, kilka uwag w Jak sprawdzić hasło w systemie Linux? (na Unix.SE , sugerowany przez AB ) są szczególnie istotne:
Aby ręcznie sprawdzić, czy ciąg znaków jest naprawdę hasłem użytkownika, należy go zaszyfrować przy użyciu tego samego algorytmu skrótu, jak we wpisie cienia użytkownika, z taką samą solą jak we wpisie cienia użytkownika. Następnie można go porównać z hasłem przechowywanym w nim.
Napisałem kompletny, działający skrypt pokazujący, jak to zrobić.
- Jeśli go nazwiesz
chkpass, możesz uruchomić, a on przeczyta wiersz ze standardowego wejścia i sprawdzi, czy to hasło.chkpass useruser
- Zainstaluj pakiet whois
, aby uzyskać mkpasswdnarzędzie, od którego zależy ten skrypt.
- Ten skrypt musi być uruchomiony jako root, aby odnieść sukces.
- Przed użyciem tego skryptu lub jego części do prawdziwej pracy zapoznaj się z uwagami bezpieczeństwa poniżej.
#!/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
Uwagi bezpieczeństwa
To może nie być właściwe podejście.
To, czy takie podejście należy uznać za bezpieczne i w inny sposób odpowiednie, zależy od szczegółowych informacji na temat przypadku użycia, którego nie podałeś (od tego momentu).
Nie został poddany audytowi.
Chociaż starałem się zachować ostrożność podczas pisania tego skryptu, nie został on odpowiednio sprawdzony pod kątem luk w zabezpieczeniach . Ma to służyć jako demonstracja i byłoby oprogramowaniem „alfa”, gdyby zostało wydane w ramach projektu. Ponadto...
Inny użytkownik, który „ogląda”, może odkryć sól użytkownika .
Ze względu na ograniczenia w sposobie mkpasswdakceptowania danych o soli, ten skrypt zawiera znaną lukę w zabezpieczeniach , którą możesz, lub nie, zaakceptować w zależności od przypadku użycia. Domyślnie użytkownicy Ubuntu i większości innych systemów GNU / Linux mogą przeglądać informacje o procesach uruchamianych przez innych użytkowników (w tym root), w tym argumenty wiersza poleceń. Ani dane wejściowe użytkownika, ani przechowywany skrót hasła nie są przekazywane jako argument wiersza polecenia do żadnego zewnętrznego narzędzia. Ale sól wyodrębniona z shadowbazy danych jest podawana jako argument wiersza polecenia mkpasswd, ponieważ jest to jedyny sposób, w jaki narzędzie przyjmuje sól jako dane wejściowe.
Gdyby
- inny użytkownik w systemie lub
- każdy, kto ma możliwość założenia dowolnego konta użytkownika (np.
www-data) uruchomienia kodu, lub
- każdy, kto w inny sposób może wyświetlić informacje o uruchomionych procesach (w tym ręcznie sprawdzając wpisy w
/proc)
jest w stanie sprawdzić argumenty wiersza poleceń mkpasswduruchamiane przez ten skrypt, a następnie uzyskać kopię soli użytkownika z shadowbazy danych. Być może będą musieli zgadnąć, kiedy to polecenie zostanie uruchomione, ale czasami jest to możliwe.
Atakujący twoją solą nie jest tak zły jak atakujący twoją solą i haszem , ale nie jest idealny. Sól nie zapewnia wystarczającej ilości informacji, aby ktoś mógł odkryć twoje hasło. Ale pozwala komuś generować tęczowe tabele lub wstępnie obliczone skróty słownika właściwe dla tego użytkownika w tym systemie. Jest to początkowo bezwartościowe, ale jeśli twoje bezpieczeństwo zostanie później naruszone i zostanie uzyskany pełny skrót, można go później złamać, aby uzyskać hasło użytkownika, zanim będzie on miał szansę go zmienić.
Tak więc ta luka w zabezpieczeniach jest czynnikiem pogarszającym bardziej złożony scenariusz ataku, a nie w pełni podatną na atak luką. I możesz uznać, że powyższa sytuacja jest zbyt daleko posunięta. Ale niechętnie zalecam jakąkolwiek metodę do ogólnego, rzeczywistego użytkowania, która przecieka wszelkie niepubliczne dane /etc/shadowdo użytkownika innego niż root.
Możesz całkowicie uniknąć tego problemu poprzez:
- pisanie część skryptu w Perlu lub jakiegoś innego języka, który umożliwia wywołanie funkcji C, jak pokazano w odpowiedzi Gilles jest do powiązanego Unix.SE pytanie , czy
- pisanie całego skryptu / programu w takim języku, zamiast używania bash. (Na podstawie sposobu otagowania pytania wydaje się, że wolisz używać bash).
Uważaj, jak wywołać ten skrypt.
Jeśli zezwalasz niezaufanemu użytkownikowi na uruchomienie tego skryptu jako root lub na uruchomienie dowolnego procesu jako root, który wywołuje ten skrypt, bądź ostrożny . Zmieniając środowisko, mogą sprawić, że ten skrypt - lub dowolny skrypt uruchamiany jako root - zrobi wszystko . O ile nie można temu zapobiec, nie wolno zezwalać użytkownikom na podniesienie uprawnień do uruchamiania skryptów powłoki.
Zobacz 10.4. Shell Scripting Languages (pochodne sh i csh) w David A. Wheeler Secure Programming for Linux and Unix HOWTO, aby uzyskać więcej informacji na ten temat. Podczas gdy jego prezentacja koncentruje się na skryptach setuid, inne mechanizmy mogą paść ofiarą tych samych problemów, jeśli nie zdezynfekują środowiska w odpowiedni sposób.
Inne notatki
Obsługuje odczytywanie skrótów tylko z shadowbazy danych.
Aby skrypt działał, hasła muszą być ukryte (tzn. Ich skróty powinny znajdować się w osobnym /etc/shadowpliku, który tylko root może czytać, a nie w nim /etc/passwd).
Tak powinno być zawsze w Ubuntu. W każdym razie, w razie potrzeby, skrypt można w prosty sposób rozszerzyć o odczyt skrótów haseł, passwdjak również shadow.
Pamiętaj IFSo modyfikowaniu tego skryptu.
Ustawiłem IFS=$na początku, ponieważ trzy dane w polu mieszającym wpisu cienia są oddzielone $.
- Mają również czołowym
$, dlatego typ hash i sól są "${ent[1]}"i "${ent[2]}"zamiast "${ent[0]}"i "${ent[1]}", odpowiednio.
Jedynymi miejscami w tym skrypcie, w których $IFSokreśla się, jak powłoka dzieli lub łączy słowa
gdy dane te zostaną podzielone na tablicę, inicjując je z niecytowanego $( )podstawienia polecenia w:
set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
gdy tablica jest odtwarzana w ciąg znaków w celu porównania z pełnym polem shadow, "${ent[*]}"wyrażenie w:
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
Jeśli zmodyfikujesz skrypt i sprawisz, że będzie on dzielił słowa (lub łączy słowa) w innych sytuacjach, musisz ustawić IFSróżne wartości dla różnych poleceń lub różnych części skryptu.
Jeśli nie będziesz o tym pamiętać i zakładasz, że $IFSustawiono zwykłą białą spację ( $' \t\n'), możesz spowodować, że skrypt będzie zachowywał się w dziwny sposób.