class Thieve

Attributes

loot[RW]

Public Class Methods

hilight?() click to toggle source
# File lib/thieve.rb, line 168
def self.hilight?
    @@hilight ||= false
    return @@hilight
end
new(hilight = false) click to toggle source
# File lib/thieve.rb, line 179
def initialize(hilight = false)
    if (ScoobyDoo.where_are_you("gpg").nil?)
        raise Thieve::Error::ExecutableNotFound.new("gpg")
    end

    if (ScoobyDoo.where_are_you("grep").nil?)
        raise Thieve::Error::ExecutableNotFound.new("grep")
    end

    @@hilight = hilight
    @loot = Hash.new
    @private = false
end

Public Instance Methods

export_loot(dir, priv_only = @private) click to toggle source
# File lib/thieve.rb, line 28
def export_loot(dir, priv_only = @private)
    exported = Hash.new
    @loot.each do |type, keys|
        next if (priv_only && !type.match(/CERTIFICATE|PRIVATE/))

        keys.each do |key|
            if (priv_only && type.match(/CERTIFICATE/))
                next if (key.match.nil?)
            end

            key.export(dir)
            exported[type] ||= Hash.new
            exported[type]["#{key.fingerprint}.#{key.ext}"] =
                key.to_json
        end
    end

    FileUtils.mkdir_p(dir)
    File.open("#{dir}/loot.json", "w") do |f|
        f.write(JSON.pretty_generate(exported))
    end
end
find_matches() click to toggle source
# File lib/thieve.rb, line 147
def find_matches
    return if (@loot["CERTIFICATE"].nil?)
    @loot["CERTIFICATE"].each do |c|
        next if (c.openssl.nil?)
        @loot.each do |type, keys|
            next if (type == "CERTIFICATE")
            keys.each do |k|
                next if (k.openssl.nil?)
                begin
                    if (c.openssl.check_private_key(k.openssl))
                        c.match = "#{k.fingerprint}.#{k.ext}"
                        k.match = "#{c.fingerprint}.#{c.ext}"
                    end
                rescue
                    # Do nothing. Private key is needed.
                end
            end
        end
    end
end
only_private(priv) click to toggle source
# File lib/thieve.rb, line 193
def only_private(priv)
    @private = priv
end
steal_from(filename, ignores = Array.new, binaries = false) click to toggle source
# File lib/thieve.rb, line 197
def steal_from(filename, ignores = Array.new, binaries = false)
    cmd = ["\\grep"]
    cmd.push("-a") if (binaries)

    ignores.each do |ignore|
        cmd.push("--exclude-dir \"#{ignore}\"")
        cmd.push("--exclude \"#{ignore}\"")
    end

    cmd.push("-I") if (!binaries)
    cmd.push("-lrs -- \"-----BEGIN\" #{filename}")

    %x(#{cmd.join(" ")}).each_line do |f|
        file = Pathname.new(f.strip).expand_path

        skip = ignores.any? do |ignore|
            File.fnmatch(ignore, file.to_s)
        end
        next if (skip)

        extract_from(file)
    end

    return @loot
end
to_s() click to toggle source
# File lib/thieve.rb, line 242
def to_s
    return summarize_loot
end

Private Instance Methods

display_exception(e, file, keydata) click to toggle source
# File lib/thieve.rb, line 12
def display_exception(e, file, keydata)
    if (@@hilight)
        $stderr.puts file.to_s.light_blue
        keydata.each do |l|
            $stderr.puts l.light_yellow
        end
        $stderr.puts e.message.white.on_red
    else
        $stderr.puts file
        $stderr.puts keydata.join("\n")
        $stderr.puts e.message
    end
    $stderr.puts
end
extract_from(file) click to toggle source
# File lib/thieve.rb, line 51
def extract_from(file)
    footer = ""
    headers = Array.new
    key = ""
    start = false

    File.open(file).each do |line|
        if (line.include?("-----BEGIN"))
            footer = ""
            headers.clear
            key = ""
            start = true
        end

        if (start)
            # Don't include newlines for now
            line = line.unpack("C*").pack("U*").strip

            case line
            when /^=[^=]+$/
                footer = line
            when /^.+:.+$/
                headers.push(line)
            else
                key += line
            end
        end

        if (line.include?("-----END"))
            # Remove " + " or ' + '
            key.gsub!(%r{["'] *\+ *["']?|["']? *\+ *["']}, "")

            # Remove bad characters
            key.gsub!(%r{[^-A-Za-z0-9+/= ]+}, "")

            # Find base64 key (accept spaces as we'll remove those
            # later)
            key_regex = [
                "(",
                "-----BEGIN ([A-Za-z0-9 ]+)-----",
                "([A-Za-z0-9+/= ]+)",
                "-----END \\2-----",
                ")"
            ].join

            # Scan for valid key
            key.scan(%r{#{key_regex}}) do |m, type, k|
                # Ignore breakpad microdumps
                next if (type.match(/BREAKPAD MICRODUMP/))

                # Remove spaces from key
                k.gsub!(/ +/, "")

                # Format the keydata
                keydata = k.scan(/.{,64}/).keep_if do |l|
                    !l.empty?
                end

                # Prepend headers
                if (headers.any?)
                    keydata.insert(0, "")
                    keydata.insert(0, headers.join("\n"))
                end

                # Append footer
                keydata.push(footer) if (!footer.empty?)

                # Prepend BEGIN
                keydata.insert(0, "-----BEGIN #{type}-----")

                # Append END
                keydata.push("-----END #{type}-----")

                begin
                    # Ensure key is base64 data
                    Base64.strict_decode64(k)

                    @loot[type] ||= Array.new
                    @loot[type].push(
                        Thieve::KeyInfo.new(
                            file,
                            type,
                            keydata.join("\n")
                        )
                    )
                rescue Exception => e
                    display_exception(e, file, keydata)
                end
            end

            start = false
        end
    end
end
hilight_type(type) click to toggle source
# File lib/thieve.rb, line 173
def hilight_type(type)
    return type if (!@@hilight)
    return type.light_cyan
end
summarize_loot(priv_only = @private) click to toggle source
# File lib/thieve.rb, line 223
def summarize_loot(priv_only = @private)
    ret = Array.new
    @loot.each do |type, keys|
        next if (priv_only && !type.match(/CERTIFICATE|PRIVATE/))

        ret.push(hilight_type(type))
        keys.each do |key|
            if (priv_only && type.match(/CERTIFICATE/))
                next if (key.match.nil?)
            end

            ret.push("#{key.to_s}\n")
        end
    end

    return ret.join("\n")
end