Existe uma maneira de executar um binário nativo a partir de um pipe?

12
echo 'main(){}' | gcc -xc - -o /dev/stdout | ???

Existe uma maneira de executar o binário de saída em um sistema semelhante ao Unix?

EDIT: Eu precisava executar a saída do g ++ em um ambiente de área restrita onde não posso gravar nenhum arquivo (nada de malicioso, prometo).

Alex B
fonte
Eu acredito que os mecanismos básicos de segurança devem impedir isso. Mas se sua intenção é executar o código C em tempo real, basta usar csh.
rozcietrzewiacz

Respostas:

9

Não acredito que isso seja possível. A chamada do sistema exec (2) sempre exige um nome de arquivo ou caminho absoluto (o nome do arquivo é sempre a char*). posix_spawntambém possui requisitos semelhantes para um nome de arquivo.

O mais próximo que você pode fazer é canalizar a saída para um canal nomeado e tentar executar a partir do canal. Isso pode funcionar, embora o shell possa se recusar a executar qualquer arquivo que não tenha os --x--x--xbits definidos. Crie o pipe mkfifo(1)e veja se consegue fazê-lo funcionar.

Outra abordagem seria escrever algo que leia a entrada padrão, grave um arquivo em uma área temporária, defina os bits --x nele, garfos e execs e exclua o arquivo. O inode e o conteúdo permanecerão até que o programa termine a execução, mas não será acessível pelo sistema de arquivos. Quando o processo termina, o inode será liberado e o armazenamento retornará à lista gratuita.

EDIT: Como aponta Mat, a primeira abordagem não funcionará, pois o carregador tentará exigir a página no executável, o que gerará tráfego de busca aleatória no arquivo, e isso não é possível em um canal. Isso deixa algum tipo de abordagem como a segunda.

ConcernedOfTunbridgeWells
fonte
2
Eu ficaria realmente surpreso se o truque do pipe funcionasse - você não pode fazer buscas aleatórias em um pipe e não pode mapeá-las - tenho certeza de que isso vai irritar o carregador / vinculador de tempo de execução :) Sua segunda sugestão parece boa no entanto, não é possível criar nada sem um arquivo temporário.
Mat
@ Mat - Acho que você está certo. A paginação por demanda no executável causaria tráfego de acesso aleatório que não funcionará no canal. Ironicamente, ele pode ter realmente funcionado no SVR2.0 (a última versão que não utilizava paginação por demanda) - Apenas para mostrar a minha idade, eu costumava ter um AT&T 3B2 / 400 uma vez com o SVR2.0 como O / S.
ConcernedOfTunbridgeWells
Pensando um pouco mais, tenho certeza de que exe packers como UPX podem fazer a descompactação e execução em mídia somente leitura. Modifique o stub que eles colam nos executáveis ​​compactados para ler de um pipe em vez de descompactar, e ... pode funcionar.
Mat
Os empacotadores @ Mat já têm uma imagem carregada e não iniciam um novo processo. Para fazer o mesmo, preciso ter um dos processos para fazer um salto arbitrário nos dados de entrada (o que seria considerado uma vulnerabilidade de segurança).
Alex B
@ Alex B: Você está perguntando especificamente como fazer um salto arbitrário nos dados de entrada. Por que você iria reclamar quando é sugerido que você faça exatamente isso? O objetivo do sandbox é especificamente impedir o que você está tentando fazer?
David Schwartz
7

Uma solução usando o memfd syscall: https://github.com/abbat/elfexec

Ele cria um descritor de arquivo nomeado na memória que pode ser usado no exec. Um pseudocódigo:

#include <linux/memfd.h>
...
int memfd = syscall(SYS_memfd_create, "someName", 0);
...
write(memfd,... elf-content...);
...
fexecve(memfd, argv, environ);
kan
fonte
1
Você não precisa do memfd.hcabeçalho, a menos que queira usá-lo MFD_CLOEXEC(o que interromperá os #! /bin/shscripts por causa de erros no linux ' fexecve()). Isso não é muito complicado, você pode incluir uma amostra de trabalho de 20 linhas em sua resposta (por exemplo, este git gist - embora isso não seja um substituto para o seu elfexec, pois isso também permitirá que você especifique argv[0]e executará um binário apenas a partir de um tubo (UUoC mandato ;-))
mosvy
Portanto, não entendo como isso deve funcionar. O gcc gravará .oarquivos em / tmp e morrerá, se não puder.
Joshua
4

Você pode tentar o tcc , que irá compilar e executar um programa em uma etapa, sem gravar nenhum arquivo intermediário. Não é o gcc, o que pode ser um problema para você, mas é espetacularmente rápido; portanto, pode até apostar melhor que o gcc para seus propósitos.

TheQUUX
fonte
2

Isso executará automaticamente a compilação do seu código, mas criará um arquivo (temparaily) no sistema de arquivos para fazê-lo.

echo 'main(){}' | gcc -xc -o /tmp/a.out && chmod u+x /tmp/a.out && /tmp/a.out && rm -f /tmp/a.out

No momento, estou testando isso agora, mas tenho certeza disso ou algo próximo funcionará para você

EDIT: Se o objetivo da sua tubulação é cortar discos físicos da equação de velocidade, considere criar um disco ram para armazenar o arquivo intermediário.

dtyler
fonte
Isso funcionará, é claro, mas erra o ponto principal da questão - executar um código binário que nunca é gravado no disco.
rozcietrzewiacz
@rozcietrzewiacz Minha esperança era que seria útil se o objetivo fosse executar facilmente um trecho de código rapidamente, sem precisar lidar com o arquivo físico necessário.
dtyler
Sim, eu entendo. Mas, para isso, pode-se simplesmente usar csh.
rozcietrzewiacz