Sous Linux, quel est le sixième caractère du hachage de mot de passe stocké dans /etc/shadow
?
Sur ma boîte Linux style chiot, si j'essaie de générer 100 mots de passe aléatoires en utilisant shuf
et /dev/urandom
, le sixième caractère est /
environ la moitié du temps.
Ma question n'est pas à des fins de production, car je le démarre à chaque fois à partir d'un CD. Cela signifie-t-il que mon système est mal configuré ou non sécurisé d'une manière ou d'une autre?
J'ai exécuté le fichier sur shuf
pour voir s'il s'agissait d'un lien busybox
.
file /usr/bin/shuf
shuf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped
Je ne pense pas que shuf
soit un lien busybox
ici.
ls -l /usr/bin/shuf
-rwxr-xr-x 1 root root 41568 Mar 7 2015 /usr/bin/shuf
tandis que
ls -l /bin/wget
lrwxrwxrwx 1 root root 14 Apr 29 03:49 wget -> ../bin/busybox
Voici une idée approximative de ce que j'ai fait:
# ! / b i n / b a s h
## don't try this on any real computer
## this is not a production script, it is just psuedo code
## with pseudo results to illustrate a point
## for this run of 100 ?random? passwords,
## 46 of the 6th character of the hash stored in
## '/ect/shadow' were '/'
function is_this_really_a_random_password () {
PERHAPS_RANDOM=''
for (( Z=0 ; Z<=8 ; Z++ )) do
PERHAPS_RANDOM="$PERHAPS_RANDOM$( shuf --head-count=1 --random-source=/dev/urandom $FILE_OF_SAFE_CHARACTERS )"
done
echo "$USER_NAME:$PERHAPS_RANDOM" | chpasswd
}
rm sixth-character-often-forward-slash.txt
for (( I=1; I<=100; I++ )) do
is_this_really_a_random_password
grep --regexp=root /etc/shadow | cut --characters=-40 >> sixth-character-often-forward-slash.txt
done
root:$5$56YsS//DE$HasM6O8y2mnXbtgeE64zK root:$5$ho8pk/4/A6e/m0eW$XmjA5Up.0Xig1e root:$5$jBQ4f.t1$vY/T/1kX8nzAEK8vQD3Bho root:$5$BJ44S/Hn$CsnG00z6FB5daFteS5QCYE root:$5$Jerqgx/96/HlV$9Wms5n1FEiM3K93A8 root:$5$qBbPLe4zYW$/zXRDqgjbllbsjkleCTB root:$5$37MrD/r0AlIC40n6$8hplf2c3DgtbM1 root:$5$.4Tt5S6F.3K7l7E$dAIZzFvvWmw2uyC root:$5$A4dX4ZlOoE$6axanr4GLPyhDstWsQ9B root:$5$HXAGhryJ/5$40tgmo7q30yW6OF7RUOE root:$5$EzNb9t5d$/nQEbEAQyug7Dk9X3YXCEv root:$5$HHS5yDeSP$LPtbJeTr0/5Z33vvw87bU root:$5$sDgxZwTX5Sm$6Pzcizq4NcKsWEKEL15 root:$5$FK1du/Paf/$hAy8Xe3UQv9HIpOAtLZ2 root:$5$xTkuy/BLUDh/N$/30sESA.5nVr1zFwI root:$5$PV4AX/OjZ$VU8vX651q4eUqjFWbE2b/ root:$5$iDuK0IUGijv4l$cdGh8BlHKJLYxPB8/ root:$5$0DEUp/jz$JBpqllXswNc0bMJA5IFgem root:$5$Wz3og/W3Jra/WKA.$6D7Wd4M1xxRDEp root:$5$ntHWB.mC3x$Kt4DNTjRZZzpbFvxpMxP root:$5$g/uEc/cq$Ptlgu8CXV.vrjrmuok9RRT root:$5$/XAHs/5x$Z9J4Zt4k6NxdjJ27PpLmTt root:$5$mgfbZeWD0h/$UDGz8YX.D85PzeXnd2K root:$5$f4Oh3/bF2Ox/eN$xt/Jkn0LxPnfKP8. root:$5$J0mZZXGJG7/v$e16VxghNvZZKRONown root:$5$SNza9XFl9i$Qq7r/N6Knt2j74no8H0x root:$5$aFCu//xiL$Ocn9mcT2izcnm3rUlBOJg root:$5$kMkyos/SLZ/Mm6$wNYxZ9QeuJ8c8T.o root:$5$ujXKC/Xnj0h/nQ$PUmePvJZr.UXmTGK root:$5$wtEhA/YKaTKH$6VCSXUiIdsfelkCYWV root:$5$I1taRlq59YZUGe$4OyIfByuvJeuwsjM root:$5$N54oH//j4nbiB$K4i6QOiS9iaaX.RiD root:$5$ps8bo/VjPGMP0y4$NTFkI6OeaMAQL7w root:$5$IRUXnXO8tSykA8$NatM5X/kKHHgtDLt root:$5$VaOgL/8V$m45M9glUYnlTKk8uCI7b5P root:$5$/lPDb/kUX73/F3$jJL.QLH5o9Ue9pVa root:$5$/sHNL/tVzuu//cr$QasvQxa02sXAHOl root:$5$hGI.SMi/7I$fYm0rZP0F5B2D1YezqtX root:$5$WsW2iENKA$4HhotPoLRc8ZbBVg4Z5QW root:$5$cN6mwqEl$q5S3U85cRuNHrlxS9Tl/PC root:$5$wwzLR/YMvk5/7ldQ$s3BJhq5LyrtZww root:$5$GUNvr/d15n8/K$CiNHwOkAtxuWJeNy1 root:$5$nGE75/8mEjM/A$pD/84iLunN/ZNI/JK root:$5$77Dn2dHLS$d5bUQhTz.OU4UA.67IGMB root:$5$EWrI//1u$uubkPk3YhAnwYXOYsvwbah root:$5$Hzfw1UCudP/N/U$Rjcdzdbov1YgozSJ root:$5$2y8CKTj.2eTq$7BEIgMWIzAJLl1SWBv root:$5$lcWsD/42g8zEEABA$r/vGxqqUZTkJ0V root:$5$LPJLc/Xz$tnfDgJh7BsAT1ikpn21l76 root:$5$ucvPeKw9eq8a$vTneH.4XasgBIeyGSA root:$5$Fwm2eUR7$ByjuLJRHoIFWnHtvayragS root:$5$yBl7BtMb$KlWGwBL6/WjgHVwXQh9fJS root:$5$1lnnh2kOG$rdTLjJsSpC3Iw4Y6nkPhq root:$5$WfvmP6cSfb066Z$1WvaC9iL11bPCAxa root:$5$qmf/hHvalWa4GE25$m3O2pdu25QBCwU root:$5$4P.oT/9HQ$Ygid4WXi0QCEObLVNsqFZ root:$5$FNr4Bkj56Y$38mG7mKV0mdb1PMCxrVd root:$5$hoNcyURtV$aTidBWHjngc1I0vUTi5bB root:$5$rzHmykYT$ATiXdUDUvUnB2fNMUQgwvE root:$5$o11Yb/ZQv2/k3wg9$5yShpVejDBk6HB root:$5$REPGN//y9H$awpPmUvCqvi6Bd/6bQxF root:$5$HbAEY/djXJx$y56GhMwavd7xTQ.jPg6 root:$5$3T1k5.LZUcy$Cup.LM5AnaBTIaJtBnF root:$5$wXaSC/P8bJ$y/0DoYJVjaP09O6GWiki root:$5$YuFfY8QPqm/dD$IIh0/tyn.18xEBl5Y root:$5$uTTBpjsKG//3Et8$9ibN9mVwSeVyOI4 root:$5$dASlMLzbVbFMnZ$N4uGBwGHhdg93z/V root:$5$03.FA/LnRBb.k7Zl$XOHU2ZlHkV9oz9 root:$5$2zL1p/VDCi$/QRT7Bo3cZ3Rxb8Y7ddo root:$5$0NpZqZs/qt/jIv.$8W/TTM3Gy2UMOWy root:$5$a4SXynoro7ucT$qFM2C79QJ15jQ0ZlL root:$5$RL0Eg/jroH8/ONP$EzceXz.pz74k104 root:$5$O3R5V/n1$U.mmCTbpID8xMXbvtzd4ch root:$5$0T2nVrv/P/xaRwUD$YVm17XF8kTsL0f root:$5$2bRwMNIXobZwn$Q228FJqg6/iRCe9GQ root:$5$PyYgL/axfgj/$uaL5y/kdzU4Kzi.JlB root:$5$A6QtfJdJ4Gwvx4$d4PA5AJ0806NzRnm root:$5$H8Mta5LDgGXp$QGdOJh.bFWgR3L719Z root:$5$H06URjv4BtOAbA$EJs1mZYhdKIVgCmn root:$5$OeB.O/GrmFB/az$SoE759KE9WIE17Uf root:$5$huiB9/sk$el3XMf7SGX81LnD3.SaF8J root:$5$fO7tfM.fjdSHA8G6$s.QIjfNniCzFdU root:$5$32at3SQJAD/xlw$HbXmBLVXTTyZfxQv root:$5$FHBFL/QdFl$FMipxpW0HlEFUIAr7IxF root:$5$sHvKf/M5OPdBuZZ$dz4qLOkTLGeCINX root:$5$hw4Vu/e34$/82lXu7ISrse.Ihk.qbqT root:$5$k1JOy/jRWZ$30YSk7kbhdKOjfDaiWVf root:$5$MnX.LUzqrB/B2$JuwqC.SmKFnMUWkEf root:$5$arRYf/PG$Xw6PpZNFO656p.Eb636iLt root:$5$5op/p8Hqs5$Nj2jA0Qxm80aG4fHW3oz root:$5$VHIT9/8yzZ$CpIK4ODps78GcqcsgiMT root:$5$.AlH7jBJoh/8$sjuVt.PcRH.vyvB3og root:$5$f7Ewinqm$nrJ2p/hKTuiEK//IfCTjth root:$5$N.dv/VCvrCADg$peSXfo35KN1dmbw/n root:$5$PSc4W./54l/SroH$CFFVOHRYK.Jj8Sp root:$5$8UBP3f4IcnAd/N1/$P.ud49qTStQ7Lw root:$5$qnXsZ/NlLZh/$nlaQVTS3FCJg1Jb2QG root:$5$xOpbbBqENR/7$boYJQzkCkZhRf7Uicf root:$5$V93tjZhzT$LrsIZWZmYo4ocRUvCixO6 root:$5$1MVz8/lf5oC/$rUKpnX23MhFx4.y2ZS
Environ la moitié des 6e caractères de hachage sont /
:
cat sixth-character-often-forward-slash.txt | cut --character=14 | sort
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
.
.
.
.
2
5
6
8
8
B
d
D
e
e
E
f
H
I
j
j
j
J
k
k
K
l
L
M
M
n
n
N
q
r
r
r
s
S
S
t
t
T
U
U
U
U
V
w
x
X
X
X
Z
Z
Z
Le format du hachage de mot de passe est $<type>$<salt>$<hash>
, Où <type>
5
Est un hachage basé sur SHA-256. Le sel contient généralement au moins 8 caractères (et se trouve dans les exemples de la question), le sixième caractère fait donc partie du sel.
Ces hachages sont probablement générés par une version de suite d'outils shadow (paquet src shadow
dans Debian, shadow-utils
dans CentOS)
J'ai essayé de savoir pourquoi, exactement, le code biaisait la barre oblique. (merci à @thrig d'avoir initialement trouvé le code.)
TLDR: C'est un peu intéressant, mais ça n'a pas d'importance.
Dans libmisc/salt.c
, Nous trouvons la fonction gensalt
qui appelle l64a
Dans une boucle:
strcat (salt, l64a (random()));
do {
strcat (salt, l64a (random()));
} while (strlen (salt) < salt_size);
La boucle prend un nombre aléatoire de random()
, le transforme en un morceau de chaîne et le concatène à la chaîne formant le sel. Répétez jusqu'à ce que suffisamment de personnages soient collectés.
Ce qui se passe dans l64a
Est cependant plus intéressant. La boucle interne génère un caractère à la fois à partir de la valeur d'entrée (qui provient de random()
):
for (i = 0; value != 0 && i < 6; i++) {
digit = value & 0x3f;
if (digit < 2) {
*s = digit + '.';
} else if (digit < 12) {
*s = digit + '0' - 2;
} else if (digit < 38) {
*s = digit + 'A' - 12;
} else {
*s = digit + 'a' - 38;
}
value >>= 6;
s++;
}
La première ligne de la boucle (digit = value & 0x3f
) Sélectionne six bits dans la valeur d'entrée et les clauses if
transforment la valeur formée par celles-ci en caractère. (.
Pour zéro, /
Pour un, 0
Pour deux, etc.)
l64a
Prend un long
mais les valeurs sorties par random()
sont limitées à Rand_MAX
, Qui semble être 2147483647 ou 2 ^ 31 - 1 sur glibc. Ainsi, la valeur qui va à l64a
Est un nombre aléatoire de 31 bits. En prenant 6 bits à la fois ou une valeur de 31 bits, nous obtenons cinq caractères répartis de manière raisonnablement égale, plus un sixième qui ne provient que d'un bit!
Le dernier caractère généré par l64a
Ne peut cependant pas être un .
, Car la boucle a également la condition value != 0
Et au lieu d'un .
Comme sixième caractère , l64a
Ne renvoie que cinq caractères. Par conséquent, la moitié du temps, le sixième caractère est un /
Et la moitié du temps l64a
Renvoie cinq caractères ou moins. Dans ce dernier cas, un l64a
Suivant peut également générer une barre oblique dans les premières positions, donc dans un sel complet, le sixième caractère devrait être une barre oblique un peu plus de la moitié du temps.
Le code a également une fonction pour randomiser la longueur du sel, c'est 8 à 16 octets. Le même biais pour le caractère barre oblique se produit également avec d'autres appels à l64a
, Ce qui ferait que les 11e et 12e caractères auraient également une barre oblique plus souvent qu'autre chose. Les 100 sels présentés dans la question ont respectivement 46 barres obliques en sixième position et 13 et 15 en 11e et 12e position. (un peu moins de la moitié des sels ont moins de 11 caractères).
Sur Debian, je ne pouvais pas reproduire cela avec un chpasswd
droit comme indiqué dans la question. Mais chpasswd -c SHA256
Montre le même comportement. Selon le manuel, l'action par défaut, sans -c
, Est de laisser PAM gérer le hachage, donc apparemment PAM sur Debian utilise au moins un code différent pour générer le sel. Cependant, je n'ai regardé le code PAM sur aucune distribution.
(La version précédente de cette réponse indiquait que l'effet n'apparaissait pas sur Debian. Ce n'était pas correct.)
Mais est-ce important? Comme l'a commenté @RemcoGerlich, ce n'est quasiment qu'une question d'encodage. Cela fixera efficacement certains bits du sel à zéro, mais il est probable que cela n'aura aucun effet significatif dans ce cas, car l'origine de ces bits est cet appel à srandom
dans seedRNG
:
srandom (tv.tv_sec ^ tv.tv_usec ^ getpid ());
Ceci est une variante de la coutume que vous avez de semer un RNG avec l'heure actuelle. (tv_sec
Et tv_usec
Sont les secondes et les microsecondes de l'heure actuelle, getpid()
donne l'ID du processus si le processus est en cours.) Comme l'heure et les PID ne sont pas très imprévisibles , la quantité d'aléatoire ici n'est probablement pas supérieure à ce que le codage peut contenir.
L'heure et le PID ne sont pas quelque chose que vous aimeriez créer touches avec, mais peuvent être assez imprévisibles pour les sels. Les sels doivent être distincts pour empêcher le test en force brute de plusieurs hachages de mot de passe avec un seul calcul, mais devrait être également imprévisible, pour empêcher ou ralentir le précalcul ciblé, qui pourrait être utilisé pour raccourcir le délai entre l'obtention des hachages de mot de passe et l'obtention des mots de passe réels.
Même avec les légers problèmes, tant que l'algorithme ne génère pas le même sel pour différents mots de passe, ça devrait aller. Et cela ne semble pas le cas, même lors de la génération de quelques dizaines dans une boucle, comme le montre la liste de la question.
De plus, le code en question n'est utilisé que pour générer des sels pour les mots de passe, il n'y a donc aucune implication sur les problèmes ailleurs.
Pour les sels, voir également, par ex. ceci sur Stack Overflow et ceci sur security.SE .
En conclusion, il n'y a rien de mal avec votre système. Il est plus utile de s'assurer que vos mots de passe sont bons et qu'ils ne sont pas utilisés sur des systèmes indépendants.
Ce caractère fait partie du sel selon le manuel crypt(3)
. Étant donné que la longueur du sel (la chaîne entre les $5$
ID et suivants $
) varie pour les hachages exposés, je ne sais pas exactement ce qu'illustre le choix d'un caractère aléatoire dans cette colonne particulière pour quelques mots de passe.
En revanche, le /
est plutôt plus répandu (102 instances) dans le sel entier par rapport aux autres caractères possibles (environ 18), donc quelque chose dans chpasswd
semble favoriser ce caractère dans le sel;
for x in `seq 1 100000`; do
echo testacct:asdfasdfasdf | chpasswd -c SHA256
awk -F: '/testacct/{print $2}' /etc/shadow | awk -F\$ '{print $3}' >> salts
done
Perl -nle 'print for m/(.)/g' salts | sort | uniq -c | sort -nr | head -5
sur un système RedHat EL 6, il se produit:
1006 /
195 X
193 U
193 q
193 e
Et, oui, le code dans shadow-utils-4.1.5.1-5.el6
montre un biais envers /
ce qui pourrait faciliter les attaques par dictionnaire:
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// these next two borrowed from libmisc/salt.c of shadow-4.1.5.1 from
// Centos 6.8 RPM at http://vault.centos.org/6.8/os/Source/SPackages/shadow-utils-4.1.5.1-5.el6.src.rpm
char *l64a(long value)
{
static char buf[8];
char *s = buf;
int digit;
int i;
if (value < 0) {
abort();
}
for (i = 0; value != 0 && i < 6; i++) {
digit = value & 0x3f;
if (digit < 2) {
*s = digit + '.';
} else if (digit < 12) {
*s = digit + '0' - 2;
} else if (digit < 38) {
*s = digit + 'A' - 12;
} else {
*s = digit + 'a' - 38;
}
value >>= 6;
s++;
}
*s = '\0';
return (buf);
}
static void seedRNG(void)
{
struct timeval tv;
static int seeded = 0;
if (0 == seeded) {
(void) gettimeofday(&tv, NULL);
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
seeded = 1;
}
}
int main(void)
{
seedRNG();
for (int x = 0; x < 1000; x++) {
printf("%s\n", l64a(random()));
}
exit(0);
}
Ce qui se traduit par:
% ./salttest | Perl -nle 'print for m/(.)/g' | sort | uniq -c | sort -nr | head -3
593 /
96 8
93 3
Et puis en utilisant les mêmes routines que les dernières https://github.com/shadow-maint/shadow/blob/master/libmisc/salt.c nous constatons qu'il y a toujours un biais vers /
. Donc euh ouais c'est un bug qui devrait être corrigé donc /
n'est pas tant favorisé, car idéalement les caractères de sel devraient être également pondérés.
mkpasswd(1)
peut être un frontal pour crypt(3)
, mais ce n'est pas la même chose que d'exécuter chpasswd(1)
, qui fait partie du paquetage "shadow-utils" sur CentOS et "passwd" sur Debian. Au lieu de cela, vous devez comparer les pommes aux pommes. Considérez le script suivant:
#!/bin/bash
# This repeatedly changes a `saltuser' password
# and grabs the salt out of /etc/shadow.
# Requires root and the existence of `saltuser' user.
if [ $EUID -ne 0 ]; then
echo "This script requires root access to read /etc/shadow."
exit 1
fi
grep -q saltuser /etc/passwd
if [ $? -ne 0 ]; then
echo "This script requires the 'saltuser' to be present."
exit 2
fi
: > /tmp/salts.txt
for i in {1..1000}; do
PW=$(tr -cd '[[:print:]]' < /dev/urandom | head -c 64)
echo "saltuser:${PW}" | chpasswd -c SHA256 -s 0 2> /dev/urandom
awk -F '$' '/^saltuser/ {print $3}' /etc/shadow >> /tmp/salts.txt
done
while read LINE; do
# 6th character in the salt
echo ${LINE:5:1}
done < /tmp/salts.txt | sort | uniq -c | sort -rn
Sortie de Debian Sid:
512 /
14 T
13 W
13 v
13 t
12 x
12 m
12 d
11 p
11 L
11 F
11 4
10 s
10 l
10 g
10 f
10 7
10 6
9 Z
9 w
9 N
9 H
9 G
9 E
9 A
8 Y
8 X
8 r
8 O
8 j
8 c
8 B
8 b
8 9
7 u
7 R
7 q
7 P
7 M
7 k
7 D
6 z
6 y
6 U
6 S
6 K
6 5
5 V
5 Q
5 o
5 J
5 I
5 i
5 C
5 a
5 3
4 n
4 h
4 e
4 2
4 0
4 .
3 8
3 1
Sortie de CentOS 7:
504 /
13 P
13 B
12 s
12 Z
11 e
11 Y
11 O
11 L
11 G
10 w
10 u
10 q
10 i
10 h
10 X
10 I
10 E
9 x
9 g
9 f
9 W
9 F
9 C
9 9
9 8
8 v
8 t
8 c
8 b
8 S
8 H
8 D
8 0
7 z
7 y
7 o
7 k
7 U
7 T
7 R
7 M
7 A
7 6
7 4
7 1
6 p
6 d
6 a
6 Q
6 J
6 5
6 .
5 r
5 m
5 j
5 V
5 3
5 2
4 n
4 l
4 N
4 K
3 7
Ainsi, le problème n'est pas propre à CentOS, mais vient probablement de l'amont d'où les deux projets tirent.