Todos los años Google organiza una competición de capturar la bandera (Capture The Flag, CTF). Las CTF de Google tienen dos categorias, la CTF y la beginners quest. Las CTFs estan divididas en desafios, y cada desafío es un acertijo tecnológico para el que, o bien se tienen los conocimientos y las herramientas adecuadas o bien se aprende sobre la marcha.
La inscripción para la CTF es alrededor del mes de Julio y tiene una fase clasificatoria y una fase final. Los mejores de la fase clasficatoria son invitados a la final. La guía para la preparación es una auténtica joya, y está disponible en inglés en el siguiente enlace: https://trailofbits.github.io/ctf/
Como competiciones que son, las CTF son lugares dónde los ojeadores de empresas y entidades gubernamentales están a la caza de talentos. Normalmente hay equipos compitiendo y los premios son bastante interesantes. Aunque desde mi punto de vista lo importante es lo que se aprende, requiere muchas capacidades distintas y por eso normalmente las CTF se juegan en equipo y cada miembro del equipo puede atacar los problemas de la CTF de distinta manera. Para los participantes una CTF requiere preparación, no solo estando al día en las tecnologías sino en sus elementos prácticos, y muchas granas de aprender. Los desafios suelen ser variados: web, cryptography, hardware, reverse engineering...
Afortunadamente cada vez hay más tutoriales y documentación disponible para prepararse para una CTF, aunque, una vez que se está en faena, hay que esperar lo inesperado.
En general, las CTF tienen una duración determinada y puntuación asociada a cada reto pero hay algunas que son un poco más relajadas, como la beginners quest de Google, no hay un limite de tiempo y no hay una puntuación asociada, sólo el deseo de ir superando los desafíos.
Este año, en mis ratos libres, he estado aprendiendo con la beginners quest (https://capturetheflag.withgoogle.com/beginners-quest). El hecho de que el nombre contenga la palabra beginner (principiante) no significa que sea fácil. Los desafíos son bastante variados y algunos laboriosos, pero la experiencia de aprendizaje es fantástica. No importa lo mucho que se sepa, siempre se aprende algo más. Además la comunidad de participantes ha tenido el apoyo de los creadores en un canal de Discord, lo cual es un autentico lujo.
Lo que viene a continuación son SPOILERS.
.
.
.
.
.
.
.
.
.
.
Desafío 1: El primer desafío suele estar ahí para entrar en calor. En este caso se presenta un panel que pide una contraseña.
https://cctv-web.2021.ctfcompetition.com/
Si en el navegador se abre las herramientas de desarrollo para ver el código de la página. En ese código hay una llamada a un script.
<script>
const checkPassword = () => {
const v = document.getElementById("password").value;
const p = Array.from(v).map(a => 0xCafe + a.charCodeAt(0));
if(p[0] === 52037 &&
p[6] === 52081 &&
p[5] === 52063 &&
p[1] === 52077 &&
p[9] === 52077 &&
p[10] === 52080 &&
p[4] === 52046 &&
p[3] === 52066 &&
p[8] === 52085 &&
p[7] === 52081 &&
p[2] === 52077 &&
p[11] === 52066) {
window.location.replace(v + ".html");
} else {
alert("Wrong password!");
}
}
window.addEventListener("DOMContentLoaded", () => {
document.getElementById("go").addEventListener("click", checkPassword);
document.getElementById("password").addEventListener("keydown", e => {
if (e.keyCode === 13) {
checkPassword();
}
});
}, false);
</script>
v sera el password que escribamos, al que se le sumará 0xCafe (hexadecimal, 51966 en decimal) y eso nos dara p. Se debe verificar que p[0]=52037, p[6]=52081... y asi sucesivamente. El password tiene 12 carácteres (p[0]...p[11]). Pues manos a la obra, si a los valores de p le restamos 51966, obtendremos los valores de los caracteres.
p=[71 111 111 100 80 97 115 115 119 111 114 100]
y pasando a char, se obtiene
GoodPassword
Para hacer los calculos he usado octave, que nos da muchas funciones para trabajar de manera sencilla y rapida. En concreto, he usado hex2dec('cafe') para calcular el valor de cafe, y char(p)
y la bandera en este caso es: CTF{IJustHopeThisIsNotOnShodan}
(Más Spoilers)
.
.
.
.
.
.
.
Desafío 2: El siguiente desafío es buscar las entradas adecuadas para que la salida de un diagrama de puertas lógicas de 1.
Las puertas lógicas del esquema que se proporcionan son AND, OR, NOT, NOR y XOR. Yo en lugar de analizar el diagrama, en este caso use un simulador de puertas lógicas, que se puede encontrar en el siguiente enlace http://weblidi.info.unlp.edu.ar/catedras/organiza/circuitos/editor_simple.html
Cada entrada tiene una letra asociada, la bandera sera CTF{ letras que tienen un valor 1 }, lo que en este caso es: CTF{BCFIJ}
(Más Spoilers)
.
.
.
.
.
.
.
Desafío 3: Este desafio es de programación en javascript. hay que programar un algoritmo para que un coche sea capaz de conducirse solo por una carretera recta sin chocar con los coches que tiene delante, ni salirse de la carretera.
https://high-speed-chase-web.2021.ctfcompetition.com/
La función a programar se llama controlCar y recibe un array llamado scanArray.
scanArray es un vector de 17 posiciones scanArray[0]...scanArray[16] que devuelve una serie de números asociados con la distancia del coche a los coches que hay delante. Se puede imaginar, que hay 17 sensores, el sensor que coincide con el centro de nuestro coche es el scanArray[8], scanArray[7] está el faro izquierdo del coche y scanArray[9] en el derecho, el resto se van extendiendo a derecha e izquierda hasta cubrir los tres carriles de la carretera y un poco de los arcenes (que no debemos pisar). Además si un sensor devuelve un 0 puede significar que en esa posicion el sensor puede estar midiendo en el arcén (fuera de la carretera).
Afortunadamente la función console.log() esta activa, y podemos pasarle scanArray para ver cómo son sus valores usando la función de depuración del navegador (F12 en Firefox y Chrome). Si se incluye un console.log(scanArray)
se pueden ver los valores que va tomando scanArray, como por ejemplo:
[0, 0, 0, 0, 0, 3.75, 3.75, 3.75, 4.25, 4.25, 4.25, 31.75, 31.75, 31.75, 0, 0, 0]
He marcado el centro del coche (scanArray[8] en negrita). En este caso, el coche va entre el carril izquierdo y el central (hay muchos ceros en el principio del vector, que corresponden a los sensores de la izquierda) y hay tanto un coche en el carril izquierdo, como un coche en el carril central, ambos muy cerca (cada coche ocupa tres sensores).
El algoritmo que yo programé es muy sencillo. Se suman los valores de los tres sensores justo a la izquierda del coche (scanArray[4,5,6]) que corresponderian al carril izquierdo si el coche esta en el centro, los tres sensores centrales (scanArray[7,8,9]) que corresponderian al carril central, y los tres sensores de la derecha (scanArray[10,11,12]). El valor mas alto me da una indicación de hacia donde puedo mover el coche, porque el coche que haya en ese carril, estará mas lejos. Este es el comportamiento general, el coche se debe mover al carril dónde el coche que esté delante este lo más lejos posible. Esta regla es general, pero no me dice que esta pasando cerca de mi coche, ya que he agrupado sensores.
Ahora vamos a introducir los giros rápidos de volante, si a la izquierda hay un coche muy cerca, debo moverme a la derecha, si a la derecha hay un coche muy cerca, debo moverme a la izquierda y si tanto a derecha como izquierda hay coches muy cerca, tengo que seguir recto.
¿Qué significa que haya un coche muy cerca? pues que esté a menos distancia de una distancia mínima que puedo elegir. El desafio tiene un pequeño error, y es que si la distancia mínima es muy pequeña, no te devuelve la bandera, te dice que algo fue mal y te pregunta si estás intentando hacer trampas. Este efecto se puede comprobar poniendo min_distance en el código a un valor de 5.
function controlCar(scanArray
{
direction = [-1, 0, 1];
function max2(d1,d2) {
if (d1>d2){
return 1
}
else {
return 2
}
}
function max3(d1,d2,d3){
if (max2(d1,d2)==1){
if (max2(d1,d3)==1){
return 1}
else {
return 3}
}
else if (max2(d1,d2)==2)
if (max2(d2,d3)==1){
return 2}
else {
return 3}
}
left_distance = 0
for (i = 4; i < 7; i++) {
left_distance = left_distance + scanArray[i]
}
center_distance = 0
for (i = 7; i < 10; i++) {
center_distance = center_distance + scanArray[i]
}
right_distance = 0
for (i = 10; i < 13; i++) {
right_distance = right_distance + scanArray[i]
}
id_max = max3(left_distance,center_distance,right_distance)
proposed_direction = direction[id_max-1]
min_distance = 6
if (scanArray[10]<min_distance){
proposed_direction=-1
}
if (scanArray[6]<min_distance){
proposed_direction=1
}
if ((scanArray[6]<min_distance)&(scanArray[10]<min_distance)){
proposed_direction=0
}
return proposed_direction
}
Y la bandera es: CTF{cbe138a2cd7bd97ab726ebd67e3b7126707f3e7f}
Y hasta aquí por ahora. En la siguiente entrada más.
No hay comentarios:
Publicar un comentario