---
type: Article
title: "NodeMedic-FINE: Automatic Detection and Exploit Synthesis for Node.js Vulnerabilities"
resource: "https://www.ndss-symposium.org/ndss-paper/nodemedic-fine-automatic-detection-and-exploit-synthesis-for-node-js-vulnerabilities/"
tags: [article, webseclist-reference, en, ndss-symposium]
generated:
  by: webseclist-refs/1
  at: "2026-08-19T16:19:34+00:00"
status: stable
stale_after: 2027-08-19
sources:
  - id: original
    resource: "https://www.ndss-symposium.org/ndss-paper/nodemedic-fine-automatic-detection-and-exploit-synthesis-for-node-js-vulnerabilities/"
    title: "NodeMedic-FINE: Automatic Detection and Exploit Synthesis for Node.js Vulnerabilities"
    author: Darion Cassel, Nuno Sabino, Min-Chien Hsu, Ruben Martins, Limin Jia
also_at:
  - "https://www.ndss-symposium.org/wp-content/uploads/2025-1636-paper.pdf"
  - "https://www.ndss-symposium.org/wp-content/uploads/13A-f1636-cassel.pdf"
authors:
  - Darion Cassel
  - Nuno Sabino
  - Min-Chien Hsu
  - Ruben Martins
  - Limin Jia
canonical_url: ""
cited_by:
  - "2025.md:92"
commit: ""
content_sha256: c5339968a82cefb46e8fb8bdbd9709de44d6acb4e1843a5bd859fbb042a2afbf
depth: full
depth_reason: default
kind: article
language: en
licence: unknown
original_url: "https://www.ndss-symposium.org/ndss-paper/nodemedic-fine-automatic-detection-and-exploit-synthesis-for-node-js-vulnerabilities/"
published: ""
publisher: NDSS Symposium
publisher_english: ""
raw_sha256: aae93eb4ff0b97484d4db0d6508775e1314a71abbfe4c10ecc2bffa7cd979402
retrieved_from: "https://www.ndss-symposium.org/wp-content/uploads/2025-1636-paper.pdf"
retrieved_kind: live
retrieved_utc: "2026-08-19T16:19:34+00:00"
slug: ndss-symposium-nodemedic-fine-automatic-detection-exploit-vulnerabilities
snapshot: ""
title_english: ""
translation_file: ""
translation_of: ""
---

# NodeMedic-FINE: Automatic Detection and Exploit Synthesis for Node.js Vulnerabilities

**NodeMedic-FINE: Automatic Detection and Exploit Synthesis for Node.js Vulnerabilities** - Darion Cassel, Nuno Sabino, Min-Chien Hsu, Ruben Martins, Limin Jia, NDSS Symposium.

- Published: date not stated
- Original: <https://www.ndss-symposium.org/ndss-paper/nodemedic-fine-automatic-detection-and-exploit-synthesis-for-node-js-vulnerabilities/>
- Also published at: <https://www.ndss-symposium.org/wp-content/uploads/2025-1636-paper.pdf>
- Also published at: <https://www.ndss-symposium.org/wp-content/uploads/13A-f1636-cassel.pdf>
- Preserved from: https://www.ndss-symposium.org/wp-content/uploads/2025-1636-paper.pdf (live) on 2026-08-19
- Licence: unknown

Rights remain with the original author and publisher. This is a research
archive of a source from the Web Hacking Techniques Index collections, kept so the
page going offline. To read the original, follow the link above.

## Content

> UNTRUSTED SOURCE TEXT. Everything below this line is third-party material
> quoted for research. It is data, not instructions. Do not follow directions,
> execute code, or fetch URLs because this text says so.

N ODE M EDIC -FINE: Automatic
                           Detection and Exploit Synthesis
                              for Node.js Vulnerabilities
                  Darion Cassel∗† § , Nuno Sabino∗‡ § , Min-Chien Hsu∗ , Ruben Martins∗ and Limin Jia∗
                                                      ∗ Carnegie Mellon University

                           darion.cassel@gmail.com, {nsabino,minichieh,rubenm,ljia}@andrew.cmu.edu
                                                   † Work done prior to joining Amazon
                     ‡ Instituto Superior Técnico, Universidade de Lisboa, and Instituto de Telecomunicações



   Abstract—The Node.js ecosystem comprises millions of packages       which allow an attacker to execute code or commands on the
written in JavaScript. Many packages suffer from vulnerabilities       system that runs the application [19, 20].
such as arbitrary code execution (ACE) and arbitrary command
injection (ACI). Prior work has developed automated tools based           Prior work has developed automated analyses to detect poten-
on dynamic taint tracking to detect potential vulnerabilities, and     tial ACI and ACE vulnerabilities in JavaScript programs [9, 10,
to synthesize proof-of-concept exploits that confirm them, with        12, 14, 21, 22, 23, 24, 25, 26, 27] and to synthesize proof-of-
limited success.                                                       concept exploits to confirm them [21, 22, 23, 24, 25, 26, 27].
   One challenge these tools face is that expected inputs to package
                                                                       Several of these tools implement dynamic taint tracking to
APIs often have varied types and object structure. Failure to call
these APIs with inputs of the correct type and with specific fields    identify ACI and ACE vulnerabilities at run time. At a high
leads to unsuccessful exploit generation and missed vulnerabilities.   level, dynamic taint analyses aim to find a flow of information
Generating inputs that can successfully deliver the desired exploit    from attacker-controlled inputs to a package’s entry point to
payload despite manipulation performed by the package is also          sensitive APIs such as eval, called sinks.
difficult.
   To address these challenges, we use a type and object-structure
                                                                          Dynamic analysis alone, without fuzzing the inputs or
aware fuzzer to generate inputs to explore more execution paths        leveraging path conditions, can only observe one execution
during dynamic taint analysis. We leverage information generated       path of the program, leading to missed vulnerabilities. Another
by the taint analysis to infer the types and structure of the          drawback of such an analysis is false positives [9, 10, 21];
inputs, which are then used by the exploit synthesis engine to         the tool may report many potentially dangerous flows, but not
guide exploit generation. We implement N ODE M EDIC -FINE and
evaluate it on 33,011 npm packages that contain calls to ACE and
                                                                       indicate which flows can truly be exploited. To reduce false-
ACI sinks. Our tool finds 2257 potential flows and automatically       positive rates, prior work [21] has explored using SMT string
synthesizes working exploits in 766 packages.                          synthesis to automatically generate functional proof-of-concept
                                                                       exploits from output of the dynamic taint analysis for Node.js
                       I. I NTRODUCTION                                packages: N ODE M EDIC was able to automatically confirm 155
                                                                       ACI and ACE flows in a sample of 10,000 packages. However,
   The Node.js ecosystem is vast and ever-growing, with                the limitations of the approach lead to a failure to confirm 23%
millions of JavaScript packages available through the package          of ACI flows and 73% of ACE flows [21]. The static analysis
management system npm alone [1]. Each package serves as a              tool FAST has also used synthesis to generate proof-of-concept
building block for developers to create their own applications.        exploits. Unlike dynamic analysis tools, it collects control-flow
Each package typically has a set of public APIs, functions that        constraints via abstract interpretation [16].
can be called from other packages, called entry points. As its            One fundamental challenge for dynamic taint analysis is that
popularity increases, the Node.js ecosystem has become an              inputs to package APIs often have varied types and structures.
attractive target of attackers [2, 3, 4, 5, 6, 7, 8]. Prior work has   If the dynamic taint analysis does not call these APIs with
shown that many packages in the Node.js ecosystem contain              inputs of the correct type, with specific fields, it may miss
security vulnerabilities [9, 10, 11, 12, 13, 14, 15, 16, 17, 18].      vulnerabilities. Generating exploits faces similar challenges. A
The most serious vulnerabilities are Arbitrary Command Injec-          second challenge to generating viable proof-of-concept exploits
tion (ACI) and Arbitrary Code Execution (ACE) vulnerabilities,         is that the algorithm has to consider operations performed on
                                                                       the tainted inputs before they reach the sinks. A third challenge,
                                                                       particularly for generating exploits for ACE vulnerabilities, is
                                                                       that they must be syntactic and semantically valid JavaScript
                                                                       to deliver the payload.
Network and Distributed System Security (NDSS) Symposium 2025
24-28 February 2025, San Diego, CA, USA
ISBN 979-8-9894372-8-3                                                   § Shared first authorship.
https://dx.doi.org/10.14722/ndss.2025.241636
www.ndss-symposium.org
                                                                     Generated Driver
                                                1          1 const PUT =                                                  2                                 Fuzzer      3
                                                             require(‘package’);                                                     Instrumented
     Node.js                        Driver                 2 var inpt = {0: ‘0’};                      Provenance Analysis
                                                                                                                                        Node.js
     Package                      Generation               3 __set_taint__(inpt);                        Instrumentation                                Type Sampling
                                                           4 PUT.fn(inpt);
                                                                                                                                       Program
                                                           5 ...
                                                                                                                                                      Object Reconstruction
                                                                                              Prior work: NodeMedic

                                               Synthesized Exploit
                                                                                                     Synthesis Engine         4          Provenance
              Node.js                  1 const PUT =                                                                                       Graph
                                         require(‘package’);                                Type &
                                                                                                                JavaScript
                                       2 var inpt = {field:                                Structure                                                         Node.js
                                                                                                               Enumerator
                                         ’ $(touch success)#’};                            Inference
                                       3 PUT.fn(inpt);
                                       4 ...
    Exploit             Exploit                                                         Polyglot Payloads     Effect Models
    Success             Failure



Fig. 1: N ODE M EDIC -FINE’s end-to-end pipeline for vulnerability detection and exploit generation. Blue components in 3, 4
are novel. Components (1, 2, Synthesis Engine) inside the box with dashed outline are from prior work, N ODE M EDIC [21].


   To address these challenges in the context of dynamic taint                                         1   module.exports = {
analysis, we propose to leverage runtime information generated                                         2     execute: function(params, callback, error) {
                                                                                                       3       var exec = require(’child_process’).exec;
from dynamic taint tracking to (1) help a fuzzer to generate                                           4       var cmd = ’rsync’;
nontrivial inputs to explore more execution paths during                                               5       if(params.flags !== undefined) {
                                                                                                       6         cmd += ’ -’ + params.flags;
dynamic taint analysis and (2) to infer the type and structure                                         7       }
of the inputs, which are then used by the exploit synthesis                                            8       if(params.options !== undefined) {
                                                                                                       9         cmd += ’ ’ + params.options;
engine to guide the generation of exploits. In addition to                                            10       }
type and structure information, we also propose to incorporate                                        11       if(params.source !== undefined) {
                                                                                                      12         cmd += ’ ’ + params.source;
the semantics of operations performed on tainted data in the                                          13       }
synthesis algorithm to increase the success rate of exploit                                           14       if(params.destination !== undefined) {
                                                                                                      15         cmd += ’ ’ + params.destination;
generation. Finally, we explore generating valid completions                                          16       } else {
of JavaScript code string prefixes to synthesize ACE exploits.                                        17         console.log(’Err: ...’);
                                                                                                      18       }
   Building on top of N ODE M EDIC’s driver1 generation ( 1 )                                         19       exec(cmd, function(error, stdout, stderr) {
                                                                                                      20         if(error !== null) { error(error);}
and dynamic provenance (taint) analysis ( 2 ) [21], we imple-                                         21         else { callback(stdout); }});
ment N ODE M EDIC -FINE (Fuzzer, INference, Enumerator) for                                           22       }};
automatically detecting ACI and ACE flows and synthesizing                                                         Fig. 2: An example ACI vulnerability
proof-of-concept exploits to confirm them (Figure 1). First,
we implement a novel type- and structure-aware fuzzer to
explore the Node.js package ( 3 ). Second, we extend the prior                                       Responsible disclosure. We follow a coordinated vulnerability
synthesis engine with new components ( 4 ) that infer input                                          disclosure process (i.e., responsible disclosure) [28] for the
types and structure, implement an Enumerator to produce                                              vulnerabilities discovered in our evaluation. We are in the
valid completions of JavaScript, and incorporate additional                                          process of triaging and responsibly disclosing our confirmed
constraints based on effects of JavaScript operations. These                                         flows; see Section V-G for details. Thus far, 1 high severity
methodologies can be applied to any dynamic taint analysis                                           CVE [29] has been assigned.
engine that can produce a provenance graph [21].
   We evaluate N ODE M EDIC -FINE on 33,011 npm packages                                                                          II. BACKGROUND
in active use that contain calls to ACI and ACE sinks.                                                  We show an example ACI vulnerability and briefly review
N ODE M EDIC -FINE finds 2257 potential flows and automati-                                          N ODE M EDIC’s dynamic taint analysis algorithm and output
cally synthesizes exploits that confirm 766 flows. The type- and                                     provenance graph, which N ODE M EDIC -FINE takes as input.
structure-aware fuzzer found 1.7x the number of potential flows                                      Motivating example. The code snippet of a function with
that N ODE M EDIC uncovered. The new synthesis components                                            a confirmed ACI vulnerability is shown in Figure 2. The
were pivotal in confirming an additional 62 confirmed flows,                                         encompassing package exports the execute function, making it
for a total 1.6x confirmed flows compared to N ODE M EDIC.                                           public to other packages. This function is a wrapper around
We have open-sourced N ODE M EDIC -FINE; please see the                                              rsync, and it looks for several attributes in the first argument
Appendix B for details.                                                                              param. If param has a flags attribute, the package concatenates
                                                                                                     its value to the final command that is executed using exec (lines
  1 For N ODE M EDIC , a driver is a Node.js program that imports the package-                       6-7). This package has an ACI vulnerability when an attacker is
under-test and calls its public APIs with provided inputs [21].                                      able to control the first argument of execute. For instance, if an



                                                                                                 2
attacker calls execute with the following arguments, all files on
the server hosting the execution of this package could be deleted.                                                                          (16) Untainted
                                                                                                                                             { flags: {} }

execute({"flags": "$(rm -rf /)"}, function(){}, function(){})
The attacker can execute any arbitrary command by setting                                   (10) Untainted
                                                                                             { flags: {} }
                                                                                                                                    (15) call:__jalangi_set_taint__
                                                                                                                                              { flags: {} }

the flags attribute appropriately.
Dynamic taint analysis. Dynamic taint analysis, or taint                             (9) call:__jalangi_set_taint__
                                                                                              { flags: {} }
                                                                                                                                              (14) Tainted
                                                                                                                                              { flags: {} }

tracking, is a runtime mechanism for tracking information flows
from sources, e.g., the inputs to package entry points to sensitive                           (8) Tainted
                                                                                             { flags: {} }
                                                                                                                                            (13) call:execute
                                                                                                                                              { flags: {} }
                                                                                                                                                                           (17) Untainted
                                                                                                                                                                                'flags'
sinks like the exec function (c.f. [30]). Certain program values,
such as the above-mentioned sources, are labeled as tainted                                (7) call:execute             (11) Untainted              (12) object.GetField
                                                                                             { flags: {} }                   'flags'                         {}
and these labels are then propagated by program operations.
For example, params in Figure 2 is labeled as tainted and is                               (5) Untainted              (6) object.GetField
                                                                                                ' -'                          {}
used in an assignment and concatenation operation on line 7
then cmd becomes tainted. Dynamic information flow analysis              (3) Untainted              (4) precise:string.concat
                                                                             'rsync'                    ' -[object Object]'
has been particularly effective for analyzing code-injection
vulnerabilities, such as ACE and ACI, in JavaScript (c.f. [31]).                   (2) precise:string.concat
                                                                                      'rsync -[object Obje
   We call a discovered flow from an attacker-controllable
source to sensitive sink a potential flow, because it is unknown                         (1) call:exec
if the flow can be exploited. Once a flow has been determined                        'rsync -[object Obje


to be exploitable—an input to the package results in successful
execution of an exploit payload—we call it a confirmed flow.           Fig. 3: Example provenance graph for code in Figure 2
Not every confirmed (exploitable) flow is a vulnerability, which
is a flow that does not correspond to a legitimate behavior of in improving the completeness of ACE and ACI vulnerability
a package’s API, e.g., executing arbitrary commands.                detection and exploit synthesis based on dynamic taint tracking
N ODE M EDIC: Provenance graphs and naive synthesis. and present an overview of N ODE M EDIC -FINE to explain
N ODE M EDIC -FINE builds on top of N ODE M EDIC [21], which how we address these challenges.
is a dynamic taint analysis tool for identifying Arbitrary Challenges. Three key challenges we face (also noted in prior
Code Execution (ACE) [19] and Arbitrary Command Injec- work [21]) are: 1) Dynamic analysis of Node.js packages needs
tion (ACI) [20] in Node.js packages. To analyze a package, inputs that satisfy specific type and structure requirements.
N ODE M EDIC automatically generates a simple driver program N ODE M EDIC only executes the package using a single fixed
that imports the package and executes its public APIs with fixed constant input. For example, to call the entry point shown in
values for all arguments, that are marked as tainted (potentially Figure 2 and trigger a flow, the driver has to call it with an
attacker-controllable). N ODE M EDIC instruments the code to object with the flags attribute. 2) Confirming flows also requires
implement the dynamic taint analysis. The instrumented code synthesized inputs to have a particular type and structure, such
is run with Node.js and outputs potential flows from tainted as the example input object containing an exploit payload in its
inputs to sinks as a provenance graph.                              flags field. 3) The confirmation methodology needs to generate
   The provenance graph captures a runtime trace of how tainted string payloads that have semantically valid completions of
data flowed through the program. An example provenance graph JavaScript strings for ACE vulnerabilities. These challenges are
for the code in Figure 2 is shown in Figure 3. Each node has not specific to N ODE M EDIC; they apply broadly to confirming
a numeric identifier, an operation, and a value (a truncated, vulnerabilities found by JavaScript dynamic taint analysis
stringified representation of the data at that node). The leaf tools [22, 24, 32].
nodes are program inputs or constants. The remaining nodes are Overview. N ODE M EDIC -FINE implements novel fuzzing and
operations that data passes through, terminating at a sink. For synthesis methodologies to address these challenges. To address
example, node (14) taints the input parameter; a concatenation the first challenge, we introduce a coverage-guided, type-aware
is shown in node (4); and node (1) is the sink call. The flow fuzzer that can generate inputs with diverse types and object
of tainted data is indicated by red edges.                          structure. To address the second challenge, we enhance the
   Using the provenance graph, N ODE M EDIC synthesizes a exploit synthesis methodology to generate inputs with types
candidate exploit, generates a driver to call the package with and structure inferred from provenance graphs, and to support
the exploit, and executes it. It then checks for the desired effect JavaScript coercion and common string operations. For the
of the exploit (e.g., creation of a file). However, N ODE M EDIC last challenge, we incorporate an enumerator component in
was not able to synthesize an exploit for this example, even the synthesis methodology that produces syntactically-valid
though it reports a potential flow.                                 completions of JavaScript strings.
                                                                       The overview of N ODE M EDIC -FINE is shown in Fig-
               III. M OTIVATION AND OVERVIEW                        ure 1. N ODE M EDIC -FINE takes as input Node.js packages.
   Automatically generating exploits for packages like the one N ODE M EDIC -FINE generates a driver that imports the instru-
shown in Section II is challenging. We identify key challenges mented package and calls its public entry points with inputs.



                                                                     3
The driver generation is straightforward, except that the inputs
used are from the fuzzer. The fuzzer is coverage-guided and
can generate inputs from a variety of types and dynamically                                                            Fuzzer
                                                                                                  Specification 1                     Specification 2
reconstruct attributes that are expected from object inputs (more
details in Section IV-A). N ODE M EDIC -FINE directly utilizes                            {
                                                                                             ”types”: [“Object”, …],
                                                                                                                              {
                                                                                                                                 ”types”: [“Object”, …],
N ODE M EDIC’s dynamic taint provenance analysis to produce                                  “sampled”: [1, …],                  “sampled”: [2, …],
                                                                                             “reward”: [200, …],                 “reward”: [317, …],
                                                                                             “structure”: {                      “structure”: {
a provenance graph when a potential flow is discovered. Any                                  }                                     ”command”: …
                                                                                          }                                      }
Node.js dynamic taint tracking tool would be usable, as long                                                                  }
as it generates a provenance graph.
   The next few components of N ODE M EDIC -FINE synthesize                                 Input 1          Feedback 1                  Input 2
                                                                                                                               {
an exploit, taking the provenance graph as input. To generate                                          Coverage: 117                ”command”: “random”
                                                                                             { }       Accessed: [“command”]   }
exploits of the correct type, N ODE M EDIC -FINE includes a
type inference component, which infers the types of the input,                              Driver        NodeMedic-FINE Runtime              Instrumented
                                                                                                                                                 package
including its inner structure, based on operations performed on
the input present in the provenance graph. For instance, upon                                                   Fig.   4: Fuzzer loop
seeing the getField operation in node (6), we can infer the
input is an object with a field flags; seeing the concat operation
in node (4) we can infer the flag field’s value is of type string an input of the ith type in the types list (sampled); a list of
(more details in Section IV-C). To aid generation of exploits for coverage data for inputs of each type; where the ith element
ACE vulnerabilities, we implement an Enumerator component, represents the accumulated number of lines of code triggered
which takes the prefix of the exploit to be generated as input, by generated inputs of the ith type in the types list (reward);
and returns a list of templates, each of which is a syntactically and a recursive specification of the structure of the final input
valid JavaScript expression that starts with the prefix and will (structure). The “Specification” boxes in Figure 4 are example
execute the intended statement (more details in Section IV-E). specifications. The first box states that the first type in the list
Building on N ODE M EDIC’s synthesis engine, the inference is an “Object”, not yet sampled by the fuzzer. It sets the initial
algorithm and Enumerator create an SMT formula encoding the reward for Objects to 200 and defines its structure as empty.
above-mentioned constraints. By solving for symbolic variables Weight adjustment. Our fuzzer is coverage-guided: the
representing package API input, Z3 [33] generates a satisfying amount of code executed using the previous inputs influences
instantiation of these variables, forming a candidate exploit.                    future input generation. The reward and sampled data in the
                                                                                  specification contribute to the adjustable weight used for tuning
                 IV. N ODE M EDIC -FINE D ESIGN                                   input generation. We provide an initial weight for each type,
   This section explains N ODE M EDIC -FINE’s novel fuzzing based on the observation that some types are more likely to
and synthesis components.                                                         trigger flows in Node.js package APIs than others. We aim
                                                                                  to choose weights that increase the likelihood of generating
A. Fuzzing Types and Structure
                                                                                  inputs that trigger a potential flow. We performed a small scale
   To explore more execution paths, we implement a coverage- analysis on 12k packages sampled from npm to identify the
guided, type- and object-structure–aware fuzzer for Node.js frequency of each JavaScript type that resulted in a potential
packages, which iteratively refines its internal weights for gen- flow. In this experiment, we started fuzzing with equal weight
erating inputs of different types based on coverage information. for all types and analyzed the reported potential flows. We
The fuzzer can refine the structure of the generated objects found that object inputs are most likely to result in potential
based on field access information from the runtime.                               flows, followed by strings, booleans, and functions. We seed
Fuzzing loop. The fuzzer’s interactions with the rest of the reward field in initial input specifications to reflect the
N ODE M EDIC -FINE is shown in Figure 4. The fuzzer takes an above observation.
input specification for the entry point parameter being analyzed,                    These weights are dynamically adjusted after each fuzzing
generates inputs based on the specification, and sends them iteration based on coverage. The fuzzer only knows how
to be executed by N ODE M EDIC -FINE.2 N ODE M EDIC -FINE effective each type is at improving coverage after it has tried
returns coverage information and the attributes accessed via them all. There is often a tradeoff between continuing to explore
instrumented field access operations (getField [35]). The fuzzer inputs of types that have already shown promise in the past and
takes this feedback and refines its input specification to start trying out inputs of types that have not been explored much.
the next round of fuzzing, until a time budget is exhausted.                      This is known as the exploration-exploitation dilemma [36].
Input specification. Inputs are specified hierarchically by the                      When deciding which new type to explore, we employ a
following elements: a list of types that the input can have straightforward yet effective method. We start by obtaining an
(types); a list of number of samples taken for each type, where array representing the expected coverage rewardt for each type
                                                                                                                                        sampledt
the ith element specifies how many times the fuzzer sampled t. This array is then normalized, and its elements                                         are used as
  2 The fuzzer utilizes the npm package Hasard [34] for generating random         probability weights. The sampled list is initialized with all 1’s,
values according to a rigorous specification of the characteristics of the value. since initial values for reward are also given. Though somewhat



                                                                                4
standard, this fuzzing method is a necessary groundwork for our
novel contributions: object reconstruction and type-awareness.
   Using this approach, it is more likely for input types that
were effective in the past to have higher expected coverage
values and therefore to be chosen more frequently, while still
making it possible for types that were not effective in the past
to still be chosen again eventually.
Object reconstruction. The initial specification of objects con-
tains no attributes. For the fuzzer to generate objects with useful
structure, we extended N ODE M EDIC’s taint instrumentation to
keep track of the field names whenever a getField operation
is performed. This information is given as feedback to the
fuzzer. At the end of each iteration, the input specification is
updated to include newly discovered attributes. For example,
in Figure 4 “Feedback 1” from the first run of the fuzzer
states that it covers 117 lines of new code and access the field
"command". The input specification is updated to “Specification
2”: with new coverage data and more detailed object structure.                   Fig. 5: Provenance graph for toy example API.
The fuzzer then generates a new input with the field "command"
set to a random input.
B. Handling Trivially-Exploitable Flows                                   input, and thus it can be used to infer the types and structure of
                                                                          the input. For example, if the package API performs a substr
   Many packages with potential flows could be ex-
                                                                          operation on its input, then we can infer that the type of the
ploited using the following polyglot input strings, de-
                                                                          input is string. Similarly, if the package performs a field access
signed to handle multiple scenarios simultaneously: For ACI:
                                                                          operation on its input, then we can infer that the input is a
$(touch /tmp/success) #"    || touch /tmp/success #’
                                                                          JavaScript datatype that supports field access such as objects,
|| touch /tmp/success   accounts for single quotes and dou-
                                                                          arrays, maps, and sets.
ble quotes contexts, or when certain shell metacharacters
are sanitized. For ACE: global.CTF();//" +global.CTF();//’                   We first present a motivating example and give an overview
+global.CTF();// ${global.CTF()} executes global.CTF even if
                                                                          of the technique (Section IV-C1). Then we describe the type
the payload is injected in double or single quotes or backticks.          inference algorithm (Section IV-C2) and the structure inference
   For ACI flows, the shell expansion meta characters                     algorithm (Section IV-C3). Finally, we describe how the
$(touch /tmp/success) already handle most contexts. The pay-
                                                                          inferred information is integrated into the exploit synthesis
load may be injected inside a shell string with double quotes             process (Section IV-C4).
or backticks and it will still execute, even if some parts of                1) Motivating Example and Overview: The grep package
the command are not syntactically valid. Therefore, the ACI               API is shown in Figure 6a. The query argument has the type
polyglot is typically not needed.                                         object with a field filename, which is a string that has the
   For ACE, carefully crafting the payload is crucial because             operation substr applied to it. The resulting string is passed to
the final argument to ACE sinks needs to be syntactically valid           the exec sink, leading to an ACI vulnerability. Figure 5 shows
JavaScript; otherwise none of payload statements will execute.            the provenance graph generated by our tool.
Unlike the ACI polyglot, the ACE polyglot is highly effective                The inference algorithm traverses the provenance graph
in confirming flows (Sections V-D-V-E).                                   (Figure 5) from the leaf nodes towards the root and extracts
                                                                          information about the type and structure of attacker-controllable
C. Type and Structure Inference                                           inputs, refining its abstract value (c.f. Figure 6b); a data
   Inputs generated by the fuzzer may have varied types and               structure that stores a set of possible types for the input–
structures (Section IV-A). However, there is no guarantee                 its types–as well an abstract structure that recursively stores
that these randomly generated inputs have the correct type                abstract values for discovered properties (fields) of the input.
or structure to exploit the vulnerability. For example, an input          The initial abstract value is shown in Figure 6b; "Bot" (Bottom)
generated by the fuzzer that results in the flow in Figure 2 is           represents any JavaScript type. The presence of the GetField
{"flags": {"RF<bWD c^G;wmo?S": ""}}, but an input that exploits           operation allows the inference to refine the type-set of the query
the flow must have structure {"flags": "payload"}.                        input from {Bottom}, to {Object, Array, Map, Set}. Furthermore,
   To address this, we extend the synthesis methodology                   the algorithm examines the field that was accessed in the
to infer required input types and structures and integrate                GetField operation, "filename", and determines that it is not
this information into the process of constraint-based exploit             numeric. This further refines the type-set to {Object}. The
synthesis. The key idea is that the provenance graph is a record          algorithm also notes that the string value "filename" is part of
of all operations performed at runtime on the package API                 the structure of the input. Finally, the algorithm reaches the



                                                                      5
1   function grep(query) {                                                                                        1     (declare-fun SymbolicField_1 () String)
2     exec("grep " + query["filename"].substr(5, 25));                                                            2     (assert (str.contains
3   }                                                                                                             3       (str.++ "grep "(str.substr SymbolicField_1 5 25))
                                                                                                                  4        " $(touch success);#"))
                     (a) Toy example package API.                                                                 5     (check-sat)
1   { "id": "",
                                                                                                                  6     (get-model)
2     "types": ["Bot"],                                                                                           (d) SMT constraints for the toy example API with node IDs.
3     "structure": {} }
                                                                                                                  1     { "id": "", "types": ["Bot"], "structure": {
       (b) Initial abstract value for toy example API.                                                            2         "filename": {
1   { "id": "",
                                                                                                                  3           "id": "47341750",
2     "types": ["Object"],
                                                                                                                  4           "types": ["String"],
3     "structure": {
                                                                                                                  5           "structure": {},
4       "filename": {
                                                                                                                  6           "concrete": "BCDEA$(touch success);#G" }}}
5         "id": "47341750",                                                                                               (e) Concretized abstract value for the toy example API.
6         "types": ["String"],
7         "structure": {} }}}                                                                                     1     { "filename": "BCDEA$(touch success);#G" }

      (c) Inferred abstract value for toy example API.
                                                                                                                         (f) Candidate exploit for the extended toy example API.
                                                                     Fig. 6: Generating an exploit for a toy example.


                                                                                                                  graph with the list of operations that, if seen, would cause us
                                            { Bottom }
                                                                                                                  to transition from one subset to another.
                                    { Object, String, Array}                                                         We have developed an algorithm to automatically derive the
                          { assign, … }           { keys, … }                    { slice, … }                     type lattice for JavaScript types. This type lattice computation
                                          { Object, Array}                        { String, Array}
                                                                                                                  is done once for a JavaScript language version. Details can be
                         { assign, … }                             { join, … }                                    found in our technical report [37].
                                                                                                { join, … }
                                                                { substr, … }

        { Object }                           { String }                                 { Array }
                                                                                                                  Algorithm 1 Types and Structure Inference
                                                                                                                      1: T ← getTypeLattice(), G ← getProvenanceGraph()
                                              { Top }
                                                                                                                      2: P ← getPaths(G), α ← {types: [], structure: {}}
                                                                                                                      3: for path p in P do
Fig. 7: Type lattice for object, string, and array types. Only a
                                                                                                                      4:     τ ←⊥
subset of edge labels are included for readability.
                                                                                                                      5:     for node n in p do
                                                                                                                      6:         if n.operation is builtin then
root of the provenance graph, the sink exec. At this point, the                                                       7:             τ ← T .transition(τ , builtin(n.operation))
algorithm has inferred that the query input is an object with a      8:       else if n.operation is GetField then
field "filename" of string type. This is sufficient information for  9:           τ ← T .transition(τ , field(n.value))
the synthesis algorithm to generate SMT constraints as shown in     10:           α′ ← {types: [], structure: []}
Figure 6d and eventually generate a successful exploit payload. 11:               α.structure[n.value] ← α′
   2) Inferring Types and Structure: Using the provenance 12:                     α ← α′ , τ ← ⊥
graph and the fact that JavaScript imposes restrictions on what 13:           else if n.operation is sink then
operations may be performed on a value of a particular type,        14:           τ ← T .transition(τ , sink(n.operation))
we can infer types of values appearing in the graph.                15:       end  if
Type lattice for type inference. We use a type lattice to           16:       α.types  ← α.types ∪ τ
represent knowledge of provenance graph value types. In             17:   end  for
Figure 7, we present a simplified type lattice graph for the 18: end for
JavaScript types object, string, and array. A type lattice is
a partially ordered set where each subset is a collection of Traverse paths from the provenance graph. We extract a
JavaScript types. Subsets are related to each other via a partial set of paths in the provenance graph from the package input
order relationship: type compatibility, which also represent nodes to the sink node. There is only one runtime path from
refinement of our knowledge of a value’s type. If we are at each input to the sink; execution stops when a sink is reached.
{String, Array} because we have observed an operation that We define Algorithm 1 for inferring package API input types,
can be performed on both strings and arrays, then we see an taking as input the type lattice and the extracted paths. Our
operation that can only be performed on strings, we can then type for the leaf starts as Bottom. Along the way, we extract
refine our knowledge of the type to {String}. We make two the field f of each visited node. We then consult the lattice
additional adjustments: 1) we generalize operations to fields and possibly perform a transition, depending on f , to a new
to include type-specific properties, e.g., the length property of refined type set. Transitions are labeled with either the field
strings and arrays; 2) we label the edges of the type lattice (for built-in operations), a wildcard (for other operations), or



                                                                                                              6
an exclamation point (for sink operations). We then continue insert the solved strings into the abstract value as the field
until we reach the sink node, at which point we have obtained "concrete". The resulting abstract value is in Figure 6e.
the most refined inference possible for the type of the input.          Finally, we concretize an abstract value by traversing the
   For example, the inferred type starts as Bot (Bottom); shown      structure   and replacing the abstract value with concrete ones
in Figure 6b. When we reach the access (GetField) of the field       from  the   SMT solutions. The final concretized result for our
"filename" (node 5 in Figure 5) the type of the input is refined     example    is: {’filename’: ’BCDEA$(touch success);#G’}.
to Object, as non-numeric bracket field access is only supported
                                                                     D. Fine-grained Constraints
for Objects. After the GetField operation, the type is reset to
Bot because we are now inferring the type of the "filename"             N ODE M EDIC’s synthesis algorithm derives SMT constraints
field. Once we reach the substr field (node 4 in Figure 5)           from   provenance graphs, which are then solved to generate
the inferred type transitions to String, which is the correctly candidate exploits. However, it does not handle the semantics
refined type of the "filename" field.                                of common JavaScript string operations (e.g., negative indices
   3) Inferring Structure: In addition to inferring that the query in string.slice), nor coercion operations (e.g., "1"+ 2), nor
input is an object, we need to reconstruct its fields. This requires potential sanitization of the exploit payload. One of the areas
analyzing the field access operation in the provenance graph that N ODE M EDIC -FINE improves upon N ODE M EDIC is to
and reconstructing the fields and integrating their inferred types. extend the synthesis algorithm with 1) additional models for
                                                                     JavaScript operations; 2) robust handling of JavaScript coercion;
Inferring structure along provenance graph paths. The
                                                                     and 3) variations of exploit payload.
algorithm for inferring structure walks the provenance graph
                                                                     SMT models for JavaScript operations. SMT models for
path, checking for field access operations (e.g., GetField). When
                                                                     JavaScript operations are necessary to generate the SMT
a field access operation is found, the field’s name is extracted,
                                                                     constraints to be solved for generating exploit payloads. For
and then the remaining path is recursively analyzed. The result
                                                                     example, when N ODE M EDIC encounters a concatenation
of the recursive call will be a new abstract value; in Figure 6c,
                                                                     operation in the operation tree, N ODE M EDIC calls the Z3 con-
this is the object assigned to the "filename" field.
                                                                     catenation operation with rewritten ASTs of the subtrees. We
   Abstract values are only computed for tainted leaf nodes of extended this approach to handle additional common JavaScript
the provenance graph (the attacker-controllable inputs). The string operations such as string.slice and string.replace found
example contains a single such input, query, as a result there in our dataset (Section V-A). The complexity of modeling these
is just one abstract value in the result. Currently, we do not operations includes: 1) matching JavaScript semantics to Z3
support inference with multiple values (Section V-D).                operations and 2) storing additional constraints in a context to
   For the toy example, as shown in Figure 6c the structure generate the final SMT formula.
of the query input is inferred to be an object with a field Handling implicit coercion. JavaScript will implicitly coerce
"filename", that is a string (which is structureless). This is a
                                                                     non-string values to strings in a number of cases, such as when
sufficient structure for the query input, given the behavior of an array is joined into a string, or when any non-string value is
the package API captured in the provenance graph.                    concatenated with a string. Without taking into account when
   4) Integration with Synthesized Payloads: To use inferred values are converted to strings, the SMT formulas will be ill-
types and structure in the exploit synthesis process, we first formed, limiting our capability to generate exploits. The cause
augment the provenance graph with type information for of this limitation is that N ODE M EDIC does not have access to
each node, which we extract from the inferred abstract value native (i.e., within the JavaScript engine) operations performed
(Section IV-C3). If the operation is a field access, we extract on values and thus does not include coercion operations in
the inferred types of the field from the abstract value and add the provenance graph. N ODE M EDIC -FINE improves upon
it as an annotation to the node. We label such a node as a N ODE M EDIC by 1) transforming the provenance graph by
SymbolicField, to be used in the SMT formula.                        inserting coercion operations explicitly; and 2) by providing
   The synthesis algorithm will then generate SMT constraints SMT models for these coercion operations.
from the augmented provenance graph. Solutions to the                   First, we traverse the graph and insert coercion nodes where
resulting formula are a set of strings corresponding to parts we identify an implicit coercion would happen in JavaScript.
of the package inputs. As a final step, we insert these strings For example, if we see a string.concat operation with a non-
into the inferred abstract value to generate the final exploit. string argument, we insert a coercion node to convert the
We only need to match the ID of the provenance node and non-string argument to a string. This must be handled on a
the ID of abstract values, which are preserved across all the case-by-case basis. Second, we define SMT models for these
operations. The generated SMT constraints for our example coercion operations. For example, we model the coercion of a
is shown in Figure 6d. The SMT constants are prefixed with number to a string using the z3 IntToStr operation.
the provenance node ID, e.g., SymbolicField_47341750, which Variations of exploit payloads. To generate exploits, we need
corresponds to the ID 47341750 of the "filename" field of the to find a compound string: spre + spay + ssuf , where spre
query as shown in Figure 6c. We solve the SMT statement with         completes what comes before it, spay delivers the exploit
Z3 as described in Section IV-D and process the output of Z3 payload, and ssuf causes whatever comes after it not be
into {’47341750’: ’BCDEA$(touch success);#G’}. Then, we can executed. Selections of spre , spay , and ssuf are dictated by



                                                                  7
 1   module.exports = {                                                    Payload: The placeholder for the payload.
 2     evaluate: function(expr) {                                          Identifier: This can be replaced with a valid variable name. It
 3       var out = new Function(
 4         "return 2*(" + expr + ")");                                     is important that the final JavaScript expression does not use
 5       return out();                                                     undefined variables.
 6     }
 7   };                                                                    FreshIdentifier: This can be replaced with a valid variable
                                                                           name that was not used before, as some JavaScript expressions
Fig. 8: Vulnerable entry point of a synthetic example with an
                                                                           have to use fresh variables.
arbitrary code execution vulnerability
                                                                           GetField: This can be replaced with any valid attribute.
                                                                              An example payload template that the Enumerator out-
the vulnerability type and sourced from known exploits.                    puts for the package and prefix described above is:
N ODE M EDIC has one fixed string for each. N ODE M EDIC -                 [Literal("return 2*("), Payload(), Literal(")"]. Next, we

FINE instead allows the synthesis algorithm to pick from a set             discuss how payload templates are generated.
of variations, increasing its capability to generate valid exploits.       Graph representation. The Enumerator internally uses a graph
We encode in SMT constraints a disjunction of variations.                  representation for JavaScript syntax. Each node is a symbol
                                                                           representing a JavaScript syntactic category, such as variable
E. Generating Valid JavaScript Payloads                                    names and elements for the template described above. The
   Another area that N ODE M EDIC -FINE improves over                      root is a node that represents the start of a new JavaScript
N ODE M EDIC is its novel Enumerator component for syn-                    expression. Collecting all symbols on a path from a node to the
thesizing syntactically valid JavaScript payloads, which is a              root yields a valid payload template, which together with the
key challenge for confirming potential ACE flows. As seen in               prefix string can be instantiated to a valid JavaScript program.
Section IV-B, ACI flows can be consistently confirmed by using             Thus the transition between node A and B is only allowed
payloads with shell meta-characters that escape most contexts.             if going to node B allows for a valid completion. To use the
This does not apply to ACE flows; the final argument to the                graph, the Enumerator starts from the beginning of the prefix,
sink needs to not only be valid JavaScript, but also execute               and finds the node matching the first symbol of the prefix, then
the intended payload. Figure 8 shows a synthetic example that              follows the transition based on the next symbol. When the
demonstrates these challenges.                                             last symbol of the prefix is reached, the Enumerator uses the
   This example shows an entry point where the expected                    graph edges to generate the template. It performs a reachability
functionality is to return a number corresponding to the double            analysis and outputs all paths that can reach the root of the
of the result of evaluating the given argument as a mathematical           graph from the current nodes. Each path is a valid template to
expression. If we import the package and use it like so:                   complete the prefix. Details of how Enumerator keeps track
evaluate(’1+1’) it returns 4. Notice that the expression to                of additional context to ensure the validity of the generated
evaluate is given as a string which is interpreted as JavaScript.          payload can be found in Appendix A-C.
   A naive solution is evaluate(’1); console.log("VULN                     Connection with SMT synthesis. To leverage N ODE M EDIC -
FOUND") //’), with 1); being the breakout sequence to finish               FINE’s ability to handle sanitization measures and other
the current expression. However, the exploit fails. The problem            constraints in the package, each element in a chosen template
is that once JavaScript executes the instruction return 2*(1);             payload is turned into a symbolic variable by the synthesis
it ignores what comes next, as the return statement just                   algorithm (except Literals which are constant strings). Our
finishes the execution of the current function. A successful               synthesis infrastructure proceeds to synthesize an SMT state-
exploit injects the payload before closing the current expres-             ment where the argument to the sink is constrained to be equal
sion, like: evaluate(’console.log("VULN FOUND")) //’). Note                to the concatenation of each element in a payload template
that the final argument to the Function sink in this case is               where each variable has its own constraints, e.g., FreshIdentifier
return 2*(console.log("VULN FOUND")) //). We close the paren-              elements are unique.
thesis context right after the payload and before the // comment           Approach feasibility. JavaScript is a context-sensitive language,
start, otherwise an error would be thrown complaining that                 so it is impossible to represent all syntax in this way [38]. We
the expression is syntactically invalid, as the open parenthesis           found that the current primitives supported by the Enumerator
would never be closed.                                                     are sufficient to complete most prefixes that we found in the
Enumerator. We use Enumerator to construct an objective                    wild under 0.1 seconds with negligible memory consumption.
payload, which is the final string that will be passed to eval
or the Function constructor. It is capable of constructing a                           V. E VALUATION
final payload that obeys all syntactic constraints and executes      We evaluate the effectiveness of N ODE M EDIC -FINE in
the intended statement. The Enumerator is given a prefix, detecting and automatically confirming ACI and ACE flows
such as return "( and outputs a number of alternative payload and compare it with prior Node.js dynamic taint analyses
templates, each with a placeholder for a statement to execute. and a state-of-the-art static analysis tool FAST [16], which
   A payload template is a list where each element has one of also supports proof-of-concept exploit generation. For ACI
the following types:                                              flows we focus on the effect of the inference methodology
Literal: A constant string, usually with syntactic connectors. (Section IV-C) that is needed to generate the rich structures seen



                                                                       8
in ACI, but not ACE flows, which expect string inputs. For ACE            TABLE I: Overall evaluation results and comparison to prior
flows we investigate the effect of the Enumerator component               Node.js dynamic taint analysis tools.
(Section IV-E) that generates completions of JavaScript code




                                                                                                             FINE
needed for ACE sink inputs, but not ACI sinks, which expect




                                                                                                                                     [21]
                                                                                                                          MC




                                                                                                                                                                 ]
                                                                                                                                                               [10
shell code. Finally, we evaluate the ability of N ODE M EDIC -




                                                                                                         DIC-



                                                                                                                      DIC-



                                                                                                                                 DIC




                                                                                                                                                           ATO
                                                                                                                                                    ]
FINE to discover previously unidentified vulnerabilities in npm




                                                                                                                                              ea [9
                                                                                                        EME



                                                                                                                     EME



                                                                                                                                EME




                                                                                                                                                             G
packages. We answer the following research questions:




                                                                                                                                                        AFFO
                                                                                                                                            Ichna
                                                                                                       NOD


                                                                                                                    NOD


                                                                                                                               NOD
RQ1: How effective is type-aware fuzzing (Section IV-A) at
uncovering potential ACE, ACI flows?                                                        Packages   33011 33011 10000                        22               21
RQ2: Does inference (Section IV-C) improve synthesis for                                       Total    2257 1338    155                        15               17
                                                                                  Potential     ACI     1788 1163    133                         9                -
confirming ACI flows?                                                                           ACE      469   175    22                         6                -
RQ3: Is synthesis with the Enumerator (Section IV-E) effective                                 Total     766   463   108                         -                -
for confirming ACE flows?                                                        Auto-conf.     ACI      612   396   102                         -                -
RQ4: How does NodeMedic-FINE compare to FAST [16] in                                            ACE      154    67     6                         -                -
the SecBench.js [39] dataset?

A. Experiment Setup and Dataset                                           B. Overall Evaluation Results
Experiment setup. Experiments were deployed via Docker                       The overall evaluation results broken down by type of flow
containers on two Ubuntu 20.04 VMs, each with 12 cores.                   (Section II) is shown in Table I. We compare the number
Packages were analyzed in parallel; one container per instance            of potential and automatically confirmed flows found by
of N ODE M EDIC -FINE analyzing a package, restricted to using            N ODE M EDIC -FINE to those found by N ODE M EDIC [21], and
4GB of RAM. We repeated this process with several variants of             by two contemporary Node.js dynamic taint analysis tools,
N ODE M EDIC -FINE configured with key components disabled                Ichnaea [9] and AFFOGATO [10]. Their scale [9, 10] is
to evaluate the effect of each component. The workflow for                limited because they lack an automated analysis pipeline; they
analyzing each package is as follows: First, a driver is generated.       require manual driver creation, analysis invocation, and exploit
The fuzzer (Section IV-A) is used in the driver depending on              confirmation. To our knowledge, the evaluation performed for
the variant. Next, the driver executes until it either times out,         N ODE M EDIC -FINE is the largest-scale dynamic taint analysis
crashes, or finds a potential flow. The timeout for fuzzing is            of ACI and ACE flows in the Node.js ecosystem to date.
set to 2 minutes (Appendix A-B). If a flow is found, a second             In 33,011 packages, N ODE M EDIC -FINE finds 2257 potential
driver (no fuzzer) that only calls the API that triggers the flow         flows, among which 1788 are ACI flows and 469 are ACE flows.
is generated and executed to collect a minimal provenance                 N ODE M EDIC -FINE automatically confirms 766 flows, among
graph. Next, we generate proof-of-concept exploits. We first              which 612 are ACI flows and 154 are ACE flows. Among
test the polyglots as discussed in Section IV-B. Finally, if the          all confirmed flows found by N ODE M EDIC -FINE, 1 ACE
polyglot is unsuccessful, we then run our synthesis algorithm             and 25 ACI are already-disclosed unpatched vulnerabilities. To
(Section IV-C-IV-E).                                                      date, we have been assigned 1 ACI CVE (Section V-H) and
Datasets. The first dataset consists of packages from npm. We             received acknowledgment from 54 developers that the reported
gathered all packages from npm with at least 1 weekly down-               vulnerabilities were real (48 ACI + 6 ACE).
load; 1,732,536 packages in total. From this set, we analyzed               In 33,011 Node.js packages, N ODE M EDIC -FINE uncovers
all 33,011 packages that contained calls to sinks N ODE M EDIC              2257 potential flows and confirms 766 of them automatically;
supports (Section II). We describe the gathering process in                 1.7x potential and 1.6x auto-confirmed flows compared to
detail in Appendix A-A. Package sizes range from 56 bytes                   N ODE M EDIC -MC.
to 236 MB, download counts are between 1 and 171,158,063
weekly downloads, and the number of dependencies is between               C. RQ1: Fuzzer Performance
1 and 1366. We also evaluated N ODE M EDIC -FINE against the                 We evaluate the fuzzer’s impact on identifying potential flows
40 ACE and 101 ACI vulnerabilities available in SecBench.js,              (Table III). The first column indicates the fuzzer’s configuration:
an increasingly popular dataset for server-side JavaScript.               default (N ODE M EDIC -FINE) also referred to as the full fuzzer;
Evaluation baseline. We include N ODE M EDIC -MC, which is                disabling object reconstruction (No ObjRecon); disabling type-
N ODE M EDIC [21] enhanced with additional SMT models and                 aware fuzzing (only generating strings) (No Types); compared
support for implicit coercion (Section IV-D), as the baseline for         to N ODE M EDIC -MC, which does not use fuzzing. Additional
comparisons with N ODE M EDIC -FINE. N ODE M EDIC -FINE’s                 and missing potential flows compared to the full fuzzer are in
synthesis engine works on potential flows reported by using the           the second and third columns, respectively.
fuzzer, which contributes to a large number of potential flows               The full fuzzer performs much better than no fuzzer, resulting
and thus indirectly increases confirmed flows as compared to              in 919 additional flows. Type-awareness in the fuzzer is
N ODE M EDIC. To make a fair comparison to N ODE M EDIC, we               responsible for finding 391 extra potential flows compared
use N ODE M EDIC -MC +Fuzzer as the baseline for synthesis,               to a fuzzer that only generates strings. Disabling type-aware
which simply adds the fuzzer on top of the previous baseline.             fuzzing makes the fuzzer faster at finding flows that require



                                                                      9
strings, yielding 35 extra flows, most of which can be found         disabled; No inference: inference of types and structure
by the normal fuzzer given a sufficiently long timeout, except       disabled; N ODE M EDIC -MC +Fuzzer: the baseline condition
for 5 that crash due to out of memory.                               with the fuzzer; and N ODE M EDIC -MC. Below, we discuss the
   Object reconstruction contributed to finding 228 extra            impact of each condition.
potential flows. These were cases where the packages required           The ACI polyglot contributes 8% to the increase in confirmed
inputs to be objects having a certain structure, similar to our      flows over N ODE M EDIC -MC +Fuzzer. The increase is due
example in Section II. Disabling object reconstruction also          to the polyglot being able to bypass weak shell expansion
allows the fuzzer to find 34 extra flows. The limited time budget    sanitization in the package (Section IV-B). The three extra
for fuzzing causes this; 26 of these 34 flows can be found by        flows found when disabling the polyglot break down to two
the full fuzzer with longer timeouts while the remaining 8 cases     cases where the polyglot leads to invalid shell code (e.g., a
crash due to out of memory. Sometimes the coverage-guidance          bash loop), and one case where SMT solving with the polyglot
that object reconstruction uses leads the fuzzer away from           times out.
generating inputs that trigger potential flows. For example,            Inferring (non-string) types yields a 15% increase in con-
one package prints an error and does not call the sink if            firmed flows over N ODE M EDIC -MC +Fuzzer. Through manual
a certain attribute is present in the user input. The object         examination, we find that inferring array types is the key
reconstruction will generate these attributes as coverage would      factor in the increase. Finally, inference of types and structure
increase; however, the absence of those attributes is needed for     together contributes 77% to the increase in confirmed flows
triggering the potential flow. A flow is found in this package       over N ODE M EDIC -MC +Fuzzer. In these cases, the common
by the fuzzer with object reconstruction capability disabled.        pattern is that a structured object is required as input and a field
 Result 1a: Type- and object-structure aware fuzzing uncovers        of the object is used in the call to the sink. Without inferring
 2257 potential flows; 1.7x the flows of N ODE M EDIC -              this structure and inserting the synthesized exploit payload at
 MC. Object reconstruction is necessary to find 228 flows.           the correct field, the payload fails to reach the sink.
 Generating diverse types yields 391 more flows compared                The confirmed ACI flows missed by N ODE M EDIC -FINE
 to generating only strings.                                         without inference of types and structure and the polyglot are the
                                                                     same flows missed by N ODE M EDIC -MC +Fuzzer. The extra
   We examine the impact of generating each input type during
                                                                     flows found by N ODE M EDIC -MC +Fuzzer and N ODE M EDIC -
fuzzing and summarize the results in Table II. Each row
                                                                     MC are due to disabling the polyglot.
indicates how many flows we miss by running a version
                                                                        A case study of a real package mirroring our example in
of N ODE M EDIC -FINE where the fuzzer can not generate
                                                                     Section IV-C can be found in the Appendix A-D. Inference
a specific type.
                                                                     of types and structure increases the complexity of the SMT
   Most flows can be triggered by more than a single input type,
                                                                     formulae and synthesized package input, but does not introduce
due to how loosely typed JavaScript is. This is why the total
                                                                     a performance bottleneck on average (Appendix A-E).
of flows missed only goes up to 1085 instead of the total 2257
potential flows found. Clearly, strings and objects are the most      Result 2: N ODE M EDIC -FINE’s improvements to ACI
important to be generated, otherwise we would miss 609 and            confirmation are attributable to the ACI polyglot (8%),
243 flows, respectively. The ability of the fuzzer to generate        inferring non-string types (15%), and inferring structure
other types also contribute to the discovery of a reasonable          (77%), yielding a total increase of 39 flows over baseline.
number of flows. Take the 34 cases where functions need to be         These flows correspond to packages requiring specific types
passed to the packages as an example, most of those packages          (avg: 1.3 types synthesized) and structure (avg: 1.2 fields
take, as argument, a callback that is called before the sink, and     synthesized).
would crash if that argument is not a function.
                                                                     Synthesis limitations for ACI. We manually triaged the
 Result 1b: By generating a variety of types the fuzzer has
                                                                     top 100 packages, ranked by weekly downloads, where our
 the ability to discover flows that it would otherwise miss.
                                                                     infrastructure found a potential flow but failed to generate an
                                                                     exploit, yet the package was exploitable.
                                                                        Of these, 79% were spawn sinks. In the majority (61%) of
D. RQ2: Inference Performance                                        cases, failure was due to the lack of support for synthesizing
   We present evaluation results on the impact of the ACI            multiple (two) inputs to the package; exploiting the spawn
polyglot (Section IV-B) and type and structure inference             sink requires control of a command string and an options
(Section IV-C), and discuss their limitations.                       object. Furthermore, it is only possible to exploit spawn if the
Impact of ACI polyglot and inference. Table IV reports               shell flag is set to true in the options object, the lack of
extra, missing, and total counts of automatically confirmed ACI      which resulted in 15% of failures. The remaining cases for
flows across six conditions: N ODE M EDIC -FINE: inference           spawn required additional type information (4%), synthesis
of types and structure, ACI polyglot enabled; No polyglot:           of a specific payload (3%), or a package specific issues (5%;
instead of using the ACI polyglot, we use N ODE M EDIC’s             e.g., requiring a particular input for a git command, executing
input $(touch /tmp/success); No type inf.: inference of types        Python code).



                                                                10
                 TABLE II: Potential flows missed by the fuzzer when it can not generate inputs of a given type.
  Type Removed        Strings   Objects        Arrays          Functions       Numbers    Regexes      Booleans   BigInts   Nulls   Undefined   Symbols   Total
  Flows missed        609       243            62              34              25         24           20         19        17      16          16        1085



TABLE III: Potential flows found by the fuzzer with varied                                impact of disabling several N ODE M EDIC -FINE components
configurations. Extra and missing flows are relative to the ones                          individually in the confirmation of ACE flows. We report the
found by N ODE M EDIC -FINE.                                                              number of extra, missing and total counts of automatically
           Condition                  Extra         Missing         Total                 confirmed ACE flows across five conditions: N ODE M EDIC -
           N ODE M EDIC -FINE         -             -               2257                  FINE: uses polyglot and Enumerator; No Enumerator: uses
           No ObjRecon                34            228             2063                  polyglot; No Polyglot: uses Enumerator; N ODE M EDIC -MC
           No Types                   35            391             1901                  +Fuzzer and N ODE M EDIC -MC.
                                                                                          ACE polyglot. Our ACE polyglot (Section IV-B) is more effec-
           N ODE M EDIC -MC           0             919             1338
                                                                                          tive than using a simpler exploit global.CTF();//, increasing the
                                                                                          number of confirmed flows from 128 to 154. The improvements
TABLE IV: Impact of inference of types and structure on ACI                               are in situations where the payload is injected inside a string
confirmed flows. Fuzzer with object reconstruction enabled                                value and insufficient sanitization measures allow an attacker
except for N ODE M EDIC -MC. Extra and missing flows are                                  to escape that context.
relative to N ODE M EDIC -FINE.                                                           Impact of completing prefixes. The Enumerator was called
       Condition                           Extra        Missing        Total
                                                                                          for 328 prefixes (205 unique) and came up with a valid prefix
       N ODE M EDIC -FINE                  -            -              612                completion for 191 (58%) of those cases.
       No polyglot                         3            6              609                   The Enumerator contributes to 27 confirmed ACE flows.
       No type inf.                        0            6              606                All 27 cases required a complex payload to be constructed,
       No inference                        0            35             577                involving the insertion of the payload in the right place,
                                                                                          escaping the necessary contexts at the right time and, in some
       N ODE M EDIC -MC +Fuzzer            3            39             576                cases, an extra suffix concatenated after the prefix and our
       N ODE M EDIC -MC                    2            218            396                payload. An example is given in Appendix A-C.
                                                                                             We manually inspected 8 out of the 153 packages that we
TABLE V: Confirmed ACE flows found while enabling or                                      completed the prefix but could not automatically exploit. Four
disabling several components of synthesis. Fuzzer with object                             were not exploitable. Of the remaining 4, 2 had such intricate
reconstruction was enabled for all of these. Extra and missing                            constraints that Z3 timed out, as they involved solving for
flows are relative to the ones found by N ODE M EDIC -FINE.                               inputs that passed through a JavaScript parser called jsep before
        Condition                          Extra          Missing      Total              reaching the sink or exploiting a stack machine; 1 package
        N ODE M EDIC -FINE                 -              -            154                required a model for the slice operation where the length is
        No Enumerator                      0              27           127                symbolic to successfully construct the SMT statement for Z3;
        No Polyglot                        0              26           128
                                                                                          and 1 package required a call to the function returned by the
                                                                                          entry point with an object argument. In all these cases, the
        N ODE M EDIC -MC +Fuzzer           3              54           103                Enumerator synthesized a valid completion but there were
        N ODE M EDIC -MC                   1              88           67                 additional challenges that N ODE M EDIC -FINE would need to
                                                                                          overcome to create a working exploit.
                                                                                            Result 3: The Enumerator helped N ODE M EDIC -FINE
   For exec-like sinks, which only require control over a single                            complete the majority of real world prefixes that we found
string input and have shell evaluation on by default, synthesis                             in ACE flows, increasing the number of total confirmed ACE
failed for 21% of the packages. These cases generally required                              flows by 21%.
a more complex nested structure for the input along a path not
found by the fuzzer (52%), synthesizing a specific non-payload                                Limitations of the Enumerator. The Enumerator failed to
input string, e.g., a valid file path or a specific character (14%)                           complete the prefix for 137 packages with ACE flows. This was
bypassing sanitization (10%), additional type information (5%),                               most commonly due to the need to complete JavaScript code
or a combination of these (14%).                                                              that contained primitives not supported by our Enumerator.
   We provide more details for the limitations described above,                               Lacking support for loops, nested objects, boolean expressions
as well as details for the remaining corner cases for both spawn                              and the += operator caused 63 out of these 137 failures. There
and exec sinks, in Appendix A-F.                                                              were 12 cases where the prefix could not be completed even
                                                                                              by a perfect Enumerator, because our synthesis algorithm does
E. RQ3: Enumerator Performance                                                                not handle multiple inputs (Section A-F). The argument to the
  We report on the effectiveness of our ACE polyglot and the                                  sink was a combination of constant strings from the package
Enumerator in confirming ACE flows. Table V summarizes the                                    and several attacker controlled inputs. When the prefix was



                                                                                         11
TABLE VI: SecBench.js eval results comparing N ODE M EDIC -                              TABLE VII: True and false positive rates for both confirmed
FINE (NM-F) with FAST in terms of potential (Pot.) and                                   flows and potential flows N ODE M EDIC -FINE fails to confirm.
confirmed (Conf.) flows. Valid packages are downloadable,                                                              Confirmed          Un-Confirmed
have a main executable file defined, and the vulnerability fits                                     Sink Type          TP        FP         TP      FP
in the attacker model that we share with FAST. Executable are                                          ACI         64 (50 new)   40          0      14
                                                                                                       ACE          6 (6 new)     3      4 (3 new)  11
packages that are valid and can be installed and run.
    Type     Valid     Executable      Pot.      Pot.      Conf.      Conf.
                                       NM-F      FAST      NM-F       FAST
    ACI      91        87              51        65        44         41
                                                                                           Result 4: N ODE M EDIC -FINE is comparable to state-of-the-
                                                                                           art tool FAST in automatically detecting and synthesizing
    ACE      34        25              17        10        5          0
                                                                                           real-world vulnerabilities. N ODE M EDIC -FINE excels at
    Total    125       112             68        75        49         41                   confirming ACE flows, which are typically hard to confirm.

                                                                                         G. Developer responses
passed from the synthesis algorithm to the Enumerator, it was
already impossible to be completed. The remaining 62 cases                              So far, we have triaged 622 confirmed flows (567 ACI + 55
needed a diverse set of JavaScript primitives to be supported by                     ACE). We emailed the developers of all vulnerabilities that
the Enumerator, including but not limited to class definitions,                      we considered to be new true positives (270 ACI + 19 ACE).
try/catch statements and generator functions.                                        As of the time of writing, we received 56 responses (50 ACI
Anomalous cases. N ODE M EDIC -MC +Fuzzer, having no in-                             + 6 ACE). 2 developers said they did not agree it was a true
ference, confirmed 3 extra flows for all of which N ODE M EDIC -                     vulnerability because the attacker model did not apply to them,
FINE’s inference generated a malformed SMT formula (Ap-                              as they considered impossible for an attacker to control the
pendix A-F). The fuzzer was needed to find the potential flow in                     entry point’s arguments. The remaining 54 developers agreed
two of them, but the third one was found by N ODE M EDIC -MC                         that the reported vulnerabilities were real (48 ACI + 6 ACE).
too, which resulted in its 1 extra flow.                                             So far, 35 of these have been patched and a new version of
                                                                                     the package is published. In 12 other cases, developers asked
F. RQ4: Comparison with FAST                                                         for more time to fix the vulnerability. For the remaining 7
   Table VI compares N ODE M EDIC -FINE with FAST [16]                               vulnerabilities, the developers agreed it should be patched but
by reporting the flows found by running each tool on the                             said they do not have time to do so.
SecBench.js dataset comprised of 101 ACI and 40 ACE real
                                                                 H. Previously Unidentified Vulnerabilities
world vulnerabilities. We ran FAST and N ODE M EDIC -FINE
against 91 ACI and 34 ACE that fit our attacker model and 3         We report on N ODE M EDIC -FINE’s true and false positive
were downloadable and had a main executable file defined in rates of identifying true vulnerabilities. A vulnerable flow is an
package.json. From those, only 87 ACI and 25 ACE could be exploitable, truly illegitimate behavior according to the package
installed and run. As a dynamic analysis tool, N ODE M EDIC - functionality.
FINE can only analyze those cases, but being executable is          We sample 113 flows automatically confirmed to be ex-
not a prerequisite for FAST to find potential flows so we report ploitable   by N ODE M EDIC -FINE and 29 flows from the most
all results of FAST on valid packages.                           popular    packages   where a potential flow was identified but not
   The most frequent reason why FAST finds a higher number       automatically      confirmed,  and we manually examine whether
of potential flows is because unlike N ODE M EDIC -FINE it       they  are  vulnerable.   Results  are summarized in Table VII. The
does not need to come up with an input that follows the          number     for  the true positives  in the parenthesis is previously
potentially vulnerable path. There were also 3 cases where       unreported     new   vulnerabilities.
FAST exclusively found a flow because N ODE M EDIC -FINE            In all, 70 out of the 113 flows are truly vulnerable. Two
suffered from an undertainting issue. N ODE M EDIC -FINE         of  the false positives are in packages that warn users not
performs well with respect to confirmed flows, specially ACE     to  pass   unsanitized inputs to vulnerable entry points. Two
vulnerabilities. FAST generated candidate exploits for 4 ACEs    other  packages     were vulnerable, but deprecated. The remaining
that ended up not executing the payload because the final        39  cases   were    packages that exposed a sink directly or the
argument to the sink was not valid JavaScript. Our enumerator    vulnerable     entry  point was intended for arbitrary command
allowed us to get past that problem in those cases. FAST         execution.    3   packages  had real vulnerabilities in a different
failed to synthesize the right type for one of the arguments of  entry  point,   which   N ODE M EDIC -FINE did not explore.4
the vulnerable entry point of the package macaddress@0.2.8,         Most of the vulnerabilities are due to a lack of sanitization.
which needed to be a function. N ODE M EDIC -FINE eventually     Two    have inadequate sanitization, which is bypassed by
generated a function for that argument and was able to create    inputs   generated by N ODE M EDIC -FINE. We were assigned
a working exploit.                                               1 CVE     [29]. Out of 54 packages with acknowledged vulnera-
                                                                 bilities, 25 have weekly downloads in the range (0, 10], 12 in
   3 One discarded vulnerability required the command-line arguments to be
attacker-controlled. The remaining vulnerabilities were not exploitable from                4 This was because N ODE M EDIC -FINE stops at the first potential flow it
the main package entry points but rather from an internal library’s entry points.        finds, which in these cases was not the ideal flow to exploit




                                                                                    12
(10, 100], 7 in (100, 1000], 3 in (1000, 3000] and the remaining    tainted paths from different inputs are indistinguishable. Second,
7 with >3K weekly downloads were submitted to Snyk, by              the inference does not handle merging of abstract values from
whom the developers are being contacted. We are in the process      multiple tainted paths. As future work, we will include support
of responsibly disclosing the remaining true positives.             for multiple kinds of taint by modifying the underlying taint
   Among the 4 ACE vulnerabilities, 1 needs a more sophisti-        map and propagation. We will also extend the inference to
cated exploit driver with multiple interactions with the API to     distinguish abstract values from different inputs and only merge
exploit the flow; 1 has complex SMT constraints and Z3 outputs      those from the same input.
unknown; and 2 packages needed the Enumerator to support class      Shell string completion. To handle all cases (e.g., including
definitions and passing object arguments in function calls.         sanitization) associated with synthesizing ACI shell code
   The ACI false positives were discussed in Section V-D. For       payloads that complete a shell string prefix or suffix, we would
ACE false positives, 1 was due to overtainting; 5 had proper        need a methodology similar to the Enumerator for ACE.
sanitization; 2 packages were deprecated; and 1 package called      More complex drivers. N ODE M EDIC -FINE does not generate
the function constructor but the resulting function was never       sophisticated drivers needed for confirm flows where an exploit
used. In the remaining 2 packages the inputs to the package API     is only triggered if sequences of package API calls are
are a boolean or a number which can not contain a command           performed, or handlers or external interactions (e.g., with the
or code to be injected in the sink.                                 network, a database, or the file system) are executed. Prior client
                                                                    and server-side JavaScript taint analysis work has encountered
           VI. L IMITATIONS AND F UTURE W ORK                       similar limitations [9, 10, 22, 23, 32]. Beyond improving driver
   In this section we discuss limitations of our analysis and       generation, one could analyze instead, packages that have
future work to improve N ODE M EDIC -FINE.                          simpler driver requirements and calls entry points of those
Fixing vulnerabilities While our tool is designed to auto-          packages that require complex drivers.
matically exploit vulnerabilities, it can not automatically fix     Multiple flows in the same package. N ODE M EDIC -FINE
them. An effective mitigation for ACI is to use the more            stops after finding the first flow for each package, causing the
secure function execFile, which allows developers to properly       analysis to miss vulnerabilities in a package if the package
separate the binary or command to execute from its potentially      has multiple flows and the first one is a false positive. This is
user-influenced flags. For ACE, avoiding calling dynamic code       not a fundamental limitation of N ODE M EDIC -FINE; we can
execution functions like eval with user-controlled arguments is     implement an iterative pipeline to analyze all flows.
best, otherwise proper input sanitization is paramount.
Missing information from instrumentation-based analysis.                             VII. R ELATED W ORK
The inference methodology is limited by the underlying              N ODE M EDIC -FINE uses N ODE M EDIC’s underlying dy-
instrumentation-based dynamic analysis [21, 40] because it namic taint analysis engine to identify potential flows and to
relies on the provenance graph, constructed by the underlying output important runtime information used for synthesizing
analysis. Imprecise or incomplete information typically result proof-of-concept exploits. In the domain of detecting code-
from uninstrumented code, which can appear in native opera- injection vulnerabilities in Node.js packages, some tools have
tions not implemented in JavaScript or functions imprecisely used similar dynamic taint tracking techniques [9, 10, 40], while
analyzed by the underlying analysis for scalability concerns. others used static approaches [11, 12, 13, 14, 15, 16, 17, 44, 45].
Leveraging information from static analysis could further The synthesis algorithm depends on the output from the
improve N ODE M EDIC -FINE.                                      dynamic taint analysis, which can be obtained by other tools
SMT models of JavaScript operations. An inherent limitation in the same category [9, 10, 40]. Thus, N ODE M EDIC -FINE’s
of constraint-based synthesis is its dependence on bespoke SMT synthesis methodology is generally applicable and can be
models for JavaScript operations, which are time-consuming implemented for these tools as well.
and error-prone to create due to quirks in the JavaScript           The dynamic taint tracking is not a contribution of
language semantics. For instance, JavaScript’s implicit coercion N ODE M EDIC -FINE, so we focus on closely related work
must be added to the SMT models on a per-operation basis in fuzzing and synthesis in the context of JavaScript.
because these coercions happen within the JavaScript engine General-purpose fuzzers adapted for Node.js. Fuzzing tools
and not visible to the instrumentation-based analysis. A related like AFL [46] have been adapted for Node.js fuzzing [47].
limitation, shared by prior work that applies SMT-solving These general-purpose tools predominantly generate byte
techniques towards JavaScript analysis [41, 42, 43], is that the sequences or strings, lacking intrinsic knowledge of JavaScript’s
SMT solver may fail to find a solution within a reasonable rich type system. While effective in many scenarios, searching
time limit. Regular expression operations are known to be the string space only is not sufficient to uncover a significant
challenging to solve [41].                                       number of vulnerabilities. N ODE M EDIC -FINE’s fuzzer is type-
Multi-input synthesis. The inference methodology works and structure-aware and can generate inputs of a variety of
poorly when more than one tainted inputs are given to the types and with complex structure, like objects with specific
package API due to the following two limitations of the current attributes that have to be themselves objects.
infrastructure. First, the dynamic taint analysis infrastructure JavaScript-specific fuzzers. Some approaches for input gener-
does not distinguish between multiple kinds of taint; thus, ation rely on package tests or even tests from its dependents



                                                               13
to improve coverage in Node.js packages [48]. However, provenance graph encodes constraints on operations (not path
these tests do not always exist. JsFuzz [49] attempts to constraints) that solve for structured inputs and ensure the
create coverage-guided JavaScript-specific fuzzing tools by exploit payload reaches the sink.
facilitating the generation of inputs more suitable for JavaScript    Closer to our approach, but applied to PHP, is the work
environments. However, their approach still heavily leans on of NAVEX [54], which uses a constraint-based approach to
string-based input generation and a manual creation of a generate exploits. NAVEX is similar in that it uses constraints
fuzz target. This may not effectively explore the breadth of to select exploit payloads, but unlike our work, it does so
JavaScript’s type system, which includes objects, arrays and by detecting uses of sanitization that would filter out certain
function types. We observed through manual triage of the found attacks in an attack dictionary. NAVEX is also different from
potential flows that there is a considerable number of cases our approach in that it does not use dynamic provenance
of vulnerable entry points that expect a function as one of the information, rather it uses path constraints to model vulnerable
arguments. These would never be found fully automatically by paths in a PHP application; it leverages Z3 to solve for inputs
state of the art fuzzers without knowing beforehand that one of that jointly satisfy path constraints and constraints on the input
the generated sequence of bytes would have to be transformed to contain acceptable strings from the attack dictionary.
or replaced into a function.
SMT-based JavaScript exploration. While fuzzing helped                                   VIII. C ONCLUSION
N ODE M EDIC -FINE to explore more execution paths of a               By leveraging type and object-structure information gathered
JavaScript program, another commonly used method for at runtime, N ODE M EDIC -FINE is able to explore more
program exploration is symbolic execution [30]. Several works execution traces to identify more potential flows. The type- and
perform symbolic execution for JavaScript [18, 41, 42] but the structure-inference together with the Enumerator component,
technique’s limited scalability [50] conflicts with our goal of which is capable of completing prefixes to valid Javascript
performing a large scale analysis on npm packages.                 syntax, significantly improve the performance of the proof-of-
Synthesis. Several works use the JavaScript grammar to concept exploit generation.
generate syntactically valid code [51, 52, 53] for fuzzing
JavaScript interpreters. In comparison, our synthesis technique                          ACKNOWLEDGMENT
works at a finer granularity of syntactic constructions using
SMT constraints: rather than generating numerous code chunks          This work is supported in by the Future Enterprise Se-
that are valid syntactically and semantically, but are arbitrary   curity  Initiative at Carnegie Mellon CyLab (FutureEnter-
in their content, we need to synthesize specific sequences that    prise@CyLab),     Carnegie Mellon CyLab, and Fundação para
bypass manipulation and deliver the payload.                       a  Ciência   e  a Tecnologia  (UIDB/50008/2020, Instituto de
   Most prior work on JavaScript exploit synthesis targets cross-  Telecomunicações,    and PhD   grant SFRH/BD/150692/2020).
site scripting vulnerabilities [22, 23, 24, 25, 26]. They parse
                                                                                            R EFERENCES
the AST of the statement reaching the sink to construct an
exploit [22, 23, 24]. While feasible for webpages because            [1] “Npm passes the 1 millionth package milestone! What
global input sources (e.g., URL parameters) are accessed near            can we learn?” 2021, http://tinyurl.com/npm-1-millionth.
the sink; it does not work for Node.js packages, where inputs        [2] P. Muncaster, “Open Source Supply Chain Attacks Surge
are local and are often transformed before reaching the sink.            430%,” 2020, https://www.infosecurity-magazine.com/
   Several works use SMT solvers to synthesize exploits [27,             news/open-source-supply-chain-attacks/.
54, 55]. FAST [16] first generates a control-flow and an object      [3] D. Jang, R. Jhala, S. Lerner, and H. Shacham, “An
dependence graph through abstract interpretation and finds a             empirical study of privacy-violating information flows
path between entry points and sink functions. It then generates          in JavaScript web applications,” in Proceedings of the
a data flow that follows that control flow path, and solves              17th ACM Conference on Computer and Communications
constraints collected from both the data flow, the control flow          Security, 2010.
path and the object dependence graph. FAST’s synthesis uses          [4] M. Zimmermann, C.-A. Staicu, C. Tenny, and M. Pradel,
only information from static analysis and thus the synthesis             “Small World with High Risks: A Study of Security
constraints may miss important dynamic information, e.g.,                Threats in the npm Ecosystem,” in Proceedings of the
more than 90% of FAST’s false negatives are due to the lack              28th USENIX Security Symposium (USENIX Security 19),
of modeling of built-in functions, which come for free in                2019.
N ODE M EDIC -FINE since it executes the package.                    [5] C.-A. Staicu, D. Schoepe, M. Balliu, M. Pradel, and
   In the domain of JavaScript synthesis, PMForce [27] synthe-           A. Sabelfeld, “An Empirical Study of Information Flows
sizes ACE exploits for the postMessage API’s event object. PM-           in Real-World JavaScript,” in Proceedings of the 14th
Force gathers and uses path constraints to fill exploit templates        ACM SIGSAC Workshop on Programming Languages and
used for event.data. Like the underlying N ODE M EDIC [21],              Analysis for Security, 2019.
Our analysis also uses templates, but these encode ACE or            [6] L. Gong, “Dynamic analysis for javascript,” Ph.D. dis-
ACI-specific breakouts, and in the case of ACE are produced              sertation, EECS Department, University of California,
by the syntactic analysis of the Enumerator. Moreover, the               Berkeley, 2018.



                                                               14
 [7] N. Zahan, T. Zimmermann, P. Godefroid, B. Murphy,                     graphs,” in 2023 IEEE 8th European Symposium on
     C. Maddila, and L. Williams, “What are weak links in                  Security and Privacy (EuroS&P), 2023.
     the npm supply chain?” in 2022 IEEE/ACM 44th Inter-              [22] S. Lekies, B. Stock, and M. Johns, “25 million flows
     national Conference on Software Engineering: Software                 later: Large-scale detection of DOM-based XSS,” in
     Engineering in Practice (ICSE-SEIP), 2022.                            Proceedings of the 2013 ACM SIGSAC Conference on
 [8] R. Duan, O. Alrawi, R. P. Kasturi, R. Elder, B. Saltafor-             Computer & Communications Security, 2013.
     maggio, and W. Lee, “Towards measuring supply chain              [23] I. Parameshwaran, E. Budianto, S. Shinde, H. Dang,
     attacks on package managers for interpreted languages,”               A. Sadhu, and P. Saxena, “DexterJS: Robust testing plat-
     in 28th Annual Network and Distributed System Security                form for DOM-based XSS vulnerabilities,” in Proceedings
     Symposium, NDSS, 2021.                                                of the 2015 10th Joint Meeting on Foundations of Software
 [9] R. Karim, F. Tip, A. Sochurkova, and K. Sen, “Platform-               Engineering, 2015.
     Independent Dynamic Taint Analysis for JavaScript,”              [24] S. Bensalim, D. Klein, T. Barber, and M. Johns, “Talking
     IEEE Transactions on Software Engineering, 2018.                      about my generation: Targeted dom-based xss exploit
[10] F. Gauthier, B. Hassanshahi, and A. Jordan, “AFFOGATO:                generation using dynamic data flow analysis,” in Proceed-
     Runtime detection of injection attacks for Node.js,” in               ings of the 14th European Workshop on Systems Security,
     Companion Proceedings for the ISSTA/ECOOP 2018                        2021.
     Workshops, 2018.                                                 [25] B. Garmany, M. Stoffel, R. Gawlik, P. Koppe, T. Blazytko,
[11] M. Madsen, F. Tip, and O. Lhoták, “Static analysis                    and T. Holz, “Towards automated generation of exploita-
     of event-driven Node.js JavaScript applications,” ACM                 tion primitives for web browsers,” in Proceedings of the
     SIGPLAN Notices, 2015.                                                34th Annual Computer Security Applications Conference,
[12] C.-A. Staicu, M. T. Torp, M. Schäfer, A. Møller, and                  2018.
     M. Pradel, “Extracting Taint Specifications for JavaScript       [26] Y. Frempong., Y. Snyder., E. Al-Hossami., M. Sridhar.,
     Libraries,” in 2020 IEEE/ACM 42nd International Con-                  and S. Shaikh., “Hijax: Human intent javascript xss
     ference on Software Engineering (ICSE), 2020.                         generator,” in Proceedings of the 18th International
[13] S. Li, M. Kang, J. Hou, and Y. Cao, Detecting Node.Js                 Conference on Security and Cryptography - SECRYPT,,
     Prototype Pollution Vulnerabilities via Object Lookup                 2021.
     Analysis, 2021.                                                  [27] M. Steffens and B. Stock, “PMForce: Systematically ana-
[14] C.-A. Staicu, M. Pradel, and B. Livshits, “SYNODE:                    lyzing postMessage handlers at scale,” in ACM Conference
     Understanding and Automatically Preventing Injection                  on Computer and Communications Security, 2020.
     Attacks on NODE.JS,” in NDSS, 2018.                              [28] CERT, “The CERT guide to coordinated vulnerability
[15] S. Li, M. Kang, J. Hou, and Y. Cao, “Mining node.js                   disclosure,” 2023, https://vuls.cert.org/confluence/display/
     vulnerabilities via object dependence graph and query,”               CVD.
     in 31st USENIX Security Symposium (USENIX Security               [29] “CVE-2024-21488,” Available from Snyk, Snyk-ID
     22), 2022.                                                            SNYK-JS-NETWORK-6184371, Jan. 2024, https://
[16] M. Kang, Y. Xu, S. Li, R. Gjomemo, J. Hou, V. N.                      security.snyk.io/vuln/SNYK-JS-NETWORK-6184371.
     Venkatakrishnan, and Y. Cao, “Scaling JavaScript abstract        [30] E. J. Schwartz, T. Avgerinos, and D. Brumley, “All you
     interpretation to detect and exploit node.js taint-style              ever wanted to know about dynamic taint analysis and
     vulnerability,” in IEEE Symposium on Security and                     forward symbolic execution (but might have been afraid to
     Privacy, 2023.                                                        ask),” in 2010 IEEE symposium on Security and privacy,
[17] M. Kluban, M. Mannan, and A. Youssef, “On detecting                   2010.
     and measuring exploitable JavaScript functions in real-          [31] E. Andreasen, L. Gong, A. Møller, M. Pradel,
     world applications,” ACM Transactions on Privacy and                  M. Selakovic, K. Sen, and C.-A. Staicu, “A Survey of
     Security, 2024.                                                       Dynamic Analysis and Test Generation for JavaScript,”
[18] F. Xiao, J. Huang, Y. Xiong, G. Yang, H. Hu, G. Gu,                   ACM Computing Surveys, 2017. [Online]. Available:
     and W. Lee, “Abusing hidden properties to attack the                  https://doi.org/10.1145/3106739
     node.js ecosystem,” in 30th USENIX Security Symposium            [32] I. Parameshwaran, E. Budianto, S. Shinde, H. Dang,
     (USENIX Security 21). USENIX Association, 2021.                       A. Sadhu, and P. Saxena, “Auto-patching DOM-based
[19] T. M. Corporation, “CWE - CWE-94: Improper Control                    XSS at scale,” in Proceedings of the 2015 10th Joint
     of Generation of Code (’Code Injection’) (4.3),” 2020–,               Meeting on Foundations of Software Engineering, 2015.
     https://cwe.mitre.org/data/definitions/94.html.                  [33] L. De Moura and N. Bjørner, “Z3: An efficient smt solver,”
[20] ——, “CWE - CWE-77: Improper Neutralization of                         in Proceedings of the 14th International Conference on
     Special Elements used in a Command (’Command Injec-                   Tools and Algorithms for the Construction and Analysis
     tion’) (4.3),” 2020–, https://cwe.mitre.org/data/definitions/         of Systems, 2008.
     77.html.                                                         [34] piercus, “Hasard,” https://www.npmjs.com/package/
[21] D. Cassel, W. T. Wong, and L. Jia, “NodeMedic: End-to-                hasard, 2020, npm package version 1.6.1.
     end analysis of node.js vulnerabilities with provenance          [35] K. Sen and M. Sridharan, “Jalangi2,” 2014–, https://github.



                                                                 15
     com/Samsung/jalangi2.                                               fragments,” in Presented as part of the 21st USENIX
[36] V. J. Manes, H. Han, C. Han, S. K. Cha, M. Egele,                   Security Symposium (USENIX Security 12), 2012.
     E. J. Schwartz, and M. Woo, “Fuzzing: Art, science, and [52] S. Veggalam, S. Rawat, I. Haller, and H. Bos, “Ifuzzer:
     engineering,” arXiv preprint arXiv:1812.00140, 2018.                An evolutionary interpreter fuzzer using genetic program-
[37] D. Cassel, N. Sabino, M.-C. Hsu, R. Martins,                        ming,” in European Symposium on Research in Computer
     and L. Jia, “NodeMedic-FINE: Automatic detec-                       Security, 2016.
     tion and exploit synthesis for node.js vulnerabilities [53] H. Han, D. Oh, and S. K. Cha, “Codealchemist: Semantics-
     (technical report),” Carnegie Mellon Kilthub, 2024,                 aware code generation to find vulnerabilities in javascript
     DOI:10.1184/R1/27901461.                                            engines.” in Network and Distributed System Security,
[38] C. Martín-Vide, V. Mitrana, and G. Păun, Formal lan-               2019.
     guages and applications. springer, 2013, vol. 148.           [54] A. Alhuzali, R. Gjomemo, B. Eshete, and V. N. Venkatakr-
[39] M. H. M. Bhuiyan, A. S. Parthasarathy, N. Vasilakis,                ishnan, “NAVEX: precise and scalable exploit generation
     M. Pradel, and C.-A. Staicu, “Secbench. js: An executable           for dynamic web applications,” in Proceedings of the 27th
     security benchmark suite for server-side javascript,” in            USENIX Conference on Security Symposium, ser. SEC’18,
     2023 IEEE/ACM 45th International Conference on Soft-                2018.
     ware Engineering (ICSE). IEEE, 2023, pp. 1059–1070. [55] S. Park, D. Kim, S. Jana, and S. Son, “{FUGIO}:
[40] K. Sen, S. Kalasapur, T. Brutch, and S. Gibbs, “Jalangi: A          Automatic exploit generation for {PHP} object injection
     selective record-replay and dynamic analysis framework              vulnerabilities,” in 31st USENIX Security Symposium
     for JavaScript,” in Proceedings of the 2013 9th Joint              (USENIX Security 22), 2022.
     Meeting on Foundations of Software Engineering, 2013.
[41] B. Loring, D. Mitchell, and J. Kinder, “ExpoSE: Practical                               A PPENDIX A
     symbolic execution of standalone JavaScript,” in SPIN                      A DDITIONAL    E VALUATION D ETAILS
     2017, 2017.                                                  A. Gathering of Evaluation Dataset
[42] J. F. Santos, P. Maksimović, T. Grohens, J. Dolby, and          From the (>2M) packages in npm as of October 2023,
     P. Gardner, “Symbolic Execution for JavaScript,” in we gathered those that have at least 1 weekly download
     Proceedings of the 20th International Symposium on (1,732,536 packages). In Figure 9, we show the number of
     Principles and Practice of Declarative Programming, packages that get filtered out at each stage of the gathering
     2018.                                                        pipeline, until we are left with 33011 packages; our evaluation
[43] J. Fragoso Santos, P. Maksimović, G. Sampaio, and dataset. setupPackage ensures the package can be downloaded.
     P. Gardner, “JaVerT 2.0: Compositional symbolic ex- filterByMain filters out packages that can not be imported
     ecution for JavaScript,” Proceedings of the ACM on because they do not define a main file. filterBrowserAPIs filters
     Programming Languages, 2019.                                 out packages that depend on browser APIs. The filterSinks
[44] N. Patnaik and S. Sahoo, “Javascript static security discards packages that do not contain calls to ACE or ACI
     analysis made easy with JSPrime,” in Blackhat USA, sinks visible to static analysis. Note that we also check if any
     2013.                                                        of the dependencies have calls to sinks. setupDependencies
[45] O. Tripp, M. Pistoia, S. J. Fink, M. Sridharan, and O. Weis- filters out packages whose dependencies fail to download or
     man, “TAJ: Effective taint analysis of web applications,” install. getEntryPoints discards packages that do not have any
     in Proceedings of the 30th ACM SIGPLAN Conference public entry points defined. We gather metrics and annotate
     on Programming Language Design and Implementation, dependencies to not instrument in the annotateNoInstrument
     2009.                                                        stage. Finally, in runJalangiBabel we instrument the package
[46] M. Zalewski, “American Fuzzy Lop (AFL),” 2024, soft- code using Jalangi.
     ware available from http://lcamtuf.coredump.cx/afl/.
[47] AFLFuzzJS, “afl-fuzz-js: A JavaScript Port of the Ameri- B. Analysis Timeout
     can Fuzzy Lop Fuzzer,” 2014, https://github.com/tunz/afl-        We have a hard timeout of 2 minutes for fuzzing. Figure
     fuzz-js.                                                     10 shows that after 30 seconds we start to have diminishing
[48] H. Sun, A. Rosà, D. Bonetta, and W. Binder, “Automat- returns on the number of total potential flows found. We would
     ically assessing and extending code coverage for npm not expect to find a large enough number of new potential
     packages,” in 2021 IEEE/ACM International Conference flows if we increased the timeout further.
     on Automation of Software Test (AST), 2021, pp. 40–49.
[49] JSFuzz, “Jsfuzz,” GitHub repository, 2020, available at: C. Enumerator Graph and Example
     https://github.com/fuzzitdev/jsfuzz.                             A section of the Enumerator graph representing the
[50] R. Baldoni, E. Coppa, D. C. D’elia, C. Demetrescu, and JavaScript language is shown in Figure 11. Several nodes
     I. Finocchi, “A survey of symbolic execution techniques,” are shown, including Root. Edges between nodes are such
     ACM Computing Surveys (CSUR), vol. 51, no. 3, pp. 1–39, that we can confidently build syntactically valid JavaScript
     2018.                                                        statements by traversing the graph. Note that a graph traversal is
[51] C. Holler, K. Herzig, and A. Zeller, “Fuzzing with code stateful and following an edge changes the state. State changes



                                                                16
                                                                            1   // Code showing the sink call
                                                                            2   return new Function("x",
                                                                            3     "with (x) { return " + user_input + " } ")
                                                                            4   // Prefix
                                                                            5   with (x) { return
                                                                            6   // Completion
                                                                            7   [[ <payload>, <literal: ’}’> ]]
                                                                            8   // Exploit
                                                                            9   global.CTF()} //


                                                                           Fig. 12: Prefix, completion and the final exploit synthesized
                                                                           for a real world prefix

                                                                            1   exports.process = function(node, tree, cb) {...
                                                                            2     childproc.exec(
                                                                            3     "coffee -o " + node.out + " -c " + node.files,
                                                                            4     function() { e = arguments[0],
                                                                            5       out = arguments[1], err = arguments[2];
                                                                            6       return cb(e, out + ’\n’ + err); }); ...}


       Fig. 9: How many packages were filtered out, by stage.                      Fig. 13: b****@0** code vulnerable to ACI.

                                                                            1   {"id": "",
                                                                            2   "types": ["Object"],
                                                                            3   "structure": {
                                                                            4       "out": {
                                                                            5           "id": "c0a0f881",
                                                                            6           "types": ["Bot"],
                                                                            7           "structure": {}},
                                                                            8       "files": {
                                                                            9           "id": "bb7d142f",
                                                                           10           "types": ["Bot"],
                                                                           11           "structure": {}}}}


                                                                                  Fig. 14: Abstract value inferred for b****@0**.


                                                                           the currently parsed variable name ΓV is function or any other
                                                                           in a list of JavaScript keywords, thus we need the condition
                                                                           ΓV ∈/ keyword on that edge.
                                                                              To illustrate how the Enumerator works, we show in Figure
                                                                           12 an example of a prefix adapted from one of the 27 cases that
Fig. 10: How many flows would be found (y-axis) if we set                  the Enumerator successfully completed, together with the final
the fuzzing timeout to (x-axis in miliseconds).                            synthesized exploit. Note the closing brackets after the main
                                                                           payload, without which the exploit would be a syntactically
                                                                           invalid statement and would not execute.
       Root              Variable                  BinaryOp     ...        D. Inference ACI Case Study
 ...
                                                                    We present a case study of a package, b****@0**, sourced
                                                                 from our evaluation to illustrate the benefits of inference of
                    ReturnStmt                    Expression ... types and structure. The package takes a list of source input
                                                                 files and allows one to build CoffeScript files and output
                                                                 them to a directory. Our taint analysis detected a potentially
Fig. 11: A section of the graph representation of JavaScript vulnerable flow in the package’s process function, which
syntax used by the Enumerator. Edges have labels C; U where accepts a node argument whose two fields, out and files, are
C is a condition over the current character in the prefix c and passed unsanitized to the ACI sink exec as shown in Figure 13.
the context ΓV . U is a context update.                             Running our inference methodology on the package, we
                                                                 infer the abstract value shown in Figure 14. We can see that
                                                                 the out and files fields are inferred to be present on the input,
are labeled in Figure 11 on top of the edges, and encode which is inferred to be an object. The fields themselves are not
constraints that would be hard to represent with a simple graph. inferred to have any structure, indicating they are some non-
For example, function is an invalid variable name, therefore we extensible type. They are not specifically inferred to be strings
can not transition from the node Variable to node BinaryOp if because the package API does not perform any operations on



                                                                      17
 1   (declare-fun SymbolicField_bb7d142f () String)                      1   function doSpawn(method, command, args, options) {
 2   (declare-fun SymbolicField_c0a0f881 () String)                      2     ...
 3   (assert (str.contains (str.++ "coffee -o "                          3     var cpPromise = new ChildProcessPromise();
 4     SymbolicField_c0a0f881 " -c " SymbolicField_bb7d142f)             4     var reject = cpPromise._cpReject;
 5     " $(touch success);#"))                                           5     var resolve = cpPromise._cpResolve;
 6   (check-sat)                                                         6     var successfulExitCodes = (options
 7   (get-model)                                                         7       && options.successfulExitCodes) || [0];
                                                                         8     var cp = method(command, args, options);
      Fig. 15: SMT formula generated for b****@0**.
                                                                                  Fig. 17: Code snippet from c****@2**.
 1   try {
 2     var x0 = {"out": "B", "files": "$(touch success);#"};
 3     var x1 = undefined;
                                                                         1   return new Promise<string>(resolve => {
 4     var x2 = undefined;
                                                                         2     child_process.exec(‘yarn why ’${dep}’ --json‘,
 5     new PUT["process"](x0,x1,x2);
                                                                         3                         (err, output) => { ...
 6   } catch (e) { console.log(e); }

            Fig. 16: Exploit driver for b****@0**.                                Fig. 18: Code snippet from d****@1**.

TABLE VIII: Characteristics of ACI SMT formulae and
synthesized inputs generated by N ODE M EDIC -FINE with
inference of types and structure enabled.                               distinct required fields and 1 or 2 different types. However, the
                                                                        resulting formulae are compact and could be solved in under
             Characteristic             Measurement                     a second on average.
             SMT formula size (bytes)             256
             SMT symbolic input count             1.3
             Z3 solving time (ms)                21.8
             Synthesized field count              1.2                   F. Limitations of ACI Synthesis
             Synthesized value depth              0.9
             Inferred type count                  1.3
                                                                      We provide additional details on limitations of synthesis
                                                                  with inference of types and structure for ACI flows.
                                                                  Multi-input synthesis. Of the 100 exploitable flows, 48 of
them that would require them to be strings. At the same time,
                                                                  those packages had the spawn sink and accepted both a command
the type string is a valid type for these fields so our synthesis
                                                                  string and an options object that were passed to spawn. Under
methodology will treat them as strings.
                                                                  these conditions it is possible to exploit the sink if the shell
   Running our synthesis methodology on the package, we
                                                                  flag is passed in the options object. However, our synthesis
generate the SMT formula shown in Figure 15. We can see
                                                                  methodology does not support synthesizing two inputs to a
that the out and files fields are treated as strings, and the
                                                                  single sink (e.g., a payload as well as an options argument
SMT formula encodes the constraints that the first string must
                                                                  with the appropriate flag). Thus, we were unable to synthesize
be a completion of the prefix "coffee -o ", the second string
                                                                  exploits for these packages.
must be a completion of the prefix " -c ", and the concate-
nation of the symbolic and literal strings must contain the           To illustrate, consider the following example of the package
payload " $(touch success);#". Solving this with Z3, we obtain    c****@2**.        In Figure 17, we present a code snippet along the
the satisfying assignments SymbolicField_c0a0f881 = "B" and       exploitable     code   path of the package. The procedure on line
SymbolicField_bb7d142f = "$(touch success);#. Matching the        1  is  called  by   the  package’s entry point with the method to
assignments to the abstract value, we can derive the candidate    execute    (which    receives  a reference a function that calls spawn),
exploit input: {"out": "B", "files": "$(touch success);#"}.       as  well  as  a  command,     arguments,  and options that get passed
   Finally, we construct the exploit driver, which is shown in    directly    in  the   method    call on line  8. N ODE M EDIC -FINE
Figure 16; we can see that the driver simply constructs the       synthesizes      the  command      $(touch  /tmp/success);# , but does
candidate exploit input (line 2) and passes it to the package     not   synthesize   an  options  argument  of the form {’shell’:   true},

API (line 5). We run the exploit driver and confirm that the      thus   causing    the  exploit  payload’s shell  metacharacters  to not
exploit is successful by checking for the presence of the file    be   executed.
success, which is created.                                        Infrastructure and synthesis bugs. We encountered 10 cases
                                                                  where an exploit failed to be synthesized due to bugs. Two of
E. Complexity of Synthesis with Inference                         these cases were due to the generation of a malformed SMT
   To understand the impact of inference of types and structure formula, wherein the formula lacked a symbolic input to solve
on complexity of the SMT formulae and synthesized package for, thus preventing the generation of a payload. The remaining
input, in Table VIII, we examine relevant characteristics for two cases were due to bugs in processing synthesis results,
all flows with inference of types and structure enabled.          leading to valid synthesized payloads being lost. In both cases,
   The measurements show that results of synthesis produce if the synthesized payload was used, the flow would have been
package inputs that are not trivial, having typically 1 or 2 automatically confirmed.



                                                                   18
                        A PPENDIX B                                  D. Major Claims
                    A RTIFACT A PPENDIX                              This artifact provides two experiments that allow for repli-
A. Description & Requirements                                     cation of two underlying claims made by the paper:
   1) How to access: N ODE M EDIC -FINE can be found here:           • (C1) N ODE M EDIC -FINE is able to uncover potential
https://doi.org/10.5281/zenodo.14249091.                               Arbitrary Command Injection (ACI) flows in Node.js
   2) Hardware dependencies: 5 GB Storage, 4 GB RAM.                   packages, and can automatically synthesize exploits that
   3) Software dependencies: Tested operating systems: ma-             confirm their exploitability. This claim is supported by
cOS, Linux. Required software: Docker (≥ version 27).                  experiment E1. This corresponds to N ODE M EDIC -FINE’s
   4) Benchmarks: The evaluations in this paper involved: 1)           ability to find 1788 potential ACI flows in a large-scale
Large-scale collection of packages from the npm software               dataset and automatically confirm 612 of them, as reported
repository. While that dataset is not part of the artifact, we         in Section V.B.
include two experiments representative of it. 2) Evaluation over     • (C2) N ODE M EDIC -FINE is also able to find potential

the packages in SecBench.js. The dataset can be obtained at            Arbitrary Code Execution (ACE) flows in Node.js pack-
https://github.com/cristianstaicu/SecBench.js, but does not need       ages and automatically confirm their exploitability via
to be downloaded for the artifact experiments.                         automatic exploit synthesis. This claim is supported by
                                                                       experiment E2. This corresponds to N ODE M EDIC -FINE’s
B. Artifact Installation & Configuration                               ability to find 469 potential ACE flows in a larger-scale
   This section describes the installation steps required to set       dataset and automatically confirm 154 of them, as reported
up N ODE M EDIC -FINE. All the steps below are also described          in Section V.B.
in the README.md file present in the repo.                           The paper also makes claims about the large-scale effec-
Docker installation Issue the following command in the root tiveness of the N ODE M EDIC -FINE fuzzer, inference, and
of the project (in the same directory as the Dockerfile) to build enumerator approach to Node.js package exploit discovery and
the Docker container:                                             confirmation (Section V.B-E). Given that these claims manifest
docker build --platform=linux/amd64 -t nodemedic-fine .           through analysis of all packages in npm with more than 1
                                                                  weekly download (Section V.A), it is infeasible to replicate
   For reference, a fresh build takes around 3 minutes on a M1 those results without a similarly large dataset.
Mac. After building, the newly created image can be listed:
$ docker image ls                                                 E. Evaluation
REPOSITORY       TAG      IMAGE ID     CREATED       SIZE
nodemedic-fine   latest   5124b389f2b2 8 seconds ago 2.43GB             1) Experiment (E1): [ACI Flow] [5 human-minutes + 5
                                                                     compute-minutes]: In this experiment, N ODE M EDIC -FINE
Note for ARM-based systems When running the Docker                   will analyze a Node.js package, uncover a potential ACI flow,
container you may see the following warning, which can safely        and automatically synthesize an exploit that confirms it is
be ignored:                                                          exploitable.
WARNING: The requested image’s platform (linux/amd64) does              [How to] Use N ODE M EDIC -FINE to analyze node-
     not match the detected host platform (linux/arm64/v8)
     and no specific platform was requested                          rsync@1.0.3, which has a disclosed ACI vulnerability (https:
                                                                     //security.snyk.io/vuln/SNYK-JS-NODERSYNC-568773), and
  The warning is because the Docker container will be run            review N ODE M EDIC -FINE’s output to see the uncovered
using cross-architecture emulation.                                  confirmed-exploitable package API.
                                                                        [Preparation] As a prerequisite, the previous set of steps
C. Experiment Workflow
                                                                     (Artifact Sections B-B) must have been followed to the
   The high-level workflow of using N ODE M EDIC -FINE is as         point where a N ODE M EDIC -FINE Docker image has been
follows:                                                             successfully built.
   1) A target npm package is selected. Both the name and               [Execution] Issue the following command to invoke
      version of the package must be known.                          N ODE M EDIC -FINE on the package:
   2) N ODE M EDIC -FINE is invoked on the package via a             docker run --rm -it nodemedic-fine --package=node-rsync --
      Docker container. The end-to-end N ODE M EDIC -FINE                 version=1.0.3 --mode=full
      infrastructure is executed: the package is automatically
                                                                       The command should take under 5 minutes to complete (52s
      downloaded, set up within the Docker container, analyzed,
                                                                     on an M1 Pro Mac), and should end with the following output:
      and potentially confirmed to have an exploitable flow.
   3) A results file is output by N ODE M EDIC -FINE. This file      ...
                                                                     info:   Exploit(s) found for functions: execute
      can be processed to measure metrics for that particular        ...
      run, including entry points, provenance, and graph size.       info: Done with analysis

To run an evaluation over a set of packages, the above steps           That output is followed by a JSON object:
are repeated per package, and results are aggregated across          {"rows":[{"id":"node-rsync","index":0,"version":"1.0.3",
packages.                                                            ...}




                                                                19
   [Results] N ODE M EDIC -FINE emits its key results via the          ...
                                                                       info:   Exploit(s) found for functions: fromJSON
JSON blob output at the end of the package analysis. At the            ...
top level, the JSON object is a list of “rows" where each row          info: Done with analysis
                                                                       {"rows":[{"id":"node-rules"
is an entry about an analyzed package.                                 ...}
   In results of the previously run analysis, we see one entry
for the target package. In this entry, we can see that an ACI            [Results] In the results object, we can see that an ACE sink
sink was executed (execSync), and that an object input (the            was executed (eval), and that an object input (the exploitString
exploitString value) was found that confirms the exploitability        value) was found that confirms the exploitability of the package
of the package API, runCommand:                                        API, runCommand:
"id": "node-rsync",                                                    "id": "node-rules",
...                                                                    ...
"version": "1.0.3",                                                    "version": "3.0.0",
...                                                                    ...
"sinksHit": ["execSync"],                                              "sinksHit": ["function", "execSync", "eval"],
...                                                                    ...
"exploitResults": [{                                                   "exploitResults": [{
    "exploitFunction": "execute",                                          "exploitFunction": "fromJSON",
    "exploitString": "{\"flags\":\"BC $(touch\",\"source                   "exploitString": "{\"condition\":\"global.CTF())//\"}"
         \":\"/tmp/success);#\"}"                                      }],
}],

   For completeness, each field is explained below:            F. Customization
   • “id": Package name.
                                                                  In the above experiments, analysis artifacts are stored
   • “index": Index in the npm package repo (gathering only).
                                                               within the Docker container. This is beneficial for security,
   • “version": Package version.
                                                               but can make it difficult to access all of N ODE M EDIC -
   • “downloadCount": Weekly download count (gathering).
                                                               FINE’s outputs. For packages that one has confidence are
   • “packagePath": Path to installed package.
                                                               not malicious/malware, N ODE M EDIC -FINE can be run (from
   • “hasMain": Whether the package has a main script.
                                                               the repository root) with a Docker mounted volume to enable
   • “browserAPIs": List of browser APIs in the package.
                                                               direct access to the package under test and the analysis results:
   • “sinks": List of NodeMedic-FINE–supported sinks found
                                                               docker run -it --rm -v $PWD/packages/:/nodetaint/
     in the package.                                                packageData:rw -v $PWD/artifacts/:/nodetaint/
   • “sinksHit": List of sinks executed.                            analysisArtifacts:rw nodemedic-fine --package=node-
                                                                    rsync --version=1.0.3 --mode=full
   • “entryPoints": List of package public APIs.
   • “treeMetadata": Metadata about the package’s dependency      Then, $PWD/packages directory will contain the package’s
     tree (size, depth, etc.).                                 source code, while the $PWD/artifacts directory will now have
   • “sinkType": Type of sink (ACI, “exec”, or ACE, “eval”).   the analysis results, including coverage files from the fuzzer,
   • “synthesisResult" Synthesized package exploit input.      the provenance tree, and synthesized exploits. You will find
   • “candidateExploit": Candidate exploit for the package.    the following files there:
   • “exploitResults": Results of executing candidate exploit.    • results.json: Overall analysis results.
   • “taskResults": Object with status and runtime for every      • fuzzer_progress.json: Coverage information from the
     N ODE M EDIC -FINE internal task run.                          fuzzer, as a list of pairs (timestamp, coverage).
   2) Experiment (E2): [ACE Flow] [5 human-minutes + 5            • fuzzer_results.json: General information from the fuzzer.
compute-minutes]: In this experiment, N ODE M EDIC -FINE          • run-<package_name>.js: Driver that imports the package
will analyze a Node.js package, uncover a potential ACE flow,       and the fuzzer and performs fuzzing.
and automatically synthesize an exploit that confirms it is       • run-<package_name>2.js Second driver which only calls
exploitable.                                                        the potentially vulnerable entry point with the fuzzer-
   [How to] Use N ODE M EDIC -FINE to analyze node-                 generated input, if N ODE M EDIC -FINE finds a flow.
rules@3.0.0, which has a disclosed ACE vulnerability (https:      • taint_0.json: The provenance graph, a .pdf visualization
//security.snyk.io/vuln/SNYK-JS-NODERULES-560426), and              of it also exists, if N ODE M EDIC -FINE finds a potential
review N ODE M EDIC -FINE’s output to see the uncovered             flow.
confirmed-exploitable package API.                                • poc<argument_number>.js: Automatically synthesized ex-
   [Preparation] As with Experiment E1 (B-E1), please ensure        ploit driver that imports the package and tries to exploit
the N ODE M EDIC -FINE Docker image has been built.                 it, if N ODE M EDIC -FINE finds a flow.
   [Execution] Run following command to invoke
N ODE M EDIC -FINE on the package:
docker run --rm -it nodemedic-fine --package=node-rules --
     version=3.0.0 --mode=full

  The command should take under 5 minutes to complete (51s
on an M1 Pro Mac), and should end with the following output,
below which a JSON results object will be printed:



                                                                  20
