Can you mitigate Log4Shell attacks in Azure using only Network Security Group?
Tags: azure, security, powershell, log4shell
Published: 2022-01-16 22:15:56

**Spoiler, not totally, log4j update is the only viable and definitive solution.**

Log4Shell is a zero-day vulnerability in the Java, Log4J component. This vulnerability is currently exploited and several bots have been identified. 
The solution is to update all log4j installations in all applications. But as log4J is heavily used this task could take time and some installations can be hidden in some vendor software. 
The solution can also be to protect HTTP applications with a web application firewall, in Azure you can use Azure WAF v2 (in application gateway or azure front door). But the solution may not be so simple, and the deployment could take time as you will need to discover all your applications and their paths.

Are there any other solutions to mitigate log4shell attacks?

What if we can leverage Network Security Groups to limit the impact of log4shell attacks? 
But wait, NSG only works at layer 4 of the OSI model while attack can only be detected on layer 7. How can it be possible?
Hopefully, some security vendors and individuals publish a list of known log4shell bots. For example, let's take a look at the list maintained by [Blotus]( https://github.com/blotus). You can find the list [here]( https://gist.github.com/blotus/f87ed46718bfdc634c9081110d243166).

The first step if we want to create an NSG rule from this list is to transform the CSV file to a PowerShell, string array of IP addresses. 
We want only, the validated IP, and leave IP without enough data or pending status.

```powershell
$ipDataURI = "https://gist.githubusercontent.com/blotus/f87ed46718bfdc634c9081110d243166/raw/bfa0c5946207bbf4157eea958a7c47aa37395702/log4j_exploitation_attempts_crowdsec.csv"

    $ipData = Invoke-WebRequest -Uri $ipDataURI | ConvertFrom-Csv | where-object status -eq "validated" | select-object ip 

    $arrIP = @()

    foreach ($ip in $ipData) {
        $arrIP += $ip.ip.toString()
    }
    $ipDataURI = "https://gist.githubusercontent.com/blotus/f87ed46718bfdc634c9081110d243166/raw/bfa0c5946207bbf4157eea958a7c47aa37395702/log4j_exploitation_attempts_crowdsec.csv"

    $ipData = Invoke-WebRequest -Uri $ipDataURI | ConvertFrom-Csv | where-object status -eq "validated" | select-object ip 

    $arrIP = @()

    foreach ($ip in $ipData) {
        $arrIP += $ip.ip.toString()
    }
```

We can now create the NSG rule in an existing NSG:

```powershell
Get-AzNetworkSecurityGroup -Name nsg1 -ResourceGroupName 01-netmgr |  Add-AzNetworkSecurityRuleConfig -Name log4j-rule -Description "log4j" -Access Deny -Protocol * -Direction Inbound -Priority 100 -SourceAddressPrefix $arrIP  -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange * | Set-AzNetworkSecurityGroup
```

It's an easy task to create this kind of rule into an empty security group. To work the deny rule should be the first rule, if HTTP or HTTPS is allowed by another rule with a lower priority, adding a rule with a higher priority will not add any protection.
You will need to add the deny rule with the lower priority possible, 100, to be sure to deny traffic from any of the  IPs in the list. But your NSG can be configured in different ways. You can have two distinct situations:

- NSG with rules starting with a priority higher than 100, in this case, we can add the rule with a priority 100
 without doing anything else. 
- NSG with a rule beginning with a 100 priority, in this case, all rules need to be moved in order to add the deny rule with a 100 priority.

Imagine these NSG rule
100 http from any to any on TCP 80
101 https from any to any on TCP 443
102 ftp from any to any on TCP 21

We need to move these first rules from 100 and 102 as a priority.
The simple way will be to add 1 to all custom security rules and finally add the rule against log4shell attack with 100 as a priority.

```powershell
$nsg =  Get-AzNetworkSecurityGroup -Name testnsg -ResourceGroupName 01-netmgr 

   foreach ($rule in $nsg.SecurityRules) {
      
            $rule.Priority = $rule.Priority + 1
        
    }

    $nsg | Set-AzNetworkSecurityGroup
    $nsg |  Add-AzNetworkSecurityRuleConfig -Name log4j-rule -Description "log4j" -Access Deny -Protocol * -Direction Inbound -Priority 100 -SourceAddressPrefix $arrIP  -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange * | Set-AzNetworkSecurityGroup
```
Adding the rule against the log4shell attack was one step. The list of IP can change several times per day. 
We also need a mechanism to update the log4j-rule.
There is a small difference when you update an existing NSG rule. Unlike the add action, which accepts an array of strings, to update, or more exactly rewrite a rule you will need to provide a System.Collections.Generic.List List`1 because Azure uses this format to store the IP information. 
We need to redefine the way the list is generated to use a generic list

```powershell
    $ipDataURI = "https://gist.githubusercontent.com/blotus/f87ed46718bfdc634c9081110d243166/raw/bfa0c5946207bbf4157eea958a7c47aa37395702/log4j_exploitation_attempts_crowdsec.csv"

    $ipData = Invoke-WebRequest -Uri $ipDataURI | ConvertFrom-Csv | where-object status -eq "validated" | select-object ip 

    $ipGenericList = New-Object System.Collections.Generic.List[string]
    foreach ($ip in $ipData) {
        $ipGenericList.Add($ip.ip.toString())
    }

$nsg = Get-AzNetworkSecurityGroup -Name testnsg -ResourceGroupName 01-netmgr 
($nsg.SecurityRules | Where-Object {$_.Name -eq "log4j-rule"}).SourceAddressPrefix = $arrIP
$nsg | Set-AzNetworkSecurityGroup
```