Does gets() ignore '\0'?

278 views Asked by At

I am learning about buffer overrun with this source code:

#include <stdio.h>
int main()
{
    char buf[16];
    gets(buf);
    printf("buf @ %8p\n", (void*)&buf);
    return 0;
}

I try to write Null character ('\0') to buf variable.

First, in gdb, I set the breakpoint at line 6, after the gets() function and run it with r <<< $(python -c 'print "\0"*11 + "AAAA"')

When I explore the stack, I realize it only write "AAAA" to buf. What happens?

(gdb) x/16xw &buf
0xffffcf80: 0x41414141  0xffffd000  0xffffd04c  0x080484a1
0xffffcf90: 0xf7fb43dc  0xffffcfb0  0x00000000  0xf7e1a637
0xffffcfa0: 0xf7fb4000  0xf7fb4000  0x00000000  0xf7e1a637
0xffffcfb0: 0x00000001  0xffffd044  0xffffd04c  0x00000000

But, when I run the program with r <<< $(python -c 'print "\1"*11 + "AAAA"'), the buf will be:

(gdb) x/16xw &buf
0xffffcf80: 0x01010101  0x01010101  0x41010101  0x00414141
0xffffcf90: 0xf7fb43dc  0xffffcfb0  0x00000000  0xf7e1a637
0xffffcfa0: 0xf7fb4000  0xf7fb4000  0x00000000  0xf7e1a637
0xffffcfb0: 0x00000001  0xffffd044  0xffffd04c  0x00000000

So the gets() function will not receive the Null character or the stdin will ignore it ?

P/S: I built it with gcc -m32 -fno-stack-protector -g stack.c -o stack on gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609.


Update: After some suggestions, I try this:

#include <stdio.h>
int main()
{
    char buf[16];
    gets(buf);
    printf("buf @ %8p\n", (void*)&buf);
    for (int i = 0; i < 16; ++i) // this is for loop all the buf
    {
        printf("%02x ", buf[i]);
    }
    return 0;
}

It works with '\0'

$ gcc -g j_stack.c -o j_stack
$ python -c 'print "AAAA" + "\0"*6 + "AAAA"'| ./j_stack 
buf @ 0xffffcfbc
41 41 41 41 00 00 00 00 00 00 41 41 41 41 00 ffffffff

But how do I provide input which contains '\0' to buf in gdb program

2

There are 2 answers

2
Lightness Races in Orbit On BEST ANSWER

No, it doesn't.

This behaviour has nothing to do with gets(), or with Python strings; it's due to the way you're providing input to your program, using a subshell and the Bash "herestring" syntax (which performs some manipulations on whatever you give it, apparently including dropping null bytes):

# python -c 'print "\0"*11 + "AAAA"' | wc -c
16
# python -c 'print "\0"*11 + "AAAA"' | hexdump
0000000 0000 0000 0000 0000 0000 4100 4141 0a41
0000010

# cat <<< $(python -c 'print "\0"*11 + "AAAA"') | wc -c
5
# hexdump <<< $(python -c 'print "\0"*11 + "AAAA"')
0000000 4141 4141 000a
0000005

# echo $(python -c 'print "\0"*11 + "AAAA"') | wc -c
5

If you run your program with a simple pipe, you should see the results you expect:

python -c 'print "\0"*11 + "AAAA"' | ./myProgram
3
Steve Summit On

No, gets does not ignore '\0'.

I changed your program to include

for(i = 0; i < 16; i++) printf("%02x", buf[i]);
printf("\n");

after calling gets. I ran the program on the input

abc\n

and saw

61626300000000000000000000000000

as I expected. I then ran the program on the input

ab\0c\n

and saw

61620063000000000000000000000000

which was also what I expected.


P.S. I'm not sure why you saw the behavior you did, but I confess I'm not sure what you're doing with <<< and those python fragments. Me, I used

echo abc | a.out

and

echo 616200630a | unhex | a.out

where unhex is a little program I have in my bin directory for, well, doing the obvious.