Script: execbot.py

Run remote commands via IRC messages. (for WeeChat ≥ 0.3.0)
Author: TxGVNN — Version: 1.1 — License: GPL3.
Added: 2018-09-29.
Download

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# -*- coding: utf-8 -*-
# execbot.py
# ExecBot - Executing bot
# ==============================
#
# Copyright (C) 2018 Giap Tran <txgvnn@gmail.com>
#  https://github.com/txgvnn/weechat-execbot
#
# == About =====================
# Execbot is a bot can execute the command from IRC message
# (You maybe want to call it is a kind of backdoor)
#
# With server is installed execbot script, the client can execute the
# command remote by send privmsg to server's nick in IRC network
#
# == Usage =====================
# /excebot list                                List server.nicknames are allowed
# /execbot add -server <server> nicknames..    Add server.nicknames are allowed to execute
# /execbot del -server <server> nicknames..    Remove server.nicknames are allowed
#
# == NOTICE ====================
# This is a POC that I want to execute a remote command via IRC
# network. It's really not security, although you can set who can
# talk with the bot.
#
# PLEASE CONSIDER CAREFULLY WHEN USING THE PLUGIN

import weechat

SCRIPT_NAME      = 'execbot'
SCRIPT_AUTHOR    = 'Giap Tran <txgvnn@gmail.com>'
SCRIPT_VERSION   = '1.1'
SCRIPT_LICENSE   = 'GPL3'
SCRIPT_DESC      = 'Executing bot'
SCRIPT_HELP_TEXT = '''
%(bold)sExecbot command options: %(normal)s
list                                   List nicknames
add -server <server>  <nicknames...>   Add server.nicknames are allowed to execute
del -server <server>  <nicknames...>   Del server.nicknames are allowed

%(bold)sExamples: %(normal)s
Allow john and jack in oftc server to talk with bot
  /execbot add -server oftc john jack
''' % {'bold':weechat.color('bold'), 'normal':weechat.color('-bold')}

execbot_config_file      = None
execbot_config_section   = {}
execbot_allows           = {}

def execbot_config_init():
    '''Init configuration file'''
    global execbot_config_file
    execbot_config_file = weechat.config_new('execbot', 'execbot_config_reload_cb', '')
    if not execbot_config_file:
        return

    execbot_config_section['allows'] = weechat.config_new_section(
        execbot_config_file, 'allows', 0, 0, 'execbot_config_allows_read_cb', '',
        'execbot_config_allows_write_cb', '', '', '', '', '', '', '')
    if not execbot_config_section['allows']:
        weechat.config_free(execbot_config_file)

def execbot_config_reload_cb(data, config_file):
    '''Handle a reload of the configuration file.'''
    global execbot_allows
    execbot_allows   = {}
    return weechat.config_reload(config_file)

def execbot_config_allows_read_cb(data, config_file, section_name, option_name, value):
    '''Read elements of the allows section from the configuration file.'''
    execbot_allows[option_name.lower()] = value
    return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED

def execbot_config_allows_write_cb(data, config_file, section_name):
    '''Write infomation to the allows section of the configuration file.'''
    weechat.config_write_line(config_file, section_name, '')
    for username, right in sorted(list(execbot_allows.items())):
        weechat.config_write_line(config_file, username.lower(), right)
    return weechat.WEECHAT_RC_OK

def execbot_config_read():
    ''' Read Execbot configuration file (execbot.conf).'''
    return weechat.config_read(execbot_config_file)

def execbot_config_write():
    ''' Write Execbot configuration file (execbot.conf) to disk.'''
    return weechat.config_write(execbot_config_file)

def execbot_command_list():
    '''List server.nicknames are allowed.'''
    nicknames = '\n'.join([' %s' % x for x in execbot_allows.keys()])
    # for nickname,_ in sorted(list(execbot_allows.items())):
    weechat.prnt(weechat.current_buffer(), 'Nicknames are allowed:\n' +nicknames)
    return weechat.WEECHAT_RC_OK

def execbot_command_add(server,nicknames):
    '''Add nicknames.'''
    for x in nicknames:
        execbot_allows['%s.%s'%(server,x.lower())] = 'allow'
        weechat.prnt(weechat.current_buffer(),'Add permission for %s' % '%s.%s'%(server,x.lower()))
    return weechat.WEECHAT_RC_OK

def execbot_command_del(server,nicknames):
    '''Remove nicknames.'''
    for x in nicknames:
        try:
            del execbot_allows['%s.%s'%(server,x.lower())]
            weechat.prnt(weechat.current_buffer(),'Deleted permission of %s' % '%s.%s'%(server,x.lower()))
        except KeyError:
            weechat.prnt(weechat.current_buffer(),'No existing %s.%s'%(server,x.lower()))
    return weechat.WEECHAT_RC_OK

def execbot_command(data, buffer, args):
    '''Hook to handle the /execbot weechat command.'''
    argv = args.split()

    # list
    if not argv or argv == ['list']:
        return execbot_command_list()

    # check if a server was set
    if (len(argv) > 2 and argv[1] == '-server'):
        server = argv[2]
        del argv[1:3]
        args = (args.split(' ', 2)+[''])[2]
    else:
        server = weechat.buffer_get_string(buffer, 'localvar_server')
    if not server:
        weechat.prnt(weechat.current_buffer(), 'Required -server option')
        return weechat.WEECHAT_RC_ERROR

    # add
    if argv[:1] == ['add']:
        if len(argv) < 2:
            return weechat.WEECHAT_RC_ERROR
        return execbot_command_add(server,argv[1:])

    # del
    if argv[:1] == ['del']:
        if len(argv) < 2:
            return weechat.WEECHAT_RC_ERROR
        return execbot_command_del(server,argv[1:])

    execbot_error('Unknown command. Try  /help execbot', buffer)
    return weechat.WEECHAT_RC_OK

def execbot_process(buffer, command, return_code, out, err):
    '''Execute the command and return to buffer.'''
    message = "%s ... | $? = %d\n" % (command.split()[0], return_code)
    if out != "":
        message += out
    if err != "":
        message += err

    weechat.command(buffer, message)
    return weechat.WEECHAT_RC_OK

def execbot_hook_signal(data, signal, signal_data):
    server = signal.split(",")[0]
    info = weechat.info_get_hashtable("irc_message_parse", { "message": signal_data })
    username = '.'.join([server,info['nick']])

    # Check the permission
    allowed = execbot_allows.get(username.lower())
    if not allowed:
        return weechat.WEECHAT_RC_OK
    # Prevent public channel
    if info['channel'].startswith('#'):
        return weechat.WEECHAT_RC_OK

    # buffer output
    buffer = weechat.buffer_search("irc", username)
    # command
    _, command = info['arguments'].split(':', 1)

    # timeout = 5 mins
    weechat.hook_process(command, 300000, "execbot_process", buffer)

    return weechat.WEECHAT_RC_OK

def execbot_unload_script():
    execbot_config_write()
    return weechat.WEECHAT_RC_OK

if __name__ == '__main__' and weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR,
                                               SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, 'execbot_unload_script','UTF-8'):
    execbot_config_init()
    execbot_config_read()
    weechat.hook_command('execbot', 'Commands to manage Execbot options and execute Execbot commands',
                         '',
                         SCRIPT_HELP_TEXT,
                         'list %- || add -server %(irc_servers) %(nicks) %-'
                         '|| del -server %(irc_servers) %(nicks) %-',
                         'execbot_command', '')
    weechat.hook_signal("*,irc_in2_privmsg", "execbot_hook_signal", "")