środa, 15 lipca 2009

Sieć na nasłuchu

Sieci komputerowe wymagają nadzoru. To nie podlega dyskusji. Nie chodzi jednak wyłącznie o nadzór techniczny. Niestety użytkownicy pozostawieni sami sobie są przyczyną wielu zagrożeń dla infrastruktury sieci, ale i bezpieczeństwa jej składowych. Jednym z głównych zagrożeń jest dostęp do nieautoryzowanych zasobów sieci www. W sieciach korporacyjnych dostęp do takich danych należy blokować, ale co robić w sieci domowej?

Jeśli w sieci istnieje brama domyślna zbudowana z jakiegoś PC, co jest bardzo dobrym rozwiązaniem, to możliwe jest kontrolowanie ruchu wychodzącego z sieci do i ze świata zewnętrznego. Przyjrzyjmy się na początek możliwościom nasłuchu, a nie samej kontroli ruchu.

Początkowo można polegać na różnych narzędziach dostępnych w sieci. Często jednak niestety są to kombajny wymagające sporej mocy obliczeniowej, albo wiedzy administratora. Prędzej czy później okazuje się, że do prowadzenia logów potrzebne są prostsze narzędzia.

Początkowo przydatny jest Wireshark (wspominany już wcześniej). Niestety to właśnie taki niewygodny kombajn. Nie tylko, że wymaga nieco wiedzy, to zbierane logi potrafią zajmować setki megabajtów, pomimo że zawierają całą masę zbędnych nam śmieci. Potem można zacząć szukać jakiś kompaktowych rozwiązań. Przyznam szczerze, że ja osobiście nic nie znalazłem.

Do stworzenia kodu, który zaprezentuję nakłonił mnie znajomy, albo może raczej zainspirował. Zaczęło się od zwykłego pytania "czy i jak można sprawdzać odwiedzane przez domowników strony www?". Sięgnąłem do swojej aktualnej wiedzy i stworzyłem DNSLogger. Wstępnie jest to aplikacja we wczesnej fazie alpha, która jeszcze nie powinna być szeroko stosowana. Z założenia działa, ale nie przeprowadziłem wystarczających testów.

Aplikacja składa się obecnie z dwóch klas. Jedna to już dość standardowy main, a druga to handler do przechwytywania pakietów. Nie znam się za bardzo na ukrywaniu programów w systemie. W C/C++ byłoby to stosunkowo proste, aczkolwiek w języku Java... Pojęcia nie mam gdzie nawet szukać. Pomysł jest jednak proszy. Wystarczy odwołać się do wiedzy o Swing i okienkach z niego. W ten sposób można dodać dość łatwo ukrytą aplikację i to głównie się dzieje w klasie Main.

klasa Main:
import java.io.IOException;

import javax.swing.JFrame;

import jpcap.JpcapCaptor;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;
import jpcap.PacketReceiver;
import handler.dnsPacketReceiver;

public class Main
{

  static JpcapCaptor cap;
  static PacketReceiver rec;

  /**
   * @param args
   */
  public static void main(String[] args)
  {

    JFrame hid=new JFrame(\"SSERVICE\");
    hid.setVisible(false);
    hid.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    hid.setFocusable(false);
    hid.setEnabled(false);
    NetworkInterface[] devices = JpcapCaptor.getDeviceList();
    int index=0;
    if(args.length<1)

    {
      System.out.println(\"usage: java Main <eth nr>\");
      
      for (int i = 0; i < devices.length; i++) {

        System.out.println(i+\" :\"+devices[i].name + \"(\" + devices[i].description+\")\");
        System.out.println(\"    data link:\"+devices[i].datalink_name + \"(\"

            + devices[i].datalink_description+\")\");
        System.out.print(\"    MAC address:\");
        for (byte b : devices[i].mac_address)
          System.out.print(Integer.toHexString(b&0xff) + \":\");

        System.out.println();
        for (NetworkInterfaceAddress a : devices[i].addresses)
          System.out.println(\"    address:\"+a.address + \" \" + a.subnet + \" \"

              + a.broadcast);
      }
    }
    else
    {
      try {
        index=Integer.parseInt(args[0]);
        cap = JpcapCaptor.openDevice(devices[index], 2000, false, 20);
        rec=new dnsPacketReceiver();

      }
      catch (NumberFormatException e) {
        e.printStackTrace();
      }
      catch (IOException e){
        e.printStackTrace();
      }
      cap.loopPacket(-1, rec);
    }
  }

}

Klasa dnsPacketReceiver jest znacznie bardziej istotna. Jej zadaniem jest przechwycenie wszystkich pakietów protokołu UDP i z tych kierowanych na port 53 zapisanie do plików logów treści zapytania. Logi są podzielone na foldery wg IP i na pliki wg dat. W każdym pliku jednemu zapytaniu odpowiada wiersz z zapisanym czasem wysłania zapytania. Problemem może być tutaj tworzenie nowego pliku wg nowej daty. Jest to fragment jak dotąd nieprzetestowany.

klasa dnsPacketReceiver:
package handler;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import jpcap.PacketReceiver;
import jpcap.packet.Packet;
import jpcap.packet.UDPPacket;

public class dnsPacketReceiver implements PacketReceiver 

{
  private String fname;
  private Date date;
  private Date sdate;
  private BufferedWriter wr;
  private SimpleDateFormat df;
  

  public dnsPacketReceiver()
  {
    date=new Date();
    sdate=new Date();
    df=new SimpleDateFormat(\"yyyy-MM-dd\");
    fname=df.format(date)+\".txt\";
  }
  
  public void receivePacket(Packet p)

  {
    String line=\"\";
    int dprt = 0;
    SimpleDateFormat ft=new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
    date=new Date();

    if(p instanceof UDPPacket)
    {
      UDPPacket t=(UDPPacket) p;
      dprt=t.dst_port;
      if(dprt==53)
      {
        line+=ft.format(date)+\" \";
        line+=t.src_port+\" \"+t.dst_port+\"\\t\";

        line+=t.src_ip.getHostAddress()+\"\\t\"+t.dst_ip.getHostAddress();
        String tmp=\"\";
        for(int i=13;t.data[i]!=0;i++)
        {
          if(t.data[i]<0x10)
          {
            tmp+=\".\";
          }

          else
          {
            char c=(char) t.data[i];
            tmp+=c;
          }
        }
        line+=\"\\t\"+tmp;
        //System.out.println(line);
        //sprawdz czy istnieje folder log

        try
        {
        File tmp_f=new File(\"./log/\");
        if(!tmp_f.exists())
          tmp_f.mkdir();
        //sprawdz czy istnieje folder dla IP
        tmp_f=new File(\"./log/\"+t.src_ip.getHostAddress()+\"/\");

        if(!tmp_f.exists())
          tmp_f.mkdir();
        //sprawdz czy nie ma juz nowego dnia
        newName();
        //sprawdz czy istnieje plik z dana data

        File f=new File(\"./log/\"+t.src_ip.getHostAddress()+\"/\"+fname);
        if(!f.exists())
          f.createNewFile();
        //stworz writer
        wr=new BufferedWriter(new FileWriter(f,true));
        //zapisz do pliku

        wr.write(line+\"\\n\");
        //zamknij plik
        wr.close();
        }
        catch (IOException e)
        {
          e.printStackTrace();
        }
        
      }
      else
        return;

    }
    else
      return;
  }
  
  private void newName()
  {
    Date tmp;
    tmp=new Date();
    SimpleDateFormat f=new SimpleDateFormat(\"dd\");

    int d1=Integer.parseInt(f.format(sdate));
    int d2=Integer.parseInt(f.format(tmp));
    
    if(d2-d1!=0)
    {
      sdate=tmp;
      fname=df.format(sdate)+\".txt\";
    }
  }
}

Aplikacja wymaga przy starcie podania numeru karty, na której prowadzony jest nasłuch. W najbliższym czasie wprowadzę pewne poprawki i udogodnienia. Gdy bliżej poznam się z filtrami z wireshark pozwolę sobie na dodanie ich do opcji aplikacji. Opcje zapisywania logów też się oczywiście przydadzą, więc i nimi trzeba się zająć.

Będę wdzięczny za wszelkie sugestie do tego kodu. W przyszłości przychodzi mi też do głowy opcja sterowania programem zdalnie przez Java RMI przykładowo i opcje statystyki logów dostępne zdalnie. To wszystko jest jednak nieco poza zasiegiem na najbliższy czas. Mam nadzieję, że ten pomysł i jego wykonanie przypadnie komuś do gustu.

Przechwytywanie pakietów odbywa się w trybie promicuus. Oznacza to, że może zostać odkryta przez innych użytkowników sieci. Brama domyślna nie może jednak być podejrzana. Zaawansowane działania sieciowe to jej zadanie ;).

Brak komentarzy:

Prześlij komentarz