A lógica não tem falha. Mas o que ela imprime — esse você pode mudar.
Terminal seguro. Autenticador. Que sempre nega.
Não tem comparação pra quebrar. Não tem jump pra inverter.
O programa simplesmente imprime ACESSO NEGADO — sempre.
Mas a mensagem existe no binário. E existe outra mensagem logo ao lado.
Use strings -o ./challenge para ver as strings com seus offsets no arquivo.
Procura ACESSO NEGADO. Agora procura ACESSO CONCEDIDO.
O que você precisaria copiar de onde pra onde?
strings -o ./challenge | grep "ACESSO"
4096 >> ACESSO NEGADO. SAIA. 4128 >> ACESSO CONCEDIDO. BEM-VINDO.
binary = bytearray(open('./challenge', 'rb').read()) old = b'>> ACESSO NEGADO. SAIA. \x00' new = b'>> ACESSO CONCEDIDO. BEM-VINDO.\x00' idx = binary.find(old) if idx != -1: binary[idx:idx+len(new)] = new open('./challenge', 'wb').write(binary) print(f"Patcheado em offset 0x{idx:x}")
r2 -w ./challenge iz # lista strings com endereços s 0x402000 # vai pro endereço de MSG_DENIED w >> ACESSO CONCEDIDO. BEM-VINDO. q
strings ./challenge | grep "ACESSO" # deve mostrar CONCEDIDO onde estava NEGADO ./challenge
Identificador: qualquer >> ACESSO CONCEDIDO. BEM-VINDO. FLAG{patch_the_string_not_the_logic}
antes do patch: .rodata offset 0x2000: ">> ACESSO NEGADO. SAIA. \0" ← main imprime isso .rodata offset 0x2020: ">> ACESSO CONCEDIDO. BEM-VINDO.\0" ← nunca executado depois do patch: .rodata offset 0x2000: ">> ACESSO CONCEDIDO. BEM-VINDO.\0" ← main imprime isso .rodata offset 0x2020: ">> ACESSO CONCEDIDO. BEM-VINDO.\0" ← inalterado nenhuma instrução foi alterada.
Por que o tamanho importa: strings em .rodata são contíguas na memória. Se a nova string fosse maior, ela invadiria a próxima string e corromperia o binário. Strings com padding nulo permitem a troca 1:1 segura. Em binários reais, sempre verifique o espaço disponível antes de patchear.
| Método | O que muda | Risco |
|---|---|---|
| sobrescrever bytes | Conteúdo da string em .rodata | Corrupção se tamanho errado |
| redirecionar ponteiro | Offset no lea rdi,[rip+X] | Baixo — string original intacta |
| injetar em padding | Nova string em area vazia do ELF | Médio — depende do layout |
Se MSG_DENIED e MSG_GRANTED tivessem comprimentos diferentes, qual das abordagens (sobrescrever bytes vs redirecionar ponteiro) ainda funcionaria sem corrupção? Por quê?