lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20190430183026.31982-1-yonhan@cisco.com>
Date:   Tue, 30 Apr 2019 14:30:26 -0400
From:   Yongkui Han <yonhan@...co.com>
To:     linux-kernel@...r.kernel.org
Cc:     Mehmet Kayaalp <mkayaalp@...ux.vnet.ibm.com>,
        David Howells <dhowells@...hat.com>,
        Yongkui Han <yonhan@...co.com>
Subject: [PATCH] Brute-force binary-scanning method to extract certificates

Hi Mehmet, David,

I came up with an enhancement to the scripts/extract-sys-certs.pl script
in Linux kernel source, so that the system certificates can be extracted
without debugging symbols or System.map file.

The idea is that the DER-format kernel X509 certificate follows some fixed
pattern. So we can search this fixed pattern to find the location of the
system certificate.

Specifically, below is the pattern I used:

    ($type0, $len0, $type1, $len1, $ver_attr) =
        unpack "nnnnN", substr($fb, $start, 12);
    if ($type0 == 0x3082 && $type1 == 0x3082 && $ver_attr == 0xa0030201) {
        my $certsize = $len0 + 4;
        printf "Have %u bytes of certificate at file offset 0x%x\n",
               $certsize, $start;

That is, in the first 12 bytes, two 2-bytes must be 0x3082, and one 4-bytes
must be 0xa0030201. The length of the certificate is also in one of the
2-bytes of the first 12 bytes array.

The script works by scanning the vmlinux file to find this fixed pattern,
and extract the certificate. I think it can be optimized to only scan the
.init* section, instead of the whole vmlinux file. However, the current
approach seems already sufficient.

I have tested with different vmlinux files, and the script works well.

The new script output is like below:

[ bxb-ads-339 ] ./extract-sys-certs.pl  vmlinux  sys_cert.x509
Have 33 sections
No symbols available, will try brute-force approach.
Length of vmlinux is: 21354392
Have 1063 bytes of certificate at file offset 0x12f99b8
Have 1070 bytes of certificate at file offset 0x12f9ddf
Have 851 bytes of certificate at file offset 0x12fa20d
Have 1188 bytes of certificate at file offset 0x12fa560
Length of extracted certificate is: 4172
[ bxb-ads-339 ]

There is no change if vmlinux contains debugging symbols or System.map
file is provided.

Enclosed please find the git diff output. I can also send you the patch
with “git format-patch” and “git send-email” for you to review.

If possible, can you also try the enhanced script with a few kernel
vmlinux files to test it?

Your review/comments are greatly appreciated.

Thanks,
Yongkui

Signed-off-by: Yongkui Han <yonhan@...co.com>
---
 scripts/extract-sys-certs.pl | 44 +++++++++++++++++++++++++++++++++---
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/scripts/extract-sys-certs.pl b/scripts/extract-sys-certs.pl
index fa8ab15118cc..06f515b4dcd6 100755
--- a/scripts/extract-sys-certs.pl
+++ b/scripts/extract-sys-certs.pl
@@ -86,6 +86,44 @@ if ($nr_symbols == 0 && $sysmap ne "") {
     parse_symbols(@lines);
 }
 
+my $buf = "";
+my $len= 0;
+my $size = 0;
+
+if ($nr_symbols == 0 && $sysmap eq "") {
+    print "No symbols available, will try brute-force approach\n";
+
+    my $filesize = -s $vmlinux;
+    open FD, "<$vmlinux" || die $vmlinux;
+    binmode(FD);
+    my $fb;
+    my $len = sysread(FD, $fb, $filesize);
+    die "$vmlinux" if (!defined($len));
+    die "Short read on $vmlinux\n" if ($len != $filesize);
+    close(FD) || die $vmlinux;
+    printf "Length of vmlinux is: %d\n", length($fb);
+
+    my ($type0, $len0, $type1, $len1, $ver_attr) = (0,0,0,0,0);
+    my $start = 0;
+    while ($start < $filesize - 256) {
+        ($type0, $len0, $type1, $len1, $ver_attr) =
+            unpack "nnnnN", substr($fb, $start, 12);
+        if ($type0 == 0x3082 && $type1 == 0x3082 && $ver_attr == 0xa0030201) {
+            my $certsize = $len0 + 4;
+            printf "Have %u bytes of certificate at file offset 0x%x\n",
+                   $certsize, $start;
+            $buf .= substr($fb, $start, $certsize);
+            $start += $certsize;
+        } else {
+            $start += 1;
+        }
+    }
+
+    $size = length($buf);
+    printf "Length of extracted certificate is: %d\n", $size ;
+}
+else {
+
 die "No symbols available\n"
     if ($nr_symbols == 0);
 
@@ -97,7 +135,6 @@ die "Can't find system certificate list"
 
 my $start = Math::BigInt->new($symbols{"__cert_list_start"});
 my $end;
-my $size;
 my $size_sym = Math::BigInt->new($symbols{"system_certificate_list_size"});
 
 open FD, "<$vmlinux" || die $vmlinux;
@@ -145,12 +182,13 @@ my $foff = $start - $s->{vma} + $s->{foff};
 printf "Certificate list at file offset 0x%x\n", $foff;
 
 die $vmlinux if (!defined(sysseek(FD, $foff, SEEK_SET)));
-my $buf = "";
-my $len = sysread(FD, $buf, $size);
+$len = sysread(FD, $buf, $size);
 die "$vmlinux" if (!defined($len));
 die "Short read on $vmlinux\n" if ($len != $size);
 close(FD) || die $vmlinux;
 
+}  ### end of ($nr_symbols == 0 && $sysmap eq "") else block
+
 open FD, ">$keyring" || die $keyring;
 binmode(FD);
 $len = syswrite(FD, $buf, $size);
-- 
2.19.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ