Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exploit prevention command injection #4966

Merged
merged 13 commits into from
Dec 19, 2024
1 change: 1 addition & 0 deletions packages/dd-trace/src/appsec/addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module.exports = {
DB_STATEMENT: 'server.db.statement',
DB_SYSTEM: 'server.db.system',

EXEC_COMMAND: 'server.sys.exec.cmd',
SHELL_COMMAND: 'server.sys.shell.cmd',

LOGIN_SUCCESS: 'server.business_logic.users.login.success',
Expand Down
19 changes: 13 additions & 6 deletions packages/dd-trace/src/appsec/rasp/command_injection.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,26 @@ function disable () {
}

function analyzeCommandInjection ({ file, fileArgs, shell, abortController }) {
if (!file || !shell) return
if (!file) return

const store = storage.getStore()
const req = store?.req
if (!req) return

const commandParams = fileArgs ? [file, ...fileArgs] : file

const persistent = {
[addresses.SHELL_COMMAND]: commandParams
const persistent = {}
const raspRule = { type: RULE_TYPES.COMMAND_INJECTION }
const params = fileArgs ? [file, ...fileArgs] : file

if (shell) {
persistent[addresses.SHELL_COMMAND] = params
raspRule.variant = 'shell'
} else {
const commandParams = Array.isArray(params) ? params : [params]
persistent[addresses.EXEC_COMMAND] = commandParams
raspRule.variant = 'exec'
iunanua marked this conversation as resolved.
Show resolved Hide resolved
}

const result = waf.run({ persistent }, req, RULE_TYPES.COMMAND_INJECTION)
const result = waf.run({ persistent }, req, raspRule)

const res = store?.res
handleResult(result, req, res, abortController, config)
Expand Down
4 changes: 3 additions & 1 deletion packages/dd-trace/src/appsec/rasp/lfi.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ function analyzeLfi (ctx) {
[FS_OPERATION_PATH]: path
}

const result = waf.run({ persistent }, req, RULE_TYPES.LFI)
const raspRule = { type: RULE_TYPES.LFI }

const result = waf.run({ persistent }, req, raspRule)
handleResult(result, req, res, ctx.abortController, config)
})
}
Expand Down
4 changes: 3 additions & 1 deletion packages/dd-trace/src/appsec/rasp/sql_injection.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ function analyzeSqlInjection (query, dbSystem, abortController) {
[addresses.DB_SYSTEM]: dbSystem
}

const result = waf.run({ persistent }, req, RULE_TYPES.SQL_INJECTION)
const raspRule = { type: RULE_TYPES.SQL_INJECTION }

const result = waf.run({ persistent }, req, raspRule)

handleResult(result, req, res, abortController, config)
}
Expand Down
4 changes: 3 additions & 1 deletion packages/dd-trace/src/appsec/rasp/ssrf.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ function analyzeSsrf (ctx) {
[addresses.HTTP_OUTGOING_URL]: outgoingUrl
}

const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
const raspRule = { type: RULE_TYPES.SSRF }

const result = waf.run({ persistent }, req, raspRule)

const res = store?.res
handleResult(result, req, res, ctx.abortController, config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ module.exports = {
ASM_AUTO_USER_INSTRUM_MODE: 1n << 31n,
ASM_ENDPOINT_FINGERPRINT: 1n << 32n,
ASM_NETWORK_FINGERPRINT: 1n << 34n,
ASM_HEADER_FINGERPRINT: 1n << 35n
ASM_HEADER_FINGERPRINT: 1n << 35n,
ASM_RASP_CMDI: 1n << 37n
}
2 changes: 2 additions & 0 deletions packages/dd-trace/src/appsec/remote_config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function enableWafUpdate (appsecConfig) {
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, true)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, true)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, true)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_CMDI, true)
}

// TODO: delete noop handlers and kPreUpdate and replace with batched handlers
Expand Down Expand Up @@ -133,6 +134,7 @@ function disableWafUpdate () {
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SSRF, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_LFI, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_SHI, false)
rc.updateCapabilities(RemoteConfigCapabilities.ASM_RASP_CMDI, false)

rc.removeProductHandler('ASM_DATA')
rc.removeProductHandler('ASM_DD')
Expand Down
6 changes: 3 additions & 3 deletions packages/dd-trace/src/appsec/reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,16 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
incrementWafInitMetric(wafVersion, rulesVersion)
}

function reportMetrics (metrics, raspRuleType) {
function reportMetrics (metrics, raspRule) {
uurien marked this conversation as resolved.
Show resolved Hide resolved
const store = storage.getStore()
const rootSpan = store?.req && web.root(store.req)
if (!rootSpan) return

if (metrics.rulesVersion) {
rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
}
if (raspRuleType) {
updateRaspRequestsMetricTags(metrics, store.req, raspRuleType)
if (raspRule) {
updateRaspRequestsMetricTags(metrics, store.req, raspRule)
} else {
updateWafRequestsMetricTags(metrics, store.req)
}
Expand Down
9 changes: 7 additions & 2 deletions packages/dd-trace/src/appsec/telemetry.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function getOrCreateMetricTags (store, versionsTags) {
return metricTags
}

function updateRaspRequestsMetricTags (metrics, req, raspRuleType) {
function updateRaspRequestsMetricTags (metrics, req, raspRule) {
if (!req) return

const store = getStore(req)
Expand All @@ -89,7 +89,12 @@ function updateRaspRequestsMetricTags (metrics, req, raspRuleType) {

if (!enabled) return

const tags = { rule_type: raspRuleType, waf_version: metrics.wafVersion }
const tags = { rule_type: raspRule.type, waf_version: metrics.wafVersion }

if (raspRule.variant) {
tags.rule_variant = raspRule.variant
iunanua marked this conversation as resolved.
Show resolved Hide resolved
}

appsecMetrics.count('rasp.rule.eval', tags).inc(1)

if (metrics.wafTimeout) {
Expand Down
4 changes: 2 additions & 2 deletions packages/dd-trace/src/appsec/waf/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function update (newRules) {
}
}

function run (data, req, raspRuleType) {
function run (data, req, raspRule) {
if (!req) {
const store = storage.getStore()
if (!store || !store.req) {
Expand All @@ -59,7 +59,7 @@ function run (data, req, raspRuleType) {

const wafContext = waf.wafManager.getWAFContext(req)

return wafContext.run(data, raspRuleType)
return wafContext.run(data, raspRule)
}

function disposeContext (req) {
Expand Down
4 changes: 2 additions & 2 deletions packages/dd-trace/src/appsec/waf/waf_context_wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class WAFContextWrapper {
this.knownAddresses = knownAddresses
}

run ({ persistent, ephemeral }, raspRuleType) {
run ({ persistent, ephemeral }, raspRule) {
if (this.ddwafContext.disposed) {
log.warn('[ASM] Calling run on a disposed context')
return
Expand Down Expand Up @@ -87,7 +87,7 @@ class WAFContextWrapper {
blockTriggered,
wafVersion: this.wafVersion,
wafTimeout: result.timeout
}, raspRuleType)
}, raspRule)

if (ruleTriggered) {
Reporter.reportAttack(JSON.stringify(result.events))
Expand Down
Loading
Loading