nginx njs is a JavaScript interpreter for nginx that lets you use server-side scripting. In March 2022, CVE-2022-27007 was disclosed, revealing a Use-After-Free vulnerability in njs_function_frame_alloc() when invoking from a frame restored with njs_function_frame_save(). This flaw affects njs version .7.2 and could let attackers trigger a crash or even execute code, depending on the exploitation technique.

Let’s walk through the technical details, show code, and see how attackers could use this CVE in the wild.

1. What Causes CVE-2022-27007?

Summary:  
When njs evaluates JavaScript using stack frames, it sometimes calls njs_function_frame_save() to store the current execution context, and later calls njs_function_frame_alloc() to resume. In njs .7.2, a bug causes the saved frame to be freed twice, leading to a Use-After-Free condition.

2. Where’s the Bug in Code?

The vulnerable code is in njs_function.c (commit before the fix). Here is a simplified version:

njs_function_frame_t *
njs_function_frame_alloc(njs_vm_t *vm, size_t size)
{
    njs_function_frame_t  *frame;

    frame = njs_mp_alloc(vm->mem_pool, size);
    if (frame == NULL) {
        return NULL;
    }

    // ... initialization logic ...

    return frame;
}

njs_function_frame_t *
njs_function_frame_save(njs_vm_t *vm)
{
    // Saves the current frame
    njs_function_frame_t *saved = vm->current_frame;

    // The bug: the saved frame can be freed too early
    njs_mp_free(vm->mem_pool, saved);

    return saved;
}

When njs_function_frame_alloc() tries to use a frame saved by njs_function_frame_save(), the memory may already be freed, causing a use-after-free.

3. How Is This Vulnerable in Practice?

Attackers can exploit this by supplying malicious JavaScript to njs, especially if user input is evaluated by the njs engine — either via the HTTP njs module or embedded scripts in nginx configurations.

Example Scenario

Suppose you have an nginx config with js_content or js_include directives that take untrusted input. An attacker might trigger the save-then-resume behavior and hit the bug.

Minimal Proof of Concept in JavaScript

Here’s a sample code crafted to trigger the use-after-free (pseudocode; real exploit may need internal knowledge):

function uaf_victim() {
    // Cause function frame to be saved
    save_current_frame();

    // Some logic to manipulate frame references

    // Possibly trigger an allocation to reuse freed memory
}

function save_current_frame() {
    // In njs, triggers njs_function_frame_save()
}

uaf_victim();

A clever attacker might spray controlled data after abusing save_current_frame(), increasing the chances their data lands in the freed slot. At this point, when the engine resumes execution, it reads attacker-controlled memory.

Denial of Service: Most likely, nginx will crash, denying service to users.

- Remote Code Execution: If the attacker can carefully control memory layout (requires skills and proper context), arbitrary code execution could happen.

5. References and Patch

- NVD Description for CVE-2022-27007
- Upstream Issue Reference
- nginx njs Security Advisory
- Patch Commit

Avoid evaluating untrusted scripts with njs.

- Apply defense-in-depth: chroot, containerization, SELinux/AppArmor, etc.

7. TL;DR and Final Words

CVE-2022-27007 is a dangerous Use-After-Free in nginx njs (up to .7.2) stemming from a bug in function frame management. If your nginx is running njs and lets users supply JavaScript, patch immediately. Proof-of-concept code is possible, so real-world attacks are feasible.

Timeline

Published on: 04/14/2022 15:15:00 UTC
Last modified on: 05/19/2022 20:15:00 UTC