How to intercept and modify Java stacktraces

This ticket was triggered by a “simple” requirement: “Change all the package names in the logs of a Java application (especially the stacktraces) from ‘abc.efg’ (put here whatever you want as name) to ‘hij.klm’ (put here also whatever you want as name) “. The first idea that popped in my mind was to change the packages names at the code level, but this was not feasible because of (rather) big codebase, the use of the (Java) reflexion and the tight timeline.

In the following lines, I will discuss possible solutions to implement this (weird) requirement.

 

Extend the log4j ThrowableRenderer

If the project is using log4j1x as log library, then a solution would be to create your own throwable renderer by extending the org.apache.log4j.spi.ThrowableRenderer. The (log4j) renderers are used to render instances of java.lang.Throwable (exceptions and errors) into a string representation.

The custom renderer that replaces the packages starting with “org.github.cituadrian” by “xxx.yyy” will look like this:

package org.github.cituadrian.stacktraceinterceptor.log4j;

import org.apache.log4j.DefaultThrowableRenderer;
import org.apache.log4j.spi.ThrowableRenderer;

public class CustomThrowableRenderer implements ThrowableRenderer {
    private final DefaultThrowableRenderer defaultRenderer =  
                   new DefaultThrowableRenderer(); 
    
    @Override 
    public String[] doRender(Throwable t) {
      String[] initialResult = defaultRenderer.doRender(t); 
      for (int i = 0; i < initialResult.length; i++) { 
        String line = initialResult[i]; 
        if (line.contains("org.github.cituadrian")) { 
           initialResult[i] = line.replaceAll("org.github.cituadrian", "xxx.yyy"); 
        } 
       } 
      return initialResult; 
    }
}

Basically, the custom renderer is delegating the task of creating a String from a Throwable to a DefaultThrowableRenderer and then it checks and replace the desired package names.

In order to be used, the renderer should be defined in the log4j.xml file:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true"
    xmlns:log4j='http://jakarta.apache.org/log4j/'>
 
  <throwableRenderer class= 
     "org.github.cituadrian.stacktraceinterceptor.log4j.CustomThrowableRenderer"/>
...

Use a log4j2 pattern layout

If your project is using log4j2 as logging library, then you can use a (log4j2) layout pattern.  The layout pattern will look like:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
 <Appenders>
 <Console name="STDOUT" target="SYSTEM_OUT">
 <PatternLayout pattern=
  "%replace{%class %log %msg %ex}{org\.github\.cituadrian}{xxx\.yyy}"/>
 </Console>
...

 

Modify (a.k.a. Weaving) the java.lang.StackTraceElement class with AOP

Before even explaining what it really means, I have to warn you that weaving JDK classes is rarely necessary (and usually a bad idea) even if it’s possible using an AOP framework like AspectJ.

For this case I used the AspectJ as AOP framwork because the weaver (aop compiler) is able to do binary weaving, meaning the weaver takes classes and aspects in .class form and weaves them together to produce binary-compatible .class files that run in any Java VM. The command line to obtain a weaved jar is the following one:

ajc -inpath rt.jar Aspect.java -outjar weavedrt.jar

In the case of weaving JDK classes one extra step is necessary in order to make the application work; we must create a new version of the rt.jar file  or create just a small JAR file with the JDK woven classes which then must be appended to the boot-classpath of the JDK/JRE when firing up the target application. The command line to execute the target application is the following one:

java -Xbootclasspath/<path to weavedrt.jar>;<path to aspectjrt.jar> TargetApplication

If you don’t want to worry about all the technical details of weaving and executing the application and you are using Maven then you can use the (marvelous) SO_AJ_MavenWeaveJDK project from gitHub (that handles everything using Maven)

The aspect that will modify the stacktrace packages looks like:

package org.github.cituadrian.stacktraceinterceptor.app;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspectpublic class StackTraceInterceptorAspect {
    @Pointcut("execution(String java.lang.StackTraceElement.getClassName()) "
            + "&& !within(StackTraceInterceptorAspect)")     
    public void executeStackTraceElementGetClassNamePointcut() {}        
    
    @Around("executeStackTraceElementGetClassNamePointcut()")    
    public Object executeStackTraceElementGetClassNameAdvice(    
                   final ProceedingJoinPoint pjp) throws Throwable {        
        Object initialResponse =  pjp.proceed();         
        if (initialResponse instanceof String 
               && ((String)initialResponse).startsWith("org.github.cituadrian")) {     
                 return ((String)initialResponse).replaceFirst("org.github.cituadrian", "xxx.zzz"); 
        }        
        return initialResponse;    
    }
 }

In a nutshell, the StackTraceInterceptorAspect will intercept all the calls to the java.lang.StackTraceElement#getClassName method and it will change the returned result of the method if the class name contains the string “org.github.cituadrian”.

If you are interested to learn more about AspectJ I really recommend you to buy a copy of the AspectJ in action (second edition) book.

 

Modify and shadow the java.lang.StackTraceElement class

 Using AOP just to intercept and modify a single method of a single class is a little bit over-killing. In this case there is another solution; the solution would be create a custom version of the java.lang.StackTraceElement class and add this custom class in the boot-classpath of the JDK/JRE when firing up the target application, so the initial version will be shadowed by the custom version.

An implementation of StacktraceElement class can be found here. So you can modify by hand the java.lang.StackTraceElement#getClassName method or the java.lang.StackTraceElement#toString method.

 To execute the target application, you must create a jar with the modified class and add it into the boot-classpath (something similar to the AspectJ solution):

java -Xbootclasspath/<path to custom class.jar> TargetApplication

 

 

(My) CSSLP Notes – Secure Software Design

Note: This notes were strongly inspired by the following books: CSSLP Certification All in one and Official (ISC)2 Guide to the CSSLP CBK, Second Edition

Design Process

Attack Surface Evaluation

A software or application’s attack surface is the measure of its exposure of CSSLP-logobeing exploited by a threat agent i.e., weaknesses in its entry and exit points that a malicious attacker can exploit to his or her advantage.
The attack surface evaluation attempts to enumerate the list of features that
an attacker will try to exploit.

Threat Modeling

Threat modeling is the process used to identify and document all the threats to  system.

The threat modeling process have 3 phases:

  1. model the system for which you want to find the threats.
  2. find the threats.
    1. STRIDE model.
    2. attack trees – An attack tree is a hierarchical tree-like structure, which has either an attacker’s objective (e.g., gain administrative level privilege, determine application makeup and configuration, bypass authentication mechanisms, etc.) or a type of attack
      (e.g., buffer overflow, cross site scripting, etc.) at its root node.
  3. address each threat found in the previous step. Once identified,each threat must be evaluated according to the risk attached to it. There are several ways to quantitatively or qualitatively determine the risk ranking for a threat. These range from the simple, non-scientific, Delphi heuristic methodology to more statistically sound risk ranking using the probability of impact and the business impact.
  4. document and validate.

More details about threat modeling can be found here : Threat Modeling for mere mortals and (My) OWASP BeNeLux Days 2016 Notes – Training Day.

Design Considerations

This part is linked to the Secure Software Concepts and contains how the security software concepts can be applied to have a secured application.

  • confidentiality – use cryptographic and masking techniques
  • integrity – use hashing (or hash functions), referential integrity design (uses primary keys and related foreign keys in the database to assure data integrity), resource locking (when two concurrent operations are not allowed on the same object (say a record in the database), because one of the operations locks that record from allowing any changes to it, until it completes its operation, it is referred to as resource locking), and code signing.
  • availability – replication, fail-over and scalability techniques can be used to design the software for availability.
  • authentication – use multi-factor authentication and single sign on (SSO). Rely of already existing mechanism if possible (like the ones offered by the operating system).
  • authorization – rely of already existing mechanism if possible.
  • accounting (audit) – determine of what elements should be logged and under what circumstances.
Some of the common, insecure design issues observed in software are the
following:
  • improper implementation of least privilege
  • software fails insecurely
  • authentication mechanisms are easily bypassed
  • security through obscurity
  • improper error handling
  • weak input validation

Architecture system with secured design principles:

  • good enough security – care should be taken to ensure that the security elements are in response with the actual risk associated with the potential vulnerability; do not over-engineer.
  • least privilege – use of accounts with non-administrative abilities.
    Modular programming is a software design technique in which the entire program is broken down into smaller sub-units or modules. Each module is discrete with unitary functionality and is said to be therefore cohesive, meaning each module is designed to perform one and only one logical operation.
  • separation of duties – the programmer should not be allowed to review his own code nor should a programmer have access to deploy code to the production environment.
  • defense in depth
    • use of input validation along with prepared statements or stored
      procedures, disallowing dynamic query constructions using user
      input to defend against injection attacks.
    • disallowing active scripting in conjunction with output encoding
      and input- or request-validation to defend against Cross-Site
      Scripting (XSS).
  • fail safe
    • the user is denied access by default and the account is locked out after the maximum number (clipping level) of access attempts is tried.
    • errors and exceptions are explicitly handled and the error messages are non-verbose in nature.
    •  not designing the software to ignore the error and resume next
      operation
  • economy of mechanism – trade-off that happens between the
    usability of the software and the security features that need to be designed and built in.
    • Unnecessary functionality or unneeded security mechanisms should be avoided.
    • Strive for simplicity.
    • Strive for operational ease of use.
  • complete mediation
  • open design – the inverse of the open design principle is security through obscurity, which means that the software employs protection mechanisms whose strength is dependent on the obscurity of the design.
  • least common mechanism – mechanisms common to more than one user or process are designed not to be shared. Design should compartmentalize or isolate the code (functions) by user roles, since this increases the security of the software by limiting the exposure.
  • psychological acceptance – security principle that states that security mechanisms should be designed to maximize usage, adoption, and automatic application.The security protection mechanisms:
    • are easy to use,
    • do not affect accessibility.
    • are transparent to the user.
  • weakest link – when designing software, careful attention must be
    given so that there are no exploitable components.
  • leverage existing components – reusing tested and proven, existing libraries and common components has good security benefits.

Securing commonly used architectures

  • mainframe architecture
  • distributed architecture
    • client/server
    • p2p
  • service oriented architecture
    • An ESB is a software architectural pattern that facilitates communication between mutually interacting software application.
    • web-services
      • SOAP
      • REST
  • rich internet aplications (RIA)

Service models:

  • Infrastructure as a Service (IaaS)  -infrastructural components such as networking equipment, storage, servers and virtual machines are provided as services and managed by the cloud service provider.
  • Platform as a Service (PaaS) -in addition to infrastructural components, platform components such as operating systems, middleware and runtime are also provided as services and managed by the cloud service provider.
  • Software as a Service (SaaS) – in addition to infrastructural and platform components, data hosting and software applications are provided as services and managed by the cloud service provider.

Digital Rights Management

The expression of rights is made possible by formal language, known as Rights Expression Language (REL). Some examples of REL include the following:
  • Open Digital Rights Language (ODRL)  – A generalized, open standard under development that expresses rights using XML.
  •  eXtensible rights Markup Language (XrML) – Another generalized REL that is more abstract than ODRL. XrML is more of a meta-language that can be used for developing other RELs.
  • Publishing Requirements for Industry Standard Metadata
    (PRISM) – Unlike ODRL and XrML, PRISM can be used to express
    rights specific to a task and is used for syndication of print media
    content such as newspapers and magazine.

Trusted computing:

  • Trusted Platform Module (TPM) – specification used in personal computers and other systems to ensure protection against disclosure of sensitive or private information as well as the implementation of the specification itself.
  • Trusted Computing Base (TCB) – the set of all hardware, firmware and software components that are critical to its security.