Commit 3d6b3051 authored by awe's avatar awe

new repo

parents
This diff is collapsed.
# docker build . -t ircbot:latest
# docker run -itd --restart=always -v $PWD:/home/ircbot --name ircbot ircbot:latest
From alpine
LABEL maintainer "awe"
RUN apk update && apk upgrade
RUN apk add python3 python3-dev gcc musl-dev libffi-dev openssl-dev capstone py3-capstone py3-lxml py2-httplib2 py-requests
RUN adduser -s /usr/sbin/nologin -k /dev/null -D -H -h /home/ircbot ircbot
RUN pip3 install TwitterAPI
# comment the lines below if you don't care about keystone
RUN apk add make cmake g++
RUN pip3 install keystone-engine
WORKDIR /home/ircbot/
USER ircbot
ENTRYPOINT ["./bot.py"]
# W3-BoT
`W3-BoT` is the W3Challs IRC bot, running on `irc.w3challs.com/6697#w3challs`
It tries to be modular enough to allow easy add/remove of modules.
W3-BoT is highly threaded, it is able to:
- connect to multiple servers
- join multiple channels
- execute multiple commands at the time for each server
- execute "slow" commands that may take minutes to finish
## Plugins
The following categories of modules can be implemented :
* **Plugins** in the `plugins/<category>` directories
* Those are called by the `!<plugin> {<arg>, ...}` commands on channels or private messages with the bot
* They are automatically loaded, and can be reloaded with `!reload`
* You can get more information about a command with `!help command` (command usage) and `!man command` (short description)
* **Hooks** in the `hook/mods-enabled/` directory
* Those are callbacks called upon every line received by the bot (_ex_: prints the youtube details if a youtube link is discovered)
* Just do a symlink from `hook/mods-available/<module>.py` to enable the module
* **Cron jobs** in the `cron/mods-enabled/` directory
* Those are jobs that run every `DELAY` seconds (_ex_: grab the latest tweets)
* Just do a symlink from `crond/mods-available/<module>.py` to enable the module
## Challenge
There's – at least – one vulnerability in the source code. You have to audit the source code to find it, and then exploit it against `W3-BoT` **IN PRIVATE** on `irc.w3challs.com/6697`.
The BoT is on a secret channel, you have to find it and access it. FYI if you find it without exploiting the bot, you'll not be able to solve.
To solve the challenge, run: `/msg W3-BoT !solve w3-bot <channel_name>:<channel_password>`
Don't be afraid by the size of the code compared to other challs ; be clever and you'll quickly identify the potential attack vectors.
**Please follow the following rules:**
- DO NOT spoil the challenge
- DO NOT DoS the bot, that's not the point
- DO NOT try anything or show anything related to the chall on channels! **Only test your ideas in private with the bot.**
- We provide the source and a `Dockerfile` to get it running easily, use it!
- You DO NOT need to `PRIVMSG` the bot 1000 times or more ;)
## Installation
* You may have to fix the API keys (Twitter, Youtube, ...) in `botconfig.py` if you enabled said modules
* Edit the `configuration.xml` to change the IRC server address, bot nickname, etc.
* Easiest is just to run the bot with Docker:
```bash
$ docker build . -t ircbot:latest
$ docker run --rm -v $PWD:/home/ircbot -it --name ircbot ircbot:latest
```
#!/usr/bin/python3
# -*- coding: utf8 -*-
# W3-BoT - A modular IRC bot in python3
# Copyright (C) 2011 Adrien Stoffel <awe.email@gmail.com>
#
# This file is part of W3-BoT.
#
# W3-BoT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# W3-BoT 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with W3-BoT. If not, see <http://www.gnu.org/licenses/>.
import os
os.chdir(os.path.dirname(os.path.realpath(__file__)))
import irc.ircbot
from irc.irccron import IrcCron
from irc.hooks import HookHandler
from botconfig import CONFIGURATION_FILE
import time
import socket
import console.term as term
import threading
import sys
import signal
import lxml.etree
class BotInit:
def __init__(self, server):
""" Init a Bot with its options from the config file, and run it """
try:
host = server.attrib['host']
name = server.attrib['botname']
port = int(server.attrib['port'])
if ('ssl' in server.attrib and server.attrib['ssl'] == '1'):
ssl = 1
else:
ssl = 0
term.fill_points("\n[+] Initialisation of bot " + term.colored(name, attrs=['bold']), term.DONE)
except:
print("Configuration file error:" + str(sys.exc_info()[0]))
sys.exit(1)
while 1:
ssl_txt = " [SSL]" if ssl else ""
while 1:
try:
actions = filter(lambda c: c.tag == 'action', server)
bot = irc.ircbot.IrcBot(name, host, port, ssl, actions)
except socket.error:
term.fill_points("[-] Connection to %s on port %d%s failed, going to retry it now" % (host, port, ssl_txt), term.FAIL)
time.sleep(5)
continue
break
if 'quit_msg' in server.attrib:
bot.server.set_quit_msg(server.attrib['quit_msg'])
for chan in filter(lambda c: c.tag == 'chan', server):
if 'pass' in chan.attrib:
bot.connect_chan(chan.attrib['name'], chan.attrib['pass'])
else:
bot.connect_chan(chan.attrib['name'])
cron = IrcCron()
cron.add_server(bot.server)
bot.run()
reconnect = bot.server.reconnect_me()
cron.del_server(bot.server.get_name())
del bot
if not reconnect:
break
time.sleep(10)
term.fill_points("[-] Connexion to %s on port %d%s closed, reconnecting" % (host, port, ssl_txt), term.FAIL)
def print_usage():
""" Print the Bot usage on standard output """
print("Usage: %s [--help] [--daemon]" % sys.argv[0])
def start_bot():
"""
Start the bot:
- Parse the configuration file
- Register Hooks before threads to avoid race conditions
- Launch every server in a different thread
- Last server is launched in the main thread
- Wait for all servers threads
"""
tree = lxml.etree.parse(CONFIGURATION_FILE)
servers = list(filter(lambda s: s.tag == 'server', tree.getroot()))
max = len(servers)
threads = []
HookHandler()
for i in range(max):
if i == max - 1:
BotInit(servers[i])
else:
threads.append(threading.Thread(None, BotInit, None, (servers[i],), None))
threads[-1].start()
for thread in threads:
thread.join()
def kill_bot(use=0, less=0):
"""
SIGKILL the bot. This is a simple and horrible solution to kill all threads :P
(especially the IrcCron thread, which is a singleton)
It should never be called...
"""
os.kill(os.getpid(), signal.SIGKILL)
if __name__ == "__main__":
""" Bot main, options: -h or --help : print the usage """
signal.signal(signal.SIGINT, kill_bot)
if len(sys.argv) > 1:
if sys.argv[1] in ('-h', '--help'):
print_usage()
else:
print("[-] Invalid option " + sys.argv[1])
else:
start_bot()
kill_bot()
# -*- coding: utf8 -*-
# W3-BoT - A modular IRC bot in python3
# Copyright (C) 2011 Adrien Stoffel <awe.email@gmail.com>
#
# This file is part of W3-BoT.
#
# W3-BoT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# W3-BoT 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with W3-BoT. If not, see <http://www.gnu.org/licenses/>.
""" FIXME: constants to edit to fit your needs """
# Log all incoming text
LOG = 1
# File to log all raw input/output (except PING/PONG)
RAW_LOG = "raw.log"
# Thread timeout (max time for a command to execute)
TIMEOUT = 5
# Flood time (number of seconds to detect a flood)
FLOOD_TIME = 10
# Flood limit (per FLOOD_TIME seconds)
FLOOD_LIMIT = 6
# Ping Timeout
PING_TIMEOUT = 15
# Number of seconds to sleep before re-checking the cron tab
CRON_SLEEP = 1
# Display all incoming text on standard output
VERBOSE = 1
# Plugins path
PLUGINS_PATH = "plugins"
# Crond Path
CRON_PATH = "crond"
# Crond Commands Directory name
CRON_COMMANDS_DIR = "mods-enabled"
# Hooks Path
HOOK_PATH = "hook"
# Hook Commands Directory name
HOOK_COMMANDS_DIR = "mods-enabled"
# Configuration file name
CONFIGURATION_FILE = "configuration.xml"
# SQLite database
DATABASE_FILE = "database/db"
# Default command prefix, *must* be 1 char
COMMAND_PREFIX = "!"
# Bot Version
VERSION = "1.0"
# Bot Sources
SOURCE = "https://git.w3challs.com/challenges/ircbot/"
# Twitter plugins - consumer key
TWITTER_CONSUMER_KEY = '**********************'
# Twitter plugins - consumer secret
TWITTER_CONSUMER_SECRET = '******************************************'
# Twitter plugins - access key
TWITTER_ACCESS_KEY = '**************************************************'
# Twitter plugins - access secret
TWITTER_ACCESS_SECRET = '******************************************'
# Youtube hooks - API key
YOUTUBE_API_KEY = "***************************************"
<?xml version='1.0' encoding='utf-8'?>
<root>
<server host="irc.w3challs.com" botname="W3-BoTz" port="6697" quit_msg="I'll be back" ssl="1">
<!-- <action dst="NickServ" msg="IDENTIFY *********************" /> -->
<user name="awe" status="operator" />
<!--
<cron event="w3challs" params="#w3challs" begin="5" />
<cron event="twitter" params="#tweets" begin="5" />
<chan name="tweets">
<user name="awe" status="operator" />
</chan>
<chan name="w3challs" pass="fuckb0tz">
<user name="awe" status="operator" />
</chan>
-->
</server>
</root>
# -*- coding: utf8 -*-
# W3-BoT - A modular IRC bot in python3
# Copyright (C) 2011 Adrien Stoffel <awe.email@gmail.com>
#
# This file is part of W3-BoT.
#
# W3-BoT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# W3-BoT 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with W3-BoT. If not, see <http://www.gnu.org/licenses/>.
from .termcolor import colored
import os
#rows, columns = os.popen('stty size', 'r').read().split()
rows, columns = (25, 80)
rows, columns = int(rows), int(columns)
DONE = colored("[ DONE ]", 'green', attrs=['bold'])
FAIL = colored("[ FAIL ]", 'red', attrs=['bold'])
PRINT_ON = 1
def fill_points(string, end_text):
"""
Write text to the standard output and complete to the end of the screen with dots.
This is activated only when PRINT_ON is set.
"""
if PRINT_ON:
print(string + ' ' + '.' * (columns - len(string) - len(end_text) + string.count("\033") * 4) + ' ' + end_text)
# Copyright (C) 2008-2009 Konstantin Lepa <konstantin.lepa@gmail.com>.
#
# This file is part of termcolor.
#
# termcolor is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later
# version.
#
# termcolor 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 General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with termcolor. If not, see <http://www.gnu.org/licenses/>.
"""ANSII Color formatting for output in terminal."""
import os
__ALL__ = ['colored']
ATTRIBUTES = dict(zip(['bold', 'dark', '', 'underline', 'blink', '', 'reverse', 'concealed'], range(1, 9)))
del ATTRIBUTES['']
HIGHLIGHTS = dict(zip(['on_grey', 'on_red', 'on_green', 'on_yellow', 'on_blue', 'on_magenta', 'on_cyan', 'on_white'], range(40, 48)))
COLORS = dict(zip(['grey', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',], range(30, 38)))
RESET = '\033[0m'
def colored(text, color=None, on_color=None, attrs=None):
"""
Colorize text.
Available text colors:
red, green, yellow, blue, magenta, cyan, white
Available text highlights:
on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white
Available attributes:
bold, dark, underline, blink, reverse, concealed
Example:
colored('Hello, World!', 'red', 'on_grey', ['blue', 'blink'])
colored('Hello, World!', 'green')
"""
if os.getenv('ANSI_COLORS_DISABLED') is None:
fmt_str = '\033[%dm%s'
if color is not None:
text = fmt_str % (COLORS[color], text)
if on_color is not None:
text = fmt_str % (HIGHLIGHTS[on_color], text)
if attrs is not None:
for attr in attrs:
text = fmt_str % (ATTRIBUTES[attr], text)
text += RESET
return text
if __name__ == '__main__':
print('Current terminal type: ', os.getenv('TERM'))
print('Test basic colors:')
print(colored('Grey color', 'grey'))
print(colored('Red color', 'red'))
print(colored('Green color', 'green'))
print(colored('Yellow color', 'yellow'))
print(colored('Blue color', 'blue'))
print(colored('Magenta color', 'magenta'))
print(colored('Cyan color', 'cyan'))
print(colored('White color', 'white'))
print('-' * 78)
print('Test highlights:')
print(colored('On grey color', on_color='on_grey'))
print(colored('On red color', on_color='on_red'))
print(colored('On green color', on_color='on_green'))
print(colored('On yellow color', on_color='on_yellow'))
print(colored('On blue color', on_color='on_blue'))
print(colored('On magenta color', on_color='on_magenta'))
print(colored('On cyan color', on_color='on_cyan'))
print(colored('On white color', color='grey', on_color='on_white'))
print('-' * 78)
print('Test attributes:')
print(colored('Bold grey color', 'grey', attrs=['bold']))
print(colored('Dark red color', 'red', attrs=['dark']))
print(colored('Underline green color', 'green', attrs=['underline']))
print(colored('Blink yellow color', 'yellow', attrs=['blink']))
print(colored('Reversed blue color', 'blue', attrs=['reverse']))
print(colored('Concealed Magenta color', 'magenta', attrs=['concealed']))
print(colored('Bold underline reverse cyan color', 'cyan', attrs=['bold', 'underline', 'reverse']))
print(colored('Dark blink concealed white color', 'white', attrs=['dark', 'blink', 'concealed']))
print('-' * 78)
print('Test mixing:')
print(colored('Underline red on grey color', 'red', 'on_grey', ['underline']))
print(colored('Reversed green on red color', 'green', 'on_red', ['reverse']))
# -*- coding: utf8 -*-
# W3-BoT - A modular IRC bot in python3
# Copyright (C) 2011 Adrien Stoffel <awe.email@gmail.com>
#
# This file is part of W3-BoT.
#
# W3-BoT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# W3-BoT 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with W3-BoT. If not, see <http://www.gnu.org/licenses/>.
class CronCommand(object):
"""
Abstract class representing a cron command.
Define usefull methods, that are surely always called.
"""
def __init__(self):
self.server = None
self.params = ""
def set_server(self, server):
self.server = server
def set_params(self, params):
self.params = params
# -*- coding: utf8 -*-
# W3-BoT - A modular IRC bot in python3
# Copyright (C) 2011 Adrien Stoffel <awe.email@gmail.com>
#
# This file is part of W3-BoT.
#
# W3-BoT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# W3-BoT 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with W3-BoT. If not, see <http://www.gnu.org/licenses/>.
from crond.croncommand import CronCommand
from irc.colors import *
from botconfig import TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, \
TWITTER_ACCESS_KEY, TWITTER_ACCESS_SECRET
from TwitterAPI import TwitterAPI
class Twitter:
"""
Get last tweets from twitter for a given account every DELAY seconds.
The account must be specified within this file by filling the 4 constants:
CONSUMER_KEY, CONSUMER_SECRET, ACCESS_KEY, ACCESS_SECRET
Those values can be found by creating an app on dev.twitter.com, linking this to the account.
Implements the Singleton design pattern.
"""
class __Twitter (CronCommand):
DELAY = 90
def __init__(self):
CronCommand.__init__(self)
self.last_id = '130998652075376641'
self.first = True
self.api = TwitterAPI(TWITTER_CONSUMER_KEY,
TWITTER_CONSUMER_SECRET,
TWITTER_ACCESS_KEY,
TWITTER_ACCESS_SECRET)
self.run()
def run(self):
try:
tweets = self.api.request('statuses/home_timeline',
{'count': 10, 'since_id': self.last_id})
for tweet in reversed(list(tweets.get_iterator())):
if any(elt not in tweet for elt in ('user', 'text', 'id_str')):
continue
self.last_id = tweet['id_str']
if self.first:
continue
txt = tweet['text']
try:
for url in tweet['entities']['urls']: