Find-BadURL

Diese Seite beschreibt das Skript Find-BadUrl, um IISLogs nach URLs und Parametern auszuwerten um Angriffe zu analysieren und Reverse-Proxy-Regel zu erstellen und zu verifizieren.

Hintergründe

Wer heute einen Webservice im Internet veröffentlicht, muss damit rechnen, dass der Service von extern „geprobt“ wird. Eine Lösung dafür ist, einen Reverse-Proxy zu installieren, der am besten eine Preauthentication macht. Das funktioniert aber nur, wenn die Dienste nur authentifiziert erreichbar sein dürfen. Anonym erreichbare Dienste können aber immer noch hinsichtlich der URL gefiltert werden. Auf einem Exchange Server gibt es z.B. keine PHP-Seiten und auch kein „/cgi-bin“-Verzeichnis. Zumindest sollte es das nicht geben. Aber ein veröffentlichter IIS kann ja auch von intern „unfreundlich erweitert“ werden und wird damit zu einem Risiko. Einen besseren Schutz kann hier ein Reverse-Proxy bieten, der nur erlaubte URLs (cs-uri-stem) weiter gibt. Bessere Produkte analysieren auch noch die URL-Parameter (cs-uri-query) oder sogar die Payload analysieren.

Daher nutzt ich das folgende Skript für folgende Dinge:

  • Auswertung von IISLogs auf ungewöhnliche Zugriffe
    Wenn ich alle „gewünschten“ URLs in einer Liste pflege, dann kann das Skript alle nicht in das Schema passenden Requests ausgeben und mir eine weitere Analyse zu erlauben. Das ist insbesondere interessant, wenn die Anfragen von internen IP-Adressen kommen und aus dem Internet oder vom Reverse Proxy.
  • Aufstellen von Filterregeln für den Reverse-Proxy
    Wenn ich auf dem Reverse-Proxy mit RegEx die URLs filtern will aber das Produkt noch nicht genau kenne, kann ich mir Filter anhand vorhandender IISLogs erstellen und mit dem Skript verifizieren, wie gut diese passen.
  • Überprüfen von Filterregeln im Nachhinein
    Wenn die erstellten Filtern dann auf dem Reverse-Proxy etabliert sind, kann ich danach  gegen die neuen Logs prüfen, ob noch Anfragen durchkommen, die ich bislang nicht gesehen habe. Das funktioniert natürlich nur, wenn der Reverse-Proxy mit einer Backlist statt einer Allowlist arbeitet. Bei einer Allowlist ist es wichtig auf dem Reverse-Proxy die Liste der abgelehnten Anfragen zu analysieren

Insofern kann das Skript zur Analyse bestehender Log-Daten als auch zur Verifikation von Regeln genutzt werden.

Die Regeln

Skype for Business, Exchange und OfficeWebApps sind die Systeme, mit denen ich am meisten zu tun habe. Entsprechend habe ich mir hierfür schon ein Regelwerk erstellt, welches diese Produkte schon abdeckt.

Es kann durchaus sein, dass neuere Versionen neue URLs addieren. Die Lync Mobility Funktion hat z.B. nachträglich das virtuelle Verzeichnis „/Mcx“ addiert. Auch „UCWA“ ist erst später dazu gekommen. Bei Exchange ist „/mapi“ noch relativ jung. Einige Urls sind z.B. vom Namen der SIP-Domain abhängig. Sie müssen die Regeln also immer auf ihre Umgebung erweitern oder anpassen.

"class"   ,"cs-uri-stem"                    ,"cs-uri-query",            "comment"
"lbtest"  ,"^/loadbalancertest.html$",      "^.*$",                     "Generic Testurl for Health"
"base"    ,"^/"$,                           "^-$",                      "Generic Acccess to Default document on Web root not dangerous"
"exchange","^/favicon.ico$",                "^.*$",                     "Generic FavIcon Access"
"exchange","^/owa/.*$",                     "^.*$",                     "Exchange Outlook Web Access"
"exchange","^/owa$",                        "^.*$",                     "Exchange Outlook Web Access 2"
"exchange","^/ews/.*$",                     "^.*$",                     "Exchange WebServices"
"exchange","^/rpc/.*$",                     "^.*$",                     "Exchange Outlook RPCHTTP"
"exchange","^/mapi/.*$",                    "^.*$",                     "Exchange Outlook MAPI"
"exchange","^/ecp/.*$",                     "^.*$",                     "Exchange Control Panel"
"exchange","^/ecp*$",                       "^.*$",                     "Exchange Control Panel 2"
"exchange","^/oab/.*$",                     "^.*$",                     "Exchange Offline Addressbook Download"
"exchange","^/powershell/.*$",              "^.*$",                     "Exchange Remote PowerShell"
"exchange","^/powershell$",                 "^.*$",                     "Exchange Remote PowerShell 2"
"exchange","^/autodiscover/.*$",            "^.*$",                     "Exchange Autodiscover"
"exchange","^/autodiscover$",               "^-$",                      "Exchange Autodiscover 2"
"exchange","^/Microsoft-Server-ActiveSync/default.eas$", "^.*$",        "Exchange ActiveSync"
"exchange","^/.*/healtchcheck.htm$",        "^.*$",                     "Exchange Generic Testurl for Health"
"exchange","^/CookieAuth.dll$",             "^.*$",                     "Exchange OWA Login Cookie"
"sfbext"  ,"^/abs.*$",                      "^.*$",                     "Lync/Skype for Business Addressbook download"
"sfbext"  ,"^/Autodiscover/.*$",            "^.*$",                     "Lync/Skype for Business Lync Discover"
"sfbext"  ,"^/CertProv/.*",                 "^.*$",                     "Lync/Skype for Business Certificate Providing Service"
"sfbext"  ,"^/CollabContent/.*$",           "^.*$",                     "Lync/Skype for Business Meeting Content"
"sfbext"  ,"^/DataCollabWeb/.*$",           "^.*$",                     "Lync/Skype for Business Meeting DataCollab"
"sfbext"  ,"^/dialin.*$",                   "^.*$",                     "Lync/Skype for Business Conference Dialin Information Web"
"sfbext"  ,"^/Fonts/.*$",                   "^.*$",                     "Lync/Skype for Business Generic Fonts"
"sfbext"  ,"^/groupexpansion/.*$",          "^.*$",                     "Lync/Skype for Business Group Expansion"
"sfbext"  ,"^/HybridConfig/.*$",            "^.*$",                     "Lync/Skype for Business Hybrid Setup"
"sfbext"  ,"^/lwa/.*$",                     "^.*$",                     "Lync/Skype for Business Lync Web Access"
"sfbext"  ,"^/Mcx.*$",                      "^.*$",                     "Lync/Skype for Business Mobil Communication Extension"
"sfbext"  ,"^/meet.*$",                     "^.*$",                     "Lync/Skype for Business meeting Join"
"sfbext"  ,"^/PassiveAuth/.*$",             "^.*$",                     "Lync/Skype for Business "
"sfbext"  ,"^/PersistentChat/.*$",          "^.*$",                     "Lync/Skype for Business PChat"
"sfbext"  ,"^/Reach/.*$",                   "^.*$",                     "Lync/Skype for Business Meeting Join Supplemental"
"sfbext"  ,"^/RequesthandlerExt/.*$",       "^.*$",                     "Lync/Skype for Business "
"sfbext"  ,"^/RgsClients/AgentService.svc/webticket_bearer$","^-$",     "Lync/Skype for Business Response Group Client"
"sfbext"  ,"^/Scheduler/.*$",               "^.*$",                     "Lync/Skype for Business Meeting Web Scheduler"
"sfbext"  ,"^/Storage/.*$",                 "^.*$",                     "Lync/Skype for Business Storage Access"
"sfbext"  ,"^/ucwa/.*$",                    "^.*$",                     "Lync/Skype for Business Unified Communication Web  API"
"sfbext"  ,"^/WebTicket/WebTicketService.svc.*$","^-$",                 "Lync/Skype for Business WebTicket Service"
"sfbext"  ,"^/.*/.*/.*$",                   "^.*$",                     "Lync/Skype generic Meeting Joint Link"
"sfbext"  ,"^/$",                           "^sipuri=.*@.*$",           "Lync/Skype for Business Base for SIP Lyncdiscover"
"sfbext"  ,"^/sip:.*@.*$",                  "^-$",                      "Lync/Skype for Business Base for SIP Lyncdiscover"
"wac"     ,"^/hosting/.*$",                 "^.*$",                     "OfficeWebApp"
"wac"     ,"^/m/.*$",                       "^.*$",                     "OfficeWebApp Powerpoint Meeting"
"wac"     ,"^/o/.*$",                       "^.*$",                     "OfficeWebApp OneNote"
"wac"     ,"^/oh/.*$",                      "^.*$",                     "OfficeWebApp OH"
"wac"     ,"^/op/.*$",                      "^.*$",                     "OfficeWebApp OP"
"wac"     ,"^/p/.*$",                       "^.*$",                     "OfficeWebApp PowerPoint"
"wac"     ,"^/we/.*$",                      "^.*$",                     "OfficeWebApp Word Editor"
"wac"     ,"^/wv/.*$",                      "^-*$",                     "OfficeWebApp Word Viewer"
"wac"     ,"^/x/.*$",                       "^.*$",                     "OfficeWebApp Excel"
"wac"     ,"^/en-us/.*$",                   "^-$",                      "OfficeWebApp Language Files"
"bad"     ,"^.*$",                          "^.*$",                     "Any other URL"

Natürlich können Sie die Liste auch anpassen und per Parameter unterschiedliche Listen spezifizieren.

Aufruf

Das Skript selbst wird in einer PowerShell interaktiv aufgerufen. Um eine automatische Verarbeitung zu erlauben, habe ich mit Write-Verbose und Write-Debug gearbeitet, damit diese Ausgaben dann auch mit Start-Transript protokollierbar sind. Der Param-Abschnitt zeigt die möglichen Parameter:

[cmdletbinding()]
param (
    [parameter(ValueFromPipeline=$True)]
    [string]$iisline,
    $urldefinitionfile = ".\find-badurl.csv",
    $inputdelimiter = " ",
    $removeselection = @("exchange","sfb","lbtest")
)
  • $IISLine
    Die „$iisline“ kommt in der Regel per Pipeline und die Regeldatei und das Trennzeichen für die IISLogs ist hier vorbelegt.
  • $urldefinitionfile
    Dies ist der Pfad zur CSV-Datei mit den URL-Suchstring
  • $InputDelimiter
    Kennzeichnet das Trennformat der IIS-Datei und ist fast immer ein Leerzeichen (Space)
  • $Removeslection
    Die Variable „$Removeslection“ bestimmt, welche Zeilen nicht ausgegeben werden. Diese Einstellung nutze ich, um die „erkannten“ Exchange und Skype for Business Zeilen gleich zu unterdrücken, so dass nur die „unklassifizierten“ oder „BAD“-Zeilen ausgegeben werden

Hier ein exemplarischer Aufruf:

Get-content `
   -path \\sfb.msxfaq.net\c$\inetpub\logs\LogFiles\W3SVC34578\*.log `
| .\find-badurl.ps1 `
   -verbose `
| Out-File result.txt

Dieser Aufruf liest als Administrator über den UNC-Pfad alle Logdateien im IISLog eines Skype for Business Servers. Da per Default die URLs von Exchange, Skype For Business und Office Web App schon gefiltert werden, bleiben nur die "unerwünschten" oder bislang nicht erkannten URLs übrig, die einer genaueren Untersuchung unterworfen werden sollten.

Beispiel eines Scan/Angriffs

Wer einen Exchange Webserver oder Skype for Business Web Services im Internet veröffentlicht, muss immer damit rechnen, dass jemand von extern das System "abklopft". Es gibt verschiedene Werkzeuge dies automatisch durchzuführen. Die "guten" Werkzeuge arbeiten derart, dass nicht kaputt geht sondern nur geprüft wird, ob der erreichte Server etwas tut, was er nicht tun sollte. Die Steigerung sind natürlich Skripte, die anhand erkannter Schwachstellen dann möglichst automatisch gleich einen Schadcode aktivieren. Die bösen Skripte tarnen sich natürlich extra hinter fremden IP-Adressen und fälschen den UserAgent. Sollten Sie z.B. "OpenVAS" finden, dann war es der folgende Scanner.

Wenn sie solche Zugriffe finden und einen entsprechenden Dienstleister mit der Überprüfung beauftrag haben, dann ist das in Ordnung. Das Scannen von fremden Systemen ohne Rücksprache oder Auftrag dürfte strafrechtlich relevant sein. Wenn ich mit dem Skript aber ein IISLog entsprechend auswerte und die erwünschten URLs ausfiltere, dann finden sich sehr schnell und einfach Zugriffe, die nicht hingehören. An den Port 8080/4443 sehen sie, dass es sich hier um einen Skype for Business Server handeln dürfte.

2015-06-23 10:51:25 192.168178.12 GET /phpMyAdmin - 8080 - 10.11.67.10 Mozilla/4.0+(compatible;+MSIE+9.0;+Windows+NT+6.1) http://62.159.244.169/phpMyAdmin 404 0 2 5
015-06-25 22:02:08 192.168178.12 GET /admin/config.php - 4443 - 10.11.67.10 python-requests/2.11.0 - 404 0 2 3
2015-06-25 22:44:54 192.168178.12 GET /admin/config.php - 8080 - 10.11.67.10 - - 404 0 2 13
2015-06-26 02:00:04 192.168178.12 GET /xmlrpc.php - 8080 - 10.11.67.10 - - 404 0 2 2
2015-06-26 09:27:22 192.168178.12 GET /phpmyadmin - 8080 - 10.11.67.10 python-requests/2.11.1 - 404 0 2 5
2015-06-26 13:26:41 192.168178.12 GET /tmUnblock.cgi - 8080 - 10.11.67.10 Wget(linux) - 404 0 2 2
2015-06-27 12:52:07 192.168178.12 GET /admin/config.php - 4443 - 10.11.67.10 curl/7.15.5+(x86_64-redhat-linux-gnu)+libcurl/7.15.5+OpenSSL/0.9.8b+zlib/1.2.3+libidn/0.6.5 - 404 0 2 201
2015-06-28 11:02:47 192.168178.12 GET /User/login - 8080 - 10.11.67.10 Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/51.0.2704.63+Safari/537.36 - 404 0 2 6
2015-06-28 16:55:58 192.168178.12 GET /admin/config.php - 4443 - 10.11.67.10 python-requests/2.11.0 - 404 0 2 204
2015-06-29 06:10:59 192.168178.12 GET /a2billing/ - 4443 - 10.11.67.10 python-requests/2.6.0+CPython/2.6.6+Linux/2.6.32-573.18.1.el6.x86_64 - 404 0 2 13
2015-06-29 15:12:57 192.168178.12 HEAD /robots.txt - 8080 - 10.11.67.10 - - 404 0 2 4
2015-07-02 03:28:20 192.168178.12 GET /index.jsp - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 4
2015-07-02 03:28:20 192.168178.12 GET /dm/index.php - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 3
2015-07-02 03:28:22 192.168178.12 GET /asbru/index.jsp - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 3
2015-07-02 03:28:23 192.168178.12 GET /scripts/index.php - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 3
2015-07-02 03:28:26 192.168178.12 GET /cgi-bin/index.php - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 3
2015-07-02 03:28:26 192.168178.12 GET /offiria/index.php - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 4
2015-07-02 03:28:28 192.168178.12 GET /phpBB2/index.php - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 3
2015-07-02 03:28:30 192.168178.12 GET /2PCw_BAHTsUs.shtml - 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 3

2015-07-02 03:42:45 192.168178.12 GET /awiki/index.php page=/winnt/win.ini 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 3
2015-07-02 03:42:48 192.168178.12 GET /scripts/add.php lang=../../../../../../../../../windows/win.ini%00 4443 - 10.11.67.10 Mozilla/5.0+[en]+(X11,+U;+OpenVAS+7.0.10) - 404 0 2 2

Interessant ist das natürlich auch z.B. für die MSXFAQ. Wenn ich mit eine Liste der gültigen URLs als Filterdatei erstelle, dann könnte ich nach ungültigen URLs suchen, die nicht mit einem 404 beantwortet wurden. Für kommerzielle Seiten ist so eine Log-Auswertung in Echtzeit eine interessante Option.

Performance

Das Skript ist aktuell nicht auf "Performance" getrimmt. So ist "Get-Content" nicht sehr schnell und vor allem die Verifikation der verschiedenen RegEx-Ausdrücke pro Zeile mit dem "-match"-Parameter kann man sicher durch vorkompilierte Ausdrücker beschleunigen. Wie lange das Skript braucht hängt auch von der Anzahl der Regeln ab,

Auf einem durchschnittlichen Office-Notebook (Lenovo T410) konnten 100 MB Logs mit ca. 541.000 Zeilen in 6,5 Minuten verarbeitet werden. Das ergibt ca. 1387 Zeilen/Sekunde oder ca. 282kb/Sekunde. Von einem 360 Megabte Logfile (Exchange) bleiben je nach Menge der "Angriffe" nur wenige Megabyte übrig.

Das Skript ist so sicher nicht für eine Permanentüberwachung von großen WebServern gedacht. Dazu wäre dann vielleicht eher ein „Security Information and Event Management (SIEM)“ geeignet, welches die Logs quasi in RealTime analysiert und weiter verarbeitet. Aber es bleibt eine Möglichkeit bei einer Momentaufnahme.

Weiterentwicklung

Jeder WebServer schreibt sehr viele Logs mit. Eigentlich wäre es durchaus interessant, solche „404 Not Found“ Zugriffe zeitlich zu erfassen und Häufungen zu erkennen. Das geht beim IIS aber schon mittels Performance Counter. Unter dem Punkt „Web Service“ gibt es den Counter „Total Not Found Errors“, der genau die Anzahl 404 Fehler meldet.

Einen Anstieg dieses Werts ist ein Zeichen, dass mal jemand wieder ihr System abscannt. Die wenigsten Firmen werden aber basierend darauf die Source-IP für eine gewisse Zeit aussperren.

Viel interessanter ist eine Analyse dahingehend, welche ein Zugriff auf eine eigentlich ungültige URL doch mit einem 2xx-Code oder 3xx Code beantwortet wurden. Das sind dann schon eher Hinweise auf einen „erfolgreichen“ Einbruch oder Missbrauch eines Servers durch nachträglich hinzugekommene Applikationen. Auch die Unterscheidung des Verb nach GET, POST, HEAD könnte Falschalarme und Erkennungsraten verbessern.

Weitere Links