Rate this post

Buffer Overflow được đặc trưng bởi việc ghi đè lên các đoạn bộ nhớ của quá trình mà lẽ ra không bao giờ được sửa đổi một cách cố ý hoặc vô ý. Việc ghi đè các giá trị của IP (Instruction Pointer), BP (Base Pointer) và các register khác gây ra các ngoại lệ, lỗi phân đoạn và các lỗi khác xảy ra. Thông thường những lỗi này kết thúc việc thực thi ứng dụng theo cách không mong muốn. Buffer Overflow xảy ra khi chúng ta thao tác trên bộ đệm kiểu char.

Các bài viết liên quan:

Buffer Overflow có thể bao gồm làm tràn ngăn xếp [Tràn ngăn xếp] hoặc tràn đống [Tràn đống]. Chúng tôi không phân biệt giữa hai điều này trong bài viết này để tránh nhầm lẫn.

Các ví dụ dưới đây được viết bằng ngôn ngữ C trong hệ thống GNU / Linux trên kiến ​​trúc x86.

Các ví dụ

ví dụ 1

#include <stdio.h>
int main(int argc, char **argv)
{
char buf[8]; // buffer for eight characters
gets(buf); // read from stdio (sensitive function!)
printf("%s\n", buf); // print out data stored in buf
return 0; // 0 as return value
}

Ứng dụng rất đơn giản này đọc từ đầu vào chuẩn một mảng các ký tự và sao chép nó vào bộ đệm kiểu char. Kích thước của bộ đệm này là tám ký tự. Sau đó, nội dung của bộ đệm được hiển thị và ứng dụng sẽ thoát.

Biên dịch chương trình:

user@spin ~/ $ gcc bo-simple.c -o bo-simple
/tmp/ccECXQAX.o: In function `main':
bo-simple.c:(.text+0x17): warning: the `gets' function is dangerous and
should not be used.

Ở giai đoạn này, ngay cả trình biên dịch cũng gợi ý rằng hàm get () không an toàn.

Ví dụ sử dụng:

user@spin ~/ $ ./bo-simple // program start
1234 // we eneter "1234" string from the keyboard
1234 // program prints out the conent of the buffer
user@spin ~/ $ ./bo-simple // start
123456789012 // we eneter "123456789012"
123456789012 // content of the buffer "buf" ?!?!
Segmentation fault // information about memory segmenatation fault

Chúng tôi quản lý (không) may mắn để thực hiện hoạt động bị lỗi bởi chương trình và khiến nó thoát ra bất thường.

Phân tích vấn đề:

Chương trình gọi một hàm, hoạt động trên bộ đệm kiểu char và không kiểm tra việc làm tràn kích thước được gán cho bộ đệm này. Do đó, có thể cố ý hoặc vô ý lưu trữ nhiều dữ liệu hơn trong bộ đệm, điều này sẽ gây ra lỗi. Câu hỏi sau được đặt ra: Bộ đệm chỉ lưu trữ tám ký tự, vậy tại sao hàm printf () lại hiển thị mười hai ?. Câu trả lời đến từ tổ chức bộ nhớ tiến trình. Bốn ký tự làm Buffer Overflow cũng ghi đè giá trị được lưu trữ trong một trong các thanh ghi, điều này cần thiết cho việc trả về hàm đúng. Tính liên tục của bộ nhớ dẫn đến việc in ra dữ liệu được lưu trữ trong vùng bộ nhớ này.

Ví dụ 2

#include <stdio.h>
#include <string.h>

void doit(void)
{
        char buf[8];

        gets(buf);
          printf("%s\n", buf);
}

int main(void)
{
        printf("So... The End...\n");
        doit();
        printf("or... maybe not?\n");

        return 0;
}

Ví dụ này tương tự như ví dụ đầu tiên. Ngoài ra, trước và sau hàm doit (), chúng ta có hai lệnh gọi hàm printf ().

Tổng hợp:

Compilation:

user@dojo-labs ~/buffer_overflow $ gcc example02.c -o example02
-ggdb
/tmp/cccbMjcN.o: In function `doit':
/home/user/owasp/buffer_overflow/example02.c:8: warning: the `gets'
function is dangerous and should not be used.

Usage example:
user@dojo-labs ~/buffer_overflow $ ./example02
So... The End...
TEST                   // user data on input
TEST                  // print out stored user data
or... maybe not?

Chương trình giữa hai lệnh gọi printf () được xác định hiển thị nội dung của bộ đệm, được lấp đầy bởi dữ liệu do người dùng nhập vào.

user@dojo-labs ~/buffer_overflow $ ./example02
So... The End...
TEST123456789
TEST123456789
Segmentation fault

Vì kích thước của bộ đệm đã được xác định (char buf [8]) và nó được lấp đầy bằng mười ba ký tự kiểu char, bộ đệm đã bị tràn.

Nếu ứng dụng nhị phân của chúng tôi ở định dạng ELF, thì chúng tôi có thể sử dụng chương trình objdump để phân tích nó và tìm thông tin cần thiết để khai thác Buffer Overflow.

Dưới đây là kết quả được tạo ra bởi objdump. Từ đầu ra đó, chúng ta có thể tìm địa chỉ, nơi printf () được gọi là (0x80483d6 và 0x80483e7).

user@dojo-labs ~/owasp/buffer_overflow $ objdump -d ./example02

 080483be <main>:
 80483be:       8d 4c 24 04             lea    0x4(%esp),%ecx
 80483c2:       83 e4 f0                and    $0xfffffff0,%esp
 80483c5:       ff 71 fc                pushl  0xfffffffc(%ecx)
 80483c8:       55                      push   %ebp
 80483c9:       89 e5                   mov    %esp,%ebp
 80483cb:       51                      push   %ecx
 80483cc:       83 ec 04                sub    $0x4,%esp
 80483cf:       c7 04 24 bc 84 04 08    movl   $0x80484bc,(%esp)
 80483d6:       e8 f5 fe ff ff          call   80482d0 <puts@plt>
 80483db:       e8 c0 ff ff ff          call   80483a0 <doit>
 80483e0:       c7 04 24 cd 84 04 08    movl   $0x80484cd,(%esp)
 80483e7:       e8 e4 fe ff ff          call   80482d0 <puts@plt>
 80483ec:       b8 00 00 00 00          mov    $0x0,%eax
 80483f1:       83 c4 04                add    $0x4,%esp
 80483f4:       59                      pop    %ecx
 80483f5:       5d                      pop    %ebp
 80483f6:       8d 61 fc                lea    0xfffffffc(%ecx),%esp
 80483f9:       c3                      ret
 80483fa:       90                      nop
 80483fb:       90                      nop

Nếu lệnh gọi thứ hai tới printf () thông báo cho quản trị viên về việc đăng xuất của người dùng (ví dụ: phiên đã đóng), thì chúng ta có thể cố gắng bỏ qua bước này và kết thúc mà không cần gọi tới printf ().

user@dojo-labs ~/buffer_overflow $ perl -e 'print "A"x12
  ."\xf9\x83\x04\x08"' | ./example02
  So... The End...
  AAAAAAAAAAAAu*.
  Segmentation fault

Ứng dụng đã kết thúc quá trình thực thi với lỗi phân đoạn, nhưng lệnh gọi thứ hai tới printf () không có chỗ đứng.

Một vài lời giải thích:

perl -e ‘print“ A ”x12.” \ xf9 \ x83 \ x04 \ x08 ”’ – sẽ in ra mười hai ký tự “A” và sau đó là bốn ký tự, thực tế là địa chỉ của lệnh chúng ta muốn thực thi. Tại sao lại là mười hai?

   8 // size of buf (char buf[8])
+  4 // four additional bytes for overwriting stack frame pointer
----
  12

Phân tích vấn đề:

Vấn đề giống như trong ví dụ đầu tiên. Không có quyền kiểm soát kích thước của bộ đệm được sao chép vào bộ đệm được khai báo trước đó. Trong ví dụ này, chúng tôi ghi đè lên thanh ghi EIP với địa chỉ 0x080483f9, thực tế là một lệnh gọi để ret vào giai đoạn cuối của quá trình thực thi chương trình.

Làm thế nào để sử dụng Buffer Overflow theo một cách khác?

Nói chung, việc khai thác các lỗi này có thể dẫn đến:

  • DoS
  • sắp xếp lại việc thực hiện các chức năng
  • thực thi mã (nếu chúng tôi có thể đưa mã shellcode, được mô tả trong tài liệu riêng)

Buffer Overflow được thực hiện như thế nào?

Những loại lỗi này rất dễ mắc phải. Trong nhiều năm, chúng là cơn ác mộng của lập trình viên. Vấn đề nằm ở các hàm C gốc, không quan tâm đến việc kiểm tra độ dài bộ đệm thích hợp. Dưới đây là danh sách các chức năng như vậy và nếu chúng tồn tại, các chức năng tương đương an toàn của chúng:

  • get () – \> fgets () – đọc ký tự
  • strcpy () – \> strncpy () – sao chép nội dung của bộ đệm
  • strcat () – \> strncat () – nối bộ đệm
  • sprintf () – \> snprintf () – điền vào bộ đệm với dữ liệu thuộc các loại khác nhau
  • (f) scanf () – đọc từ STDIN
  • getwd () – trả về thư mục làm việc
  • realpath () – trả về đường dẫn tuyệt đối (đầy đủ)

Sử dụng các hàm tương đương an toàn để kiểm tra độ dài bộ đệm bất cứ khi nào có thể. Cụ thể:

  • gets () – \> fgets ()
  • strcpy () – \> strncpy ()
  • strcat () – \> strncat ()
  • sprintf () – \> snprintf ()

Những chức năng không có giá trị tương đương an toàn nên được viết lại với các bước kiểm tra an toàn được triển khai. Thời gian dành cho việc đó sẽ có lợi trong tương lai. Hãy nhớ rằng bạn chỉ phải làm điều đó một lần.

Sử dụng các trình biên dịch có khả năng xác định các chức năng không an toàn, lỗi logic và kiểm tra xem bộ nhớ có bị ghi đè khi nào và ở đâu không.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Contact Me on Zalo
Call now