Preface
Unixand classUnixoperating system providedptracesystem call support one process to control another process, often used for program debugging, analysis and monitoring tools, such asgdb,straceetc. Throughptracecan view and modify the internal state of the controlled process, so penetration attacks in injectionshellcodewhen it will also useptrace.This article introduces aLinuxusingptraceHidden injectionshellcodeand defense methods.
Background
different versions of operating systems have their own implementationsptracesystem call methods, this article only focuses onLinuxenvironment, so let's briefly explainLinuxbelowptracesystem call usage. First define the controlled process(tracer)and the controlled process(tracee),tracercan observe and controltraceeexecution flow, check and modifytraceememory and register contents, atraceecan only be associated withattach)atracer,atracercan be associated with multipletracee.It should be noted thatLinuxnexttraceeis actually a thread, each thread in a process containing multiple threads can be associated with its owntracer.AllptraceFunction is called through an interface function, the format is as follows:

long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
The first parameterrequestincludes the specific functions called, the meanings of the next three parameters are related to the first parameter, different functions need to set the corresponding parameters, detailed definitions can be found in the operating system documentation (man ptrace)。Figure1DemonstratestracerControl atraceeprocess.
Figure1 ptracecontrol flow
1) tracercallPTRACE_ATTACHfunction associated withtracee, totraceeSendSIGSTOPsignal, and callwaitpidwaittraceestate change;
2) WhentraceeThe state becomesSTOP,waitpidReturn;
3) tracercallPTRACE_SYSCALLfunction makestraceeEnter single-step execution state and callwaitpidwaittraceestate change;
4) repeat steps2)and steps3);
5) tracercallPTRACE_DETACHfunction makestraceeresume running, and disassociate.
steps3)intracercan check and modifytraceememory and register content, inject for penetration attacksshellcodeprovides the possibility, and the next step describes the exploitationptraceHidden injectionshellcodeTechnical details.
Three, Technology
to achieve hidden injectionshellcodeThe target needs to solve three problems:
1) shellcodewhere it is stored?
2) How to executeshellcode؟
3) How to avoid being easily discovered while runningshellcode؟
To solve the first problem, it is necessary to understandLinuxThe process's memory structure, as shown in the figure2as shown.
Figure2 LinuxProcess Memory Structure (x86,x86-64(similar to)
executecat /proc/<pid>/mapscan view the process<pid>of the memory structure, figure3istopThe process's memory structure.
Figure3 topProcess Memory Structure
The second field of each line indicates the attributes of the memory segment, including'x'with executable permissions. It is easiest to write toshellcodeThe location isMemory Mapping SegmentIt is possible to apply for an anonymous memory segment with the attributes ofrwxpand writeshellcodeThe pseudocode is as follows:
// Associate with tracee ptrace(PTRACE_ATTACH, tracee_pid, NULL, NULL); waitpid(tracee_pid, 0, 0) // Modify the system call to SYS_mmap and execute step by step, restore the original code after execution mem_addr = remote_mmap(tracee_pid, NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); // Write shellcode to the allocated memory segment poke_text(tracee_pid, (size_t) mem_addr, shellcode, SHELL_LEN);
The first problem has been solved:shellcodewhere it is stored, and then solve the second problem: how to executeshellcodeThis problem is relatively simple, modify the registerripismem_addrThen runtraceeThen it can be executedshellcodeThe pseudocode is as follows:
// Read the tracee registers and backup ptrace(PTRACE_GETREGS, tracee_pid, NULL, ®s); memcpy(&old_regs, ®s, sizeof(struct user_regs_struct)); // Modify rip to mem_addr (the address of shellcode) regs.rip = (u_int64_t) mem_addr; regs.rip += 2 // Set tracee registers ptrace(PTRACE_SETREGS, tracee_pid, NULL, ®s) // Execute shellcode, assuming the shellcode ends with a getpid system call for (;;) { ptrace(PTRACE_SYSCALL, tracee_pid, NULL, NULL) waitpid(tracee_pid, 0, 0) ptrace(PTRACE_GETREGS, tracee_pid, 0, ®s) if (regs.orig_rax == 39) { // After executing getpid system call, restore tracee state ptrace(PTRACE_SETREGS, tracee_pid, NULL, &old_regs) break } } // Restore tracee execution ptrace(PTRACE_DETACH, tracee_pid, NULL, NULL)
However, the above code is onlytraceeprocess (thread)shellcodestill cannot achieve the purpose of hiding the injection. A simple solution is to executetraceea new thread is created in the process, andshellcodein the process whereshellcodeis added to the loop that can keep running. At this point, it is difficult to detect the injectedshellcode; iftracee; the process originally contains multiple threads, and it is difficult to accurately determine whether it has been injected by monitoring thread statusshellcode; although the checktraceeThe memory segment of the process can find an anonymous memory segment with execution permissions, but some processes inherently have anonymous memory segments with execution permissions, and it is still not accurate to determine whether they existshellcodeIn summary, this new thread executionshellcodeThe method can solve the third problem: how not to be easily discovered while runningshellcodeThe pseudocode is as follows:
// Set the stack for the new thread stack_addr = remote_mmap(tracee_pid, NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0) stack_top = stack_addr + 4096 poke_text(tracee_pid, (size_t)stack_addr, (char *)&mem_addr, sizeof(void *)) // Modify the system call to SYS_clone and execute it step by step, restore the execution of the original code after creating the new thread thread_pid = remote_clone(pid, CLONE_PTRACE | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES, stack_top) // Execute shellcode in the newly created thread ptrace(PTRACE_GETREGS, thread_pid, NULL, ®s) regs.rip = (u_int64_t) mem_addr; ptrace(PTRACE_SETREGS, thread_pid, NULL, ®s) ptrace(PTRACE_DETACH, thread_pid, NULL, NULL)
Four, defense
Linuxkernel uses the graph4described algorithm to check the caller (caller) relative to the target (target) ofptraceaccess permission. First check whether the caller and the target are in the same thread group, if yes, allow (allowed) to useptracefunction; then according to the user number of the caller and the target user (uid) and group number (gid) are consistent, and whether the target has a dumpable (dumpable) attributes, whether the caller hasCAP_SYS_PTRACEpermission, to determine whether to refuse (denied) to useptracefunction; then callLinuxSecurity module (LSM), for example:SELinux,Yama,Smacketc., different security modules have their own check and judgment rules; finally, if the previous check does not refuse to useptracefunction, then it is allowed to use.
Figure4 LinuxkernelptraceAccess mode check algorithm
Except when obtaining access in the same thread group,ptracefunction permission must go throughLinuxSecurity module (LSMTherefore, it can be configuredLSMlimitptracefunction, usingYamaFor example: set the parameter/proc/sys/kernel/yama/ptrace_scope(direct assignment or modification/etc/sysctl.confinkernel.yama.ptrace_scopeThe parameter) can controlptraceThe function, parameter value definition is as follows:
0: A process can use other processes it has permission overPTRACE_ATTACHFunction.
1: A process can only use its subordinate child processes or threadsPTRACE_ATTACHFunction.
2: Only those who haveCAP_SYS_PTRACEThe process with permission can use other processesPTRACE_ATTACHFunction.
3: Any process cannot usePTRACE_ATTACHorPTRACE_TRACEMEFunction, and the parameterptrace_scopeCannot be changed.
The value can be set according to the application requirements, the appropriate/proc/sys/kernel/yama/ptrace_scopeValue, for example: production environment set to3DisableptraceFunction, the development environment is set to1Used to debug programs.
In addition, according to atraceeCan only associate atracerThe rules, can be used at the beginning of the programPTRACE_TRACEMEFunction will turn the current thread intotracee, other processes can no longer usePTRACE_ATTACHBecausePTRACE_ATTACHAct on a thread, all related threads need to usePTRACE_TRACEMEIn order to avoid the process being usedPTRACE_ATTACHFunction.
It can also be usedprctlSystem call to disable the process's dump function, specific usage is as follows:
prctl(PR_SET_DUMPABLE, SUID_DUMP_DISABLE, 0, 0, 0);
After using the above system call, the process cannot be accessed by other processes (those that haveCAP_SYS_PTRACEPermission processes except) usePTRACE_ATTACHFunction. This method is only applicable to the caller withoutCAP_SYS_PTRACEIn the case of permission, but it can act on the running process, the specific method is as follows:
1) The callprctlMake the operation into ashellcode;
2) UsingptraceTransfer1)Generated inshellcodeInject into the running target process and execute;
3) Restore the target process state and continue running.
V. Summary
ptraceSystem calls bring convenience to program development and debugging, but because of their powerful functions, they become a double-edged sword. This article introduces hidden injectionshellcodeTechnical cooperation with other penetration attack methods can pose a serious threat to system security, therefore, attention should be paid to prevention in security protection work.
Note: The code mentioned in this article is inkali-rolling (x86_64)The test was successful.
References:
https://sploitfun.wordpress.com/2015/02/11/syscalls-used-by-malloc/ (Source of Memory Structure Diagram)
https://github.com/Srakai/Adun (The idea of creating a new thread comes from here)
https://www.kernel.org/doc/html/latest/admin-guide/LSM/Yama.html

评论已关闭