Categories
ASP.NET Web

Proteger la descarga de archivos en ASP.NET 2

En la anterior entrega (Protección de archivos con ASP.NET 2 y IIS 6), vimos que a partir de IIS 6, es posible proteger archivos que no son procesados directamente por ASP.NET; si bien es cierto que este método es fácil de implementar, tiene el inconveniente que en hostings compartidos es difícil o imposible hacer eso ya que generalmente no se tiene acceso a la configuración de IIS.

Otra forma de proteger la descarga de archivos de usuarios anónimos, es crear una página intermedia que antes de entregar los contenidos de éstos, realice las validaciones pertinentes al usuario que hace la petición. Para evitar que los usuarios descarguen directamente los recursos protegidos (por si conocen la ruta) es mejor que éstos estén ubicados en lugares no accesibles directamente desde el navegador (Ejemplo: App_Data o el directorio padre de la aplicación).

El siguiente ejemplo, ilustra de manera básica como proteger los archivos que están ubicados en App_Data\Protected:.

asp:

<%@ Page AutoEventWireup="true" CodeFile="Default.aspx.cs" EnableViewState="false"
    Inherits="_Default" Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Download</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="messageLabel" runat="server"></asp:Label>
        <asp:Repeater ID="filesRepeater" runat="server">
            <HeaderTemplate>
                <ul>
            </HeaderTemplate>
            <ItemTemplate>
                    <li><a href='?file=<%# HttpUtility.UrlEncode(Container.DataItem.ToString()) %>'><%# Container.DataItem %></a></li>
            </ItemTemplate>
            <FooterTemplate>
                </ul>
            </FooterTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

csharp:

using System;
using System.IO;
using System.Web;

public partial class _Default : System.Web.UI.Page
{
    readonly static string basePath = HttpContext.Current.Server.MapPath("~/App_Data/Protected");

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(Request.Params["file"]))
        {
            DownloadFile(Request.Params["file"]);
        }
        LoadFiles();
    }
    void LoadFiles()
    {
        string[] files = Directory.GetFiles(basePath);
        for (int i = 0; i < files.Length; i++)
        {
            files[i] = Path.GetFileName(files[i]);
        }
        filesRepeater.DataSource = files;
        filesRepeater.DataBind();
    }
    void DownloadFile(string file)
    {

        /*
         * Usar autenticación de formularios sobre esta página
         * o realizar algún otro proceso de validación del usuario
         */
        file = Request.MapPath(file, "~/App_Data/Protected", false);
        // Sólo descargar si el archivo está dentro de App_Data/Protected
        if (!file.StartsWith(basePath) || !File.Exists(file))
        {
            messageLabel.Text = "El archivo no existe";
            return;
        }
        Response.Clear();       
        Response.ContentType = "application/octet-stream";
        Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(file));
        Response.TransmitFile(file);
        Response.End();

    }
}

Al implementar este tipo de cosas, hay que validar bien el archivo a descargar, puesto que por un descuido, podemos hacer que el código de nuestra aplicación esté disponible a todo el mundo. 🙂

Categories
.NET Software Libre

Cecil: Examina y modifica ensamblados .NET al vuelo

Cecil es una librería creada por Jean-Baptiste Evain que permite examinar y modificar los ensamblados .NET de manera sencilla, a su vez estos cambios pueden sobreescribir el original o ser almacenados en un ensamblado nuevo.

Veamos dos ejemplos sencillos que muestran como funciona Cecil:

  1. Convertir una aplicación de consola a una aplicación Windows.- Si compilamos el siguiente código como una aplicación de consola, al ejecutarse mostrará 2 ventanas (una para la consola y otra para el formulario creado):

    csharp:

    // console.cs
    using System;
    using System.Windows.Forms;

    class Program
    {
        static void Main(string[] args)
        {
            Form frm = new Form();
            frm.Height = 100;
            frm.Width = 100;
            frm.ShowDialog();
        }
    }

    Para hacer la conversión usando Cecil, lo único que se tiene que hacer es:

    csharp:

    using Mono.Cecil;
    using Mono.Cecil.Cil;

    class Program
    {
        static void Main(string[] args)
        {
            AssemblyDefinition assembly = AssemblyFactory.GetAssembly("consola.exe");
            assembly.Kind = AssemblyKind.Windows;
            AssemblyFactory.SaveAssembly(assembly, "windows.exe");
        }
    }

    Si ejecutamos windows.exe, esta vez sólo se mostrará el formulario creado.

  2. Cambiar el espacio de nombres y modificadores de acceso: En este ejemplo se va a cambiar el espacio de nombres de las clases y los modificadores de acceso de un ensamblado denominado ClassLibrary1.dll

    csharp:

    namespace ClassLibrary1.Utility
    {
        public enum HashMethod
        {
            MD5,
            SHA1
        }

        internal class Security
        {
            public static string MD5(string password)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
        }
       
        class Validation
        {
            public static bool IsEmail(string email)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
            private void IsUrl(string url)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
            private void IsIp(string ip)
            {
                throw new System.Exception("The method or operation is not implemented.");
            }
        }
    }

    Para hacer realizar los cambios en el ensamblado de arriba:

    csharp:

    using Mono.Cecil;
    using Mono.Cecil.Cil;

    namespace CecilDemo
    {
        class Program
        { 
            static void Main(string[] args)
            {
                string assemblyName = "ClassLibrary1.dll";

                /* Cargar el ensamblado */
                AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyName);

                /* Iterar sobre los tipos del módulo principal */
                foreach (TypeDefinition type in assembly.MainModule.Types)
                {
                    /* Cambiar el espacio de nombres y el
                     * nivel de acceso a todas las clases
                     */

                    if ("<Module>" != type.Name)
                    {
                        type.Namespace = type.Namespace.Replace("ClassLibrary1", "Buayacorp");
                        type.Attributes |= TypeAttributes.Public;                   
                    }
                   
                    /* Hacer que todos los métodos de la clase Validation
                     * sean públicos y miembros de clase (static).
                     */

                    if ("Validation" == type.Name)
                    {
                        foreach (MethodDefinition method in type.Methods)
                        {
                            /* Eliminar el atributo 'private' de cada uno de los métodos */
                            if ((method.Attributes & MethodAttributes.Private) == MethodAttributes.Private)
                            {
                                method.Attributes ^= MethodAttributes.Private;
                            }

                            method.Attributes |= MethodAttributes.Public | MethodAttributes.Static;                       
                        }
                    }
                }
                /* Almacenar el nuevo ensamblado en CustomClassLibrary1.dll */
                AssemblyFactory.SaveAssembly(assembly, "Custom" + assemblyName);
            }
        }
    }

    Si observamos el ensamblado modificado con Reflector, veremos que se realizaron los cambios.

El uso de esta herramienta obviamente va a depender de las necesidades y posibles limitaciones de acceso al código fuente que tengamos, puesto que no sería nada productivo usar Cecil para hacer cosas parecidas a los ejemplos que muestro. 😀

Categories
Seguridad WordPress

Cuidado con los siguientes plugins para WordPress

Luego de una somera revisión del reporte de páginas no encontradas (gracias a AWStats), veo que hay varios intentos de acceso a plugins que no están instalados en este blog: wordtube, spamx, wp-table, myflash. Si se hace una búsqueda de cualquiera de los plugins mencionados (ejemplo), se puede ver que todos tuvieron el mismo problema de inclusión remota de archivos (Remote File Inclusion).

  • /wp-content/plugins/wordtube/wordtube-button.php: exploit
  • /wp-content/plugins/spamx/MTBlackList.Examine.class.php: exploit
  • /wp-content/plugins/wp-table/js/wptable-button.php: exploit
  • /wp-content/plugins/myflash/myflash-button.php: exploit

Actualización: Según gutielua, el plugin democracy también tiene problemas:

Quisiera comentar otro plugin que muestra fallas de seguridad se trata del DEMOCRACY

[/wordpress/wp-content/plugins/democracy/basic.css]

Uno de los administradores del hosting en donde me hospedo me comento que este archivo en especial hizo que se genera mucho trafico en el servidor, lo que provoco que me suspendieran el sitio por varios días.

Si alguien está usando estos plugins, es recomendable que actualice a las últimas versiones de éstos para evitar problemas posteriores.

Una vez más se puede ver que no importa que tan seguro sea el código principal de WordPress, si nosotros instalamos plugins que son desarrollados – a diestra y siniestra – por gente con poco o nulo interés en temas de seguridad.

Categories
.NET ASP.NET Miniposts PHP Seguridad Sql Injection Web XSS

Versión estable de PHPIDS (Intrusion Detection System)

Hoy acaban de anunciar que ya existe un versión estable de PHPIDS, un sistema de detección de intrusos basado en expresiones regulares. Pueden descargar el código desde el repositorio o hacer pruebas para ver como funciona esta pequeña librería.

Por otro lado, también existe .NETIDS, que es una versión en .NET -- realizada por Martin Hinks -- de PHPIDS.

Categories
WordPress

Pequeños ajustes en WordPress

El primero tiene que ver con un pequeño problema de seguridad que permite a cualquier usuario eliminar los archivos adjuntos que se suben utilizando WordPress, para corregirlo tienen que buscar la función mw_newMediaObject en xmlrpc.php. En la siguiente porción de código, se muestra como debería quedar finalmente esa función.

php:

/* metaweblog.newMediaObject uploads a file, following your settings */
function mw_newMediaObject($args) {
        // adapted from a patch by Johann Richard
        // http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/

        global $wpdb;

        $blog_ID     = (int) $args[0];
        $user_login  = $wpdb->escape($args[1]);
        $user_pass   = $wpdb->escape($args[2]);
        $data        = $args[3];

        $name = sanitize_file_name( $data['name'] );
        $type = $data['type'];
        $bits = $data['bits'];

        logIO('O', '(MW) Received '.strlen($bits).' bytes');
        if ( !$this->login_pass_ok($user_login, $user_pass) )
                return $this->error;
        set_current_user(0, $user_login);
        if ( !current_user_can('upload_files') ) {
                logIO('O', '(MW) User does not have upload_files capability');
                $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.'));
                return $this->error;
        }
        if ( $upload_err = apply_filters( "pre_upload_error", false ) )
                return new IXR_Error(500, $upload_err);

        if(!empty($data["overwrite"]) && ($data["overwrite"] == true)) {
                // Get postmeta info on the object.
                $old_file = $wpdb->get_row("
                        SELECT ID
                        FROM {$wpdb->posts}
                        WHERE post_title = '{$name}'
                                AND post_type = 'attachment'
                ");

                // Delete previous file.
                wp_delete_attachment($old_file->ID);

                // Make sure the new name is different by pre-pending the
                // previous post id.
                $filename = preg_replace("/^wpid\d+-/", "", $name);
                $name = "wpid{$old_file->ID}-{$filename}";
        }

        $upload = wp_upload_bits($name, $type, $bits, $overwrite);
        if ( ! empty($upload['error']) ) {
                $errorString = 'Could not write file ' . $name . ' (' . $upload['error'] . ')';
                logIO('O', '(MW) ' . $errorString);
                return new IXR_Error(500, $errorString);
        }
        // Construct the attachment array
        // attach to post_id -1
        $post_id = -1;
        $attachment = array(
                'post_title' => $name,
                'post_content' => '',
                'post_type' => 'attachment',
                'post_parent' => $post_id,
                'post_mime_type' => $type,
                'guid' => $upload[ 'url' ]
        );

        // Save the data
        $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
        wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );

        return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ) );
}

El segundo cambio, es más cosmético y es para que los emoticonos se muestren aún cuando éstos no estén rodeados de espacios o inicio y fin de cadena ;).

Tienen que cambiar la siguiente línea (vars.php:90 en la versión 2.1 y wp-includes/functions.php:1477 en la versión 2.2):

php:

$wp_smiliessearch[] = '/(\s|^)'.preg_quote($smiley, '/').'(\s|$)/';

por

php:

$wp_smiliessearch[] = '/(\B|^)'.preg_quote($smiley, '/').'(\B|$)/';

*: Este problema si no me equivoco, afecta a la versión 2.2 y 2.1.