Google CTF JS1.0


Neste tutorial, apresentarei a vocês minha solução para o desafio JS SAFE 1.0 do GoogleCTF2018 para iniciantes, todos os anos o Google lança uma série de desafios fascinantes em uma variedade de tópicos, como:

  • CRYPTO
  • MISC
  • PWN
  • RE
  • WEB

Tento todos os anos resolver pelo menos um desafio da web – Em segundo lugar, esses são desafios relativamente difíceis em relação a outras competições da CTF das quais participo. Além da competição, desta vez o Google também lançou uma série de desafios para iniciantes.

Os desafios são muito fáceis para quem é novo na área, recomendo fortemente que tente resolvê-los. Em nossos cursos até dou os exercícios como lição de casa e apresento as soluções na aula.

O objetivo do desafio js_safe_1 é entender como o código funciona e encontrar a senha do cofre.

A fim de lhe ensinar o método de pensamento e como resolver esses desafios, gravei o seguinte vídeo para você:

Este desafio começa quando você recebe o arquivo js_safe_1 Para baixar o arquivo

Para encontrar a função que lida com a entrada de dados no campo de entrada, clique em inspecionar e vá para a guia Event Listeners:

Agora clique na função e ela o levará diretamente para a guia source:

Procure a função open_safe em um arquivo parecido com este:

Neste código temos uma expressão regular que se não existir ou que se a função x devolver False o cofre imprime-nos Access Denied caso contrário obteremos Access granted.

A função x é definida acima e contém o seguinte código, iremos dividir o código em várias partes da seguinte maneira:

Detalhes da divisão que fiz:

  1. Nesta seção, vemos uma string Unicode que contém muitos caracteres, se você focar nos caracteres, notará que alguns deles contêm os nomes das funções dentro env.
  2. Esta seção contém a variável env que contém uma série de operações padrão com as quais podemos produzir qualquer função que quisermos durante a execução, as operações são:
  • (x,y) => x[y]
  • Function.constructor.apply.apply(x,y)

`

a = {
    test1: (x,y) => Function.constructor.apply(x, y),
	test2: (x,y) => Function.constructor.apply.apply(x, y)
};

roman_apply = a["test1"](this,[["x","y"],"return x+y"]);
console.log(roman_apply(2,3));

roman_apply_apply = a["test2"](roman_apply,[,[2,3]]);
console.log(roman_apply_apply);

`

  • (x,y) => x+y
  • (x) => String.fromCharCode(x)
  • • Os comandos a seguir contêm os dígitos 0 e 1, esses dígitos são usados pelas funções dinâmicas para criar diferentes números usando operações matemáticas.
  • new TextEncoder().encode(password)Esta ação converte texto em byte array que precisamos para determinadas operações, geralmente operações de criptografia.
  • A última ação h é um valor de retorno que se repetirá em nosso software e deve ser 0, caso contrário obteremos a resposta false.
  1. Nesta parte, vemos o seguinte loop:


Você pode ver que há uma execução no código em saltos de 4 que são divididos em:

  • lhs – O valor do resultado da operação
  • fn – Função qualquer a ser executada
  • arg1 – A primeira variável
  • arg2 – A segunda variável

Além disso, temos uma função de criação, execução e salvamento do valor do resultado, então basicamente este código cria para nós todo o resto da lógica de forma dinâmica. Eu recomendo que você examine o código usando um depurador e veja por si mesmo como as funções são criadas durante a execução.

Claro que vai demorar muito para testar tal exercício em um depurador, então pensei no seguinte “atalho” – usaremos o console para imprimir todas as operações e testar o resultado.

Foi assim que cheguei aos seguintes comandos, adicioneo-os ao seu código e execute-os:

`

console.log(i, env[lhs], env[fn], arg1 + "=" + env[arg1], arg2 + "=" + env[arg2]);
console.log("-".repeat(50));

`

Olhe para o console e você verá as operações sendo realizadas:

Você pode ver primeiro que há uma criação de letras e números para a criação de palavras como a palavra return que será usada por nós nas funções abaixo.

Você pode então ver a função sha-256 que é executada em nossa senha no formato bytes array e o resultado é:

Então, se nós mesmos testarmos usando python:

59 na base hexadecimal é equivalente a 89 na base decimal (O primeiro caractere emconsole) Então nós temos a mesma coisa.

Você pode então ver uma ação XOR entre o sha256 de nossa senha contra sha256 calculado pelo desafio e, em seguida, fazer ou com a variável H que é o nosso valor de retorno

Devemos continuar com este método até percorrermos todos os sha256 e chegarmos ao último resultado deixado em h, além do fato de termos que retornar h com o valor 0, podemos concluir que a única maneira de conseguir isso é realizando as operações matemáticas no mesmo h-hash.

Para chegar ao hash, pegaremos todos os caracteres que comparamos com eles usando o seguinte código:
`

let output = []
let cursor = 964;

if ( i == cursor )
{
	cursor += 20
	output.push(env[arg2]);
}

`

E vamos receber:

Se pegarmos os primeiros 32 caracteres, que é basicamente a quantidade de caracteres em sha256:

`

output = [ "%.2x" % i for i in [230, 104, 96, 84, 111, 24,205, 187, 205, 134, 179, 94, 24, 181, 37, 191, 252, 103, 247, 114, 198, 80, 206, 223, 227, 255, 122, 0, 38, 250, 29, 238]]
"".join(output)

`

Vamos colocar em nosso código Python e obter: e66860546f18cdbbcd86b35e18b525bffc67f772c650cedfe3ff7a0026fa1dee

Usaremos a pista que aparece no início do desafio, e vamos procurar no Google o sha256 que saiu: TODO: check if they can just use Google to get the password once they understand how this works.

E vamos receber:

Vamos inserir a senha, resolvendo assim o desafio:

Outros artigos que você pode ter interesse: