Example scenario of using JS Recon
This document highlights using the modules of JS-Recon individually. All of this can be automated, which can be found at the end of the file in the Run Module.
This example assumes that the app is a Next.js target. The tool is currently optimized for Next.js.
The lazyload
module will work on Next.js, Nuxt.js, and Svelte apps. All other modules are only expected to work on Next.js apps.
Target
The client provides the pentester a wildcard target *.example.com
Initial recon
The pentester starts by gathering subdomains for the target, and then uses an HTTP probe to filter out all the available HTTP servers.
They decide to analyze JavaScript on all the websites to gain access to an additional attack surface.
Downloading JS files
To download JS files, the pentester can use the lazyload
module of the tool. To run this, they can use the following command:
js-recon lazyload -u https://app.example.com
The tool will then analyze the responses from the server and download all the JS files it could find. The JS files will be written in the ./output/app.example.com
directory. If the app contains external scripts, then those will be written in the ./output/<domain>
directory.
The pentester could get the URL from the top of each file, as the tool writes all the JS files with their source commented at the top.
At the time of writing this, the tool is capable of downloading JS files for Next.js, Nuxt.js, and Svelte. For all other apps, it will download the JS files that are loaded on the webpage
Finding strings
Once the pentester has downloaded all the JS files, the first thing that they would like to do is to analyze all the strings that are found in the app. To do so, they can run the strings
module.
js-recon strings -d output/app.example.com -e
Breakdown of the command:
strings
: Run the module to analyze strings-d
: Shorthand flag for--directory
. Defines the directory containing the JS files. If any changes to default values haven't been done in the previous command, it should be./output/<domain>
-e
: Shorthand flag for--extract-urls
. Iterates through all the strings found, and prints any potential URLs or paths by matching against the regex- The output for this flag is
extracted_urls.json
- In case the pentester wants to output it as an OpenAPI collection to load in an API client, they can use the
--openapi
flag. It will be written inextracted_urls-openapi.json
- The output for this flag is
The extracted_urls.json
has the following structure:
{
"urls": [
"https://api.example.com",
"https://rum.example.com",
"https://www.example.com",
"https://app.example.com",
"https://internal.app.com"
],
"paths": [
"/v1/admin",
"/v1/dashboard",
"/v1/members",
"/v1/report",
"/v1/settings",
"/v1/edit"
]
}
Subsequent requests
JS-Recon has found that the app is using Next.js. This framework has a feature that, upon sending requests to a valid client-side endpoint along with the RSC: 1
header, the application returns a response with content type text/x-component
, which contains more client-side paths and JS files. To get this, the tool requires the extracted_urls.json
from the strings module, which has been generated in the previous step.
To use this method, the pentester can pass the --subsequent-requests
flag to the lazyload command:
js-recon lazyload -u https://app.example.com --subsequent-requests
By passing this flag, the tool will read the extracted_urls.json
file, make HTTP requests accordingly, and then save the files found in the output/app.example.com
directory.
Getting more with string analysis
Now that the tester has more JS files, they can run string analysis again. This time, they should pass a few more arguments to the tool:
js-recon strings -d output/app.example.com -e -p --openapi -s
Breakdown of additional flags:
-p
: Shorthand flag for--permutate
. This will permute theurls
andpaths
in theextracted_urls.json
file. The output will be a plain-text (.txt
) file calledextracted_urls.txt
--openapi
: This flag will generate an output file calledextracted_urls-openapi.json
. This file is based on thepaths
in theextracted_urls.json
, and can be directly loaded into an API client like Postman or Bruno-s
: Shorthand flag for--scan-secrets
. This will iterate over all the strings found and match them against a regex for popular secrets
Getting client-side endpoints
Now that the pentester has got all the JS files and a rough ideation of what the app can do (through string analysis), they can now get the exact client-side endpoints. Apart from unique implementations, there are some common ways a web-app stores client-side endpoints. The tool utilizes the common methods to find the client-side endpoints in the web app.
To do so, they can use the endpoints
module of the tool
js-recon endpoints -d output/app.example.com -u https://app.example.com -t next --subsequent-requests-dir output/app.example.com/___subsequent_requests
Breakdown of the command:
endpoints
: This module extracts client-side endpoints from the app-d
: Shorthand flag for--directory
. Defines the directory in which the JS files are stored for the given target-u
: Shorthand flag for--urls
. The URL of the target (the paths found are prepended to it)-t
: Shorthand flag for--tech
. Defines the framework (aka tech) that the target is using. It is required to find suitable methods- Run with
-l
/--list
to see list of supported tech:js-recon endpoints -l
- Run with
--subsequent-requests-dir
: Flag specific to Next.js (-t next
) targets. Defines the directory containing response texts for requests withRSC: 1
header. By default, it isoutput/<domain>/___subsequent_requests
(triple underscore_
beforesubsequent_requests
)
This command will write a file called endpoints.json
. Following is an example of this file:
{
"https://app.example.com": {
"/": {},
"/dash": {
"/dash/clients",
"/dash/automations",
"/dash/usage"
},
"/settings": {
"/settings/clients": {
"/settings/clients/edit",
"/settings/clients/add"
},
"/settings/automations",
"/settings/usage"
}
},
"https://internal.example.com": {
"/prod": {
"/prod/env"
}
}
}
Mapping all the functions
At this point, the pentester has an idea of what the app looks like. They have also used the app to see the functionality in action. Now, they suspect that the app contains a secret endpoint. They have seen https://internal.app.com
in the strings output, but are unsure how it works. They decide to manually analyze the JS files. This is where the map
modules could help them.
They would like to first get all the instances of fetch()
to know which functions can make the API calls. Apart from this, they would also like to get the AI-generated descriptions for the functions, as it would significantly speed up the process of analyzing the flow of all the functions. To do so, they can run the map
module of the tool.
js-recon map -d output/app.example.com -t next --ai description
Breakdown of the command:
map
: Runs themap
module-d
: Shorthand flag for--directory
. Defines the directory of the JS files-t
: Shorthand flag for--tech
. Defines the technology (aka framework) used by the app- Run with
-l
-/--list
flag to see supported frameworks js-recon map -l
- Run with
--ai
: Enable AI parsing.description
is used as its value, which means that the tool will write descriptions for the functions
The pentester can also adjust some AI settings:
--ai-provider
: AI provider to useopenai
andollama
are supported as of writing this
--model
: AI model to use--openai-api-key
: API key to use for OpenAI- The value for the environment variable
$OPENAI_API_KEY
will be used if not provided
- The value for the environment variable
--ai-threads <threads>
: Number of threads to simultaneously run to generate descriptions- Refer to Organization Limits in OpenAI API Platform for limits for your OpenAI Account
- For Ollama, adjust the value as per the capacity of the machine running Ollama
--ai-endpoint
: Endpoint to use with AI models- Defaults to
https://api.openai.com/v1
for OpenAI- Some providers like xAI support using the OpenAI SDK to use their models. Refer to their docs to know the latest updates
- Defaults to
http://127.0.0.1:11434
for Ollama
- Defaults to
Launching interactive console
Now that the pentester has the mappings of all the functions, they can use the interactive console. To launch it, they can add -i
/--interative
flag to the previous command
js-recon map -d output/app.example.com -t next --ai description -i
This feature might look complex, so it is recommended to get an overview through the Interactive Mode Docs before reading further.
The pentester would first like to get the instances of fetch()
, so that they can know the sites where an API call could be made. So, they will run the following command in interactive mode:
list fetch
This will list all the functions that have a fetch function. If the pentester had enabled the AI descriptions, then they could also see a brief of what the function does. Now, they can go to any function that seems suspicious.
go to 1234
By running this command, the tool would clear the output of the interactive mode, and print the code of the function. The tool provides vim like interface and shortcuts. The user can scroll on the function to go either up or down. Also, they can press Esc
, and then use the arrow keys to navigate. To focus again on the input box, they can press the i
key. To quit the app, they can press Esc
and then q
or Ctrl-c
Since the pentester prefers to see the function code in the IDE of their choice, they can write this to a separate file. To do so, they can run the following command in the interactive mode:
set funcwritefile <filename>
Now, every time when the go to <id>
command will be ran, the tool will write the code to the specified file.
Once they open it in the IDE of their choice, they can go to the instances of fetch by finding it through C-f
in the IDE. Once they find an instance, they could manually reverse engineer the full request.
To assist in doing the same, they used the following commands:
list all
: Lists all the functions, their descriptions, and the file they are found intrace <functionName>
: This lists all the other function that the given function imports, as well as the functions to which this is being exported- For example, a function required to modify the UI, so it will import those function, and hence they will be listed as imports
- This function is called at multiple places, which are listed as exports
go back
: This will take the pentester to the previous function they viewedgo ahead
: This will take the pentester to the next function they viewed (should work if they usedgo back
)
Run Module
If this process seems tedious (which it is), the pentester can use the run
module of the tool. It will:
- Download all the JS files
- Find all the strings
- Check for subsequent requests if required
- Run string analysis again
- Get all client-side endpoints
- Map all the functions
Here's what it will NOT do:
- Understand the output files to build a good attack vector
- The pentester can fuzz the
paths
from thestrings
module on multiple hosts - They can see the UI on the client-side paths found by
endpoints
module - They can come up with new methods to get the most from the output of different modules
- The pentester can fuzz the
- Completely resolve all the requests (it would go as deep as possible, but couldn't fully resolve some requests as of writing this)
The docs for the run
module can be found here
The pentester could now automate the mentioned steps by running the following command:
js-recon run -u https://app.example.com --secrets --ai description