[Перевод] Я нашёл 213 уязвимостей безопасности в кодовой базе при помощи GPT-3

6llv_tiuizppef3wcpv7fbqvd9a.png


«Меня удручает ваш уровень кибербезопасности»

Краткая сводка: GPT-3 обнаружила 213 уязвимостей безопасности в git-репозитории. Для сравнения: один из лучших коммерческих инструментов на рынке (разработанный респектабельной компанией, которая занимается кибербезопасностью) нашёл лишь 99 проблем; впрочем, этот инструмент предоставляет контекст в более структурированном формате. После ручной проверки случайной выборки 50 из 213 уязвимостей, выявленных GPT-3, только одна оказалась ложноположительной. У обоих инструментов было много ложноотрицательных результатов.


За последние годы сфера искусственного интеллекта и машинного обучения испытывала бешеный рост, открыв новое пространство возможностей. Одной из таких возможностей, привлёкших внимание, стал анализ кода на основе ИИ, а конкретно использование ИИ-моделей для выявления уязвимостей безопасности в коде. В этом эксперименте мы использовали GPT-3 компании OpenAI для поиска уязвимостей безопасности в репозитории кода, содержащем 129 файлов с уязвимостями.

Как это работает


Использованный мной вариант GPT-3 (text-davinci-003) имеет окно контекста в 4000 токенов, что приблизительно равно трём тысячам английских слов. Это значит, что за один запрос он может обрабатывать не больше нескольких сотен строк кода. К сожалению, современная архитектура GPT-3 не может справиться с целым репозиторием за раз.

Чтобы обойти эту проблему, мне пришлось по отдельности сканировать каждый файл при помощи GPT-3. Значит, у GPT-3 могли возникнуть трудности с поиском уязвимостей безопасности, возникающих при взаимодействии нескольких файлов кода; впрочем, иногда импорт/экспорты оказывались достаточно понятны, чтобы без изучения кода сделать предположение о том, что выполняют эти функции. Такое бывало довольно часто, особенно когда исходный код использовал популярные библиотеки наподобие express.js, Flask, стандартной библиотеки Python, стандартной библиотеки C и так далее. Вероятно, GPT-3 частично или полностью запомнил самые популярные библиотеки (или закодировал их каким-то иным способом). В случае кода, анализируемого в этой статье, у GPT-3 имелось достаточно знаний об импортированных библиотеках, чтобы точно выявлять уязвимости безопасности без необходимости изучения кода импортированных библиотек.

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

Анализируемый код


Каждая папка в репозитории имеет имя, соответствующее типу уязвимости безопасности, и содержит файлы с примером кода, включающего одну или несколько уязвимостей. В некоторых файлах находится тривиальный код, однако во многих содержатся довольно реалистичные фрагменты кода, с которыми вы можете столкнуться в кодовой базе продакшена (примечание:, но это всё равно фрагменты и в них отсутствует контекст более крупной кодовой базы). Файл README.md в каждой папке содержит анализ уязвимостей безопасности, проведённый GPT-3 для всех файлов в этой папке.

Давайте рассмотрим несколько примеров, чтобы узнать, как же поработал GPT-3!

Пример 1 (тривиальный)


Это простая программа на C, уязвимая к Format String Attack:

#include 
 
int main(int argc, char **argv) {
    printf(argv[1]);
 
    return 0;
}


А вот анализ GPT-3 (совершенно верный):

Vulnerabilities Detected:
1. Unvalidated user input: The program does not check the length of the user input, which could lead to a buffer overflow attack.
2. Format string vulnerability: The program does not check the format of the user input, which could lead to a format string attack.

Перевод:

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


Пример 2 (менее тривиальный)


Давайте проверим ИИ с менее тривиальной программой на высокоуровневом языке, например, с этой программой на C#, содержащей уязвимость Log Forging. Лично я видел схожие (хоть и менее тривиальные) примеры кода в опенсорсных проектах и кодовых базах продакшена:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebFox.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class LogInjection : ControllerBase
    {
        private readonly ILogger _logger;


        public LogInjection(ILogger logger)
        {
            _logger = logger;
        }

        [HttpGet("{userInfo}")]
        public void injectLog(string userInfo)
        {
            _logger.LogError("error!! " + userInfo);
        }
    }
}


А вот вывод GPT-3:

Vulnerabilities Detected:
1. Log injection vulnerability: The code is vulnerable to log injection attacks as user input is being directly logged without any sanitization.

Перевод:

Выявленные уязвимости:
1. Уязвимость Log injection: код уязвим к атакам log injection, поскольку логирование пользовательского ввода выполняется напрямую и без санации.


Пример 3 (нетривиальный)


Приведённая ниже программа на C считывает и обрабатывает изображение. Она содержит множество уязвимостей безопасности, в частности Out Of Bounds read и write:

#include
#include
#include

struct Image
{
    char header[4];
    int width;
    int height;
    char data[10];
};

int ProcessImage(char* filename){

    FILE *fp;
    char ch;
    struct Image img;

    fp = fopen(filename,"r"); 

    if(fp == NULL)
    {
        printf("\nCan't open file or file doesn't exist.");
        exit(0);
    }

    printf("\n\tHeader\twidth\theight\tdata\t\r\n");

    while(fread(&img,sizeof(img),1,fp)>0){
        printf("\n\t%s\t%d\t%d\t%s\r\n",img.header,img.width,img.height,img.data);
    
        int size1 = img.width + img.height;
        char* buff1=(char*)malloc(size1);

        memcpy(buff1,img.data,sizeof(img.data));
        free(buff1);
    
        if (size1/2==0){
            free(buff1);
        }
        else{
            if(size1 == 123456){
                buff1[0]='a';
            }
        }

        int size2 = img.width - img.height+100;
        //printf("Size1:%d",size1);
        char* buff2=(char*)malloc(size2);

        memcpy(buff2,img.data,sizeof(img.data));

        int size3= img.width/img.height;
        //printf("Size2:%d",size3);

        char buff3[10];
        char* buff4 =(char*)malloc(size3);
        memcpy(buff4,img.data,sizeof(img.data));

        char OOBR_stack = buff3[size3+100];
        char OOBR_heap = buff4[100];

        buff3[size3+100]='c';
        buff4[100]='c';

        if(size3>10){
                buff4=0;
        }
        else{
            free(buff4);
        }

        free(buff2);
    }
    fclose(fp);
}

int main(int argc,char **argv)
{
    ProcessImage(argv[1]);
}


Вывод GPT-3:

Vulnerabilities Detected:
1. Unchecked return value: The return value of the fopen () function is not checked, which could lead to a null pointer dereference.
2. Out-of-bounds read: The memcpy () function is used to copy data from img.data to buff1 and buff2 without checking the size of the destination buffer, which could lead to an out-of-bounds read.
3. Out-of-bounds write: The buff3 and buff4 arrays are written to without checking the size of the source buffer, which could lead to an out-of-bounds write.
4. Uninitialized memory access: The OOBR_stack and OOBR_heap variables are accessed without being initialized, which could lead to undefined behavior.
5. Memory leak: The buff4 array is not freed if the size3 variable is greater than 10, which could lead to a memory leak.

Перевод:

Выявленные уязвимости:
1. Непроверяемое возвращаемое значение: возвращаемое функцией fopen () значение не проверяется, что может привести к разыменованию нулевого указателя.
2. Out-of-bounds read: функция memcpy () используется для копирования данных из img.data в buff1 и buff2 без проверки размера целевого буфера, что может привести к out-of-bounds read.
3. Out-of-bounds write: запись в массивы buff3 и buff4 выполняется без проверки размера исходного буфера, что может привести к out-of-bounds write.
4. Доступ к неинициализированной памяти: доступ к переменным OOBR_stack и OOBR_heap выполняется без инициализации, что может привести к неопределённому поведению.
5. Утечка памяти: массив buff4 не освобождается, если переменная size3 больше 10, что может привести к утечке памяти.


GPT-3 снова прав!

Пример 4


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

class Utils
{
    // Function to serialize an object and write it to a file
    public static void SerializeToFile(Object obj, String filename)
    {
        try
        {
            FileOutputStream file = new FileOutputStream(filename);
            ObjectOutputStream out = new ObjectOutputStream(file);

            // Serialization of the object to file
            System.out.println("Serializing " + obj.toString() + " to " + filename);
            out.writeObject(obj);

            out.close();
            file.close();
        }
        catch(Exception e)
        {
            System.out.println("Exception: " + e.toString());
        }
    }

    // Function to deserialize an object from a file
    public static Object DeserializeFromFile(String filename)
    {
        Object obj = new Object();

        try
        {
            FileInputStream file = new FileInputStream(filename);
            ObjectInputStream in = new ObjectInputStream(file);

            // Deserialization of the object to file
            System.out.println("Deserializing from " + filename);
            obj = in.readObject();

            in.close();
            file.close();
        }
        catch(Exception e)
        {
            System.out.println("Exception: " + e.toString());
        }

        return obj;
    }
}


Вывод GPT-3:

Vulnerabilities Detected:
1. Insecure Deserialization: Deserializing untrusted data can lead to remote code execution.
2. Insecure File Permissions: The file being written to or read from may have insecure permissions, allowing unauthorized access.

Перевод:

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


GPT-3 снова прав.

Важный «сдвиг влево» в кибербезопасности


Сдвиг влево — это процесс применения мер безопасности на более ранних этапах процесса разработки, например, тестирование на баги безопасности до развёртывания кода в продакшене.

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

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

О дате выпуска GPT-4 пока не заявлено, но я уверен, что эти большие языковые модели продолжат развиваться, получая всё больше и больше возможностей.

Ложноположительные срабатывания


После ручного анализа примерно 50 из 213 уязвимостей, выявленных GPT-3, я нашёл единственную ложноположительную в строке 13 этой программы на Javascript:

var express = require('express');
var cookieParser = require('cookie-parser');
var escape = require('escape-html');
var serialize = require('node-serialize');
var app = express();
app.use(cookieParser())
 
app.get('/', function(req, res) {
 if (req.cookies.profile) {
   var str = new Buffer(req.cookies.profile, 'base64').toString();
   var obj = serialize.unserialize(str);
   if (obj.username) {
     res.send("Hello " + escape(obj.username)); // <--- GPT-3 makes a mistake here
   }
 } else {
     res.cookie('profile', "eyJ1c2VybmFtZSI6ImFqaW4iLCJjb3VudHJ5IjoiaW5kaWEiLCJjaXR5IjoiYmFuZ2Fsb3JlIn0=", {
       maxAge: 900000,
       httpOnly: true
     });
 }
 res.send("Hello World");
});
app.listen(3000);


Вывод GPT-3:

Vulnerabilities Detected:
— Insecure cookie settings (no secure flag set)
— Unvalidated deserialization of user input (cookie)
— Potential XSS vulnerability due to lack of escaping of user input (username)

Перевод:

Выявленные уязвимости:
— Небезопасные параметры куки (не установлен флаг secure)
— Невалидированная сериализация пользовательского ввода (cookie)
— Потенциальная уязвимость XSS из-за отсутствия экранирования пользовательского ввода (username)


GPT-3 прав насчёт первых двух уязвимостей, однако третья уязвимость ложноположительная — obj.username экранирован, хотя GPT-3 утверждает, что это не так.
Результаты эксперимента показали, что GPT-3 способен выявить уязвимости безопасности в 85 из 129 просканированных файлов. Впечатляет!

Скрипт summarize_results.py генерирует полную сводку результатов GPT-3:

Vulnerabilities detected in 86 / 129 files.
Detected 213 vulnerabilities in total.

Frequency of introductory sentences used in GPT-3's responses (1 response / file scanned):
{'vulnerabilities detected': 73, 'no vulnerabilities detected.': 43, 'vulnerability detected': 6, 'answer': 2, 'potential vulnerabilities detected': 2, 'analysis': 1, 'security vulnerabilities detected': 1, 'no response given': 1}

Distribution of file types scanned:
129 files of code in total (excluding markdown and flatfiles)
{'.php': 50, '.js': 20, '.cs': 16, '.c': 14, '.java': 9, '.py': 8, '.rb': 5, '.asp': 3, '.ts': 2, '.go': 1, '.html': 1}

Перевод:

Уязвимости выявлены в 86 / 129 файлов.
Всего выявлено 213 уязвимостей.

Частота вводных предложений, использованных в ответах GPT-3 (1 ответ / просканированный файл):
{'vulnerabilities detected': 73, 'no vulnerabilities detected.': 43, 'vulnerability detected': 6, 'answer': 2, 'potential vulnerabilities detected': 2, 'analysis': 1, 'security vulnerabilities detected': 1, 'no response given': 1}

Распределение типов просканированных файлов:
Всего 129 файлов с кодом (за исключением markdown и неструктурированных файлов)
{'.php': 50, '.js': 20, '.cs': 16, '.c': 14, '.java': 9, '.py': 8, '.rb': 5, '.asp': 3, '.ts': 2, '.go': 1, '.html': 1}


Сравнение с коммерческими инструментами


Чтобы закончить эксперимент, я сравнил результаты GPT-3 с результатами коммерческого сканера уязвимостей Snyk Code, разработанного Snyk — компанией, которая, по моему мнению, делает превосходные продукты в сфере безопасности. Пропустив этот репозиторий через Snyk Code, я выявил 99 уязвимостей безопасности по сравнению с 213, найденными GPT-3.

9crb7nsmeib5nzh5kr-8gv_ogem.png


Одним из ограничивающих факторов стало то, что Snyk Code поддерживает не все языки программирования репозитория, поэтому смог просканировать только 103 файлов, а не 129, как GPT-3.
Фрагменты уязвимого кода в этом репозитории взяты с потрясающего ресурса snoopysecurity/Vulnerable-Code-Snippets. Я постарался удалить все встроенные в код комментарии, сообщавшие о том, какие уязвимости безопасности содержатся в конкретных фрагментах. Для этого потребовалось удалить комментарии, содержащие ссылки на посты и статьи, откуда были взяты эти примеры фрагментов. Все ссылки на источники, присутствующие в исходном репозитории, можно найти в файле attributions.md.

© Habrahabr.ru