Alters

Hacking COBOL: Data Poisoning (I)

Buenas!

Vaya cacao que tenemos montado, ¿eh? Espero que estéis todos bien :)
Yo, aquí sigo... a tope con el trabajo, y aprovechando que esta semana hay vacaciones para traeros una buena entrada (también acabo de subir un nuevo repositorio en GitHub)!

Hoy os comentaré un poco sobre el Data Poisoning en COBOL, para variar un poquito.

¡Vamos allá!




¿Qué es de Data Poisoning?

Como indica su nombre en inglés, es, básicamente, envenenar datos (una técnica de hacking para, por ejemplo, corromper memoria); con ello logramos que los programas muestren comportamientos inesperados, que nosotros, como atacantes, aprovecharemos.


¿Cómo se logra hacer?

Pues, depende de cada situación. Hay situaciones en la que es más sencillo, ya sea por la disponibilidad del código fuente (un punto bastante importante a tener en cuenta), o debido a la flexibilidad del lenguaje de programación usado.


Data Poisoning en COBOL
Antes de entrar en materia, recordad que disponéis de toda una sección de recursos de COBOL a vuestro alcance en el blog.

Como sabéis, COBOL es un lenguaje fuertemente tipado, es decir, cada variable tiene un tipo de dato estático. Además, los tipos de datos en COBOL son algo más complejos que en otros lenguajes de programación de la misma generación (por ejemplo, C).

Por otra parte, por mucho que nos empeñemos en llamar "variables" a los elementos de la WORKING-STORAGE SECTION de COBOL, la verdad es que no son variables al uso - ¡son sinónimos de punteros!

Este último punto es importante, ya que abre una puerta a este tipo de ataque, que, bien ejecutado, puede otorgarnos grandes resultados.


En este ejemplo, podemos ver una representación de una WORKING-STORAGE SECTION, que equivaldría a la siguiente transcripción

[code lan=cobol] WORKING-STORAGE SECTION 01 WS-VAR-1 PIC 9(1). 01 WS-COMP1. 05 WS-VAR2 PIC X(1). 05 WS-VAR3 PIC 9(3). 01 WS-TABLA1 OCCURS 5 PIC X(1). 01 WS-VAR4 PIC 9(1). [/code]


Es decir, podemos ver al WSS como una "caja" que contiene, de manera secuencial, todas nuestras variables (ojo, punteros).

Cuando accedemos a WS-VAR1, accedemos a la posición de un hipotético puntero WSS(1:1), y cuando accedemos a  WS-VAR4, accedemos a WSS(11:1).

Estos offset se calculan durante la compilación y se guarda una relación "nombre - offset".

¿Y qué me quieres decir con todo esto?

Pues... que es posible acceder a WS-VAR4 sin hacer uso de esa variable. Suena descabellado, pero se hace continuamente.

Por ejemplo, en nuestro ejemplo, es lo mismo acceder a WS-COMP(2:3) que acceder a WS-VAR3, ¿verdad?


Provocar Data Poisoning en COBOL

En primer lugar, cabe destacar que gran parte del Data Poisoning que se puede realizar en COBOL se produce por una mala praxis, y por tanto, puede ser remediado de manera relativamente sencilla.


Data Poisoning en OCCURS con acceso aleatorio

¿Cómo se produce?


Ocurre algo muy curioso con los OCCURS (sea en una declaración o en un REDEFINES), y es que la cláusula OCCURS solo se tiene en cuenta cuando se construye la tabla de punteros del ejecutable; es decir, un párrafo tal que

[code lan=cobol] PERFORM VARYING WS-VAR1 FROM 1 BY 1 UNTIL WS-VAR1 = 6 MOVE 'A' TO WS-TABLA1(WS-VAR1) END-PERFORM. [/code]


No produciría un error de compilación, ni tampoco un error de ejecución.

¿No?

No. Como el concepto de variable es difuso en COBOL, lo que estaríamos haciendo aquí sería acceder a la posición WS-TABLA1 + *WS-VAR1 del puntero global... es decir, en el último MOVE estaríamos moviendo una "A" a WS-VAR4, y en caso de, por ejemplo, realizar:

ADD 1    TO WS-VAR4

Provocaríamos un abend 0C7 (Data Exception); y es que, para eso existe la cláusula IS [NOT] NUMERIC :-)


¿Cómo se arregla?


La manera más habitual de evitar estas situaciones es añadir la variable "DEPENDING ON" en nuestro OCCURS.

[code lan=cobol] 01 WS-TABLA1. 05 WS-T1-MAX PIC 9(2). 05 WS-T1-REG. 10 WS-T1-T OCCURS 1 TO 99 DEPENDING ON WS-T1-MAX. 15 WS-T1-R PIC X(1). [/code]


De esta manera, establecemos una tabla que variará de 1 hasta 99 repeticiones (99 por ser el valor máximo de WS-T1-MAX), y cuyo tamaño irá ligado a WS-T1-MAX.

Finalmente, iteramos la tabla VARYING WS-VAR FROM 1 BY 1 UNTIL WS-VAR > WS-T1-MAX, y así garantizamos no exceder nunca el límite de la tabla.

Otro punto a tener en cuenta es el de añadir siempre una condición de escape por defecto a nuestros bucles

Incorrecto:

[code lan=cobol] PERFORM VARYING WS-CONTADOR FROM 1 BY 1 UNTIL WS-DATOS(WS-CONTADOR) = WS-CONSTANTE [/code]

Correcto:

[code lan=cobol] PERFORM VARYING WS-CONTADOR FROM 1 BY 1 UNTIL WS-DATOS(WS-CONTADOR) = WS-CONSTANTE OR WS-CONTADOR > WS-MAX-TABLE [/code]



Data Poisoning en OCCURS con tabla multipropósito

¿Cómo se produce?

Este tipo de error es más común, sobre todo teniendo en cuenta el contexto - COBOL se ha usado durante unos 60 años, así que en muchas ocasiones se optimizaban los programas para ahorrar memoria.

No es extraño ver que se reutilizan variables (¡punteros!), y sobre todo, tablas de OCCURS. Imaginemos que estamos tratando la relación "alumno - asignaturas", y tenemos los siguientes datos

  • Alumno 1
    • Asignatura 1
  • Alumno 2
    • Asignatura 1
    • Asignatura 2
  • Alumno 3
    • Asignatura 2
Si toda esta información / alumno se va almacenando en una tabla de OCCURS mal gestionada, (que confía en el límite establecido en una variable interna) podríamos encontrarnos con el siguiente escenario:

  • Loop 1:
    • Asignatura 1
      • Límite: 1
  • Loop 2:
    • Asignatura 1
    • Asignatura 2
      • Límite: 2
  • Loop 3:
    • Asignatura 2
    • Asignatura 2
      • Límite: 1

Tanto en cuanto respetemos el "Límite", todo estará bien, pero ¿Y si en algún punto perdemos el control del límite?


¿Cómo se arregla?


En la estructura que exponía anteriormente:

[code lan=cobol] 01 WS-TABLA1. 05 WS-T1-MAX PIC 9(2). 05 WS-T1-REG. 10 WS-T1-T OCCURS 1 TO 99 DEPENDING ON WS-T1-MAX. 15 WS-T1-R PIC X(1). [/code]

He dejado, a propósito, un registro "05 WS-T1-REG" que es capaz de referenciar toda la tabla entera. Con esto en mente, al inicio del programa  entre loops, se debería añadir:


INITIALIZE WS-T1-REG

Esto es importante, ya que no podemos hacer un INITIALIZE de WS-T1-T sin hacer referencia a un SUBSCRIPT (es decir, a un índice del array), y por tanto, de no tener WS-T1-REG, deberíamos hacer un bucle que fuera por cada SUBSCRIPT de la tabla realizando INITIALIZE.

Con esta instrucción, estamos vaciando la zona de memoria a la que apunta el puntero referenciado, y por tanto, si en algún punto perdemos el control sobre "Límite", no arrastraremos datos indeseados.



Data Poisoning en OCCURS con tabla infradimensionada

¿Cómo se produce?


En este caso, estaríamos hablando de un programa que se está viendo superado en sus capacidades, y además, no contemplaba esta superación.

Siguiendo con el ejemplo inicial, tenemos una tabla OCCURS 5, que bien podríamos decir que nos sirve para almacenar las asignaturas de los alumnos.

¿Y si un alumno está matriculado en seis asignaturas?

Pues, o bien se controla correctamente, o bien estamos realizando un Data Poisoning.


¿Cómo se arregla?


Por lo general, yo no soy partidario de almacenar datos de dimensión desconocida en un OCCURS de COBOL, precisamente por esto mismo.

En mi opinión, un programa que utilice este recurso debe replantearse, simplificarse, o simplemente dividirse en varios subprocesos (dada la gran capacidad de gestión de ficheros de COBOL, se puede ir gestionando la información a través de ficheros).

En definitiva, un OCCURS debería servir para almacenar datos de dimensión finita, por ejemplo:

Incorrecto
  • Almacenar las asignaturas en las que está matriculado un alumno
Correcto
  • Almacenar los alumnos de un aula, teniendo en cuenta que hay un máximo de 50 plazas por aula.

¿Hay más tipos de Data Poisoning?

Por supuesto. Algunos ejemplos son
  • Debido a "ACCEPT"
  • Debido al uso de ficheros con múltiples propósitos
  • Debido a un mal uso de LINKAGE SECTION

No os preocupéis, los iremos tratando en sucesivas entradas ;-)

Espero que os haya entretenido (al menos) este pequeño monográfico sobre Data Posioning.


¡Hasta la próxima!


¿Te han resultado útiles mis conocimientos de COBOL?
¡Llévatelos donde quieras!




No hay comentarios:

Publicar un comentario