August 2009 Archives

14-08-2009 20:53:22

[Secu] Bypass du controle de l'extension PHP (suite)

Suite au post d'hier il s'avère que c'est bien un comportement du module mime de Apache qui provoque ce résultat.
 j0rn@parsifal:~/Bureau/httpd-2.2.11$ grep -ri addtype . | head -2
./modules/http/mod_mime.c:    char *forced_type;                /* Additional AddTyped stuff */
./modules/http/mod_mime.c:    AP_INIT_ITERATE2("AddType", add_extension_info,
Le AddType que j'évoquais hier est donc bien géré par mod_mime contrairement à ce que je pensais.

La documentation d'Apache de Mime indique également ceci :
Files with Multiple Extensions

Files can have more than one extension, and the order of the extensions is normally irrelevant. 
For example, if the file welcome.html.fr maps onto content type text/html and language French then the file welcome.fr.html 
will map onto exactly the same information. If more than one extension is given which maps onto the same type of meta-information, 
then the one to the right will be used, except for languages and content encodings. For example, if .gif maps to 
the MIME-type image/gif and .html maps to the MIME-type text/html, then the file welcome.gif.html will be associated with 
the MIME-type text/html.

Languages and content encodings are treated accumulative, because one can assign more than one language or encoding to a particular resource. 
For example, the file welcome.html.en.de will be delivered with Content-Language: en, de and Content-Type: text/html.

Care should be taken when a file with multiple extensions gets associated with both a MIME-type and a handler. 
This will usually result in the request being by the module associated with the handler. For example, if the .imap extension 
is mapped to the handler imap-file (from mod_imap) and the .html extension is mapped to the MIME-type text/html, then t
he file world.imap.html will be associated with both the imap-file handler and text/html MIME-type. When it is processed, 
the imap-file handler will be used, and so it will be treated as a mod_imap imagemap file.

Voilà le pourquoi du comment ! Le mime type PHP ajouté via le AddType passe prioritaire à quelque chose qu'il ne connait pas. S'il y a plusieurs MIME, il choisit la dernière extension. S'il y a un Handler, celui ci passe prioritaire à une extension MIME

Si on fouille un peu plus dans les sources on tombe sur la fonction qui parse l'extension :
/*
 * find_ct is the hook routine for determining content-type and other
 * MIME-related metadata.  It assumes that r->filename has already been
 * set and stat has been called for r->finfo.  It also assumes that the
 * non-path base file name is not the empty string unless it is a dir.
 */
static int find_ct(request_rec *r)
{
    mime_dir_config *conf;
    apr_array_header_t *exception_list;
    char *ext;
    const char *fn, *type, *charset = NULL, *resource_name;
    int found_metadata = 0;

    if (r->finfo.filetype == APR_DIR) {
        ap_set_content_type(r, DIR_MAGIC_TYPE);
        return OK;
    }

    if (!r->filename) {
        return DECLINED;
    }

    conf = (mime_dir_config *)ap_get_module_config(r->per_dir_config,
                                                   &mime_module);
    exception_list = apr_array_make(r->pool, 2, sizeof(char *));
	
	/* If use_path_info is explicitly set to on (value & 1 == 1), append. */
    if (conf->use_path_info & 1) {
        resource_name = apr_pstrcat(r->pool, r->filename, r->path_info, NULL);
    }
    else {
        resource_name = r->filename;
    }

    /* Always drop the path leading up to the file name.
     */
    if ((fn = ap_strrchr_c(resource_name, '/')) == NULL) {
        fn = resource_name;
    }
    else {
        ++fn;
    }

    /* The exception list keeps track of those filename components that
     * are not associated with extensions indicating metadata.
     * The base name is always the first exception (i.e., "txt.html" has
     * a basename of "txt" even though it might look like an extension).
     */
    ext = ap_getword(r->pool, &fn, '.');
    *((const char **)apr_array_push(exception_list)) = ext;

    /* Parse filename extensions which can be in any order
     */
    while (*fn && (ext = ap_getword(r->pool, &fn, '.'))) {
        const extension_info *exinfo = NULL;
        int found;
		if (*ext == '\0') {  /* ignore empty extensions "bad..html" */
            continue;
        }

        found = 0;

        ap_str_tolower(ext);

        if (conf->extension_mappings != NULL) {
            exinfo = (extension_info*)apr_hash_get(conf->extension_mappings,
                                                   ext, APR_HASH_KEY_STRING);
        }

        if (exinfo == NULL || !exinfo->forced_type) {
            if ((type = apr_hash_get(mime_type_extensions, ext,
                                     APR_HASH_KEY_STRING)) != NULL) {
                ap_set_content_type(r, (char*) type);
                found = 1;
            }
        }

        if (exinfo != NULL) {

            if (exinfo->forced_type) {
                ap_set_content_type(r, exinfo->forced_type);
                found = 1;
            }

            if (exinfo->charset_type) {
                charset = exinfo->charset_type;
                found = 1;
            }
			
			if (exinfo->language_type) {
                if (!r->content_languages) {
                    r->content_languages = apr_array_make(r->pool, 2,
                                                          sizeof(char *));
                }
                *((const char **)apr_array_push(r->content_languages))
                    = exinfo->language_type;
                found = 1;
            }
            if (exinfo->encoding_type) {
                if (!r->content_encoding) {
                    r->content_encoding = exinfo->encoding_type;
                }
                else {
                    /* XXX should eliminate duplicate entities
                     *
                     * ah no. Order is important and double encoding is neither
                     * forbidden nor impossible. -- nd
                     */
                    r->content_encoding = apr_pstrcat(r->pool,
                                                      r->content_encoding,
                                                      ", ",
                                                      exinfo->encoding_type,
                                                      NULL);
                }
                found = 1;
            }
			
			/* The following extensions are not 'Found'.  That is, they don't
             * make any contribution to metadata negotation, so they must have
             * been explicitly requested by name.
             */
            if (exinfo->handler && r->proxyreq == PROXYREQ_NONE) {
                r->handler = exinfo->handler;
                if (conf->multimatch & MULTIMATCH_HANDLERS) {
                    found = 1;
                }
            }
            /* XXX Two significant problems; 1, we don't check to see if we are
             * setting redundant filters.    2, we insert these in the types config
             * hook, which may be too early (dunno.)
             */
            if (exinfo->input_filters && r->proxyreq == PROXYREQ_NONE) {
                const char *filter, *filters = exinfo->input_filters;
                while (*filters
                    && (filter = ap_getword(r->pool, &filters, ';'))) {
                    ap_add_input_filter(filter, NULL, r, r->connection);
                }
                if (conf->multimatch & MULTIMATCH_FILTERS) {
                    found = 1;
                }
            }
            if (exinfo->output_filters && r->proxyreq == PROXYREQ_NONE) {
                const char *filter, *filters = exinfo->output_filters;
                while (*filters
                    && (filter = ap_getword(r->pool, &filters, ';'))) {
                    ap_add_output_filter(filter, NULL, r, r->connection);
                }
				
				if (conf->multimatch & MULTIMATCH_FILTERS) {
                    found = 1;
                }
            }
        }

        if (found || (conf->multimatch & MULTIMATCH_ANY)) {
            found_metadata = 1;
        }
        else {
            *((const char **) apr_array_push(exception_list)) = ext;
        }
    }

    /*
     * Need to set a notes entry on r for unrecognized elements.
     * Somebody better claim them!  If we did absolutely nothing,
     * skip the notes to alert mod_negotiation we are clueless.
     */
    if (found_metadata) {
        apr_table_setn(r->notes, "ap-mime-exceptions-list",
                       (void *)exception_list);
    }
char *tmp = base_content_type;
            memcpy(tmp, ctp->type, ctp->type_len);
            tmp += ctp->type_len;
            *tmp++ = '/';
            memcpy(tmp, ctp->subtype, ctp->subtype_len);
            tmp += ctp->subtype_len;
            *tmp = 0;
            ap_set_content_type(r, base_content_type);
            while (pp != NULL) {
                if (charset && !strcmp(pp->attr, "charset")) {
                    if (!override) {
                        ap_set_content_type(r,
                                            apr_pstrcat(r->pool,
                                                        r->content_type,
                                                        "; charset=",
                                                        charset,
                                                        NULL));
                        override = 1;
                    }
                }
                else {
                    ap_set_content_type(r,
                                        apr_pstrcat(r->pool,
                                                    r->content_type,
                                                    "; ", pp->attr,
                                                    "=", pp->val,
                                                    NULL));
                }
                pp = pp->next;
            }
    if (r->content_type) {
        content_type *ctp;
        int override = 0;

        if ((ctp = analyze_ct(r, r->content_type))) {
            param *pp = ctp->param;
            char *base_content_type = apr_palloc(r->pool, ctp->type_len +
                                                 ctp->subtype_len +
                                                 sizeof("/"));
			if (charset && !override) {
                ap_set_content_type(r, apr_pstrcat(r->pool, r->content_type,
                                                   "; charset=", charset,
                                                   NULL));
            }
        }
    }

    /* Set default language, if none was specified by the extensions
     * and we have a DefaultLanguage setting in force
     */

    if (!r->content_languages && conf->default_language) {
        const char **new;

        if (!r->content_languages) {
            r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
        }
        new = (const char **)apr_array_push(r->content_languages);
        *new = conf->default_language;
    }

    if (!r->content_type) {
        return DECLINED;
    }

    return OK;
}
Le comportement est donc bien connu et documenté :) Cela n'empeche que cela peut rester une source de bypass sur un controle d'extension pour tout type d'ailleurs, pas uniquement pour le PHP.

Merci à j0rn et sh4ka pour toutes ces précisions.

Posted by cloud | Permanent link | File under: OpenSource, Security, Coding

13-08-2009 19:55:58

[Secu] Bypass du controle de l'extension PHP

PHP est exécuté par Apache car on lui indique quelle extension l'interpréteur doit lire. Cela se traduit en général avec la directive suivante dans le httpd.conf :
AddType application/x-httpd-php .php


L'interpréteur PHP s'exécute donc dès qu'il voit un fichier .php . Sauf qu'en réalité, celui ci s'exécute dès qu'il voit un .php dans un nom de fichier. Ainsi un fichier test.php.cequetuveu sera intéprété par PHP.

Super et alors vous me direz ? Et alors des développeurs font parfois leur controle de type de fichier sur les extensions. Ainsi un script vérifiant juste que l'on envoie pas un fichier avec une extension .php sera bypasssable en ajoutant une 2e extension derrière puis en appelant ce fichier :)

Après il faudrait peut etre étudié le pourquoi du comment cela réagit comme cela. StalkR parle du module mime mais je ne suis pas persuadé que cela vienne de la car l'extension pour le module PHP ne me semble pas géré par ce module.

Si quelqu'un a des réponses, qu'il n'hésite pas à me mailer :)

Posted by cloud | Permanent link | File under: Security, Coding

09-08-2009 14:37:22

[Buzz] TV 132cm pour 179euros sur les 3 Suisses

"Hey mec, va vite sur le site des 3 Suisses, y a une télé 132cm pour moins de 200euros !" . C'est le coup de fil que j'ai recu hier soir :)

Le buzz du week end est très certainement gagné haut la main par les 3 Suisses ! En effet le site propose une TV LCD 132cm TNT-HD HDTV 1080p Samsung LE-52B620 pour la modique somme de ... 179,99euros au lieu de 1899,99euros.

Donc bien sur cela ressemble forte à une belle erreur de saisie qui aurait pu couter cher. Seulement la loi francaise protège de ce genre de bourde et la vente sera probablement très vite annulée. On peut voir une explication sur le site de 01net.

Ce qui est assez marrant c'est le buzz que cela a créé, avec des groupes facebook pour l'annoncer, des annonces twitter, des news dans tous les sens et le plus drole c'est la crédulité des gens qui y croient. Certes les 3 Suisses pourront en envoyer 10 pour faire croire que ce n'était pas un bug et ensuite annoncé qu'ils n'ont plus de stock pour annuler le reste. La nature humaine est bien faite et malgré l'énormitude de la chose, les gens ont espoir :)

Dans l'histoire, ils ont surtout gagné une grosse quantité de comptes à qui ils vont pouvoir envoyer leur pub. D'une autre coté, je pense que les gens vont etre assez mécontent :). Cependant le site est quasi inaccessible depuis hier donc meme si c'est un buzz, les ventes ne doivent pas etre folichonnes durant ce week end ce qui est clairement une bonne perte pour le site de VPC. Le plus étonnant, c'est que l'annonce persiste ... et ca c'est dur à expliquer.

En tout cas, lundi matin, une bonne revue de leur système de validation de mise en page des produits et de controle des erreurs va etre à revoir. Une grosse quantité de mail d'annulation va également etre envoyé à tout le monde :)

Peut etre ont ils besoin d'un petit audit de leurs procédures et de leurs outils de saisie ? :)

Posted by cloud | Permanent link | File under: Fun / Divers

06-08-2009 22:56:34

[Secu] Vulnerabilite dans l'implementation XML

Une vulnérabilité a été annoncée par la société Codenomicon dans l'implémentation de la librairie XML dans certains produits. Celle ci impacte entre autre Sun, Apache et Python donc autant dire qu'il va falloir patcher dur :)

Cette vulnérabilité a été trouvée à partir de technique de fuzzing, très à la mode depuis pas mal de temps et qui semble donner des résultats.

Codenomicon a travaillé avec le CERT-FI pour traiter cette vulnérabilité. Les principaux constructeurs impactés ont été prévenus et des patch sont à venir. Il restera surement des librairies atypiques qui vont rester quelques temps impactées donc faites attention si vous manipulez du XML.

Pour plus d'info sur cette vulnérabilité, la source ici.

Posted by cloud | Permanent link | File under: Security