This is similar to the program in the book Hacking: The Art of Exploitation. It is rewritten to fit my needs.
There are two files, takenote and readnote. To write a note simply use takenote with your note as a command line argument: ./takenote "Buy eggs"
. The note gets added to the file /tmp/notes
with User ID (non readable form) and timestamp, seperated by colons (:).
To read your notes simply use ./readnote
. This only displays notes that were written by the current user.
To avoid being able to just cat /tmp/notes
, change the user of both takenote and readnote to root and then use the setuid permission:
sudo chown root takenote readnote
sudo chmod u+s takenote readnote
This should be done before using takenote for the first time.
takenote.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#define FILENAME "/tmp/notes"
void usage(char*);
void fatal(char*);
int main(int argc, char* argv[]) {
int fd; // file descriptor
uid_t uid;
time_t raw_time;
struct tm* date_time;
if (argc < 2)
usage(argv[0]);
// Open file
fd = open(FILENAME, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1)
fatal("opening file");
// Write UID
uid = getuid();
if (write(fd, &uid, sizeof(uid)) == -1)
fatal("writing UID");
if (write(fd, ":", 1) == -1) // Adding seperator
fatal("writing seperator");
// Write timestamp
time(&raw_time);
date_time = localtime(&raw_time);
if (write(fd, asctime(date_time), 24) == -1)
fatal("writing timestamp");
if (write(fd, ":", 1) == -1) // Adding seperator
fatal("writing seperator");
// Write note
if (write(fd, argv[1], strlen(argv[1])) == -1)
fatal("writing note");
if (write(fd, "\n", 1) == -1) // Adding newline
fatal("writing newline");
// Close file
if (close(fd) == -1)
fatal("closing file");
return 0;
}
void usage(char* prog_name) {
printf("Usage: %s <note to be saved in %s>\n", prog_name, FILENAME);
exit(-1);
}
void fatal(char* msg) {
fprintf(stderr, "Error %s.\n", msg);
exit(-1);
}
readnote.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define FILENAME "/tmp/notes"
void fatal(char*);
int print_notes(int, uid_t);
int find_user_note(int, uid_t);
int main() {
int fd; // file descriptor
uid_t uid;
// Open file
fd = open(FILENAME, O_RDONLY);
if (fd == -1)
fatal("opening file");
// Read notes for UserID uid
uid = getuid();
while (print_notes(fd, uid) != -1) // Read until EOF.
;
// Close file
if (close(fd) == -1)
fatal("closing file");
return 0;
}
void fatal(char* msg) {
fprintf(stderr, "Error %s.\n", msg);
exit(-1);
}
// Function to read all user notes with corresponding UID.
// Returns -1 if EOF, 0 otherwise.
int print_notes(int fd, uid_t uid) {
int note_length;
char* buffer;
note_length = find_user_note(fd, uid);
if (note_length == -1) // If EOF
return -1;
// Copy note into buffer and print it.
buffer = (char*) malloc(note_length + 1);
if (buffer == NULL)
fatal("allocating memory");
if (read(fd, buffer, note_length) == -1)
fatal("reading note");
buffer[note_length] = 0;
printf("%s", buffer);
free(buffer);
return 0;
}
// Function to find the next user note. Returns -1
// if EOF, note length otherwise.
int find_user_note(int fd, uid_t uid) {
uid_t note_uid;
unsigned char byte;
size_t note_length;
note_uid = -1;
while (note_uid != uid) {
// If UID is not fully read return EOF.
if (read(fd, ¬e_uid, sizeof(uid)) != sizeof(uid))
return -1;
// If seperator (:) is not read return EOF.
if (read(fd, &byte, 1) != 1)
return -1;
byte = 0;
note_length = 0;
// Read date, time and note
while (byte != '\n') {
// If we can't read another byte return EOF.
if (read(fd, &byte, 1) != 1)
return -1;
++note_length;
}
}
// Rewind read pointer to beginning of note (excluding UID).
lseek(fd, note_length * -1, SEEK_CUR);
return note_length;
}
Compiled with
gcc takenote.c -o takenote
gcc readnote.c -o readnote