I am looking for a way to check and make sure a file is readable, writeable, and exists, and if it is not I want to print a message stating so. I believe the information I am looking for can be found using fstat(), I just don't know the correct way to get it.
If open sets a specific errno if I try to open a unreadable, unwriteable, or non-existant file with O_RDRW, I think that would be the ideal solution.
Here is what I have tried:
//function to open the file
int f_open(const char *filename){
int fid;
if ((fid = open (filename, O_RDWR)) < -1){
return -1;
}
struct stat fileStat;
if (fstat(fid, &fileStat) < 0){
return -1;
}
//check write permission
if (!S_IWUSR(fileStat.st_mode)){
printf("Not writeable\n");
return -1;
}
//check read permissions
if (!S_IRUSR(fileStat.st_mode)){
printf("Not readable\n");
return -1;
}
return fid;
}
I am receiving the following error when I try and compile:
tester.c: In function 'f_open':
tester.c:56:14: error: called object '128' is not a function
tester.c:60:14: error: called object '256' is not a function
The macros you are using are bit values, not functions.
//check write permission
if ((S_IWUSR & fileStat.st_mode) == 0){
printf("Not writeable\n");
return -1;
}
Example Here: http://codewiki.wikidot.com/c:system-calls:fstat
EDIT: The 'access' function indicated above is a better choice IWUSR doesnt tell you if YOU can write it, it tells you if the owner can.
You can use access for permission checking
int rw = access(filename, R_OK | W_OK);
if (rw == 0) {
/* read/write granted */
}
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.
Title says it all: can one use stat() after fopen() to avoid Time of Check to Time of Use (TOCTOU) race conditions?
Some details:
I am writing a C program that only reads files, but needs to error properly when asked to read a directory. As of right now, it uses open() (with O_RDWR) to generate an error and then checks errno for EISDIR, like so:
int fd = open(path, O_RDWR);
if (fd == -1) {
if (errno == EISDIR) return PATH_IS_DIR;
else return FILE_ERR;
}
The problem with the above solution is that this program only needs to read files, so by opening a file with O_RDWR, I might wrongly get a permissions error if the user has read permissions, but not write permissions.
Is it possible to do the following to avoid TOCTOU race conditions?
struct stat pstat;
FILE *f = fopen(path, "r");
if (!f) return FILE_ERR;
if (stat(path, &pstat) == -1) {
fclose(f);
return FILE_ERR;
}
if (S_ISDIR(pstat.st_mode)) {
fclose(f);
return PATH_IS_DIR;
}
If it is not possible, is there another solution to prevent TOCTOU bugs and also wrong permission errors?
No, the code presented in the question does not avoid a TOCTOU race.
Testing after use is prone to exactly the same errors as testing before use. In both cases, the name is resolved at two different times, with possibly different results. This is the cause of the race, and it can happen whichever access happens first.
The only way to avoid this is to open the file once, and use the file descriptor so obtained for any other checks you need. Modern OSes provide interfaces such as fstat() for exactly this purpose.
If you want to use C's buffered I/O, you can get the file descriptor from a FILE* using fileno() - or you can create a FILE* from a file descriptor using fdopen().
It requires a very small change to your code:
# Untested
struct stat pstat;
FILE *f = fopen(path, "r");
if (!f) return FILE_ERR;
if (fstat(fileno(f), &pstat) == -1) {
// ^^^^^^^^^^^^^^^ <-- CHANGED HERE
fclose(f);
return FILE_ERR;
}
if (S_ISDIR(pstat.st_mode)) {
fclose(f);
return PATH_IS_DIR;
}
EDIT (2018-10-25): Toby Speight's answer is better.
There is a solution: use open(), then
fstat().
An example:
struct stat pstat;
int fd = open(path, O_RDONLY);
if (fd == -1) return FILE_ERR;
if (fstat(fd, &pstat) == -1) {
close(fd);
return FILE_ERR;
}
if (S_ISDIR(pstat.st_mode)) {
close(fd);
return PATH_IS_DIR;
}
I found this while checking that I had covered all of my bases before asking this question.
Using PellesC on Windows 8.1.
I know this topic has been addressed many times with many solutions. I have read solutions stating the usage of CreateFile, PathFileExists, GetFileAttributes, _access which I somewhat understand.
I have also read an important point about race conditions in the answer to the questions Quickest way to check whether or not file exists
and What's the best way to check if a file exists in C? (cross platform).
So if I open a file using fopen() in C and when it fails (for any reason) and passes NULL back; then can I further check for errno == ENOENT and be content with it and report correctly that the file does not exists.
#include <stdio.h>
#include <string.h>
#include <errno.h>
int file_exists(char filename[]) {
int err = 0; //copy of errno at specific instance
int r = 0; //1 for exists and 0 for not exists
FILE *f = NULL;
//validate
if (filename == NULL) {
puts("Error: bad filename.");
return 0;
}
printf("Checking if file %s exists...\n", filename);
//check
errno = 0;
f = fopen(filename, "r");
err = errno;
if (f == NULL) {
switch (errno) {
case ENOENT:
r = 0;
break;
default:
r = 1;
}
printf("errno = %d\n%s\n", err, strerror(err));
} else {
fclose(f);
r = 1;
}
if (r == 0) {
puts("It does not.");
} else {
puts("It does.");
}
return r;
}
fopen needs to do lots of stuff and checking before the file is opened. ENOENT implies that the file does not exist, but file not existing does not imply ENOENT.
It is possible for a file to not exist and you to get another error, such as EACCES for not being able to read the parent directory for example.
On the other hand, ENOENT from fopen does not mean that some other process couldn't have created the file even before fopen returns or before you're inspecting the errno and so on; this is the reason why C11 added the x flag for opening a file for writing in exclusive mode - failing if the file already exists.
To sum it up: if you get ENOENT, the file did not exist when you tried opening it. If you get some other error, then each other error code would belong to one of 3 these classes - that it is certain that either
the file existed, or
it cannot have existed
or it might have existed at the time
at the time of opening. It is up to you and your required logic how you'd handle these other errors. A simple way would be to refuse to continue processing, reporting the error to the user.
I'm writing a simple HTTP server and I'm getting a file does not exists return value when the file does exist
printf("%s\n", html_path);
if ((fd = open(html_path, "r")) >= 0){ //file found
stat(file_name, &st);
file_size = st.st_size;
printf("%d\n", file_size);
while (read(fd, response, RCVBUFSIZE) > 0){
}
}
else { //file not found
strcpy(response, "404 File not found\n");
send(clntSocket, response, 32, 0);
}
the print statement is to verify the path and it looks like this:
/mounts/u-zon-d2/ugrad/kmwe236/HTML/index.html
note that this path is on a server that we use at our university. it's the path that shows when I command pwd
I have confirmed that the file exists. is there something wrong with my path?
There was an error opening the file, but you don't know that it was because the file was not found because you're didn't check the value of errno.
In the else section, add the following:
else { //file not found
// copy the value of errno since other function calls may change its value
int err = errno;
if (err == ENOENT) {
strcpy(response, "404 File not found\n");
send(clntSocket, response, 32, 0);
} else {
printf("file open failed: error code %d: %s\n", err, strerror(err));
}
}
If the file does not in fact exist you'll handle the error properly. If not, you'll print an error message that tells you what did happen.
You're also calling open incorrectly. The second parameter is an int containing flags. To open a file for reading, use O_RDONLY.
open does not have the 2nd parameter as a string. You using open with the parameters of fopen.
For a webserver fopen, fprintf, fclose is a better choise then more lowlevel open, read, ...
Cheers,
Chris
You need to check where you program is executing as it will try to open the path relative from that location. To check use:
char cwd[1024];
getcwd(cwd, sizeof(cwd));
puts(cwd);
Then you can concatenate your path using:
strncat(cwd, html_path, 100);
You may find that you have to go up one directory or something to then find the file you're looking for.
Also note that if you're debugging your program via gdb it may execute from a different location from your regular build location which may make it harder to spot bugs.
I am wondering if anyone could assist me with this, I am try to figure out how to handle a time of check, time of use problem and drop privileges when they are not needed, in case the for example it is a symbolic link to the file which could be changed to say the shadow file. Assuming the function below is invoked while the calling process is running with elevated privileges.
int
updatefile(char *file)
{
int fd;
if (access(file, R_OK|W_OK)) {
perror("access()");
return (-1);
}
fd = open(file, O_RDWR);
/*
* file is written to here.
*/
printf("Updated %s on...\n", file);
system("date");
/*
* elevated privileges are required here...
*/
return (0);
}
Assuming that your access function checks the file type and determines if the user has the appropriate privileges to manipulate the file, you are concerned about a potential TOCTTOU error between the call to access and the call to open.
The typical way to avoid this would be:
int updatefile(char *file)
{
int fd = -1;
if(-1 != (fd = open(file, R_OK | W_OK)))
{
struct stat buf;
if(0 == fstat(fd, &buf))
{
/* perform any necessary check on the here */
/* do what ever else you need to do */
/* write to the file here */
/* gain elevated permissions here */
/* do privileged task */
/* drop back to normal permissions here */
close(fd);
}
else
{
/* handle error stating the file */
}
}
else
{
/* handle error for not opening file */
}
}
The reason that this works, is that we postpone doing any checks on the file till after we get a "handle" to the file. We can tell if the user doesn't have the permissions to open the file by the value of errno in the outer else block;
If we are able to get a "handle" to the file, we then can do what ever checks we want. Because we maintain the "handle" from the time that we open the file, through when we perform our checks and finally when we use the file; a malicious would not be able to modify the file system between check and use.
Hope this helps
T.