Я студент, веду проект, идея которого в следующем: на c++ пишу маленький "сниффер", который следит за трафиком на своей машине, делает предварительный анализ и отдаёт отчёт серверу. Планируется поставить его на машины в аудиториях и отслеживать подозрительную активность с помощью сервера.
Немного подробностей (или воды):
Одной из предварительных проверок я решил сделать анализ на сканирование портов. Для этого я передаю лог, в котором записи имеют следующий вид:
#pragma once
#include <string>
struct PacketInfo {
std::string srcIp;
std::string dstIp;
std::string protocol;
int srcPort;
int dstPort;
int payloadLen;
int count;
bool operator==(const PacketInfo& other) const;
bool operator!=(const PacketInfo& other) const;
};
bool PacketInfo::operator==(const PacketInfo& other) const {
return srcIp == other.srcIp && dstIp == other.dstIp
&& srcPort == other.srcPort && dstPort == other.dstPort
&& protocol == other.protocol;
}
bool PacketInfo::operator!=(const PacketInfo& other) const {
return !(*this == other);
}
Как понятно по коду, в логе 10 пакетов с одинаковыми сокетами и одинаковым направлением займут одну запись со счётчиком 10. (если были ответы - будут 2 записи со счётчиками 10).
исходя из этих данных я хочу отследить: были ли какие-то попытки просканировать порты. логично, что нужно смотреть многократные попытки по одним и тем же сокетам но с разными dstPort. Поэтмоу мой анализатор выглядит так (кстати, рекомендации по рефакторингу кода приветсвуются):
#pragma once
#include "IAnalyzer.hpp"
#include "PacketInfo.h"
#include <string>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <vector>
struct ScanResult {
std::string srcIp;
std::string dstIp;
int uniqueDstPorts;
bool suspicious;
};
class PortScanningAnalyzer : public IAnalyzer
{
public:
nlohmann::json analyze(const std::vector<PacketInfo>& packets) override;
private:
std::vector<ScanResult> detectPortScanning(const std::vector<PacketInfo>& packets, int threshold);
};
#include "PortScanningAnalyzer.h"
#include <nlohmann/json.hpp>
nlohmann::json PortScanningAnalyzer::analyze(const std::vector<PacketInfo>& packets) {
nlohmann::json result;
result["rule"] = "portScan";
const int threshold = 100;
std::vector<ScanResult> scanResults = detectPortScanning(packets, threshold);
nlohmann::json counts = nlohmann::json::object();
std::set<std::string> suspiciousIps;
for (const auto& r : scanResults) {
counts[r.srcIp] = counts.contains(r.srcIp) ? counts[r.srcIp].get<int>() + r.uniqueDstPorts : r.uniqueDstPorts;
if (r.suspicious) {
suspiciousIps.insert(r.srcIp);
}
}
result["dst_ports_count"] = counts; // это чисто проверочная инфа, потом она будет удалена или изменена
result["suspicious_ips"] = suspiciousIps;
result["threshold"] = threshold;
return result;
}
std::vector<ScanResult> PortScanningAnalyzer::detectPortScanning(const std::vector<PacketInfo>& packets, int threshold) {
std::unordered_map<std::string, std::unordered_map<std::string, std::set<int>>> portsMap;
for (const auto& p : packets) {
if (p.protocol == "TCP" || p.protocol == "UDP") {
portsMap[p.srcIp][p.dstIp].insert(p.dstPort);
}
}
std::vector<ScanResult> results;
for (const auto& [srcIp, dstMap] : portsMap) {
for (const auto& [dstIp, portSet] : dstMap) {
ScanResult r;
r.srcIp = srcIp;
r.dstIp = dstIp;
r.uniqueDstPorts = static_cast<int>(portSet.size());
r.suspicious = r.uniqueDstPorts > threshold;
results.push_back(r);
}
}
return results;
}
А теперь к проблеме.
А вот теперь к проблеме:
я несколько раз пробовал просканировать порты разными способами, в том числе прямо с той же машины, где была эта программа. Даже интереса ради пробовал сканировать из этой машины другую, что (по идее) тоже должно было обнаружиться. И да, так и было, но далеко не все попытки сканирования были замечены. (я пользовался какой-то прогой на телефоне рандомной, она комп просканировала, но моя программа этого не заметила и действительно все счётчики уникальных портов с каждого ip не превышали 4-10 запросов). А когда я пробовал просканить со своей машины порты на другом компе, то там вообще счётчик уникальных портов остановился на ~100 (якобы я отправил не 1000 запросов а 100)
Суть вопроса:
1. Есть ли замечания по нынешнему алгоритму?
2. Какие ещё признаки существуют? например, когда я скормил отчёт чату gpt вместе с логами он упомянул широковещательные пакеты, но так размыто, что я ничего не понял.
P.s.: Для понимания: я не безопасник и не сетевик, я учусь на программной инженерии, где и то и дургое рассматривается поверхностно, но кое-что по сетям понимаю (стараюсь). Поэтому просьба пояснить более-менее доступно, без излишних слэнговых извращений.