e como podemos ver, o resultado gerado pela suposta flag eh diferente da flag cifrada que precisamos gerar, ainda assim os primeiros caracteres sao iguais, ou seja, podemos deduzir que a flag real possui HTB{ como primeiros 5 caracteres, o que nao eh nenhuma surpresa sabendo que esse eh o formato das flags usadas pelo HTB. Porem vamos seguir usando esta entrada em nossos testes a fim de analisar o comportamento e encontrar o algoritmo usado pra criptografar o input
- - - -
Bem, voltando pra funçao, podemos ver o seguinte.
A funçao abre o arquivo data.dat e faz um loop onde itera a
Pra continuar precisamos descobrir o que aquele bloco de operaçoes em
Para isso vamos usar
Uma vez la podemos observar que as operações visam encontrar um numero múltiplo de 6 (eh usado uma multiplicação
e no final da iteraçao substraindo -1 o valor de
- - - -
Bem, voltando pra funçao, podemos ver o seguinte.
A funçao abre o arquivo data.dat e faz um loop onde itera a
*var_4* e com fgetc extrai um caractere do arquivo e faz desvios comparativos entre 0 e 1 e dependendo do resultado armazena na stack o 0 ou 1, com cada comparaçao aumenta +1 dentro da *var_4* (equivalente a um (int i = 0; i <= 216; i++) ) onde i eh a *var_4* e passa a executar um bloco de operaçoes (*0x00001541*) com o valor de *var_4* e dependendo do resultado das operaçoes volta pra o inicio do loop fazendo um salto com JNE ou entra pra um pedaço de instruçoes as quais substrai 1 a *var_4* (i) e armazena o resultado em *var_8* (ou seja *var_8* = *var_4* - 1) e entra em outro loop for usando *var_10* com iterador e armazenando 0 dentro de 2 variaveis novas (*var_c*, *var_10*)Pra continuar precisamos descobrir o que aquele bloco de operaçoes em
*0x00001541* faz e da qual o resultado sera usado em *var_4* pra entrar na iteraçao com *var_10*Para isso vamos usar
GDB (com GEF) pra fazer uma analise dinamica e analisar os resultados em stack, vamos abrir o binario com GDB e vamos colocar um breakpoint no endereço equivalente a 0x00001541 e uma vez no breakpoint vamos analisar a stack, nesse caso a *var_4* no r2 eh equivalente a *rbp-0x4* e vamos colocar outro breakpoint no endereço de test antes de entrar no loop iterador (0x00001565)Uma vez la podemos observar que as operações visam encontrar um numero múltiplo de 6 (eh usado uma multiplicação
imul e 2 operaçoes de deslocamento de bits shr e sar, eh bastante interessante conhecer elas) e uma vez atingindo o resultado de igualdade JNE, entra na iteraçao com *var_8*(*rbp-0x8*) = 5 e *var_10* = 0e no final da iteraçao substraindo -1 o valor de
rbp-0x8 e adicionando +1 no rbp-0x10
(int RBP_0x10 = 0; for RBP_0x10 <= 5; RBP_0x10++)
..
..
RBP_0x8 = RBP_0x8 -1
dentro do loop, usa o valor de *var_10* como argumento a uma chamada a fcn.000013abAqui temos mais uma funçao que eh basicamente um loop com o argumento inserido, neste loop o argumento eh uma potencia de 2, ou seja, podemos representar com um pequeno script em
python
var_8h = 2 #nao confundir com rbp-0x8 da funçao anterior, as duas estao em frames diferentes da stack
for i in range(6):
var_4 = var_8 ** 2
print(var_4)
onde
i eh equivalente a *var_10* = [0, 1, 2, 3, 4, 5] e a cada saida da funçao sera retornado um inteiro equivalente a potencia de 2 [1, 2, 4, 8, 16, 32] durante os 6 loops de *var_10*e ao voltar a funçao basicamente aqui ele pegando a posiçao atual do arquivo e ta substraindo as iteraçoes ) e tira o bit relativo a posiçao pra ser multiplicado pelo retorno da funçao anterior (
- e soh pra esclarecer, faz bastante sentido jah que 216 / 6 = 36 (que como lembramos eh o tamanho da nossa flag criptograda) e 216 bits sao 27 bytes, o tamanho da nossa entrada e como confirmamos antes, o unico tamanho possivel
entao temos que cada valor da potencia de
e dps de terminar o
fcn.000014ba cada resultado eh armazenado na variavel *var_20*, dps passa *var_8* (*var_20*) e ser armazenado na *var_c*. E como no arquivo soh existem 0 e 1, logo cada retorno ou vai adicionar 0 (nada) ou vai adicionar algum elemento relativo ao retorno da funçao anterior - e soh pra esclarecer, faz bastante sentido jah que 216 / 6 = 36 (que como lembramos eh o tamanho da nossa flag criptograda) e 216 bits sao 27 bytes, o tamanho da nossa entrada e como confirmamos antes, o unico tamanho possivel
entao temos que cada valor da potencia de
2 (0..5) = [1, 2, 4, 8, 16, 32] pode ser somado a *var_c* dependendo do que tiver na posiçao (0 nao soma, 1 soma) e dps de terminar o
for com *var_10* eh feita a chamada a funçao fcn.000013e9 com *var_c* como argumento👍1
e
Entao, basicamente o que precisamos pra gerar a string criptografa eh passar no argumento o valor equivalente a posiçao no vetor da letra na string *lag.enc
podemos fazer um pequeno script com
BOOM (agr de vdd), vemos agr o algoritmo de permutação, onde basicamente temos um vetor (começando pelo rbp-0x190 ateh rbp-0x158) e como vemos no endereço 0x000014a1 o argumento eh usado como valor pra procurar no índice do vetor e colocar no terminal com putsEntao, basicamente o que precisamos pra gerar a string criptografa eh passar no argumento o valor equivalente a posiçao no vetor da letra na string *lag.enc
podemos fazer um pequeno script com
python pra nao precisar fazer na mao#!usr/bin/python3
vetor = "RSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQabcdefghijklmnopqrstuvwxyz" # vetor (rbp-0x190 - rpb-0x158)
key = "9W8TLp4k7t0vJW7n3VvMCpWq9WzT3C8pZ9Wz" # flag criptograda
ii = 0
keyPosition = [] # lista onde vamos guardar a posiçao das letras da flag criptografada no vetor
for iz in key:
for i in vetor:
ii = ii + 1
if i == iz:
keyPosition.append(ii-1)
if ii == 62: # aqui vamos procurar no vetor e usar ii que sera somado +1 como indice pra o vetor em python, caso a letra da chave seja encontrada, armazena ii - 1 na lista e repete ateh o final da key
ii = 0
print(keyPosition)
dps de usar nosso script, descobrimos que os argumentos (
E como fazemos isso??
Sabendo que cada numero que retorna do loop for com
e juntando tdo temos os bits
- - - -
e
*var_c*) que precisamos passar pra funçao fcn.000013e9 sao (18,5,17,2,30,51,13,46,16,55,9,57,28,5,16,49,12,4,57,31,21,51,5,52,18,5,61,2,12,21,17,51,8,18,5,61) para atingir as posiçoes no vetorE como fazemos isso??
Sabendo que cada numero que retorna do loop for com
*var_10* na funçao anterior sera somado a *var_c* dependendo dos valores 0 ou 1, vamos testar isso na pratica, lembrando que cada byte tem 8 bits e a funçao usa 6 elementos [1, 2, 4, 8, 16, 32], entao vamos fazer 4 vezes pra gerar um multiplo de 8 (8 * 3 = 24 || 6 * 4 = 24) e ter uma string que nao esteja quebrada2 + 16 = (18) ||32 16 8 4 2 1
0 1 0 0 1 0
1 + 4 = (5)
0 0 0 1 0 1
1 + 16 = 17
0 1 0 0 1
2 = 2
0 0 0 0 1 0
e juntando tdo temos os bits
01001000010101001000010, convertendo pra ascii obtemos a string HTB- - - -
e
BOOM, conseguimos reverter o algoritmo de criptografia do nosso binario, agr soh basta automatizar e obter nossa string#!/usr/bin/python3
keyPosition = [18, 5, 17, 2, 30, 51, 13, 46, 16, 55, 9, 57, 28, 5, 16, 49, 12, 4, 57, 31, 21, 51, 5, 52, 18, 5, 61, 2, 12, 21, 17, 51, 8, 18, 5, 61]
var_4 = [32, 16, 8, 4, 2, 1]
keyBin = []
for i in keyPosition:
for ii in var_4:
zz = i > ii
if zz == False:
if i == ii:
keyBin.append('1')
i = i - ii
print(zz, ii, i)
else:
k.append("0")
else:
i = i - ii
keyBin.append('1')
print(zz, ii, i)
print(k)
e bem, assim vamos obter a sequencia de bits que ao converter em ascii, vai nos dar a nossa flag