Bate-papo geográfico, bots nocivos e esteganografia: enriquecendo o conhecimento sobre o telegrama


O que você sabe sobre telegramas no telegrama? E você pode distinguir a esteganografia no VideoNote (popularmente - round-robin)? Analisamos a mesma tarefa NeoQUEST-2020 , que causou mais perguntas e exclamações ao nosso apoio! Spoiler: sim, e também haverá um pouco de criptografia aqui :)

Na lenda do NeoQUEST-2020 , encontramos um link para o perfil de um robô viajante no Instagram. Nada fora do comum, certo? Por isso, decidimos também, mas ainda precisamos resolver o problema, por isso consideramos cuidadosamente todas as fotos no perfil e procuramos pelo menos algumas pistas. Um pouco de meditação sobre uma bela foto do Lago Baikal, e chegamos à conclusão de que a pista está no último post:


Graças à imagem, entendemos que precisamos conectar de alguma forma Baikal (Shaman Rock) e Telegram ("Você pode se juntar ao meu ..." - isso não lembra nada?). Inicialmente, decidimos não dar aos participantes uma dica direta do geo-chat (que é exatamente isso!). E muitos deles lidaram com êxito com a tarefa usando um emulador ou um dispositivo móvel com a capacidade de alterar a localização geográfica. Shamanim Definimos as coordenadas (53.20074, 107.349426) (você pode ver) na área da rocha Shamanka e preparamos a coisa mais difícil - a espera. O telegrama trabalha estranhamente com o posicionamento geográfico e puxa contatos e bate-papos relevantes por uma hora. Por nossa diligência e paciência, somos totalmente recompensados ​​- o bate-papo desejado aparece na seção Contatos -> Encontrar pessoas próximas -> Grupos próximos.


Voila, estamos no negócio!


O bot nos encontra com uma tarefa na forma de um arquivo some.bytes com conteúdo não identificado, no qual podemos ler as linhas "Decrypt me" e "Apocalypse Spares Nobody Ninguém".

Entendemos a primeira linha sem problemas, mas o que a segunda significa? ... Aqui, os participantes se dividiram em dois campos: alguns nos escreveram por correio, porque chegaram a um beco sem saída, enquanto outros analisaram cuidadosamente a frase "Apocalypse Spares Nobody Ninguém" e discerniram que ? Direita! Bom e velho formato ASN.1 ( aqui já escrevemos sobre como analisá-lo).


Vamos acertar. No interior são 2 estruturas. Em um deles, encontramos um conjunto de bytes marcados como "Decifrar-me", do qual assumimos que este é um texto cifrado. Na segunda estrutura, vemos dois números. É improvável que essa seja uma chave apresentada generosamente pelo participante, juntamente com o texto cifrado, o que significa mais provável. Estamos lidando com uma chave pública. Todas as informações coletadas nos levam à conclusão óbvia - por que não tentar a RSA ?

Então, nós temos um módulo e um indicador aberto, que, a propósito, é bastante grande. Após um estudo convulsivo da RSA para refletirmos, concluímos que o indicador fechado é pequeno, o que significa o quê? Bingo! Definitivamente, podemos interpretar bandidos e usar o ataque de Wiener .

Nós pensamos nisso até para quem não gosta de criptografia - você pode usar uma versão pronta do ataque, por exemplo, isso .

Em seguida, obter o valor de uma figura fechada d=40553818206320299896275948250950248823966726834704013657854904761789429403771e descriptografar texto cifrado: key=nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u;pass=passCxws3jzYhp0HD5Fy84.

Obtemos a chave "nq2020faAeFeGUCBjYf7UDrH9FapFCdFPa4u" na primeira parte do trabalho e a senha "passCxws3jzYhp0HD5Fy84", que você precisa alimentar o representante do bot. Pode ser encontrado entre os participantes do bate-papo sob o nome @neoquestbot.

Estando na onda de positivo ao receber a primeira chave, não percebemos imediatamente que o bot é exigente na comunicação e o tempo todo diz que não vê o interlocutor:


Mas o bot recebe felizmente as mensagens redondas do VideoNote e até as responde ... da mesma forma redonda:


Parece que o vídeo e o som são os mesmos, mas isso é apenas à primeira vista. E se o nosso bot nos der alguns sinais secretos? Para descobrir, salvaremos e compararemos o vídeo original com a resposta do bot. Para isso e para os próximos passos, o pacote FFmpeg é ótimo para nós . Então, vamos ver o que há lá:



Formato aac -> flac, frequência 44100 Hz -> 98000 Hz. Eles descobriram que continuamos a trabalhar mais com o áudio.

Com um movimento hábil das mãos, retiramos do vídeo:


O mesmo pode ser feito com a nossa mensagem original, para que possamos compará-las mais tarde. Para maior clareza, abra as duas faixas no Audacity .


O salto em amplitude na resposta de áudio do bot chama sua atenção imediatamente (especialmente estranho se estivéssemos em silêncio). Em uma inspeção mais detalhada, notamos os limites claros dos intervalos durante a alternância do "silêncio das ondas":


Sugerimos colocar todas as coisas de lado e contar um pouco. Analisamos por fragmentos:

0 - 0,005 - silêncio
0,005 - 0,01 - onda
0, 01 - 0,0225 - silêncio
0,025 - 0,04 - onda
0,04 - 0,045 - silêncio

O menor intervalo é 0,005 e todos os outros intervalos são múltiplos de 0,005.
Tomamos a presença de uma onda de 0,005 para 1 e o silêncio para 0. Não obtemos nada além de um código binário!
Lembramos que a frequência mudou e tentamos observar o gráfico de espectro (Analysis -> Spectrum graph):


Vemos que o sinal mais poderoso está na frequência de ~ 44100 Hz, que é o ultrassom.
Então, você deve trabalhar apenas com altas frequências.

De fato, o bot sobrepõe seu sinal ao áudio original no espectro audível. E os participantes que tinham som no vídeo original perceberam isso no Audacity.

Cortamos as altas frequências com um filtro passa-alto no Audacity ou no mesmo ffmpeg:


Portanto, temos um arquivo wav mono de 16 bits. Consiste em um cabeçalho, fluxo de áudio não compactado e metadados. O fluxo de áudio em si é dividido em quadros (e os quadros podem armazenar várias amostras, mas essa é uma história completamente diferente), no nosso caso, 16 bits cada (isso é indicado pelas letras pcm_s16 nas capturas de tela). Quadros são sequências de bits que descrevem a amplitude da onda de cada vez para um ou mais canais (no nosso caso, para um). A frequência de amostragem do fluxo de áudio é de 98000 quadros (ou seja, 98000 quadros por um segundo), 490 quadros por 0,005 segundo de intervalo.

Portanto, ainda trabalhamos de acordo com um algoritmo simples: lemos 490 quadros, determinamos se é uma onda ou silêncio e, dependendo disso, definimos o bit como 0 ou 1. Usaremos

python e um pacotewave para analisar arquivos wav.
Se o erro "wave.Error: unknown format: 65534" ocorrer ao abrir o arquivo, substitua "wFormatTag" no cabeçalho de 'FE FF' para '01 00 ':

fh = open(input_file, "r+b")
fh.seek(20)
fh.write(b'\x01\x00')
fh.close()

Então, abra o arquivo, processe 490 quadros e calcule o valor médio:

file = wave.open(input_file,"r")
    for i in range (1, int(file.getnframes()/490)+1):
        frames = file.readframes(490)
        bit = 0
        sum = 0
        for k in range(0, 246):
            frame_bytes = frames[k*2:k*2+2]
            sum += int.from_bytes(frame_bytes, "big")
        if sum/490 > 16000:
            bit = 1
        bits.append(bit)

É possível que, onde houver silêncio (compare com a imagem no Audacity), o ruído permaneça. Portanto, definimos o limite (que seja 16000), excedendo o que consideramos o sinal como 1.

Em seguida, agrupamos os bits em bytes:

bytes = []    
for i in range (1, int(len(bits)/8)+1):
        b1 = bits[i*8-8]
        b2 = bits[i*8-7]
        b3 = bits[i*8-6]
        b4 = bits[i*8-5]
        b5 = bits[i*8-4]
        b6 = bits[i*8-3]
        b7 = bits[i*8-2]
        b8 = bits[i*8-1]
        byte = (b1 << 7) | (b2 << 6) | (b3 << 5) | (b4 << 4) | (b5 << 3) | (b6 << 2) | (b7 << 1) | b8
        bytes.append(byte.to_bytes(1, byteorder='big')) 

Se tudo for feito corretamente, o resultado será a sequência "Fornecer a senha". Como o bot se comunica em círculos usando a esteganografia, será lógico inserir uma senha (e a recebemos juntamente com a chave como resultado da descriptografia) no mesmo formato.

Para começar, componha uma faixa de áudio com uma senha. Para isso, usamos os dados obtidos durante a análise da mensagem do bot: frequência de amostragem 98000 Hz; a duração do sinal que descreve cada bit é de 5 ms; a frequência do sinal correspondente ao valor do bit "1" - como vimos nos gráficos, 44100 Hz.

Agora precisamos "gerar" silêncio. Fazemos isso neutralizando:

sample_rate = 98000.0
def generate_silence(duration_milliseconds=5):
    fragment = []
    num_samples = duration_milliseconds * (sample_rate / 1000.0)
    for x in range(int(num_samples)): 
        fragment.append(0.0)
    return fragment

Para gerar som, usaremos uma onda senoidal (as informações podem ser lidas aqui ):

def generate_sinewave(
        freq=41000.0, 
        duration_milliseconds=5, 
        volume=0.5):
    fragment = []
    amplitude = volume * 32767.0
    num_samples = duration_milliseconds * (sample_rate / 1000.0)
    for x in range(int(num_samples)):
        fragment.append(amplitude * math.sin(2 * math.pi * freq * ( x / sample_rate )))
    return fragment

Agora a coisa é pequena: resta converter a senha em bits e depois em som.

Nota: O bot usa a faixa de vídeo de entrada original para sobrepor sua mensagem, como mencionado anteriormente. Portanto, você precisa adicionar alguns bytes zero após a senha para retirar toda a chave do bot, e não apenas seu início (o comprimento da chave era 36 bytes).

Geração de som
    audio = []
    f = open(input_file, 'rb')
    for character in f.read():
        a = character
        b8 = a & 0b00000001 
        b7 = (a & 0b00000010) >> 1 
        b6 = (a & 0b00000100) >> 2
        b5 = (a & 0b00001000) >> 3
        b4 = (a & 0b00010000) >> 4
        b3 = (a & 0b00100000) >> 5
        b2 = (a & 0b01000000) >> 6
        b1 = (a & 0b10000000) >> 7
        if b1 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b2 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b3 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b4 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b5 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b6 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b7 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()
        if b8 == 1:
            audio += generate_sinewave()
        else:
            audio += generate_silence()


Agora vamos criar o arquivo wave finalizado:

wav_file=wave.open(file_name,"w")
    nchannels = 1
    sampwidth = 2
    nframes = len(audio)
    comptype = "NONE"
    compname = "not compressed"
    wav_file.setparams((nchannels, sampwidth, sample_rate, nframes, comptype, compname))
    for sample in audio:
        wav_file.writeframes(struct.pack('h', int(sample)))
    wav_file.close()

Salvamos nossa trilha, por exemplo, em pass.wav. Ao longo do caminho, verificamos com nosso decodificador stego se a senha é reconhecida. Se tudo estiver bem, obteremos um novo vídeo com uma senha do vídeo original my_video.mp4, substituindo a faixa de áudio:


Agora precisamos fazer do VideoNote isso. Você pode tentar procurar os que estão funcionando (alguns dos participantes, por exemplo, encontraram @TelescopyBot), ou pode escrever seu bot usando o TelegramAPI.


Enfim, encaminhe para o nosso bot:


Recebemos uma nova rodada e parabéns (teríamos feito um trabalho assim!), Decodificamos o áudio de acordo com o cenário já elaborado e obtivemos a chave: “nq2020SyOMK7SnnJP1sNlvbTs8zt35vUrrsD”

Bem, não é à toa que a esteganografia é considerada uma das áreas mais difíceis da segurança cibernética - tente adivinhar essas coisas! Mas os participantes do NeoQUEST mostraram excelente destreza e senso de humor durante esta tarefa, por isso estamos abordando nossa sincera (dos parabéns pelo bot) sincera admiração!

All Articles