ไม่คาดหวังสิทธิ์เรียกใช้จาก mmap เมื่อไฟล์ประกอบรวมอยู่ในโครงการ


94

ฉันกระแทกหัวของฉันกับกำแพงด้วยสิ่งนี้

ในโครงการของฉันเมื่อฉันจัดสรรหน่วยความจำด้วยmmapการจับคู่ ( /proc/self/maps) แสดงให้เห็นว่ามันเป็นพื้นที่ที่สามารถอ่านได้และปฏิบัติการได้แม้ว่าฉันจะขอหน่วยความจำที่สามารถอ่านได้เท่านั้น

หลังจากตรวจสอบ strace (ซึ่งดูดี) และการดีบักอื่น ๆ ฉันสามารถระบุสิ่งเดียวที่ดูเหมือนว่าจะหลีกเลี่ยงปัญหาแปลก ๆ นี้ได้: การลบไฟล์ชุดประกอบออกจากโครงการและปล่อยให้เฉพาะ C. บริสุทธิ์ (อะไร?!)

ดังนั้นนี่คือตัวอย่างที่แปลกประหลาดของฉันฉันกำลังทำงานบน Ubunbtu 19.04 และ gcc เริ่มต้น

หากคุณรวบรวมเป้าหมายไฟล์ที่เรียกใช้งานได้ด้วยไฟล์ ASM (ซึ่งว่างเปล่า) mmapจะส่งคืนพื้นที่ที่สามารถอ่านได้และสามารถเรียกใช้งานได้หากคุณสร้างโดยไม่ต้องดำเนินการอย่างถูกต้อง ดูผลลัพธ์/proc/self/mapsที่ฉันได้ฝังในตัวอย่างของฉัน

example.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

example.s : เป็นไฟล์ว่าง!

เอาท์พุท

ด้วยรุ่น ASM รวม

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

ไม่มี ASM เวอร์ชันรวม

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

5
นี่มันแปลกจริง ๆ
fuz

6
ฉันจัดการเพื่อทำซ้ำสิ่งนี้ด้วยเพียง GCC (ไม่มี CMake) ดังนั้นฉันจึงแก้ไขคำถามเพื่อทำให้ตัวอย่างมีน้อยที่สุด
Joseph Sible-Reinstate Monica


คุณอาจจะถูกต้องส่วนหนึ่งของคำตอบที่เขาจะต้องมีประมาณ READ_IMPLIES_EXEC ตัว
Ben Hirschberg

-Wa,--noexecstackประกอบไฟล์ที่มาของคุณด้วย
jww

คำตอบ:


90

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

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

หากคุณรวบรวมไฟล์นั้นพร้อมกับ.sไฟล์เปล่าคุณจะเห็นว่ามันเปิดใช้งาน แต่ไม่มีไฟล์ใดไฟล์หนึ่งจะถูกปิดใช้งาน ค่าเริ่มต้นของเรื่องนี้มาจากเอลฟ์ meta-ข้อมูลในไบนารีของคุณ readelf -Wl exampleDo คุณจะเห็นบรรทัดนี้เมื่อคุณรวบรวมโดยไม่มี.sไฟล์ว่างเปล่า:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

แต่อันนี้เมื่อคุณคอมไพล์ด้วย:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

หมายเหตุแทนเพียงRWE RWเหตุผลสำหรับสิ่งนี้คือตัวเชื่อมโยงสันนิษฐานว่าไฟล์ชุดประกอบของคุณต้องการ read-implies-exec เว้นแต่จะมีการบอกอย่างชัดเจนว่าไม่ทำและหากส่วนใดส่วนหนึ่งของโปรแกรมของคุณต้องการ read-implies-exec ก็จะเปิดใช้งานสำหรับโปรแกรมทั้งหมดของคุณ . แอสเซมบลีไฟล์ที่คอมไพล์ GCC บอกว่าไม่จำเป็นต้องใช้ไฟล์นี้กับบรรทัดนี้ (คุณจะเห็นสิ่งนี้หากคุณคอมไพล์ด้วย-S):

        .section        .note.GNU-stack,"",@progbits

ใส่บรรทัดexample.sนั้นแล้วมันจะทำหน้าที่บอก linker ว่าไม่ต้องการด้วยเช่นกันและโปรแกรมของคุณจะทำงานตามที่คาดไว้


13
อึศักดิ์สิทธิ์นั่นเป็นค่าเริ่มต้นที่แปลก ฉันเดาว่า toolchain มีอยู่ก่อน noexec และการทำให้ noexec เป็นค่าเริ่มต้นอาจทำให้ทุกอย่างเสียหาย ตอนนี้ฉันอยากรู้ว่าแอสเซมบลีอื่น ๆ เช่น NASM / YASM สร้าง.oไฟล์อย่างไร! แต่อย่างไรก็ตามฉันเดาว่านี่เป็นกลไกที่gcc -zexecstackใช้และทำไมมันจึงไม่เพียง แต่สร้างสแต็กเท่านั้น
Peter Cordes

23
@ Peter - นั่นทำไมโครงการเช่น Botan ลับ ++ และ OpenSSL -Wa,--noexecstackซึ่งใช้ประกอบเพิ่ม ฉันคิดว่ามันเป็นขอบคมที่น่ารังเกียจมาก การสูญเสีย nx-stack แบบเงียบ ๆ ควรเป็นจุดอ่อนด้านความปลอดภัย คน Binutil ควรแก้ไข
jww

14
@ jww มันเป็นปัญหาด้านความปลอดภัยแปลกที่ไม่มีใครรายงานมาก่อน
Ben Hirschberg

4
+1 แต่คำตอบนี้จะดีกว่ามากถ้าความหมาย / ตรรกะของบรรทัด.note.GNU-stack,"",@progbitsถูกอธิบาย - ตอนนี้มันเป็นสีทึบซึ่งเทียบเท่ากับ "สายอักขระวิเศษนี้ทำให้เกิดเอฟเฟกต์นี้" แต่สตริงดูเหมือนชัดเจนว่ามันมีอะไรบางอย่าง อรรถศาสตร์
mtraceur

33

เป็นอีกทางเลือกหนึ่งในการแก้ไขไฟล์ชุดประกอบของคุณด้วยชุดคำสั่งส่วนเฉพาะของ GNU คุณสามารถเพิ่ม-Wa,--noexecstackบรรทัดคำสั่งสำหรับสร้างไฟล์ประกอบ ตัวอย่างเช่นดูวิธีที่ฉันทำใน musl's configure:

https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a

ฉันเชื่อว่าอย่างน้อยบางรุ่นของ clang ที่มี integrated-assembler อาจต้องการให้ส่งเป็น--noexecstack(โดยไม่ต้อง-Wa) ดังนั้นสคริปต์กำหนดค่าของคุณควรตรวจสอบทั้งสองและดูว่าได้รับการยอมรับหรือไม่

คุณยังสามารถใช้-Wl,-z,noexecstackเวลาลิงก์ (เป็นLDFLAGS) เพื่อให้ได้ผลลัพธ์เดียวกัน ข้อเสียของสิ่งนี้คือมันไม่ช่วยถ้าโครงการของคุณผลิต.aไฟล์ไลบรารีแบบคงที่ ( ) เพื่อใช้งานโดยซอฟต์แวร์อื่นเนื่องจากคุณจะไม่ควบคุมตัวเลือกลิงค์เวลาเมื่อใช้งานโดยโปรแกรมอื่น


1
อืม ... ฉันไม่รู้ว่าคุณเป็น Rich Felker ก่อนที่จะอ่านโพสต์นี้ ทำไมชื่อที่แสดงของคุณไม่เป็น dalias
SS Anne
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.