tshark to konsolowy brat znanego i lubianego sniffera pakietów -
Wiresharka. Powyższy oneliner będzie nasłuchiwał na interfejsie o nazwie
Wi-Fi
pakietów
ICMP Echo Request (pakiety ICMP o typie 8), czyli na popularne pingi. Następnie, będzie wyświetlał ładunek takich pakietów w wersji tekstowej. Co ciekawe, analizując domyślny ładunek takich pingów, możemy stwierdzić z jakiego systemu operacyjnego został wysłany.
W celu wygodniejszej pracy w konsoli z
tsharkiem, moglibyśmy dodać katalog
Wiresharka do zmiennej środowiskowej
$env:PATH
, co zostało omówione w
aptm.in/protip/0075:
[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path','Machine')+';C:\Program Files\Wireshark', 'Machine')
Moglibyśmy wtedy po prostu użyć:
tshark -i Wi-Fi -l -T fields -e data "icmp[0]=8" | % { -join ($_ -split "(.{2})" -match "." | % { [char][byte]"0x$_"}) }
Żeby wszystko było jasne, rozbijmy sobie naszego jednolinijkowca na części pierwsze.
& "C:\Program Files\Wireshark\tshark.exe
- uruchamiamy aplikację tshark.exe
wykorzystując operator wywołania &
, który pozwala uruchomić aplikację, skrypt bądź blok skryptu,
-i Wi-Fi
- wskazujemy interfejs, na którym chcemy sniffować pakiety, w naszym wypadku jest to interfejs Wi-Fi
; by zobaczyć dostępne interfejsy sieciowe, możemy wykorzystać tshark
a z parametrem -D
, czyli: & "C:\Program Files\Wireshark\tshark.exe" -D
lub jeśli Wireshark jest w zmiennej $env:PATH
, po prostu: tshark -D
,
-l
- wyświetlaj natychmiast każdy przechwycony pakiet,
-T fields -e data
- wskazujemy, że interesuje nas jedynie pole data
pakietu, które w przypadku pakietów ICMP Echo request będzie zawierało payload pinga,
"icmp[0]=8"
- definiujemy filtr, przekazując, że interesują nas jedynie pakiety ICMP o typie 8 (ICMP Echo request),
| % { ... }
- każdy przechwycony pakiet wysyłamy do dalszego przetwarzania wykorzystując cmdlet ForEach-Object
posiadający alias %
,
-join ($_ -split "(.{2})" -match "." | % { [char][byte]"0x$_" })
- jako, że tshark
w polu danych zwróci strumień bajtów w postaci szesnastkowej, dzielimy cały string na dwuznakowe łańcuchy (wykorzystując trick z operatorem -split
omówiony już w protipie aptm.in/protip/0072), następnie każdy taki szesnastkowy dwuliterowiec rzutujemy na bajt, a następnie na znak; operator -join
na początku wyrażenia, składa nam tak wygenerowaną tablicę znaków w jeden string.
W powyższym przykładzie nie mamy informacji o adresie pakietu - wyłącznie payload. Dlatego, zamiast
-T fields -e data
możemy poprosić
tshark
a o zwracanie danych w postaci JSON - możemy do tego wykorzystać
-T ek
, co spowoduje wyświetlanie pakietów jako jednolinijkowe stringo JSON (
ek
oznacza format przeznaczony do importu przez
Elasticsearch). Co ciekawe, naturalnym mogłoby się tutaj wydawać
-T json
, jednak nie nada to się do naszych celów, bo tak generowany JSON jest wyświetlany w wielu linijkach, z wcięciami, co utrudnia parsowanie (ale ładnie wygląda;). A dlaczego potrzebujemy każdy pakiet jako jednolijkowy JSON? Ponieważ wykorzystując cmdlet
ConvertFrom-Json
, z łatwością przekonwertujemy pakiety zwracane przez
tshark
a do PowerShellowych obiektów:
& "C:\Program Files\Wireshark\tshark.exe" -i Wi-Fi -l -T ek "icmp[0]=8"|% {
$pkt = ($_|ConvertFrom-Json)
if ($pkt.layers.icmp.data.data_data_data) {
$data = -join ($pkt.layers.icmp.data.data_data_data -split ":"|%{[char][byte]"0x$_"})
$pkt.layers.ip.ip_ip_src + ":" + $data
}
}
Zwróćcie uwagę na to, że tym razem payload pakietu ICMP jest przedstawiony również jako dwuznakowe, szesnastkowe bajty, ale tym razem są rozdzielone dwukropkami, co nieco upraszcza całość.
Żeby poznać z jakimi właściwościami pakietu mamy do czynienia, moglibyśmy na przykład jako ćwieczenie skonwertowany ze stringa obiekt przekonwertować ponownie na JSON, czyli:
& "C:\Program Files\Wireshark\tshark.exe" -i Wi-Fi -l -T ek "icmp[0]=8"|% {
$pkt = ($_|ConvertFrom-Json)
$pkt | ConvertTo-Json
}
Przeanalizujcie poniższy skrypt i zobaczcie co się wydarzy, kiedy do hosta, na którym ten skrypt jest uruchomiony ktoś wyśle pakiet ICMP Echo request z payloadem
nosiemasiema
(np. wykorzystując klasę
Net.NetworkInformation.Ping
którą już wcześniej wykorzystywaliśmy:
aptm.in/protip/0072).
$cmds = @{
"nosiemasiema" = {
$rule = New-NetFirewallRule -DisplayName "8080 - icmp knocking, src=$($args.ip)" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 8080 -RemoteAddress $args.ip
Write-Host "Witamy witamy, regula dla $($args.ip) dodana ($($rule.Name))";
}
}
& 'C:\Program Files\Wireshark\tshark.exe' -i Wi-Fi -l -T ek "icmp[0]=8" | ForEach-Object {
$json = ConvertFrom-Json -InputObject $_
$ip = $json.layers.ip.ip_ip_src
if ($json.layers.icmp.data.data_data_data) {
$data = -join ($json.layers.icmp.data.data_data_data -split ":" | ForEach-Object { [char][Convert]::ToInt16($_, 16) })
if ($cmds.Contains($data)) {
Invoke-Command -ScriptBlock $cmds.$data -ArgumentList @{ip=$ip}
}
}
}
A na koniec wiecie, PowerShell działa również świetnie na Linuksach czy MacOS-ach.
Jeśli chcesz otrzymywać nowe, mięsiste protipy na maila, a także zostać czasem powiadomiony o wartościowych wydarzeniach - dołącz do subskrybentów.