Vulnerabilities and Weaknesses in J2EE Softwares | Lucideus Research

Introduction This post is the first part of a series of blogs wherein we’ll discuss the common weaknesses that are found in J2EE software.
Since security is a major concern for any organization and developers of any enterprise application that either handles data or talks to users via any communication protocol. Attackers use weaknesses/vulnerabilities in software that are introduced in the software when requirements are not implemented properly keeping security as the top priority. 
The below-listed weaknesses if mitigated properly during the development phase of any application would certainly help in reducing the risks and efforts later.

CWE-5: J2EE Misconfiguration: Data Transmission Without Encryption
Description: Encryption of information is a critical part of data transmission over a network which should not be ignored in any case of access controlled pages. The data sent without encryption can be read or modified by an attacker during transmission of data if it is sent in plaintext or is weakly encrypted.

Potential Mitigation: To avoid this problem, one can ensure the data encryption is done using standard security protocol SSL/TLS or an encryption mechanism of equivalent strength at the very least for access controlled pages. If the said is done properly, the application should make it impossible to view any access controlled pages without SSL. Below listed are three common ways SSL can be bypassed:
  • Explicitly mentioning HTTP instead of HTTPS while accessing a URL.
  • Attackers intentionally send a user to an insecure URL.
  • A programmer erroneously creates a relative link to a page in the application, which does not switch from HTTP to HTTPS. (This is particularly easy to do when the link moves between public and secured areas on a website.)
CWE-6: J2EE Misconfiguration: Insufficient Session-ID Length
Description: Session Hijacking is a common way of getting unauthorized access to information or web APIs of an application. The default configuration of J2EE application uses an insufficient session ID length. The session id of small length can be guessed and used to take over a user’s session. With increased session ID length, there would be a large number of possible session IDs which would be better mechanism if not a full proof method that will make the session id a little harder to be guessed.

Potential Mitigations:
  •  Session Id should be at the very least long enough to prevent brute force approach for guessing session ID. Session IDs of 128 bits long is good enough to prevent that.
  • Session id’s with enough bits in length to prevent brute force attack would be pointless if no standard encryption mechanism is in place to protect it from being sniffed.
  • There should be a lower bound on the number of valid session Ids to be used at any given point of time for active users which should include the users who abandon the session without logging out for which the application may use short-lived sessions. A lower bound on the number of valid session identifiers that are available to be guessed is the number of users that are active on a site at any given moment.
CWE-7: J2EE Misconfiguration: Missing Custom Error Page 
Description: The default error page of any application must not disclose any information which would make an attacker’s life a little easier. Nobody wants that. The default error pages that come with web application frameworks provide information such as stack trace of an error occurred during processing of a request. An application must define a custom error page for errors of HTTP error codes 4XX and 5XX and catch java exceptions to prevent the exposure of information about the system to attackers.
A stack trace may show an attacker a malformed SQL query, the type of user database and information about application container. The disclosed information enables an attacker to target known vulnerabilities in these components. While this might be helpful in a non-production environment, it must be avoided in production.

Example: In the below piece of code, an unchecked runtime exception thrown from within the try block may cause the application container to disclose full stack trace among other things.
Public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
try {
...
} catch (ApplicationSpecificException ase) {
logger.error("Caught: " + ase.toString());
}


Potential Mitigations :
  • Properly handle exceptions and errors in source code. 
  • Always define custom error pages with minimum information that must be disclosed in order to resolve the error either by the application user or developers. Handling HTTP error codes is always useful and user-friendly. A good configuration will also define an error handler that catches all exceptions possibly thrown by the application.
CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')
Description: This type of attack is executed when an application does not validate or correctly neutralize the user code prior using the input in an evaluation call. It gives an attacker to execute code or possibly run system commands with the same permissions as the web service.

Impacts :
  • Restricted data or files can be accessed.
  • Code injection attacks can lead to loss of data integrity.  
Potential Mitigations : 
  • Assume all input is malicious. Instead of validating the input against a list of bad code points known as the blacklist, validate the input using an “accepted known good” which is commonly referred to as whitelist of acceptable inputs. Reject anything that does not follow the specifications, or transform it into something that does.
  • When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values and conformance to business rules.
CWE-111: Direct Use of Unsafe JNI
Description: When a Java application uses the Java Native Interface (JNI) to call code written in another programming language, it can expose the application to weaknesses in that code, even if those weaknesses cannot occur in Java. Many safety features that programmers may take for granted simply do not apply for native code, so you must carefully review all such code for potential problems. The languages used to implement native code may be more susceptible to buffer overflows and other attacks. Native code is unprotected by the security features enforced by the runtime environment, such as strong typing and array bounds checking.
Potential Mitigations :
  • Implement error handling around the JNI call.
  • Do not use JNI calls if you don't trust the native library.
  • Be reluctant to use JNI calls. A Java API equivalent may exist.  
CWE-191: Integer Underflow (Wrap or Wraparound) 
Description: In java, overflow or underflow conditions never throw a runtime exception. The flowed output is predictable and reproducible. That is, its behaviour is the same every time you run the program. Arithmetic integer operations are performed in 32-bit precision. When the resultant value of an operation is larger than 32 bits than the low 32 bits only taken into consideration and the high order bits are discarded.
Impacts:
  • In the case of overflows involving loop index variables, the likelihood of infinite loops is also high which might result in an application crash or denial or service.
  • An integer overflow often passes undetected by the affected application. Because of this, the condition may lead to a security breach through a buffer overflow or other malicious code.
  • When calculating a purchase order total, an integer overflow could allow the total to shift from a positive value to a negative one. This would, in effect, give money to the customer in addition to their purchases, when the transaction is completed. 
CWE-192: Integer Coercion Error 
Description: Type coercion is a way of changing an entity of a data type into another. Several flaws fall under the category of integer coercion errors. For the most part, these errors in and of themselves result only in availability and data integrity issues. However, in some circumstances, they may result in other, more complicated security-related flaws, such as buffer overflow conditions.
Impacts:
  • Integer coercion often leads to undefined states of execution resulting in infinite loops or crashes.
  • Integer coercion errors may lead to exploitable buffer overflow condition.
  • It may result in an incorrect value being stored for a variable. 
Potential Mitigations:
  • Design objects in such a way that does not require multiple or complex casts.
  • Understand the data type casting that must be done in order to achieve a certain functionality. 
CWE-197: Numeric Truncation Error
Description: Truncation errors occur when a primitive is cast to a primitive of a smaller size and data is lost in the conversion resulting in the loss of the high order bits of the large value, potentially resulting in an unexpected value which may be utilized as an index for loop or buffer. In this scenario, the value cannot be used since it may lead the application to an undefined state. 
 
Potential Mitigation: Do no use anycast implicit or explicit moving from a larger size primitive to a smaller size primitive or use check before doing so.

In the following Java example, before calling the method updateSalesCount, the integer values are converted to the primitive type short since the method requires a short type for the method arguments.
Example
// update sales database for number of product sold with product ID
public void updateSalesForProduct(String productID, int amountSold) {

// get the total number of products in inventory database
int productCount = inventory.getProductCount(productID);
// convert integer values to short, the method for the

// sales object requires the parameters to be of type short
short count = (short) productCount;
short sold = (short) amountSold;
// update sales database for product
sales.updateSalesCount(productID, count, sold);
}
CWE-245: J2EE Bad Practices: Direct Management of Connections
Description: Direct management of connections is restricted by the J2EE standards. Applications must use the container’s connection management facilities to obtain the connections to resources or APIs. Duplicating the functionality in an application would be difficult and error-prone. 
Example: The following example code opens and manages a connection to a database for a J2EE application. The method openDBConnection opens a connection to the database using a DriverManager.

public class DatabaseConnection {
private static final String DB_URL = "jdbc:mysql://localhost:3306/applicationdb";
private Connection connection = null;

public void openDBConnection() {
try {
connection = DriverManager.getConnection(DB_URL);
} catch (SQLException ex) {...}
}
// Member functions for retrieving database connection and accessing database
...
}
The following example shows the usage of container’s resource management facilities to open a connection to database.

public class DatabaseConnection {
private static final String DATASOURCE_REF = "jdbc:mysql://localhost:3306/applicationdb";
private Connection connection = null;

public void openDatabaseConnection() {
try {
InitialContext context = new InitialContext();
DataSource ds = (DataSource) context.lookup(DATASOURCE_REF);
connection = ds.getConnection();
} catch (NamingException ex) {...}
} catch (SQLException ex) {...}
}
// Member functions for retrieving database connection and accessing database
...
}
CWE-246: J2EE Bad Practices: Direct Use of Sockets
Description : The J2EE application must not implement the direct use of sockets instead of using framework method calls. The use of socket is permitted only for the purpose of communication with legacy systems where the standard higher level protocol for the communication is unavailable. Developing a custom communication protocol is difficult and error prone which may result in multiple security issues.
Example: The following example opens a socket to connect to a remote server.

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// Perform servlet tasks.
...
// Open a socket to a remote server (bad).

try {
Socket socket = new Socket(remoteHostname, 3000);
// Do something with the socket.
...
} catch (Exception e) {
...
}
}

The above example is a dangerous way of directly opening a Socket object within the servlet.
Potential Mitigation: Use the framework method calls instead of using sockets directly. 

CWE-248: Uncaught Exception
Description: An uncaught exception may cause the application to crash or disclose critical data/information about the system or application such as a malformed SQL query.

Impact: An uncaught exception could get the application in an undefined state leading to a crash, exposure of critical information.
Example: The following example attempts to resolve a hostname.

protected void doPost (HttpServletRequest req, HttpServletResponse res) throws IOException {
String ip = req.getRemoteAddr();
InetAddress addr = InetAddress.getByName(ip);
...
out.println("hello " + addr.getHostName());
}
The servlet method should not be throwing any exception. Implementing a custom error page or a response filter would be helpful in tackling the problem.
CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')
Description: A race condition is design error that occurs when two or more threads can access the shared resource and try to change it concurrently which may result in an undefined state of the shared resource. This happens due to the unknown order of threads execution defined by platform-dependent thread scheduling algorithm. Problems often occur when threads use the “check then act” strategy which checks for a particular state of an object and then defines the next action based on the previous value. 
A race condition occurs within concurrent environments and is eventually a property of code which, depending on the context, may be a function call, a set of operations, a series of program invocations, etc.
A race condition defines the following properties :

  • Mutual Exclusion - A property of concurrency control which requires that two or more threads can not enter its respective critical section controlling the modification of shared resource at the same point in time. The same thing can happen if two concurrent threads are supposed to just read the contents of the shared resource.
  • Atomicity - Set of actions is atomic if they all execute as a single operation, in an indivisible manner. If two or more threads perform several steps on the shared resource, they may overlap.
Impacts:
  • Resource exhaustion is one of the things that race condition can cause.
  • Since a race condition lets multiple code sequences to access a shared resource simultaneously, it may lead to undefined states, possibly resulting in a crash. 
CWE-365: Race Condition in Switch
Description: If the variable used to execute a set of instructions inside switch cases is changed while the switch execution is still in progress, it may lead to out of sync undefined activity or in some cases could even result in memory corruption. This issue is particularly important in the case of switch statements that involve fall-through style case statements - ie., those which do not end with a break.

Impact: This weakness may lead to an unexpected system state, resulting in unpredictable behaviour.

Potential Mitigation: It is important for variables subjected to race conditions should be locked before the switch statement starts and only unlocked after the statement ends.

CWE-374: Passing Mutable Objects to an Untrusted Method
Description: Sending non-cloned mutable data as an argument may result in that data being altered or deleted by the called function, thereby putting the calling function into an undefined state. If this data was not previously cloned, the modified data might not be valid in the context of execution.

Impact: Possible tampering of Information/data.

In the following example, the bookstore class manages the sale of books in a bookstore. The method retrieves a Book object from the inventory using the ISBN number, then calls a method of sales object to update the sales information and then updates inventory.

Example :
public class BookStore {
private Inventory inventory;
private Sales sales;
...
// constructor for BookStore
public BookStore() {
this.inventory = new Inventory();
this.sales = new Sales();
...
}
public void updateSalesAndInventoryForBookSold(String isbn) {
// Get book object from inventory using ISBN
Book book = inventory.getBookWithISBN(isbn);
// update sales information for book sold
sales.updateSalesInformation(book);
// update inventory
inventory.updateInventory(book);
}
// other BookStore methods
...
}
public class Book {
private String title;
private String author;
private String isbn;
// Book object constructors and get/set methods
...
}


Potential Mitigations:

  • Pass in the constant or immutable object of data to untrusted functions.
  • The preferred way would be cloning a mutable data object before passing it to an external function.
CWE-375: Returning a Mutable Object to an Untrusted Caller
Description: Sending non-cloned mutable data as a return value may result in that data being altered or deleted by the calling function. In cases where methods return references to a mutable object, it is possible that the caller function makes changes to the object returned from the current method. If this object was not cloned previously, the class will then be using modified data which may alter its internal state.

This class has a private list of patients, but provides a way to see the list :

public class ClinicalTrial {
private PatientClass[] patientList = new PatientClass[50];
public getPatients(...){
return patientList;
}
}
 
While the above piece of code is supposed to provide read access to the patient list, the getPatients() method returns a reference to the original patient list instead of a copy of the list. Any caller of this method can modify the patient list even though it is a private member of the class.

Potential Mitigations :

  • Return constant or immutable data to preserve the integrity of the data.
  • The preferred way of doing this would be to clone all mutable data before returning it. 
Conclusion
These weaknesses are pretty common to be noticed in the software. Since the security of the application and its data is of prime concern for any organisation, every developer should keep these weaknesses at bay for a less vulnerable software. The next part of this series will soon be published. 

References

Post a Comment

0 Comments