Показаны сообщения с ярлыком Кодинг. С. Показать все сообщения
Показаны сообщения с ярлыком Кодинг. С. Показать все сообщения

Кодерские хаки

[stranger@localhost ~]$ date
Сбт Дек 5 00:23:09 MSK 2009

[stranger@localhost ~]$ cat /home/stranger/h3kcode.txt
Ну для начала стоит определиться что такое «хаки».
В моем понимании это какие то нестандартные, красивые, оригинальные решение, которые явно отличаются от других. В общем что то умное и не ординарное. Собственно в этой статье я приведу пример нескольких хаков на тему кодинг. Вообще зачем они нужны? По моему главная причина это так называемый brainfuke, который является любимый занятием любого хакера/кодера. Ну и вторая причина читать данный материал – повышение уровня знания языка. Кстати примеры будут на Си.
Ну что ж. Теперь, когда я заполнил выделенную память введением, можно начинать fuke.


---[h3k 01] ---
//Очень маленькое тело
Первый хак заключается в том, что бы написать программу, которая выводила 10 раз подряд рандомные (псевдослучайные) числа. Но. Тело функции main() не должно превышать 4 символов. При том – нельзя писать доп. Функции, нельзя инклудить какие либо файлы кроме stdio.h.

Думаю ясно что 4 символами такую программу не написать (а может можно?)). Нам нужно куда то вынести этот код:

int i=0;
while(i[10)
{
printf(%i,rand(1000));
}

Но не забываем, что функции создавать нельзя. Но с помощью макроподстановок в дириктиве #define можно выполнить этот код. Смотрим:

#include [stdio.h]
#define RND int i=0,g=random(100); while(i[10){printf("%d",g);printf("\n");i++;}

int main()
{
RND;
}

Ну вот.) получилась программа из трех букв xdd
На самом деле макроподстановка довольно удобная вещь. К примеру показ баннера вашей программы. Достаточно написать

#define banner printf(«Powed by StraNger»);

И не нужно пихать код в отдельную функцию

---[h3k 02] ---
//Цикл без цикла
Давайте попробуем сделать такой трюк – нужно написать программу которая бы перебирала указанные числа по порядку и каждое новое число сумировало с предыдущим.
Но главная задача в том, что бы не использовать при этом операторы циклов.

Для начала давайте прикинем какой код будет если мы внедрим цикл(для 10 раз):

- - -
int i=0, stat=5;
while(i[10)
{
stat=stat+(stat-1);
i++;
}
- - -

Теперь нужно избавится от цикла. Конечно, можно было бы выполнять 10 раз схожий код, но это как минимум глупо. Давайте лучше вспомним, что такое оператор goto. Как известно от служит для “прыжка” программы к какой либо метке. Ну например:

- - -
if(a]0)
goto error;
else
//Тут какие то действия
errot:
printf(“OH!\n”);
- - -

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

#include [stdio.h]
int main()
{
int i=2,stat=0;
//Первая ссылка
one:
if(stat[10)
{
i=i+(i-1);
stat++;
goto tho;
}
else
printf("The end, i=%d",i);

}
}

Когда программа доходит до первой ссылки она проверяет переменную stat. Если она еще меньше 10, то нужно выполнять вычисления (как очередной шаг цикла) и увеличивать счетчик. Далее происходит переход ко второй метке , там выполяется точно такая же проверка.
Внимательный читатель заметит, что программу можно значительно уменьшить.
Вот так:

#include [stdio.h]
int main()
{
int i=2,stat=0;

one:
if(stat[10)
{
i=i+(i-1);
stat++;
goto one;
}
else
printf("The end, i=%d",i);
}

Как видите мы попросту выполнили переход к этой же метке.
goto самый ненавистный си оператор.
Эдсгера Дейкстры сказал: “качество программного кода обратно пропорционально количеству операторов GOTO в нём"
Действительно код становится мене понятным. Но как видите иногда его можно применить весьма оригинальным способом.
На самом деле, это не единственный способ организовать “цикл без цикла”
Если вызывать функцию рекурсивной, а в ней проверять значение счетчика получится примерно тоже самое. Вот так:

int step();
int i;
int main()
{
step();
}

int step()
{
if(i[10)
{
i++;
step();
}
else
printf("%i",i);
}



---[h3k 03] ---
//Обманутый strcmp
В этом хаке мы попытаемся обмануть функцию strcmp. Как надеюсь вы знаете он сравнивает две строки, нам же нужно что бы эти строки были не равны между собой, а функция определило противное.

На самом деле тут ничего сложного. Давайте посмотрим как например строка fukeworld располагается в памяти:
f a k e w o r l d\0
нас интересует именно конечный символ \0 он указывает на окончание строки.
Теперь вспомним, как работает strcmp. Она сравнивает две строки посимвольно до символа окончания строки (\0). Вы наверное поняли к чему я веду. Если нет смотрим пример:

#include [stdio.h]
int main(int argc, char argv[])
{
int integ;
char *w1="fuke\0world";
char *w2="fake";
integ=strcmp(w1,w2);
printf("%i\n",integ);
}

Здесь мы так скажем принудительно указали конец строки, используя символ \0. Если отсечь часть после него, то получатся как раз равные строки.
Если вы протестируете пример, то программа должна вывести на дисплей цифру 0. Это результат выполнения функции strcmp, значит строки равны.

Ну а если сделать так:

char *w1="\0fuke world";
то все будут думать, что строка пуста.

---[h3k 04] ---
//forever... oh no!
Представим себе вот такой код:

#include [stdio.h]
int main(int argc, char argv[])
{
int i=1;
for(i=1;i]0;i++)
{
printf("forever");
}
}

Как видите здесь запускается бесконечный цикл. В нем на экран выводится слово forever.
Хак заключается в том, что бы изменить программу таким образом, что наш цикл через какое то время останавливался и не был бесконечным. При том нельзя писать какие то операторы перед циклом (и в нем соответственно), нельзя создавать функции.

Здесь нам на помощь приходит опять #define. Нужно задать какую то константу, которая бы изменила работу цикла. В теле используется только printf, значит ее и нужно подставить в define:


#include [stdio.h]
#define printf continue;
int main(int argc, char argv[])
{
int i=1;
for(i=1;i]0;i++)
{
printf("forever");
}
}

Попробуйте запустить и вы уведите, что цикл не бесконечный. Точнее он не будет выполняться вообще.

---[h3k 05] ---
//10 способов создать бесконечный цикл.
С первого взгляда кажется способов выполнения действия бесконечное число раз не так уж и много. Но на самом деле это не так. Посидев 5 минут с блокнотом я нашел 10 способов (откинув однотипные решения), хотя думаю что можно найти их гораздо больше


***************
1) while(true) {}
***************


**************
2)for (;;) {}
**************


**************
3)
int i;
for (i=0;i]0;i++) {}
**************


**************
4)
int i=1;
while(i]0)
{i++}
**************


**************
5)
repeat:
printf("h");
goto repeat;
**************


**************
6)
int i;
repeat:
i=0;
while(i[1)
{
goto repeat;
i++;}
*************


*************
7)
while(1==1)
{}
*************


*************
8)
while("")
{}
*************


*************
9)
int i;
rock:
do
{
goto rock;
i++;
}
while(i[10); //самое интересное, что тут условие вообще будет игнорироваться
************


************
10)
int i;

for(i=0;i[10;i++)
{
if(i[10)
i--;
}
************
---[h3k 06] ---

//без условия.
Хак заключается в следующем – нужно написать программа которая бы принимало какое то число аргументов. В каждом аргументе находится один символ, программе нужно найти среди них нужный (пусть будет 'w'). При том – нельзя пользоваться условными конструкциями (кроме условия И ИЛИ).

Для сравнения строк будет использовать функцию strcmp(). При полном совпадении она возвращает ноль. Проблема как раз в том, как проверить результат функции.
Смотрим код:


#include [stdio.h]

int main(int argc, char *argv[])
{
int i;
for(i=0;i[argc;i++)
{
(strcmp(argv[i],"w")!=0) || printf("Нужная буква в(во) %i аргументе\n",i+1);
}
}

Происходит примерно так:
значение функции strcmp не равно нулю или (равно нулю) мы нашли нужный символ. Как видите несложно.
Первый хак заключается в том, что бы написать программу, которая выводила 10 раз подряд рандомные (псевдослучайные) числа. Но. Тело функции main() не должно превышать 4 символов. При том – нельзя писать доп. Функции, нельзя инклудить какие либо файлы кроме stdio.h.

EOF
StraNger

Баги в исходных кодах

Еще не просматривали bugtrack? Я уже заглянул сегодня. Опять куча уязвимостей. Опять куча эксплоитов. Программисты – тоже люди, а потому могут допускать ошибки. И эти ошибки стоят root-a. JИтак, сегодня мы поговорим о такой вещи, как «баги» исходного кода.  Как вы знаете, ошибки допускаются и в скриптах, и в прикладных программах. В этой статье я хотел бы рассказать об ошибках в программах на языке Си. От вас понадобится знание языка, основ переполнения буфера и построения эксплоитов.
GO!
Вообще, цель использования уязвимости в программе – это передать целевой системе shellcode, который выполнит необходимые действия. Но некоторые уязвимости можно реализовать, не прибегая к шеллкоду. Например, вызвать «отказ от обслуживания» (DoS).
Давайте разложим все это по полочкам: при поиске уязвимостей бывают частные и нечастные случаи. К частным можно отнести известные «баги» вроде ошибки форматной строки или переполнения буфера (Buffer Overflow). К нечастным случаям можно отнести то, что мы находим  при определённых обстоятельствах. Эти уязвимости нигде не описаны, т.е. приходится рассчитывать только на свои мозги.
Если копать глубже, то бывают ошибки, которые можно использовать локально, а бывают, которые мы реализуем удаленно. Естественно, это зависит от приложения, в котором была найдена уязвимость. Ошибки в разных сетевых службах, как вы, наверное, догадались, мы реализуем удаленно. Часто такие ошибки  эксплуатируем для DoS. Но бывает, что можно поднять свои права.
А если у нас есть доступ к серверу и вы нашли там программу с SUID битом, в которой присутствует уязвимость, то можно запустить shell с правами рута. Ошибка может находиться в самом ядре системы, что тоже может повысить наши привилегии.
К примеру, недавно была обнаружена дыра в Linux kernel 2.6.30, она позволяет выполнять произвольный код с правами root. Уязвимость появилась из-за ошибки разыменования нулевого указателя.
Как видите, ошибка не тривиальная. И для грамотного поиска нужно очень хорошо знать язык Си и особенности системы. Давайте поговорим о том, на что нужно
обращать внимание при поиске уязвимых мест.
Поиск жучков.
При аудите исходного кода (в жаргоне – «сорца»), в первую очередь нужно обязательно смотреть на данные, которые поступают из внешних источников (часто от пользователя). Т.е. большинство переполнений бывает как раз из-за неграмотной проверки входящих данных. Вообще специалисту по безопасности кода можно жить только с одним правилом: «Проверяй все входящие данные».
Так же нужно обращать внимание на буферы со статистическим выделением памяти, то есть буферы фиксированной длины. Данные, которые туда поступают, могут не проверяться, что приведет к тому же переполнению.
При вычислениях нужно обращать внимание, не выходит ли значение за пределы диапазона типа переменной. Если непонятно, приведу пример: переменная типа «integer». Ее диапазон – 2147483648 до 2147483648, а если значение будет больше, то поведение программы может выйти из-под контроля. Например, в gcc такая ошибка приводит к тому, что программа выполняет обратное действие. Если вы прибавляли 10, то будет делать наоборот, т.е. отнимать. Это  называется Integer Overflow.
Опасным может быть некорректное приведение типов. И еще стоит сказать, что неправильное выделение памяти и работа с ней могут привести к плачевным последствиям, например, к отказу от обслуживания.
Кроме всего прочего нужно заострять внимание на таких опасных функциях, как
strcpy() ,getc(), strcat(), spintf(), printf(), vsprinf(), system() и т.п. Замечу одну интересную вещь: как известно, потенциально опасная функция strcpy() может быть заменена якобы безопасной функцией strncpy(). Но безопасной эта функция будет только при правильном использовании.
Вот пример: stridency(a,b,sizeof(b));Вроде все правильно, и кусок кода безопасный. Но если сделать так (как многие и делают):
strncpy(a,b,strlen(b));
то функция становится опасной к переполнению.
Хочу еще сказать, что для аудита безопасности кода существует множество программ. Думаю, не стоит говорить, что доверять им нельзя. Но, все же, я приведу в пример несколько:
BOON (Buffer Overrun detectiON)- из названия уже ясно, что программа производит поиск ошибок переполнения буфера в коде.
FlexeLint – у этой программы круг уязвимостей больше: например она предупреждает об опасном стиле кода. Тоже ищет переполнения буфера, а также арифметические переполнения и т.п.
KlocWork K7 – аналогично выявляет дефекты и проблемы безопасности исходного кода.
Если вас заинтересовали эти сканеры, подробнее можете почитать здесь: http://www.codenet.ru/progr/other/code-analysers.php
Примеры реальных уязвимостей
Ниже я приведу несколько потенциально уязвимых кусков кода, попробуйте самостоятельно найти «багу», а если не получится, то читайте мое решение.
int main(int argc, char *argv[])
{
char in[255];
int r,f;
sprintf(in,"ls",argv[1]);
r=system(in);
if(!r)
{
f=open("/tmp/log",O_RDONLY,0);
printf("OK!");
}
}

Во-первых, сразу видим, что буфер «in» статистический. Память для него выделена сразу же. Впоследствии это может привести к переполнению при работе с функцией sprintf():
Во-вторых, используется функция system(). Она, как известно, становится опасной, если ее аргументы не проверять. Тут  как раз такой случай. Можно передать строку, которая скомпрометирует целевую систему на действия нежелательные для администратора.
int baga(char *arg){
char *v;
int i,f;
v=(char*)malloc(sizeof(arg))
i=strlen(v);
if(i>10)
{
f=creat("/tmp/import/",0666);
}
else-
{
printf("Sorry. this prorgamm lol\n");
}
}



Уязвимость присутствует при создании временного файла «/tmp/exampl». Дело в том, что можно создать жёсткую ссылку “/tmp/exampl” на какой-нибудь другой файл, и произойдет “конкуренция доступа к каталогу tmp”.
int main(int argc, char *argv[])
{
char v[100];
if(argc>1)
{
strcpy(v,argv[1]);
}
else
{
printf("No symbol\n");
}
}

Тут присутствует самое банальное переполнение буфера v []. Его размер 50, а функцией strcpy() мы можем переполнить буфер. Количество входных данных не проверяется.
int main(int argc, char *argv[])
{
if(argc>1)
{
printf("argv[0]");
}
else
{
printf("No symbol\n");
}

В этом примере, если аргументы больше единицы,  мы печатаем имя исполняемого файла… Невооружённым глазом видна ошибка форматной строки. Если в названии программы будут спецификаторы для printf(), то можно произвести кое- какие действия. Например, если название будет таким: “%x_%x_%x_name”, можно получить содержимое стека.
int main(int argc, char *argv[])
{
add=atoi(argv[1])
real=1000000000;
printf("Введите кол-во у.е. которые вы хотите добавить\n");
real+=add;
}

Тут есть место атаки класса integer overflow. О ней я уже говорил.
Переменная add не проверяется, т.е. все это может выйти за пределы диапазона integer.
А как же сетевые службы? Приведу опять же пример. Допустим, есть ftp демон. После подключения к нему запрашивается login:password. Так вот, они могут не проверяться. Их переполнение приводит к DoS.
Мы разобрали несколько опасных уязвимостей. Чтобы как то подкрепить полученные знания практикой, я покажу пример простенького сплоита. Он реализует  ошибку переполнения буфера. Если вы знакомы с ней, то знаете, что shellcode можно передать через стек, через кучу и через переменную окружения. Через стек, наверное, самый распространённый способ. Я покажу реализацию передачи шелл кода через переменную окружения.
Скажу сразу, что эксплоит лишь показательный. В реальных условиях маловероятно, что его можно применить. Но для примера он подходит. Объяснять технику написания сплоитов я не буду, т.к. это тема заслуживает отдельной книги.
Итак, возьмём уже известную нам программу:
//proga
int main(int argc, char *argv[])
{
char v[100];
if(argc>1)
{
strcpy(v,argv[1]);
}
else
{
printf("No symbol\n");
}
}

Вот так примерно можно передать шеллкод через переменную окружения:
#include
#include
#include
int main()
{
char shellcod[]=//Собсно сам код. Но его не пишу.
char *e[2]={shellcod,NULL};
char b[127];
int i,ret,*ptr;
ptr=(int*)(b);
ret=0xbfffffff-5-strlen(shellcod)-strlen("./proga");
for(i=0;i<127;i+=4)
{*ptr++=ret;
}
execle("./proga","proga",b,NULL,e);
}

Сначала мы подготавливаем буфер для внешней переменной, в которой будет шелл код:
char *e[2]={shellcod,NULL};
Потом буфер для переполнения:
ptr=(int*)(b);
Потом подсчитываем адрес шеллкода, по которому он будет после исполнения функции execle
ret=0xbfffffff-5-strlen(shellcod)-strlen("./proga");
Далее загружаем программу с переполняющим буфером и shell кодом во внешние переменные
execle("./proga","proga",b,NULL,e);
Произошло переполнение.
Вот и все.
OUTRO
Ошибки были, есть и будут. Этого не избежать. Вопрос только во времени и в вашей осведомленности в данной области.
Эта статья рассказывает лишь о малой доле «дыр» в исходных кодах. Если хотите продолжать, то нужно практиковаться: читать код, искать лазейки. Советую чаще посещать bugtrack, в нем обычно всегда описывают причину уязвимости и часто к описанию прилагается exploit.
Вот несколько толковых багтраков:
Надеюсь вы узнали что то новое и не потратили время зря. Удачи!

StraNger aka reaL StraNger

Получение информации о файле, кодинг под linux

Что то давненько я про кодинг не писал.
Сегодня я приведу еще один пример на тему программирования в Linux.
На этот раз мы разберем такую вещь как получения информации о файле.
Для этих целей существует вызов Stat.
Он читает информацию о нужном файле.
Синтаксис таков:

stat(char *file,strict stat fbuf);

Возвращает -1 при ошибке.

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

------------------------------------------------------
#include [stdio.h]
#include [sys/stat.h]// необходимый заголовочный файл
int main(int ac,char *ag[])
{
struct stat buffer; // структура для хранения инфы
if(ac!=1) если кол во аргументов не 1 то
{
if(stat(ag[1],&buffer)!=-1) //если не произошла ошибка то
printf("uid: %d\n",buffer.st_uid); //вывод
else
printf("Не коректное имя файла\n"); // если ошибка
}
else
printf("Пожауйста введите необходимые аргументы\n");
}

---------------------------------------------------------------------------
Думаю все ясно.
Можно вывести более подробную информацию, вот некоторые поля структуры stat:

st_mode - тип, права доступа.
st_atime - время последнего доступа.
st_size - размер.
st_uid - юид юзера.
st_gid - идентификатор группы пользователей.

Кстати, если ввести команду ls -l то в списках можно увидеть как раз результат работы вызова stat.

Copyleft (Ɔ); StraNger ideas