#!/usr/bin/python
""" Script for attempting to put a machine into stealth mode so that it isn't giving away
personally identifiable information to a network.
For privacy reasons obviously, definitely shouldn't be used for stealing wireless or anything
else unruly or nefarious.
Author: sham (sham@southerndarkness.net)
Usage:
First define a policy, generally one of the built in ones will work for you. Then run it with
silentrunning.py {policy_name}
Policies are defined as dicts, the following keys are available for use:
firewall: A dict describing a firewall policy
bad_services: List of service names controlled by OS service handler e.g. init.d that must be stopped
un_services: List of service names that don't need to be there, cos they could give away things, but doesn't
really matter if they don't stop
bad_process: List of processes we need dead, things like NetworkManager that could give away SSIDs etc
commands: List of commands you want run as part of setting a policy
out_iface: The name of the interface packets will go out
"""
import logging
import os
import sys
import socket
import struct
import optparse
from fcntl import ioctl
import shamlib
BIN_REQUIRES = ['pidof']
AUTO_SUDO = True
fw_policies = {}
policies = {}
## Firewall Policies
fw_policies['dns_outbound'] = {
'name' : 'dns_outbound',
'outbound_tcp' : ['53'],
'outbound_udp' : ['53'],
'inbound_tcp' : [],
'inbound_udp' : []
}
fw_policies['tor_only'] = {
'name' : 'tor_only',
'outbound_tcp' : [9050],
'outbound_udp' : [],
'inbound_tcp' : [],
'inbound_udp' : []
}
fw_policies['ssh_only'] = {
'name' : 'ssh_only',
'outbound_tcp' : [22],
'outbound_udp' : [53],
'inbound_tcp' : [],
'inbound_udp' : []
}
fw_policies['open_outbound'] = {
'name' : 'open_outbound',
'outbound_tcp' : ['any'],
'outbound_udp' : ['any'],
'inbound_tcp' : [],
'inbound_udp' : []
}
fw_policies['open'] = {
'name' : 'open',
'outbound_tcp' : ['any'],
'outbound_udp' : ['any'],
'inbound_tcp' : ['any'],
'inbound_udp' : ['any']
}
## Full Policies
"""
Policy configuration options:
name: string, display name of the policy
bad_services: list of strings, services that are critical to stop via system stop method (/init.d)
un_services: as per bad_services, but not considered critical if they don't stop
good_services: list of strings, services to start via system service manager
bad_processes: list of strings, processes to kill by name (kill -9)
commands: list of strings, commands to run once things are configured
firewall: dict of firewall policy as per fw_policies dict
out_iface: string, the outbound interface (if non-existant or default we will guess)
"""
policies['dns_tunnel'] = {
'name' : 'DNS Tunnel',
'bad_services' : ['cupsys', 'bluetooth', 'avahi-daemon', 'vmware', 'lirc',
'vmware-server'],
'un_services' : ['xinetd', 'inetd', 'mysql', 'mysql-server', 'lirc'],
'good_services' : ['tor', 'nstx'],
'bad_processes' : ['NetworkManager'],
'commands' : [],
'firewall' : fw_policies['dns_outbound'],
'out_iface' : 'ath0'
}
policies['ssh_tunnel'] = {
'name': 'SSH Tunnel',
'bad_services': ['cupsys', 'bluetooth', 'avahi-daemon', 'vmware', 'lirc', 'vmware-server'],
'un_services': ['xinetd', 'inetd', 'mysql', 'mysql-server', 'lirc'],
'good_services': [],
'bad_processes': ['NetworkManager'],
'commands': [],
'firewall': fw_policies['ssh_only'],
'options': []
}
# globals
originalmacs = {}
def main():
print ''
usagestring = '%prog [-v] policy\n'
usagestring += 'where policy is one of [' + ' '.join(policies) + ']'
version = '0.1'
parser = optparse.OptionParser(usage = usagestring, version = version)
parser.add_option('-v', '--verbose', action="count", dest='verbosity', default=0,
help='turn on verbosity')
(options, args) = parser.parse_args()
# setup logging
stdout_logformat='%(message)s'
if options.verbosity > 1:
loglevel = logging.DEBUG
#elif options.verbosity > 0:
# loglevel = logging.INFO
else:
loglevel = logging.INFO
logging.basicConfig(level=loglevel, format=stdout_logformat)
# check we have a policy arg
if len(args) < 1:
logging.error("You didn't provide a policy")
parser.print_usage()
sys.exit(1)
else:
policy_str = sys.argv[1]
if not policy_str in policies:
logging.error("Error: invalid location")
parser.print_usage()
sys.exit()
else:
policy = policies[policy_str]
logging.info('#############################################################################')
logging.info('Silent Running')
logging.info('sham@southerndarkness.net')
logging.info('#############################################################################')
logging.info('using policy ' + policy_str + ': ' + policy['name'])
logging.debug(policy)
# root privs check
if os.getuid():
if AUTO_SUDO and shamlib.BinDep('sudo'):
# this is probably bad practice, but it saves typing :)
iam = os.path.abspath(__file__)
logging.warn('!!!!!!!!!!!! Lacking privs, attempting to respawn with sudo using: ' + iam)
args = ['sudo',iam]
args.extend(sys.argv[1:])
os.execvp('sudo', args)
else:
logging.error("You normally need root priviliges to run this, you will probably get errors!")
# check perms
if shamlib.CheckSecurePerms(__file__):
logging.warn('This script will run as root, but is world writeable, you should fix this')
# get the MAC addresses for each interface
original_macs = {}
for interface in shamlib.GetInterfaces():
original_macs['interface'] = shamlib.GetInterfaceMAC(interface)
# services that can sometimes actively give away information about your machine
# stop services
if policy.has_key('bad_services'):
logging.info('Shutting down services')
for service in policy['bad_services']:
if not shamlib.StopService(service, True):
logging.error('Could not stop bad service "%s" terminating' % service)
sys.exit(1)
# un services
if policy.has_key('un_services'):
logging.info('Shutting down unecessary services')
for service in policy['bad_services']:
shamlib.StopService(service)
# start services
if policy.has_key('good_services'):
logging.info('Starting up services')
for service in policy['good_services']:
shamlib.StartService(service)
# TODO stop ipv6 in case we leak inadvertently
# Take out any automated network managers they tend to do undesirable things like give away our home SSID
if policy.has_key('bad_processes'):
logging.info('Killing Bad Processs')
for process in policy['bad_processes']:
logging.info(' killing %s' % process)
if not shamlib.ProcessKillByName(process, confirm=True, signal=9):
logging.error('Could not stop bad process "%s" terminating' % process)
sys.exit(1)
## Randomize macs
logging.info('Randomizing MAC addresses')
# get original mac addresses
interfaces = shamlib.GetInterfaces()
for interface in interfaces:
originalmacs['interface'] = shamlib.GetInterfaceMAC(interface)
# do the changes
up_interfaces = shamlib.GetUpInterfaces()
interfaces.remove('lo')
for interface in interfaces:
# an interface usually has to be down to accept a MAC change
if interface in up_interfaces:
logging.info('Downing interface %s' % interface)
os.system('ifconfig %s down' % interface)
shamlib.RandomizeMAC(interface)
logging.info('Raising interface %s' % interface)
os.system('ifconfig %s up' % interface)
else:
shamlib.RandomizeMAC(interface)
# TODO check that none of our interfaces have their original MAC (in case something failed)
shamlib.CheckMAC(interfaces, original_macs)
# Kill all interfaces except for required ones
# TODO
# Run up an iptables to block everything outbound except what is specifically required
logging.info('Setting up iptables policies')
logging.debug('Using policy ' + str(policy))
# Run up an iptables
if policy.has_key('firewall') and policy['firewall']:
if 'out_iface' not in policy:
policy['out_iface'] = shamlib.GetDefaultRouteInterface()
if not policy['out_iface']:
logging.warn('Cannot set firewall policy, no out interface specified')
else:
logging.info('Setting up iptables policies')
logging.info('Using policy ' + policy['firewall']['name'])
iptables = shamlib.IPTablesGen()
iptables_script = iptables.Generate(policy['firewall'], policy['out_iface'])
logging.debug(iptables_script)
for line in iptables_script:
os.system(line)
logging.info('Firewall policy configured')
else:
logging.warn('No firewall policy configured for this policy, leaving as is')
if __name__ == "__main__":
main()