Безпечний огляд вихідного коду 1: Небезпечна десеріалізація + Джерело та Місце призначення

Огляд коду для забезпечення безпеки є критичним процесом для виявлення та пом'якшення вразливостей у вашому програмному забезпеченні. Поглиблений аналіз коду дозволяє інженерам з безпеки виявити недоліки, які можуть бути непомічені автоматизованими інструментами, тим самим забезпечуючи безпеку та стійкість додатків. У цій статті ми розглянемо, як проводити огляд коду для виявлення будь-яких вразливостей.

Далі в статті ми поглиблено розглянемо одну цікаву вразливість, що називається Небезпечна десеріалізація (Insecure Deserialization), і побачимо, як можна використовувати помилки огляду коду для експлуатації вразливостей небезпечної десеріалізації в різних форматах серіалізації.

Але спочатку треба зрозуміти ці два терміни: Source та Sink.

Source (Джерело): Джерело — це точка, звідки надходить ненадійний або потенційно шкідливий вхід. Це може бути введення користувача, дані з зовнішніх систем чи будь-який інший вхід, що потрапляє в межі програми.

Приклади джерел (Sources):

  • Дані, надані користувачем ($_GET, $_POST, $_COOKIE в PHP).
  • Параметри запиту у веб-запитах.
  • Завантаження файлів.
  • Дані з зовнішніх API або сторонніх сервісів.
  • Вхідні дані з командного рядка чи змінних середовища.

Приклад у PHP:

$username = $_GET['username']; // Джерело: Вхід користувача з рядка запиту URL

Приклад у Python:

search_term = input("Enter search term: ") # Джерело: Вхід, наданий користувачем через консоль

Sink (Приймач): Приймач — це місце, де використовується ненадійні дані, що може призвести до вразливості безпеки, якщо дані не були належним чином очищені або перевірені. Приймачі представляють собою операції або функції, які виконують дії з даними, наприклад, рендеринг виводу, виконання системних команд чи взаємодія з базами даних.

Приклади приймачів (Sinks):

  • SQL запити.
  • Рендеринг HTML.
  • Операції з файлами (запис, видалення або переміщення файлів).
  • Системні команди (через функції exec, system чи subprocess).
  • Функції десеріалізації.
  • Динамічна оцінка коду (eval, exec).

Приклад у PHP:

$query = "SELECT * FROM users WHERE username = '$username'"; // Приймач: Виконання SQL запиту  
$result = $conn->query($query);

Приклад у Python:

os.system(f"grep {search_term} /var/log/syslog") # Приймач: Виконання команди з ненадійними даними

Потік даних від джерела до приймача (Source-to-Sink Flow)

Термін потік від джерела до приймача описує шлях, яким ненадійні дані потрапляють від точки введення (джерела) до потенційно небезпечної операції (приймача). Розуміння цього потоку є критичним для виявлення та пом'якшення вразливостей.

Приклад у PHP:

$username = $_GET['username']; // Джерело: Вхід користувача  
$query = "SELECT * FROM users WHERE username = '$username'"; // Приймач: Виконання SQL запиту  
$result = $conn->query($query);
  • Джерело (Source): $_GET['username']
  • Приймач (Sink): Виконання SQL запиту через $conn->query($query)

Приклад у Python:

search_term = input("Enter search term: ") # Джерело: Вхід, наданий користувачем  
os.system(f"grep {search_term} /var/log/syslog") # Приймач: Виконання команди
  • Джерело (Source): Функція input()
  • Приймач (Sink): Функція os.system()

Як захистити потік від джерела до приймача

  1. Перевірка введених даних: Переконайтесь, що дані з джерел відповідають очікуваним форматам, типам або діапазонам.
  2. Очищення даних: Видаляйте або екрануйте потенційно небезпечні символи до того, як дані потраплять до приймача.
  3. Використання безпечних функцій: Вибирайте безпечні функції (наприклад, підготовлені запити для SQL).
  4. Обмеження обсягу: Мінімізуйте кількість даних, що підконтрольні користувачеві, які передаються до критичних операцій.

Тепер, коли ми розглянули поняття джерела та приймача, давайте детальніше розглянемо кілька прикладів вразливостей у коді:
Десеріалізація в .NET

Вразливість: Небезпечна десеріалізація з використанням BinaryFormatter.

// Уразливий код: Десеріалізація без перевірки  
using System;  
using System.IO;  
using System.Runtime.Serialization.Formatters.Binary;  

public class Program  
{  
 public static void Main(string[] args)  
 {  
 // Прийом вхідних даних від користувача  
 byte[] input = Convert.FromBase64String(args[0]);  

 // Десеріалізація вхідних даних (уразливо)  
 BinaryFormatter formatter = new BinaryFormatter();  
 using (MemoryStream ms = new MemoryStream(input))  
 {  
 var obj = formatter.Deserialize(ms); // Точка для експлуатації  
 Console.WriteLine("Десеріалізований об’єкт: " + obj.ToString());  
 }  
 }  
}

Проблема з BinaryFormatter полягає в його вроджених вразливостях, які роблять його небезпечним при роботі з вхідними даними, контрольованими користувачем.

Ключові проблеми з BinaryFormatter

  1. Відсутність перевірки введених даних:
  • BinaryFormatter десеріалізує будь-які дані, які йому надаються, без перевірки типу або вмісту.
  • При обробці вхідних даних, контрольованих користувачем, шкідливі дані можуть використати цю відсутність перевірки для виклику непередбачених поведінок.

2. Виконання довільного коду:

  • BinaryFormatter може викликати конструктори, методи або інший код під час процесу десеріалізації.
  • Зловмисники можуть створювати серіалізовані дані для виконання довільних команд або маніпулювання станом додатку.

3. Сплутування типів:

  • Десеріалізація дозволяє створювати об’єкти довільних типів, що призводить до вразливостей сплутування типів.
  • Наприклад, зловмисники можуть вставляти непередбачувані типи об’єктів в логіку програми.

4. Широка площа для атак:

  • BinaryFormatter у .NET підтримує складні типи та об’єкти, що розширює площу для атак.
  • Багато класів у .NET Framework можуть бути використані для атаки, якщо десеріалізувати їх неправильно.

5. "Гаджети" для десеріалізації:

  • Зловмисники можуть використовувати "ланцюги гаджетів" (експлуатовані класи, що існують у бібліотеках .NET) для виконання шкідливих дій під час десеріалізації.
  • Інструменти на кшталт ysoserial.net генерують корисне навантаження, яке експлуатує ці гаджети для виконання довільного коду.

Експлуатація

Створіть корисне навантаження за допомогою ysoserial.net, орієнтуючись на BinaryFormatter.

ysoserial -f BinaryFormatter -g ObjectDataProvider -c "calc.exe" > payload.bin

Це корисне навантаження виконує calc.exe на машині жертви після десеріалізації.

Вплив

  • Виконання довільного коду на сервері.

Виправлення

  • Уникайте використання BinaryFormatter для даних, контрольованих користувачем. Використовуйте безпечніші альтернативи, такі як System.Text.Json, яка має строгий механізм перевірки схеми.
  • Реалізуйте перевірку введених даних і автентифікацію перед обробкою даних.

Приклад безпечного коду

// Безпечний код: Використовуйте безпечні методи серіалізації  
using System;  
using System.Text.Json;  

public class Program  
{  
 public static void Main(string[] args)  
 {  
 try  
 {  
 // Десеріалізація з використанням Json з перевіркою  
 var obj = JsonSerializer.Deserialize

**Дозволяє десеріалізацію довільних типів**:

- На відміну від `DataContractSerializer`, який серіалізує лише явно визначені типи, `NetDataContractSerializer` включає повну інформацію про тип у серіалізовані дані.
- Це означає, що процес десеріалізації не обмежує, які типи об'єктів можуть бути відновлені з серіалізованих даних.

**2. Дає змогу атаки через сплутування типів**:

- Зловмисники можуть створити корисне навантаження з шкідливими типами або об'єктами, щоб експлуатувати вразливості в програмі або її залежностях.
- Якщо ці типи містять небезпечну поведінку (наприклад, виконання команд, зміна даних або виклик винятків), програма може бути скомпрометована.

**3. Довіряє інформації про тип у корисному навантаженні**:

- Десеріалізатор припускає, що інформація про тип у корисному навантаженні є легітимною.
- Це довір'я може бути використано для десеріалізації об'єктів у непередбачувані або шкідливі типи, що дозволяє віддалене виконання коду (RCE) або зміну даних.

## Експлуатація

Зловмисник створює шкідливе корисне навантаження з довільним типом, наприклад, за допомогою інструментів, таких як `ysoserial.net`. Це корисне навантаження може виконувати шкідливі дії, наприклад, викликати системні команди.

1. **Створення корисного навантаження**: Створіть корисне навантаження за допомогою `ysoserial.net` або вручну, використовуючи ланцюг гаджетів для десеріалізації:

ysoserial -f NetDataContractSerializer -g ObjectDataProvider -c "calc.exe" > payload.bin
```

2. Надсилання корисного навантаження: Перетворіть корисне навантаження на Base64 і надішліть його у вразливий метод:

string base64Payload = Convert.ToBase64String(File.ReadAllBytes("payload.bin")); NetDataContractExample.DeserializePayload(base64Payload);

3. Результат: Процес десеріалізації виконує корисне навантаження, наприклад, запускає calc.exe або виконує команди, вказані зловмисником.

Вплив

  • Виконання довільного коду за допомогою гаджетів десеріалізації.

Виправлення

  1. Уникайте використання NetDataContractSerializer:
  • Замість нього використовуйте безпечніші альтернативи, такі як DataContractSerializer або System.Text.Json.

2. Обмежте дозволені типи:

  • Якщо використання NetDataContractSerializer є необхідним, явно обмежте десеріалізацію лише до відомих безпечних типів.
NetDataContractSerializer serializer = new NetDataContractSerializer(); serializer.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;

3. Перевірка введених даних:

  • Санітуйте та перевіряйте всі серіалізовані дані перед десеріалізацією.

4. Використовуйте підписані корисні навантаження:

  • Вимагайте, щоб корисні навантаження були підписані та перевірені криптографічно для забезпечення їх цілісності та автентичності.

5. Увімкніть безпечну конфігурацію:

  • Якщо можливо, вимкніть інформацію про типи в серіалізованих корисних навантаженнях.

Приклад безпечного коду

Використання DataContractSerializer для обробки серіалізації з явними визначеннями типів:

using System;  
using System.IO;  
using System.Runtime.Serialization;  
[DataContract]  
public class SafeObject  
{  
 [DataMember]  
 public string Data { get; set; }  
}  
public class SecureDeserializationExample  
{  
 public static void DeserializePayloadSecurely(string base64Payload)  
 {  
 byte[] payload = Convert.FromBase64String(base64Payload);  
 DataContractSerializer serializer = new DataContractSerializer(typeof(SafeObject));  
 using (MemoryStream ms = new MemoryStream(payload))  
 {  
 // Безпечна десеріалізація  
 var deserializedObject = (SafeObject)serializer.Deserialize(ms);  
 Console.WriteLine("Десеріалізовані дані: " + deserializedObject.Data);  
 }  
 }  
}

Неправильне оброблення десеріалізації JSON

## Вразливий код:

using System;
using Newtonsoft.Json;

public class JsonDeserialization
{
public static void DeserializeJson(string jsonInput)
{
// Десеріалізація JSON без перевірки введених даних
var obj = JsonConvert.DeserializeObject(jsonInput);
Console.WriteLine("Десеріалізований об'єкт: " + obj.ToString());
}
}
```

Експлуатація:

  • Експлуатація динамічного JSON для впровадження непередбачених структур:
{  
 "$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",  
 "MethodName": "Start",  
 "ObjectInstance": {  
 "$type": "System.Diagnostics.Process, System",  
 "StartInfo": {  
 "FileName": "calc.exe"  
 }  
 }  
}

Вплив:

  • Виконання довільних команд за допомогою розв'язування типів.

Виправлення:

  • Вимкніть вказівки типів, налаштувавши серіалізатор
JsonSerializerSettings settings = new JsonSerializerSettings  
{  
 TypeNameHandling = TypeNameHandling.None  
};  

var obj = JsonConvert.DeserializeObject(jsonInput, settings);

4. Експлуатація інтерфейсу IFormatter

Вразливий код:

using System;  
using System.IO;  
using System.Runtime.Serialization;  

public class IFormatterExample  
{  
 public static void DeserializeInput(string base64Input)  
 {  
 byte[] inputBytes = Convert.FromBase64String(base64Input);  
 IFormatter formatter = new BinaryFormatter();  

 using (MemoryStream stream = new MemoryStream(inputBytes))  
 {  
 // Вразлива десеріалізація  
 var obj = formatter.Deserialize(stream);  
 Console.WriteLine("Десеріалізований об'єкт: " + obj.ToString());  
 }  
 }  
}

Експлуатація:

  • Зловмисник використовує реалізацію BinaryFormatter для виконання ланцюгів гаджетів.

Вплив:

  • Той самий, що і в першому прикладі (довільне виконання коду).

Виправлення:

  • Уникайте використання інтерфейсу IFormatter з ненадійними даними.

Кращі практики для мінімізації вразливостей десеріалізації

  1. Уникайте небезпечних серіалізаторів:
  • Уникайте використання BinaryFormatter, NetDataContractSerializer та подібних серіалізаторів для введених користувачем даних.

2. Використовуйте альтернативи:

  • Використовуйте System.Text.Json, DataContractSerializer або XmlSerializer з явними схемами.

3. Забезпечте перевірку типів:

  • Обмежте дозволені типи під час десеріалізації, щоб запобігти ланцюгам гаджетів.

4. Реалізуйте перевірку введених даних:

  • Санітуйте та перевіряйте всі введені дані перед обробкою.

5. Аудит і забезпечення безпеки залежностей:

  • Перевіряйте сторонні бібліотеки на наявність потенційних вразливостей десеріалізації.

Cybersecurity #ApplicationSecurity #OWASP #SecureCoding #DevSecOps #WebSecurity #MobileSecurity #SourceCodeReview

Перекладено з: Secure Source Code Review 1 : Insecure Deserialisation + Source & Sink

Leave a Reply

Your email address will not be published. Required fields are marked *