01 Мар, 2023

Условия гонки в многопоточных приложениях

Vulnerability Assessment as a Service (VAaaS)

Tests systems and applications for vulnerabilities to address weaknesses.

Что такое многопоточное приложение?

Многопоточное приложение - это компьютерная программа, которая использует несколько потоков для одновременного выполнения нескольких задач или операций. Поток - это облегченный процесс, который может выполняться независимо от других потоков в рамках того же процесса. Используя несколько потоков, программа может повысить свою производительность, быстродействие и масштабируемость.

В многопоточном приложении каждый поток выполняет отдельную последовательность инструкций, которые могут выполняться одновременно с другими потоками в той же программе. Это позволяет программе выполнять несколько задач или операций одновременно, таких как обработка пользовательского ввода, а также выполнение фоновых задач, таких как файловый ввод / вывод (ввод / вывод) или сетевая связь.

Многопоточность может использоваться в различных приложениях, включая веб-серверы, системы управления базами данных и мультимедийные приложения. Многопоточное приложение может обеспечить более отзывчивый пользовательский интерфейс, поскольку фоновые задачи могут выполняться одновременно, не блокируя поток пользовательского интерфейса.

Что такое уязвимость в условиях гонки?

В кибербезопасности состояние гонки возникает, когда уязвимость системы безопасности возникает из-за времени и порядка событий в системе. Условия гонки могут позволить злоумышленникам получить несанкционированный доступ к ресурсам или информации, обойти средства контроля безопасности или повысить привилегии.

Например, в многопользовательской системе может возникнуть состояние гонки, если два пользователя попытаются одновременно получить доступ к одному и тому же ресурсу. Если система не обеспечивает надлежащего контроля доступа или механизмов блокировки ресурсов, один пользователь может получить доступ к ресурсу раньше другого, что потенциально может поставить под угрозу его конфиденциальность, целостность или доступность.

Другим примером состояния гонки в кибербезопасности является уязвимость от времени проверки до времени использования (TOCTTOU). Это происходит, когда программа проверяет условие или ресурс в один момент времени, но затем использует или полагается на это условие или ресурс в более поздний момент времени. Если злоумышленник может изменить или манипулировать условием или ресурсом в течение прошедшего периода времени, он может воспользоваться уязвимостью и получить несанкционированный доступ или выполнить произвольный код.

Для предотвращения конфликтных ситуаций в области кибербезопасности разработчики и системные администраторы должны внедрять методы безопасного кодирования, такие как надлежащее использование механизмов синхронизации и контроля доступа, а также тщательное тестирование и аудит своих систем для выявления и устранения уязвимостей. Также важно поддерживать программное обеспечение и системы в актуальном состоянии с помощью исправлений и обновлений безопасности для устранения известных уязвимостей.

Что такое нить?

В вычислительной технике поток - это наименьшая единица выполнения, которую программа может запланировать для выполнения. Поток - это последовательность инструкций, которые могут выполняться независимо от других потоков в рамках одной и той же программы. Несколько потоков могут выполняться одновременно в рамках одного процесса, что позволяет программе выполнять несколько задач или операций одновременно.

При одновременном запуске нескольких потоков приложение может выполнять несколько задач одновременно, что может привести к более быстрой и гибкой производительности. Используя несколько потоков, приложение может легче масштабироваться для обработки больших рабочих нагрузок и повышенных требований пользователей. Использование потоков может помочь разбить сложные задачи на более мелкие, более управляемые фрагменты кода, что упрощает написание, тестирование и обслуживание больших и сложных приложений.

Потоки могут обмениваться данными и совместно использовать ресурсы с другими потоками в том же процессе, такие как переменные, память или файлы. Однако это также создает новые проблемы, такие как условия гонки, взаимоблокировки и проблемы с синхронизацией, которые при неправильном обращении могут привести к непредсказуемому и нежелательному поведению.

Чтобы разработать программу, использующую потоки, разработчики должны тщательно спроектировать свою программу, чтобы обеспечить безопасный и синхронизированный доступ к общим ресурсам. Это требует использования методов синхронизации, таких как блокировки, семафоры или атомарные операции, чтобы гарантировать, что только один поток может получить доступ к общему ресурсу одновременно. Кроме того, разработчики должны протестировать и отладить свою программу, чтобы выявить и устранить любые условия гонки или проблемы с синхронизацией, которые могут возникнуть.

Что такое условия гонки в многопоточном приложении

Условия гонки в многопоточных приложениях возникают, когда два или более потоков одновременно обращаются к общему ресурсу, и конечный результат работы программы зависит от порядка выполнения потоков. Это может привести к непредсказуемому и нежелательному поведению, включая повреждение данных, сбои программ или уязвимости в системе безопасности.

В многопоточном приложении потоки часто совместно используют доступ к таким ресурсам, как память, файлы или сетевые подключения. Если два или более потоков пытаются изменить один и тот же ресурс одновременно, может возникнуть состояние гонки. Например, если один поток читает из файла, в то время как другой поток пытается его удалить, конечный результат зависит от того, какой поток выполняется первым.

Условия гонки может быть трудно обнаружить и воспроизвести, поскольку они часто возникают спорадически и зависят от таких факторов, как загрузка системы и время выполнения потока. Их также может быть трудно исправить, поскольку они требуют тщательной синхронизации и координации между потоками для обеспечения безопасного и предсказуемого доступа к общим ресурсам.

Чтобы избежать условий гонки в многопоточных приложениях, разработчикам следует использовать методы синхронизации, такие как блокировки, семафоры или атомарные операции, чтобы гарантировать, что только один поток может одновременно обращаться к общему ресурсу. Они также должны тщательно управлять порядком выполнения потоков, чтобы обеспечить последовательный и предсказуемый доступ к общим ресурсам. Кроме того, тщательное тестирование и отладка могут помочь выявить и устранить условия гонки до того, как они могут вызвать серьезные проблемы.

Как пользователи могут определять условия гонки в многопоточном приложении

Условия гонки в многопоточных приложениях могут проявляться различными способами, но некоторые общие поведения приложений, которые могут указывать на состояние гонки, включают:

Неверный или неожиданный вывод. Если выходные данные приложения неверны или неожиданны, это может указывать на то, что несколько потоков одновременно обращаются к одному и тому же ресурсу и выдают противоречивые результаты.

Сбой или зависание. Если приложение неожиданно выходит из строя или зависает, это может быть признаком того, что из-за состояния гонки программа зависает или перестает отвечать на запросы.

Непоследовательное поведение. Если поведение приложения непоследовательно, это может указывать на то, что разные потоки по-разному обращаются к общим ресурсам, что приводит к неожиданным результатам.

Проблемы с производительностью. Если производительность приложения снижается при высокой нагрузке или параллелизме, это может указывать на то, что несколько потоков конкурируют за одни и те же ресурсы и вызывают задержки или узкие места.

Тупики. Взаимоблокировка возникает, когда два или более потоков блокируются в ожидании освобождения ресурса друг другом, что приводит к ситуации, когда ни один из потоков не может выполнить прогресс. Взаимоблокировки могут быть признаком состояния гонки, если они возникают, когда несколько потоков обращаются к общим ресурсам.

Как пентестеры или разработчики могут обнаруживать условия гонки в многопоточном приложении

Проверка кода. Одним из способов обнаружения условий гонки является тщательный анализ исходного кода приложения. Разработчики могут искать области кода, в которых доступ к общим ресурсам, таким как переменные и структуры данных, осуществляется несколькими потоками без надлежащей синхронизации.

Пример 1 

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

				
					import threading

# A shared variable
counter = 0

def increment():
    global counter
    for i in range(100000):
        counter += 1

# Create two threads that both call the increment function
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)

# Start the threads
thread1.start()
thread2.start()

# Wait for the threads to finish
thread1.join()
thread2.join()

# The expected result is 200000, but due to the race condition, the 
# actual result may be lower
print("Counter value:", counter)
				
			

В этом примере у нас есть общая переменная с именем counter это увеличивается каждым потоком в цикле. В increment функция увеличивает переменную счетчика на 1 в 100000 раз. Однако, поскольку у нас есть два потока, выполняющих increment функционируя одновременно, они могут одновременно считывать и записывать в переменную счетчика, вызывая состояние гонки. В результате переменная счетчика может быть увеличена не так, как ожидалось, и конечное значение может быть ниже ожидаемого значения 200000.

Пример 2

Если несколько потоков записывают данные в один и тот же файл без надлежащей синхронизации, может возникнуть состояние гонки, при котором записи чередуются, а выходные данные искажаются.

				
					import threading

def write_to_file(file_name, text):
    with open(file_name, 'a') as f:
        f.write(text)

threads = []

# Create 5 threads that write to the same file
for i in range(5):
    t = threading.Thread(target=write_to_file, args=('output.txt', f'Thread {i}\n'))
    threads.append(t)
    t.start()

# Wait for all threads to finish
for t in threads:
    t.join()
				
			
				
					Thread 1
Thread 2
ThThread 3
read 4
d 0
				
			

В этом коде создается несколько потоков для записи текста в один и тот же файл output.txt. Однако, поскольку синхронизация между потоками отсутствует, текст из каждого потока может чередоваться, а выходные данные могут искажаться. Например, вывод в output.txt может выглядеть примерно так, как описано выше. 

Пример 3

Когда несколько потоков обновляют одну и ту же структуру данных без надлежащей синхронизации, может возникнуть состояние гонки. Например, если два потока пытаются добавить элемент в один и тот же список одновременно, они оба могут прочитать одну и ту же длину списка, вставить свой элемент, а затем записать обратно новую длину списка, в результате чего один из элементов будет перезаписан.

				
					import threading

# A shared list
my_list = []

def add_item():
    global my_list
    # Read the current length of the list
    current_length = len(my_list)
    # Add an item to the list
    my_list.append(threading.current_thread().name)
    # Write back the new length of the list
    new_length = len(my_list)
    print(f"{threading.current_thread().name} added an item to the list. List length: {new_length}")
    # Check for race condition
    if new_length != current_length + 1:
        print(f"Race condition detected by {threading.current_thread().name}! List length should be {current_length + 1}, but is {new_length}.")

# Create multiple threads that add an item to the list
t1 = threading.Thread(target=add_item)
t2 = threading.Thread(target=add_item)
t1.start()
t2.start()
t1.join()
t2.join()

				
			

В этом примере два потока пытаются добавить элемент в один и тот же список. В add_item() функция считывает текущую длину списка, добавляет элемент в список, а затем записывает обратно новую длину списка. Однако, поскольку синхронизация между потоками отсутствует, может возникнуть состояние гонки, когда оба потока считывают список одинаковой длины, вставляют свой элемент, а затем записывают обратно ту же новую длину списка, в результате чего один из элементов перезаписывается.

Когда вы запустите этот код, вы можете увидеть следующие выходные данные:

				
					Thread-1 added an item to the list. List length: 1
Thread-2 added an item to the list. List length: 1
Race condition detected by Thread-1! List length should be 2, but is 1.

				
			

В этом случае условие гонки возникло из-за того, что оба потока прочитали одинаковую длину списка, а затем записали обратно ту же новую длину списка, в результате чего был добавлен только один элемент вместо двух. В if заявление в add_item() функция определяет состояние гонки и выводит сообщение на консоль.

Пример 4 

В этом примере есть два потока и две блокировки. Каждый поток получает одну блокировку, а затем пытается получить другую блокировку. Если один поток получает lock1, а другой поток получает lock2, то возникает взаимоблокировка, потому что каждый поток ожидает, пока другой поток снимет необходимую ему блокировку.

				
					import threading

# Create two locks
lock1 = threading.Lock()
lock2 = threading.Lock()

def thread1():
    lock1.acquire()
    lock2.acquire()
    # Do something
    lock1.release()
    lock2.release()

def thread2():
    lock2.acquire()
    lock1.acquire()
    # Do something else
    lock2.release()
    lock1.release()

# Create two threads and start them
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()
t1.join()
t2.join()

				
			

Тестирование. Пентестеры могут выполнять комплексное тестирование приложения, используя такие инструменты, как стресс-тестирование и нагрузочное тестирование, для моделирования сценариев с высоким трафиком и одновременным доступом. Это может помочь выявить потенциальные условия гонки и проблемы с синхронизацией.

Отладка. Если есть подозрение на состояние гонки, разработчики могут использовать отладчик для пошагового выполнения кода и отслеживания состояния переменных и других ресурсов по мере обращения к ним разных потоков. Это может помочь определить первопричину состояния гонки.

Ведение журнала. Разработчики могут добавлять операторы ведения журнала в код, чтобы отслеживать последовательность событий и выявлять любые несоответствия или неожиданное поведение, которые могут указывать на состояние гонки.

ЛУЧШИЕ CWES для условий гонки в многопоточных приложениях

CWE-362: Одновременное выполнение с использованием общего ресурса с неправильной синхронизацией (‘Состояние гонки’) Этот CWE возникает, когда несколько потоков выполняются одновременно, обращаются к общему ресурсу без надлежащей синхронизации и приводят к неожиданному чередованию ресурса. Это может привести к противоречивым данным, неверным результатам и сбоям.

CWE-835: Цикл с недостижимым условием выхода (‘Бесконечный цикл’) Этот CWE возникает, когда цикл выполняется в многопоточной среде, и один поток изменяет переменную цикла, в то время как другой поток ожидает завершения цикла. Это может привести к бесконечному циклу, и программа перестанет отвечать на запросы.

CWE-831: Неправильная синхронизация Этот CWE возникает, когда несколько потоков обращаются к общему ресурсу без надлежащей синхронизации, что приводит к состоянию гонки. Это может привести к несогласованности данных, неправильным результатам и сбоям.

CWE-833: Взаимоблокировка Этот CWE возникает, когда два или более потоков блокируются, ожидая, пока друг от друга освободится ресурс, необходимый им для продолжения работы. Это может привести к взаимоблокировке, когда потоки не могут продолжить работу и программа перестает отвечать.

CWE-834: Неправильно контролируемая модификация атомарной операции Этот CWE возникает, когда несколько потоков изменяют атомарную операцию, такую как целое число или логическое значение, без надлежащей синхронизации. Это может привести к противоречивым данным и неверным результатам.

ЛУЧШИЕ CVE для условий гонки в многопоточных приложениях

CVE-2016-2324 – Обход Apache Tomcat Security Manager через загрузчик классов контекста потока Этот CVE влияет на Apache Tomcat и связан с состоянием гонки в реализации Security Manager. Его можно использовать для обхода диспетчера безопасности и получения несанкционированного доступа к ресурсам.

CVE-2018-16640 – WordPress 4.9.8 Повышение привилегий Этот CVE влияет на WordPress и связан с состоянием гонки в механизме обновления. Его можно использовать для повышения привилегий и выполнения произвольного кода в уязвимой системе.

CVE-2019-11477 – Linux Kernel SACK Panic Этот CVE влияет на ядро Linux и связан с состоянием гонки в реализации TCP. Он может быть использован для того, чтобы вызвать атаку типа "отказ в обслуживании" (DoS) путем сбоя системы.

CVE-2019-11478 – Замедление работы ядра Linux Этот CVE также влияет на ядро Linux и связан с состоянием гонки в реализации TCP. Он может быть использован для того, чтобы вызвать DoS-атаку, замедляя работу системы.

CVE-2020-25223 – Уязвимость состояния гонки Apache Struts2 Этот CVE влияет на Apache Struts2 и связан с состоянием гонки в реализации TokenInterceptor. Его можно использовать для обхода аутентификации и получения несанкционированного доступа к ресурсам.

CVE-2021-32027 – Apache Tomcat jsp: уязвимость forward Этот CVE влияет на Apache Tomcat и связан с состоянием гонки в реализации jsp:forward. Его можно использовать для обхода ограничений безопасности и получения несанкционированного доступа к ресурсам.

CVE-2021-31800 – Уязвимость удаленного выполнения кода на сервере Microsoft Exchange Этот CVE влияет на сервер Microsoft Exchange и связан с состоянием гонки в приложении панели управления Exchange (ECP). Он может быть использован для выполнения произвольного кода в уязвимой системе.

Примеры из реального мира 

В январе 2021 года в macOS Big Sur от Apple была обнаружена ошибка, которая могла вызвать состояние гонки, когда несколько процессов одновременно пытались получить доступ к одному и тому же файлу. Это может привести к повреждению данных и привести к сбоям системы.

В марте 2021 года было сообщено об уязвимости в Microsoft Exchange Server, которая может позволить злоумышленникам выполнять произвольный код в уязвимой системе, используя состояние гонки в приложении.

В апреле 2021 года в Apache Cassandra была обнаружена уязвимость, которая могла позволить злоумышленникам выполнять произвольный код в уязвимой системе, используя условие гонки при обработке приложением соединений TLS / SSL

В мае 2020 года Microsoft Windows столкнулась с уязвимостью, связанной с состоянием гонки в интерфейсе графического устройства Windows (GDI). Злоумышленники могут использовать эту уязвимость для повышения привилегий и выполнения произвольного кода в уязвимой системе.

В июле 2020 года Apple macOS столкнулась с уязвимостью, связанной с состоянием гонки в коде ядра. Злоумышленники могут воспользоваться этой уязвимостью, чтобы получить повышенные привилегии и выполнить произвольный код в уязвимой системе.

В августе 2020 года операционная система Android столкнулась с уязвимостью, связанной с состоянием гонки в драйвере binder. Злоумышленники могут воспользоваться этой уязвимостью, чтобы получить повышенные привилегии и выполнить произвольный код в уязвимой системе.

Как защитить приложение от условий гонки при многопоточности

Синхронизация: Используйте механизмы синхронизации, такие как блокировки, семафоры и мониторы, чтобы гарантировать, что только один поток может одновременно обращаться к общим ресурсам. Правильная синхронизация предотвратит одновременный доступ нескольких потоков к одному и тому же ресурсу или его изменение.

Атомарные операции: Используйте атомарные операции, которые гарантируют, что операция будет выполнена за один неделимый шаг. Атомарные операции особенно полезны для простых операций с общими переменными, таких как увеличение или уменьшение счетчика.

Безопасность резьбы: Убедитесь, что код с самого начала разработан таким образом, чтобы быть потокобезопасным. Потокобезопасность может быть достигнута за счет отказа от глобальных переменных, использования неизменяемых структур данных и отделения состояния от поведения.

Обзоры кода: Проведите проверку кода, чтобы убедиться, что приложение разработано с учетом параллелизма. Обзоры кода могут выявить потенциальные условия конкуренции и гарантировать, что код написан таким образом, чтобы его было легко понять и поддерживать.

Заключение

В заключение, условия гонки в многопоточных приложениях могут быть значительным источником уязвимостей в системе безопасности и других проблем, которые могут повлиять на производительность и стабильность приложения. Эти проблемы могут возникнуть, когда несколько потоков пытаются получить доступ к общим ресурсам без надлежащей синхронизации или когда приложение не разработано с учетом параллелизма.

Чтобы устранить условия гонки в многопоточных приложениях, разработчики должны внедрить механизмы синхронизации, использовать атомарные операции, спроектировать код так, чтобы он был потокобезопасным с самого начала, выполнить тщательное тестирование и провести проверку кода. Кроме того, разработчики должны быть в курсе последних уязвимостей и исправлений системы безопасности, чтобы гарантировать защиту своих приложений от новых и возникающих угроз.

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

Другие Услуги

Готовы к безопасности?

Связаться с нами