Yığın kanarya kirliliği nasıl kaydedilir?


11

Bayrak GCC -stack-koruyucu bayrağı, yığın taşması koruması için yığın kanaryalarının kullanılmasını sağlar. Bu bayrağın varsayılan olarak kullanımı son yıllarda daha belirgindir.

Bir paket -fstack-protector ile derlenmişse ve programda bir arabellek taşması durumunda, büyük olasılıkla aşağıdaki gibi bir hata alırız:

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

Ancak, bu hata iletilerinden "kim" sorumludur? Bu mesajlar nereye kaydedilir? Syslog arka plan programı bu mesajları alıyor mu?

Yanıtlar:


10

Yığın parçalanması, libsspparçası olan tarafından algılanır gcc. Bu çok çalışır bir terminale çıkışa mesajı ve sadece bu sistem günlüğüne log, başarısız olursa - yani pratikte size cinleri ve belki GUI uygulamaları için günlüklerinde mesajlar taşma tampon göreceksiniz.

'S çıkışı onun mesajının sonra, libsspuygulamayı kilitlenen dahil çıkmak için yollar, çeşitli çalışır; bu, anormal çıkış kaydedicilerinden biri tarafından yakalanabilir, ancak bu garanti edilmez.


1
Bu açıklamayı daha fazla araştırmanın bir yolu olarak somut bir örnek vereyim. Bu örnek için nginx'i seçelim. Nginx'i yığın kanaryalar ile derledim. Nginx'i çalıştırdığımda bir işlem başlatır ancak kabuğa hiçbir şey vermez. Bunun yerine, iletiler birden çok günlük dosyasına kaydedilir. Eğer nginx yığın parçalamayı tespit ederse libssp, mesajını nginx tarafından kullanılan stderr çıkışı ile gönderir. Ardından, libsspişlemden (veya nginx için alt işlemden) çıkmayı deneyebilir. Uygulamayı çökmesine "gerek yok" ise, anormal çıkış kaydedicileri bunu almayacaktır. Bu doğru bir yorum mu?
aedcv

Pek - bu olacak kullanarak, uygulamanın çökmesine deneyin __builtin_trap()statüsüne 127. ile çıkarken, bir kesimi ihlali kışkırtmaya çalışıyor, yani başarısız sonra eğer ilk ve başarısız yalnızca
Stephen KITT

Mesajların bir bölümünün yazdırılması, bir çekirdek sağlama yöntemi (örn. abort()) Yoluyla çıkıştan daha iyi bir başarı garantisi değildir .
maxschlepzig

7

CentOS / Fedora gibi modern Linux dağıtımları , varsayılan olarak bir kilitlenme arka plan programı (örn. systemd-coredumpVeya abortd) oluşturur.

Böylece, programınız anormal bir şekilde sonlandığında (segfault, yakalanmayan istisna, iptal, yasadışı talimat vb.) Bu olay o daemon tarafından kaydedilir ve kaydedilir. Böylece, sistem günlüğünde bazı iletiler ve bazı ek ayrıntılar içeren bir dizine (örneğin çekirdek dosya, günlükler, vb.) Bir referans bulabilirsiniz.

Misal

$ 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]);
}

Derleme:

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

Yürütme:

$ ./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'

Çıkış durumu, 128 + 6 olan 134'tür, yani 128 artı iptal sinyal numarası.

Sistem günlüğü:

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()

Yollarla Yani gelen giriş olsun auditddenetim cini vesystemd-coredump çökme işleyicisi.

Bir çökme işleme arka plan programının yapılandırılıp yapılandırılmadığını doğrulamak için şunları kontrol edebilirsiniz /proc:

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

(Fedora 26, x86-64'te test edilen her şey)


1
Bu örneği gönderdiğiniz için çok mutluyum. Kanaryalar gcc tarafından yerleştirilir. (Eğer yanılıyorsam lütfen düzeltin) Ne olacağını varsayıyorum: gcc kanarya işlevselliğini uygulamak için programa "ekstra kod" koyar; yürütme sırasında ve bir işlev dönmeden önce değer kontrol edilir; kirlenirse, program "yığın parçalanması algılandı" mesajını verir ve bir hata oluşturur. Bu hata işletim sistemi tarafından alınır, bir bölümleme hatasını tanır ve gönderdiğiniz geri iz ve bellek haritasını yazdırır. Son olarak, OS uygulamayı öldürür, bir çekirdek dökümü oluşturur ve sys günlüğüne kaydeder
aedcv

@aedcv, bu hemen hemen hikaye - daha kesin olmak gerekirse: abort()bir iptal sinyali veren kontrol kod çağrıları smashing yığını , yani devam eden bir segmentasyon hatası yoktur. Sadece iptal / segmentasyon hatası vb. İçin varsayılan sinyal işleyicileri aynı eylemi sağlar: çekirdeği yazın ve işlemden sinyal numarasını kodlayan eşit olmayan bir çıkış durumu ile çıkın. Çekirdek yazma çekirdek tarafından yapılır ve davranışı ile yapılandırılabilir /proc/.../core_pattern. Yukarıdaki örnekte, bir kullanıcı-alanı yardımcısı yapılandırılmış ve bu şekilde adlandırılmıştır. Çekirdek ayrıca denetimi de tetikler.
maxschlepzig

@maxschlepzig bu kadar değil abort(), SSP kodu kullanıyor __builtin_trap()(ancak etki aynı).
Stephen Kitt

1
@StephenKitt, yukarıdaki örnekte yığın izlemesine bir göz atın. Orada nasıl abort()çağırıldığını açıkça görüyorsunuz .
maxschlepzig

1
@maxschlepzig evet, elbette, ama bu bir uygulama detayı (GCC kodu, __builtin_trap()açık bir bağımlılığa sahip olmaktan kaçınmak için kullanır abort()). Diğer dağıtımlar farklı yığın izlerine sahiptir.
Stephen Kitt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.