Linux에서 비밀번호 해싱의 6번째 문자는 무엇입니까? 왜 보통 슬래시입니까?

Linux에서 비밀번호 해싱의 6번째 문자는 무엇입니까? 왜 보통 슬래시입니까?

Linux에서 에 저장된 비밀번호 해시의 여섯 번째 문자는 무엇입니까 /etc/shadow?

shuf내 강아지 스타일의 Linux 시스템에서 100개의 무작위 비밀번호를 사용하고 생성하려고 하면 /dev/urandom6번째 문자가 /절반 정도 됩니다.

내 문제는 매번 CD에서 재부팅하므로 생산 목적이 아닙니다. 이는 내 시스템이 어떤 방식으로든 잘못 구성되었거나 안전하지 않음을 의미합니까?

shuf링크 인지 확인하기 위해 파일을 실행해봤습니다 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

여기 링크 shuf는 아닌 것 같아요 .busybox

ls -l /usr/bin/shuf

    -rwxr-xr-x 1 root root 41568 Mar  7  2015 /usr/bin/shuf

하지만

ls -l /bin/wget

    lrwxrwxrwx 1 root root 14 Apr 29 03:49 wget -> ../bin/busybox

내가 한 일에 대한 대략적인 아이디어는 다음과 같습니다.

# ! / 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

6번째 해시 문자 중 약 절반은 다음과 같습니다 /.

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

답변1

해시 형식 및 소스

비밀번호 해시의 형식은 SHA-256 기반 해시입니다 $<type>$<salt>$<hash>. <type> 5Salt는 일반적으로 길이가 8자 이상이므로(질문의 예에서는) 여섯 번째 문자가 Salt의 일부입니다.

이 해시는 아마도 다음 버전에 의해 생성되었을 것입니다.그림자 도구 키트(소스 패키지shadow데비안에서는shadow-utilsCentOS에서)

코드가 슬래시 쪽으로 편향된 이유가 정확히 무엇인지 알아내려고 노력 중입니다. (원래 코드를 파헤친 @thrig에게 감사드립니다.)

요약: 좀 웃기긴 하지만 괜찮아요.


소금을 생성하는 코드

루프에서 호출되는 함수를 libmisc/salt.c찾습니다 .gensaltl64a

strcat (salt, l64a (random()));
do {
       strcat (salt, l64a (random()));
} while (strlen (salt) < salt_size);

루프는 그것으로부터 임의의 숫자를 가져와 random()문자열의 일부로 변환하고 솔트를 형성하는 문자열에 연결합니다. 충분한 문자가 수집될 때까지 반복합니다.

하지만 일어난 일은 l64a훨씬 더 흥미로웠습니다. 내부 루프는 (에서) 입력 값을 기반으로 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++;
}

루프( digit = value & 0x3f)의 첫 번째 줄은 입력 값에서 6비트를 선택하고, 절은 if이러한 값으로 구성된 값을 문자로 변환합니다. ( .0, /1, 02 등)

l64aa를 허용 long하지만 출력 값은 glibc에서 2147483647 또는 2^31 - 1로 보이는 로 random()제한됩니다 . RAND_MAX따라서 전송되는 값 l64a은 31비트 난수입니다. 한 번에 6비트 또는 31비트 값을 사용하면 합리적으로 균등한 간격의 문자 5개와 단 1비트에서 6번째 문자를 얻을 수 있습니다!

그러나 생성된 마지막 문자는 루프에도 조건이 있기 때문에 l64aa가 될 수 없으며 a는 여섯 번째 문자 가 아닌 5개의 문자만 반환됩니다. 따라서 절반의 경우 6번째 문자는 이고 나머지 절반은 5자 이하를 반환합니다. 후자의 경우 후속 문자 도 첫 번째 위치에 슬래시를 생성할 수 있으므로 전체 솔트에서 여섯 번째 문자는 절반 이상 슬래시여야 합니다..value != 0.l64a/l64al64a

코드에는 솔트의 길이(8~16바이트)를 무작위로 지정하는 기능도 있습니다. 슬래시 문자의 동일한 편향은 추가 호출에서도 발생하므로 l64a11번째 및 12번째 문자에도 다른 문자보다 슬래시가 더 자주 사용됩니다. 문제에 제시된 100개의 솔트에는 6번째 위치에 46개의 슬래시가 있고, 11번째 위치와 12번째 위치에 각각 13개와 15개의 슬래시가 있습니다. (솔트의 절반 미만이 11자보다 짧습니다.)

데비안에서

데비안에서는 chpasswd질문에 표시된 직선으로 이것을 재현할 수 없습니다. 그러나 chpasswd -c SHA256동일한 동작을 나타냅니다. 매뉴얼에 따르면 기본 작업( 없이 -c)은 PAM이 해시를 처리하도록 하는 것이므로 데비안의 PAM은 최소한 다른 코드를 사용하여 솔트를 생성합니다. 그러나 나는 어떤 배포판에서도 PAM 코드를 살펴보지 않았습니다.

(이 답변의 이전 버전에서는 이 효과가 데비안에 나타나지 않는다고 명시했습니다. 이는 올바르지 않습니다.)

소금의 의미와 요구사항

하지만 그게 중요할까요? @RemcoGerlich가 언급했듯이 이는 코딩 문제일 뿐입니다. 솔트의 일부 비트를 0으로 효과적으로 수정하지만 해당 비트의 출처가 in 에 대한 srandom호출 이므로 이 경우에는 큰 영향을 미치지 않을 것입니다 seedRNG.

srandom (tv.tv_sec ^ tv.tv_usec ^ getpid ());

이는 현재 시간에 RNG를 시드하는 고대 관습의 변형입니다. ( tv_sec그리고 실행 중인 프로세스인 경우 프로세스 ID를 제공하는 tv_usec현재 시간의 초와 마이크로초입니다 getpid().) 시간과 PID는 예측할 수 없는 것이 아니므로 여기서 임의성의 양은 인코딩이 수용할 수 있는 것보다 크지 않을 것입니다.

시간과 PID는 당신이 만들고 싶은 것이 아닙니다열쇠하지만 소금으로는 예측할 수 없습니다. 염류~ 해야 하다단일 계산으로 여러 비밀번호 해시에 대한 무차별 대입 테스트를 방지하는 것은 다르지만~해야 한다또한 예측할 수 없는 대상 사전 계산을 방지하거나 속도를 늦추는데, 이는 비밀번호 해시를 가져오는 데서 실제 비밀번호를 가져오는 데 걸리는 시간을 단축하는 데 사용할 수 있습니다.

사소한 문제가 있더라도 알고리즘이 서로 다른 비밀번호에 대해 동일한 솔트를 생성하지 않는 한 괜찮습니다. 질문의 목록에서 알 수 있듯이 루프에서 수십 개를 생성해도 작동하지 않는 것 같습니다.

또한 문제의 코드는 비밀번호 솔트 생성 이외의 용도로 사용되지 않으므로 다른 곳에서는 문제에 영향을 미치지 않습니다.

소금에 대해서는 다음을 참조하세요.이것은 스택 오버플로에 있습니다그리고보안에 대해. SE.

결론적으로

결론은 시스템에는 문제가 없다는 것입니다. 비밀번호가 양호하고 관련 없는 시스템에서 사용되지 않는지 확인하는 것은 고려할 가치가 있는 것 이상입니다.

답변2

매뉴얼에 따르면 이 문자는 salt 의 일부입니다 crypt(3). 솔트의 길이( $5$ID와 뒤에 오는 문자열 $)가 표시된 해시 값에 따라 달라지므로 몇 가지 비밀번호에 대해 해당 특정 열에서 임의의 문자를 선택하는 것이 무엇인지 잘 모르겠습니다.

반면에,/ (102개 인스턴스)에서 더 일반적임모두소금의 특정 캐릭터는 다른 가능한 캐릭터(약 18개)에 비해 chpasswd소금의 해당 캐릭터를 선호하는 것 같습니다 .

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

RedHat EL 6 시스템에서 실행하면 다음이 나타납니다.

   1006 /
    195 X
    193 U
    193 q
    193 e

예, 의 코드에는 사전 공격을 더 쉽게 만들 수 있는 편향이 있습니다 shadow-utils-4.1.5.1-5.el6./

#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);
}

결과 :

% ./salttest | perl -nle 'print for m/(.)/g' | sort | uniq -c | sort -nr | head -3
 593 /
  96 8
  93 3

그런 다음 최신 동일한 루틴을 사용하십시오.https://github.com/shadow-maint/shadow/blob/master/libmisc/salt.c우리는 편견이 여전히 존재한다는 것을 발견했습니다 /. 그래서 어, 예, 이것은 패치되어야 하는 버그이므로 /이상적으로는 솔트 문자가 동일한 가중치를 가져야 하기 때문에 덜 선호됩니다.

답변3

mkpasswd(1)의 프런트엔드일 수 있지만 CentOS의 "shadow-utils" 패키지와 Debian의 "passwd" 패키지의 일부인 를 crypt(3)실행하는 것과는 다릅니다 . chpasswd(1)대신 사과를 사과와 비교해야 합니다. 다음 스크립트를 고려해보세요.

#!/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

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

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

따라서 이 문제는 CentOS에만 국한된 것이 아니라 두 프로젝트가 모두 상주하는 업스트림에서 발생할 가능성이 높습니다.

관련 정보