CVE-2022-1292的分析
2022-9-14 21:40:35 Author: xz.aliyun.com(查看原文) 阅读量:65 收藏

由于本人不会perl, 目前也没有别的文章参考, 所以有问题还请大家包容, 指正

漏洞信息

openssl中的c_rehash存在命令注入, 允许以c_reash脚本的权限执行命令

在某些系统下, c_rehash会被自动运行, 可能可以用于提权, 漏洞详情

复现

在版本1.1.1n下复现

创建文件名带有"`"的文件

并在第一行写入-----BEGIN CERTIFICATE-----以通过c_rehash对文件内容的检查

然后在当前文件夹执行c_rehash .

可以发现一个名为happi0的空文件被创建

分析与调试

c_rehash部分内容如下, 省去了无关部分

# 将命令行传入的参数赋值给dirlist
if (@ARGV) {
    @dirlist = @ARGV;
} elsif ($ENV{SSL_CERT_DIR}) {
    @dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR};
} else {
    $dirlist[0] = "$dir/certs";
}

# 判断$dirlist[0]目录是否存在, 并把工作目录修改为$dirlist[0]
# 检查是否存在$openssl是否存在 
if (-d $dirlist[0]) {
    chdir $dirlist[0];
    $openssl="$pwd/$openssl" if (!-x $openssl);
    chdir $pwd;
}

# 检查$dirlist中的每一项是否存在且可写, 均满足则调用hash_dir函数
foreach (@dirlist) {
    if (-d $_ ) {
            if ( -w $_) {
        hash_dir($_);
            } else {
                print "Skipping $_, can't write\n";
                $errorcount++;
            }
    }
}
exit($errorcount);

接下来进入hash_dir函数, 也就是存在问题的函数

sub hash_dir {
    my %hashlist;
    print "Doing $_[0]\n";
    chdir $_[0];
    opendir(DIR, ".");
    # 问题出在这里, 将改目录所有文件名读入到flist数组, 但没有处理, 导致文件名命令注入的可能
    my @flist = sort readdir(DIR);
    closedir DIR;
    if ( $removelinks ) {
        # Delete any existing symbolic links
        foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) {
            if (-l $_) {
                print "unlink $_" if $verbose;
                unlink $_ || warn "Can't unlink $_, $!\n";
            }
        }
    }
    # 这里也不是很严格, 仅仅需要文件名含有所定义的关键字即可
    FILE: foreach $fname (grep {/\.(pem)|(crt)|(cer)|(crl)$/} @flist) {
        # Check to see if certificates and/or CRLs present.
        my ($cert, $crl) = check_file($fname);
        if (!$cert && !$crl) {
            print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n";
            next;
        }
        link_hash_cert($fname) if ($cert);
        link_hash_crl($fname) if ($crl);
    }
}

使用perl -d c_rehash "."调试一下, perl自带的调式工具, 用法和gdb差不多

可以看到$flist[5]文件名中存在"`"特殊字符

由于文件名检测, 只要求文件名含有(pem)|(crt)|(cer)|(crl)字符即可, 所以恶意文件名通过检测, 进入check_file函数

sub check_file {
    my ($is_cert, $is_crl) = (0,0);
    my $fname = $_[0];
    open IN, $fname;
    while(<IN>) {
        if (/^-----BEGIN (.*)-----/) {
            my $hdr = $1;
            if ($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) {
                $is_cert = 1;
                last if ($is_crl);
            } elsif ($hdr eq "X509 CRL") {
                $is_crl = 1;
                last if ($is_cert);
            }
        }
    }
    close IN;
    return ($is_cert, $is_crl);
}

主要检查文件头格式是否满足要求, 这样是为什么最开始制作恶意文件需要带上-----BEGIN CERTIFICATE-----

检查通过到link_hash_cert函数也就是注入发生的函数

sub link_hash_cert {
        my $fname = $_[0];
        # 这里对于传过来的参数也没有检查, 并且把$fname直接用``包裹, 直接执行命令, 导致$fname中的命令也可以执行
        $fname =~ s/\"/\\\"/g;
        my ($hash, $fprint) = `"$openssl" x509 $x509hash -fingerprint -noout -in "$fname"`;
...

link_hash_crl的情况和link_hash_cert的情况相似, 就不跟踪了

修复分析和思考

官方修复

主要增加一个了如下一个函数

核心在于以下这句

if (!open($fh, "-|", @_)) {

在调试状态下看一下

@_其实是执行的一个命令, 由于debug模式下没有显示空格, 但也能大致看出是执行和原本通过"`"执行的一样的一条命令

这里是一个perl的特殊用法, 找到资料

可以通过open执行命令, 这里我进行了一些测试, 确实解决了命令注入的问题, 但和官方建议的写法不一样

后来找到了资料, 他说用two-argument form of open()不安全, 所以意思大概是用三个参数就是比较安全的叭

这里写法和官方建议不一样, 可能会出现一定问题, 但由于本人能力有限, 并不理解开发者这样写的深意, 又或者只是因为开发者图方便?

希望有大佬可以指教.


文章来源: https://xz.aliyun.com/t/11703
如有侵权请联系:admin#unsafe.sh