forked from NodeSecure/js-x-ray
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
395 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Encoded literal | ||
|
||
| Code | Severity | i18n | Experimental | | ||
| --- | --- | --- | :-: | | ||
| encoded-literal | `Information` | `sast_warnings.encoded_literal` | ❌ | | ||
|
||
## Introduction | ||
|
||
The SAST scanner assert all Literals in the tree and search for encoded values. JS-X-Ray currently supports three types of detection: | ||
- Hexadecimal sequence: `'\x72\x4b\x58\x6e\x75\x65\x38\x3d'` | ||
- Unicode sequence: `\u03B1` | ||
- Base64 encryption: `z0PgB0O=` | ||
|
||
Hexadecimal and Unicode sequence are tested directly on the raw Literal provided by meriyah. For base64 detection we use the npm package [is-base64](https://github.com/miguelmota/is-base64). | ||
|
||
Example of a JavaScript implementation: | ||
```js | ||
const hasHexadecimalSequence = /\\x[a-fA-F0-9]{2}/g.exec(node.raw) !== null; | ||
const hasUnicodeSequence = /\\u[a-fA-F0-9]{4}/g.exec(node.raw) !== null; | ||
const isBase64 = isStringBase64(node.value, { allowEmpty: false }); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# Obfuscated code | ||
|
||
| Code | Severity | i18n | Experimental | | ||
| --- | --- | --- | :-: | | ||
| obfuscated-code | `Critical` | `sast_warnings.obfuscated_code` | ✔️ | | ||
|
||
## Introduction | ||
|
||
An **experimental** warning capable of detecting obfuscation and sometimes the tool used. The scanner is capable to detect: | ||
|
||
- [freejsobfuscator](http://www.freejsobfuscator.com/) | ||
- [jjencode](https://utf-8.jp/public/jjencode.html) | ||
- [jsfuck](http://www.jsfuck.com/) | ||
- [obfuscator.io](https://obfuscator.io/) | ||
- morse | ||
- [trojan source](https://trojansource.codes/) | ||
|
||
Example of obfuscated code is in the root `examples` directory. | ||
|
||
### Technical note | ||
A complete G.Drive document has been written to describe the patterns of obfuscation tools and some way of detecting them: | ||
|
||
- [JSXRay - Patterns of obfuscated JavaScript code](https://docs.google.com/document/d/11ZrfW0bDQ-kd7Gr_Ixqyk8p3TGvxckmhFH3Z8dFoPhY/edit?usp=sharing) | ||
|
||
> **Note** There is no frozen implementation and this is an early implementation | ||
## Example | ||
|
||
The following code uses Morse code to obfuscate its real intent. This was used in an attack and I find it quite funny so i implemented morse detection 😂. | ||
|
||
```js | ||
function decodeMorse(morseCode) { | ||
var ref = { | ||
'.-': 'a', | ||
'-...': 'b', | ||
'-.-.': 'c', | ||
'-..': 'd', | ||
'.': 'e', | ||
'..-.': 'f', | ||
'--.': 'g', | ||
'....': 'h', | ||
'..': 'i', | ||
'.---': 'j', | ||
'-.-': 'k', | ||
'.-..': 'l', | ||
'--': 'm', | ||
'-.': 'n', | ||
'---': 'o', | ||
'.--.': 'p', | ||
'--.-': 'q', | ||
'.-.': 'r', | ||
'...': 's', | ||
'-': 't', | ||
'..-': 'u', | ||
'...-': 'v', | ||
'.--': 'w', | ||
'-..-': 'x', | ||
'-.--': 'y', | ||
'--..': 'z', | ||
'.----': '1', | ||
'..---': '2', | ||
'...--': '3', | ||
'....-': '4', | ||
'.....': '5', | ||
'-....': '6', | ||
'--...': '7', | ||
'---..': '8', | ||
'----.': '9', | ||
'-----': '0', | ||
}; | ||
|
||
return morseCode | ||
.split(' ') | ||
.map(a => a.split(' ').map(b => ref[b]).join('')) | ||
.join(' '); | ||
} | ||
|
||
var decoded = decodeMorse(".-- --- .-. -.. .-- --- .-. -.."); | ||
console.log(decoded); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Parsing Error | ||
|
||
| Code | Severity | i18n | Experimental | | ||
| --- | --- | --- | :-: | | ||
| ast-error | `Information` | `sast_warnings.ast_error` | ❌ | | ||
|
||
## Introduction | ||
|
||
Parsing Error is throw when the library [meriyah](https://github.com/meriyah/meriyah) fail to parse the javascript source code into an AST. But it can also happen when the AST analysis fails because we don't manage a case properly. | ||
|
||
> **Note** If you are in the second case, please open an issue [here](https://github.com/NodeSecure/js-x-ray/issues) | ||
## Example | ||
|
||
```json | ||
{ | ||
"kind": "parsing-error", | ||
"value": "[10:30]: Unexpected token: ','", | ||
"location": [[0,0],[0,0]], | ||
"file": "helpers\\asyncIterator.js" | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Short identifiers | ||
|
||
| Code | Severity | i18n | Experimental | | ||
| --- | --- | --- | :-: | | ||
| short-identifiers | `Warning` | `sast_warnings.short_identifiers` | ❌ | | ||
|
||
## Introduction | ||
|
||
The SAST store in memory all Identifiers id so we are able later to sum the length of all ids. We are looking at several ESTree Node in the tree: | ||
- VariableDeclarator: `var boo;` | ||
- AssignmentExpression: `(boo = 5)` | ||
- FunctionDeclaration: `function boo() {}` | ||
- Property of ObjectExpression: `{ boo: 5 }` | ||
|
||
However, we do not take into consideration the properties of Objects for this warning. The warning is generated only if: | ||
|
||
- The file is not already declared as **Minified**. | ||
- There is more than **five** identifiers. | ||
- The sum of all identifiers name length is below `1.5`. | ||
|
||
## Example | ||
|
||
```json | ||
{ | ||
"kind": "short-identifiers", | ||
"location": [[0,0], [0,0]], | ||
"value": 1.5, | ||
"file": "lib\\compile-env.js" | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Suspicious literal | ||
|
||
| Code | Severity | i18n | Experimental | | ||
| --- | --- | --- | :-: | | ||
| suspicious-literal | `Warning` | `sast_warnings.suspicious_literal` | ❌ | | ||
|
||
## Introduction | ||
|
||
Thats one of the most interesting JS-X-Ray warning. We designed it with the idea of detecting long strings of characters that are very common in malicious obfuscated/encrypted codes like in [smith-and-wesson-skimmer](https://badjs.org/posts/smith-and-wesson-skimmer/). | ||
|
||
The basic idea is to say that any string longer than 45 characters with no space is very suspicious... Then we establish a suspicion score that will be incremented according to several criteria: | ||
|
||
- if the string contains **space** in the first **45** characters then we set the score to `zero`, else we set the score to `one`. | ||
- if the string has more than **200 characters** then we add `1` to the score. | ||
- we add one to the score for each **750 characters**. So a length of __1600__ will add `two` to the score. | ||
- we add `two` point to the score if the string contains more than **70 unique characters**. | ||
|
||
So it's possible for a string with more than 45 characters to come out with a score of zero if: | ||
- there is space in the first 45 characters of the string. | ||
- less than 70 unique characters. | ||
|
||
The implementation is done in the [@nodesecure/sec-literal](https://github.com/NodeSecure/sec-literal/blob/main/src/utils.js) package and look like this: | ||
```js | ||
function stringCharDiversity(str, charsToExclude = []) { | ||
const data = new Set(str); | ||
charsToExclude.forEach((char) => data.delete(char)); | ||
|
||
return data.size; | ||
} | ||
|
||
// --- | ||
const kMaxSafeStringLen = 45; | ||
const kMaxSafeStringCharDiversity = 70; | ||
const kMinUnsafeStringLenThreshold = 200; | ||
const kScoreStringLengthThreshold = 750; | ||
|
||
function stringSuspicionScore(str) { | ||
const strLen = stringWidth(str); | ||
if (strLen < kMaxSafeStringLen) { | ||
return 0; | ||
} | ||
|
||
const includeSpace = str.includes(" "); | ||
const includeSpaceAtStart = includeSpace ? str.slice(0, kMaxSafeStringLen).includes(" ") : false; | ||
|
||
let suspectScore = includeSpaceAtStart ? 0 : 1; | ||
if (strLen > kMinUnsafeStringLenThreshold) { | ||
suspectScore += Math.ceil(strLen / kScoreStringLengthThreshold); | ||
} | ||
|
||
return stringCharDiversity(str) >= kMaxSafeStringCharDiversity ? suspectScore + 2 : suspectScore; | ||
} | ||
``` | ||
|
||
> **Note** The warning is generated only if the sum of all scores exceeds **three**. |
Oops, something went wrong.