one_gadget 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +34 -6
- data/bin/one_gadget +8 -4
- data/lib/one_gadget.rb +12 -4
- data/lib/one_gadget/builds/libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.rb +8 -0
- data/lib/one_gadget/fetcher.rb +7 -34
- data/lib/one_gadget/fetchers/amd64.rb +21 -0
- data/lib/one_gadget/fetchers/base.rb +64 -0
- data/lib/one_gadget/fetchers/i386.rb +54 -0
- data/lib/one_gadget/helper.rb +13 -1
- data/lib/one_gadget/logger.rb +21 -0
- data/lib/one_gadget/version.rb +1 -1
- data/spec/data/{libc-2.19.so → libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so} +0 -0
- data/spec/data/libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so +0 -0
- data/spec/data/{libc-2.23.so → libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so} +0 -0
- data/spec/data/libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so +0 -0
- data/spec/helper_spec.rb +8 -2
- data/spec/{one_gadget_spec.rb → one_gadget_amd64_spec.rb} +1 -1
- data/spec/one_gadget_i386_spec.rb +24 -0
- metadata +24 -11
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: fc34f437cb6bb8562f408c558c9d58f9c6b88658
         | 
| 4 | 
            +
              data.tar.gz: 0604cd9f82c3c66500ce54b529255fa3fc323af3
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 3ff369c093af7bffab44931cf6cac32e8f961f91576e435a48245d5cc7b3c6bec17b3ca5d450f337d86852c5339b010d56dc39e1ddc8e4828894ab12ab317f1e
         | 
| 7 | 
            +
              data.tar.gz: 78d0f190661d5275ab4bb29d3063f8dd883fbe8a37422dead64c2b9fc0ad32bfe006dc13a2e52a3130deda3fba3488410bb58b85f4b0853c5d0a7aa0e4aafc45
         | 
    
        data/README.md
    CHANGED
    
    | @@ -13,11 +13,16 @@ This gem provides such gadget finder, no need to use IDA-pro every time like a f | |
| 13 13 |  | 
| 14 14 | 
             
            Also provides the command-line tool `one_gadget` for easy usage.
         | 
| 15 15 |  | 
| 16 | 
            -
            Note:  | 
| 16 | 
            +
            Note: Supports amd64 and i386!
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            Note2: still work in progress, the gem version might update frequently :p.
         | 
| 17 19 |  | 
| 18 20 | 
             
            ## Install
         | 
| 19 21 |  | 
| 20 | 
            -
             | 
| 22 | 
            +
            Available on RubyGems.org!
         | 
| 23 | 
            +
            ```bash
         | 
| 24 | 
            +
            gem install one_gadget
         | 
| 25 | 
            +
            ```
         | 
| 21 26 |  | 
| 22 27 | 
             
            ## Usage
         | 
| 23 28 |  | 
| @@ -26,9 +31,10 @@ I'll push to rubygems.org.. | |
| 26 31 | 
             
            ```bash
         | 
| 27 32 | 
             
            one_gadget
         | 
| 28 33 | 
             
            # Usage: one_gadget [file] [options]
         | 
| 29 | 
            -
            #     -b, --build-id BuildID           BuildID[sha1] of libc
         | 
| 30 | 
            -
            #     - | 
| 31 | 
            -
            #     - | 
| 34 | 
            +
            #     -b, --build-id BuildID           BuildID[sha1] of libc.
         | 
| 35 | 
            +
            #     -f, --[no-]force-file            Force search gadgets in file instead of build id first.
         | 
| 36 | 
            +
            #     -r, --[no-]raw                   Output gadgets offset only, split with one space.
         | 
| 37 | 
            +
            #     -s, --script exploit-script      Run exploit script with all possible gadgets.
         | 
| 32 38 | 
             
            #                                      The script will be run as 'exploit-script $offset'.
         | 
| 33 39 |  | 
| 34 40 | 
             
            one_gadget -b 60131540dadc6796cab33388349e6e4e68692053
         | 
| @@ -43,6 +49,28 @@ one_gadget -b 60131540dadc6796cab33388349e6e4e68692053 | |
| 43 49 | 
             
            # offset: 0xf0567
         | 
| 44 50 | 
             
            # constraints:
         | 
| 45 51 | 
             
            #   [rsp+0x70] == NULL
         | 
| 52 | 
            +
            #
         | 
| 53 | 
            +
            # offset: 0xf5b10
         | 
| 54 | 
            +
            # constraints:
         | 
| 55 | 
            +
            #   [rbp-0xf8] == NULL
         | 
| 56 | 
            +
            #   rcx == NULL
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            one_gadget /lib/i386-linux-gnu/libc.so.6
         | 
| 59 | 
            +
            # offset: 0x3ac69
         | 
| 60 | 
            +
            # constraints:
         | 
| 61 | 
            +
            #   esi is the address of `rw-p` area of libc
         | 
| 62 | 
            +
            #   [esp+0x34] == NULL
         | 
| 63 | 
            +
            #
         | 
| 64 | 
            +
            # offset: 0x5fbbe
         | 
| 65 | 
            +
            # constraints:
         | 
| 66 | 
            +
            #   esi is the address of `rw-p` area of libc
         | 
| 67 | 
            +
            #   eax == NULL
         | 
| 68 | 
            +
            #
         | 
| 69 | 
            +
            # offset: 0x12036c
         | 
| 70 | 
            +
            # constraints:
         | 
| 71 | 
            +
            #   esi is the address of `rw-p` area of libc
         | 
| 72 | 
            +
            #   eax == NULL
         | 
| 73 | 
            +
             | 
| 46 74 | 
             
            ```
         | 
| 47 75 |  | 
| 48 76 | 
             
            #### Combine with exploit script
         | 
| @@ -59,7 +87,7 @@ one_gadget ./spec/data/libc-2.19.so -s 'echo "offset ->"' | |
| 59 87 | 
             
            ```ruby
         | 
| 60 88 | 
             
            require 'one_gadget'
         | 
| 61 89 | 
             
            OneGadget.gadgets(file: '/lib/x86_64-linux-gnu/libc.so.6')
         | 
| 62 | 
            -
            # => [283242, 980676, 984423]
         | 
| 90 | 
            +
            # => [283242, 980676, 984423, 1006352]
         | 
| 63 91 | 
             
            ```
         | 
| 64 92 |  | 
| 65 93 | 
             
            ## Screenshots
         | 
    
        data/bin/one_gadget
    CHANGED
    
    | @@ -7,11 +7,15 @@ usage = 'Usage: one_gadget [file] [options]' | |
| 7 7 | 
             
            parser = OptionParser.new do |opts|
         | 
| 8 8 | 
             
              opts.banner = usage
         | 
| 9 9 |  | 
| 10 | 
            -
              opts.on('-b', '--build-id BuildID', 'BuildID[sha1] of libc') do |b|
         | 
| 10 | 
            +
              opts.on('-b', '--build-id BuildID', 'BuildID[sha1] of libc.') do |b|
         | 
| 11 11 | 
             
                options[:build_id] = b
         | 
| 12 12 | 
             
              end
         | 
| 13 13 |  | 
| 14 | 
            -
              opts.on('- | 
| 14 | 
            +
              opts.on('-f', '--[no-]force-file', 'Force search gadgets in file instead of build id first.') do |b|
         | 
| 15 | 
            +
                options[:force_file] = b
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              opts.on('-r', '--[no-]raw', 'Output gadgets offset only, split with one space.') do |v|
         | 
| 15 19 | 
             
                options[:raw] = v
         | 
| 16 20 | 
             
              end
         | 
| 17 21 |  | 
| @@ -32,7 +36,7 @@ end | |
| 32 36 | 
             
            if options[:build_id]
         | 
| 33 37 | 
             
              gadgets = OneGadget.gadgets(build_id: options[:build_id], details: true)
         | 
| 34 38 | 
             
            elsif ARGV[0]
         | 
| 35 | 
            -
              gadgets = OneGadget.gadgets(file: ARGV[0], details: true)
         | 
| 39 | 
            +
              gadgets = OneGadget.gadgets(file: ARGV[0], details: true, force_file: options[:force_file])
         | 
| 36 40 | 
             
            else
         | 
| 37 41 | 
             
              puts parser.help
         | 
| 38 42 | 
             
              exit(1)
         | 
| @@ -41,7 +45,7 @@ end | |
| 41 45 | 
             
            extend OneGadget::Helper::ClassMethods
         | 
| 42 46 | 
             
            if options[:script]
         | 
| 43 47 | 
             
              gadgets.map(&:offset).each do |offset|
         | 
| 44 | 
            -
                 | 
| 48 | 
            +
                OneGadget::Logger.info("Trying #{colorize(format('0x%x', offset), sev: :integer)}...\n")
         | 
| 45 49 | 
             
                execute(options[:script], offset)
         | 
| 46 50 | 
             
              end
         | 
| 47 51 | 
             
              exit(0)
         | 
    
        data/lib/one_gadget.rb
    CHANGED
    
    | @@ -11,19 +11,26 @@ module OneGadget | |
| 11 11 | 
             
                #   The relative path of libc.so.6.
         | 
| 12 12 | 
             
                # @param [String] build_id
         | 
| 13 13 | 
             
                #   The BuildID of target libc.so.6.
         | 
| 14 | 
            +
                # @param [Boolean] details
         | 
| 15 | 
            +
                #   Return gadget objects or offset only.
         | 
| 16 | 
            +
                # @param [Boolean] force_file
         | 
| 17 | 
            +
                #   When +file+ is given, {OneGadget} will search gadgets according its
         | 
| 18 | 
            +
                #   build id first. +force_file = true+ to disable this feature.
         | 
| 14 19 | 
             
                # @return [Array<OneGadget::Gadget::Gadget>, Array<Integer>]
         | 
| 15 20 | 
             
                #   The gadgets found.
         | 
| 16 21 | 
             
                # @example
         | 
| 17 22 | 
             
                #   OneGadget.gadgets(file: './libc.so.6')
         | 
| 18 23 | 
             
                #   OneGadget.gadgets(build_id: '60131540dadc6796cab33388349e6e4e68692053')
         | 
| 19 | 
            -
                def gadgets(file: nil, build_id: nil, details: false)
         | 
| 24 | 
            +
                def gadgets(file: nil, build_id: nil, details: false, force_file: false)
         | 
| 20 25 | 
             
                  if build_id
         | 
| 21 26 | 
             
                    OneGadget::Fetcher.from_build_id(build_id, details: details)
         | 
| 22 27 | 
             
                  elsif file
         | 
| 23 28 | 
             
                    file = OneGadget::Helper.abspath(file)
         | 
| 24 | 
            -
                     | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 29 | 
            +
                    unless force_file
         | 
| 30 | 
            +
                      build_id = OneGadget::Helper.build_id_of(file)
         | 
| 31 | 
            +
                      gadgets = OneGadget::Fetcher.from_build_id(build_id, details: details)
         | 
| 32 | 
            +
                      return gadgets unless gadgets.empty?
         | 
| 33 | 
            +
                    end
         | 
| 27 34 | 
             
                    OneGadget::Fetcher.from_file(file, details: details)
         | 
| 28 35 | 
             
                  end
         | 
| 29 36 | 
             
                end
         | 
| @@ -32,4 +39,5 @@ end | |
| 32 39 |  | 
| 33 40 | 
             
            require 'one_gadget/fetcher'
         | 
| 34 41 | 
             
            require 'one_gadget/helper'
         | 
| 42 | 
            +
            require 'one_gadget/logger'
         | 
| 35 43 | 
             
            require 'one_gadget/version'
         | 
| @@ -0,0 +1,8 @@ | |
| 1 | 
            +
            require 'one_gadget/gadget'
         | 
| 2 | 
            +
            # Ubuntu GLIBC 2.23-0ubuntu5
         | 
| 3 | 
            +
            # ELF 32-bit LSB shared object, Intel 80386
         | 
| 4 | 
            +
            build_id = File.basename(__FILE__, '.rb').split('-').last + 'a'
         | 
| 5 | 
            +
            rw_area_constraint = 'esi is the address of `rw-p` area of libc'
         | 
| 6 | 
            +
            OneGadget::Gadget.add(build_id, 0x3ac69, constraints: [rw_area_constraint, '[esp+0x34] == NULL'])
         | 
| 7 | 
            +
            OneGadget::Gadget.add(build_id, 0x5fbbe, constraints: [rw_area_constraint, 'eax == NULL'])
         | 
| 8 | 
            +
            OneGadget::Gadget.add(build_id, 0x12036c, constraints: [rw_area_constraint, 'eax == NULL'])
         | 
    
        data/lib/one_gadget/fetcher.rb
    CHANGED
    
    | @@ -1,4 +1,6 @@ | |
| 1 1 | 
             
            require 'one_gadget/helper'
         | 
| 2 | 
            +
            require 'one_gadget/fetchers/amd64'
         | 
| 3 | 
            +
            require 'one_gadget/fetchers/i386'
         | 
| 2 4 | 
             
            require 'one_gadget/gadget'
         | 
| 3 5 |  | 
| 4 6 | 
             
            module OneGadget
         | 
| @@ -32,43 +34,14 @@ module OneGadget | |
| 32 34 | 
             
                  #   contains offset only.
         | 
| 33 35 | 
             
                  #   Otherwise, array of gadgets is returned.
         | 
| 34 36 | 
             
                  def from_file(file, details: false)
         | 
| 35 | 
            -
                     | 
| 36 | 
            -
             | 
| 37 | 
            -
                       | 
| 38 | 
            -
                       | 
| 39 | 
            -
             | 
| 40 | 
            -
                    end
         | 
| 41 | 
            -
                    candidates.map! do |candidate|
         | 
| 42 | 
            -
                      # remove other calls
         | 
| 43 | 
            -
                      lines = candidate.lines
         | 
| 44 | 
            -
                      to_rm = lines[0...-1].rindex { |c| c.include?('call') }
         | 
| 45 | 
            -
                      lines = lines[to_rm + 1..-1] unless to_rm.nil?
         | 
| 46 | 
            -
                      lines.map! { |s| s.gsub(/#\s+#{bin_sh_hex}\s+<.*>$/, "# #{bin_sh_hex} \"/bin/sh\"") }
         | 
| 47 | 
            -
                      lines.join
         | 
| 48 | 
            -
                    end
         | 
| 49 | 
            -
                    gadgets = candidates.map { |c| convert_to_gadget(c) }
         | 
| 37 | 
            +
                    gadgets = {
         | 
| 38 | 
            +
                      amd64: OneGadget::Fetcher::Amd64,
         | 
| 39 | 
            +
                      i386: OneGadget::Fetcher::I386,
         | 
| 40 | 
            +
                      unknown: OneGadget::Fetcher::Base
         | 
| 41 | 
            +
                    }[OneGadget::Helper.architecture(file)].new(file).find
         | 
| 50 42 | 
             
                    return gadgets if details
         | 
| 51 43 | 
             
                    gadgets.map(&:offset)
         | 
| 52 44 | 
             
                  end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                  private
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                  def str_offset(file, str)
         | 
| 57 | 
            -
                    match = `strings -tx #{file} | grep '#{str}'`.lines.map(&:strip).first
         | 
| 58 | 
            -
                    return nil if match.nil?
         | 
| 59 | 
            -
                    # 17c8c3 /bin/sh
         | 
| 60 | 
            -
                    match.split.first.to_i(16)
         | 
| 61 | 
            -
                  end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                  def convert_to_gadget(assembly)
         | 
| 64 | 
            -
                    lines = assembly.lines.map(&:strip)
         | 
| 65 | 
            -
                    offset = lines.first.scan(/^([\da-f]+):/)[0][0].to_i(16)
         | 
| 66 | 
            -
                    # fetch those might be constraints lines.
         | 
| 67 | 
            -
                    important_lines = lines.select { |line| ['rsi'].any? { |r| line.include?(r) } }.map do |line|
         | 
| 68 | 
            -
                      line.split("\t").last.gsub(/\s+/, ' ')
         | 
| 69 | 
            -
                    end
         | 
| 70 | 
            -
                    OneGadget::Gadget::Gadget.new(offset, constraints: important_lines)
         | 
| 71 | 
            -
                  end
         | 
| 72 45 | 
             
                end
         | 
| 73 46 | 
             
                extend ClassMethods
         | 
| 74 47 | 
             
              end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            require 'one_gadget/fetchers/base'
         | 
| 2 | 
            +
            module OneGadget
         | 
| 3 | 
            +
              module Fetcher
         | 
| 4 | 
            +
                # Fetcher for amd64.
         | 
| 5 | 
            +
                class Amd64 < OneGadget::Fetcher::Base
         | 
| 6 | 
            +
                  def find
         | 
| 7 | 
            +
                    bin_sh_hex = str_offset('/bin/sh').to_s(16)
         | 
| 8 | 
            +
                    cands = candidates do |candidate|
         | 
| 9 | 
            +
                      next false unless candidate.include?(bin_sh_hex) # works in x86-64
         | 
| 10 | 
            +
                      next false unless candidate.lines.last.include?('execve') # only care execve
         | 
| 11 | 
            +
                      true
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                    cands.map do |candidate|
         | 
| 14 | 
            +
                      convert_to_gadget(candidate) do |line|
         | 
| 15 | 
            +
                        ['rsi'].any? { |r| line.include?(r) }
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            require 'shellwords'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module OneGadget
         | 
| 4 | 
            +
              module Fetcher
         | 
| 5 | 
            +
                # define common methods for gadget fetchers.
         | 
| 6 | 
            +
                class Base
         | 
| 7 | 
            +
                  attr_reader :file
         | 
| 8 | 
            +
                  # @param [String] file Absolute path of target libc.
         | 
| 9 | 
            +
                  def initialize(file)
         | 
| 10 | 
            +
                    @file = ::Shellwords.escape(file)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def find; raise NotImplementedError
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # Fetch candidates that end with call exec*.
         | 
| 17 | 
            +
                  # @return [Array<String>]
         | 
| 18 | 
            +
                  #   Each +String+ returned is multi-lines of assembly code.
         | 
| 19 | 
            +
                  def candidates(&block)
         | 
| 20 | 
            +
                    cands = `objdump -w -d -M intel #{file}|egrep 'call.*<exec[^+]*>$' -B 20`.split('--').map do |cand|
         | 
| 21 | 
            +
                      cand.lines.map(&:strip).reject(&:empty?).join("\n")
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                    # remove all calls, jmps
         | 
| 24 | 
            +
                    cands = slice_prefix(cands, &method(:branch?))
         | 
| 25 | 
            +
                    cands.select!(&block) if block_given?
         | 
| 26 | 
            +
                    cands
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  private
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def slice_prefix(cands)
         | 
| 32 | 
            +
                    cands.map do |cand|
         | 
| 33 | 
            +
                      lines = cand.lines
         | 
| 34 | 
            +
                      to_rm = lines[0...-1].rindex { |c| yield(c) }
         | 
| 35 | 
            +
                      lines = lines[to_rm + 1..-1] unless to_rm.nil?
         | 
| 36 | 
            +
                      lines.join
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  # If str contains a branch instruction.
         | 
| 41 | 
            +
                  def branch?(str)
         | 
| 42 | 
            +
                    %w(call jmp je jne jl jb ja jg).any? { |f| str.include?(f) }
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  def str_offset(str)
         | 
| 46 | 
            +
                    match = `strings -tx #{file} -n #{str.size} | grep " #{::Shellwords.escape(str)}"`.lines.map(&:strip).first
         | 
| 47 | 
            +
                    return nil if match.nil?
         | 
| 48 | 
            +
                    # 17c8c3 /bin/sh
         | 
| 49 | 
            +
                    match.split.first.to_i(16)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def convert_to_gadget(assembly, &block)
         | 
| 53 | 
            +
                    lines = assembly.lines
         | 
| 54 | 
            +
                    offset = lines.first.scan(/^([\da-f]+):/)[0][0].to_i(16)
         | 
| 55 | 
            +
                    # fetch those might be constraints lines.
         | 
| 56 | 
            +
                    important_lines = lines.select(&block).map do |line|
         | 
| 57 | 
            +
                      ar = line.split("\t")
         | 
| 58 | 
            +
                      "#{ar.first} #{ar.last.gsub(/\s+/, ' ')}"
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
                    OneGadget::Gadget::Gadget.new(offset, constraints: important_lines)
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            require 'one_gadget/fetchers/base'
         | 
| 2 | 
            +
            module OneGadget
         | 
| 3 | 
            +
              module Fetcher
         | 
| 4 | 
            +
                # Fetcher for i386.
         | 
| 5 | 
            +
                class I386 < OneGadget::Fetcher::Base
         | 
| 6 | 
            +
                  def find
         | 
| 7 | 
            +
                    rw_off = rw_offset
         | 
| 8 | 
            +
                    bin_sh = str_offset('/bin/sh')
         | 
| 9 | 
            +
                    minus_c = str_offset('-c')
         | 
| 10 | 
            +
                    rel_sh_hex = (rw_off - bin_sh).to_s(16)
         | 
| 11 | 
            +
                    rel_minus_c = (rw_off - minus_c).to_s(16)
         | 
| 12 | 
            +
                    cands = candidates do |candidate|
         | 
| 13 | 
            +
                      next false unless candidate.include?(rel_sh_hex)
         | 
| 14 | 
            +
                      true
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                    # remove lines before and with -c appears
         | 
| 17 | 
            +
                    cands = slice_prefix(cands) do |line|
         | 
| 18 | 
            +
                      line.include?(rel_minus_c)
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                    # special handle for execl call
         | 
| 21 | 
            +
                    cands.map! do |cand|
         | 
| 22 | 
            +
                      lines = cand.lines
         | 
| 23 | 
            +
                      next cand unless lines.last.include?('execl')
         | 
| 24 | 
            +
                      # Find the last three +push+, or mov [esp+0x8], .*
         | 
| 25 | 
            +
                      # Make it call +execl("/bin/sh", "sh", NULL)+.
         | 
| 26 | 
            +
                      if cand.include?('esp+0x8')
         | 
| 27 | 
            +
                        to_rm = lines.index { |c| c.include?('esp+0x8') }
         | 
| 28 | 
            +
                      else
         | 
| 29 | 
            +
                        push_cnt = 0
         | 
| 30 | 
            +
                        to_rm = lines.rindex do |c|
         | 
| 31 | 
            +
                          push_cnt += 1 if c.include?('push')
         | 
| 32 | 
            +
                          push_cnt >= 3
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                      lines = lines[to_rm..-1] unless to_rm.nil?
         | 
| 36 | 
            +
                      lines.join
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                    cands.map do |candidate|
         | 
| 39 | 
            +
                      convert_to_gadget(candidate) do |_|
         | 
| 40 | 
            +
                        true
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  private
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  def rw_offset
         | 
| 48 | 
            +
                    # How to find this offset correctly..?
         | 
| 49 | 
            +
                    line = `readelf -d #{file}|grep PLTGOT`
         | 
| 50 | 
            +
                    line.scan(/0x[\da-f]+/).last.to_i(16) & -0x1000
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
            end
         | 
    
        data/lib/one_gadget/helper.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ require 'shellwords' | |
| 3 3 | 
             
            require 'net/http'
         | 
| 4 4 | 
             
            require 'openssl'
         | 
| 5 5 | 
             
            require 'tempfile'
         | 
| 6 | 
            +
            require 'one_gadget/logger'
         | 
| 6 7 |  | 
| 7 8 | 
             
            module OneGadget
         | 
| 8 9 | 
             
              # Define some helpful methods here.
         | 
| @@ -120,7 +121,18 @@ module OneGadget | |
| 120 121 | 
             
                  def ask_update(msg: '')
         | 
| 121 122 | 
             
                    name = 'one_gadget'
         | 
| 122 123 | 
             
                    cmd = colorize("gem update #{name}")
         | 
| 123 | 
            -
                     | 
| 124 | 
            +
                    OneGadget::Logger.info(msg + "\n" + "Update with: $ #{cmd}" + "\n")
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  # Fetch the file archiecture of +file+.
         | 
| 128 | 
            +
                  # @param [String] The target ELF filename.
         | 
| 129 | 
            +
                  # @return [String]
         | 
| 130 | 
            +
                  #   Only supports :amd64, :i386 now.
         | 
| 131 | 
            +
                  def architecture(file)
         | 
| 132 | 
            +
                    str = `file #{::Shellwords.escape(file)}`
         | 
| 133 | 
            +
                    return :amd64 if str.include?('x86-64')
         | 
| 134 | 
            +
                    return :i386 if str.include?('Intel 80386')
         | 
| 135 | 
            +
                    :unknown
         | 
| 124 136 | 
             
                  end
         | 
| 125 137 | 
             
                end
         | 
| 126 138 | 
             
                extend ClassMethods
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            require 'logger'
         | 
| 2 | 
            +
            require 'one_gadget/helper'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module OneGadget
         | 
| 5 | 
            +
              # A logger for internal usage.
         | 
| 6 | 
            +
              module Logger
         | 
| 7 | 
            +
                @logger = ::Logger.new(STDOUT)
         | 
| 8 | 
            +
                @logger.formatter = proc do |_severity, _datetime, _progname, msg|
         | 
| 9 | 
            +
                  prep = ' ' * 12
         | 
| 10 | 
            +
                  message = msg.lines.map.with_index do |str, i|
         | 
| 11 | 
            +
                    next str if i.zero?
         | 
| 12 | 
            +
                    prep + str
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                  "[#{OneGadget::Helper.colorize('OneGadget', sev: :reg)}] #{message.join}"
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def self.info(msg)
         | 
| 18 | 
            +
                  @logger.info(msg)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
    
        data/lib/one_gadget/version.rb
    CHANGED
    
    
| 
            File without changes
         | 
| Binary file | 
| 
            File without changes
         | 
| Binary file | 
    
        data/spec/helper_spec.rb
    CHANGED
    
    | @@ -3,10 +3,11 @@ require 'one_gadget/helper' | |
| 3 3 | 
             
            describe OneGadget::Helper do
         | 
| 4 4 | 
             
              before(:all) do
         | 
| 5 5 | 
             
                OneGadget::Helper.color_on!
         | 
| 6 | 
            -
                @libcpath = File.join(File.dirname(__FILE__), 'data', 'libc-2.23.so')
         | 
| 6 | 
            +
                @libcpath = File.join(File.dirname(__FILE__), 'data', 'libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so')
         | 
| 7 7 | 
             
              end
         | 
| 8 8 | 
             
              it 'abspath' do
         | 
| 9 | 
            -
                expect(OneGadget::Helper.abspath('./spec/data/libc-2.23.so')) | 
| 9 | 
            +
                expect(OneGadget::Helper.abspath('./spec/data/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so'))
         | 
| 10 | 
            +
                  .to eq @libcpath
         | 
| 10 11 | 
             
              end
         | 
| 11 12 |  | 
| 12 13 | 
             
              it 'build_id_of' do
         | 
| @@ -16,4 +17,9 @@ describe OneGadget::Helper do | |
| 16 17 | 
             
              it 'colorize' do
         | 
| 17 18 | 
             
                expect(OneGadget::Helper.colorize('123', sev: :integer)).to eq "\e[38;5;12m123\e[0m"
         | 
| 18 19 | 
             
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              it 'architecture' do
         | 
| 22 | 
            +
                expect(OneGadget::Helper.architecture(@libcpath)).to be :amd64
         | 
| 23 | 
            +
                expect(OneGadget::Helper.architecture(__FILE__)).to be :unknown
         | 
| 24 | 
            +
              end
         | 
| 19 25 | 
             
            end
         | 
| @@ -3,7 +3,7 @@ require 'one_gadget' | |
| 3 3 | 
             
            describe 'one_gadget' do
         | 
| 4 4 | 
             
              before(:each) do
         | 
| 5 5 | 
             
                @build_id = '60131540dadc6796cab33388349e6e4e68692053'
         | 
| 6 | 
            -
                @libcpath = File.join(File.dirname(__FILE__), 'data', 'libc-2.19.so')
         | 
| 6 | 
            +
                @libcpath = File.join(File.dirname(__FILE__), 'data', 'libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so')
         | 
| 7 7 | 
             
              end
         | 
| 8 8 |  | 
| 9 9 | 
             
              it 'from file' do
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            require 'one_gadget'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe 'one_gadget' do
         | 
| 4 | 
            +
              before(:each) do
         | 
| 5 | 
            +
                @build_id = '926eb99d49cab2e5622af38ab07395f5b32035e9'
         | 
| 6 | 
            +
                @libcpath19 = File.join(File.dirname(__FILE__), 'data', 'libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so')
         | 
| 7 | 
            +
                @libcpath23 = File.join(File.dirname(__FILE__), 'data', 'libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so')
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              it 'from file libc-2.19' do
         | 
| 11 | 
            +
                expect(OneGadget.gadgets(file: @libcpath19, force_file: true)).to eq [0x3fd27, 0x64c60, 0x1244a6]
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              it 'from file libc-2.23' do
         | 
| 15 | 
            +
                expect(OneGadget.gadgets(file: @libcpath23, force_file: true)).to eq [0x3ac69, 0x5fbbe, 0x12036c]
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              describe 'from build id' do
         | 
| 19 | 
            +
                it 'normal' do
         | 
| 20 | 
            +
                  # only check not empty because the gadgets might add frequently.
         | 
| 21 | 
            +
                  expect(OneGadget.gadgets(build_id: @build_id)).not_to be_empty
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: one_gadget
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.1.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - david942j
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2017-02- | 
| 11 | 
            +
            date: 2017-02-13 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rspec
         | 
| @@ -81,9 +81,11 @@ dependencies: | |
| 81 81 | 
             
                  - !ruby/object:Gem::Version
         | 
| 82 82 | 
             
                    version: '0.6'
         | 
| 83 83 | 
             
            description: |2
         | 
| 84 | 
            -
                When playing ctf pwn challenges we usually needs the one-gadget of  | 
| 85 | 
            -
             | 
| 86 | 
            -
                 | 
| 84 | 
            +
                When playing ctf pwn challenges we usually needs the one-gadget of execve('/bin/sh', NULL, NULL).
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                This gem provides such gadget finder, no need to use IDA-pro every time like a fool :p.
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                Also provides the command-line tool +one_gadget+ for easy usage.
         | 
| 87 89 | 
             
            email:
         | 
| 88 90 | 
             
            - david942j@gmail.com
         | 
| 89 91 | 
             
            executables:
         | 
| @@ -96,15 +98,23 @@ files: | |
| 96 98 | 
             
            - lib/one_gadget.rb
         | 
| 97 99 | 
             
            - lib/one_gadget/abi.rb
         | 
| 98 100 | 
             
            - lib/one_gadget/builds/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.rb
         | 
| 101 | 
            +
            - lib/one_gadget/builds/libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.rb
         | 
| 99 102 | 
             
            - lib/one_gadget/fetcher.rb
         | 
| 103 | 
            +
            - lib/one_gadget/fetchers/amd64.rb
         | 
| 104 | 
            +
            - lib/one_gadget/fetchers/base.rb
         | 
| 105 | 
            +
            - lib/one_gadget/fetchers/i386.rb
         | 
| 100 106 | 
             
            - lib/one_gadget/gadget.rb
         | 
| 101 107 | 
             
            - lib/one_gadget/helper.rb
         | 
| 108 | 
            +
            - lib/one_gadget/logger.rb
         | 
| 102 109 | 
             
            - lib/one_gadget/version.rb
         | 
| 103 | 
            -
            - spec/data/libc-2.19.so
         | 
| 104 | 
            -
            - spec/data/libc-2. | 
| 110 | 
            +
            - spec/data/libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so
         | 
| 111 | 
            +
            - spec/data/libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so
         | 
| 112 | 
            +
            - spec/data/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so
         | 
| 113 | 
            +
            - spec/data/libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so
         | 
| 105 114 | 
             
            - spec/gadget_spec.rb
         | 
| 106 115 | 
             
            - spec/helper_spec.rb
         | 
| 107 | 
            -
            - spec/ | 
| 116 | 
            +
            - spec/one_gadget_amd64_spec.rb
         | 
| 117 | 
            +
            - spec/one_gadget_i386_spec.rb
         | 
| 108 118 | 
             
            - spec/spec_helper.rb
         | 
| 109 119 | 
             
            homepage: https://github.com/david942j/one_gadget
         | 
| 110 120 | 
             
            licenses:
         | 
| @@ -131,9 +141,12 @@ signing_key: | |
| 131 141 | 
             
            specification_version: 4
         | 
| 132 142 | 
             
            summary: one_gadget
         | 
| 133 143 | 
             
            test_files:
         | 
| 134 | 
            -
            - spec/ | 
| 135 | 
            -
            - spec/ | 
| 144 | 
            +
            - spec/one_gadget_i386_spec.rb
         | 
| 145 | 
            +
            - spec/one_gadget_amd64_spec.rb
         | 
| 146 | 
            +
            - spec/data/libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so
         | 
| 147 | 
            +
            - spec/data/libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so
         | 
| 148 | 
            +
            - spec/data/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so
         | 
| 149 | 
            +
            - spec/data/libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so
         | 
| 136 150 | 
             
            - spec/spec_helper.rb
         | 
| 137 151 | 
             
            - spec/helper_spec.rb
         | 
| 138 | 
            -
            - spec/one_gadget_spec.rb
         | 
| 139 152 | 
             
            - spec/gadget_spec.rb
         |