Skip to content

Commit 5ad30d0

Browse files
committed
Land rapid7#6067, @bigendiansmalls' MainframeShell class
2 parents 9adfd29 + d70d30c commit 5ad30d0

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/base/sessions/command_shell'
4+
5+
module Msf::Sessions
6+
7+
###
8+
#
9+
# This class provides basic interaction with a Unix Systems Service
10+
# command shell on a mainframe (IBM System Z) running Z/OS
11+
# This session is initialized with a stream that will be used
12+
# as the pipe for reading and writing the command shell.
13+
#
14+
# Date: Oct 8, 2015
15+
# Author: Bigendian Smalls
16+
#
17+
###
18+
class MainframeShell < Msf::Sessions::CommandShell
19+
20+
#
21+
# This interface supports basic interaction.
22+
#
23+
include Msf::Session::Basic
24+
25+
#
26+
# This interface supports interacting with a single command shell.
27+
#
28+
include Msf::Session::Provider::SingleCommandShell
29+
30+
##
31+
#
32+
# initialize as mf shell session
33+
#
34+
def initialize(*args)
35+
self.platform = "mainframe"
36+
self.arch = "zarch"
37+
self.translate_1047 = true
38+
super
39+
end
40+
41+
##
42+
#
43+
# Returns the session description.
44+
#
45+
def desc
46+
"Mainframe shell"
47+
end
48+
49+
##
50+
#
51+
# override shell_read to include decode of cp1047
52+
#
53+
def shell_read(length=-1, timeout=1)
54+
#mfimpl
55+
if self.respond_to?(:ring)
56+
return Rex::Text.from_ibm1047(shell_read_ring(length,timeout))
57+
end
58+
59+
begin
60+
rv = Rex::Text.from_ibm1047(rstream.get_once(length, timeout))
61+
framework.events.on_session_output(self, rv) if rv
62+
return rv
63+
rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e
64+
shell_close
65+
raise e
66+
end
67+
end
68+
69+
##
70+
#
71+
# override shell_write to include encode of cp1047
72+
#
73+
def shell_write(buf)
74+
#mfimpl
75+
return unless buf
76+
77+
begin
78+
framework.events.on_session_command(self, buf.strip)
79+
rstream.write(Rex::Text.to_ibm1047(buf))
80+
rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e
81+
shell_close
82+
raise e
83+
end
84+
end
85+
86+
def execute_file(full_path, args)
87+
#mfimpl
88+
raise NotImplementedError
89+
end
90+
91+
# need to do more testing on this before we either use the default in command_shell
92+
# or write a new one. For now we just make it unavailble. This prevents a hang on
93+
# initial session creation. See PR#6067
94+
undef_method :process_autoruns
95+
96+
def desc
97+
"Mainframe USS session"
98+
end
99+
100+
attr_accessor :translate_1047 # tells the session whether or not to translate
101+
# ebcdic (cp1047) <-> ASCII for certain mainframe payloads
102+
# this will be used in post modules to be able to switch on/off the
103+
# translation on file transfers, for instance
104+
105+
protected
106+
107+
##
108+
#
109+
# _interact_ring overridden to include decoding of cp1047 data
110+
#
111+
def _interact_ring
112+
begin
113+
rdr = framework.threads.spawn("RingMonitor", false) do
114+
seq = nil
115+
116+
while self.interacting
117+
# Look for any pending data from the remote ring
118+
nseq,data = ring.read_data(seq)
119+
120+
# Update the sequence number if necessary
121+
seq = nseq || seq
122+
123+
# Write output to the local stream if successful
124+
user_output.print(Rex::Text.from_ibm1047(data)) if data
125+
126+
begin
127+
# Wait for new data to arrive on this session
128+
ring.wait(seq)
129+
rescue EOFError => e
130+
print_error("EOFError: #{e.class}: #{e}")
131+
break
132+
end
133+
end
134+
end
135+
136+
while self.interacting
137+
# Look for any pending input or errors from the local stream
138+
sd = Rex::ThreadSafe.select([ _local_fd ], nil, [_local_fd], 5.0)
139+
140+
# Write input to the ring's input mechanism
141+
shell_write(user_input.gets) if sd
142+
end
143+
ensure
144+
rdr.kill
145+
end
146+
end
147+
148+
end
149+
end
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# -*- coding:binary -*-
2+
require 'spec_helper'
3+
require 'msf/base/sessions/mainframe_shell'
4+
5+
##
6+
#
7+
# A quick test that MainframeShell is operable
8+
# Author: Bigendian Smalls
9+
#
10+
describe Msf::Sessions::MainframeShell do
11+
it 'extends Msf::Sessions::CommandShell to include EBCDIC cp1047 codepage translation' do
12+
args=[0,
13+
{:datastore=>
14+
{"VERBOSE"=>false,
15+
"WfsDelay"=>0,
16+
"EnableContextEncoding"=>false,
17+
"DisablePayloadHandler"=>false,
18+
"ConnectTimeout"=>10,
19+
"TCP::max_send_size"=>0,
20+
"TCP::send_delay"=>0,
21+
"RPORT"=>4444,
22+
"payload"=>"mainframe/shell_reverse_notranslate_tcp",
23+
"LPORT"=>5555,
24+
"LHOST"=>"127.0.0.1",
25+
"RHOST"=>"127.0.0.2",
26+
"CPORT"=>0,
27+
"ReverseConnectRetries"=>5,
28+
"ReverseAllowProxy"=>false,
29+
"ReverseListenerThreaded"=>false,
30+
"InitialAutoRunScript"=>"",
31+
"AutoRunScript"=>"",
32+
"ReverseListenerBindPort"=>0,
33+
"TARGET"=>0},
34+
:expiration=>0,
35+
:comm_timeout=>0,
36+
:retry_total=>0,
37+
:retry_wait=>0}]
38+
expect(described_class.name).to eq('Msf::Sessions::MainframeShell')
39+
mfshell = Msf::Sessions::MainframeShell.new(args)
40+
expect(mfshell.platform).to eq('mainframe')
41+
expect(mfshell.arch).to eq('zarch')
42+
expect(mfshell.respond_to?(:translate_1047))
43+
end
44+
end

0 commit comments

Comments
 (0)