writing irc bots - obscurity systems

40
IRC Bots Lance Buttars AKA Nemus Code From This Talk https://github.com/obscuritysystems/IRC_TALK

Upload: others

Post on 09-Feb-2022

10 views

Category:

Documents


0 download

TRANSCRIPT

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 non-blocking.

•  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

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,"[email protected]","SHEEP")

sheep_regex = '.*[sS$5]+[hH(|-|)]+[eE3]+[pP]+.*’ #metacortex

Text Messaging

import smtplib

SERVER = "10.x.x.1"

FROM = "[email protected]"

TO = ["[email protected]"] # must be a list

SUBJECT = "Hello!"

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)

# Send the mail

server = smtplib.SMTP(SERVER)

server.sendmail(FROM, TO, message)

server.quit()

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/daemon-example.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)

b = StringIO.StringIO()

c.setopt(c.COOKIEFILE, '')

c.setopt(c.FAILONERROR, True)

c.setopt(c.HTTPHEADER, ['Accept: application/json', 'Content-Type: application/x-www-form-urlencoded'])

c.setopt(pycurl.WRITEFUNCTION, b.write)

try:

c.perform()

bitcoin_data = json.loads(b.getvalue())

print bitcoin_data

except pycurl.error, error:

errno, errstr = error

print 'An error occurred: ', errstr

Executing a command getting output

########### pg1

import subprocess

output=subprocess.Popen(["ls", "-lah"], stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()

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 generate_sig(msg,salt):

signature = hmac.new(salt,msg,sha1).hexdigest()

command = msg + '|signature:'+signature

return command

def validate_sig(msg,salt):

rslt = command_sig.split('|')

sig_time = rslt[1].split(':')[1]

sig_cmd = rslt[2]

sig = rslt[3].split(':')[1]

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:

return True

else:

return False

Encryption

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, '[email protected]')

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

http://www.saltycrane.com/blog/2011/10/python-gnupg-gpg-example/

Next Phase Nemus WoRm!

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

http://www.darknet.org.uk/2006/12/writing-worms-for-fun-or-profit/

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