Messages in this thread Patch in this message | | | From | Yongkui Han <> | Subject | [PATCH] Brute-force binary-scanning method to extract certificates | Date | Tue, 30 Apr 2019 14:30:26 -0400 |
| |
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@cisco.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
| |