diff --git a/docs/.gitignore b/docs/.gitignore index 600c6c1..5c8a277 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,7 +1,7 @@ /* bibliography/texput.log !.gitignore -!document.pdf +document.pdf !document.tex !Makefile !original_template/ diff --git a/docs/bibliography/bibliography.bib b/docs/bibliography/bibliography.bib index 6e27e98..d136755 100644 --- a/docs/bibliography/bibliography.bib +++ b/docs/bibliography/bibliography.bib @@ -1,5 +1,3 @@ -%%INTRODUCTION - @report{ransomware_paloalto, institution = {Palo Alto Networks}, title = {Ransomware Threat Report 2022}, diff --git a/docs/chapters/annex.tex b/docs/chapters/annex.tex index 5b36a7a..5e1bb60 100644 --- a/docs/chapters/annex.tex +++ b/docs/chapters/annex.tex @@ -2,12 +2,10 @@ % ANEX %---------- -%M-> Mentioned putting some demos and PoCs here... % %Including bpftool commands here to be referenced. Is it a good idea? - \chapter* {Appendix A - Bpftool commands} \label{annex:bpftool_flags_kernel} \pagenumbering{gobble} % Las páginas de los anexos no se numeran \section*{eBPF-related kernel compilation flags} @@ -195,6 +193,15 @@ pop rbp # 5D jmp qword ptr [rip+0x0] # FF2500000000
+\end{lstlisting} -\end{lstlisting} \ No newline at end of file +\chapter* {Appendix D - Rootkit flow diagrams} \label{annex:flow_diagrams} +\pagenumbering{gobble} % Las páginas de los anexos no se numeran +\section*{Library injection via GOT hijacking} \label{annexsec:lib_injection} +\begin{figure}[htbp] + \centering + \includegraphics[width=15cm]{flow_lib_injection_compact.png} + \caption{Flow diagram of execution of a successful library injection.} + \label{fig:flow_lib_injection_compact} +\end{figure} \ No newline at end of file diff --git a/docs/chapters/chapter3.tex b/docs/chapters/chapter3.tex index d71d41d..7f7c408 100644 --- a/docs/chapters/chapter3.tex +++ b/docs/chapters/chapter3.tex @@ -26,7 +26,7 @@ Therefore, a malicious privileged eBPF program can access and modify other progr \section{Abusing tracing programs} eBPF tracing programs (kprobes, uprobes and tracepoints) are hooked to specific points in the kernel or in the user space, and call probe functions once the flow of execution reaches the instruction to which they are attached. This section details the main security concerns regarding this type of programs. -\subsection{Access to function arguments} +\subsection{Access to function arguments} \label{subsection:tracing_arguments} As we saw in section \ref{section:ebpf_prog_types}, tracing programs receive as a parameter those arguments with which the hooked function originally was called. These parameters are read-only and thus, in principle, they cannot be modified inside the tracing program (we will show this is not entirely true in section \ref{section:mem_corruption}). The next code snippets show the format in which parameters are received when using libbpf (Note that libbpf also includes some macros that offer an alternative format, but the parameters are the same). \begin{lstlisting}[language=C, caption={Probe function for a kprobe on the kernel function vfs\_write.}, label={code:format_kprobe}] diff --git a/docs/chapters/chapter4.tex b/docs/chapters/chapter4.tex index b571e17..1826144 100644 --- a/docs/chapters/chapter4.tex +++ b/docs/chapters/chapter4.tex @@ -89,6 +89,9 @@ In 2019, Jeff Dileo presented in DEFCON 27 the first technique to achieve arbitr Figure \ref{fig:rop_evil_ebpf_1} shows an overview on the process memory and the eBPF programs loaded. For this injection, we will use the stack scanning technique (section \ref{subsection:bpf_probe_write_apps}) using the arguments of a system call whose arguments are passed using the stack (sys\_timerfd\_settime, which receives two structs utmr and otmr). Therefore, a kprobe is attached to the system call, so that it can start to scan for the return address of the system call, which we know is the original value of register rip which was pushed into the stack (ret). +%TODO This figure needs a remodel. I tried to keep it simple to explain the main concepts on the technique described afterwards, but after writing the next section I realised it gets some things wrong: +% - It does not show .got and .plt sections. +% - It shows the RBP register in an incorrect place. \begin{figure}[htbp] \centering \includegraphics[width=15cm]{rop_evil_ebpf_1.jpg} @@ -176,11 +179,15 @@ This technique works both in compilers with low hardening fetaures by default (C For this research work, the rootkit is prepared to perform this attack on any process that makes use of either the system call sys\_openat or sys\_timerfd\_settime, which are called by the standard library glibc. +We will now describe the multiple exploitation stages for our technique. Appendix \ref{annexsec:lib_injection} shows a flow diagram with the complete process. + + \textbf{Stage 1: eBPF tracing and scan the stack}\\ We load and attach a tracepoint eBPF program at the \textit{enter} position of syscall sys\_timerfd\_settime. Firstly, we must ensure that the process calling the tracepoint is one of the processes to hijack. We will then proceed with the stack scanning technique, as we explained in section \ref{subsection:bpf_probe_write_apps}. In this case, we will take one of the syscall parameters and scan forward in the stack. For each iteration, we must check if the data at the stack corresponds to the saved return address of the PLT stub that jumps to glibc where the syscall sys\_timerfd\_settime is called. Figure \ref{fig:lib_stage1} shows an overview of how these call instructions relate each memory section. + \begin{figure}[htbp] \centering \includegraphics[width=13cm]{plt_got_glibc_flow.jpg} @@ -227,6 +234,10 @@ We analyse the jump instruction and, again, take the address at which it jumps. Once we ensured we reached the correct glibc function, we are now sure that the data we found at the stack is the return address of the PLT stub that jumped to glibc and called the syscall sys\_timerfd\_settime. Most importantly, we know the address of the GOT section which we want to overwrite. +Our rootkit also incorporates an alternative scanning technique for processes calling the syscall sys\_openat(). This technique enables to scan the stack even when the system call does not incorporate any arguments from the userspace (and thus we cannot take them from our eBPF tracing program to use them as a foothold in the stack). + +As we explained in section \ref{subsection:tracing_arguments}, tracepoint programs receive an struct pt\_regs pointer as an argument. We can take this struct and use the value of register rbp as our starting point for scanning the stack. As we can see on figures \ref{fig:plt_clang}, \ref{fig:plt_gcc} and \ref{fig:settime_glibc}, the PLT does not contain any function prologue (it does not modify the value of rsp) and the function at glibc does not change this value either. Therefore, in our eBPF program, since we are hooking the syscall at the beginning of its execution, the value of rbp will be the original frame pointer before calling the PLT, and therefore we can use it as our starting address for stack scan, proceeding to scan forward until we find the saved return address. + \textbf{Stage 2: Programming shellcode}\\ Once that we have the address of the GOT section, we need to prepare our shellcode to be injected into the process memory. We will overwrite the value at GOT and redirect the flow of execution to the address at which our shellcode is stored in memory. @@ -324,15 +335,12 @@ Once the shellcode is loaded at the code cave, eBPF can proceed to overwrite the Therefore, our rootkit will modify GOT using bpf\_probe\_write\_user() with the address of an static code cave for those programs compiled with Clang (Partial RELRO, no PIE), and use \textit{/proc//mem} for modifying GOT with the value of code cave found using \textit{/proc//maps} for those programs compiled using GCC (Full RELRO, PIE active). -\textbf{Second syscall, execution of the library}\\ +\textbf{Stage 5: Second syscall, execution of the library}\\ Once we have overwriten GOT with the address of our code cave, the next time the same syscall is called, the PLT stub will jump to our code cave and execute our shellcode. As instructed by it, the malicious library will be loaded and afterwards the flow of execution jumps back to the original glibc function. %Explain reverse shell? With respect to the malicious library, it forks the process (to keep the malicious execution in the background) and spawns a simple reverse shell which the attacker can use to execute remote commands. -%TODO INCLUDE A DIAGRAM OF OVERALL ATTACK -%TODO EXPLAIN ALTERNATIVE SCANNING TECHNIQUE USING PT_REGS STRUCT - diff --git a/docs/document.pdf b/docs/document.pdf deleted file mode 100644 index 9b3d2e0..0000000 Binary files a/docs/document.pdf and /dev/null differ diff --git a/docs/document.tex b/docs/document.tex index af91f82..7c0f2b2 100644 --- a/docs/document.tex +++ b/docs/document.tex @@ -114,6 +114,8 @@ hmargin=3cm font=small, } +%CUSTOM RULES +\chardef\_=`_ % FIGURES DESIGN diff --git a/docs/images/flow_lib_injection_compact.png b/docs/images/flow_lib_injection_compact.png new file mode 100644 index 0000000..b6888a0 Binary files /dev/null and b/docs/images/flow_lib_injection_compact.png differ diff --git a/src/common/constants.h b/src/common/constants.h index c15bb93..3f6eeb7 100644 --- a/src/common/constants.h +++ b/src/common/constants.h @@ -28,7 +28,7 @@ //LIBRARY INJECTION WITH ROP #define TASK_COMM_NAME_INJECTION_TARGET_TIMERFD_SETTIME "simple_timer" -#define CODE_CAVE_ADDRESS_STATIC 0x0000000000402e95 +#define CODE_CAVE_ADDRESS_STATIC 0x00000000004012c4 #define CODE_CAVE_SHELLCODE_ASSEMBLE_1 \ "\x55\x50\x51\x52\x53\x57\x56\ \xbf\x00\x20\x00\x00\x48\xbb" diff --git a/src/helpers/Makefile b/src/helpers/Makefile index 036f58c..7f8f68a 100644 --- a/src/helpers/Makefile +++ b/src/helpers/Makefile @@ -15,10 +15,10 @@ simple_timer: simple_timer.o gcc -g -o simple_timer simple_timer.o simple_open.o: simple_open.c $(HEADERS) - gcc -g -c simple_open.c + clang -g -c simple_open.c simple_open: simple_open.o - gcc -g -o simple_open simple_open.o + clang -g -o simple_open simple_open.o execve_hijack.o: execve_hijack.c $(HEADERS) gcc -g -c execve_hijack.c diff --git a/src/helpers/peda-session-simple_open.txt b/src/helpers/peda-session-simple_open.txt index 41ce7a4..5142e51 100644 --- a/src/helpers/peda-session-simple_open.txt +++ b/src/helpers/peda-session-simple_open.txt @@ -1,2 +1,2 @@ -break *(main+79) +break *(main+52) diff --git a/src/helpers/simple_open b/src/helpers/simple_open index 85936c3..7e3811d 100755 Binary files a/src/helpers/simple_open and b/src/helpers/simple_open differ diff --git a/src/helpers/simple_open.o b/src/helpers/simple_open.o index f40afa0..01b8e52 100644 Binary files a/src/helpers/simple_open.o and b/src/helpers/simple_open.o differ diff --git a/src/helpers/simple_timer b/src/helpers/simple_timer index 70ee125..5429874 100755 Binary files a/src/helpers/simple_timer and b/src/helpers/simple_timer differ diff --git a/src/helpers/simple_timer.o b/src/helpers/simple_timer.o index 0081e44..b55150a 100644 Binary files a/src/helpers/simple_timer.o and b/src/helpers/simple_timer.o differ