Skip to content

pingbird/tangent

Repository files navigation

Tangent - A discord bot with access to a full Linux VM

How it works

Tangent uses libvirt to manage a qemu controlled Debian 9 virtual machine and communicates to it through a custom json rpc like protocol.

tangent-server contains the server that runs as an unprivelaged user on the VM, allowing the bot on the host machine to start processes, read stdin/stdout, and access files safely.

In it's current configuration the VM is on a closed virtual network with only the host machine being routable (192.168.69.1), iptables are set up so requests from the VM to the host are blocked to prevent it from attempting to connect to SSH or other services it should not have access to.

The only way for information to go in and out of the VM is through connections initiated by the host.

System resources are also heavily limited with 1 thread, 256MB of memory, and a 16GB virtual disk.

If you manage to put the VM in an unusable state for example by killing the server process, Tangent will automatically use virsh to reboot the VM which only takes around 4 seconds.

As a last resort if someone obtains root and bricks the system a qcow2 snapshot can restore the system state to brand new using the qclean command.

The bot itself is a Dart application and is designed to be as fault tolerant as possible, all buffers that the vm send to are capped, any malformed packets will instantly terminate the connection, and all of the async wrappers for files and processes are destroyed properly when closed.

Usage

Prefixes

Currently there are 3 command prefixes for convenience: ., α, @Tangent (Will have per-server configuration later)

Any of these prefixes can be used to execute commands

Misc commands

purge <n> - Purges a number of messages (admin only)

Managment

qstart - Start the VM (trusted only)
qrestart - Restart the VM (trusted only)
qclean - Revert the VM state to a clean snapshot (trusted only)

Upload / Download

upload [file/dir] - Upload an attachment to the VM at the specified directory or file name, defaults to the home directory
download <file> - Download a specific file from the VM to Discord

Languages

Language commands support discord's code block formatting, for example:

lua ```lua
print("Hello, World!")
```

will interpret the contents of the code block.

You can provide compiler arguments and program arguments by putting text before and after the code block:

.c -ldl ```c
#include <stdio.h>
int main(int argc, char** argv) {
    printf("Hello\n");
    for (int i = 0; i < argc; i++) {
        printf("%i: %s\n", i, argv[i]);
    }
}
```
extra arguments

Outputs:

Hello
0: ./tangent
1: extra
2: arguments

The bot is interactive, if you edit your message with a command it will re-run your command and update it's message. When a user enters a new command or edit a previous one it will kill the previous process to prevent anything from lingering.

Commands:

sh <code> - Standard /bin/sh

Example
echo Hello from /bin/sh

bash <code> - Standard /bin/bash

Example
echo Hello from /bin/bash

arm <code> - ARM Assembly

Example
.globl main
main:
    stmfd sp!, {lr}
    ldr r0, =hello_text
    bl puts
    mov r0, #0
    ldmfd sp!, {lr}
    bx lr
hello_text:
    .string "Hello from ARM\n"

x86 <code> - x86_64 Assembly

Example
.intel_syntax noprefix
.globl main
main:
    push rax
    mov edi, offset hello_text
    xor eax, eax
    call puts
    xor eax, eax
    pop rcx
    ret
hello_text:
    .string "Hello from x86\n"

c <code> - GCC 6.3.0

Example
#include <stdio.h>
int main() {
    puts("Hello from C\n");
}

cpp <code> - G++ 6.3.0

Example
#include <iostream>
int main() {
    std::cout << "Hello from C++\n";
}

lua <code> - Lua 5.3 Reference interpreter

lua5.2 <code> - Lua 5.2 Reference interpreter

lua5.1 <code> - Lua 5.1 Reference interpreter

luajit <code> - LuaJIT 2.0

Example
print("Hello from " .. _VERSION)

py <code> - Python 3

py2 <code> - Python 2

Example
from platform import python_version
print('Hello from Python {}'.format(python_version()))

js <code> - Node.JS v4.8.2

Example
console.log("Hello from Node.js " + process.version);

pl <code> - Perl

Example
print "Hello from Perl\n";

java <code> - OpenJDK 8

Example
public class Tangent {
    public static void main(String[] args) {
        System.out.println("Hello from Java");
    }
}

lisp <code> - SBCL

Example
(format t "Hello from Lisp")

bf <code> - Brainfuck

Example
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++.>+.+++++++..+++.<<++.>>---------.++++++++++++.---.--.<<.>------.>+++++.-----------------.++++++++.+++++.--------.+++++++++++++++.------------------.++++++++.

cs <code> - .NET Core C#

Example
class Program {
    static void Main() {
        System.Console.WriteLine("Hello from C#");
    }
}

fs <code> - .NET Core F#

Example
[<EntryPoint>]
let main argv =
    printfn "Hello from F#"
    0

haskell <code> - GHC

Example
main = putStrLn "Hello from Haskell"

php <code> - PHP7

Example
<?php
echo('Hello from PHP');

cobol <code> - OpenCOBOL

Example
PROGRAM-ID. HELLO.
PROCEDURE DIVISION.
    DISPLAY 'Hello from COBOL'.
    STOP RUN.

go <code> - Go 1.7.4

Example
package main
import "fmt"
func main() {
    fmt.Println("Hello from Go")
}

ruby <code> - Ruby 2.3.3

Example
puts 'Hello from Ruby'

apl <code> - GNU APL

Example
"Hello from APL"

prolog <code> - SWI Prolog

Example
:- initialization hello, halt.
hello :-
    write('Hello from Prolog'), nl.

ocaml <code> - OCaml 4.02.3

Example
print_string "Hello from OCaml\n";;

sml <code> - SML-NJ v110.79

Example
print "Hello from SML\n";

crystal <code> - Crystal 0.28.0

Example
puts "Hello from Crystal"

ada <code> - GNAT

Example
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
begin
   Put_Line ("Hello from Ada");
end Hello;

d <code> - GDC

Example
import std.stdio;
void main() {
    writeln("Hello from D");
}

groovy <code> - Groovy 2.4.8

Example
println "Hello from Groovy"

dart <code> - Dart 2.3.1

Example
main() {
	print("Hello from Dart");
}

erlang <code> - Erlang

Example
-module(tangent).
-export([main/0]).
main() -> io:fwrite("Hello from Erlang\n").

forth <code> - GNU FORTH

Example
.( Hello from FORTH) CR

pascal <code> - Free Pascal Compiler 3.0.0

Example
program Tangent(output);
begin
  writeln('Hello from Pascal');
end.

hack <code> - HipHop VM 4.8.0

Example
echo 'Hello from Hack';

julia <code> - Julia 0.4.7

Example
println("Hello from Julia")

kotlin <code> - Kotlin 1.3.31

Example
fun main(args : Array<String>) {
    println("Hello from Kotlin")
}

scala <code> - Scala 2.12.8

Example
object Tangent {
  def main(args: Array[String]): Unit = {
    println("Hello from Scala")
  }
}

swift <code> - Swift 4.1

Example
import Swift
print("Hello from Swift")```

typescript <code> - Typescript 2.1.5 on Node.JS v4.8.2

Example
console.log("Hello from TypeScript");

verilog <code> - Icarus Verilog

Example
module test;
  initial begin
    $display("Hello from Verilog");
  end
endmodule

wasm <code> - Webassembly WAVM

Example
(module
  (import "env" "_fwrite" (func $__fwrite (param i32 i32 i32 i32) (result i32)))
  (import "env" "_stdout" (global $stdoutPtr i32))
  (import "env" "memory" (memory 1))
  (export "main" (func $main))
  (data (i32.const 8) "Hello from WASM\n")
  (func (export "establishStackSpace") (param i32 i32) (nop))
  (func $main (result i32)
    (local $stdout i32)
    (local.set $stdout (i32.load align=4 (global.get $stdoutPtr)))
    (call $__fwrite
       (i32.const 8)
       (i32.const 1)
       (i32.const 16)
       (local.get $stdout)
    )
    (return (i32.const 0))
  )
)

scheme <code> - MIT Scheme 9.1.1

Example
(begin
  (display "Hello from Scheme")
  (newline))

awk <code> - GNU Awk 4.1.4

Example
BEGIN { print "Hello from AWK" }

clojure <code> - Clojure 1.8.0

Example
(println "Hello from Clojure")

tibasic <code> - Limited TI-BASIC interpreter from patrickfeltes/ti-basic-interpreter

Example
Disp "Hello from TI-BASIC"

batch <code> - WINE's cmd.exe

Example
@echo off
echo Hello from Batch

racket <code> - Racket 6.7

Example
#lang racket/base
(print "Hello from Racket")

Rust <code> - rustc 1.24.1

Example
fn main() {
	println!("Hello from Rust");
}

bc <code> - GNU Basic Calculator

Example
print "Hello from BC\n"

ir <code> - LLVM IR

Example
@.str = private unnamed_addr constant [20 x i8] c"Hello from LLVM IR\0A\00"

define i32 @main() #0 {
  %1 = call i32 @puts(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @puts(i8*) #1

About

Discord bot with a secure, interactive virtual machine

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published