มลพิษของหมู่เกาะคานารีถูกบันทึกไว้อย่างไร?


11

แฟล็ก GCC -fstack-protector แฟล็กเปิดใช้งานการใช้ canaries สแต็กสำหรับการป้องกันโอเวอร์โฟลว์สแต็ก การใช้งานค่าสถานะนี้ตามค่าเริ่มต้นมีความโดดเด่นมากขึ้นในปีที่ผ่านมา

หากแพ็คเกจถูกคอมไพล์ด้วย -fstack-protector และเราล้นบัฟเฟอร์ในโปรแกรมเราก็น่าจะได้รับข้อผิดพลาดเช่น:

*** buffer overflow detected ***: /xxx/xxx terminated

อย่างไรก็ตาม "ใคร" เป็นผู้รับผิดชอบข้อความผิดพลาดเหล่านี้? ข้อความเหล่านี้ถูกบันทึกไว้ที่ไหน? syslog daemon เลือกข้อความเหล่านี้หรือไม่

คำตอบ:


10

สแต็คยอดเยี่ยมถูกตรวจพบโดยซึ่งเป็นส่วนหนึ่งของlibssp gccมันพยายามอย่างหนักมากที่จะส่งข้อความไปยังเทอร์มินัลและถ้ามันล้มเหลวก็จะล็อกเข้าสู่ระบบบันทึก - ดังนั้นในทางปฏิบัติคุณจะเห็นข้อความบัฟเฟอร์ล้นในบันทึกสำหรับ daemons และโปรแกรม GUI

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


1
ผมขอยกตัวอย่างที่เป็นรูปธรรมเพื่อสำรวจคำอธิบายนี้ต่อไป ลองเลือก nginx สำหรับตัวอย่างนี้ ฉันได้รวบรวม nginx พร้อมกับหมู่เกาะคานารี เมื่อฉันเรียกใช้ nginx มันจะเริ่มต้นกระบวนการ แต่จะไม่ส่งผลอะไรต่อเชลล์ แต่ข้อความใด ๆ จะถูกบันทึกไว้ในไฟล์บันทึกหลายไฟล์ หาก nginx ตรวจพบการทับซ้อนของสแต็กlibsspจะส่งข้อความออกโดยเอาต์พุต stderr ที่ใช้โดย nginx จากนั้นlibsspอาจพยายามออกจากกระบวนการ (หรือกระบวนการลูกสำหรับ nginx) หาก "ไม่จำเป็นต้อง" ทำให้แอปพลิเคชันขัดข้องตัวบันทึกการออกจากระบบที่ผิดปกติจะไม่มารับสิ่งนี้นี่เป็นการตีความที่ถูกต้องหรือไม่
aedcv

ไม่มาก - มันจะพยายามทำให้แอปพลิเคชันขัดข้องโดยใช้__builtin_trap()ก่อนจากนั้นหากล้มเหลวพยายามที่จะก่อให้เกิดการละเมิดกลุ่มและเฉพาะในกรณีที่ล้มเหลวออกจากสถานะ 127
สตีเฟ่น Kitt

ส่วนของการพิมพ์ข้อความไม่ได้รับประกันความสำเร็จได้ดีไปกว่าทางออกผ่านวิธีการให้ผลผลิตหลัก (เช่นabort())
maxschlepzig

7

การแจกจ่าย Linux สมัยใหม่เช่น CentOS / Fedora ตั้งค่า daemon การจัดการความผิดพลาด (เช่นsystemd-coredumpหรือabortd) ตามค่าเริ่มต้น

ดังนั้นเมื่อโปรแกรมของคุณสิ้นสุดลงในรูปแบบที่ผิดปกติ (segfault, ข้อยกเว้นที่ไม่ได้ตรวจสอบ, ยกเลิก, คำแนะนำที่ผิดกฎหมาย ฯลฯ ) เหตุการณ์นี้ได้รับการลงทะเบียนและบันทึกโดย daemon นั้น ดังนั้นคุณจะพบข้อความบางส่วนในวารสารระบบและอาจอ้างอิงไปยังไดเรกทอรีที่มีรายละเอียดเพิ่มเติมบางอย่าง (เช่นไฟล์หลักบันทึก ฯลฯ )

ตัวอย่าง

$ cat test_stack_protector.c 
#include <string.h>

int f(const char *q)
{
  char s[10];
  strcpy(s, q);
  return s[0] + s[1];
}

int main(int argc, char **argv)
{
  return f(argv[1]);
}

รวบรวม:

$ gcc -Wall -fstack-protector test_stack_protector.c -o test_stack_protector

ดำเนินการ:

$ ./test_stack_protector 'hello world'
*** stack smashing detected ***: ./test_stack_protector terminated
======= Backtrace: =========
/lib64/libc.so.6(+0x7c8dc)[0x7f885b4388dc]
/lib64/libc.so.6(__fortify_fail+0x37)[0x7f885b4dfaa7]
/lib64/libc.so.6(__fortify_fail+0x0)[0x7f885b4dfa70]
./test_stack_protector[0x400599]
./test_stack_protector[0x4005bd]
/lib64/libc.so.6(__libc_start_main+0xea)[0x7f885b3dc50a]
./test_stack_protector[0x40049a]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:28 1151979                            /home/juser/program/stackprotect/test_stack_protector
00600000-00601000 r--p 00000000 00:28 1151979                            /home/juser/program/stackprotect/test_stack_protector
00601000-00602000 rw-p 00001000 00:28 1151979                            /home/juser/program/stackprotect/test_stack_protector
0067c000-0069d000 rw-p 00000000 00:00 0                                  [heap]
7f885b1a5000-7f885b1bb000 r-xp 00000000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b1bb000-7f885b3ba000 ---p 00016000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b3ba000-7f885b3bb000 r--p 00015000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b3bb000-7f885b3bc000 rw-p 00016000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b3bc000-7f885b583000 r-xp 00000000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b583000-7f885b783000 ---p 001c7000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b783000-7f885b787000 r--p 001c7000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b787000-7f885b789000 rw-p 001cb000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b789000-7f885b78d000 rw-p 00000000 00:00 0 
7f885b78d000-7f885b7b4000 r-xp 00000000 00:28 945341                     /usr/lib64/ld-2.25.so
7f885b978000-7f885b97b000 rw-p 00000000 00:00 0 
7f885b9b0000-7f885b9b3000 rw-p 00000000 00:00 0 
7f885b9b3000-7f885b9b4000 r--p 00026000 00:28 945341                     /usr/lib64/ld-2.25.so
7f885b9b4000-7f885b9b6000 rw-p 00027000 00:28 945341                     /usr/lib64/ld-2.25.so
7ffc59966000-7ffc59987000 rw-p 00000000 00:00 0                          [stack]
7ffc5999c000-7ffc5999f000 r--p 00000000 00:00 0                          [vvar]
7ffc5999f000-7ffc599a1000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
zsh: abort (core dumped)  ./test_stack_protector 'hello world'

สถานะทางออกคือ 134 ซึ่งเท่ากับ 128 +6 เช่น 128 บวกกับหมายเลขยกเลิก

วารสารระบบ:

Oct 16 20:57:59 example.org audit[17645]: ANOM_ABEND auid=1000 uid=1000 gid=1000 ses=3 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 pid=17645 comm="test_stack_prot" exe="/home/juser/program/stackprotect/test_stack_protector" sig=6 res=1
Oct 16 20:57:59 example.org systemd[1]: Started Process Core Dump (PID 17646/UID 0).
Oct 16 20:57:59 example.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=systemd-coredump@21-17646-0 comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Oct 16 20:57:59 example.org systemd-coredump[17647]: Process 17645 (test_stack_prot) of user 1000 dumped core.

                           Stack trace of thread 17645:
                           #0  0x00007f885b3f269b raise (libc.so.6)
                           #1  0x00007f885b3f44a0 abort (libc.so.6)
                           #2  0x00007f885b4388e1 __libc_message (libc.so.6)
                           #3  0x00007f885b4dfaa7 __fortify_fail (libc.so.6)
                           #4  0x00007f885b4dfa70 __stack_chk_fail (libc.so.6)
                           #5  0x0000000000400599 f (test_stack_protector)
                           #6  0x00000000004005bd main (test_stack_protector)
                           #7  0x00007f885b3dc50a __libc_start_main (libc.so.6)
                           #8  0x000000000040049a _start (test_stack_protector)
Oct 16 20:57:59 example.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=systemd-coredump@21-17646-0 comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Oct 16 20:58:00 example.org abrt-notification[17696]: Process 17645 (test_stack_protector) crashed in __fortify_fail()

นั่นหมายความว่าคุณจะได้รับการเข้าสู่ระบบจากauditdภูตตรวจสอบและsystemd-coredumpจัดการความผิดพลาด

เพื่อตรวจสอบว่า daemon การจัดการความผิดพลาดได้รับการกำหนดค่าหรือไม่คุณสามารถตรวจสอบ/procได้เช่น:

$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %e

(ทุกอย่างทดสอบกับ Fedora 26, x86-64)


1
ฉันดีใจมากที่คุณโพสต์ตัวอย่างนี้ นกขมิ้นถูกวางในสถานที่โดย gcc (โปรดแก้ไขให้ฉันด้วยถ้าฉันผิด) ฉันคิดว่าสิ่งที่เกิดขึ้นคือ: gcc ใส่ "รหัสพิเศษ" ในโปรแกรมเพื่อใช้งานฟังก์ชั่นนกขมิ้น ระหว่างการประมวลผลและก่อนที่ฟังก์ชันจะส่งคืนค่าจะถูกตรวจสอบ หากมีมลพิษโปรแกรมจะแสดงข้อความ "ตรวจพบยอดเยี่ยม" และเกิดข้อผิดพลาด ข้อผิดพลาดนี้เกิดขึ้นจากระบบปฏิบัติการรับรู้ข้อผิดพลาดของการแบ่งกลุ่มและพิมพ์แผนที่ย้อนหลังและแผนที่หน่วยความจำที่คุณโพสต์ สุดท้ายระบบปฏิบัติการจะฆ่าแอปพลิเคชันสร้างคอร์ดัมพ์และบันทึกลงใน sys journal
aedcv

@aedcv นี่เป็นเรื่องราวที่ค่อนข้างชัดเจน - ยิ่งมีความแม่นยำมากขึ้น: การตรวจสอบโค้ดที่ยอดเยี่ยมเรียกการสแต็กabort()ซึ่งให้สัญญาณการยกเลิกนั่นคือไม่มีการแบ่งส่วนที่ผิดพลาดเกิดขึ้น เป็นเพียงตัวจัดการสัญญาณเริ่มต้นสำหรับความล้มเหลวในการยกเลิก / การแบ่งเซ็กเมนต์ ฯลฯ ให้ผลการดำเนินการเดียวกัน: เขียนหลักและออกจากกระบวนการด้วยสถานะออกไม่เท่ากับศูนย์ที่เข้ารหัสหมายเลขสัญญาณ การเขียนหลักจะทำโดย kernel /proc/.../core_patternและพฤติกรรมของมันคือการกำหนดค่าผ่านทาง ในตัวอย่างข้างต้นผู้ช่วยพื้นที่ผู้ใช้มีการกำหนดค่าและเรียกว่า เคอร์เนลยังทริกเกอร์การตรวจสอบ
maxschlepzig

@maxschlepzig ไม่มากabort()รหัส SSP ใช้__builtin_trap()(แต่ผลเหมือนกัน)
Stephen Kitt

1
@StephenKitt เอาล่ะลองดู stack trace ในตัวอย่างด้านบน คุณเห็นอย่างชัดเจนว่าabort()มีการเรียก
maxschlepzig

1
@maxschlepzig แน่นอน แต่นั่นคือรายละเอียดการใช้งาน (รหัส GCC ใช้__builtin_trap()เพื่อหลีกเลี่ยงการพึ่งพาอย่างชัดเจนabort()) การแจกแจงอื่นมีการติดตามสแต็กที่แตกต่างกัน
Stephen Kitt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.