Mainframes are the unseen workhorses that carry the load for many services we use on a daily basis: Withdrawing money from an ATM, credit card payments, and airline reservations to name just a few of the high volume workloads that are primarily handled by mainframes. For those that like to see figures to support this claim mainframes are:
Despite the importance of these powerful machines there are few security professionals with an understanding of their intricacies and even fewer actively engaged in penetration testing and research. In this article, we demonstrate an entry level technique for penetration testers to get started using a different twist on a familiar technology to attack these computing giants.
Today, when referring to mainframe computers, we are almost always talking about IBM hardware running z/OS. z/OS is a 64-bit operating system for IBM z/Architecture released in March 2001 as a successor to OS/390 and the MVS operating system line with its origins dating back to the 1960s. IBM Z maintains a very high level of backwards compatibility and the latest systems are capable of running many unmodified applications written over 50 decades ago. In this blog post we will be focusing on an integral component of z/OS: z/OS UNIX, a subsystem often called Unix System Services (USS). USS is an implementation of the UNIX operating system certified by The Open Group (the certifying body for the UNIX trademark to ensure compliance with established standards) which:
Before diving into how we can abuse some of these services we will look at a core concept of the z/OS that, somewhat surprisingly, derives directly from job cards like the one pictured here:
Most of the work carried out by a mainframe is performed through the use of jobs. A job is a unit of work, consisting of one or more steps that are executed to perform a task. Originally these jobs were defined using punch cards like the one above, today these physical cards are replaced with files written in Job Control Language (JCL). The management of these jobs and the resources they require is handled by the Job Entry Subsystem (JES). JES receives job submissions, prioritizes them, and schedules them for execution. The execution of jobs is then coordinated by JES to ensure efficient use of system resources by allocating necessary resources and monitoring their progress. On completion, JES manages the job output for collection or distribution to the appropriate destinations.
Let us look at a basic job card written in JCL. The job will create a new file or, as it is described in the mainframe world, allocate a dataset. One of the first hurdles when talking about mainframes is to get handle on all the new terminology and a plethora of acronyms.
//USERJOB JOB ACC-INFO,’User Name’,MSGCLASS=A,
// MSGLEVEL=(1,1),NOTIFY=&SYSUID
//STEP1 EXEC PGM=IEFBR14
//DDNAME DD DSN=USER.DATASET.NAME,
// DISP=(NEW,CATLG),
// UNIT=SYSALLDA,SPACE=(TRK,1)
/*
Some notes on JCL syntax:
Job cards are made up of a series of statements. All job cards use the following three main types of JCL statements:
At this point it may still be a little unclear exactly what use this has for us. Perhaps a small selection of some common programs we could execute might lead us further into understanding the impact of job submission:
Now that we have covered the basics, let’s start the fun part.
The mainframe FTP service has some additional functionality that can be extremely useful during penetration testing. Let us assume a situation where we have access to an FTP service and a set of valid credentials. This scenario is not uncommon, especially in red team engagements where credentials are often discovered unprotected in exposed code repositories, configuration files, Confluence, Jira, the list goes on. Armed with these credentials and (hopefully) permissions that allow us to submit jobs, there are potentially multiple avenues of investigation that we can use for exploitation.
To begin we should start by looking at how we submit our JCL via FTP. The first step is to create some JCL on our local machine. For testing purposes, we can use the IEFBR14 and allocate a dataset and see what happens. We assume that our FTP user is called NVISO, we are working locally from a Linux machine, and create our job, much like the example above and save it to our local machine:
//NVISO1 JOB ,’Nviso Tester’,MSGCLASS=A,
// MSGLEVEL=(1,1),NOTIFY=&SYSUID
//STEP1 EXEC PGM=IEFBR14
//DDNAME DD DSN=NVISO.TEST.DATA,
// DISP=(NEW,CATLG),
// UNIT=SYSALLDA,SPACE=(TRK,1)
/*
We can now log into the FTP server with our credentials:
$ ftp [email protected]
Connected to victim-mf.com.
220-FTPD1 IBM FTP CS V2R5 at victim-mf, 14:39:14 on 2024-07-02.
220 Connection will close if idle for more than 30 minutes.
331 Send password please.
Password:
230 NVISO is logged on. Working directory is “/u/nviso”.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ascii
200 Representation type is Ascii NonPrint
At this point, the user is authenticated and the representation type has been set to ascii. We now use the “site” subcommand to change into JES mode, followed by the “put” command which in JES mode will submit our job.
ftp> site file=jes
200 SITE command was accepted
ftp> put test.jcl
local: test.jcl remote: test.jcl
229 Entering Extended Passive Mode (|||21352|)
125 Sending Job to JES internal reader FIXrecfm 80
100% |*| 231 8.17 MiB/s –:– ETA
250-It is known to JES as JOB00093
250 Transfer completed successfully.
231 bytes sent in 00:00 (8.42 KiB/s)
The output indicates that we were able to transfer our JCL to JES and it is now in the job queue with the ID JOB00093. From here we can query the status of our job with the “ls” command:
ftp> ls
229 Entering Extended Passive Mode (|||21387|)
125 List started OK for JESJOBNAME=NVISO*, JESSTATUS=OUTPUT and JESOWNER=NVISO
JOBNAME JOBID OWNER STATUS CLASS
NVISO1 JOB00093 NVISO OUTPUT A RC=0000 4 spool files
250 List completed successfully.
The output RC=0000 (return code zero) indicates that our job completed successfully, meaning our user is able to submit jobs, our JCL syntax is appropriate, and our user can allocate datasets.
But what can we do if our job does not run as expected?
Depending on the configuration of the FTP server we may be able to review the output of our jobs. The important configuration item in this case is a JESINTERFACELEVEL of 2, without this we are likely only to see the return code of the job and none of the details. For this part we shall look at a job that was submitted to copy a dataset that does not exist. The list command for this job ID will look something like this:
ftp> ls JOB00094
229 Entering Extended Passive Mode (|||21314|)
125 List started OK for JESJOBNAME=NVISO*, JESSTATUS=ALL and JESOWNER=NVISO
JOBNAME JOBID OWNER STATUS CLASS
NVISO2 JOB00094 NVISO OUTPUT A (JCL error)
——–
ID STEPNAME PROCSTEP C DDNAME BYTE-COUNT
001 JES2 N/A S JESMSGLG 700
002 JES2 N/A S JESJCL 434
003 JES2 N/A S JESYSMSG 880
3 spool files
250 List completed successfully.
We can see that a JCL error occurred. When jobs are submitted to JES the validity of the JCL is checked before the job is scheduled for execution. This way JCL errors, including invalid references, will be shown almost immediately after submission. We can retrieve the output of the spool files with the “get” command followed by the job ID and the ID of the spool file. For example, `get JOB00094.3` to retrieve JESYSMSG, which can be viewed on our local machine:
└─$ cat JOB00094.3
ICH70001I NVISO LAST ACCESS AT 10:39:23 ON FRIDAY, JULY 5, 2024
IEFA111I NVISO2 IS USING THE FOLLOWING JOB RELATED SETTINGS:
SWA=BELOW,TIOT SIZE=64K,DSENQSHR=DISALLOW,GDGBIAS=JOB
IEFA107I NVISO2 COPYSTEP INMVS – DATA SET NONEXISTANT.DATA.SET NOT FOUND
IEF272I NVISO2 COPYSTEP – STEP WAS NOT EXECUTED.
IEF373I STEP/COPYSTEP/START 2024187.1040
IEF032I STEP/COPYSTEP/STOP 2024187.1040
CPU: 0 HR 00 MIN 00.00 SEC SRB: 0 HR 00 MIN 00.00 SEC
VIRT: 0K SYS: 0K EXT: 0K SYS: 0K
ATB- REAL: 224K SLOTS: 0K
VIRT- ALLOC: 13M SHRD: 0M
IEF375I JOB/ NVISO2/START 2024187.1040
IEF033I JOB/ NVISO2/STOP 2024187.1040
CPU: 0 HR 00 MIN 00.00 SEC SRB: 0 HR 00 MIN 00.00 SEC
Highlighting has been added to show the line indicating that the specified dataset was not found.
There is also the possibility that our user has been defined as a surrogate for another user. This would allow jobs to be submitted as that user and executed with that user’s permissions. If our user is able to execute TSO commands (IKJEFT01 maybe?), the command “RLIST SURROGAT *” will display any surrogate permissions assigned, if we are lucky the output will include something like this:
Indicating that our user can submit jobs on behalf of the user “RICK01”. To do so, we only need to add “,USER=RICK01” to the parameters of our job statement.
In addition to the site subcommand’s ability to interact with JES, there is another mode that can be accessed. By calling “site file=sql” it is possible to run DB2 queries if the user is permitted to do so.
From here the only limitations are our imagination and the permissions allocated to our user. Using the common programs in the list above we can run TSO and UNIX commands, create and copy files, and execute REXX scripts. There are several PoCs and even a Metasploit module that build on this technique, including a version of netcat adapted to suit OMVS for those looking to get a shell. We should tip our hats at this point to Bigendian Smalls (Chad Rikansrud) and Solider of Fortran (Phil Young) for all their work on mainframe hacking, their commitment to an open exchange regarding security and numerous tools for this platform without which the landscape would be quite dull indeed. Look out for more mainframe hacking articles here in the future.
Jonathan joined NVISO as a Senior Security Consultant in 2019 and is part of the software security assessment team. With more than 10 years professional experience in the IT industry Jonathan has worked extensively in the topics of security assessments, software development lifecycle, asset management, system hardening, and infrastructure design.
Jonathan’s main areas of focus within NVISO are smart contract auditing, software assessments, and mainframe security.