PlaidCTF – The Game write-up

Este fin de semana pudimos jugar al CTF que prepararon los PPP (Plaid Parliament of Pwning) que, para mi gusto, es uno de los más complicados, además de divertidos. La verdad es que no conseguimos pasar del puesto 103 pero aún así nos lo pasamos genial concursando.

 

Entre el resto de equipos españoles, destacar a los int3pids que quedaron en el puesto 11, PentSec en el 74, Activalink en el 80, y algunos otros que quedaron por detrás nuestra.

 

Nosotros pasamos únicamente 4 retos, pero este concretamente me gustó porque había que programar y no sólo estrujarse el cerebro pensando. Está catalogados como nivel medio-fácil (viendo que sólo vale 100 puntos), así que podéis imaginar cómo serían los difíciles 🙂

 

El enunciado decía esto:

 

Robots enjoy some strange games and we just can’t quite figure this one out. Maybe you will have better luck than us.
23.22.16.34:6969

 

(nota: por saturación del servidor llegaron a cambiar de máquina 2 veces)

 

Tras conectar con un Netcat al servidor obteníamos algo así:

You have gotten 0 of 75
Choice 1 = f17775ea8c035349a2aca2a8bb3072897e
Choice 2 = 8ad96c125229183197c5ac7901216ba07f
Which one is bigger? (1 or 2)

A lo que debíamos aceptar 75 veces cuál de los dos era mayor, pero tras perder mucho tiempo buscando la lógica de los hashes:

 

– Mirando cuál era mayor
– Sumando los números
– Sumando las letras
– Comparando cada dígito
etc.

 

Al final optamos por programar un script que memorizara las soluciones a la espera de que se produjeran repeticiones.

 

Lo que hace el script es:

1- Preparamos 2 arrays para almacenar las parejas de tokens, en el primer array guardaremos los mayores y en el segundo, los menores.
2- Verificamos si la combinación solicitada está en nuestra lista de tokens almacenados (T1 > T2 o T2 > T1).
3- Si no está, verificamos si tenemos esos tokens guardados y hay algún valor intermedio que nos permita deducir si es mayor o menor (T1 > x > T2 o T2 > x > T1).
4- Si no tenemos la respuesta, probamos con un T1 > T2 y guardamos el resultado dependiendo de si hemos acertado o fallado.

 

Además de almacenar los tokens en arrays, los guardamos en disco para no perder los avances en caso de reiniciar por a algún fallo del server.

 

Y el script con el que conseguimos pasar la prueba es:

#!/usr/bin/perl

# by Pepelux

use IO::Socket;

my @t1;	# valores mayores
my @t2;	# valores menores

open (MYFILE, 'TheGame.txt');

while (<MYFILE>) {
	chomp;

	(my $token1, my $token2) = split(/ /, $_, 2);

	push (@t1, $token1);
	push (@t2, $token2);
}

close (MYFILE);

my $sc = new IO::Socket::INET (PeerAddr => '23.22.16.34',
		 PeerPort => '6969',
		 Timeout => '10',
		 Proto => 'tcp')
		 or die("Server is not available.\n");

while (1) {
	# cogemos los datos
	recv($sc, my $data1, 500, undef);
	my $tmp = $data1;
	$tmp =~ /gotten\s([0-9|\s|[a-z]*)/;
	my $values = "Found $1";
	$values =~ s/\n//g;

	if ($data1 =~ /76 of 75/i) {
		print "$data1\n\n";
		print "$data2\n\n";
		print "$data3\n\n";

		# guardamos en un fichero de logs
		open (MYFILE, '>>TheGame.log');
		print MYFILE "$data1\n";
		print MYFILE "$data2\n";
		close (MYFILE);
		exit;
	}

	if ($data1 !~ /bigger/) {
		recv($sc, my $data2, 500, undef);
	}

	# obtenemos los tokens
	my $tmp = $data1;
	$tmp =~ /Choice\s1\s=\s([a-f|0-9]*)\s/;
	my $token1 = $1;

	$tmp = $data1;
	$tmp =~ /Choice\s2\s=\s([a-f|0-9]*)\s/;
	my $token2 = $1;

	my $res = "1";
	my $new = 1;
	my $type = 0;

	# comprobamos si tenemos ese token guardado
	for (my $i = 0; $i < $#t1; $i++) {
		if (($t1[$i] eq $token1) && ($t2[$i] eq $token2)) {
			$res = "1";
			$new = 0;
			$type = 1;
		}

		if (($t2[$i] eq $token1) && ($t1[$i] eq $token2)) {
			$res = "2";
			$new = 0;
			$type = 2;
		}
	}

	if ($new eq 1) {
		# comprobamos si tenemos que token1 > que algún otro > token2
		FOO1: {
			for (my $i = 0; $i < $#t1; $i++) {
				if ($t1[$i] eq $token1) {
					my $tmp = $t2[$i];

					for (my $j = 0; $j < $#t1; $j++) {
						if (($t1[$j] eq $tmp) && ($t2[$j] eq $token2)) {
							$res = "1";
							$new = 2;
							$type = 3;
							last FOO1;
						}
					}
				}
			}
		}
	}

	if ($new eq 1) {
		# comprobamos si tenemos que token2 > que algún otro > token1
		FOO2: {
			for (my $i = 0; $i < $#t1; $i++) {
				if ($t1[$i] eq $token2) {
					my $tmp = $t2[$i];

					for (my $j = 0; $j < $#t1; $j++) {
						if (($t1[$j] eq $tmp) && ($t2[$j] eq $token1)) {
							$res = "2";
							$new = 2;
							$type = 4;
							last FOO2;
						}
					}
				}
			}
		}
	}

	# enviamos la respuesta y obtenemos el resultado
	print $sc "$res\n";
	recv($sc, my $data3, 500, undef);

	if ($data3 =~ /key/i) {
		print "$data1\n\n";
		print "$data2\n\n";
		print "$data3\n\n";

		# guardamos en un fichero de logs
		open (MYFILE, '>>TheGame.log');
		print MYFILE "$data1\n";
		print MYFILE "$data2\n";
		close (MYFILE);
		exit;
	}

	my $c = "Correct";
	my $ft1 = $token1;
	my $ft2 = $token2;

	# si no estaba almacenada esa combinación de tokens, la guardamos
	if ($new > 0)	{
		# si la respuesta es incorrecta, cambiamos el resultado
		if ($data3 =~ /Wrong/) {
			$c = "Wrong";

			if ($res eq "1") {
				push (@t1, $token2);
				push (@t2, $token1);
				$ft1 = $token2;
				$ft2 = $token1;
			}
			else {
				push (@t1, $token1);
				push (@t2, $token2);
			}
		}
		else {
			if ($res eq "1") {
				push (@t1, $token1);
				push (@t2, $token2);
			}
			else {
				push (@t1, $token2);
				push (@t2, $token1);
				$ft1 = $token2;
				$ft2 = $token1;
			}
		}

		# guardamos en un fichero los tokens
		open (MYFILE, '>>TheGame.txt');
		print MYFILE "$ft1 $ft2\n";
		close (MYFILE);
	}
	else {
		$c = "Wrong" if ($data =~ /Wrong/);
	}

	# guardamos en un fichero de logs
	open (MYFILE, '>>TheGame.log');
	print MYFILE "$data1\n";
	print MYFILE "$data2\n";
	print MYFILE "$data3\n";
	close (MYFILE);

	$type = "T1 > T2" if ($type eq 1);
	$type = "T2 > T1" if ($type eq 2);
	$type = "T1 > x > T2" if ($type eq 3);
	$type = "T2 > x > T1" if ($type eq 4);

	print "$values - " . ($#t1+1) . " token pairs saved - $c (res=$res)";
	print " - match found ($type)" if ($new ne 1);
	print "\n";
}

Finalmente, una vez acertadas las 75 respuestas seguidas, en el fichero TheGame.log podemos ver la key para pasar el reto, que es: d03snt_3v3ry0n3_md5

 

5 comentarios

  1. Gracias por la explicación, le invertí bastante tiempo a este reto y no pude pasarlo jeje, bastante bueno el CTF, muy entretenido.

    Saludos

  2. Yo hice una gorrinada con un diccionario de python. Les asignaba valores numéricos «tentativos» y los iba modificando según iba fallando.

    Ya te contare xD

  3. Pues creo que sí que lo saqué, pero haciendo pruebas cerraron el servicio, así que no pude terminarmelo de probar. Me hice yo un servicio que emulaba al suyo para terminar el script y me funcionaba, pero ya no pillé la clave, claro 😛

    El lunes tengo programado un post para publicarlo.

Deja un comentario