Всем привет. Недавно тов. R_Voland рассказал о своём http ханипоте. Он меня и вдохновил к написанию этого поста. Но в этом случае, будем ловить все tcp и udp сканы, а не только http. Запросы будем ловить с помощью tcpdump.
Для tcp ловим только syn пакеты:
Для udp все входящие udp пакеты
В теории вывод от tcpdump'а можно перенаправить в файл и дальше парсить его по необходимости, но я ещё тот извращенец, поэтому напишем сервис на nodejs, который будет слушать tcpdump и сохранять результаты в mysql базу.
Шапка скрипта со всем необходимым:
lastTcpLine — нужен для временного хранения последней строки полученной из stdout. Т.к мы получаем данные не строка за строкой, а блоками, в которых последняя строка может быть не полной, и её 2я половина прилетит со вторым блоком данных.
excludePorts и excludeAddrs нам нужны для исключения каких-нибудь своих собственных коннектов. На 80 порту у нас будет отдельный расширенный логгер, локалхост тоже слушать не будем.
Вешаем листенер:
IP к нам прилетают в виде address.port, например 192.168.1.1.443, разбираем на адрес и порт:
Сохраняем результат в базу данных:
Код для udp листенера совподает на 100%, не буду повторяться, исходники можно посмотреть на гитхабе: github.com/hololoev/honeypot_tcpdump_logger.git
Теперь нам нужна новая виртуалочка, на которую ставим nginx с заглушкой «Under construction» и наш логгер. НЕ направляем на него никакой домен, всячески изображаем вид свежесозданного сервера, среднестатистического вебмастера. Через пару дней (с 5 по 9 мая) получаем результаты:
Топ 5 tcp портов:
Топ 5 udp портов:
Топ 10 самых активных ip адресов:
Карта с топ 100 самых активных адресов:
Для tcp ловим только syn пакеты:
tcpdump -n "tcp[tcpflags] & (tcp-syn) != 0"
Для udp все входящие udp пакеты
tcpdump -n inbound and udp
В теории вывод от tcpdump'а можно перенаправить в файл и дальше парсить его по необходимости, но я ещё тот извращенец, поэтому напишем сервис на nodejs, который будет слушать tcpdump и сохранять результаты в mysql базу.
Шапка скрипта со всем необходимым:
#!/usr/bin/nodejs
'use strict';
const net = require('net');
const spawn = require('child_process').spawn;
const mysql = require('mysql2');
const config = require('./config');
const connection = mysql.createConnection(config.mysql);
const tcpdump = spawn('tcpdump', ['-n', 'tcp[tcpflags] & (tcp-syn) != 0']);
const excludePorts = [ 80 ];
const excludeAddrs = [
'127.0.0.1',
];
let lastTcpLine = '';
lastTcpLine — нужен для временного хранения последней строки полученной из stdout. Т.к мы получаем данные не строка за строкой, а блоками, в которых последняя строка может быть не полной, и её 2я половина прилетит со вторым блоком данных.
excludePorts и excludeAddrs нам нужны для исключения каких-нибудь своих собственных коннектов. На 80 порту у нас будет отдельный расширенный логгер, локалхост тоже слушать не будем.
Вешаем листенер:
tcpdump.stdout.on('data', (data) => {
let lines = `${data}`.split('\n'); // разбиваем блок данных на строки
// далее идёт немного логики которая узнаёт нужно или нет дополнять первую строку сохранённым в lastTcpLine куском, и нужно ли сохранять новый фрагмент в lastTcpLine
let lastTcpLineNum = lines.length - 1;
let toNum = lines.length - 1;
lines[ 0 ] = lines[ 0 ] + lastTcpLine;
if( lines[ lastTcpLineNum ].indexOf('\n') == -1 ) {
lastTcpLine = lines[ lastTcpLineNum ];
toNum --;
} else
lastTcpLine = '';
for(let i=0; i<=toNum; i++) {
saveLog( parseLine(lines[ i ], 'tcp') ); // парсим и сохраняем всё в бд
}
});
IP к нам прилетают в виде address.port, например 192.168.1.1.443, разбираем на адрес и порт:
function parseLine(line, proto) {
let parts = line.split(' '); // разбиваем строку на запчасти
let dstAddrParts = parseIP(parts[ 4 ]); // тут будет адрес, куда прилетел запрос
let srcAddrParts = parseIP(parts[ 2 ]); // тут откуда
return {
addr: srcAddrParts.addr,
port: dstAddrParts.port,
proto: proto,
req_time: parseInt(new Date() / 1000),
};
}
function parseIP(ipStr) {
let addrParts = ipStr.split('.'); // разбиваем строку на куски, разделённые точкой
let port = addrParts[ addrParts.length - 1 ]; // последний кусок - это порт
let ipOctets = []; // собираем оставшиеся куски в строку
for(let i=0; i<=(addrParts.length-2); i++)
ipOctets.push(addrParts[ i ]);
let addr = ipOctets.join('.');
if( !net.isIP(addr) ) // и, на всякий случай проверяем, что мы разобрали, если что-то не так, отдаём null вместо адреса
addr = null;
return {
addr: addr,
port: parseInt(port)
};
}
Сохраняем результат в базу данных:
function saveLog(info) {
if( excludePorts.indexOf(info.port) > -1 ) // если порт в исключениях - ничего не делаем
return;
if( excludeAddrs.indexOf(info.addr) > -1 ) // если ип в исключениях - ничего не делаем
return;
for(let key in info) // если какое-то из значений не определено - ничего не делаем
if( !info[ key ] ) {
console.log('Bad info:', info);
return;
}
let fields = []; // собираем заголовки столбцов
for(let key in info)
fields.push('`' + key + '`');
let values = []; // и значений
for(let key in info) {
if( typeof(info[ key ]) == 'number' )
values.push(info[ key ]);
else
values.push(`'` + info[ key ] + `'`);
}
let query = 'INSERT INTO access_logs (' + fields.join(',') + ') VALUES(' + values.join(',') + ')'; // и фигачим это всё в запрос
connection.query(query);
}
Код для udp листенера совподает на 100%, не буду повторяться, исходники можно посмотреть на гитхабе: github.com/hololoev/honeypot_tcpdump_logger.git
Теперь нам нужна новая виртуалочка, на которую ставим nginx с заглушкой «Under construction» и наш логгер. НЕ направляем на него никакой домен, всячески изображаем вид свежесозданного сервера, среднестатистического вебмастера. Через пару дней (с 5 по 9 мая) получаем результаты:
Total adresses | tcp Scans | udp Scans | http Scans |
---|---|---|---|
4324 | 38558 | 543 | 101 |
Топ 5 tcp портов:
tcp port | Scans |
---|---|
445 | 2538 |
23 | 1515 |
22 | 1304 |
3306 | 151 |
3389 | 148 |
Топ 5 udp портов:
udp port | Scans |
---|---|
5060 | 95 |
161 | 41 |
1900 | 32 |
123 | 30 |
137 | 23 |
Топ 10 самых активных ip адресов:
Address | Total scans | Location | http | tcp | udp |
---|---|---|---|---|---|
40.115.124.127 | 25822 | IE/Dublin | 0 | 25822 | 0 |
77.72.82.101 | 861 | RU/ | 0 | 861 | 0 |
77.72.82.22 | 760 | RU/ | 0 | 760 | 0 |
145.239.134.1 | 550 | GB/ | 0 | 550 | 0 |
101.128.72.140 | 282 | ID/Jakarta | 0 | 282 | 0 |
77.72.85.25 | 244 | RU/ | 0 | 244 | 0 |
181.214.87.34 | 208 | US/Las Vegas | 0 | 208 | 0 |
5.188.11.91 | 189 | RU/Saint Petersburg | 0 | 189 | 0 |
128.199.141.239 | 173 | SG/Singapore | 0 | 173 | 0 |
5.188.11.79 | 156 | RU/Saint Petersburg | 0 | 156 | 0 |
Карта с топ 100 самых активных адресов:
Alevat
По утверждению 2ip адреса подсети 77.72.82.0/24 розданы бритам
hololoev Автор
Для геолокации использовал www.npmjs.com/package/geoip-local в нём могут быть неполные/неактуальные данные, тк. юзается бесплатна база.