@mta77 (mta77)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Get-NetTCPConnectionIPアドレス監視

解決したいこと

・特定のIPアドレス範囲のホワイトリストを作成しTCPConnectionされている
IPアドレスを監視したい
・特定のIPアドレス以外のIPからコネクトがあった場合はCSV出力したい

例)
Ruby on RailsでQiitaのようなWebアプリをつくっています。
記事を投稿する機能の実装中にエラーが発生しました。
解決方法を教えて下さい。

ホワイトリストのIPアドレス範囲

LocalAddress        LocalPort   RemoteAddress   RemotePort
192.168.140.0/24    80,443,445  ‐     80,443,24,587
172.16.0.0/16       80,443,445  ‐     80,443,24,588
172.17.0.0/16       80,443,445  ‐     80,443,24,589
172.18.0.0/16       80,443,445  ‐     80,443,24,590

例)

NameError (uninitialized constant World)

または、問題・エラーが起きている画像をここにドラッグアンドドロップ

該当するソースコード

Get-NetTCPConnection | Where-Object {$_.LocalAddress} | 
Where-Object {$_.LocalPort -eq 24 -or $_.LocalPort -eq 80 -or $_.LocalPort -eq 443 -or $_.LocalPort -eq 587} 
| Where-Object {$_.RemoteAddress -match "(^192\.168\.140\.)" -or $_.RemoteAddress -match "(^172\.1[6-8]\.)" -or $_.RemoteAddress -match "(^10\.)"} 
| Where-Object {$_.RemotePort -eq 80 -or $_.RemotePort -eq 443 -or $_.RemotePort -eq 24 -or $_.RemotePort -eq 587} 
|Where-Object {$_.State -eq "Established" -or $_.State -eq "Listen"}

例)

def greet
  puts Hello World
end

自分で試したこと

確認したいこと
・上記式で検索結果は得られますがcsv、jsonなどでIPアドレス範囲を
ホワイトリスト化しpoweshellでImport-csvし,現状のGet-NetTCPConnectionと比較する方法がわかりません。
CIDR 172.16.0.0/16 IPアドレス範囲をすべてcsvに書込む方法もあるかと思いますが、データが膨大
な為、他に正規表現などでCIDRを表現しCSVに記載、Import-csvで読み込み比較できないかをご教授頂けると幸いです。

開発経験が浅い為、素人質問になってしまい申し訳ございませんが
ご教授いただければ幸いです。

以上、よろしくお願いいたします。

0 likes

6Answer

例えば JSON を読み込むのは ConvertFrom-Json を使えばできます。(CSV 読み込みなら Import-Csv

読み込んだ内容をもとに、独自の filter を作って、Get-NetTCPConnection の結果を絞り込めばよいと思います。

CSV に出力するのも Export-Csv を使えばできます。

とりあえず IPv4 のアドレスだけを対象にするなら、以下のような感じでしょうか。

xxxx.ps1
# JSON を読み込み
$json = ConvertFrom-Json -InputObject (Get-Content .\allowList.json -Raw)

# 許可リストの作成
$allowList = @()
foreach($item in $json)
{
    $allowItem = New-Object allowItem
    $allowItem.LocalAddress = New-Object Ip4Address($item.LocalAddress)
    $allowItem.LocalPorts = $item.LocalPorts
    $allowItem.RemoteAddress = New-Object Ip4Address($item.RemoteAddress)
    $allowItem.RemotePorts = $item.RemotePorts
    $allowList += $allowItem
}

Function IsAllowed([CimInstance] $connection)
{
    if ($connection.LocalAddress.Contains(":"))
    {
        # IPv6 は不許可扱い
        return $FLASE
    }
    foreach($allowItem in $allowList)
    {
        $local = New-Object Ip4Address($connection.LocalAddress)
        $remote = New-Object Ip4Address($connection.RemoteAddress)

        if ($allowItem.LocalAddress.Contains($local) -and
            # ポート番号が1つもない時にはどれでもOK
            ( $allowItem.LocalPorts.Contains([int]$connection.LocalPort) -or $allowItem.LocalPorts.Length -eq 0 ) -and
            $allowItem.RemoteAddress.Contains($remote) -and
            # ポート番号が1つもない時にはどれでもOK
            ( $allowItem.RemotePorts.Contains([int]$connection.RemotePort) -or $allowItem.RemotePorts.Length -eq 0 ) )
        {
            return $TRUE
        }
    }
    return $FALSE
}

# フィルターの定義
filter SelectAllowConnection()
{
    if (IsAllowed($_))
    {
        return $_
    }
}

filter SelectDenyConnection()
{
    if (!(IsAllowed($_)))
    {
        return $_
    }
}

# 実行:許可リストにあるものを画面表示
Get-NetTCPConnection -State Established,Listen |
  SelectAllowConnection

# 実行:許可リストにないものをCSV出力
Get-NetTCPConnection -State Established,Listen |
  SelectDenyConnection |
  Select-Object -Property LocalAddress,LocalPort,RemoteAddress,RemotePort |
  Export-Csv -path xxxx.log

# IPv4のアドレスクラスの定義
class Ip4Address
{
    [int64] $Address
    [int64] $Mask
    [int] $Width

    # 渡せるアドレスは "192.168.140.0/24"、あるいは "192.168.140.10" など
    Ip4Address([String] $addressCidr){
        if($addressCidr -eq "")
        {
            $this.Address = 0
            $this.Mask = 0
            $this.Width = 0
            return
        }

        $temp = $addressCidr -split "/"
        $ip = $temp[0] -split "\."
        $this.Width = 32
        if(2 -eq $temp.Length)
        {
            $this.Width = [int]$temp[1]
        }
        $this.Mask = 0
        if($this.Width -ne 0)
        {
            $this.Mask = 0xffffffffL -bxor ( [Math]::Pow(2,32 - $this.Width) - 1 )
        }
        $this.Address = 0
        foreach($octet in $ip)
        {
            $this.Address = $this.Address * 256 + [int]$octet
        }
        $this.Address = $this.Address -band $this.Mask
    }

    [bool]Contains([Ip4Address] $other){
        return $this.Width -le $other.Width -and ( $other.Address -band $this.Mask ) -eq $this.Address
    }
}

# 許可リスト用の項目クラス
class allowItem
{
    [Ip4Address] $LocalAddress
    [int[]] $LocalPorts
    [Ip4Address] $RemoteAddress
    [int[]] $RemotePorts
}

許可リストのJSONは以下のような感じ。

allowList.json
[
  {
    "LocalAddress": "192.168.140.0/24",
    "LocalPorts": [80,443,445],
    "RemoteAddress": "",
    "RemotePorts": [80,443,24,587]
  },
  {
    "LocalAddress": "172.16.0.0/16",
    "LocalPorts": [80,443,445],
    "RemoteAddress": "",
    "RemotePorts": [80,443,24,588]
  },
  {
    "LocalAddress": "172.17.0.0/16",
    "LocalPorts": [80,443,445],
    "RemoteAddress": "",
    "RemotePorts": [80,443,24,589]
  },
  {
    "LocalAddress": "172.18.0.0/16",
    "LocalPorts": [80,443,445],
    "RemoteAddress": "",
    "RemotePorts": [80,443,24,590]
  }
]

取り急ぎでっちあげたものなのであまりキレイではありません。参考レベルで見てください。

1Like

ご回答ありがとうございます!綺麗なコードで作成頂き大変ありがとうございます。
頂いた内容で希望の処理が可能でした!
合わせての質問ですが、頂いたシェルをループさせて一定時間走らせた後、CSVに出力したいのですが、for文またはWhiteで可能かど思いますが、可能でしょうか、
たびたびの質問で申し訳ございません。

0Like

for文で指定回数ループさせることは普通にできますよね(以下のページ参照。同じページに ForEach-Objectforeach の説明がありますが、そのあとの for です。 while でも大丈夫です。sleepしながらの例もあります)

Export-Csv-Append で追加書き込みすることができるので、このオプションをつければとりあえずは出力できると思います。

もし、同じ接続情報は1つにしたい(重複排除)などあれば Sort-Object Get-Unique などを使えばできます(もしもっと細かい判定が必要なら、個別のロジックを組むべきでしょう)。

詳細はオフィシャルページなりをご確認ください。

0Like

ご回答ありがとうございます。
すみません。前のシェルスクリプトについて質問させてください。

ホワイトリストの許可ポート、アドレスに変更があり、JSONを以下に
変更しました。

変更したjson

[
{
"LocalAddress": "",
"LocalPorts": [20,21,80,443,445],
"RemoteAddress": "192.168.140.0/24",
"RemotePorts": [24,80,443,445,587]
},
{
"LocalAddress": "",
"LocalPorts": [20,21,80,443,445],
"RemoteAddress": "172.16.0.0/16",
"RemotePorts": [24,80,443,445,587]
},
{
"LocalAddress": "",
"LocalPorts": [20,21,80,443,445],
"RemoteAddress": "172.17.0.0/16",
"RemotePorts": [24,80,443,445,587]
},
{
"LocalAddress": "",
"LocalPorts": [20,21,80,443,445],
"RemoteAddress": "172.18.0.0/16",
"RemotePorts": [24,80,443,445,587]
}
]

シェルで実行し、SelectDenyConnectionのCSVに、RemoteAddress,RemotePortが
172.18.30.xxx,445の項目がありました。

シェルでは、foreach($allowItem in $allowList)許可リストとGet-NetTCPConnectionの
項目比較をしていると想定しておりますが、JSONで不備などあればご指摘頂ければ幸いです。

また、# 実行:許可リストにあるものを画面表示においてpowershell上では
何も表示されず、Export-Csv -path .\test.csvとした所、出力はされましたが
0バイトで許可リストが表示されません。

0Like

シェルで実行し、SelectDenyConnectionのCSVに、RemoteAddress,RemotePortが
172.18.30.xxx,445の項目がありました。

一つは以前のソースにバグがありました。
(許可されていないものを、過って許可しているケースがありました。括弧が足りなかった)

         if ($allowItem.LocalAddress.Contains($local) -and
             # ポート番号が1つもない時にはどれでもOK
-            $allowItem.LocalPorts.Contains([int]$connection.LocalPort) -or $allowItem.LocalPorts.Length -eq 0 -and
+            ( $allowItem.LocalPorts.Contains([int]$connection.LocalPort) -or $allowItem.LocalPorts.Length -eq 0 ) -and
             $allowItem.RemoteAddress.Contains($remote) -and
             # ポート番号が1つもない時にはどれでもOK
-            $allowItem.RemotePorts.Contains([int]$connection.RemotePort) -or $allowItem.RemotePorts.Length -eq 0 )
+            ( $allowItem.RemotePorts.Contains([int]$connection.RemotePort) -or $allowItem.RemotePorts.Length -eq 0 ) )
         if($addressCidr -eq "")
         {
             $this.Address = 0
             $this.Mask = 0
-            $this.Width = 32
+            $this.Width = 0
             return
         }

もう一つは、許可リストの指定ですが

{
  "LocalAddress": "",
  "LocalPorts": [20,21,80,443,445],
  "RemoteAddress": "172.18.0.0/16",
  "RemotePorts": [24,80,443,445,587]
}

ローカルポートを絞り込んでいますよね。

そのため例えば、ローカルポート 8000 <-> リモートポート 445 というコネクションは、許可リストにないことになります。

リモートホストとリポートポートだけで絞り込みたいのであれば

{
  "LocalAddress": "",
  "LocalPorts": [],
  "RemoteAddress": "172.18.0.0/16",
  "RemotePorts": [24,80,443,445,587]
}

のようにしたらよいのではないでしょうか

0Like

Comments

  1. @mta77

    Questioner

    ご指摘ありがとうございます。
    ”そのため例えば、ローカルポート 8000 <~”
    そうですね。おっしゃる通りでした。

    JSONでリモートホストとリポートポートだけであれば、ご指摘の内容で問題ないのですが狙いが送信、受信ごとに許可ポートとアドレスを決めて対応したいです。

    以下が対応したい内容となります。
    ------------------------
    受信許可 ←
    remoteaddress localport
    192.168.140.0/24, ←WEB:tcp80/443 , SMB:tcp/445
    172.16.0.0/16, ←WEB:tcp80/443 , SMB:tcp/445
    172.18.0.0/16 ←WEB:tcp80/443 , SMB:tcp/445

    送信許可 →
    remoteaddress remoteport
    グローバル →WEB:tcp80/443 , SMTP:tcp/24,587 , FTP:tcp/20,21
    ------------------------

    その為、JSONで記載した場合、以前変更したJSONで対応可能かと想定しておりました。


  2. 別々に定義したらよいのではないでしょうか

    {
    "LocalAddress": "192.168.140.0/24",
    "LocalPorts": [],
    "RemoteAddress": "",
    "RemotePorts": [80,443,445]
    },
    {
    "LocalAddress": "172.16.0.0/16",
    "LocalPorts": [],
    "RemoteAddress": "",
    "RemotePorts": [80,443,445]
    },
    {
    "LocalAddress": "172.18.0.0/16",
    "LocalPorts": [],
    "RemoteAddress": "",
    "RemotePorts": [80,443,445]
    },
    {
    "LocalAddress": "",
    "LocalPorts": [80,443,24,587,20,21],
    "RemoteAddress": "",
    "RemotePorts": []
    }
  3. @mta77

    Questioner

    ご回答大変ありがとうございます。
    JSONの式で1つのブロックに纏める必要があると思っていた為、頂いた内容で求める出力が可能でした。

    なお、確認させて頂きたいことがあり以下に記載いたします。

    ・for、Whileループ対応について
    参考情報とした対応を元に
    while ($true){}をFunction IsAllowed([CimInstance] $connection) ~ }まで
    はさむ形で対応致しましたが、うまく行きません。
    実行部分は 上記の構文に対してかけると想定しておりますが、認識違っておりますでしょうか。

    ・結果出力のログ出力時間の実装
    Get-NetTCPConnection | memberで"CreationTime"が存在しており、出力項目に実装したいと
    思っております。
    "SelectAllowConnection","SelectDenyConnection"はIsAllowed内で実行した結果を出力している為
    "CreationTime"を出力する場合、Get-NetTCPConnectionに記載する形になりますでしょうか。

    何度も申し訳ございません
  4. ループは Export-Csv まででよいと思います。

    以前話をした通り、Export-Csv には -Append をつける必要があります。

    また、sleep なしで連続で処理すると、出力する量も、CPU負荷も大きくなります。適当な sleep を実行したほうが良いと思います。詳しくは Start-Sleep を調べてください。
  5. @mta77

    Questioner

    ありがとうございます。頂いた内容でroopできました。sleep なし~は一定時間ログを取りたかったので、
    --------------
    $timeout = new-timespan -Minutes 1
    $sw = [diagnostics.stopwatch]::StartNew()
    while ($sw.elapsed -lt $timeout){

    }
    ---------------
    で対応いたしました。
  6. @mta77

    Questioner

    以前質問させて頂いた・結果出力のログ出力時間の実装について再度質問させてください。
    Get-NetTCPConnection | memberで"CreationTime"が存在しており、出力項目に実装したいと
    考えおり、JSON側に以下を追加
    ----
    {
    "CreationTime": ""
    },
    ----

    "# 許可リストの作成"部分に
    ----
    $allowItem.CreationTime = $item.CreationTime
    ----
    "# 実行:許可リストにあるものをCSV出力"に
    ----
    Select-Object -Property CreationTime,LocalAddress,LocalPort,RemoteAddress,RemotePort
    ----
    を追加し実施致しましたが、このオブジェクトにプロパティ 'CreationTime' が見つかりませんとでます。

    構文について、お手数ですがご教授いただければ幸いです。
  7. 許可リストの作成の方は修正は不要だと思います

    CSV出力側にだけ足してみたらいいと思います
  8. @mta77

    Questioner

    ご連絡が遅くなり申し訳ありません。ご連絡どおり対応可能でした。
  9. @mta77

    Questioner

    ありがとうございました。

Comments

  1. @mta77

    Questioner

    承知しました。新規投稿で対応いたします。

Your answer might help someone💌