The Ladybird browser engine, a relatively new entrant originating from the SerenityOS project, has been found to contain a critical security vulnerability. Security researcher Jess has identified a use-after-free (UAF) vulnerability in Ladybirdβs JavaScript engine, LibJS, tracked as CVE-2025-47154 with a CVSS score of 9.1.
The vulnerability lies within LibJS, specifically in the handling of the interpreterβs argument buffer. According to the researcher, it is triggered by βusing a proxied function object as a constructor, together with a malicious [[Get]] handler.β To understand the mechanics of this vulnerability, consider the basic JavaScript constructor:
function Construct() {}new Construct();
The execution of a constructor involves several steps. In Ladybird, these steps include:
Taking a reference to an argument buffer (arguments_list).Creating a new object with the same prototype as the constructor function.Executing the constructor with the arguments in arguments_list.The core issue arises if the vector referenced by arguments_list is freed between steps 1 and 3. In this scenario, arguments_list becomes a dangling pointer, and its subsequent use leads to a use-after-free error.
The ordinary_create_from_constructor function, called during the constructorβs execution, is of particular interest. While seemingly simple, this method can have unintended consequences when the constructor is a proxy object.
βIf we override the constructor functionβs [[Get]] internal method,β the researcher writes, βthe call to get_prototype_from_constructor can execute arbitrary JavaScript codeβ. This capability to execute arbitrary code becomes dangerous if it can be used to free the argument buffer.
The argument buffer used by the interpreter can be manipulated in size, and crucially, it can be freed and reallocated. This behavior can be exploited to free the buffer before it is used, triggering the UAF.
function Construct() {}
let handler = { get() { function meow() {} meow(0x41, 0x41, 0x41); }};
let ConstructProxy = new Proxy(Construct, handler);
new ConstructProxy(0x41);
In this code, the [[Get]] internal method of the Construct proxy is overridden to reallocate the argument buffer, ultimately leading to the UAF.The consequences of a UAF vulnerability can be severe. As the report highlights, βUAFβs tend to be a nice primitive to work withβ. In this case, because the UAF occurs in the glibc malloc heap, it allows for powerful exploitation techniques.
The researcher demonstrated the ability to:
Leak Object Addresses: By carefully crafting memory layouts, itβs possible to leak the addresses of JavaScript objects, bypassing address space layout randomization (ASLR).Create Fake Objects: Attackers can also create fake JavaScript objects, providing them with controlled memory structures.Achieve Arbitrary Read/Write: By manipulating object properties and memory, the vulnerability can be leveraged to achieve arbitrary read and write capabilities, effectively gaining control over the browserβs memory.Execute Arbitrary Code: Ultimately, the arbitrary read/write primitive can be used to achieve code execution. The researcher notes that βthe most reliable method for getting code execution appears to be overwriting a return pointerβ. This involves leaking the stack address and overwriting a return pointer with a return-oriented programming (ROP) chain to execute malicious code.While the report focuses on the technical details of the vulnerability, itβs crucial that developers using the Ladybird browser engine apply the necessary patches or updates to mitigate this threat.