Skip to content

Commit

Permalink
Fix powershell test cases for in-depth escaping scenario
Browse files Browse the repository at this point in the history
This also handles a few other cases not specifically covered by the new
scenario but which are obviously easily fixable
  • Loading branch information
pimterry committed Jul 2, 2024
1 parent 91ba1b4 commit 24847f2
Show file tree
Hide file tree
Showing 26 changed files with 83 additions and 40 deletions.
37 changes: 29 additions & 8 deletions src/targets/powershell/common.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
'use strict'

const CodeBuilder = require('../../helpers/code-builder')
const { escape } = require('../../helpers/format')
const helpers = require('../../helpers/headers')

// Within a single quote, the ONLY character to worry about is the single quote
// itself (escaped by doubling). Newlines, backticks, slashes etc are all treated
// as literal characters.
const psSqEscape = function (input) {
return input
.replace(/'/g, "''")
}

module.exports = function (command) {
return function (source, options) {
const code = new CodeBuilder()
Expand All @@ -23,7 +30,10 @@ module.exports = function (command) {
code.push('$headers=@{}')
headers.forEach(function (key) {
if (key !== 'connection') { // Not allowed
code.push('$headers.Add("%s", "%s")', key, escape(source.headersObj[key], { escapeChar: '`' }))
code.push("$headers.Add('%s', '%s')",
psSqEscape(key),
psSqEscape(source.headersObj[key])
)
}
})
commandOptions.push('-Headers $headers')
Expand All @@ -36,21 +46,32 @@ module.exports = function (command) {
source.cookies.forEach(function (cookie) {
code.push('$cookie = New-Object System.Net.Cookie')

code.push("$cookie.Name = '%s'", cookie.name)
code.push("$cookie.Value = '%s'", cookie.value)
code.push("$cookie.Domain = '%s'", source.uriObj.host)
code.push("$cookie.Name = '%s'", psSqEscape(cookie.name))
code.push("$cookie.Value = '%s'", psSqEscape(cookie.value))
code.push("$cookie.Domain = '%s'", psSqEscape(source.uriObj.host))

code.push('$session.Cookies.Add($cookie)')
})
commandOptions.push('-WebSession $session')
}

if (source.postData.text) {
commandOptions.push("-ContentType '" + helpers.getHeader(source.allHeaders, 'content-type') + "'")
commandOptions.push("-Body '" + source.postData.text + "'")
const contentType = helpers.getHeader(source.allHeaders, 'content-type')
if (contentType) {
commandOptions.push("-ContentType '" + psSqEscape(contentType) + "'")
}

commandOptions.push(
"-Body '" + psSqEscape(source.postData.text) + "'"
)
}

code.push("$response = %s -Uri '%s' -Method %s %s", command, source.fullUrl, source.method, commandOptions.join(' '))
code.push("$response = %s -Uri '%s' -Method %s %s",
command,
psSqEscape(source.fullUrl),
source.method,
commandOptions.join(' ')
)
return code.join()
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "application/x-www-form-urlencoded")
$headers.Add('content-type', 'application/x-www-form-urlencoded')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/x-www-form-urlencoded' -Body 'foo=bar&hello=world'
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "application/json")
$headers.Add('content-type', 'application/json')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/json' -Body '{"number":1,"string":"f\"oo","arr":[1,2,3],"nested":{"a":"b"},"arr_mix":[1,"a",{"arr_mix_nested":{}}],"boolean":false}'
2 changes: 1 addition & 1 deletion test/fixtures/output/powershell/restmethod/compression.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("accept-encoding", "deflate, gzip, br")
$headers.Add('accept-encoding', 'deflate, gzip, br')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method GET -Headers $headers
4 changes: 2 additions & 2 deletions test/fixtures/output/powershell/restmethod/full.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
$headers=@{}
$headers.Add("accept", "application/json")
$headers.Add("content-type", "application/x-www-form-urlencoded")
$headers.Add('accept', 'application/json')
$headers.Add('content-type', 'application/x-www-form-urlencoded')
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$cookie = New-Object System.Net.Cookie
$cookie.Name = 'foo'
Expand Down
6 changes: 3 additions & 3 deletions test/fixtures/output/powershell/restmethod/headers.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("accept", "application/json")
$headers.Add("x-foo", "Bar")
$headers.Add("quoted-value", "`"quoted`" 'string'")
$headers.Add('accept', 'application/json')
$headers.Add('x-foo', 'Bar')
$headers.Add('quoted-value', '"quoted" ''string''')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method GET -Headers $headers
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("content-type", "application/json")
$headers.Add('content-type', 'application/json')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/json' -Body '{
"foo": "bar"
}'
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "application/json")
$headers.Add('content-type', 'application/json')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/json' -Body '{"foo":null}'
13 changes: 13 additions & 0 deletions test/fixtures/output/powershell/restmethod/malicious.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
$headers=@{}
$headers.Add('squote-value-test', '''')
$headers.Add('dquote-value-test', '"')
$headers.Add('backtick-value-test', '`')
$headers.Add('dollar-parenthesis-value-test', '$(')
$headers.Add('hash-brace-value-test', '#{')
$headers.Add('percent-parenthesis-value-test', '%(')
$headers.Add('percent-brace-value-test', '%{')
$headers.Add('double-brace-value-test', '{{')
$headers.Add('null-value-test', '\0')
$headers.Add('string-fmt-value-test', '%s')
$headers.Add('slash-value-test', '\')
$response = Invoke-RestMethod -Uri 'http://example.test/%27%22%60$(%(%%7B%7B%7B/0%s//?''=squote-key-test&squote-value-test=''&%22=dquote-key-test&dquote-value-test=%22&%60=backtick-key-test&backtick-value-test=%60&%24(=dollar-parenthesis-key-test&dollar-parenthesis-value-test=%24(&%23%7B=hash-brace-key-test&hash-brace-value-test=%23%7B&%25(=percent-parenthesis-key-test&percent-parenthesis-value-test=%25(&%25%7B=percent-brace-key-test&percent-brace-value-test=%25%7B&%7B%7B=double-brace-key-test&double-brace-value-test=%7B%7B&%5C0=null-key-test&null-value-test=%5C0&%25s=string-fmt-key-test&string-fmt-value-test=%25s&%5C=slash-key-test&slash-value-test=%5C' -Method POST -Headers $headers -Body ''' " ` $( #{ %( %{ {{ \0 %s \'
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("content-type", "multipart/form-data; boundary=---011000010111000001101001")
$headers.Add('content-type', 'multipart/form-data; boundary=---011000010111000001101001')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001
Content-Disposition: form-data; name="foo"; filename="hello.txt"
Content-Type: text/plain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("content-type", "multipart/form-data; boundary=---011000010111000001101001")
$headers.Add('content-type', 'multipart/form-data; boundary=---011000010111000001101001')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001
Content-Disposition: form-data; name="foo"; filename="hello.txt"
Content-Type: text/plain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
$headers.Add('Content-Type', 'multipart/form-data; boundary=---011000010111000001101001')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001
Content-Disposition: form-data; name="foo"
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/output/powershell/restmethod/text-plain.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "text/plain")
$headers.Add('content-type', 'text/plain')
$response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'text/plain' -Body 'Hello World'
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "application/x-www-form-urlencoded")
$headers.Add('content-type', 'application/x-www-form-urlencoded')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/x-www-form-urlencoded' -Body 'foo=bar&hello=world'
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "application/json")
$headers.Add('content-type', 'application/json')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/json' -Body '{"number":1,"string":"f\"oo","arr":[1,2,3],"nested":{"a":"b"},"arr_mix":[1,"a",{"arr_mix_nested":{}}],"boolean":false}'
2 changes: 1 addition & 1 deletion test/fixtures/output/powershell/webrequest/compression.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("accept-encoding", "deflate, gzip, br")
$headers.Add('accept-encoding', 'deflate, gzip, br')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method GET -Headers $headers
4 changes: 2 additions & 2 deletions test/fixtures/output/powershell/webrequest/full.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
$headers=@{}
$headers.Add("accept", "application/json")
$headers.Add("content-type", "application/x-www-form-urlencoded")
$headers.Add('accept', 'application/json')
$headers.Add('content-type', 'application/x-www-form-urlencoded')
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$cookie = New-Object System.Net.Cookie
$cookie.Name = 'foo'
Expand Down
6 changes: 3 additions & 3 deletions test/fixtures/output/powershell/webrequest/headers.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("accept", "application/json")
$headers.Add("x-foo", "Bar")
$headers.Add("quoted-value", "`"quoted`" 'string'")
$headers.Add('accept', 'application/json')
$headers.Add('x-foo', 'Bar')
$headers.Add('quoted-value', '"quoted" ''string''')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method GET -Headers $headers
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("content-type", "application/json")
$headers.Add('content-type', 'application/json')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/json' -Body '{
"foo": "bar"
}'
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "application/json")
$headers.Add('content-type', 'application/json')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'application/json' -Body '{"foo":null}'
13 changes: 13 additions & 0 deletions test/fixtures/output/powershell/webrequest/malicious.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
$headers=@{}
$headers.Add('squote-value-test', '''')
$headers.Add('dquote-value-test', '"')
$headers.Add('backtick-value-test', '`')
$headers.Add('dollar-parenthesis-value-test', '$(')
$headers.Add('hash-brace-value-test', '#{')
$headers.Add('percent-parenthesis-value-test', '%(')
$headers.Add('percent-brace-value-test', '%{')
$headers.Add('double-brace-value-test', '{{')
$headers.Add('null-value-test', '\0')
$headers.Add('string-fmt-value-test', '%s')
$headers.Add('slash-value-test', '\')
$response = Invoke-WebRequest -Uri 'http://example.test/%27%22%60$(%(%%7B%7B%7B/0%s//?''=squote-key-test&squote-value-test=''&%22=dquote-key-test&dquote-value-test=%22&%60=backtick-key-test&backtick-value-test=%60&%24(=dollar-parenthesis-key-test&dollar-parenthesis-value-test=%24(&%23%7B=hash-brace-key-test&hash-brace-value-test=%23%7B&%25(=percent-parenthesis-key-test&percent-parenthesis-value-test=%25(&%25%7B=percent-brace-key-test&percent-brace-value-test=%25%7B&%7B%7B=double-brace-key-test&double-brace-value-test=%7B%7B&%5C0=null-key-test&null-value-test=%5C0&%25s=string-fmt-key-test&string-fmt-value-test=%25s&%5C=slash-key-test&slash-value-test=%5C' -Method POST -Headers $headers -Body ''' " ` $( #{ %( %{ {{ \0 %s \'
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("content-type", "multipart/form-data; boundary=---011000010111000001101001")
$headers.Add('content-type', 'multipart/form-data; boundary=---011000010111000001101001')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001
Content-Disposition: form-data; name="foo"; filename="hello.txt"
Content-Type: text/plain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("content-type", "multipart/form-data; boundary=---011000010111000001101001")
$headers.Add('content-type', 'multipart/form-data; boundary=---011000010111000001101001')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001
Content-Disposition: form-data; name="foo"; filename="hello.txt"
Content-Type: text/plain
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
$headers=@{}
$headers.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001")
$headers.Add('Content-Type', 'multipart/form-data; boundary=---011000010111000001101001')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001
Content-Disposition: form-data; name="foo"
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/output/powershell/webrequest/text-plain.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
$headers=@{}
$headers.Add("content-type", "text/plain")
$headers.Add('content-type', 'text/plain')
$response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'text/plain' -Body 'Hello World'
4 changes: 0 additions & 4 deletions test/targets.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ const skipMe = {
clojure: {
clj_http: ['jsonObj-null-value', 'jsonObj-multiline']
},
powershell: {
restmethod: ['malicious'],
webrequest: ['malicious']
},
python: {
requests: ['malicious']
},
Expand Down

0 comments on commit 24847f2

Please sign in to comment.