Skip to main content
bias is in the shadow code
Source Link
thrig
  • 35.9k
  • 4
  • 70
  • 88
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

... and now to look at how And, yes, the code in shadow-utils-4.1.5.1-5.el6 exhibits a bias towards / which might make dictionary attacks easier:

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

Which results in:

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

And then using the same routines from the latest https://github.com/shadow-maint/shadow/blob/master/libmisc/salt.c we find that salt is being generatedthere's still a bias towards ./. So uh yeah this is a bug that should be patched so / is not favored so much, as ideally the salt characters should be equally weighted.

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

... and now to look at how that salt is being generated ...

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

And, yes, the code in shadow-utils-4.1.5.1-5.el6 exhibits a bias towards / which might make dictionary attacks easier:

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

Which results in:

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

And then using the same routines from the latest https://github.com/shadow-maint/shadow/blob/master/libmisc/salt.c we find that there's still a bias towards /. So uh yeah this is a bug that should be patched so / is not favored so much, as ideally the salt characters should be equally weighted.

bias
Source Link
thrig
  • 35.9k
  • 4
  • 70
  • 88

That character is part of the salt per the crypt(3) manual. Given that the length of the salt (the string between the $5$ ID and subsequent $) varies for the exhibited hashes, I'm not exactly sure what picking a random character from that particular column for a few passwords illustrates.

On the other hand, the / is rather more prevalent (102 instances) in the entire salt compared to the other possible characters (around 18), so something in chpasswd does appear to favor that character in the salt;

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

on a RedHat EL 6 system run turns up:

   1006 /
    195 X
    193 U
    193 q
    193 e

... and now to look at how that salt is being generated ...

That character is part of the salt per the crypt(3) manual. Given that the length of the salt (the string between the $5$ ID and subsequent $) varies for the exhibited hashes, I'm not exactly sure what picking a random character from that particular column for a few passwords illustrates.

That character is part of the salt per the crypt(3) manual. Given that the length of the salt (the string between the $5$ ID and subsequent $) varies for the exhibited hashes, I'm not exactly sure what picking a random character from that particular column for a few passwords illustrates.

On the other hand, the / is rather more prevalent (102 instances) in the entire salt compared to the other possible characters (around 18), so something in chpasswd does appear to favor that character in the salt;

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

on a RedHat EL 6 system run turns up:

   1006 /
    195 X
    193 U
    193 q
    193 e

... and now to look at how that salt is being generated ...

Source Link
thrig
  • 35.9k
  • 4
  • 70
  • 88

That character is part of the salt per the crypt(3) manual. Given that the length of the salt (the string between the $5$ ID and subsequent $) varies for the exhibited hashes, I'm not exactly sure what picking a random character from that particular column for a few passwords illustrates.