kevinhakanson.com

Scanning for OWS SQL Injection Protection

August 12, 2019 #security #codequality #devops #powershell

A few months ago, I was reviewing some code that was flagged by Veracode for multiple CWE-89: Improper Neutralization of Special Elements used in an SQL Command (‘SQL Injection’) flaws. When working with the development team, they demonstrated that the SQL generation which triggers these flaws is part of OpenWebStudio (OWS) and is driven by configuration data. I said that if we can prove we are generating from safe input, I could mitigate, but I wanted an in-depth review of the code paths.

OWS does include the ability to Protect Against SQL Injection and even explicitly mentions to “ALWAYS check the box to protect against SQL Injection”. Here is a screenshot from the Variable documentation.

OWS Variable configuration

Since OWS is open source, I was able to search the code and find that the FormatQueryVariable implementation inside Render.Variable.vb works as expected.

  If EscapeQuotes Then
      Value = Value.Replace("'", "''")
      If (Left Is Nothing OrElse Left.Length = 0) AndAlso (Right Is Nothing OrElse Right.Length = 0) Then
          'NO LEFT AND RIGHT ARE PROVIDED, BUT SQL INJECTION IS CHECKED
          Firewall.Firewall(Value, False, Utilities.Firewall.FirewallDirectiveEnum.Any, False)
      End If
  End If

The configuration from that Variable UI Dialog above is serialized as JSON data into an XML based Microsoft ResX file.

<data name="Module.Load" xml:space="preserve">
  <value>
  <!-- OWS JSON configuration -->
  </value>
</data>

Inside that JSON configuration string is a block for each of the Variable configurations. The JSON sample below is representative of the UI Dialog above. Note that Protected is set to "true".

  "ChildActions": [{
      "Index": 66,
      "Level": 0,
      "Parameters": {
          "VariableType": "&lt;Form&gt;",
          "VariableDataType": "Any",
          "Formatters": "",
          "QuerySource": "frmContent",
          "QueryTarget": "@frmContent",
          "QueryTargetLeft": "'",
          "QueryTargetRight": "'",
          "QueryTargetEmpty": "NULL",
          "EscapeListX": "0",
          "Protected": "true",
          "EscapeHTML": "false"
      },
      "ActionType": "Template-Variable",
      "ChildActions": []
  }

I had asked the team to write a script to scan all .ascx.resx files in the project and verify SQL Protection is being set as expected. However, when I checked back with them, this was still on the backlog. I took this as an opportunity to help out and write the code myself. I usually use python for my utility scripts, but I decided to try out PowerShell for this since it would be more familiar to that .NET based team.

The first thing I wanted to do was initialize some counters and recursively find all the *.ascx.resx files. Since I kept seeing examples using | in my google results, I read up on How Pipelines Work in PowerShell, including the ForEach-Object function and the $_ automatic variable.

$FileCount = 0
$IndexCount = 0
$FilePathLength = (Get-Location).Path.Length + 1

Get-ChildItem *.ascx.resx -Recurse | ForEach-Object {
  $CurrentFile = $_.FullName.substring($FilePathLength)
  [String[]] $FileMessages = @()
  
  # continued below

I used Select-Xml with an XPath selector of //data[@name='Module.Load']/value, eventually converting the JSON string value into a PSCustomObject using ConvertFrom-Json. I then iterated over the ChildActions and called a ScanChildActions function I created to return an array of any violations.

  $obj = Select-Xml -Path $_.FullName -XPath "//data[@name='Module.Load']/value" |
  ForEach-Object {$_.node.InnerXML} |
  ConvertFrom-Json

  $obj.messageItems | ForEach-Object {
    $_.ChildActions | ForEach-Object {
      $FileMessages = ScanChildActions $_ $FileMessages
    }
  }
  
  # continued below

The ChildActions function is a recursive scan that looks for any ActionType of Template-Variable and inspects the Protected parameter. If "false", it appends an error message with the ️Index value as the key for finding the offending Variable later.

function ScanChildActions($ChildActions, [String[]]$ErrorMessages) {
  $ChildActions | ForEach-Object {
    if ($_.ActionType -eq "Template-Variable") {
      if ($_.Parameters.Protected -eq "false") {
        $message = "️Index=$($_.Index)  Protected=false"
        $ErrorMessages += $message
      }
    }

    $ErrorMessages = [String[]](ScanChildActions $_.ChildActions $ErrorMessages)
  }

  return $ErrorMessages
}

With all the error messages gathered for the file, it was time to output the results and increment $FileCount / $IndexCount and output either a success (✅) or failure (❌) line for that filename.

  # ... continued from above

  if ($FileMessages.length -eq 0) {
    Write-Output "✅  $($CurrentFile)"

  } else {
    $script:FileCount++
    $script:IndexCount += $FileMessages.Length

    Write-Output "❌  $($CurrentFile)"
    $FileMessages | ForEach-Object { Write-Output "️  ☒ $($_)" }
  }
}

Because I intended for this to be run as part of the build, I made sure to return a non-zero exit code so this script could be used to fail either the build or a pre-commit hook or whatever worked for the team’s development flow.

Write-Output "$($FileCount) files had $($IndexCount) instances of Variable SQL Injection"
if ($FileCount -gt 0) {
  exit -1
}
exit 0

I am early in my PowerShell script development journey, so there are likely improvements I could make to this code. However, since my goal was to improve security through automation, I feel good about the solution.


Kevin Hakanson

Multi-Cloud Certified Architect | DevSecOps | AppSec | Web Platform | Speaker | Learner | Builder
Twitter | LinkedIn | GitHub | Stack Overflow | Credly

© 2024 Kevin Hakanson (built with Gatsby)