It’s not every day that we run into kiosks, terminals, etc. that have HyperTerminal as one of the available applications. This may be a corner case, but it’s another example to add to Scott’s blog about break out methods. In this example, we encountered a terminal setup, where the system was a fairly locked down Windows XP machine. HyperTerminal was one of the only applications in the start menu, and other functionality (shortcut keys, right-click, run) was not available. The method here is pretty simple, but now you can add HyperTerminal as another program to use for breaking out.
Steps to Exploit
First off, we want to open up HyperTerminal and create a new connection to write to. In this example, we’ll just use our non-connected COM1 port as a connection. This is pretty easy to set up, it’s more or less clicking next until you are dropped into the HyperTerminal window below.
At this point, we will want to turn on the “Echo typed characters locally” setting, so we can see what we’re doing. This can be found under File -> Properties -> Settings Tab -> ASCII Setup
We will want to save the text that we’re typing to the HyperTerminal screen, so select Transfer, then Capture Text.
Since the user we are using has rights to write to the startup folder, we are just going to save a batch file that will run at the user’s next logon (C:Documents and SettingsAll UsersStart MenuProgramsStartuptest.bat). You may not have rights to save there, but you might have access to save the file to another location that you could run the script from. Once the capture is started, type the command(s) that you want to run into the HyperTerminal window and stop the capture. Here we are just going to type cmd and stop, so that the script will pop up a cmd shell when we login. You have plenty of other possible programs that you could run here.
We can see in the example screen that the test.bat file was saved to the startup folder and when the script is executed, a command shell pops up.
You may never have to use HyperTerminal to break out, but keep it in mind if you are locked out of other routes. For our sysadmin readers, don’t allow HyperTerminal on your terminals, kiosks, etc.
For high-level languages such as .NET or Java, it is quite trivial to reverse-engineer application binaries to recover the original source code if the application doesn’t employ any type of encryption or obfuscation. Some popular decompilers are: Dotpeek, Reflector, JustDecompile (.NET), Java Decompiler, Mocha, DJ Java Decompiler (Java). In a penetration test, program’s source code is valuable information that can be used to attack application and trusted resources. To help prevent the recovery of source code, obfuscation is often being used. Obfuscation is the intentional act of converting original source code to a form that is difficult for human to understand. The main goal of obfuscation is to provide “security through obscurity” in order to prevent attackers from:
Recovering original source code (Intellectual Property (IP) theft)
Tampering with business logic by modifying source code
Obfuscators (programs that help obfuscating source code), generally achieve its purposes by:
Name obfuscation: renaming classes, methods, packages, fields, etc.
Code and Data Flow obfuscation: alternating program logic or adding gibberish instructions that may not be decompiled into valid source code
Inject Unnecessary Code
Remove debug information
This blog post aims to provide a simple example of Java Obfuscation. Hopefully it will be helpful for programmers wanting to protect their applications.
Choosing Java Obfuscator
If you just want to simply do name/data flow obfuscation for your Java program, free obfuscators such as ProGuard or yGuard will suit your needs. For stronger protection and extra capabilities such as String Encryption, Incremental Obfuscation, Stack Trace Transplantation, you may need to choose a commercial product. This article provides a great comparison among popular Java Obfuscators I am usually a proponent of open source programs; however for this tutorial I will be using Zelix KlassMaster to obfuscate a custom server/client Java program. In my opinion, it provides decent feature set for a reasonable price point. For complete feature listing visit Zelix Klassmaster
Let’s take a look at this client/server program. On the Server side, we have following classes:
ServerRun.java: Main Program
Person.java, SearchObject.java,SearchResult.java: shared classes between client/server
Compiling server classes give us Java Archive file: Server.jar On the Client side, we have following classes:
ClientRun.java: Main Program
Person.java, SearchObject.java,SearchResult.java: shared classes between client/server
As mentioned above, it is trivial to recover original source code with a Decompiler Program such as Java Decompiler (JD-GUI). Here is what it looks like when decompiling Client.jar with JD-GUI:
The original source code has been recovered with great accuracy. With this information, attackers can learn the business logic of the program or import decompiled project into an IDE, modify code flow to bypass program restrictions.
Bad Obfuscation Example
The most common mistake when dealing with obfuscation is to involve it later in the game, after the implementation phase, when the binaries are made and ready for testing. In this bad example below, Client.jar is obfuscated with Zelix KlassMaster with the following settings:
After obfuscation the result binary will have the following structure:
Decompiling it will result in an empty class file as shown below:
The command to run Client program will be the same as before: java -classpath Client.jar com.client.ClientRun However it will generate exception when trying to communicate with Server Program. The reason is: Server and Client programs share some common classes: Person, SearchObject and SearchResult. When Server program attempts to read com.commons.Person object, for example, in Client program it is transformed into com.client.a, causing the Exception in the screenshot below. For both of the programs to work, Client and Server programs must be using the exact same classes, including package name.
General Obfuscation Procedures
Below is any overview of common obfuscation procedures.
Identify common public interfaces and methods that will be used either within the same program or by another components
Identify serialized classes that will be shared among different components
Refactor classes in step 1 and step 2 into separate project
Compile them into Java Archive (.jar) file
Obfuscate common libraries (optional)
Import necessary library into relevant components
Identify main entry points
Obfuscate the rest of the code
Identify common public interfaces and methods that will be used either within the same program or by another componentsRemote Procedure Call (RPC) Applications typically communicate with each other through some common interfaces. They can use those interfaces to invoke functions on another machine or network. Those interfaces must be made available to all involved components. Some examples:
JBoss Remoting Interfaces
Apache XML RPC Interfaces
This principle can also be applied for programs that provide Application Programmable Interface (API) for other software components.The example doesn’t have any of the above interfaces so this step can be ignored.
Identify serialized classes that will be shared among different componentsSerialization allows programmers to save and restore state of objects. In case of transmitting objects between different components, serializable classes must be defined the same in all components. In Java, Serializable classes are easily recognized because they must implement Serializable interface. Example:public classPersonimplementsSerializable. In this example, serializable classes are: Person, SearchObject and SearchResult
Refactor aforementioned classes into separate projectsIn this step, the excluded classes/interfaces mentioned in step 1 and step 2 are refactored or moved into separate projects. It is also a good design practice to refactor common classes into new components, since it will eliminate redundancy, keep the code base clean and easy to maintain.In Eclipse, I created a new project called ServletCommons and import serializable classes in step 2:
Compile them into Java Archive (.jar) librariesThe purpose of this step is to generate jar files that can later be imported into components that will reuse/extend them. The following command will generate servlet-commons.jar file for project ServletCommons. C:ServletCommonsbin> jar cvf servlet-commons.jar *.*
Obfuscate common libraries (optional)If you follow good software design practice, step 1-4 may have already been done. It is now up to you to decide if these libraries should be obfuscated. If your software is well-designed, RPC Interfaces/ API should not publicly expose sensitive methods, and consequently need no obfuscation. The same principle applies for Serializable classes: they should not contain any application logic, other than object’s data. Obfuscate common libraries will provide greater protection, but with a cost. Your vanilla code base will now contain original source code and obfuscated libraries. It will probably add more overhead and introduce more efforts into your development and testing environment.
Import libraries into relevant components, when necessaryNow you will need to import those libraries to projects that need them. This can be done with an IDE, ant or maven build script. You may also need to deploy them on the server, depending on your setup. The example in the screenshot below show how to do it in Eclipse:
If you choose to obfuscate common libraries in step 5, this is the time to fix potential compilation errors due to class/package/method renaming.
Identify main entry points, retain mapping of entry pointsFor complex applications that contain native code (C, C++) call out to Java component, the main entry points to Java components need to be clearly identified and kept consistent. For this reason you should keep track of the mapping between entry points and obfuscate code, and update components that are calling now-obfuscated entry points. For more information check out this documentation: https://www.zelix.com/klassmaster/docs/tutorials/incrementalObfuscation.html
Import necessary libraries and obfuscateFirst you will need to set the classpath of the project. As mentioned in step 4, servlet-common.jar is required for both Client and Server program
The following settings are used to obfuscate Client Program. Note that I excluded public static void main(String args) as it is required to run any Java application:
Specify change log location:
Save obfuscated classes into a new jar file:
Decompiling it with JD-GUI:
Review change log for the new names of entry points. Update all components that are using those entry points. Sample change log will look like: Class: public com.client.ClientRun => d Source: “ClientRun.java” Now move on to obfuscate Server program. To keep it simple, I opt to exclude servlet class name from obfuscation:
Save obfuscated classes into new jar file:
Decompiling it with JD-GUI:
TestDeploy Server.jar and servlet-commons.jar to Tomcat. Run the client with the newly obfuscated entry point: java -classpath Client.jar;servlet-commons.jar d
Obfuscation, in general, makes reverse-engineering a time-consuming process for most attackers. Obfuscation cannot prevent other attack vectors such as bytecode manipulation or communication manipulations. To protect your application from such threats, consider implementing code signing in your application. Regarding Zelix Klassmaster itself, Security Researcher Yiannis Pavlosoglou has discovered that the its String Encryption routine is actually composing of five XOR operations, with encryption keys hard-coded in the class file. More information can be viewed here: https://www.defcon.org/images/defcon-15/dc15-presentations/dc-15-subere.pdf
When assessing an application for weaknesses in a linux environment, we won’t always have the luxury of freely available source code or documentation. As a result, these situations require more of a black box approach where much of the information about the application will be revealed by attempting to monitor things such as network communications, calls to cryptographic functions, and file I/O.
One method of monitoring applications to extract information is to attach a debugger, such as GDB, to the process and to dump register or stack values as breakpoints are hit for the desired function calls. While this has the advantage of giving fine grained control over things such as code flow and register contents, it is also a cumbersome process compared to hooking the function calls of interest to modify their behavior.
Function call hooking refers to a range of techniques used to intercept calls to pre-existing functions and wrap around them to modify the function’s behavior at runtime. In this article we’ll be focusing on function hooking in linux using the dynamic loader API, which allows us to dynamically load and execute calls from shared libraries on the system at runtime, and allows us to wrap around existing functions by making use of the LD_PRELOAD environment variable.
The LD_PRELOAD environment variable is used to specify a shared library that is to be loaded first by the loader. Loading our shared library first enables us to intercept function calls and using the dynamic loader API we can bind the originally intended function to a function pointer and pass the original arguments through it, effectively wrapping the function call.
Let’s use the ubiquitous “hello world” demonstration as an example. In this example we’ll intercept the puts function and change the output. Here’s our helloworld.c file:
int puts(const char *message)
int (*new_puts)(const char *message);
new_puts = dlsym(RTLD_NEXT, "puts");
if(strcmp(message, "Hello world!n") == 0)
result = new_puts("Goodbye, cruel world!n");
result = new_puts(message);
Let’s take a moment to examine what’s going on here in our libexample.c file:
Line 5 contains our puts function declaration. To intercept the original puts we define a function with the exact same name and function signature as the original libc puts function.
Line 7 declares the function pointer new_puts that will point to the originally intended puts function. As before with the intercepting function declaration this pointer’s function signature must match the function signature of puts.
Line 10 initializes our function pointer using the dlsym() function. The RTLD_NEXT enum tells the dynamic loader API that we want to return the next instance of the function associated with the second argument (in this case puts) in the load order.
We compare the argument passed to our puts hook against “Hello world!n” on line 12 and if it matches, we replace it with “Goodbye, cruel world!n”. If the two strings do not match we simply pass the original message on to puts on line 14.
First we compile helloworld.c as one normally would. Next we compile libexample.c into a shared library by specifying the -shared and -fPIC compile flags and link against libdl using the -ldl flag. The -D_GNU_SOURCE flag is specified to satisfy #ifdef conditions that allow us to use the RTLD_NEXT enum. Optionally this flag can be replaced by adding “#define _GNU_SOURCE” somewhere near the top of our libexample.c file. After compiling our source files, we set the LD_PRELOAD environment variable to point to the location of our newly created shared library.
As expected, when our helloworld binary is executed the puts function is intercepted and “Goodbye, cruel world!” rather than the original “Hello world!” string is displayed. Now that we’re familiar with the process of hooking function calls let’s apply it towards a bit more practical example. Let’s pretend for a moment that we have an application that we are assessing and that this application uses OpenSSL to encrypt communications of sensitive data. Let’s also assume that attempts to man-in-the-middle these communications at the network level have been fruitless. To get at this sensitive data we will intercept calls to SSL_write, the function responsible for encrypting then sending data over a socket. Intercepting SSL_write will allow us to log the string sent to the function and pass the original parameters along, effectively bypassing the encryption protections while allowing the application to run normally. To get started let’s take a look at the SSL_write function definition:
int SSL_write(SSL *ssl, const void *buf, int num);
Here’s the code I’ve written to intercept SSL_write in hook.c:
As we can see our function definition needs to return an integer and take three arguments: a pointer to an SSL context, a pointer to a buffer containing the string to encrypt, and the number of bytes to write. In addition to our intercepting function definition we define a matching function pointer that will point to the originally intended SSL_write function and initialize it with the dlsym function. After pointing our pointer to the original function, we log the process ID of the process calling SSL_write, and the string sent to it. Next we compile our source to a shared library:
The only difference between this compilation and last is the -lssl flag, which we specify in order to link our code against the OpenSSL library. Now let’s go ahead and set LD_PRELOAD to point to our newly created libhook library:
Now that LD_PRELOAD is set we’re ready to start intercepting calls to SSL_write on processes executed from here onward. To test this let’s go ahead and use the curl utility over HTTPS and intercept the HTTPS request.
sigma@ubuntu:~/code$ curl https://www.netspi.com > /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed
100 19086 0 19086 0 0 37437 0 --:--:-- --:--:-- --:--:-- 60590
After successful completion of the command there should be a log file that we can examine:
As we can see the request has been logged in plaintext, while the application was allowed to function normally. Had this been a scenario where data integrity relied heavily upon SSL encryption and the assumption that man-in-the-middle attacks would be occurring only at the network level, any such integrity would have been compromised. These are really just a few examples of what’s possible using the dynamic loader API and LD_PRELOAD. Since the shared library we create will be loaded into the running process’ memory space we could do things like dump the memory of the process to examine the memory at runtime or tamper with runtime variables. Other uses for this method of function call hooking and loading generally fall under the use case of user-land rootkits and malware, which will be the focus on the next article in this series.
When assessing an application, one may run into files that have strange or unknown extensions or files not readily consumed by applications associated with those extensions. In these cases it can be helpful to look for tell-tale file format signatures and inferring how the application is using them based on these signatures, as well as how these formats may be abused to provoke undefined behavior within the application. To identify these common file format signatures one typically only need look as far as the first few bytes of the file in question. This is what’s often called “magic bytes”, a term referring to a block of arcane byte values used to designate a filetype in order for applications to be able to detect whether or not the file they plan to parse and consume is of the proper format. The easiest way to inspect the file in question will be to examine it with a hex editor. Personally for this task I prefer HxD for windows or hexdump under Linux, but really any hex editor should do just fine. With a few exceptions file format signatures are located at offset zero and generally occupy the first two to four bytes starting from the offset. Another notable detail is that these initial sequences of bytes are generally not chosen at random; that is most developers of a given format will choose a file signature whose ASCII representation will be fairly recognizable at a glance as well as unique to the format. This allows us to use the known ASCII representations of these signatures as a sort of mnemonic device to quickly identify a given file’s format. Here’s a few examples of common file signatures and their accompanying mnemonics:
0x4D 0x5A ... 0x50 0x45 0x00 0x00
Mach-O Executable (32 bit)
0xFE 0xED 0xFA 0xCE
Mach-O Executable (64 bit)
0xFE 0xED 0xFA 0xCF
0x7F 0x45 0x4C 0x46
0x50 0x4B 0x03 0x04
0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00
0x4F 0x67 0x67 0x53
0x45 0x1A 0xA3 0xDF
Image File Formats
0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
0x47 0x49 0x46 0x38 0x37 0x61
0x47 0x49 0x46 0x38 0x39 0x61
Let’s take what we’ve learned so far and apply it toward an “unknown” file, calc.exe.
To avoid confusion it’s worth noting that the PE32 executable format actually contains at minimum two sets of magic bytes: one set for the DOS executable header for DOS system compatibility and the other set to mark the beginning of the PE32 executable header. In this screenshot I’ve highlighted the DOS header, where we can see that the beginning of said header is marked with “MZ”. Another characteristic of the DOS header that’s an immediate give-away is the text “This program cannot be run in DOS mode.”, which some may recognize as the error text displayed when one attempts to run a windows application in DOS mode.
Following the DOS header and preceding the PE header is what’s known as the rich header and is represented in our mnemonic list as the “…” between the DOS and PE magic bytes. This header remains largely undocumented, however, so examining it at length is unlikely to yield any insightful information.
Finally, following the DOS and rich headers comes the PE header marked by “PE..”, or the byte sequence x50x45x00x00 which indicates that this file is a PE32 executable. Identifying other formats will follow the same principle, only one will generally only need the first step of the above process to identify the file format.
Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.
YouTube session cookie.
Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.
Analytics cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.
Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.
Unclassified cookies are cookies that we are in the process of classifying, together with the providers of individual cookies.
Cookies are small text files that can be used by websites to make a user's experience more efficient. The law states that we can store cookies on your device if they are strictly necessary for the operation of this site. For all other types of cookies we need your permission. This site uses different types of cookies. Some cookies are placed by third party services that appear on our pages.
Discover why security operations teams choose NetSPI.