커널 모듈 서명을 확인하는 방법은 무엇입니까?

커널 모듈 서명을 확인하는 방법은 무엇입니까?

커널 소스 코드를 컴파일할 때 커널 모듈 서명 옵션을 사용하도록 선택할 수 있습니다 CONFIG_MODULE_SIG*. 이 modinfo도구는 모듈 서명을 확인하는 작업을 처리하도록 되어 있지만 수년에 걸쳐 몇 가지 버그가 있었고 도구는 더 이상 작업을 수행할 수 없습니다. 내가 얻는 것은 다음과 같습니다.

sig_id:         PKCS#7
signer:
sig_key:
sig_hashalgo:   md4
signature:      30:82:02:F4:06:09:2A:86:48:86:F7:0D:01:07:02:A0:82:02:E5:30:
                ...

따라서 키가 없으며 해싱 알고리즘은 md4이며 커널에서 컴파일되지도 않습니다.

그렇다면 모듈 서명을 수동으로 확인하고 검증하는 방법은 무엇입니까? 가능합니까?

답변1

예, 가능합니다. 하지만 매우 복잡합니다.

먼저 모듈 서명을 추출해야 합니다. 다음을 사용할 수 있습니다.extract-module.sig.pl커널 소스 코드의 스크립트:

$ scripts/extract-module-sig.pl -s MODULE.ko >/tmp/modsig.
Read 789006 bytes from module file
Found magic number at 789006
Found PKCS#7/CMS encapsulation
Found 670 bytes of signature [3082029a06092a864886f70d010702a0]

둘째, 사용할 수 있는 인증서와 공개 키를 추출해야 합니다.extract-sys-certs.pl스크립트:

$ scripts/extract-sys-certs.pl /PATH/TO/vmlinux /tmp/cert.x509
Have 32 sections
Have 28167 symbols
Have 1346 bytes of certs at VMA 0xffffffff81be6db8
Certificate list in section .init.data
Certificate list at file offset 0xde6db8
$ openssl x509 -pubkey -noout -inform der -in /tmp/cert.x509 -out /tmp/pubkey

certs/signing_key.x509Linux 커널의 빌드 디렉터리에 있는 파일에서 공개 키를 추출 할 수도 있습니다 .certs/signing_key.pem

이 작업을 완료하면 필요한 모든 데이터가 확보되며 /tmp/modsigPKCS /tmp/cert.x509#7 서명을 확인하는 데 필요한 12개 정도의 단계를 진행할 수 있습니다.

이 블로그를 확인하실 수 있습니다우편 엽서전체 레시피.


나는 전체 프로세스(해당 extract-certs.pl단계 제외)를 Perl 스크립트에 넣으려고 합니다.

다음과 같이 사용할 수 있습니다.

perl checkmodsig.pl /path/to/cert.x509 mod1.ko mod2.ko ...

청소년MMV. 저는 sha512 서명이 있는 맞춤형 커널만 사용해 보았습니다. 물론 느리고 깨지기 쉬운 호출로 장난을 치는 것보다 openssl 라이브러리를 사용하여 직접 수행하는 것이 더 좋습니다 openssl x509.asn1parsersautl

checkmodsig.pl

use strict;

sub through {
    my ($cmd, $data, $cb) = @_;
    use IPC::Open2;
    my $pid = open2 my $from, my $to, ref $cmd ? @$cmd : $cmd;
    print $to $data; close $to; my $out;
    if($cb){ while(<$from>){ last if $out = $cb->($_) } }
    else { local $/; $out = <$from>; }
    waitpid ($pid, 0);
    die "status $?" if $? != 0;
    $out;
}
sub gethash {
    my ($d) = @_; my ($alg, $hash);
    through [qw(openssl asn1parse -inform der)], $d, sub {
        if(/(\d+):d=\d+ +hl= *(\d+) +l= *(\d+) +prim: +OCTET STRING/){
            $hash = substr $d, $1 + $2, $3
        }elsif(/prim: +OBJECT +:(sha\w+)/){
            $alg = $1;
        }
        undef
    };
    $alg, $hash
}

use File::Temp;
my $tf = new File::Temp;
my $pub_key;
my @type = qw(PGP X509 PKCS7);
my $r = 0;
if((my $cert = shift) =~ /(\.x509)$|\.pem$/i){
    $pub_key = $tf->filename;
    system qw(openssl x509 -pubkey -noout),
        '-inform', $1 ? 'der' : 'pem',
        '-in', $cert, '-out', $pub_key;
    die "status $?" if $? != 0;
}
die "no certificate/key file" unless $pub_key;
for my $kof (@ARGV){
    open my $ko, '<', $kof or die "open $kof: $!\n";
    seek $ko, -4096, 2 or die "seek: $!";
    read $ko, my $d, 4096 or die "read: $!";
    my ($algo, $hash, $type, $signer_len, $key_id_len, $sig_len, $magic) =
        unpack 'C5x3Na*', substr $d, -40;
    die "no signature in $kof"
        unless $magic eq "~Module signature appended~\n";
    die "this script only knows about PKCS#7 signatures"
        unless $type[$type] eq 'PKCS7';

    my $hash = gethash substr $d, - 40 - $sig_len, $sig_len;
    die "hash not found" unless $hash;

    my ($alg, $vhash) = gethash
        through [qw(openssl rsautl -verify -pubin -inkey), $pub_key],
            $hash;

    seek $ko, 0, 0 or die "seek: $!";
    read $ko, my $d, (-s $ko) - $sig_len - 40 or die "read: $!";
    use Digest::SHA;
    my $fhash = new Digest::SHA($alg)->add($d)->digest;

    if($fhash eq $vhash){
        print "OK $kof\n";
    }else{
        print "**FAIL** $kof\n";
        $r = 1;
        warn 'orig=', unpack('H*', $vhash), "\n";
        warn 'file=', unpack('H*', $fhash), "\n";
    }
}
exit $r;

관련 정보