0

Consider the following program (vul.c) with buffer overflow vulnerability.

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    char buf[10];
    strcpy(buf, argv[1]);
    printf("%s\n", buf);
    return 0;
}

Above program compiled using gcc -o vul vul.c and executed on arch linux - linux 4.4.16-1-lts x86-64 gave following output when executed in terminal with ./vul $(perl -e 'print "A"x100') command:

AAAAAAAAAAA...A
Segmentation fault (core dumped)

Then checking the program status using echo $? command gave 139 output.

Following program (exp.c) (for crashing the above program)

#include <stdlib.h>

int main(void)
{
    printf("%d\n", system("./vul $(perl -e 'print \"A\"x100')"));
    return 0;
}

compiled using gcc -o exp exp.c when executed with ./exp command on same system gave following output:

AAAAAAAAAAAA...A
139

I have two questions:

  1. Why no error message was generated by 2nd program? and,
  2. I need to compile the program with -fstack-protector flag to enable the *** stack smashing detected *** error messages in arch linux but not in Ubuntu. In Ubuntu, it might be that this flag is include by default in gcc or is there any other reason?
rht
  • 106
  • 9
  • 1
    I'm not sure I understand the question... or your `printf` implementation... You do know `system` returns an `int` with the error code (0 if successful), it doesn't return a string. A simple `if(system(...))` will give you the error. If you want to print it as anice looking message, you can probably use [`strerror`](http://linux.die.net/man/3/strerror). – Myst Aug 17 '16 at 05:45

4 Answers4

1

As I pointed out in my comment,system returns an int with the programs's return value, which is normally it's error code (0 if successful).

If you want to print the error as a nice looking message, you can probably use strerror.

According to @rht's comment (see my next edit) and the answers to the question referenced in that comment, the returned value will be 0 on success and on error it will be error | 0x80. To get the original error code, use 128 - err_code.

try this:

#include <stdlib.h>
#include <errno.h>

int main(void)
{
    int tmp = system("./vul $(perl -e 'print \"A\"x100)");
    if(tmp < 0)
       error("Couldn't run system command");
    else if(tmp >0)
       printf(stderr, "System command returned error: %s", strerror(128 - tmp));
    else
       ; // nothing
    return 0;
}

The fact that vul.c does (or does not) print an error message should be irrelevant for your exp.c program, since it depends on vul.c's compile flags values and any default compiler flags - things exp.c can't control.

EDIT(2) - in answer to the comment.

It could be that the error message returned isn't an errno value, but a signal trap value.

These are sometimes hard to differentiate and I have no good advice about how you can tell which one it is without using memcmp against the answer.

In this case you know vul.c will never return it's errno value, which leaves you only with signal trap errors, so you can use strsignal to print the error message.

As pointed out in @rht's comment, which references this question:

Passing tmp to strsignal generates the same error message: "unknown signal 139". The reason is that there is no signal with this signal number. /usr/include/bits/s‌​ignum.h contains all the signals with their signal numbers. Passing tmp-128 to strsignal works.

i.e.

#include <stdlib.h>
#include <string>

int main(void)
{
    int tmp = system("./vul $(perl -e 'print \"A\"x100)");
    if(tmp < 0)
       error("Couldn't run system command");
    else if(tmp >0)
       printf(stderr, "System command returned error: %s", strsignal(tmp - 128));
    else
       ; // nothing
    return 0;
}

EDIT

The question was edited because it's code was mis-copied. I altered the answer to reflect that change.

Community
  • 1
  • 1
Myst
  • 18,516
  • 2
  • 45
  • 67
  • On `Ubuntu`, first program generates `*** stack smashing detected ***` error message along with the output and the same happens in the second program. In `arch linux` too, including the `-fstack-protector` flag when compiling the `vul.c` program generates the same output and error messages in both the programs as in `Ubuntu`. And I don't understand why `printf` would crash as the system creates a new process for running `vul.c` program. If the new process crashes then it will simply return the control back to the `system()` and printf will print the status. Please correct if wrong. – rht Aug 17 '16 at 06:04
  • When the control returns to `system`, then `system` returns a number to your program. When you use a number in `printf` it is translated to a string pointer, so `printf` prints whatever might be at the address of the error code... i.e. 0x0008a (for error-code 139). – Myst Aug 17 '16 at 06:07
  • I am really sorry. I copied the wrong program. My mistake. Editing the program with correction. – rht Aug 17 '16 at 06:14
  • @rht - No worries. I updated the answer to reflect the change. – Myst Aug 17 '16 at 06:25
  • Thanks for updating the answer. I know the eror message is irrelevant for `exp.c`. But how can I get a useful error message for printing as printing the error using `strerror()` prints `Unknown error 139`? – rht Aug 17 '16 at 06:47
  • @rht there's also `strsignal`, I edited my answer to include this option. – Myst Aug 17 '16 at 07:08
  • Passing `tmp` to `strsignal` generates the same error message: `unknown signal 139`. The reason is that there is no signal with this signal number. `/usr/include/bits/signum.h` contains all the signals with their signal numbers. Passing `tmp-128` to `strsignal` works. http://stackoverflow.com/questions/1101957/are-there-any-standard-exit-status-codes-in-linux http://linux.die.net/man/2/wait – rht Aug 17 '16 at 08:04
  • I posted an answer about the use of `strsignal(tmp-128)` which I think is not always correct as described in the answer. http://stackoverflow.com/a/38996769/3641224 – rht Aug 17 '16 at 12:38
1

From my comment to @Myst 's answer for "passing tmp-128 to strsignal()" function, after experimenting a little I found that it does not work in situations where the program exited normally but returned status other than 0.

Following are the contents of my /usr/include/bits/waitstatus.h:

/* If WIFEXITED(STATUS), the low-order 8 bits of the status.  */
#define __WEXITSTATUS(status)   (((status) & 0xff00) >> 8)

/* If WIFSIGNALED(STATUS), the terminating signal.  */
#define __WTERMSIG(status)      ((status) & 0x7f)

/* Nonzero if STATUS indicates normal termination.  */
#define __WIFEXITED(status)     (__WTERMSIG(status) == 0)

/* Nonzero if STATUS indicates termination by a signal.  */
#define __WIFSIGNALED(status) \
  (((signed char) (((status) & 0x7f) + 1) >> 1) > 0)

Above code show that, exit status of a program is a 16bit number, the high order 8 bits of which are the status that the program returned and some/all of the remaining bits are set if the program exited because of a signal, 7 bits of which denote the signal that caused the program to exit. That's why subtracting 128 from the exit status returned by system() will not work in the situation as described above.

System()'s source code

Since system() function too uses fork() to create a new process and waits for the termination of the process, the same method of checking a child process's status in parent process can also be applied here. Following program demonstrates this:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>

int main(void)
{
    int status = system("prg_name");
    if (WIFEXITED(status))
        printf("Exited Normally, status = %d\n", WEXITSTATUS(status));
    else if (WIFSIGNALED(status))
        printf("Killed by Signal %d which was %s\n", WTERMSIG(status), strsignal(WTERMSIG(status)));
    return 0;
}
rht
  • 106
  • 9
0

Answering my own 2nd question.

gcc -Q -v vul.c command displayed the options passed to the gcc. The options in Ubuntu included -fstack-protector-strong flag but not in arch-linux. So in Ubuntu, the flag is passed by default to gcc.

rht
  • 106
  • 9
0

There exists two problems in your vul.c and exp.c.

In vul.c,

char buf[10];

10 is not sufficient in this case, since the argv[1], i.e., $(perl -e 'print "A"x100', is larger than the buffer to be allocated. Enlarge the buf size should fix the segmentation fault.

In exp.c, you're missing one single quote, and should be modified as followed:

printf("%d\n", system("./vul $(perl -e 'print \"A\"x100')"));
CWLiu
  • 3,913
  • 1
  • 10
  • 14
  • Thanks for pointing out missing single quote. I'll edit the question. And I am trying to get a segmentation fault error message. My question is why the second program is not printing any error messages. – rht Aug 17 '16 at 06:52