2016-04-10 8 views
1

У меня очень рудиментарное понимание C (хотя я вообще понимаю понятия программирования). У меня есть задание создать переполнение буфера, которое дает что-то (например, доступ к несанкционированной области, бесплатные деньги и т. Д.), А не просто сбой программы.Как запустить код с разбитым стеклом?

Я пробовал буферы различного размера и всегда могу «свернуть» программу, но я не могу заставить ее запускать какой-либо код (т. Е./Bin/su). Подхожу ли я к этому неправильно?

Вот код:

#include <stdio.h> 
#include <stdlib.h> 
#include <float.h> 
#include <limits.h> 
#include <string.h> 
#define BUFSIZE 20 

int main() { 
    int month=12; 
    int day=31; 
    int year=2016; 
    int confirm = 0; 
    double dollars = 5.00; 
    char *sitenum="97871"; 
    char acctnum[BUFSIZE]; 

    printf("Welcome to the Acme AP-AR System. This is the Accounts Receivable module. \n"); 
    /* Gathering date information */  
    printf("Please enter the month of transaction as an integer value (2 digits). \n"); 
    printf("For example, July would be 07, December would be 12. Please input the month: "); 
    for (;;) { /* Start of month input validation loop */ 
    scanf("%d", &month); 
     if(month>=1 && month<=12) { 
      printf("Validated. \n"); 
      break;   
     } 
     else { 
      printf("Please enter a value between 1 and 12! \n"); 
      continue; 
     } 
    } /* End of month input validation loop */ 
    printf("\nPlease enter the day of transaction as an integer value (2 digits). \n"); 
    printf("For example, the 3rd would be 03, the 25th would be 25. Please input the day: "); 
    for (;;) { /* Start of day input validation loop */ 
    scanf("%d", &day); 
     if(day>=1 && day<=31) { 
      printf("Validated. \n"); 
      break;   
     } 
     else { 
      printf("Please enter a value between 1 and 31! \n"); 
      continue; 
     } 
    } /* End of day input validation loop */ 

    /* Gathering sender account number */ 
    printf("\nPlease enter the sender Account Number: "); 
    scanf("%s", acctnum); 

    /* Gathering transaction amount */ 
    printf("\nPlease enter the USD amount (including cents) received: $ "); 
    scanf("%lf", &dollars); 

    /* Confirming data entry */ 
    printf("\nTransaction information.\n Date: %d-%d-%d \n", month,day,year); 
    printf("Account: %s-%s \n", sitenum, acctnum); 
    printf(" Amount: $ %.2lf \n", dollars); 
    printf("\nProcess transaction information? (Yes=1/No=0) "); 
    for (;;) { /* Start of confirmation validation loop */ 
    scanf("%d", &confirm); 
     if(confirm==1) { 
      printf("Transaction processed. \n"); 
      break;   
     } 
     else { 
      printf("Transaction voided! \n"); 
      break; 
     } 
    } /* End of confirmation validation loop */ 

    return (EXIT_SUCCESS); 
} 

При выполнении, если ввести 25 символов в день месяца, программа будет продолжаться до конца. Только после того, как последний вход завершится с ошибкой разбиения стека. Боюсь, я пытаюсь сделать что-то, что не может быть сделано, но день (буквально, за последние 8 часов) поисковых запросов Google не привел пример, который я смог использовать.

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

+0

я добавил еще немного примечания в моем ответе, чтобы решить, что я считаю фактическим намерением задания. Надеюсь, это поможет. – Aenimated1

ответ

0

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

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

EDIT: Кстати, я не думаю, что назначение предназначалось для фактического выполнения произвольного кода. Я предполагаю, что на основе кода, который вы опубликовали, вы должны просто перезаписать часть стека, чтобы было похоже, что вы обращаетесь к другому «sitenum». Это определенно возможно, так как указатель sitenum будет храниться после acctnum в стеке (по крайней мере, обычно). Поэтому, если вы тщательно обработаете свой буфер, вы можете изменить указатель sitenum, чтобы указать куда-то еще. Например, если предположить, что указатель sitenum сразу после acctnum в стеке), вы можете ввести 1 дополнительный символ в acctnum, а нулевой завершающий символ перезапишет младший значащий байт указателя sitenum, который, скорее всего, укажет на другое место тогда.

На мой взгляд, это страшное задание, потому что 1) стек может быть устроен по-разному на основе большого количества факторов, и 2) большинство современных сред разработки по умолчанию будут добавлять проверки выполнения во избежание такого типа стека коррупция. Например, в MS Visual C++ вам нужно будет отключить функции проверки базовой проверки и проверки безопасности буфера, чтобы избежать исключения.

В любом случае, надеюсь, что это поможет.

+0

Это мой выбор, что делать с переполнением. Предполагается, что назначение должно имитировать что-то полезное (например, ATM), а переполнение дает что-то непреднамеренное (свободные деньги). Я решил имитировать привилегированный доступ, но доступ к другому сайту может также работать. Предполагается, что он будет запущен в 32-разрядной среде Kali Linux. Я не могу заставить его рушиться там (только на Ubuntu), поэтому я подозреваю, что на Kali есть некоторые дополнительные защиты стека (канарейка и т. Д.). Я думаю, что это тоже ужасное задание. – ernieg92

+0

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

+0

Нет ничего плохого в коде, который вы опубликовали как таковой. Но этого недостаточно, чтобы просто разбивать стек с переполнением произвольных данных. Вам нужно будет действительно знать содержимое стека, чтобы вы знали, что вы переписываете. В частности, если вы хотите использовать уязвимость для запуска другого кода, вам нужно точно знать, где адрес возврата хранится в фрейме стека, и вам нужно тщательно сконструировать «номер учетной записи», который перезаписывает обратный адрес. – Aenimated1

0

Вот простой пример перезаписывания обратного адреса в стеке для выполнения другой функции (после этого произойдет кратковременное сбой). Работает в Windows VS2015 на x86.

#include "stdafx.h" 

void hello() 
{ 
    printf("hello world!\n"); 
} 
void run(int a) 
{ 
    int * ret = &a; 
    --ret; // stack grows downward on x86 
    *ret = (int)hello; 
} 
int main() 
{ 
    int a = 42; 
    run(a); 
    printf("this won't print\n"); 
} 
+0

Я скомпилировал этот (с 32-разрядным коммутатором) и произвел ошибку сегментации после печати «привет мир!». - это ожидаемый результат? Если да, то мне нужно, чтобы математический расчет превышал буфер, я думаю. – ernieg92

+0

Да, это то, что я имел в виду, «затем быстро сработает». –

0

Вот еще один простой пример (VS2015/x86), который сохраняет адрес возврата, а затем после привета() выполняются, поместит адрес возврата к основным() обратно в стек. Обратите внимание, что сначала он начинается с локальной переменной, объявленной в run(), а не в качестве аргумента. Это сводится к пониманию, какой порядок обратного адреса, аргументы переданы, направление стека, и где начинается текущий стек стека.Вы, вероятно, получите уведомление о проваливать проверки во время выполнения в вашей среде отладчика после выполнения, но вы должны увидеть это напечатанным на консоль:

привет мир
главного

#include "stdafx.h" 

int saveret; 

void hello() 
{ 
    int a = 43; 
    printf("hello world!\n"); 
    // put saved return address to main() back on stack 
    int * ret = &a; 
    ret += 4; 
    *ret = saveret; 
} 
void run() 
{ 
    int a = 42; 
    int * ret = &a; 
    ret += 4; // stack grows downward on x86 
    saveret = (int)*ret; 
    *ret = (int)hello; 
} 
int main() 
{ 
    run(); 
    printf("main\n"); 
} 
+0

Итак, этот не сработал, но он только напечатал «основной» (я выполняю на Linux и должен заменить «stadafx.h» на «stdio.h», так что это может быть и почему). Ни один из ваших примеров не требует ввода, но я думаю, что вижу, как это работает. Мне нужно, чтобы несколько переменных повторяли достаточно времени, чтобы вытащить стопку. – ernieg92

 Смежные вопросы

  • Нет связанных вопросов^_^