Book Read Free

PoC or GTFO, Volume 2

Page 29

by Manul Laphroaig


  This is the trickiest part of breaking NJE. Recalling our earlier discussion of connecting, you need a valid RHOST (any systems node name) and OHOST (the target systems node name). If the RHOST or OHOST are wrong, the system replies with an NJE NAK reply and a reason code R. Oftentimes the node name of a mainframe is the same as the host name; so you should try those first. Otherwise, it will likely be documented somewhere on a corporate intranet or in some example JCL code with /*XEQ—or you could just ask someone, and they’ll probably tell you.

  If you have access to the target mainframe already, you could try a few things, like reading SYS1.PARMLIB(JES2PARM) and searching for NJEDEF/NODE. You could also issue the JES2 command $D NJEDEF or $D NODE, which will list all the nodes and their names.

  If none of those options work for you, it’s time to use brute force. When you connect to an NJE port and send an invalid OHOST or RHOST, you get a type of NAK with a reason code of R=1. However, when you connect to NJE and place the RHOST value in the OHOST field, it replies with a NAK but with a reason code of 4! Now this is something we can use to our advantage.

  Using Nmap again, we can now use a newly-released NSE script nje-node-brute.nse to brute-force a system’s OWNNODE node name.48

  NJE node communication is made up of an OHOST and an RHOST. Both fields must be present when conducting the handshake. This script attempts to determine the target systems NJE node name.

  By default, the script will try to brute-force a system’s OHOST value. First trying the mainframe’s hostname and then using Nmap’s included list of default hosts. Since NJE nodes will generally only have one node name, it’s best to use the script argument brute.firstonly=true.

  With the OHOST determined (POTATO), we can brute-force valid RHOSTs on the target system. Using the same nje-node-brute Nmap script, we use the argument ohost=POTATO. Before running the script, it’s best to do some recon and discover names of other systems, decommissioned systems, etc. These can be placed in the file rhosts.txt and passed to the script using the argument hostlist=rhosts.txt.

  Note: If CACTUS was connected at the time this script was run, it wouldn’t show up in the list of valid systems. This is due to the fact that a node may only connect once. So if you’re doing this kind of testing, you might want to wait for maintenance windows to try and brute-force. With valid RHOSTs (SANDBOX, CACTUS, and LPAR5) and the OHOST (POTATO) in hand we can now pretend to be a node.

  In most places, this will be enough to allow you to fake being a node. In some places, however, they’ll have set the PASSWORD parameter in the NJEDEF config. This means that we’ve got one more piece to brute-force.

  Thankfully, there’s yet another new Nmap script for brute-forcing I records, nje-pass-brute.

  After successfully negotiating an OPEN connection request, NJE requires sending, what IBM calls, an “I record.” This initialization record may sometimes require a password. This script, provided with a valid OHOST/RHOST for the NJE connection, brute forces the password.

  Using this script is fairly straightforward. You pass it an RHOST and OHOST, and it will attempt to brute-force the I record password field:

  Behind the scenes, this script is connecting and trying “I Records” setting the NCCILPAS and NCCINPAS variables to the passwords in your word list.

  I’m a Pretender

  Using the information we’ve gathered, we could set up our own mainframe, add an NJEDEF section to the JES2 configuration file, and connect to POTATO as a trusted node. But who’s got millions to spend on a mainframe? The good news is you don’t have to worry about any of that. Since getting your hands on a real mainframe is all but impossible, your author wrote a Python library that implements the NJE specification, allowing you to connect to a mainframe and pretend to be a node.49

  Using the NJE library, we can do a couple of interesting things, such as sending commands and messages, or sending JCL as any user account.

  First, we’re going to create our own node, just in case the node we’re pretending to be comes back online (preventing us from using it). Using iNJEctor.py we can send commands we’d like to have processed by the target node. Before doing that, we need to see how many nodes are currently declared with $D NJEDEF,NODENUM:

  We’ll increase that by one with the command $T NJEDEF, NODENUM=5, then add our own node called h4ckr using the commands $T NODE(5),name=H4CKR and $add socket(h4ckr). See Figure 12.14.

  The node h4ckr has now been created. Finally, we’ll want to give it full permission to do anything it wants with the command $T node(h4ckr), auth=(Device=Y,Job=Y,Net=Y,System=Y). See Figure 12.15

  Good, we have our own node now. This will only allow us to send commands and messages. If we wanted, we could mess with system administrators now.

  Figure 12.14: Example use of inJEctor.py

  Figure 12.15: iNJEctor.py giving full permissions.

  And when Margo logs on, or tries to do anything she would receive this message:

  That is fun and all, but we could also do real damage, such as shutting off systems or lowering resources to the point where a system becomes unresponsive. But where’s the fun in that? Instead, let’s make our node trusted.

  We’ll need to find a user with the appropriate permissions first. From previous research, I know Margo runs operations and has a userid of margo. Using jcl.py we can send JCL to a target node. This script uses the NJELib library and manipulates the NJHTOUSR and NJHTOGRP settings in the Job Header Security Section to be any user we’d like. We already know CACTUS is a trusted node on POTATO, so let’s use that trust to submit a job as Margo.

  To check if she has the permissions we need, we use IKJEFT01, which executes TSO commands, and the RACF TSO command lu, which lists a user’s permissions. Figure 12.16.

  The important line here is ATTRIBUTES=SPECIAL, meaning that she can execute any RACF command. This, in turn, means she has the ability to add trusted nodes for us. Now that we confirmed she has administrative access, we submit some JCL that executes the commands we need to add a new trusted node. While we’re at it, might as well add a new superuser named DADE, as shown in Figure 12.17.

  Figure 12.16: JCL Permissions Check

  Now we added the node H4CKR as a trusted node. Therefore, any userid that exists on POTATO is now available to us for our own nefarious purposes. In addition, we added a superuser called DADE with access to both TSO and UNIX. From here we could shutdown POTATO, execute any commands we’d like, create new users, reset user passwords, download the RACF database, create APF authorized programs. The ownage is endless.

  Conclusion

  NJE is relatively unknown despite being so widely used and important to most mainframe implementations. Hopefully, this article showed you how powerful NJE is, and how dangerous it can be. Everything in this article could be prevented with a few simple tweaks. Not using the PASSWORD parameter and instead using SSL certificates for system authentication would make these attacks useless. On top of that, instead of declaring the nodes to RACF, you could give very specific access rights to users from various nodes. This would prevent a malicious user from submitting as any user they please.

  If you’re really interested in this protocol, NJELib also supports a debug mode, which gives information about everything happening behind the scenes. It’s very verbose. Another feature of NJELib is the ability to deconstruct captured packets.

  You should now have a grasp of the mainframe and NJE. If your interest has been piqued about the endless potential of mainframe hacking, there are some great writeups about buffer overflows and crypto on z/OS at bigendiansmalls.com and mainframed767.-tumblr.com.

  Figure 12.17: Adding a Superuser

  12:7 Exploiting Weak Shellcode Hashes to Thwart Module Discovery; or, Go Home, Malware, You’re Drunk!

  by Mike Myers and Evan Sultanik

  There is a famous Soviet film called Ирония судьбы, или C лёгким паром! (The Irony of Fate, or Enjoy Your Bath!) that pokes fun at the uniformity of Brezhne
v-era public architecture and housing. The protagonist of the movie gets drunk and winds up on a plane bound for Leningrad. When he arrives, he mistakenly believes he landed in his home town of Moscow. He stumbles into a taxi and gives the address of his apartment. Sure enough, the same address exists in Leningrad, and the building looks identical to his apartment in Moscow. His key even unlocks the apartment with the same number, and the furniture inside is nearly identical to his, so he decides to go to sleep. Everyone’s favorite heart-warming romantic comedy ensues, but that’s another story.

  Neighbors, the goal of this article is to convince you that Microsoft is Brezhnev, Windows is the Soviet Union, kernel32.dll is the apartment, and malware is the drunk protagonist. Furthermore, dear neighbor, we will provide you with the knowledge of how to coax malware into tippling from our proverbial single malt waterfall so that it mistakenly visits a different apartment in a faraway city.

  Background: PIC and Malware

  Let’s begin with a look at how position-independent code (PIC) used by malware is different from benign code, and then examine the logic of the Metasploit payload known as “windows/exec,” which is a representative example of both exploit shellcode and malware-injected position-independent code. If you’re already familiar with how malware-injected position-independent code works, it’s safe for you to skip to the section on Shellcode Havoc, page 547.

  Most executable code on Windows is dynamically linked, meaning it is compiled into separate modules and then is linked together at runtime by the operating system’s executable loader as a system of imports and exports. This dynamic linkage is either implicit (the typical kind; dynamic library dependence is declared in the header and the loader performs the address lookups at load time) or explicit (less common; the dynamic library is optionally loaded when needed and address lookups are performed with the GetProcAddress system API).

  Much of maliciously delivered code—such as nearly all remote exploits and most instances of code that is injected by one process into another—shares a common trait of being loaded illegitimately: it circumvents the legitimate sequence of being loaded and initialized by the OS executable loader. It is therefore common for malicious code to not run as benign code does in its own process. Because attackers want to run their code within the access and privilege of a target process, malicious code is injected into it either by a local malicious process or by an arbitrary code execution exploit. These two approaches (code injection and exploit shellcode) can be treated similarly in that both of them involve position-independent injected code.

  Unlike benign code that is loaded by the operating system as a legitimate executable module from a file on disk, illicit position-independent code must search and locate essential addresses in memory on its own without the assistance of the loader. Because of Address Space Layout Randomization (ASLR), the injected code cannot simply use pre-determined hardcoded addresses of these locations; neither can it rely on the GetProcAddress routine, because it doesn’t know that address either.

  Typically, the first goal of injected code is to find kernel-32.dll, because it contains the APIs necessary to bootstrap the remainder of the malware’s computation. Before Windows 7, everyone was using shellcode that assumed kernel32.dll was the first module in the linked list pointed to by the Process Environment Block (PEB), because it was the first DLL module loaded by the process. Windows 7 came along and started loading another module first, and that broke everyone’s shellcode.

  A common solution these days is just as fragile. Some have proposed shellcode that assumes kernel32.dll is the first DLL with a 12-character name in the list (the shellcode just looks for a module name length match). If we were to load in a DLL named PoCrGTFO.dll before kernel32.dll, that shellcode would fail. Other Windows 7 shellcode assumes that kernel32.dll is the second (now third) DLL in the linked list; we would be invalidating that assumption, too.

  The Metasploit Framework is perhaps the most popular exploit development and delivery framework. One can create a custom exploit reusing standard components that Metasploit provides, greatly accelerating development time. One important component is the payload. A “payload” in Metasploit parlance is the generic (reusable by many exploits) portion of position-independent exploit code that attackers execute after they have successfully begun executing arbitrary instructions, but before they have managed to do anything of value. A payload’s function can be to either establish a barebones command & control capability (e.g., a remote shell), to download and execute a second stage payload (most common in real-world malware), or to simply execute another program on the victim. The latter is the purest example of a payload, and this is what we will show here. The logic of the “windows/exec” payload is presented in Algorithm 1. As you can see, it employs a relatively sophisticated method for discovering kernel32.dll, by walking the PEB data structure and matching the module by a hash of its name.

  On the following pages, we have included an annotated listing of the disassembly for this payload. We encourage the reader to follow our comments in order to get an understanding for how injected code gets its bearings. Although this code directly locates the function it wants, if it were going to find more than one, it would probably just use this method to find GetProcAddress instead and use that from there on out.

  For clarity, the disassembly is shown with relative addresses (offsets) only. The address operands in relative jump instructions have been similarly formatted.

  Shellcode Havoc: Generating Hash Collisions

  In the previous section, we described how PIC that is injected at runtime is inherently “drunk”: since it circumvents the normal loader, it needs to bootstrap itself by finding the locations of its required API calls. If the code is malicious, this imposes additional constraints, such as size restrictions (on the shell-code) and the inability to hardcode function names (to avoid fingerprinting). Some malware is very naïve and simply matches function names based on length or their position in the EAT; such approaches are easily thwarted, as described above. Others have proposed completely relocating the Address of Functions table and catching page faults when any code tries to access it (cf. Phrack Volume 0x0b, Issue 0x3f, Phile #0x0f).

  Most modern (Windows 7 and newer) malware payloads temper their drunkenness by hashing the module and function names of the APIs they need to find. Unfortunately, the aforementioned constraints on shellcode mean that a cryptographically secure hashing algorithm would be too cumbersome to employ. Therefore, the hashing algorithms they use are vulnerable to collisions.

  If we can generate a new module and/or function name that hashes to the same value that the malware is looking for, and if we ensure that the decoy module/function occurs before the real one in the EAT linked list, then any time that function is called we will know it is from malicious code.

  Shellcoder’s Handbook Hash

  First, let’s take a look at the hashing algorithm espoused by Didier Stevens in The Shellcoder’s Handbook. In C, it’s a nifty little one-liner:

  for(hash=0; *str; hash = (hash + (*str++ | 0x60)) << 1);

  Using this algorithm, the string “LoadLibraryA” hashes to 0x0D-5786.

  The first thing to notice is that the least significant bit of every hash will always be a zero, so let’s just shift the hash right by one bit to get rid of the zero. Next, notice that if the value of the hash is less than 256, then any single character that bit-wise matches the hash except for its sixth and seventh most significant bits (0x60 = 0b01100000) will be a collision. Therefore, we can try all four possibilities: hash, hash XOR 0x20, hash XOR 0x40, and hash XOR 0x60. In the case when the value of hash is greater than 256, we can inductively apply this technique to generate the other characters.

  The collision is constructed by building a string from right to left. A Python script that enumerates all possible collisions is as follows.

  Running collide(“LoadLibraryA”) yields over 100,000 collisions in the first five seconds alone, and can likely produce orders of m
agnitude more. The following are the first ten, but of course, just one collision is sufficient.

  4baaaabaabaa 3daaaabaabaa

  2faaaabaabaa 1haaaabaabaa

  0jaaaabaabaa 4acaaabaabaa

  3ccaaabaabaa 2ecaaabaabaa

  1gcaaabaabaa 0icaaabaabaa

  Metasploit Payload Hash

  Next, let’s examine the Metasploit payload’s hashing function described in the previous section. This function is a bit more complex, because it involves bit-wise rotations, making a brute-force approach (like we used for The Shellcoder’s Handbook algorithm) infeasible. The Metasploit hash works like this: at each byte of a NULL-terminated string (including the terminating NULL byte), it circularly shifts the hash right by 0xD (13) places and then adds the new byte. This hash was likely chosen because it is very succinct: the inner part of the loop requires only two instructions (ror and add).

  The key observation here is that, since the hash is additive, any prefix of a string that hashes to zero will not affect the overall hash of the entire string. That means that if we can find a string that hashes to zero, we can prepend it to any other string and the result will have the same hash:

  HASH(A) = 0 ⇒ HASH(B) = HASH(A + B).

  This hash is relatively easy to encode as a Satisfiability Modulo Theories (SMT) problem, for which we can then enlist a solver like Microsoft’s Z3 to enumerate all strings of a given length that hash to zero. To find strings of length n that hash to zero, we create n character variables, c1, . . . , cn, and n + 1 hash variables, h0, h1, . . . , hn, where hi is the value of the hash for the substring of length i, and h0 is of course zero. We constrain the character variables such that they are printable ASCII characters (although this is not technically necessary, since Windows allows other characters in the EAT), and we also constrain the hash variables according to the hashing method:

 

‹ Prev