由于本人不会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()
不安全, 所以意思大概是用三个参数就是比较安全的叭
这里写法和官方建议不一样, 可能会出现一定问题, 但由于本人能力有限, 并不理解开发者这样写的深意, 又或者只是因为开发者图方便?
希望有大佬可以指教.