290 words
1 minute
Template Injection in a Report System

This vulnerability was described publicly as an SQL injection, but in reality it is a template injection.

The product contains a class named TemplateUtils, which is responsible for expression handling.

User-supplied expressions are rendered via the render method:

image-20240723224905849

The template-parameter rendering method is renderParameter4Tpl, which keeps calling another render method. At the beginning it creates a new Calculator:

image-20240723225237830

In the middle, evalRenderAction specifies a RenderAction. The other details are not that important; keep following the flow:

image-20240723232033511

In the final renderTpl, we see the expression parsing logic: it parses via ParameterProvider.PARAMETERPATTERN and extracts the content inside the expression:

image-20240723225516954

image-20240723225420954

After parsing, it extracts the content inside ${} and passes it into the RenderAction (the one passed earlier) for rendering:

image-20240723230757983

image-20240723230545758

So var2.render is actually the earlier evalRenderAction:

image-20240723231732934

Going deeper, it reaches evalString. Here it converts the input into an Expression:

image-20240723233046321

Following this eval, we hit another eval — this is on the earlier Calculator:

image-20240723233646180

Then it calls the next eval, which is the expression’s own eval. In practice this is RelationExpression, whose parent is BinaryExpression:

image-20240724002636625

Next it evaluates another Node. This eval uses an index-based check: it actually retrieves the content on the right side of =. If it’s a function call, it becomes FunctionCall:

image-20240724002924552

FunctionCall.eval calls resolveMethod, which is the key part of this vulnerability:

image-20240724003555779

resolveMethod eventually resolves a namespace and calls getMethod:

image-20240724003707923

image-20240724003842295

image-20240724003943915

At the function-dispatch point (where the vulnerability happens), the implementation of getMethod in DefaultNameSpace uses attacker-controlled input to load classes:

In practice it calls into com.fr.function.xxx. That package contains many functions. After resolving it, the engine continues to evaluate the expression via evalExpression:

image-20240724005229688

image-20240724005158851

Some reports online call this “SQL injection.” If you look at the SQL class… it’s pretty self-explanatory:

image-20240724005341582

image-20240724005009251

Entry point: image-20240724013008799

In practice this has a lot of flexibility; you don’t necessarily have to use SQL-style payloads for PoC/detection.

Now included in the “premium detection package”:

Template Injection in a Report System
https://springkill.github.io/en/posts/某报表模板注入/
Author
SpringKill
Published at
2024-07-23
License
CC BY-NC-SA 4.0