package de.tudarmstadt.informatik.hostage; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.Socket; import java.security.SecureRandom; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.json.JSONObject; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.net.ConnectivityManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.widget.Toast; import de.tudarmstadt.informatik.hostage.commons.HelperUtils; import de.tudarmstadt.informatik.hostage.logging.MyLocationManager; import de.tudarmstadt.informatik.hostage.logging.UglyDbHelper; import de.tudarmstadt.informatik.hostage.protocol.HTTP; import de.tudarmstadt.informatik.hostage.protocol.Protocol; import de.tudarmstadt.informatik.hostage.ui.MainActivity; /** * Background service running as long as at least one protocol is active. * Service controls start and stop of protocol listener. Notifies GUI about * events happening in the background. Creates Notifications to inform the user * what it happening. * * @author Mihai Plasoianu * @author Lars Pandikow * @author Wulf Pfeiffer */ public class HoneyService extends Service { private static Context context; /** * Returns the application context. * * @return context. */ public static Context getContext() { return HoneyService.context; } private LinkedList implementedProtocols; private ArrayList listeners = new ArrayList(); private NotificationCompat.Builder builder; private SharedPreferences connectionInfo; private Editor connectionInfoEditor; public List getListeners() { return listeners; } private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { public HoneyService getService() { return HoneyService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { super.onCreate(); HoneyService.context = getApplicationContext(); implementedProtocols = getImplementedProtocols(); connectionInfo = getSharedPreferences(getString(R.string.connection_info), Context.MODE_PRIVATE); connectionInfoEditor = connectionInfo.edit(); createNotification(); registerNetReceiver(); updateConnectionInfo(); getLocationData(); String sharedPreferencePath = getString(R.string.shared_preference_path); boolean useQotd = getSharedPreferences(sharedPreferencePath , MODE_PRIVATE).getBoolean("useQotd", true); if(useQotd) { new QotdTask().execute(new String[] {}); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { // We want this service to continue running until it is explicitly // stopped, so return sticky. return START_STICKY; } @Override public void onDestroy() { cancelNotification(); unregisterNetReceiver(); super.onDestroy(); } /** * Starts an Instance of MyLocationManager to set the location within this * class. */ private void getLocationData() { MyLocationManager locationManager = new MyLocationManager(this); locationManager.getUpdates(60 * 1000, 3); } /** * Deletes all session related data. */ private void deleteConnectionData() { connectionInfoEditor.clear(); connectionInfoEditor.commit(); } /** * Register broadcast receiver for connectivity changes */ private void registerNetReceiver() { // register BroadcastReceiver on network state changes IntentFilter intent = new IntentFilter(); intent.addAction(ConnectivityManager.CONNECTIVITY_ACTION); // "android.net.conn.CONNECTIVITY_CHANGE" registerReceiver(netReceiver, intent); } /** * Unregister broadcast receiver for connectivity changes */ private void unregisterNetReceiver() { unregisterReceiver(netReceiver); } /** * Receiver for connectivity change broadcast. * * @see MainActivity#BROADCAST */ private BroadcastReceiver netReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String bssid_old = connectionInfo.getString(getString(R.string.connection_info_bssid), ""); String bssid_new = HelperUtils.getBSSID(context); if (bssid_new == null || !bssid_new.equals(bssid_old)) { deleteConnectionData(); updateConnectionInfo(); getLocationData(); notifyUI(this.getClass().getName(), new String[]{getString(R.string.broadcast_connectivity)}); } } }; /** * Notifies the GUI about a event. * * @param sender * Source where the event took place. * @param key * Detailed information about the event. */ public void notifyUI(String sender, String[] values) { createNotification(); // Send Notification if (sender.equals(HoneyHandler.class.getName()) && values[0].equals(R.string.broadcast_started)) { attackNotification(); } // Inform UI of Preference Change Intent intent = new Intent(getString(R.string.broadcast)); intent.putExtra("SENDER", sender); intent.putExtra("VALUES", values); Log.i("Sender" ,sender); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } /** * Returns an LinkedList with the names of all implemented protocols. * * @return ArrayList of * {@link de.tudarmstadt.informatik.hostage.protocol.Protocol * Protocol} */ private LinkedList getImplementedProtocols() { String[] protocols = getResources().getStringArray(R.array.protocols); String packageName = Protocol.class.getPackage().getName(); LinkedList implementedProtocols = new LinkedList(); for (String protocol : protocols) { try { implementedProtocols.add((Protocol) Class.forName(String.format("%s.%s", packageName, protocol)).newInstance()); } catch (Exception e) { e.printStackTrace(); } } return implementedProtocols; } /** * Returns the default port number, if the protocol is implemented. * @param protocolName The name of the protocol * @return Returns the default port number, if the protocol is implemented. Else returns -1. */ private int getDefaultPort(String protocolName){ for (Protocol protocol : implementedProtocols) { if(protocolName.equals(protocol.toString())){ return protocol.getDefaultPort(); } } return -1; } /** * Determines if there any listener is currently running. * * @return True if there is a running listener, else false. */ public boolean hasRunningListeners() { for (HoneyListener listener : listeners) { if (listener.isRunning()) return true; } return false; } /** * Determines if a protocol with the given name is running on its default port. * @param protocolName The protocol name * @return True if protocol is running, else false. */ public boolean isRunning(String protocolName){ int port = getDefaultPort(protocolName); return isRunning(protocolName, port); } /** * Determines if a protocol with the given name is running on the given port. * @param protocolName The protocol name * @param port Specific port * @return True if protocol is running, else false. */ public boolean isRunning(String protocolName, int port){ for (HoneyListener listener : listeners) { if (listener.getProtocolName().equals(protocolName) && listener.getPort() == port) { return listener.isRunning(); } } return false; } /** * Determines the number of active connections for a protocol running on its default port. * @param protocolName The protocol name * @return Number of active connections */ public int getNumberOfActiveConnections(String protocolName){ int port = getDefaultPort(protocolName); return getNumberOfActiveConnections(protocolName, port); } /** * Determines the number of active connections for a protocol running on the given port. * @param protocolName The protocol name * @param port Specific port * @return Number of active connections */ public int getNumberOfActiveConnections(String protocolName, int port){ for (HoneyListener listener : listeners) { if (listener.getProtocolName().equals(protocolName) && listener.getPort() == port) { return listener.getHandlerCount(); } } return 0; } /** * Creates a Listener for a given protocol on a specific port. * After creation the Listener is not started. * Checks if the protocol is implemented first. * * @param protocolName Name of the protocol * @param port Port on which to start the Listener * @return Returns the created HoneyListener, if creation failed returns null. */ private HoneyListener createListener(String protocolName, int port){ for(Protocol protocol : implementedProtocols){ if(protocolName.equals(protocol.toString())){ HoneyListener listener = new HoneyListener(this, protocol, port); listeners.add(listener); return listener; } } return null; } /** * Starts all listeners which are not already running. */ public void startListeners() { for (HoneyListener listener : listeners) { if (!listener.isRunning()) { listener.start(); } } Toast.makeText(getApplicationContext(), "SERVICES STARTED!", Toast.LENGTH_SHORT).show(); } /** * Stops all running listeners. */ public void stopListeners() { for (HoneyListener listener : listeners) { if (listener.isRunning()) { listener.stop(); } } Toast.makeText(getApplicationContext(), "SERVICES STOPPED!", Toast.LENGTH_SHORT).show(); } /** * Starts the listener for the specified protocol. * Creates a new HoneyService if no matching HoneyListener is found. * * @param protocolName * Name of the protocol that should be started. */ public boolean startListener(String protocolName) { return startListener(protocolName, getDefaultPort(protocolName)); } /** * Starts the listener for the specified protocol and port. * Creates a new HoneyService if no matching HoneyListener is found. * * @param protocolName * Name of the protocol that should be started. * @param port The port number in which the listener should run. */ public boolean startListener(String protocolName, int port) { for (HoneyListener listener : listeners) { if (listener.getProtocolName().equals(protocolName) && listener.getPort() == port) { if (!listener.isRunning()) { if(listener.start()){ Toast.makeText(getApplicationContext(),protocolName + " SERVICE STARTED!", Toast.LENGTH_SHORT).show(); return true; } Toast.makeText(getApplicationContext(),protocolName + " SERVICE COULD NOT BE STARTED!", Toast.LENGTH_SHORT).show(); return false; } } } HoneyListener listener = createListener(protocolName, port); if(listener != null){ if(listener.start()){ Toast.makeText(getApplicationContext(), protocolName + " SERVICE STARTED!", Toast.LENGTH_SHORT).show(); return true; } } Toast.makeText(getApplicationContext(),protocolName + " SERVICE COULD NOT BE STARTED!", Toast.LENGTH_SHORT).show(); return false; } /** * Stops the listener for the specified protocol. * * @param protocolName * Name of the protocol that should be stopped. */ public void stopListener(String protocolName) { stopListener(protocolName, getDefaultPort(protocolName)); } /** * Stops the listener for the specified protocol. * * @param protocolName * Name of the protocol that should be stopped. * @param port The port number in which the listener is running. */ public void stopListener(String protocolName, int port) { for (HoneyListener listener : listeners) { if (listener.getProtocolName().equals(protocolName) && listener.getPort() == port) { if (listener.isRunning()) { listener.stop(); } } } Toast.makeText(getApplicationContext(), protocolName + " SERVICE STOPPED!", Toast.LENGTH_SHORT).show(); } /** * Task for accuiring a qotd from one of four possible servers. * * @author Wulf Pfeiffer */ private class QotdTask extends AsyncTask { @Override protected String doInBackground(String... unused) { String[] sources = new String[] { "djxmmx.net", "ota.iambic.com", "alpha.mike-r.com", "electricbiscuit.org" }; SecureRandom rndm = new SecureRandom(); StringBuffer sb = new StringBuffer(); try { Socket client = new Socket(sources[rndm.nextInt(4)], 17); BufferedReader in = new BufferedReader(new InputStreamReader( client.getInputStream())); while (!in.ready()) ; while (in.ready()) { sb.append(in.readLine()); } in.close(); client.close(); } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } @Override protected void onPostExecute(String result) { if (result != null) HTTP.setHtmlDocumentContent(result); } }; /** * Updates the connection info and saves them in the the SharedPreferences * for session data. * * @param context * Needs a context to get system recourses. * @see MainActivity#CONNECTION_INFO */ private void updateConnectionInfo() { SharedPreferences pref = context.getSharedPreferences( getString(R.string.connection_info), Context.MODE_PRIVATE); Editor editor = pref.edit(); editor.putString(getString(R.string.connection_info_ssid), HelperUtils.getSSID(context)); editor.putString(getString(R.string.connection_info_bssid), HelperUtils.getBSSID(context)); editor.putString(getString(R.string.connection_info_internal_ip), HelperUtils.getInternalIP(context)); editor.commit(); SetExternalIPTask async = new SetExternalIPTask(); async.execute(new String[] { "http://ip2country.sourceforge.net/ip2c.php?format=JSON" }); } /** * Task to find out the external IP. * * @author Lars Pandikow */ private class SetExternalIPTask extends AsyncTask { @Override protected String doInBackground(String... url) { String ipAddress = null; try { HttpClient httpclient = new DefaultHttpClient(); HttpGet httpget = new HttpGet(url[0]); HttpResponse response; response = httpclient.execute(httpget); HttpEntity entity = response.getEntity(); entity.getContentLength(); String str = EntityUtils.toString(entity); JSONObject json_data = new JSONObject(str); ipAddress = json_data.getString("ip"); } catch (Exception e) { e.printStackTrace(); } return ipAddress; } @Override protected void onPostExecute(String result) { connectionInfoEditor.putString(getString(R.string.connection_info_external_ip), result); connectionInfoEditor.commit(); notifyUI(this.getClass().getName(), new String[]{getString(R.string.broadcast_connectivity)}); } }; // Notifications /** * Creates a Notification in the notification bar. */ private void createNotification() { UglyDbHelper dbh = new UglyDbHelper(this); boolean activeHandlers = false; boolean bssidSeen = false; boolean listening = false; for (HoneyListener listener : listeners) { if(listener.isRunning()) listening = true; if (listener.getHandlerCount() > 0) { activeHandlers = true; } if (dbh.bssidSeen(listener.getProtocolName(), HelperUtils.getBSSID(getApplicationContext()))) { bssidSeen = true; } } builder = new NotificationCompat.Builder(this).setContentTitle( getString(R.string.app_name)).setWhen( System.currentTimeMillis()); if(!listening){ builder.setSmallIcon(R.drawable.ic_launcher); builder.setContentText("HosTaGe is not active."); } else if (activeHandlers) { builder.setSmallIcon(R.drawable.ic_service_red); builder.setContentText("Network is infected!"); } else if (bssidSeen) { builder.setSmallIcon(R.drawable.ic_service_yellow); builder.setContentText("Network has been infected in previous session!"); } else{ builder.setSmallIcon(R.drawable.ic_service_green); builder.setContentText("Everything looks fine!"); } TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(new Intent(this, MainActivity.class)); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(resultPendingIntent); builder.setOngoing(true); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(1, builder.build()); } /** * Updates the notification when a attack is registered. */ private void attackNotification() { SharedPreferences defaultPref = PreferenceManager .getDefaultSharedPreferences(this); String strRingtonePreference = defaultPref.getString( "pref_notification_sound", "content://settings/system/notification_sound"); builder = new NotificationCompat.Builder(this) .setContentTitle(getString(R.string.app_name)) .setTicker("Honeypot under attack!") .setContentText("Honeypot under attack!") .setSmallIcon(R.drawable.ic_service_red).setAutoCancel(true) .setWhen(System.currentTimeMillis()) .setSound(Uri.parse(strRingtonePreference)); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(new Intent(this, MainActivity.class)); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(resultPendingIntent); if (defaultPref.getBoolean("pref_vibration", false)) { builder.setVibrate(new long[] { 100, 200, 100, 200 }); } NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(2, builder.build()); } /** * Cancels the Notification */ private void cancelNotification() { NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.cancel(1); } }