Skip to content

Commit

Permalink
Untrusted deserialization vulnerability detection
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyasShabi committed Dec 31, 2024
1 parent 4d6a8e3 commit 34b09fe
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 16 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/appsec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,17 @@ jobs:
- uses: ./.github/actions/node/latest
- run: yarn test:appsec:plugins:ci
- uses: codecov/codecov-action@v3

node-serialize:
runs-on: ubuntu-latest
env:
PLUGINS: node-serialize
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/node/setup
- uses: ./.github/actions/install
- uses: ./.github/actions/node/oldest
- run: yarn test:appsec:plugins:ci
- uses: ./.github/actions/node/latest
- run: yarn test:appsec:plugins:ci
- uses: codecov/codecov-action@v3
1 change: 1 addition & 0 deletions packages/datadog-instrumentations/src/helpers/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ module.exports = {
mysql2: () => require('../mysql2'),
net: () => require('../net'),
next: () => require('../next'),
'node-serialize': () => require('../node-serialize'),
'node:child_process': () => require('../child_process'),
'node:crypto': () => require('../crypto'),
'node:dns': () => require('../dns'),
Expand Down
22 changes: 22 additions & 0 deletions packages/datadog-instrumentations/src/node-serialize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const shimmer = require('../../datadog-shimmer')
const { channel, addHook } = require('./helpers/instrument')

const nodeUnserializeCh = channel('datadog:node-serialize:unserialize:start')

function wrapUnserialize (serialize) {
return function wrappedUnserialize (obj) {
if (nodeUnserializeCh.hasSubscribers) {
nodeUnserializeCh.publish({ obj })
}

return serialize.apply(this, arguments)
}
}

addHook({ name: 'node-serialize', versions: ['0'] }, serialize => {
shimmer.wrap(serialize, 'unserialize', wrapUnserialize)

return serialize
})
1 change: 1 addition & 0 deletions packages/dd-trace/src/appsec/iast/analyzers/analyzers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = {
SSRF: require('./ssrf-analyzer'),
TEMPLATE_INJECTION_ANALYZER: require('./template-injection-analyzer'),
UNVALIDATED_REDIRECT_ANALYZER: require('./unvalidated-redirect-analyzer'),
UNTRUSTED_DESERIALIZATION_ANALYZER: require('./untrusted-deserialization-analyzer'),
WEAK_CIPHER_ANALYZER: require('./weak-cipher-analyzer'),
WEAK_HASH_ANALYZER: require('./weak-hash-analyzer'),
WEAK_RANDOMNESS_ANALYZER: require('./weak-randomness-analyzer'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict'

const InjectionAnalyzer = require('./injection-analyzer')
const { UNTRUSTED_DESERIALIZATION } = require('../vulnerabilities')

class UntrustedDeserializationAnalyzer extends InjectionAnalyzer {
constructor () {
super(UNTRUSTED_DESERIALIZATION)
}

onConfigure () {
this.addSub('datadog:node-serialize:unserialize:start', ({ obj }) => this.analyze(obj))
}

_areRangesVulnerable () {
return true
}
}

module.exports = new UntrustedDeserializationAnalyzer()
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ class SensitiveHandler {

this._sensitiveAnalyzers = new Map()
this._sensitiveAnalyzers.set(vulnerabilities.CODE_INJECTION, taintedRangeBasedSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.TEMPLATE_INJECTION, taintedRangeBasedSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.COMMAND_INJECTION, commandSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.NOSQL_MONGODB_INJECTION, jsonSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.HARDCODED_PASSWORD, (evidence) => {
return hardcodedPasswordAnalyzer(evidence, this._valuePattern)
})
this._sensitiveAnalyzers.set(vulnerabilities.HEADER_INJECTION, (evidence) => {
return headerSensitiveAnalyzer(evidence, this._namePattern, this._valuePattern)
})
this._sensitiveAnalyzers.set(vulnerabilities.LDAP_INJECTION, ldapSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.NOSQL_MONGODB_INJECTION, jsonSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.SQL_INJECTION, sqlSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.SSRF, urlSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.TEMPLATE_INJECTION, taintedRangeBasedSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.UNVALIDATED_REDIRECT, urlSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.HEADER_INJECTION, (evidence) => {
return headerSensitiveAnalyzer(evidence, this._namePattern, this._valuePattern)
})
this._sensitiveAnalyzers.set(vulnerabilities.HARDCODED_PASSWORD, (evidence) => {
return hardcodedPasswordAnalyzer(evidence, this._valuePattern)
})
this._sensitiveAnalyzers.set(vulnerabilities.UNTRUSTED_DESERIALIZATION, taintedRangeBasedSensitiveAnalyzer)
}

isSensibleName (name) {
Expand Down
1 change: 1 addition & 0 deletions packages/dd-trace/src/appsec/iast/vulnerabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
SSRF: 'SSRF',
TEMPLATE_INJECTION: 'TEMPLATE_INJECTION',
UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT',
UNTRUSTED_DESERIALIZATION: 'UNTRUSTED_DESERIALIZATION',
WEAK_CIPHER: 'WEAK_CIPHER',
WEAK_HASH: 'WEAK_HASH',
WEAK_RANDOMNESS: 'WEAK_RANDOMNESS',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict'

const { prepareTestServerForIast } = require('../utils')
const { storage } = require('../../../../../datadog-core')
const iastContextFunctions = require('../../../../src/appsec/iast/iast-context')
const { newTaintedString } = require('../../../../src/appsec/iast/taint-tracking/operations')
const { SQL_ROW_VALUE } = require('../../../../src/appsec/iast/taint-tracking/source-types')

describe('untrusted-deserialization-analyzer with node-serialize', () => {
withVersions('node-serialize', 'node-serialize', version => {
let obj
before(() => {
obj = JSON.stringify({ name: 'example' })
})

describe('unserialize', () => {
prepareTestServerForIast('untrusted deserialization analyzer',
(testThatRequestHasVulnerability, testThatRequestHasNoVulnerability) => {
let lib
beforeEach(() => {
lib = require(`../../../../../../versions/node-serialize@${version}`).get()
})

testThatRequestHasVulnerability(() => {
const store = storage.getStore()
const iastContext = iastContextFunctions.getIastContext(store)
const str = newTaintedString(iastContext, obj, 'query', 'Request')
lib.unserialize(str)
}, 'UNTRUSTED_DESERIALIZATION')

testThatRequestHasVulnerability(() => {
const store = storage.getStore()
const iastContext = iastContextFunctions.getIastContext(store)
const str = newTaintedString(iastContext, obj, 'query', SQL_ROW_VALUE)
lib.unserialize(str)
}, 'UNTRUSTED_DESERIALIZATION', undefined, undefined, undefined,
'Should detect UNTRUSTED_DESERIALIZATION vulnerability with DB source')

testThatRequestHasNoVulnerability(() => {
lib.unserialize(obj)
}, 'UNTRUSTED_DESERIALIZATION')
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const excludedTests = [
'Query with single quoted string literal and null source', // does not apply
'Redacted source that needs to be truncated', // not implemented yet
'CODE_INJECTION - Tainted range based redaction - with null source ', // does not apply
'TEMPLATE_INJECTION - Tainted range based redaction - with null source ' // does not apply
'TEMPLATE_INJECTION - Tainted range based redaction - with null source ', // does not apply
'UNTRUSTED_DESERIALIZATION - Tainted range based redaction - with null source ' // does not apply
]

function doTest (testCase, parameters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2912,7 +2912,8 @@
"XSS",
"CODE_INJECTION",
"EMAIL_HTML_INJECTION",
"TEMPLATE_INJECTION"
"TEMPLATE_INJECTION",
"UNTRUSTED_DESERIALIZATION"
]
},
"input": [
Expand Down Expand Up @@ -2971,7 +2972,8 @@
"XSS",
"CODE_INJECTION",
"EMAIL_HTML_INJECTION",
"TEMPLATE_INJECTION"
"TEMPLATE_INJECTION",
"UNTRUSTED_DESERIALIZATION"
]
},
"input": [
Expand Down Expand Up @@ -3032,7 +3034,8 @@
"XSS",
"CODE_INJECTION",
"EMAIL_HTML_INJECTION",
"TEMPLATE_INJECTION"
"TEMPLATE_INJECTION",
"UNTRUSTED_DESERIALIZATION"
]
},
"input": [
Expand Down Expand Up @@ -3087,7 +3090,8 @@
"XSS",
"CODE_INJECTION",
"EMAIL_HTML_INJECTION",
"TEMPLATE_INJECTION"
"TEMPLATE_INJECTION",
"UNTRUSTED_DESERIALIZATION"
]
},
"input": [
Expand Down Expand Up @@ -3167,7 +3171,8 @@
"XSS",
"CODE_INJECTION",
"EMAIL_HTML_INJECTION",
"TEMPLATE_INJECTION"
"TEMPLATE_INJECTION",
"UNTRUSTED_DESERIALIZATION"
]
},
"input": [
Expand Down Expand Up @@ -3244,7 +3249,8 @@
"XSS",
"CODE_INJECTION",
"EMAIL_HTML_INJECTION",
"TEMPLATE_INJECTION"
"TEMPLATE_INJECTION",
"UNTRUSTED_DESERIALIZATION"
]
},
"input": [
Expand Down Expand Up @@ -3318,7 +3324,8 @@
"XSS",
"CODE_INJECTION",
"EMAIL_HTML_INJECTION",
"TEMPLATE_INJECTION"
"TEMPLATE_INJECTION",
"UNTRUSTED_DESERIALIZATION"
]
},
"input": [
Expand Down

0 comments on commit 34b09fe

Please sign in to comment.