Book Read Free

PoC or GTFO, Volume 2

Page 28

by Manul Laphroaig


  If, instead of using UNIX commands, you wanted to execute TSO commands, you could use IKJEFT01, as in Figure 12.7.

  Figure 12.7: IKJEFT01 for Executing TSO Commands

  MACHINE ROOM

  THIS IS A LARGE ROOM FULL OF ASSORTED HEAVY MACHINERY, WHIRRING NOISILY. THE ROOM SMELLS OF BURNED RESISTORS. ALONG ONE WALL ARE THREE BUTTONS WHICH ARE, RESPECTIVELY, ROUND, TRIANGULAR, AND SQUARE. NATURALLY, ABOVE THESE BUTTONS ARE INSTRUCTIONS WRITTEN IN EBCDIC...

  Security

  You need to understand that OS/360 didn’t really come with security, and it wasn’t until SHARE in 1974 that the decision to create security products for the mainframe was made. IBM didn’t release the first security product for the mainframe until 1976. Later, competing products would be released, specifically ACF2 in 1978 and Top Secret sometime after that. IBM’s security product was RACF, or Resource Access Control Facility, and is what is commonly referred to as a SAF, or Security Access Facility. (ACF2/Top Secret are also SAFs.)

  Within RACF you have classes and permissions. You can create users, assign groups. You get what you’d expect from modern identity managers, but it’s very arcane and the command syntax makes no sense. For example, to add a user the command is ADDUSER:

  Adding a group is similar. Luckily, as with all things, z/OS IBM has really good documentation on how to use RACF.

  The key thing to know is that RACF is one huge database stored as data within a dataset. (You can see the location by typing RVARY.)

  Networking

  Mainframes run a full TCP/IP stack. This shouldn’t really come as a shock, as you saw NETSTAT above! TCP/IP has been available since the 80s on z/OS and has slowly replaced SNA (System Network Architecture, a crazy story beyond the scope of this article).

  TCP/IP is configured in a parmlib. I’m being vague here, not to protect the innocent, but because z/OS is so configurable that you can put these configuration files anywhere. Likely, however, you’ll find it in SYS1.TCPPARMS (a PDS).

  So, we’ve got TCP/IP configured and ready to go, and we understand that a lot of a mainframe’s power comes from batch processing. So far so good.

  Network Job Entry

  Understand that mainframes are expensive. Very expensive. When you buy one, you’re not in it for the short term. But, say you’re an enterprise in the 80s and have a huge printing facility designed to print checks in New Mexico. You buy a mainframe to handle all the batch processing of those printers and keep track of what was printed where and when. Unfortunately, the data needed for those checks is kept in a system in Ohio, and only the system in Idaho knows when it’s ready to kick off new print jobs automatically. Enter Network Job Entry.

  Using Network Job Entry (or NJE), you can submit a job in one environment, say the Idaho mainframe POTATO, and have it execute the JCL on a different system, for example the New Mexico mainframe CACTUS.

  An interesting property of NJE, depending on the setup, is that in the default configuration JES2 will take the userid of the submitter and pass that along to the target system. If that user exists on the target system and has the appropriate permissions, it will execute the job as that user. No password, or tokens. How it does this is explained below in section 4.1.

  Here’s the same UNIX JCL we saw above, but this time, instead of executing on our local system (CACTUS), it will execute on POTATO:

  The new line “/*XEQ POTATO” tells JES2 we’d like to execute this on POTATO, instead of our local system.

  Within NJE these systems are referred to as nodes in a trusted network of mainframes.

  The Setup

  NJE can use SNA, but most companies use TCP/IP for their NJE setup today. Configuring NJE requires a few things before you get started. First, you’ll need the IP addresses for the systems in your NJE network, then you need to assign names to each system (these can be different than hostnames), then you turn it all on and watch the magic happen. You’ll need to know all the nodes before you set this up; you can’t just connect to a running NJE server without it being defined.

  Let’s use our example from before:

  System

  Name

  IP

  System 1

  POTATO

  10.10.10.1

  System 2

  CACTUS

  10.10.10.2

  Somewhere on the mainframe there will be the JES2 startup procedures, likely in SYS1.PARMLIB(JES2PARM), but not always. In that file there will be a few lines to declare NJE settings. The section begins with NJEDEF, where the number of nodes and lines are declared, as well as the number of your own node. Then, the nodes are named, with the NODE setting and the socket setup with NETSRV, LINE, and SOCKET as shown in Figure 12.8.

  Figure 12.8: Nodes in our network

  With this file you can turn on NJE with the JES2 console command $S NETSERV1. This will enable NJE and open the default port, 175, waiting for connections. To initiate the connection, you could connect from POTATO to CACTUS with this JES2 command: $SN,LINE1,N=CACTUS, or, to go the other way, $SN,LINE1,N=POTATO.

  You can also password protect NJE by adding the PASSWORD variable on the NODE lines.

  The commands, in this case, don’t change when you connect, but a password is sent. These passwords don’t need to be the same, as you can see in the example. But once you start getting five or more nodes in a network, all with different passwords, managing these configs can become a pain, so most places just use a single, shared password, if they use passwords at all.

  NJE communication can also use SSL, with a default port of 2252. If you’re not using SSL, all data sent across the network is sent in cleartext.

  With this setup we can send commands to the other nodes by using the $N JES2 command. To display the current nodes connected to POTATO from CACTUS, you’d enter $N 1,’$D NODE’. These commands, sent with $N, are referred to as Nodal Message Records or NMR.

  Nodes!

  The current setup will only allow NMRs to be sent from one node to another. We need to set up trust between these systems. Thankfully, with RACF this is a fairly easy and painless setup. This setup can be done with the following commands on POTATO. Note, this is ultra insecure! Do not use this type of setup if you are reading this. This is just an example of what the author has seen in the wild:

  What this does is tell RACF that, for any job coming in from CACTUS, POTATO can assume that the RACF databases are the same. NJE doesn’t actually require users to sign in or send passwords between nodes. Instead, as described in more detail below, it attaches the submitting the user’s userid from the local node and passes that information to the node expected to perform the work. With the above setup the local node assumes that the RACF databases are the same (or similar enough), and that users from one system are the same on another. This isn’t always the case and can easily be manipulated to our advantage. Thus, in our current setup to submit work from one system to another, the user jsmith would have to exist on both.

  Figure 12.9: 33-byte NJE handshake packet

  Inside NJE

  With the high level discussion out of the way, it’s time to dissect the innards of NJE, so we can make it do what we want. Fortunately, IBM has documented how NJE works in the document has2a620.pdf or more commonly known as “Network Job Entry Formats and Protocols.” Throughout the rest of this article, you’ll see page references to the sections within this document that describe the process or record format being discussed.

  The Handshake

  I’m not going to go into the TCP/IP handshake, as you should be already familiar with it. After you’ve established a TCP connection nothing happens, literally. If you find an open port on an NJE server and connect to it with anything, the server will not send a banner or let you know what’s up. It just sits there and waits. It waits for a very specific initialization packet that is 33 bytes long.36 Figure 12.9 shows a breakdown of this packet.

  Figure 12.10: Packets from and to Cactus.

  Taking a look at a connection to POTATO from CA
CTUS, we see that CACTUS sends and receives the packet in Figure 12.10.

  This is the expected response when sending valid OHOST and RHOST fields. If you send an OPEN, and either of those are incorrect, you get a NAK response TYPE, followed by 24 zeroes and a reason code. Notice that you don’t need a valid OIP/RIP; it can be anything.

  Here’s the reply when we send an RHOST and an OHOST of

  FAKE: D5 C1 D2 40 40 40 40 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01

  SOH WHAT?

  Once an ACK NJE packet is received, the server is expecting a SOH/ENQ packet.37 From this point on, every NJE packet sent is surrounded by a TTB and a TTR.38 I’m sure these had acronyms at some point, but this is no longer documented. We just need to know that a TTB is eight bytes long with the third and fourth bytes being the length of the packet plus itself. Think of the B as BLOCK. Following the TTB is a TTR. An NJE packet can have multiple TTRs but only one TTB. A TTR is four bytes long and represents the length of the RECORD. SOH in EBCDIC is 0x01, ENQ is 0x2D.

  The NJE server replies with:

  or DLE (0x10) ACK0 (0x70). These are the expected control responses to our SOH/ENQ.

  NCCR, not a Cruise Line!

  The next part of initialization is sending an ‘I’ record. NJE has a bunch of different types of records, I, J, K, L, M, N, and B. These are known as Networking Connection Control Records (NCCR) and control NJE node connectivity.39 The important ones to know are I (Initial Signon), J (Signon Reply), and B (Close Connection).

  An initial sign-on record is made up of many components. The important things to know here are that the RCB is 0xF0, the SRCB is the letter ‘I’ in EBCDIC (0xC9), and that there are fields within an NCCR I record called NCCILPAS and NCCINPAS that are used for password-protected nodes. NCCILPAS × 2 is used when the nodes passwords are the same, whereas you’d use NCCINPAS if the local password is different from the target password. For example, if we set the PASSWORD in NJEDEF to NJEROCKS, we’d put NJEROCKS in both the NCCILPAS and NCCINPAS fields.

  We send an I record, then receive a J record, and now the two mainframes are connected to one another. Since we added trusted nodes with RACF, we can now submit jobs between the two mainframes as users from one system to another. If a user exists on both mainframes, jobs submitted from one mainframe to run on another will be executed as that user on the target system. The assumption is that both mainframes are secure and trusted (otherwise why would you set them up?)

  Bigger Packets

  As we get deeper into the NJE connection, more layers get added on. Once we’ve reached this phase, additional items are are now included in every NJE packet: TTB → TTR → DLE → STX → BCB → FCS → RCB → SRCB → DATA

  We already talked about TTB and TTR. DLE (0x10) and STX (0x02) are transmission control. The BCB, or Block Control Byte, is always 0x80 plus a modulo 16 number. It is used for tracking the current sequence number and is incremented each time data is sent.40 FCS is the Function Control Sequence. The FCS is two bytes long and identifies the stream to be used.41 RCB is a Record Control Byte, which can be one of the following:42

  SRCB is a Source Record Control Byte. For each RCB a SRCB is required. (IBM calls it a Source Record Control Byte, but I like to think of it as “Second.”)43

  And finally here is the data. The maximum length of a record (or TTR) is 255 bytes. Each record must have an RCB and a SRCB, which effectively means that each chunk of data cannot be longer than 253 bytes. That’s not a lot of room! Fortunately, NJE implements compression using SCB, or String Control Bytes.44 SCB compresses duplicate characters and repeated spaces using a control byte that uses a byte’s two high order bits to denote that either the following character should be repeated x times (101x xxxx), a blank should be inserted x times (100x xxx), or the following x characters should be skipped to find the next control byte (11xx xxxx). 0x00 denotes the end of compressed data, whereas 0x40 denotes that the stream should be terminated. Not everything needs to be compressed; for example, NCCR records don’t need to be.

  Figure 12.11 shows a breakdown of a packet, 00 00 00 3b 00 00 00 00 00 00 00 2b 10 02 82 8f cf 9a 00 cd 90 77 00 09 d5 c5 e6 e8 d6 d9 d2 40 01 a8 00 c6 d7 d6 e3 c1 e3 d6 82 ca 01 5b c4 40 d5 d1 c5 c4 c5 c6 00 00 00 00 00.

  Since this is an NMR (RCB = 0x9A), we can break down the data after decompression using the format described by IBM.45 The decompressed payload is shown in Figure 12.12.

  Therefore, this rather long packet was used to send the command $D NJEDEF from the node POTATO to the node NEWYORK.

  Abusing NJE

  As discussed earlier, userids are expected to be the same across nodes. But knowing how enterprises operate requires conducting a little test.

  Pretend that you work for a large enterprise with multiple mainframe environments all connected through NJE. In this example, two nodes exist: (1) DEV and (2) PROD.

  Figure 12.11: Example NJE packet

  A user named John Smith, who manages payroll, frequently works in the production environment (PROD) and has an account on that system with the userid “JSMITH.”

  A developer named Jennifer Smith is hired to help with transaction processing. Jennifer will only ever do work on the development environment, so an “Identity Manager” assigns her the user id “JSMITH” on the DEV mainframe.

  What is the problem in this example? How could Jennifer exploit her access on DEV to get a bigger paycheck?

  Well, the problem is that whoever set up the accounts didn’t bother to check all the environments before creating the new user account on DEV. Since DEV and PROD are trusted nodes in an NJE network, Jennifer could submit jobs to the production environment (using /*XEQ PROD), and the JCL would execute under Johns permissions—not a very secure setup. Worse still, the logs on PROD will show that John was the one messing with payroll to give Jennifer a raise.

  Garbage SYSIN

  When JCL is sent between nodes, it is called SYSIN data. To control who the data is from, the type of data, etc., a few more pieces of data are added to the NJE record. When JES2 processes JCL, it creates the SYSIN records. As it processes the JCL, it identifies the /*XEQ command and creates the Job Header, Job Data, and Job Footer.46

  Job Data is the JCL being sent, Job Footer is some trailing information, and Job Header is where the important components (for us) live.

  Within the Job Header itself there are four sub-sections: General, Scheduling, Job Accounting, and Security.

  The first three are boring and are just system stuff. (They’re actually very exciting, but for this writeup they aren’t important.) The good bits are in the Security Section Job Header. The security section header is made up of 18 settings,47 shown in Figure 12.13.

  The two most important of these are the NJHTOUSR and NJHTOGRP variables. These define the User ID and Group ID of the job coming into the system. If someone were able to manipulate these fields within the Job Header before it was sent to an NJE server, they could execute anything as any user on the system (so long as they had the ability to submit jobs, something almost every user does). At this point you’re basically two fields away from owning a system.

  Command and Control

  In an earlier section, we discussed NMR, Nodal Message Records. These have an RCB of 0x9A. By far the most interesting property of NMRs is their ability to send commands from one node to another. This exists to allow easier, centralized management of a bunch of mainframe (NJE) nodes on a network. You send commands, and the reply gets routed back to you for display.

  For example, we can send the JES2 command $D JQ that will tell us all the jobs that are currently running. To display all the jobs running on CACTUS from POTATO, we simply add $N 2 in front of the command we wish to execute: $N 2,’$D JQ’

  To make changes at a target system we can issue commands with $T. The command $D JOBDEF,JOBNUM tells us the maximum number of jobs that are allowed to run at one time. We can increase (or decrease) this number with $T JOBDEF,JOBNUM=#.

&nbs
p; Figure 12.12: Decompressed payload from Figure 12.11.

  Figure 12.13: Security Section Job Header

  We can do the exact same thing with NJE, but instead pass it a node number $N 2,’$T JOBDEF,JOBNUM=3001’. This is the power of NMR commands. Notice that there are no userids or passwords here, only commands going from one system to another.

  A reference for every single JES2 command exists. Some interesting JES2 commands are the ones we already talked about (lowering/increasing number of concurrent jobs), but you can also profile a mainframe using the various $D (for display) commands. JOBDEF, INITINFO, NETWORK, NJEDEF, JQ, NODE etc. NJEDEF is especially important!

  Breaking In

  It’s now time to make NJE do what we want so we can own a mainframe. But there’s some information you’ll need to know:

  - IP/Port running NJE

  - RHOST and OHOST names

  - Password for I record (not always)

  - A way to connect

  Finding a Target System

  Of all the steps, this is likely the easiest step to perform. The most recent version of Nmap (7.10) received an update to probe for NJE listening ports.

  Using Nmap it’s now easy to find NJE.

  RHOST, OHOST, and I Records

 

‹ Prev