Тебе, наверное, интересно, почему всякие там кул хацкеры используют C/С++, вместо того чтобы выучить и заюзать рульный скрипт - Perl, Bash, PHP, VBS (нужное подчеркнуть). Отвечаю:

1. Скорость. В отличие от скриптов, язык C/C++ - компилируемый, поэтому код получается быстрее, также язык C/C++ более низкоуровневый, поэтому на нем проще избежать накладных расходов.

2. Надежность. Даже если хакер получит доступ на чтение к папкам, на которых лежат исполняемые CGI-файлы, то его постигнет разочарования, т.к. он не найдет исходного кода твоей CGI-шки, т.к. C-шных исходник ты хранишь в банке под надежным замком, а на сервере лежит только скомпилированная программа. И хакер будет долго разбираться в дизассемблере, чтобы понять, есть в твоей программе баги или нет. Также C, а особенно C++, является строго типизированным языком, поэтому о многих ошибках тебе компилятор скажет еще на этапе компиляции.

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

4. Время/опыт. Лучше потратить время на углубление своих знаний и повышения опыта по использованию C/C++, чем потратить свое время на изучение очередного скрипта. Так знание C/C++ поможет тебе написать сниффер, keygen, троян, вирус (список можно продолжать до бесконечности), а зная только скрипты, ты будешь привязан к узкому кругу задач.

C++ РУЛИТ

Скажу тебе по секрету, что все продвинутые хакеры уже давно вместо С используют C++ из-за его мощности и удобства. В C++ очень легко избежать или свести к минимуму все вышеприведенные ошибки. Так вместо страшно неудобных и очень бажных строк char *, а значит, соответственно, и использования функций strcat, strcpy, можно использовать класс строк - std::string. Вместо опасного printf/scanf/gets можно использовать std::stream и operator >>/<<. А обычные C`шные массивы можно заменит на std::vector, std::list, std::map.

Также не секрет, что многие ошибки в программах связаны с тем, что программеру было просто лень писать более сложный код и он ограничивается самым простым (но и, к сожалению, самым бажным) вариантом. Но и здесь C++ облегчает жизнь программисту, т.к. в язык уже включена большая и безопасная библиотека STL, с помощью которой многие сложные вещи, занимающие на C несколько экранов, пишутся на C++ в несколько строчек. Также использование С++ упрощает повторное использование кода, что позволяет один раз написать свою небольшую библиотечку, а потом ее использовать во всех своих программах.

Если тебя еще не убедили вышеприведенные аргументы, то вот тебе еще один. Плюсы поддерживают конструкцию try/catch, при ее аккуратном применении можно забыть о таких сообщениях, как "core dumped", "access violation", "division by zero" и т.д.

А сейчас давай посмотрим, какие ошибки можно поиметь при написании CGI-шек на C/C++ (мы ведь с тобой кул хацкеры и не пишем CGI-шки на всяких там скриптах). Сразу тебя обрадую, на C (в дальнейшем, говоря C, я подразумеваю C/C++) нет этих дурацких ошибок с нулевым символом, а также с выполнением программ во время простейшего открытия файла. Но в C нас с тобой поджидают не менее опасные ошибки: переполнение буфера, ошибка форматной строки и др. Ошибки я буду показывать на примере обычных консольных программ (это программы, которые работают в текстовом режиме), т.к. CGI-шка как раз и является обычной консольной программой.

1 (a). Переполнение буфера при использовании стандартных функций

Одна из самых распространенных и опасных ошибок, т.к. под данную ошибку очень легко написать exploit.
Пример грубой ошибки:

char name[300];
scanf("%s", name);

Число 300 взято от балды, вместо него могло быть и 30, и 300000, от ошибки это не спасает, т.к. все равно можно ввести (или написать прогу, которая будет вводить) строку большей длины.

Пример тонкой ошибки:

char s[6];
itoa (value, s, 10);

Если переменная value вводилась пользователем, то он мог ввести число, в котором цифр больше, чем 5, т.к. самое длинное целое число (-2147483648) содержит 13 символов, разницы в 7 символов часто хватает с лихвой, чтобы написать под эту ошибку exploit.

Ошибке "переполнение буфера" подвержены следующие стандартные функции (в скобочках указан безопасный аналог, если он есть) - gets (fgets), strcpy (strncpy), strcat(strncat), при неосторожном обращении опасны также функции - sprintf (snprintf), vsprintf (vsnprintf), scanf, fscanf, sscanf, itoa.

1 (б). Переполнение буфера в своем коде

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

Пример грубой ошибки:

int index, value, array[100];
std::cout >> index >> value; //вводим номер элемента массива, вводим число
array[index] = value; //записываем введенное число в массив

В данной задаче надо было проверять переменную index на правильное значение - оно должно было быть больше нуля и меньше 100.

Пример тонкой ошибки:

char buf[200], *p = buffer;
int i = 0;
for (i = 0; (buf[i] = getchar()) != ' ' && i < 200; ++i){}
buf[i] = 0;

Максимально введенная строка будет занимать 201 байт, а не 200, как рассчитывалось. Exploit на данный конкретный случай будет сложно написать, но уронить программу можно будет запросто, а при небольшом везении может все-таки получиться и exploit.

Рекомендации по избежанию данной ошибки просты:
a. Проверяй все входные параметры на корректность.
b. Будь внимателен при написании кода, работающего с массивами.
2. Ошибка форматной строки
При выводе строк с помощью функций семейства printf многие ленивые программисты или "крутые оптимизаторы" (которые стараются экономить даже на спичках) пишут вместо printf ("%s", str) просто printf (str).

Во втором случае, если строка str вводилась пользователем, в нее можно вставить форматные спецификаторы (%s, %d и т.д.), которые могут не только завалить программу, но и поспособствовать выполнению exploit`а.

Данной ошибке подвержены следующие функции: все функции семейства printf (sprintf, fprintf, vsprintf и т.д.), функции семейства scanf (если форматная строка ввода формируется с использованием введенных пользователем строк), функции, основанные на printf - syslog, setproctitle и т.д.

Рекомендация одна - "не ленись" и пиши полный вариант вызова, а также осторожно используй введенные пользователем строки при формировании форматной строки.

3. Чтение и запись произвольных файлов

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

Пример грамотного кода, который сначала отфильтровывает точки и только потом использует для создания директории введенную строку:

char buf[1024] ={0};;
fgets (stdin, sizeof(buf)/sizeof(buf[0]), buf);
for (int j = 0, i = 0; ; ++i)
{
if (buf[i] != '.')
buf[j++] = buf[i];
if (!buf[i]) break;
}
mkdir (buf);

Данную фильтрацию надо применять для всех функций работы с файлами - open, fopen, mkdir, rmdir, chdir, tempnam, tmpnam, функции exec, функции spawn.

4. Функция system

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

Посылаем введенную пользователем строку себе на WinPopup:

std::string s;
std::cin >> s;
std::string s2 = "net send 127.0.0.1 ";
for (std::string::iterator it = s.begin(); it != s.end(); ++it)
{
if (*it == '"') s2+= '"'; //или s2+=''
s2+= *it;
}
std::system (s2);
EXIT(0)

В данной статье я рассмотрел только самые широко распространенные ошибки, за бортом остались мелкие и специфичные ошибки, а также те ошибки, которые невозможно (очень сложно) использовать для внедрения exploit`а.

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