Re: zlibscan : script to find suid binaries possibly affected by zlib vulnerability

From: Florian Weimer (Weimerat_private-Stuttgart.DE)
Date: Wed Mar 13 2002 - 10:53:41 PST

  • Next message: Bernd Jendrissek: "Re: zlibscan : script to find suid binaries possibly affected by zlib vulnerability"

    hologram <holoat_private> writes:
    
    > The following is a quick shell script to find suid binaries that are
    > potentially affected by the zlib vulnability (i.e., those dynamically
    > linked).
    
    The hard case are statically linked binaries, though.  I've written a
    short Perl script which scans for signatures found in compiled zlib
    code.  The signatures are based on zlib's data tables, not machine
    code, so they are fairly architecture-independent (modulo the 32/64
    bit and endian issues addressed by the script).
    
    -- 
    Florian Weimer 	                  Weimerat_private-Stuttgart.DE
    University of Stuttgart           http://CERT.Uni-Stuttgart.DE/people/fw/
    RUS-CERT                          +49-711-685-5973/fax +49-711-685-5898
    
    #!/usr/bin/perl -w
    
    # find-zlib - scan for zlib tables in compiled code
    # Copyright (C) 2002 RUS-CERT, University of Stuttgart.
    # Written by Florian Weimer <Weimerat_private-Stuttgart.DE>.
    #
    # This program is free software; you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation; either version 2 of the License, or
    # (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    
    # $Id: find-zlib,v 1.8 2002/03/13 18:42:44 rusfw Exp $
    # <http://CERT.Uni-Stuttgart.DE/files/fw/find-zlib>
    
    use strict;
    
    if (@ARGV == 0 or $ARGV[0] eq '-h') {
        print <<EOF;
    find-zlib - scan for zlib tables in compiled code
    Copyright (C) 2002 RUS-CERT, University of Stuttgart.
    
    Usage: find-zlib [-v] filename...
    
    In non-verbose mode (without the "-v" flag), find-zlib scans only for
    three signatures:
    
      - The "cplenx table".  It is not specific to zlib, but used
        by any inflate decoder.
      - The "cplext table".  This table is specific to zlib and
        also gives some version information.
      - The "inflate" copyright string.
    
    Even if the copyright string has been removed, the other two signatures
    permit the identification of zlib inflate code.
    
    In verbose mode, additional signatures are tested:
    
      - The "configuration table".  It is specific to zlib, but not
        required by the inflate code.
      - Some common messages found in zlib.
      - The "deflate" copyright string. Note that the deflate code is
        independent of the inflate code.
    
    Thanks to Mark Adler for helpful suggestions.
    
    Send comments to fw-tracker\@CERT.Uni-Stuttgart.DE.
    
    EOF
        exit 1;
    }
    
    my $verbose = 0;
    
    if ($ARGV[0] eq "-v") {
      $verbose = 1;
      shift @ARGV;
    }
    
    $/ = undef;
    
    my @cplens_table = (3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
    		     35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227,
    		     258, 0, 0);
    my @cplext_table_092 = (0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
    			3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 128, 128);
    my @cplext_table_104 = (0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
    			3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192);
    my @cplext_table_114 = (0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
    			3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112);
    
    sub table_to_re (@) {
        my $be = "";
        my $le = "";
        my $e;
        foreach $e (@_) {
    	$be .= pack "N", $e;
    	$le .= pack "V", $e;
        }
        return (quotemeta($be), quotemeta($le));
    }
    
    sub table_to_re_config (@) {
        my $be = "";
        my $le = "";
        my $e;
        foreach $e (@_) {
    	$be .= pack "n", $e;
    	$le .= pack "v", $e;
        }
        return (quotemeta($be), quotemeta($le));
    }
    
    sub table_to_re_combined (@) {
      my ($be, $le) = table_to_re(@_);
      return "($be|$le)";
    }
    
    my ($cplens_table_be, $cplens_table_le) = table_to_re (@cplens_table);
    my ($cplext_table_092, $cplext_table_104, $cplext_table_114) =
      (table_to_re_combined(@cplext_table_092),
       table_to_re_combined(@cplext_table_104),
       table_to_re_combined(@cplext_table_114));
    
    my $line;
    my (@config_table_le, @config_table_be) = ();
    foreach $line ([8,   32, 128, 256],
    	       [32, 128, 258, 1024],
    	       [32, 258, 258, 4096]) {
        my ($be, $le) = table_to_re_config(@$line);
        push @config_table_be, $be;
        push @config_table_le, $le;
    }
    my ($config_table_be_32,
        $config_table_be_64,
        $config_table_le_32,
        $config_table_le_64)
        = (join("....", @config_table_be),
           join("........", @config_table_be),
           join("....", @config_table_le),
           join("........", @config_table_le));
    
    my $file;
    my $found = 1;
    for $file (@ARGV) {
      warn "$file: non-regular file ignored\n" unless -f $file;
      unless (open(FILE, "<$file")) {
        warn("$file: cannot read file\n"); 
        next;
      }
      my $data = <FILE>;
      close FILE;
    
      if ($data =~/inflate ([0-9][ 0-9a-zA-Z.\-]{1,100}[0-9a-zA-Z.\-])/) {
        print "$file: inflate version: \"$1\"\n";
        $found = 0;
      }
      if ($verbose
          and $data =~/deflate ([0-9][ 0-9a-zA-Z.\-]{1,100}[0-9a-zA-Z.\-])/) {
        print "$file: deflate version: \"$1\"\n";
        $found = 0;
      }
    
      if ($data =~ /$cplens_table_le/o) {
        print "$file: zlib cplens table, little endian\n";
        $found = 0;
      }
      if ($data =~ /$cplens_table_be/o) {
        print "$file: zlib cplens table, big endian\n";
        $found = 0;
      }
      if ($data =~ /$cplext_table_092/o) {
        print "$file: zlib cplext table (version 0.1 to 0.92)\n";
        $found = 0;
      }
      if ($data =~ /$cplext_table_104/o) {
        print "$file: zlib cplext table (version 0.93 to 1.0.4)\n";
        $found = 0;
      }
      if ($data =~ /$cplext_table_114/o) {
        print "$file: zlib cplext table (version 1.0.5 to 1.1.4)\n";
        $found = 0;
      }
      next if not $verbose;
    
      if ($data =~ /$config_table_le_32/o) {
        print "$file: zlib configuration table, little endian, 32 bit\n";
        $found = 0;
      }
    
      if ($data =~ /$config_table_be_32/o) {
        print "$file: zlib configuration table, big endian, 32 bit\n";
        $found = 0;
      }
      if ($data =~ /$config_table_le_64/o) {
        print "$file: zlib configuration table, little endian, 64 bit\n";
        $found = 0;
      }
      if ($data =~ /$config_table_be_64/o) {
        print "$file: zlib configuration table, big endian, 64bit\n";
        $found = 0;
      }
    
      my $msg = 0;
      my $total = 0;
      $msg++ if $data =~ /empty distance tree with lengths/; $total++;
      $msg++ if $data =~ /incomplete distance tree/; $total++;
      $msg++ if $data =~ /incomplete dynamic bit lengths tree/; $total++;
      $msg++ if $data =~ /incomplete literal\/length tree/; $total++;
      $msg++ if $data =~ /incorrect data check/; $total++;
      $msg++ if $data =~ /incorrect header check/; $total++;
      $msg++ if $data =~ /invalid bit length repeat/; $total++;
      $msg++ if $data =~ /invalid block type/; $total++;
      $msg++ if $data =~ /invalid stored block lengths/; $total++;
      $msg++ if $data =~ /invalid stored block lengths/; $total++;
      $msg++ if $data =~ /invalid window size/; $total++;
      $msg++ if $data =~ /need dictionary/; $total++;
      $msg++ if $data =~ /oversubscribed distance tree/; $total++;
      $msg++ if $data =~ /oversubscribed dynamic bit lengths tree/; $total++;
      $msg++ if $data =~ /oversubscribed literal\/length tree/; $total++;
      $msg++ if $data =~ /too many length or distance symbols/; $total++;
      $msg++ if $data =~ /too many length or distance symbols/; $total++;
      $msg++ if $data =~ /unknown compression method/; $total++;
      if ($msg > 0) {
        print "$file: $msg out of $total messages\n";
        $found = 0;
      }
    }
    exit $found;
    



    This archive was generated by hypermail 2b30 : Wed Mar 13 2002 - 14:58:37 PST