Envoyer des syslog en Powershell

Envoyer des syslog en Powershell

Salut à tous, aujourd’hui je vais m’arrêter sur un bout de script dont j’ai eu besoin il y a quelques mois de cela pour envoyer des syslog en Powershell et que chaque machines où je faisait exécuter un script envoie directement ses résultats à à un serveur syslog. Et puis tant qu’on y était avec Grégoire, on a fait le récepteur syslog au passage histoire de pouvoir tester ça en local sans se faire trop mal à monter un vrai serveur syslog.

Syslog ?

Pour rappel Syslog est un protocole réseau qui permet d’envoyer des logs. Bien connu et largement utilisé, il permet de déporter les logs entre l’équipement et la machine qui les stocke, voire les traite (qui a dit Splunk?). Il y a 2 RFC qui cadrent la norme la RFC 3164 et la RFC 5424. En grande majorité, syslog passe sur UDP, le problème c’est que ça ne garantie pas la bonne arrivée de 100% des logs sur vos collecteurs syslog. Mais on commence à trouver de plus en plus de TCP dans l’utilisation de Syslog.

Chiffrement ? Signature ?

Une petite digression pour votre culture, même si en TCP on garantie la bonne réception du log, rien ne garantie dans syslog qu’un attaquant (en man in the middle par exemple) n’a pas intercepté et modifié les logs reçu par le serveur. Les RFC décrivent un envoi non-chiffre et non-signe, c’est très performant et léger , bien inscrit dans le paysage ce qui n’inquiète pas les admins du coup, mais sans garantie pour un protocole largement utilisé notamment dans les SOC pour des besoin sécurité…

Il existe bien la RFC5848 qui décrit un protocole signé, néanmoins elle est très peu mise en œuvre ou supporté dans les produits, j’ai simplement trouvé ce plugin pour syslog-ng. Un autre effet sympa, c’est que la charge de chiffrement/déchiffrement (signature, c’est la même chose au final) pour les récepteur syslog devient monstrueuse quand le nombre de source augmente. De plus, pour faire proprement cela implique de distribuer, proprement des certificats issu d’une PKI d’entreprise à chaque machine source de logs. Autant vous dire que c’est pas prêt d’arriver demain. L’état de du lard allant plutôt vers des collecteurs HTTP (comme dans Splunk) ou des technos en natifs comme pour Windows PowerShell ou faire du spécifique en interne quand c’est nécessaire.

Envoyer des syslog en Powershell

En UDP

La bonne nouvelle, c’est qu’en UDP il existe déjà tout un tas de gens qui ont fait le taf depuis longtemps et j’ai pas eu besoin de vous redévelopper un truc. Voici ma petite sélection des projets intéressant :

En TCP, c’est mieux!

Le trucs c’est que les projets ci-dessus sont tous en UDP only (au moment ou j’écris ces lignes) et du coup on avait quand même envie de ne pas perdre nos logs dans l’histoire, donc a recodé le script du SANS pour le passer en TCP. Au final, ça a donnée le bout de script ci-dessous :

# Sender
# envoie de syslog en tcp :
$port=514
$remoteHost = "syslog-server.fq.dn.tld"
$Message = 'This is a test of powershell to syslog'
#ref : https://cyber-defense.sans.org/blog/2016/06/01/powershell-function-to-send-udp-syslog-message-packets
$Severity = 5 #notice
$Facility = 4 #Security
$pri = "<" + ($Facility +""+ $Severity) + ">"
$LocaleUS = New-Object System.Globalization.CultureInfo("en-US")
$timestamp =(Get-Date).tostring("MMM dd HH:mm:ss",$LocaleUS) 
$header = $timestamp + " " + ($env:computername).tolower().replace(" ","").trim() + " "
$Tag = "AppTag"
$msg = $pri + $header + $tag + ": " + $Message
# Convert message to array of ASCII bytes.
 $bytearray = $([System.Text.Encoding]::ASCII).getbytes($msg)
# RFC3164 Section 4.1: "The total length of the packet MUST be 1024 bytes or less."
 if ($bytearray.count -gt 996) { $bytearray = $bytearray[0..995] }
$socket = new-object System.Net.Sockets.TcpClient($remoteHost, $port)
$stream = $socket.GetStream()
$stream.Write($bytearray, 0, $bytearray.Length)
$stream.Flush()

Voilà pour envoyer des syslog en Powershell sur TCP. Pour les tests, voici un récepteur syslog TCP en PowerShell qui fonctionne de concert avec le bout de code précédent :

# Listenner
$port = 56666
$endpoint = New-Object System.Net.IPEndPoint ([IPAddress]::Any, $port)
while($true){
    Try {
        $listener = new-object System.Net.Sockets.TcpListener $endpoint
        $listener.start()
        $client = $listener.AcceptTcpClient()
        $stream = $client.GetStream()
    } Catch {
        "$($Error[0])"
    } 
    while(-not $Stream.DataAvailable)
    {
        Start-Sleep(1)
        Write-Host("Listening...")
    } 
    $stringBuilder = New-Object Text.StringBuilder
    Do {
        [byte[]]$byte = New-Object byte[] 1024
        Write-Verbose ("{0} Bytes Left" -f $client.Available)
        $bytesReceived = $Stream.Read($byte, 0, $byte.Length)
        If ($bytesReceived -gt 0) {
            Write-Verbose ("{0} Bytes received" -f $bytesReceived)
            [void]$stringBuilder.Append([text.Encoding]::Ascii.GetString($byte[0..($bytesReceived - 1)]))
        } Else {
            Break
        }
    } While ($Stream.DataAvailable)
    $stringBuilder.ToString()
    $listener.Stop()
} 

Je voulais pas perdre ce bout de code, j’espère qu’il vous servira ! C’est toujours pratique de pouvoir appuyer un script (de logon ou de contrôle par exemple) sur syslog, ça permet de centraliser les résultats sans charger le disque sur les machines avec des logs. La seul limite c’est le réseau, mais sauf si vous envoyez des volumes important, syslog ne consomme pas trop de données en overhead non plus.

Voilà, c’est tout pour aujourd’hui, moi je vous dis à la prochaine.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.