IRC Bots Lance Buttars AKA Nemus Code From This Talk https://github.com/obscuritysystems/irc_talk
Ready Made Bots Phenny - Python http://inamidst.com/phenny/ Cinch Ruby https://github.com/cinchrb/cinch Egg Drop C,TCl http://www.eggheads.org/ http://en.wikipedia.org/wiki/comparison_of_internet_relay_chat_bots
Why Write an IRC Bot? Arduino Bot pyserial Control Real Devices Speaking Bot espeak Say Things Command and Control Bot - bash Run Commands Error Notification Bot sockets / web API Send messages to groups of people
NemusBOT! (Sheep) Python Code https://github.com/obscuritysystems/nemusbot IRC Components Socket connection Join channel. Check for identity and register. While Loop Message parsing. - Executing commands.
UDP Socket Client import socket UDP_IP = "127.0.0.1" UDP_PORT = 5005 MESSAGE = "Hello, World!" print "UDP target IP:", UDP_IP print "UDP target port:", UDP_PORT print "message:", MESSAGE sock = socket.socket(socket.af_inet, socket.sock_dgram) sock.sendto(message, (UDP_IP, UDP_PORT))
UPD Socket Server import socket UDP_IP = "127.0.0.1" UDP_PORT = 5005 sock = socket.socket(socket.af_inet,socket.sock_dgram) sock.bind((udp_ip, UDP_PORT)) while True: data, addr = sock.recvfrom(1024) print "received message:", data
TCP Client import socket TCP_IP = '127.0.0.1' TCP_PORT = 5005 BUFFER_SIZE = 1024 MESSAGE = "Hello, World! s = socket.socket(socket.af_inet, socket.sock_stream) s.connect((tcp_ip, TCP_PORT)) s.send(message) data = s.recv(buffer_size) s.close() print "received data:", data
TCP Server #!/usr/bin/env python import socket TCP_IP = '127.0.0.1' TCP_PORT = 5005 BUFFER_SIZE = 20 # Normally 1024, but we want fast response s = socket.socket(socket.af_inet, socket.sock_stream) s.bind((tcp_ip, TCP_PORT)) s.listen(1) conn, addr = s.accept() print 'Connection address:', addr while 1: data = conn.recv(buffer_size) print "received data:", data conn.send(data) # echo conn.close()
Blocking Sockets http://docs.python.org/2/howto/sockets.html In Python, you use socket.setblocking(0) to make it nonblocking. When the socket is non blocking it will throw an error when there is no data or the socket is not available to write data. The major mechanical difference is that send, recv, connect and accept can return without having done anything. You have (of course) a number of choices. You can check return code and error codes and generally drive yourself crazy.
Python Socket Select ready_to_read, ready_to_write, in_error = \ select.select( potential_readers, potential_writers, potential_errs, timeout) You pass select three lists: the first contains all sockets that you might want to try reading; the second all the sockets you might want to try writing to, and the last (normally left empty) those that you want to check for errors. You should note that a socket can go into more than one list. The select call is blocking, but you can give it a timeout In return, you will get three lists. They contain the sockets that are actually readable, writable and in error. Each of these lists is a subset (possibly empty) of the corresponding list you passed in.
Connecting to an IRC SERVER ### CODE ircsock = socket.socket(socket.af_inet, socket.sock_stream) # Here we connect to the server using the port 6667 ircsock.connect(( chat.freenode.net, 6667)) ircsock.send('user '+nicks+' host '+host_name+' : Nemus Brand Bot\r\n') ### OUPUT :pratchett.freenode.net NOTICE * :*** Looking up your hostname... :pratchett.freenode.net NOTICE * :*** Checking Ident :pratchett.freenode.net NOTICE * :*** Found your hostname :pratchett.freenode.net NOTICE * :*** No Ident response :pratchett.freenode.net 001 WhyYouMakeaMeBot :Welcome to the freenode Internet Relay Chat Network WhyYouMakeaMeBot :pratchett.freenode.net 002 WhyYouMakeaMeBot :Your host is pratchett.freenode.net[192.168.25.107/6667], running version ircd-seven-1.1.3
IRC Protocol IRC RFC http://tools.ietf.org/html/rfc2812 IRC Over Telnet http://oreilly.com/pub/h/1963 The message format Parameters: :<prefix> <command> <params> :<trailing> test@localhost PRIVMSG #mychannel :Hello everyone!
Prefix Parameter The prefix of the messages represents the origin of the message, If there is no prefix, then the source of the message is the server for the current connection, as in the PING example. PING :wright.freenode.net The presence of a prefix is indicated by the message beginning with a colon character. The prefix cannot contain a white space so you can parse the first part and stop at the prefix. So in this example :write.freenode.net is the prefix :wright.freenode.net NOTICE * :*** Looking up your hostname
The Command Parameter The command part is the meat of the IRC message instructing your bot what action needs to be taken. Example Commands PRIVMSG, QUIT, JOIN, MODE, and PING. In the case of the PRIVMSG example, an bot might log a users message to the database, display it on the terminal or execute some command. With QUIT and JOIN, the Bot would keep change state to keep track a user has quit the server or joined the channel
Numeric Commands Some messages contain commands as text other messages have numeric replies. An IRC bot will receive numeric replies in the event it sends a requests to the irc server. Example we send the command NICK BadABot We Receive :irc.localhost.localdomain 433 BadABot:Nickname is already in use In the message the 433 portion is the command Looking in the RFC for the IRC protocol we see the reply of 433 is used if a nickname is already in use.
The Params Parameter The params portion is a set of space separated parameters. Not all messages have parameters, but many do. test@localhost PRIVMSG #channel :Hello The channel name (#channel) of the PRIVMSG, JOIN, and MODE messages is a parameter
The Trail The Trail is the last parameter and because params are separated by space, it isn't possible to include the trail parameter with a space in the normal set of parameters. The very last parameter is indicated with a leading colon, this means that everything after the colon should be interpreted together. This allows a message to carry one fully textual piece. Later we will further parse this string for our specific IRC BOT commands The defining characteristic of the trailing part is that it also begins with a colon but is preceded by a space. The trailing portions continues until the end of the message Simply grab the substring of the message that begins at the first occurrence of " :" (a space and colon).
Regular Expression Parsing import re string1 = ':wright.freenode.net NOTICE * :*** Looking up your hostname... regex = '^(:(\S+) )?(\S+)( (?!:)(.+?))?( :(.+))?$ matchobj = re.match(regex, string1, re.m re.i) if matchobj: print "matchobj.group() : ", matchobj.group() print "matchobj.group(1): ", matchobj.group(1) print "matchobj.group(2): ", matchobj.group(2) print "matchobj.group(3): ", matchobj.group(3) print "matchobj.group(4): ", matchobj.group(4) print "matchobj.group(5): ", matchobj.group(5) print "matchobj.group(6): ", matchobj.group(6) else: print "No match!!"
Code Parsing of IRC messages def parsemsg(s): prefix = '' trailing = [] if not s: raise IRCBadMessage("Empty line.") if s[0] == ':': prefix, s = s[1:].split(' ', 1) if s.find(' :')!= -1: s, trailing = s.split(' :', 1) args = s.split() args.append(trailing) else: args = s.split() command = args.pop(0) return prefix, command, args
Regex Sheep sheep_regex = '.*[ss$5]+[hh( - )]+[ee3]+[pp]+.* #metacortex if self.sheep and text.find(':!sheep_paging') == -1: regex_array = re.findall(self.sheep_regex,text) self.sendm(parsed_msg['handle'].split('!~')[0] + ' said \'sheep\'. Paging L34N. Someone said \'sheep\''); self.send_email(parsed_msg, self.sheep_number,"sheepalarm@dc801.com","sheep")
Text Messaging import smtplib SERVER = "10.x.x.1" FROM = "sender@example.com" TO = ["801xxxxxxx@vtext.com"] # must be a list SUBJECT = "Hello!" # Send the mail server = smtplib.smtp(server) server.sendmail(from, TO, message) server.quit() TEXT = "This message was sent with Python's smtplib. # Prepare actual message message = """\ From: %s To: %s Subject: %s %s """ % (FROM, ", ".join(to), SUBJECT, TEXT)
Basic Bot # Import some necessary libraries. import socket import time # Some basic variables used to configure the bot server = "chat.freenode.net" # Server channel = "#test198" # Channel botnick = "NemusBot2" # Your bots nick nicks = "NemusBot2" host_name = "Test
Basic Bot 2 # This is our first function! It will respond to server Pings. def ping(): ircsock.send("pong :pingis\n ) # This is the send message function, it simply sends messages to the channel. def sendmsg(chan, msg): ircsock.send("privmsg "+ chan +" :"+ msg +"\n ) # This function is used to join channels. def joinchan(chan): ircsock.send("join "+ chan +"\n ) # This function responds to a user that inputs "Hello Mybot" def hello(): ircsock.send("privmsg "+ channel +" :Hello!\n )
Basic Bot 3 ircsock = socket.socket(socket.af_inet, socket.sock_stream) # Here we connect to the server using the port 6667 ircsock.connect((server, 6667)) ircsock.send('user '+nicks+' host '+host_name+' : Nemus Brand Bot\r\n') # here we actually assign the nick to the bot ircsock.send("nick "+ botnick +"\n") # Join the channel using the functions we previously defined joinchan(channel)
Basic Bot 4 while 1: # receive data from the server ircmsg = ircsock.recv(2048) # removing any unnecessary linebreaks. ircmsg = ircmsg.strip('\n\r') # Here we print what's coming from the server print(ircmsg) # If we can find "Hello Mybot" it will call the function hello() if ircmsg.find(":hello "+ botnick)!= -1: hello() # if the server pings us then we've got to respond! if ircmsg.find("ping :")!= -1: ping()
Process Forking
Forking #!/usr/bin/env python """A basic forking action" import os def my_fork(): child_pid = os.fork() if child_pid == 0: print "Child Process: PID# %s" % os.getpid() else: print "Parent Process: PID# %s" % os.getpid() if name == " main ": my_fork()
Daemon Class #!/usr/bin/env python import sys, time from daemon import Daemon class MyDaemon(Daemon): def run(self): while True: time.sleep(1) #bot code here if name == " main ": daemon = MyDaemon('/tmp/daemonexample.pid') if len(sys.argv) == 2: if 'start' == sys.argv[1]: daemon.start() elif 'stop' == sys.argv[1]: daemon.stop() elif 'restart' == sys.argv[1]: daemon.restart() else: print "Unknown command sys.exit(2) sys.exit(0) else: print "usage: %s start stop restart" % sys.argv[0] sys.exit(2) http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python
Speaking Bot https://github.com/obscuritysystems/ SpeakingNemusBot
Simple IPC
Sqlite3 Simple IPC (Inter Process Communcation) import sqlite3 as lite self.con = lite.connect('/tmp/speakbot.db') cur = self.con.cursor() cur.execute("create TABLE if not exists talk ( id INTEGER PRIMARY KEY AUTOINCREMENT, handle TEXT, channel TEXT, message TEXT)") self.con.commit() cur = self.con.cursor() cur.execute('insert INTO talk(handle,channel,message) values (?,?,?)',(parsed_msg['handle'],parsed_msg['channel'],parsed_msg['text'])) self.con.commit()
Espeak for Speaking Bot while self.running: time.sleep(1) cur = self.con.cursor() cur.execute("select id,handle,channel,message from talk limit 1") rows = cur.fetchall() for row in rows: #print row speak = re.sub(r'[^\w]', ' ', row[3]) print row cmd = 'espeak -s 130 "%s"'% speak print cmd cur.execute("delete from talk where id =?",(row[0],)) self.con.commit()
Python Curl Web Calls import pycurl import json import StringIO c = pycurl.curl() c.setopt(c.url, 'http://data.mtgox.com/api/2/btcusd/ money/ticker') c.setopt(c.connecttimeout, 5) c.setopt(pycurl.followlocation, 1) c.setopt(c.timeout, 8) try: c.perform() bitcoin_data = json.loads(b.getvalue()) print bitcoin_data except pycurl.error, error: errno, errstr = error print 'An error occurred: ', errstr b = StringIO.StringIO() c.setopt(c.cookiefile, '') c.setopt(c.failonerror, True) c.setopt(c.httpheader, ['Accept: application/json', 'Content-Type: application/x-www-formurlencoded']) c.setopt(pycurl.writefunction, b.write)
Executing a command getting output ########### pg1 import subprocess output=subprocess.popen(["ls", "-lah"], stdout=subprocess.pipe,stderr=subprocess.pipe).communica te() print output ################### pg2 import os retvalue = os.system( ls") print retvalue Goes to standard out
Command Function def command(msg): regex = '^(:(\S+) )?(\S+)( (?!:)(.+?))?( :(.+))?$' matchobj = re.match(regex, msg, re.m re.i) trail = matchobj.group(6) commands = trail.split() commands.remove(commands[0]) # remove first part of the trail print commands output=subprocess.popen(commands, stdout=subprocess.pipe,stderr=subprocess.pipe).communicate() pre = "PRIVMSG "+ channel +" :"+str(output)+"\n" ircsock.send(pre)
Authorization import hmac, time from hashlib import sha1 from base64 import b64encode, b64decode def validate_sig(msg,salt): rslt = command_sig.split(' ') sig_time = rslt[1].split(':')[1] def generate_sig(msg,salt): sig_cmd sig = rslt[2] = rslt[3].split(':')[1] signature = hmac.new(salt,msg,sha1).hexdigest() command = msg + ' signature:'+signature return command now = time.time() time_diff = abs(float(sig_time) - now) if time_diff < 5.00 : command = rslt[0]+' '+rslt[1] + ' '+rslt[2] print command gen_signature = hmac.new(salt,command,sha1).hexdigest() if gen_signature == sig: else: return True return False
Encryption http://www.saltycrane.com/blog/2011/10/python-gnupg-gpg-example/ import gnupg gpg = gnupg.gpg(gnupghome='/home/testgpguser/gpghome') unencrypted_string = 'Who are you? How did you get in my house?' encrypted_data = gpg.encrypt(unencrypted_string, 'testgpguser@mydomain.com') encrypted_string = str(encrypted_data) print 'ok: ', encrypted_data.ok print 'status: ', encrypted_data.status print 'stderr: ', encrypted_data.stderr print 'unencrypted_string: ', unencrypted_string print 'encrypted_string: ', encrypted_string
Next Phase Nemus WoRm! http://www.darknet.org.uk/2006/12/writing-worms-for-fun-or-profit/ SSH Brute Forcing Twisted Python Conch http://twistedmatrix.com/documents/current/conch/ examples/ VNC Brute Forcing and Control https://github.com/sibson/vncdotool RDP using rdesktop http://www.soldierx.com/tutorials/brute-forcing-rdp-linux-rdesktop
San's Paper on Writing Code for BotNets http://www.sans.org/reading_room/ whitepapers/covert/byob-build-botnet_33729
That s it for the Sexy Boting