I've created a code that should be able to copy a file a user suggests. What I am wondering is this: how do I set the output file mode and how do I determine what the output file mode permissions will be in this code?
#include <stdio.h>
#include <stdlib.h>
int main()
{
char c;
char source_file, target_file;
FILE *in, *out;
printf("Enter name of file to copy\n");
gets(source_file);
printf("Enter name of file to copy to\n");
gets(target_file);
in = (source, O_RDONLY);
out = (target_file, O_CREAT|WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
/* error handing */
if( in == NULL )
{
printf("Error. \n");
exit(0);
}
printf("Enter the copied file name \n");
gets(target_file);
out = fopen(target_file, "w");
/*error handing*/
if( out == NULL )
{
fclose(in);
printf("File opening error.\n");
exit(0);
}
while(( c = fgetc(in) ) != EOF )
fputc(c,out);
fclose(in);
fclose(out);
return 0;
}
Controlling file permissions using standard I/O
One of the demerits of the standard I/O library is that you can't control the permissions on the files that are created, primarily because such permissions are rather platform-specific (more so than the C standard allows for, anyway). The POSIX open() function allows you to control the permissions on the file as it is created.
With a POSIX-like system, you can use the chmod() or fchmod() system calls. You need to know that your rw-rw-rw- pattern is octal 0666.
chmod(target_file, 0666);
fchmod(fileno(out), 0666);
The functions can fail; you should check that they don't.
You can also use the umask() function or (with care) the umask command to influence the default permissions. For example, setting umask 022 in the shell means that files will not be created that are writable by group or others.
Revising the modified code
You don't need to worry about the permissions on a file you open for reading (or, at least, you seldom need to do so).
Worrying about the permissions on the file you write to is more normal.
Your current code proposal is:
in = (source, O_RDONLY);
out = (target_file, O_CREAT|WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
This does not invoke open(), and assigns an integer value to the two FILE * variables, which should be generating compiler warnings. Note that the comma expressions evaluate the LHS and then the RHS of the expression, yielding the RHS as the overall value. O_RDONLY is classically 0; the combination of S_IRUSR etc terms is not zero.
If you're going to open the file with those options, then you need something like:
int fd_i = open(source_file, O_RDONLY);
if (fd_i < 0)
…report error opening source_file…
FILE *in = fdopen(fd_i, "r");
if (in == 0)
…report error creating file stream for source_file…
int fd_o = open(target_file, O_CREAT|WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if (fd_o < 0)
…report error opening target_file…
FILE *out = fdopen(fd_o, "w");
if (out == 0)
…report error creating file stream for target_file…
However, I would probably not use fdopen() for the input file — I'd use fopen() directly as you did originally — but I might use it for the output file.
Related
I have the following code as an executable that I want to exploit for a course in order to spawn a shell with elevated privileges. I am a user of levelX and the executable has setgid of levelX+1. I am not allowed to alter any of the code.
As I do not have root privileges, setguid(0) fails. I was not able to change the return address of the function or main function. Could anyone point to the right direction?
int main (int argc, char** argv)
{
if (exec(argv[1]) != 0)
{
fprintf(stderr, "Cannot execute your command\n");
return -1;
}
return 0;
}
int exec(char *command)
{
FILE *f = NULL;
char entry[64];
char line[256];
f = fopen("log", "a");
if (f == NULL)
{
fprintf(stderr, "Can't open file\n");
return -1;
}
snprintf(entry, 64, "%d: %s\n", getuid(), command);
fprintf(f, entry, NULL);
fclose(f);
f = fopen("sudoers", "r");
if (f == NULL)
{
fprintf(stderr, "Can't open\n");
return -1;
}
while(fgets(line, 256, f) != NULL)
{
if (atoi(line) == getuid())
{
if (setuid(0) == 0) {
system(command);
} else {
fprintf(stderr, "check permissions\n");
}
fclose(f);
return 0;
}
}
fprintf(stderr, "Error\n");
fclose(f);
return -1;
}
From the code you posted, it appears you are supposed to write your own sudoers file to any directory you have write access to, then run this program in that directory, so it reads your file.
So, simply write your own UID to this fake sudoers file, and then give a command parameter such as bash to get a shell. There's no need to do any buffer overflow exploitation.
Presumably the real exploitable program has suid bit set in the file permissions, so it can perform the setuid(0) call. I guess the purpose of the exercise is to demonstrate how all input needs to be sanitized when you are dealing with suid programs, including things like relative paths (which effectively take current working directory as input) like any user-supplied paths and other input.
But, since the program only has setgid bit (as said in comment), you need find something you do with just the group id. That something could be that log file write. You could create a symbolic link with file name log, pointing to whatever file you want to append to, which that group has write permissions for. Also, that file needs to have format such, that the log line format does not make the file corrupted. Remember, you can put newlines etc into command line arguments!
After all it was a format string exploit on fprintf(f, entry, NULL); inside int exec(char *command) where you overwrite the return address with %n format.
i've been trying to redirect the output to a file and reading from file instead of stdin by using a function, but it doesnt seem to be working as when i redirect into a file and check to see if a file has been created with the output there is nothing there. what could be wrong with this function.
/* Redirect a standard I/O file descriptor to a file
* Arguments: filename the file to/from which the standard I/O file
* descriptor should be redirected
* flags indicates whether the file should be opened for reading
* or writing
* destfd the standard I/O file descriptor which shall be
* redirected
* Returns: -1 on error, else destfd
*/
int redirect(char *filename, int flags, int destfd){
int ret;
if(flags == 0){
destfd = open(filename,O_RDONLY);
if (destfd < 0){
return -1;
}
ret = dup2(0,destfd);
if(ret < 0){
return -1;
}
close(destfd);
}
if(flags == 1){
destfd = open(filename,O_APPEND|O_WRONLY);
if (destfd < 0){
return -1;
}
ret = dup2(destfd,1);
if(ret < 0){
return -1;
}
close(destfd);
}
return destfd;
}
There are several problems with your code, not least of which the really awful formatting which make it very hard to read.
For example this call to dup2 is backwards - it's replacing the recently opened destfd with a copy of stdin.
ret = dup2(0,destfd);
a then a few lines later you close destfd.
Your if statements could benefit from you learning about else and else if
if(flags == 0) {
// code
} else if(flags == 1) {
// code
}
Really though you could simplify the whole function by treating the flags parameter as the same flags you'd pass to open and have destfd as the file descriptor you want to replace.
int redirect(char *filename,int flags, int destfd)
{
int fd;
fd=open(filename,flags,S_IRUSR|S_IWUSR);
if(fd!=-1)
{
if(dup2(fd,destfd)==-1)
{
close(fd);
fd=-1;
}
else
{
close(fd);
}
}
return fd;
}
Then you could call it like
redirect("output.txt",O_CREAT|O_WRONLY,1); // Redirect stdout
redirect("input.txt",O_RDONLY,0); // Redirect stdin
The os function dup2() should provide what you need (if not references to exactly what you need).
More specifically, you can dup2() the stdin file descriptor to another file descriptor, do other stuff with stdin, and then copy it back when you want.
The dup() function duplicates an open file descriptor. Specifically, it provides an alternate interface to the service provided by the fcntl() function using the F_DUPFD constant command value, with 0 for its third argument. The duplicated file descriptor shares any locks with the original.
On success, dup() returns a new file descriptor that has the following in common with the original:
Same open file (or pipe)
Same file pointer (both file descriptors share one file pointer)
Same access mode (read, write, or read/write)
Everything I said can be found on the manpage of dup
The following is my code for a method that copies a file from a path to a file to a directory provided as the destination. The copy works perfectly fine, however my chmod call assigns the wrong permissions to the copied file in the destination. If the permission in the source is 644, the copied file has a permission of 170 or 120.
I have been attempting to debug this for hours and it's driving me slightly crazy so any help is greatly appreciated.
void copy_file(char* src, char* dest) {
char a;
//extract file name through a duplicate ptr
char* fname = strdup(src);
char* dname = basename(fname);
//open read and write streams
FILE* read;
FILE* write;
read = fopen(src, "r");
chdir(dest);
write = fopen(dname, "w");
//error checking
if (read == NULL) //|| (write == NULL))
{
perror("Read Error: ");
exit(0);
}
else if (write == NULL)
{
perror("Write Error: ");
exit(0);
}
//write from src to dest char by char
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
//close files
fclose(read);
fclose(write);
// this is where I attempt to assign source file permissions
//and it goes horribly wrong
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
chmod(dname, src_st.st_mode);
printf("%o\n", src_st.st_mode & 0777);
}
You fopen(src, "r"), then you chdir(dest). This means that when you later call stat(src, &src_st), there is no reason to think that stat will access the same file as fopen did, or indeed that stat will access any file at all.
If stat fails, you proceed to call chmod anyway, so you pass whatever random junk was in src_st.st_mode to chmod.
You should use fstat(fileno(read), &src_st) before calling fclose(src), instead of calling stat(src, &src_st).
The basic problem is you have to check your system calls like fopen, chdir, and stat immediately.
For example, first thing I tried was copy_file( "test.data", "test2.data" ) not realizing it expected a destination directory.
char* fname = strdup(src);
char* dname = basename(fname);
dname is now test.data, same as the source.
read = fopen(src, "r"); // succeeds
chdir(dest); // fails
write = fopen(dname, "w"); // blows away test.data, the source
You do eventually check read and write, but after the damage has been done.
Blowing away your source file is really bad. It's important that your code deals with failed system calls. If you don't, it will sail along causing confusion and destruction.
Most system calls in C return 0 for success. This is an anti-pattern where the return value is an error flag, so false is failure, and anything else indicates what kind of error (though stat doesn't use that, it uses errno).
When it fails, stat returns -1 which is true. So this is the wrong way around.
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
Instead, you have to check for non-zero.
struct stat src_st;
if(stat(src, &src_st) != 0 ){
// Note that I don't use perror, it doesn't provide enough information.
fprintf(stderr, "Could not stat %s: %s\n", src, strerror(errno));
exit(1);
}
As you can guess this gets tedious in the extreme, and you're going to forget, or do it slightly different each time. You'll want to write wrappers around those functions to do the error handling for you.
FILE *fopen_checked( const char *file, const char *mode ) {
FILE *fp = fopen(file, mode);
if( file == NULL ) {
fprintf(stderr, "Could not open '%s' for '%s': %s", file, mode, strerror(errno));
exit(1);
}
return fp;
}
It's not the best error handling, but it will at least ensure your code appropriately halts and catches fire.
A note about chdir: if you can avoid it don't use it. chdir affects the global state of the program, the current working directory, and globals add complexity to everything. It's very, very easy for a function to change directory and not change back, as yours does. Now your process is in a weird state.
For example, if one did copy_file( "somefile", "foo" ) this leaves the program in foo/. If they then did copy_file( "otherfile", "foo" ) they'd be trying to copy foo/otherfile to foo/foo/otherfile.
And, as #robmayoff pointed out, your stat fails because the process is now in a different directory. So even the function doing the chdir is confused by it.
Ensuring that your functions always chdir back to the original directory in a language like C is very difficult and greatly complicates error handling. Instead, stay in your original directory and use functions like basename to join paths together.
Finally, avoid mixing your file operations. Use filenames or use file descriptors, but try not to use both. That means if you're using fopen, use fstat and fchmod. You might have to use fileno to get a file descriptor out of the FILE pointer.
This avoids having to carry around and keep in sync two pieces of data, the file descriptor and the filename. It also avoids issues with chdir or the file being renamed or even deleted, the file descriptor will still work so long as it remains open.
This is also a problem:
char a;
...
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
fgetc() returns int, not char. Per the C Standard, 7.21.7.1 The fgetc function:
7.21.7.1 The fgetc function
Synopsis
#include <stdio.h>
int fgetc(FILE *stream);
Assuming sizeof( int ) > sizeof( char ), char values are signed, 2s-complement integers, and EOF is an int defined to be -1 (all very common values), reading a file with char a = fgetc( stream ); will fail upon reading a valid 0xFF character value. And if your implementation's default char value is unsigned char, char a = fgetc( stream ); will never produce a value that matches EOF.
It's possible to make a code that recognizes whether a file was passed like:
program.out < file.dat
I search an answer for this because I want to write code to do something like this:
int main (int argc, char *argv[])
{
char filename[50];
if ( argc > 1 )
{
strcpy (filename, argv[1]);
}
else if ( SOMETHING )
{
/* copy the stdin into fin (?) */
}
FILE *fin;
fin = fopen (filename, "r");
/* ... */
fclose(fin);
}
return 0;
}
In which SOMETHING evaluates to 1 if the file was passed with <, and 0 otherwise.
If it's possible, I am looking for a solution working in standard C.
We cannot detect this in ISO C (that is, without resorting to platform extensions, like getting the file descriptor using fileno on POSIX and then running some tests on it by obtaining attributes with fstat and so forth.)
The stdin stream is required by ISO C to be line buffered if it is connected to an interactive device. This doesn't help us, however, since there are no portable functions to inquire about the buffering mode of a FILE *: there are only "setters", no "getters". The GNU C library has a __flbf (FILE *stream) which reports whether or not a stream is line-buffered, but it is an obvious extension, declared in a <stdio_ext.h> header.
If your program must work with a file, and not with standard input from an interactive device, then a good solution is to make the argument to the program mandatory. Make it require a filename argument and always open that file. Then you're sure you have the file.
You can also make the argument optional, and if it is missing, then open a default file, ignoring stdin.
You can also use freopen to make stdin point to a file. Then code which works with stdin implicitly will take input from that file:
Pseudo-code:
name = "some default"
if we have an argument
name = that argument
if (freopen(name, mode, stdin) == 0)
handle error
else
stdin is now a file; process it
If you really must support the program < file situation, while flagging the program situation (interactive input) as invalid, you need the aforementioned platform-specific hacks.
If you're OK with a Unix-specific solution, you can use isatty():
FILE *fin;
int need_to_close;
if (isatty(fileno(STDIN))) { // I/O not redirected
fin = fopen("file.dat", "r");
need_to_close = 1;
} else {
fin = stdin;
need_to_close = 0;
}
/* ... */
if (need_to_close) {
fclose(fin);
}
May be this answer can help:
It says,
On a Posix system, you can test whether or not cin comes from a
terminal or is redirected using isatty
#include <unistd.h>
if (isatty(STDIN_FILENO)) {
// not redirected
} else {
// redirected
}
At the moment my program has no problem reading in a .txt file, but my program needs to read in a text file with a different file extension (.emu is the requirement). When simply changing the same file's extension to .emu, the variable 'file' is NULL and therefore the file isn't opened, can anyone help?
Had a little look around and haven't been able to find a solution so any help is much appreciated
here's the source code:
void handleArgs (const char *filename, int trace, int before, int after) {
FILE *file = fopen(filename, "r");
char *address = malloc(MAX_ADD_LENGTH * sizeof(char));
char *instruction = malloc(MAX_INS_LENGTH * sizeof(char));
long int addressDecoded;
if (file == NULL || file == 0) {
fprintf(stderr, "Error: Could not open file");
}
else {
if (ferror(file) == 0) {
while (fscanf(file, "%s %s", address, instruction) != EOF) {
if (strlen(address) == 8 && strlen(instruction) == 8) {
addressDecoded = strtol(address, NULL, 16);
printf("%ld\n", addressDecoded);
//instruction = decodeInstruction(instruction);
}
else {
fprintf(stderr, "Error: particular line is of wrong length");
}
}
}
}
fclose(file);
}
argument 'filename' when executing is simply '/foopath/test.emu'
There's nothing special to C about the file extension. Reread your code for simple errors like changing the filename in one place, but not the other. If you're passing in the filename, pass the whole name, not just the part to the left of the period.
Files are data, and have names. What comes before the dot in a name, is just as much a part of it as what comes after -- the extensions were created just as hints as to what the file contains, but they are NOT required to be strictly related to the file's contents.
The file may not exist, or your priviledges may not be enough to open it. Or maybe there's some other kind of error. How can you diagnose this?
When you use a system call and it doesn't behave the way you want to, there's a variable called errno in errno.h (#include <errno.h>) that will contain a number representing the status of the last call. There's a huge list of symbolic constants to put names to these values, you can google it up.
For example, if you try to open a file and the returned pointer is useless, you might want to check errno to see if the file existed, or if you're exceding system restrictions for opened files, etc.