¿Es posible XSS cuando no se escapa, pero tampoco se muestra si va seguido de un carácter?

10

Descubrí que < no estaba codificado como &lt; . En lugar de eso, simplemente no es enviado por el servidor si es seguido directamente por algo. Por ejemplo, <a no mostrará nada. Pero puedo mostrar < a> (note el espacio), lo cual no es útil.

Excepto el carácter < , no hay restricción ni caracteres codificados.

Aquí está el código dado:

<span>USER INPUT</span>

Aquí está el tipo de contenido de la página:

text/html; charset=utf-8

Y el tipo de contenido para la solicitud de entrada:

application/x-www-form-urlencoded; charset=UTF-8 

Otras informaciones específicas:

Server: nginx
X-Powered-By: PHP/5.5.18-1~dotdeb.1

¿Hay alguna manera de que este método sea vulnerable a XSS?

    
pregunta Xavier59 09.05.2016 - 23:04
fuente

4 respuestas

8

No, en un contexto HTML no puedes inyectar nuevas etiquetas sin permitir letras después del corchete de apertura. Aún así, esta técnica de filtrado es innecesariamente arriesgada.

El analizador HTML de su navegador web analiza el código como una máquina de estado . Para comprender cuáles son sus opciones, eche un vistazo a la especificación de sintaxis HTML y las posibles transiciones de estado.

Su punto de inyección se encuentra en el estado de los datos (que es el " estado predeterminado, fuera de cualquier etiqueta):

8.2.4.1 Data state

Consume the next input character:

U+0026 AMPERSAND (&)
    Switch to the character reference in data state.
"<" (U+003C)
    Switch to the tag open state.
U+0000 NULL
    Parse error. Emit the current input character as a character token.
EOF
    Emit an end-of-file token.
Anything else
    Emit the current input character as a character token.

Para XSS, la única continuación interesante es abrir una etiqueta con < y cambiar a etiqueta estado abierto :

8.2.4.8 Tag open state

Consume the next input character:

"!" (U+0021)
    Switch to the markup declaration open state.
"/" (U+002F)
    Switch to the end tag open state.
Uppercase ASCII letter
    Create a new start tag token, set its tag name to the lowercase version of the current input character (add 0x0020 to the character's code point), then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.)
Lowercase ASCII letter
    Create a new start tag token, set its tag name to the current input character, then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.)
"?" (U+003F)
    Parse error. Switch to the bogus comment state.
Anything else
    Parse error. Switch to the data state. Emit a U+003C LESS-THAN SIGN character token. Reconsume the current input character. 

Aquí sus opciones son a-z , A-Z , ! , / y ? . No ha sido claro si los caracteres especiales también están en la lista negra. Pero incluso si no lo están, no tiene suerte:

  • Desde <! solo se obtiene un comentario ( <!-- ), una declaración doctype ( <!DOCTYPE ) o una sección CDATA ( <![CDATA ). Estos no son nodos DOM reales y, por lo tanto, son inútiles para XSS. (Por ejemplo, no podría adjuntar un controlador de eventos a un comentario).
  • <? puede ser interesante en XML, pero se trata como un comentario en HTML.
  • </ solo te permitirá cerrar etiquetas.

Es posible que hayas notado que la especificación tampoco admite caracteres de relleno, como espacios, pestañas, nuevas líneas o caracteres de control.

Si desea profundizar un poco más y verificar la implementación, siempre puede consultar el código fuente. Por ejemplo, este extracto es parte del tokenizer HTML5 utilizado en Mozilla Firefox . Como puede ver, el estado de etiqueta abierta se adhiere estrechamente a la especificación:

case NS_HTML5TOKENIZER_TAG_OPEN: {
  for (; ; ) {
    if (++pos == endPos) {
      NS_HTML5_BREAK(stateloop);
    }
    c = checkChar(buf, pos);
    if (c >= 'A' && c <= 'Z') {
      endTag = false;
      clearStrBufAndAppend((char16_t) (c + 0x20));
      state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
      NS_HTML5_BREAK(tagopenloop);
    } else if (c >= 'a' && c <= 'z') {
      endTag = false;
      clearStrBufAndAppend(c);
      state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
      NS_HTML5_BREAK(tagopenloop);
    }
    switch(c) {
      case '!': {
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      case '/': {
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_CLOSE_TAG_OPEN, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      case '\?': {
        if (viewingXmlSource) {
          state = P::transition(mViewSource, NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION, reconsume, pos);
          NS_HTML5_CONTINUE(stateloop);
        }
        if (P::reportErrors) {
          errProcessingInstruction();
        }
        clearStrBufAndAppend(c);
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      case '>': {
        if (P::reportErrors) {
          errLtGt();
        }
        tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 2);
        cstart = pos + 1;
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
      default: {
        if (P::reportErrors) {
          errBadCharAfterLt(c);
        }
        tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
        cstart = pos;
        reconsume = true;
        state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
        NS_HTML5_CONTINUE(stateloop);
      }
    }
  }
  tagopenloop_end: ;
}

Entonces, el filtro XSS que usted describe parece ser seguro según la especificación HTML. Es un hielo muy delgado, resistente. Nunca se sabe, si algún proveedor tiene una implementación peculiar que aún podría habilitar un exploit. (Microsoft, te estoy mirando!)

La protección XSS correcta es, por lo tanto, simplemente escapar del corchetes .

    
respondido por el Arminius 17.05.2016 - 04:26
fuente
4

Muy improbable. El único vector que puedo ver es un error de codificación de caracteres en el que el lenguaje backend (por ejemplo, PHP) está configurado para usar una codificación de caracteres diferente a la de UTF-8, y no tiene en cuenta la codificación al generar la cadena.

Dicho esto, diría que es casi seguro que no es posible obtener XSS en este caso. Sin embargo, podría obtener XSS en cualquier ubicación donde ya esté dentro de una etiqueta (por ejemplo, hacer eco en un atributo).

    
respondido por el Polynomial 10.05.2016 - 11:24
fuente
1

En este caso particular, está inyectando su entrada como código HTML (ya que se encuentra entre las etiquetas de intervalo y el tipo de contenido es texto / html). Por lo tanto, la única manera en que puedo pensar en el código JavaScript de inyección aquí es mediante etiquetas HTML. Dado que el servidor está respondiendo de nuevo, es muy improbable que pueda inyectar JavaScript en este caso particular.

Tienes 2 opciones: primero, puedes intentar buscar si puedes encontrar otro lugar donde ya estés dentro de una etiqueta HTML (como se menciona en @Polynomial). En segundo lugar, puede encontrar un lugar en la página, donde esta entrada se inyecta como JavaScript (por ejemplo, como un valor para los controladores de eventos, dentro de las etiquetas, etc.), en lugar de HTML.

Si puede encontrar una segunda condición (inyectar la entrada como JS) y se encuentra en una situación similar en la que el servidor intenta incluir en la lista negra de ciertos caracteres, puede intentar usar algo como JSF##k para evitar un archivador / lista negra en particular. JSF ## k es un estilo de programación esotérico y educativo basado en las partes atómicas de JavaScript. Utiliza solo seis caracteres diferentes para escribir y ejecutar código. Echa un vistazo a esta publicación para más información al respecto.

    
respondido por el Rahil Arora 16.05.2016 - 22:41
fuente
1

En primer lugar, XSS depende completamente del contexto en el que se muestra la salida. Forzar todas las entradas a través del mismo filtro nunca funcionará todo el tiempo y requerirá que los desarrolladores y los evaluadores de penetración busquen todas las inyecciones. La política de seguridad de contenido ayudará a evitar todo tipo de XSS, incluso cuando la aplicación sufra. De saneamiento de salida rota.

Existen varias formas en que XSS es posible dadas estas restricciones arbitrarias. Lo primero que me viene a la mente es XSS basado en DOM y inyección de controlador de eventos . Reflective XSS también funcionará en las siguientes condiciones:

<script>
var a="$xss";
</script>

Prueba de concepto: "+alert(document.cookie)+"

O si es una ubicación:

<a href="$xss">click me</a>
<iframe src="$xss"/>

Prueba de concepto: javascript:alert(1)

    
respondido por el rook 17.05.2016 - 05:53
fuente

Lea otras preguntas en las etiquetas