cretonjs

CretonJS is an HTTP abstraction layer for creating custom NodeJS HTTP clients when performing red teaming / penetration testing to accurately test defenses.

Usage no npm install needed!

<script type="module">
  import cretonjs from 'https://cdn.skypack.dev/cretonjs';
</script>

README

CretonJS

Overview

CretonJS is an HTTP abstraction layer / wrapper around the Request library for creating a sophisticated, non browser based HTTP client. This framework will enable you to leverage free (and paid) proxy networks to create scripts that are able to accurately simulate real world attacks on web applications.

This framework is to be used by penetration testers or developers who wish to test the security of their applications.

Currently, the framework supports:

  • Location filtering on proxy nodes e.g. Region -> Country -> City
  • Automatic proxy rotation
  • Automatic validation that the proxies do in fact work
  • HTTP Header randomization e.g. random ordering of all headers, random content of User-Agent, Accept-Language and GA Cookies
  • "Sticky" HTTP Sessions to ensure continuity of Cookies, IPs, User Agents etc.

In the future this framework will support:

  • "Smokescreen" functionality to create erroneous SOC alerts. e.g. Large volumes of requests from IPs outside of the target GEO, using static UA's and headers.
  • Support for Luminati and other paid proxy services

Usage

Installation

npm install cretonjs --save

Getting started

To create an instance of Creton to use for your HTTP Client, instantiate the class.

const Creton = require('cretonjs');

let creton = new Creton();

To create a new HTTP client using the proxy and HTTP library provided by Creton, you'll need to create a new instance of the Creton HTTPService class.

By default, your Creton instance will select the next proxy in the chain to use for the new HTTP client. If you wish to use your own proxy, you can provide the IP and port as a string when calling the createNewHTTPClient function.

let HTTPService = creton.createNewHTTPClient(); // Will load the next proxy in the chain
let HTTPService = creton.createNewHTTPClient('127.0.0.1:8080'); // Will use the provided proxy

Setting the HTTP Request options

There are two mechanisms to set / update the HTTP headers that will be used by the current HTTPClient instance. Firstly, you can use the setOptionsForFirstRequest function, or the updateRequestOptionsForNextRequest. These are typically chained when sending multiple requests per HTTPClient.

They both take three arguments: <uri>, <HTTPMethod>, <HTTPBody> and are used as follows:

HTTPService.setOptionsForFirstRequest('http://example.com', 'GET');
// Send request.....

// When it's time for the next request
HTTPService.updateRequestOptionsForNextRequest('http://example.com/login','POST',{username:"test", password:"test"});

See /examples/chainingHTTPGetRequestsFromASingleIP.js for a practical example of how to use this functionality.

Filtering proxies

When it comes to filtering proxies, you have three levels of control: Region, Country, City.

The filtering is done via passing a simple config JSON object into the Creton class upon instantiation.

Note: Country and Region codes follow ISO 3166 , you can search through the codes here

let proxyFilter = {"country": "FR"}

let creton = new Creton({
    proxyFilter: proxyFilter
});

Custom proxy list

If you wish to load your own proxy list, it must adhere to the following pattern. IP:PORT\n.

You can see a practical, implemented example in the /examples/useCustomProxyList.js file.

Simple example:

127.0.0.1:8080
10.0.0.1:1337

To load this into your Creton instance, add a config flag for proxyListPath like such:

let proxyListPath = '~/proxies.txt';

let creton = new Creton({
    proxyListPath: proxyListPath
});

Note: You can still perform geographic filtering on a custom proxy list using the regular proxyFilter config object.

Header randomization

Currently, Creton will handle the generation of random HTTP headers. This includes:

  • Randomization of the user-agent header with strings from mobile devices and desktops
  • Randomization of the accept-language header with the top 10 common variants
  • Randomization of the HTTP Header ordering, to avoid HTTP based fingerprinting.

This is all handled behind the scenes, however you can see the output of this by enabling the debug flags on your Creton instance.

Debug logging

To gain extra visbility into what is occuring within Creton, you can simply add a debug:true flag whenever you are instantiating a new class.

const Creton = require('cretonjs');

let creton = new Creton({
    proxyFilter: proxyFilter,
    debug: true // Debug:true flag
}); 

let HTTPService = creton.createNewHTTPClient();

Accessing the HTTP response

You have the ability to access the HTTP response to a Creton request in two ways. Firstly, a custom callback that takes in the following arguments: err => any errors encountered during the HTTP request; response => the raw HTTP response, including headers; body => the Body of the HTTP response

An example callback would be:

httpClient.sendHTTPRequest((err, resp, body) => {
    if (err) {
        console.log(err);
        return;
    }
    if (resp.statusCode !== 200) {
        console.log('Hmm Looks like something went wrong... This proxy needs to be discarded.');
    } else {
        console.log('We loaded the resource! This proxy works!');
        console.log(body);
    }
});

The other way to access the HTTP response is via the HTTP Client httpResponse object. This will be filled on the HTTP request has been executed and will contain the <response> and <body>.

 // After the HTTP request has been sent and response received
 // you can access the following attributes

let HTTPStatusCode = httpClient.httpResponse.response.statusCode;

let HTTPResponseHeaders = httpClient.httpResponse.response.headers;

let HTTPResponseBody = httpClient.httpResponse.body;

Accessing HTTP request options from the previous request

If you need to access any information from the request that was just sent, that called the current callback function, you can access this directly from the HTTPClient. Call the getPreviousRequestOptions() function to have the request options that were passed through to the underlying Request library.

httpClient.sendHTTPRequest((err, resp, body) => {
    if (err) {
        console.log(err);
        return;
    }
    // If the previous request was a POST, you can access the body that was sent to the server.
    console.log(this.getPreviousRequestOptions().body);
    
    // Access the full HTTP Header object from the previous request
    console.log(this.getPreviousRequestOptions().headers);

});

Continuity of sessions

To make traffic less suspicious, it can be important to have continuity within each individual session. This involves setting HTTP Cookies for subsequent HTTP requests and ensuring that the HTTP Headers remain the same on a "Per IP" basis.

To use this functionality within Creton, simply add the stickySessions:true flag when instantiating an instance of Creton.

let creton = new Creton({
    stickySessions: true
});

Note: See ./examples/simpleBruteForce.js for a practical example / use case for sticky sessions.

Pulling new proxy lists

Upon install, Creton will pull down the latest proxy lists from the default sources. However you can refresh / update this list at any point by calling the creton.proxy.getLatestProxyLists function.

const Creton = require('cretonjs');

let creton = new Creton();

creton.proxy.getLatestProxyLists();

Validating proxies

To ensure the highest success rate of requests, CretonJS supports automatic proxy validation on instantiation. This happens in an async manner, so until any proxies are validated, Creton will just return the next proxy in the list. This will ensure that there are always requests going out.

To use this functionality, simply add the validateProxies:true flag when instantiating an instance of Creton.

let creton = new Creton({
    validateProxies: true
});

Note: See ./examples/validatedProxiesOnly.js for a practical example / implementation of this functionality.

Validating proxies using custom configuration

To avoid a case where proxies must be tested against a specific domain / endpoint and there are performance requirements, CretonJS supports custom validation config.

You can specific a domain / path to hit + method, expected HTTP response status code, and the maximum acceptable latency of the proxy.

let creton = new Creton({
    validateProxies: true,
        proxyValidationConfiguration: {
            // whether or not we use https
            secure: false,
            // domain and path to send the health check request to
            endpoint: 'www.example.com/favicon.ico',
            // HTTP method
            method: 'GET',
            // HTTP Response code from target that indicates "success"
            expectedStatusCode: 200,
            // The maximum acceptable latency from the proxies in ms
            maxLatency: 2500
        },
});

Note: see ./examples/validatedProxiesWithCustomEndpoint.js for a practical example.

Random selection of next proxy

To avoid the exact same number of requests per IP being sent, Creton supports "random" selection of available proxies.

This will create a more random distribution of requests, which will more accurately simulate a real world, highly distributed attack.

To use this functionality simply add the randomProxySelection:true flag when instantiating an instance of Creton.

let creton = new Creton({
    randomProxySelection: true
});

Note: See ./examples/validatedProxiesOnly.js for an example of this in a real implementation.

HTTP Request header manipulation

Adding custom HTTP headers

To add custom HTTP headers to an outbound request, call the addOrModifyHTTPHeader function on your HTTPClient. This takes the header name and value as parameters.

httpClient.addOrModifyHTTPHeader('x-custom-header','abc-123');

// You can add as many as you need...
httpClient.addOrModifyHTTPHeader('authorization','abc-123-456');

Note: These headers are currently not randomized, however since all the default headers are, risk of HTTP fingerprinting is low. See /examples/addingACustomHeader.js for a practical example.

Adding custom HTTP Cookies

To add custom HTTP cookies to your Creton instance, call the addCustomHTTPCookies function. This takes a string of the cookies. e.g. "sessionID=abc-123;userID=def-456".

let cookiesAsString = "sessionID=abc-123-456-def;userToken=fed-654-321-cba;";
// Add the custom cookie
httpClient.addCustomHTTPCookies(cookiesAsString);

Note: These cookies will be added on top of any others received by Creton if stickySessions is enabled. See /examples/addCustomCookieToRequest.js for a practical example.

Examples

See /examples for example code. This will be added to in order to be more comprehensive.