Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze the rendering principle and potential trouble of template engine in Nodejs

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)05/31 Report--

How to analyze the Nodejs template engine rendering principle and potential hidden trouble discussion, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain in detail for you, people with this need can come to learn, hope you can gain something.

I. background

Previously, when members of the laboratory combed the nodejs prototype chain contamination vulnerability, they found that the prototype chain contamination vulnerability could be combined with the rendering of the template engine to achieve the effect of remote command execution. Why can prototype chain contamination be combined with template engine to achieve this effect? How exactly does the template engine work? Apart from prototype chain contamination, is there any other way to achieve the same effect? With this doubt, the lab members decided to explore the inherent mechanism of the nodejs template engine.

II. Current situation of Nodejs template engine

At present, there are many template engines in the JavaScript ecosystem, and the implementation principles and characteristics of each engine are different, so it is difficult to find a template engine with rich functions, simple writing and common front and back ends, so some developers have set up a website garann.github.io/template-chooser that selects different engines according to different needs.

According to the official data of npm, the current popular template engine downloads are shown in figure 2. According to the existing server engine framework, Wuheng Lab selected several template engines commonly used by developers for analysis, and found that the rendering principles of template engines are basically the same. it's just that some details are not handled in the same way.

Third, the use of the engine

Most template rendering engines basically provide a render function when working. This function generally requires at least two parameters. One is the rendered template template, which is usually a string, and the other is the data data to be rendered into the template, which is passed in the form of object (that is, key-value pairs). Render functions generally have the following form:

Take the following ejs rendering code as an example. ". / views/index.ejs" is the location of the template file, and {message: 'test'} is the data to be rendered into the template:

Among them, the contents of the ". / views/test.ejs" file are as follows:

The rendering result is as follows. Render the value test corresponding to message into the template.

In the render function, in addition to these two parameters, most template engines also provide a parameter options that can control the rendering characteristics, which are used to control the rendering characteristics of the template, such as whether debugging is enabled, whether caching is enabled, and whether error messages are printed. Take ejs as an example, you can open debugging statements by setting compileDebug.

Fourth, engine rendering mechanism

Basically the rendering mechanism of all template engines consists of two steps:

Step 1: locate and segment according to the template data, and find the data to be replaced according to the special symbols defined by each template.

Step 2: replace and splice the values according to the key value pairs and positioning results provided, and finally get the rendering result.

In this analysis, Wuheng Lab mainly describes the rendering mechanism of Mustache and ejs engines, and the rendering process of other engines is roughly the same.

4.1 location

Most rendering engines specify that the data to be replaced must be wrapped in certain symbols. For example, the default symbol for Mustache is {{}}, while the default symbol for ejs is, so when the engine works, it should first find these special symbols in the incoming template string. Different engines implement this step differently. For example, Mustache,Nunjucks scans the template string through lexical parsing, that is, character scanning, to locate the location of special symbols. Take the rendering code of the following Mustache as an example:

The corresponding location code for Mustache is as follows, where scanner is responsible for scanning the template string, and the result of the scan is that it will be stored in multiple token objects.

Finally, each scanned token will be classified and the corresponding value and location will be stored, and put into the tokens for subsequent use.

The scan result of the sample template string "whoareyou {{title}}" is shown below. "whoareyou" will be marked as text type, that is, plain text, and "titile" will be marked as name type, which is the data to be replaced.

On the other hand, ejs circularly locates the position of special symbols by means of regular matching. Finally, the template engine can find the original data to be replaced according to the location of the special symbol, taking the following template string as an example:

Ejs calls the parsteTemplateText function to locate, where this.templateText is the original template string, this.regex is the specified special symbol (that is, the''specified by ejs, etc.), and then the original template string is divided by continuous matching through the while loop.

The final result of splitting the sample template string is shown below, and you can see that the data message to be replaced has been located.

4.2 replacement

After locating the original data to be replaced, the template engine begins to replace the original data according to the input. In this step, different engines are replaced in different ways. The replacement of Mustache (including Handlebars) is very simple and crude, that is, direct replacement, and the code is as follows:

As shown above, if it is detected that token is of type name, that is, the type to be replaced, the escapedValue function is called, where lookup is called and the corresponding value is found based on token.

Context.lookup will eventually find the value of the corresponding title, that is, joe.

The replacement process of Pug, Nunjucks, ejs and other engines is a little troublesome. In the render function of ejs, the handleCache function is called first.

Pulling out the following code, you can see that the result of handleCache (opts, template) is an anonymous function.

Result is the final rendering result, which means that the specific rendering process of ejs is controlled by functions, which will eventually be equivalent to:

In the handleCache function, the anonymous function is compiled by compile.

Following up on the compile function, you can see that the Function function constructor is finally called to dynamically construct an anonymous function.

Take the above ejs sample code as an example. The anonymous function constructed by the rendering engine is shown below. The template data to be rendered is obtained through _ _ append. Here, escapeFn is also used to prevent xss. You can see the processing process of ejs. In fact, it is also the process of stitching based on the result of positioning and segmentation.

5. Remote code execution vulnerability of ejs template engine (CVE-2020-35772)

From the above analysis, we can see that the rendering of ejs is realized through * * "dynamically generate function code-> generate anonymous function-> call anonymous function" (in fact, many engines such as Nunjucks, pug, etc.), and this method is quite dangerous, once there is a user-controllable part of the dynamically generated function code. A malicious user can insert malicious code into an anonymous function and be executed by the rendering engine, resulting in a remote code execution vulnerability. The title of GYCTF2020 Ez_Express before is to take advantage of this feature of ejs.

The principle of its use is as follows: when rendering, ejs will obtain some values of the rendering options and splice them into the function code, including the value of dynamic splicing outputFunctionName into the function code, but the author of ejs has prohibited users from manipulating outputFunctionName in the rendering options from the very beginning, as well as other options. Since it cannot be manipulated from the front, you can use the javascript prototype chain contamination to inject the outputFunctionName attribute into the Object to manipulate.

5.1 pollution of rendering options

Can malicious code be injected without prototype chain contamination?

Let's take a look at the entry function render for ejs rendering, which accepts three parameters, the first is template, the template string, the second is data, which is generally user-controllable data, and the third is the rendering option opts, which controls the generation of anonymous function codes.

In order to maintain availability, ejs allows the second parameter (usually a user-controllable parameter) to use some rendering options, but these options are limited and are limited by the utils.shallowCopyFromList (opts, data, _ OPTS_PASSABLE_WITH_DATA) function in the code. "_ OPTS_PASSABLE_WITH_DATA" specifies a controllable range as follows:

Eventually, these rendering options will enter the compile function, that is, anonymous function code generation, that is, if the user data can control the incoming data options, then malicious users can pollute "delimiter", "scope", "context", "debug", "compileDebug", "client", "_ with", "rmWhitespace", "strict", "filename" and "async" in opts.

5.2 Generation of anonymous function code

So what impact can these polluting options have on the generation of anonymous function code? Continuing to follow up the rendering engine, it is found that the anonymous function code is composed of three parts in the process of dynamic generation, namely, prepended, this.source and append, and the code of these three parts is affected by the rendering options.

Step by step to follow up the generation of anonymous function code, first of all, you can see that several rendering options such as outputFunctionName, localsName, destructuredLocals can affect the generation of prepended code, but these options are not within the scope of control.

Continue to follow up the process of generating the function code, as shown below. CompileDebug is within the controllable range, and filename is also within the controllable range, but filename is converted by JSON.stringify and cannot escape, so it cannot contaminate the function code.

To follow up, we can see that in the following code, both compileDebug and filename are controllable, and finally filename is directly concatenated into the code of anonymous functions, so here you can inject malicious code by controlling the filename option.

5.3 injection of malicious code

Write the following code directly to see what the source code of the resulting anonymous function looks like.

As shown below, you can see that / etc/passwd is finally inserted into a comment line:

At this point, modify the payload to add the newline escape comment.

The result is as follows, you can see that the comment has been successfully escaped through the newline character, but for such a function, when the normal flow goes to the try code block, unless there is an exception, otherwise, the execution will be finished directly to the return code, and it will not come to the code we injected at all. Unless you can trigger the exception to continue, but if you look at the code in the try block, there is basically no logic to trigger the exception.

5.4 finally Escape try catch limit

In common programming languages, try catch can actually add another finally. No matter what the final try cath is, it will go to the code block in finally, but in the case of return, can you still execute finally? the paylaod is as follows:

Take a look at the resulting code:

After a try, you can still get to the finally code block, and the injected code is finally executed by the engine.

In fact, among the engines that use dynamic generation functions for rendering, in addition to ejs, pug,nunjuck and so on, do other engines have the same problem? Wuheng Lab also analyzed other template engines and found that although other engines also use this way to render, but better control the impact of rendering options on the function code.

5.5 affect version

In the actual testing process, it is found that this vulnerability does not exist on the older version of ejs, and the range of affected versions is:

2.7.2 (including 2.7.2) to latest version (3.1.5).

5.6 difficulty of utilization

The above vulnerability actually requires the server code to render user-controllable json data directly, including key and value. For a normal server-side framework, framework middleware can convert incoming json data into json objects, and relatively few developers will directly render the entire json data, but there is also the use of research and development to directly use JSON.parse to parse user data for template rendering.

5.7 interim remediation programme

After discovering the vulnerability, Wuheng Labs contacted the author of ejs in time, and the author of ejs explained that this actually belongs to the characteristics of the template engine itself, that user-controllable json data should not be passed in directly for use, and there is no plan to provide a repair version for the time being.

Wuheng Labs disclosed the problem, hoping to help the affected business avoid potential security risks. If the wired business code meets the trigger conditions of the vulnerability, as there is currently no fixed version of ejs, Wuheng Lab provides the following temporary fixes:

If there is no version requirement for ○, it is recommended that you use a version that is not within the scope of influence, such as 2.7.1.

If ○ is within the scope of the affected version, it is recommended that you do not directly obtain the user's json data for template rendering.

If ○ needs to obtain user data directly for template rendering, make sure that there are no compileDebug and filename options in the user data or that the values of these options are uncontrollable. The following security detection function can be used.

Is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report