it-swarm-pt.tech

stdin não espera pela próxima entrada após a cadeia ROP

Eu estava tentando explorar uma vulnerabilidade em um ctf, mas não consigo fazer fgets () reabrir stdin para colocar minha cadeia ROP de segundo estágio. Estou usando o pwntool, mas o problema está mais voltado para o soquete.

Recriei a situação acima. Em um programa de teste. O conceito é simples. Depois de transbordar o rasgo, volto ao principal para dar/alterar a entrada. O problema surge quando pwntools/bash etc fecha o segundo stdin.

Localmente, posso superar o problema fazendo algo como:

cat <(cat myinput.txt) - | ./test.txt

Mas como posso fazer isso em um soquete? Usar pwntools.interactive resolve o problema, mas quero enviar caracteres não imprimíveis. Aqui estão alguns exemplos de capturas de tela do meu programa ./test

int main (){
    char mastr[150];
    printf("lol\n");

    fflush(stdout);
    fflush(stdin);
    fgets(&mastr,600,stdin);
    puts(mastr);

    return 0;
}

Com pwntools.interactive () enter image description here

Sem pwntools.interactive () enter image description here

Qual é a teoria por trás dos soquetes e stdin? Eu realmente não sei os termos corretos para pesquisá-lo.

2
ItsYou

Uma observação sobre fgets(): Isso é o que observei ao percorrer o depurador. Quando os dados canalizados são enviados para fgets(), são armazenados em buffer no heap e, em seguida, são transferidos para a pilha um por um até

  1. Uma instrução de comparação retorna true em um contador e o valor que é passado no registro RSI para a função fgets(); que é o comprimento da string especificado pelo programador.

  2. Ou recebimento de um byte 0x0a ("\ n")

Assim que qualquer um deles for atendido, a função será encerrada e STDIN será interrompido. Portanto, é E/S armazenada em buffer e não E/S real para um descritor de arquivo usado para se comunicar com o sistema operacional.

De qualquer forma, para gerenciar stdin como você está fazendo com bash no Python, você pode usar a função Popen do Subprocess para se conectar diretamente ao serviço, mas há um problema. Python bloqueia SIGPIPE por padrão, o que evita manter o fd aberto para gravação. Você pode superar isso usando o módulo de sinais e criando uma função de patch que substitui a configuração de Subprocessos para permitir SIGPIP:

sinais de importação 
 def restore_signals (): 
 sinais = ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ') 
 para sinais de assinatura: 
 se hasattr ( sinal, sig): 
 sinal.sinal (getattr (sinal, sig), sinal.SIG_DFL)

Em seguida, chame seu programa diretamente (sem piping) por meio de um shell de subprocesso, passando o patch SIGPIP por meio de preexec_fn Arg. Também especifiquei o bash como o Shell que gostaria de usar, mas não é obrigatório:

myproc = Popen ("./ test", 
 stdin = PIPE, 
 stdout = PIPE, 
 Shell = True, 
 executável = '/ bin/bash' , 
 preexec_fn = ** restore_signals () **)

Isso manterá um cano aberto e você apenas terá que fechá-lo manualmente. Presumo que, do ponto de vista do desenvolvedor de exploit, isso realmente não importará para você.

Para enviar dados, não use o método .communicate(). Você terá que gerenciar manualmente a conexão enviando seus bytes de código do shell da seguinte forma:

#preparar o buffer de recebimento limpando-o primeiro 
 myproc.stdout.flush ()
#Envie seu shellcode onde shellcode é uma var contendo seu shellcode 
 Myproc.stdin.write (shellcode + "\ n")

Observe o "\ n" para simular enter para que fgets() saiba como encerrar o buffer.

Então, para ler a saída, você provavelmente não vai querer usar o método .readline() a menos que saiba que a saída (como um banner) terminará com um byte de nova linha (0x0a/'\ n'). Você terá que gerenciá-lo manualmente usando o método .read().

O problema com .read() é que você precisa ter alguma noção dos dados que são retornados. Portanto, se você está esperando o retorno de algo como um vazamento de memória ou string e sabe o tamanho, vai querer especificar isso para que seu script não bloqueie enquanto espera por mais dados. Não me lembro qual é o tamanho do buffer padrão na leitura, mas não retornará até que o buffer seja preenchido. .readline() retornará em um byte 0x0a. Portanto, se você está esperando uma string de 10 bytes:

 #sleep 1 segundo após o envio do código do shell para dar tempo ao processo 
 time.sleep (1) 
 output = myproc.stdout.read (10)

Lembre-se de que, se estiver enviando várias linhas, você desejará flush() o buffer padrão a cada vez ou sua resposta lerá 10 bytes dos dados recebidos anteriormente.

Repita conforme necessário. Espero que ajude.

1
cyberjitz