O'Reilly Hacks
oreilly.comO'Reilly NetworkSafari BookshelfConferences Sign In/My Account | View Cart   
Book List Learning Lab PDFs O'Reilly Gear Newsletters Press Room Jobs  


 
Buy the book!
IRC Hacks
By Paul Mutton
July 2004
More Info

HACK
#69
Get a Remote Shell
It's great to have remote access to an operating system. Build such features into your own IRC bot!
The Code
[Discuss (0) | Link to this hack]

The Code

Because this bot is going to allow remote access to a shell, it will be called ShellBot. The access password will be stored in the field password, and it will respond to any private message that contains only this password.

When a user sends the correct password, the bot will start up a new ProcessThread in which to handle the shell process.

Save the following as ShellBot.java:

import org.jibble.pircbot.*;

public class ShellBot extends PircBot {
    
    private String password = "password";
    
    public ShellBot(String name) {
        setName(name);
    }
    
    public void onPrivateMessage(String sender, String login,
            String hostname, String message) {
        
        if (message.trim( ).equals(password)) {
            // Password was correct, so create a new thread to handle the shell.
            Thread thread = new ProcessThread(this, sender);
            thread.start( );
        }
    }
    
}

The ProcessThread class launches the shell process in its own thread so that it doesn't block the normal operation of the bot. The thread starts by sending a DCC CHAT request to the user and waits for up to 120,000 milliseconds for the recipient to establish the connection to the bot. If the connection is not made in time, the bot gives up and the user will have to resend the correct password and try again.

If the user's IRC client accepts the DCC CHAT request in time, it will establish a direct TCP connection to the bot. This connection will be used to provide the remote shell access, as it is not subject to flood prevention restrictions like normal IRC messages.

This particular bot is set up to run on Windows, so it executes cmd.exe to provide shell access. If you are using an older version of Windows, you may have to change this to command.com. If you wish to use ShellBot on Unix/Linux or Mac, you will need to change it to /bin/sh or suchlike.

Save the following as ProcessThread.java:

import org.jibble.pircbot.*;
import java.io.*;

public class ProcessThread extends Thread {
    
    private PircBot bot;
    private String nick;
    
    public ProcessThread(PircBot bot, String nick) {
        this.bot = bot;
        this.nick = nick;
    }
    
    public void run( ) {
        DccChat chat = null;
        try {
            // Send a DCC CHAT request.
            chat = bot.dccSendChatRequest(nick, 120000);
            if (chat != null) {
                Runtime runtime = Runtime.getRuntime( );
                
                // Use "/bin/sh" on Linux.
                Process p = runtime.exec("cmd.exe");
                
                // Start a new thread to handle input from the client.
                BufferedWriter writer = new BufferedWriter(
                        new OutputStreamWriter(p.getOutputStream( )));
                ProcessInputThread inputThread =
                        new ProcessInputThread(chat, writer);
                inputThread.start( );
                
                // Return all lines from the shell.
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(p.getInputStream( )));
                String line;
                while ((line = reader.readLine( )) != null) {
                    chat.sendLine(line);
                }
            }
        }
        catch (IOException e) {
            // Chat session ended unexpectedly.
        }
        
        if (chat != null) {
            try {
                chat.close( );
            }
            catch (IOException e) {
                // Do nothing.
            }
        }
        
    }
    
}

The ProcessThread runs the shell process and reads all lines that it outputs. Each line is then sent down the DCC connection to the connected client. The shell process is running all the time, so you must create another thread to handle input from the connected client.

Save the following as ProcessInputThread.java:

import org.jibble.pircbot.*;
import java.io.*;

public class ProcessInputThread extends Thread {
    
    private DccChat chat;
    private BufferedWriter writer;
    
    public ProcessInputThread(DccChat chat, BufferedWriter writer) {
        this.chat = chat;
        this.writer = writer;
    }
    
    public void run( ) {
        try {
            // Kludge... write a blank line to shell so path shows up.
            writer.write("\r\n");
            writer.flush( );
            
            // Pass each line from the client to the process.
            String line;
            while ((line = chat.readLine( )) != null) {
                writer.write(line + "\r\n\r\n");
                writer.flush( );
            }
        }
        catch (Exception e) {
            // Just let the thread end...
        }
    }
    
}

Because DCC CHAT is designed to send and receive only complete lines, you will not see the command-line prompt, as it does not end with a newline character. The kludge fix for this is to enter a blank line after every input from the user. This is implemented in the ProcessInputThread class and causes the command-line prompt to appear each time the shell is ready to receive the next command; it also causes it to be echoed a second time when each command is processed.

The ShellBot doesn't need to join any channels, as it responds only to private messages, but it is sometimes useful to place such a bot in a channel so that you know it's there.

Save the following as ShellBotMain.java:

public class ShellBotMain {
    
    public static void main(String[] args) throws Exception {
        ShellBot bot = new ShellBot("ShellBot");
        bot.setVerbose(true);
        bot.connect("irc.freenode.net");
        bot.joinChannel("#irchacks");
    }
    
}


O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.