From ac4f180dd8be1505f33931597181cef22566fbc1 Mon Sep 17 00:00:00 2001 From: kageru Date: Thu, 2 Jan 2020 23:10:28 +0100 Subject: [PATCH] Implement bot --- .gitignore | 2 +- Cargo.toml | 4 ++-- README.md | 2 +- config.toml | 24 +++++++++++++++++++++--- src/commands.rs | 43 +++++++++++++++++++------------------------ src/config.rs | 18 ++++++++++++++++++ src/file.rs | 9 --------- src/main.rs | 24 ++++++++---------------- 8 files changed, 70 insertions(+), 56 deletions(-) create mode 100644 src/config.rs delete mode 100644 src/file.rs diff --git a/.gitignore b/.gitignore index a0bd527..7d27a2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /target **/*.rs.bk -secret +prod_config.toml diff --git a/Cargo.toml b/Cargo.toml index f00aec3..40c22c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ authors = ["kageru "] edition = "2018" [dependencies] -serenity = "0.7" +serenity = "0.7.4" serde = "1.0.104" toml = "0.5.5" lazy_static = "1.4.0" -itertools = "0.8.2" +cmd_lib = "0.7.8" diff --git a/README.md b/README.md index 1beba97..321932e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # RCEAADB ## I’m sorry? -RCEAABD. Remote code execution as a discord bot. +RCEAADB. Remote code execution as a discord bot. Almost as good as ncmpcpp. ## But why? Do you hate security that much? diff --git a/config.toml b/config.toml index 7c6504f..a2b3211 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,22 @@ +secret = "your login secret" + [[command]] -trigger = ">restart_example" -command = "/usr/bin/restart_something" -users = [12345, 54321] +# The prefix (a constant in the source code) +# does not need to be specified here. +# It is added automatically. +trigger = "create_file" +# Spaces are not escaped here +command = "touch /tmp/test-rce" +users = [137780880344088576] + +[[command]] +trigger = "append_to_file" +# Pipes and redirection are allowed +command = "echo asd >> /tmp/test-rce" +users = [137780880344088576] + +[[command]] +trigger = "delete_file" +command = "rm /tmp/test-rce" +# Multiple user IDs can be added here +users = [137780880344088576, 123456789098765] diff --git a/src/commands.rs b/src/commands.rs index ba550be..030e6aa 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,41 +1,36 @@ -use itertools::Itertools; -use lazy_static; +use super::config::CONFIG; +use cmd_lib::{CmdResult, Process}; use serde::Deserialize; use serenity::model::channel::Message; #[derive(Deserialize, Debug)] -pub struct Command<'a> { - trigger: &'a str, - command: &'a str, +pub struct Command { + trigger: String, + command: String, users: Vec, } -#[derive(Deserialize, Debug)] -struct Config { - #[serde(rename = "command")] - commands: Vec>, -} - -lazy_static! { - static ref RAW_CONFIG: String = super::file::read_file("config.toml").join("\n"); - static ref COMMANDS: Vec> = { - toml::from_str::(&RAW_CONFIG).expect("Error in config").commands - }; -} - pub fn print_commands() { - COMMANDS.iter().for_each(|c| println!("{:?}", c)); + CONFIG.commands.iter().for_each(|c| println!("{:?}", c)); } pub fn find_matching(message: &str) -> Option<&Command> { - COMMANDS.iter().find(|&c| message.starts_with(&c.trigger)) + CONFIG.commands.iter().find(|&c| message[1..] == c.trigger) } -impl<'a> Command<'a> { - pub fn execute(&self, msg: &Message) -> Result<(), &'static str> { +impl Command { + /// Check permissions for a command and execute it. + /// Returns an error for insufficient permissions or non-zero return codes. + pub fn execute(&self, msg: &Message) -> Result<(), String> { + println!( + "User {} tried to execute command {}", + &msg.author, &msg.content + ); if !self.users.contains(&msg.author.id.0) { - return Err("You don’t have the permissions to execute this command."); + return Err("You don’t have the permissions to execute this command.".to_owned()); } - unimplemented!() + Process::new(self.command.clone()) + .wait::() + .map_err(|e| format!("{:?}", e)) } } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..e434881 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,18 @@ +use super::commands::*; +use serde::Deserialize; +use std::fs; + +pub fn read_config() -> String { + fs::read_to_string("config.toml").expect("Could not read config file. Make sure a file named config.toml exists in the current working directory.") +} + +#[derive(Deserialize, Debug)] +pub struct Config { + #[serde(rename = "command")] + pub commands: Vec, + pub secret: String, +} + +lazy_static! { + pub static ref CONFIG: Config = toml::from_str(&read_config()).unwrap(); +} diff --git a/src/file.rs b/src/file.rs deleted file mode 100644 index dda620e..0000000 --- a/src/file.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::fs::File; -use std::io::{prelude::*, BufReader}; - -pub fn read_file(path: &'static str) -> impl Iterator { - let reader = BufReader::new(File::open(path).expect("File not found")); - reader - .lines() - .map(|l| l.expect(&format!("Could not read from file"))) -} diff --git a/src/main.rs b/src/main.rs index 52e65a4..42b4bcd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,24 +5,17 @@ use serenity::model::channel::Message; use serenity::model::id::ChannelId; use serenity::prelude::*; mod commands; -mod file; +mod config; pub fn main() { - /* - Client::new( - file::read_file("secret") - .next() - .expect("The file containing the login secret is present but empty"), - Handler, - ) - .expect("Error creating client") - .start() - .expect("Could not connect to discord"); - */ print_commands(); + Client::new(&config::CONFIG.secret, Handler) + .expect("Error creating client") + .start() + .expect("Could not connect to discord"); } -const PREFIX: char = '$'; +const PREFIX: char = '>'; pub struct Handler; impl EventHandler for Handler { @@ -33,10 +26,9 @@ impl EventHandler for Handler { if let Some(command) = find_matching(&msg.content) { let response = match command.execute(&msg) { Err(e) => e, - // TODO: maybe check the return code here? - Ok(()) => "Done", + Ok(()) => "Done".to_owned(), }; - send(msg.channel_id, response, &ctx); + send(msg.channel_id, &response, &ctx); } } }