Mainframes are ever being included in Red Team Engagements to demonstrate impact. If an adversary can access your mainframe environment they could cause material damage to customer data, cause an outage or potentially steal money. However, when an adversary gets on a mainframe, the account they have may not provide enough access to do anything. NetSPI has performed multiple Mainframe Penetration Tests where the base account was locked down enough to prevent them from doing any real damage.

In a vacuum, that’s fine. But adversaries typically don’t operate in a vacuum and will leverage whatever access they have to further develop their target list. In this blog post we’ll outline the risks of allowing all users the ability to run the LISTUSER command against any user.

The LISTUSER command is a RACF command that you use in TSO that allows you to list information about accounts on the system. TSO is the terminal shell for mainframes, similar to something you’d find on a Linux server, but the prompt is “READY” instead of $. Issuing the LISTUSER command without any arguments outputs information about the currently logged in user.

If you have access to list other user information, you can issue the LISTUSER command with the argument for any user you want to see. For example running the “LISTUSER NETSPI” would output information about that specific user.

Allowing anyone to just list anyone else’s username and group membership is dangerous. In fact, once you’ve confirmed you can access other user profiles through the LISTUSER command you can issue the command LISTUSER * to list all users in RACF! From an adversarial perspective this makes enumerating users and conducted password sprays or targeted attacks very easy.

Although, that’s not 100% true. For a typical user, if you’re allowed to list other users it will return user information, but if the user profile you tried to list is an administrative user (i.e. that user has system SPECIAL in RACF) you get an error message that your access is denied. Once we know this, enumerating administrative accounts is trivial.

Typically, mainframe shops will have username conventions. For this example (and the sake of time) we’ll assume a z/OS userid starts with a letter followed by five numbers. With the ability to list user IDs we can use some REXX to identify every account on the system and all the privileged accounts:

/* REXX */ 
parse arg len 
SAY "LISTUSER RACF User Enumeration Tool" 
SAY "VER 1" 
/* License: GPLv3 */ 

ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 
NUM = '0123456789' 
DO I=1 to 26 
 S1 = RIGHT(LEFT(ALPHA,I),1) 
 DO J=1 to 10 
  S2 = RIGHT(LEFT(NUM,J),1) 
  DO K=1 to 10 
   S3 = RIGHT(LEFT(NUM,K),1) 
   DO L=1 to 10 
    S4 = RIGHT(LEFT(NUM,L),1) 
    DO M=1 to 10 
     S5 = RIGHT(LEFT(NUM,M),1) 
     DO N=1 to 10 
      S6 = RIGHT(LEFT(NUM,N),1) 
         L = OUTTRAP('LUO.') 
         LU S1||S2||S3||S4||S5||S6 
         L = OUTTRAP('OFF') 
         if POS('USER=',LUO.1) > 0 THEN DO 
          PARSE VAR LUO.1 USER . 
          PARSE VAR LUO.3 ATTR . 
          SAY USER ATTR 
     END 
    END 
   END 
  END 
 END 
END

After this script completes, I have a list of five users who are privileged accounts — now I know to target those users either in a phishing campaign or stealing their credentials using other means.

We know now that allowing access to LISTUSER is unsafe. Fortunately IBM explains how to limit who should be able to run the LISTUSER command in its z/OS Security Server RACF Security Administrator’s Guide. In summary, to prevent enumeration quickly you can limit access to the IRR.LISTUSER profile in the FACILITY class.

This, however, only prevents an attacker from listing user details. Due to a quirk in the way IBM implemented the LISTUSER command, we can still enumerate all the accounts on the system. If you enter the LISTUSER command and don’t have access to perform that function RACF returns an access denied error message. Conversely, if we enter a user that doesn’t exist, we get the message “that user doesn’t exist.”

Therefore, using the same REXX code as above, we can still enumerate every user on the system; we just can’t get details about that user. A few changes to the code left up to the reader would still allow you to enumerate all users. Additionally, for a sophisticated attacker it wouldn’t be hard to link mainframe IDs to domain user IDs and hone their target selection for either spear phishing or password spray attacks.

If you were hoping to be able to catch this type of attack in SMF, that is currently not possible. According to IBM, you cannot detect this type of attack even if you’ve sufficiently locked down who can issue the LISTUSER command. From IBM: “RACF does not log failed access attempts to IRR.LU resources. Successful accesses to IRR.LU resources are logged at the installation’s discretion.”

Despite these limitation NetSPI strongly recommends you limit who is able to run the LISTUSER command against other users, limiting the information available to attackers make it much harder to profile the system and other users. NetSPI offers multiple different types of Mainframe Penetration Tests to help you better understand the threats and mitigations to your enterprise mainframes, identifying the misconfiguration is just one item we look for when conducting penetration tests or red team activities.