Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
57 views

Shell Code Injection

Uploaded by

fonek98783
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
57 views

Shell Code Injection

Uploaded by

fonek98783
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 19

Pure In-Memory (Shell)Code Injection

In Linux Userland
Introduction

Scenario

Payload (Shellcode) Delivery

Demonstration Environment

In-Memory-Only Methods

OPSEC Considerations

Acknowledgements

References

Appendix A

Appendix B

date: 2018-12-14, author: rb

Introduction
[ ↑ ] (res/2018/#md-page-menu)
Typical post-exploitation activities include reconnaissance, information gathering and privilege
escalation. Sometimes an adversary may need additional functionality, such as when the target
system does not provide the necessary tools by default, or when they need to speed up one of
these post-exploitation actions.
In most cases dedicated tools are uploaded to the target system and ran. The biggest caveat of this
approach is that artifacts left on disk, if detected, may reveal additional information to the
defenders and potentially compromise the whole operation.

A lot of research has been conducted in recent years on performing code injection in the Windows
operating system without touching the disk ([1] (res/2018/#References), [2]
(res/2018/#References), [3] (res/2018/#References), [4] (res/2018/#References), [5]
(res/2018/#References) to name a few). The same cannot be said about *NIX (and Linux
specifically), but there are some great works from the past: skape and jt [2]
(res/2018/#References), the grugq [6] (res/2018/#References), Z0MBiE [7] (res/2018/#References),
Pluf and Ripe [8] (res/2018/#References), Aseem Jakhar [9] (res/2018/#References), mak [10]
(res/2018/#References) or Rory McNamara [11] (res/2018/#References).

Scenario
[ ↑ ] (res/2018/#md-page-menu)
Imagine yourself sitting in front of a blinking cursor, using a shell on a freshly compromised Linux
server, and you want to move forward without leaving any traces behind. You need to run
additional tools, but you don't want to upload anything to the machine. Or, you simply cannot run
anything because the noexec option is set on mounted partitions. What options remain?

This paper will show how to bypass execution restrictions and run code on the machine, using only
tools available on the system. It's a bit challenging in an everything-is-a-file OS, but doable if you
think outside the box and use the power this system provides.

The following paper is a direct result of experiments conducted by Sektor7 labs where new and
improved o!ensive methods are researched and published.

Payload (Shellcode) Delivery


[ ↑ ] (res/2018/#md-page-menu)
Finding a reliable and stealthy way to deliver a payload/tool to a target machine is always a
challenge for an adversary.

The most common method is to establish a new connection with C2 or a 3rd party server which
hosts the desired tool and download it to the victim. This potentially generates additional artifacts
on the network infrastructure (ie. netflow, proxy logs, etc.).

In many situations, an attacker forgets that there is already an open control channel to the target
machine - the shell session. This session can be used as a data link to upload a payload to the
victim without the need to establish a new TCP connection with external systems. The downside of
this approach is that a network glitch could result in the loss of both the data transfer and control
channel.

In this paper, the two delivery methods will be referred to as out-of-band and in-band,
respectively. The latter option will be used as the primary way of transferring (shell)code.
(res/2018/img/in-

band.png)

Demonstration Environment
[ ↑ ] (res/2018/#md-page-menu)
Our demonstrations and experiments will use the following setup:

Victim machine running recent Kali Linux as a virtual machine


Attacker machine – Arch Linux running as a host system for VMs
SSH connection from the Attacker's machine to the Victim, simulating shell access
Simple ‘Hello world’ shellcode for x86_64 architecture (see Appendix A
(res/2018/#Appendix_A))

In-Memory-Only Methods
[ ↑ ] (res/2018/#md-page-menu)

Tmpfs
The first place an adversary can store files is tmpfs. It puts everything into the kernel internal
caches and grows and shrinks to accommodate the files it contains. Additionally, starting from
glibc 2.2, tmpfs is expected to be mounted at /dev/shm for POSIX shared memory (shm_open(),
shm_unlink()).

Here is an example view on mounted tmpfs virtual filesystems (from Kali):

(res/2018/img/tmpfs1.png)

By default /dev/shm is mounted without the noexec flag set. If a paranoid administrator turns it on,
it e!ectively kills this method – we can store data but cannot execute (execve() will fail).
victim$mount|grepshm
tmpfson/dev/shmtypetmpfs(rw,nosuid,nodev,noexec)

victim$cp'whichuname'/dev/shm/.

victims/dev/shm/uname
bash:/dev/shm/uname:Permissiondenied

victim$strace/dev/shm/uname
execve("/dev/shm/uname",["/dev/shm/uname"],0x7fff31c89a90/*23vars*/)=-1EACCES
(Permissiondenied)
fstat(2,{st_mode=S_IFCHR|0620,st_rdev=makedev(136,1),...})=0
write(2,"strace:exec:Permissiondenied\n",32strace:exec:Permissiondenied
)=32
getpid() =3556
exit_group(1) =?
+++exitedwith1+++

(res/2018/img/tmpfs2.png)

We will come back to /dev/shm later.

GDB
GNU Debugger is a default debugging tool for Linux. It’s not commonly installed on production
servers, but sometimes can be found in development environments and in a few
embedded/dedicated systems. According to the gdb(1) manual:

GDB can do four main kinds of things (plus other things in support of these) to
help you catch bugs in the act:
* Start your program, specifying anything that might affect its behavior.
* Make your program stop on specified conditions.
* Examine what has happened, when your program has stopped.
* Change things in your program, so you can experiment with correcting the eff
ects
of one bug and go on to learn about another.

The last aspect of GDB can be used to run shellcode in memory only, without touching disk.

First we convert our shellcode into a byte string:

(res/2018/img/gdb1.png)
Then, run /bin/bash under the control of gdb, set a breakpoint at main(), inject the shellcode and
continue. Below is a one-liner:

(res/2018/img/gdb2.png)

Python
Python is a very popular interpreted programming language and, unlike GDB, is commonly found
in many default Linux deployments.

Its functionality can be extended with many modules including ctypes , which provides C
compatible data types and allows calling functions in DLLs or shared libraries. In other words,
ctypes enables the construction of a C-like script, combining the power of external libraries and
direct access to kernel syscalls.

To run our shellcode in memory with Python, our script has to:

load the libc library into the Python process


mmap() a new W+X memory region for the shellcode
copy the shellcode into a newly allocated bu!er
make the bu!er 'callable' (casting)
and call the bu!er

Below is the complete script (Python 2):


(res/2018/img/py1.png)

The whole script is converted into a Base64-encoded string:


(res/2018/img/py2.png)

And delivered to a target machine with a one-liner:

(res/2018/img/py3.png)

Self-modifying dd
On rare occasions, when none of the above methods are possible, there's one more tool installed
by default on many Linux systems (part of the coreutils package) that may be used. The tool is
called dd and is commonly used to convert and copy files. If we combine it with a procfs filesystem
and the /proc/self/mem special file - exposing the process’s own memory - there is, potentially, a
small window in which to run shellcode in-memory only. To do that, we need to force dd to
modify itself on the fly (aka to shinji-nize itself ).

The default dd runtime behavior is depicted below:


(res/2018/img/dd1.png)

And this is how a self-modifying dd runtime should look like:

(res/2018/img/dd2.png)

The first thing needed is a place to copy shellcode inside the dd process. The entire procedure
must be stable and reliable across runs since it's a running process overwriting its own memory.

A good candidate is the code that’s called after the copy/overwrite is successful. It directly
translates to process exit. Shellcode injection can be done either in the PLT (Procedure Linkage
Table) or somewhere inside the main code segment at exit() call, or just before the exit().

Overwriting the PLT is highly unstable, because if our shellcode is too long it can overwrite some
critical parts that are used before the exit() call is invoked.

After some investigation, it appears the fclose(3) function is called just before the exit():
(res/2018/img/dd3.png)

fclose() is called only from 2 places:

(res/2018/img/dd4.png)

Further tests show that the code at 0x9c2b ( jmp 1cb0 ) is the one used at runtime and it’s
followed by a large chunk of code which, potentially, can be overwritten without crashing the
process.

There are two additional obstacles we have to address to make this technique to work:

1. stdin, stdout and stderr file descriptors are being closed by dd after the copy:
attacker$straceddif=/dev/zeroof=/dev/nullcount=12>&1|egrep
close(0) =0
close(1) =0
close(2)
(res/2018/img/dd5.png) =0
2. Address Space Layout Randomization

The first problem can be solved by creating stdin and stdout duplicate file descriptors with the
help of bash (see bash(1)):
Duplicating File Descriptors
The redirection operator

[n]<&word

is used to duplicate input file descriptors. If word expands to one or


more digits, the file descriptor denoted by n is made to be a copy of
that file descriptor.

and prefixing our shellcode with dup() syscalls:

;dup(10)+dup(11)
xorrax,rax
xorrdi,rdi
movdi,10
movrax,0x20
syscall (res/2018/img/dd6.png)
7
8 xorrax,rax
9 incrdi
10 movrax,0x20
11 syscall

The second problem is more serious. Nowadays, in most Linux distributions, binaries are compiled
as PIE (Position Independent Executable) objects:

(res/2018/img/dd7.png)

and ASLR is turned on by default:


(res/2018/img/dd8.png)

Fortunately, Linux supports di!erent execution domains (aka personalities ) for each process.
Among other things, execution domains tell Linux how to map signal numbers into signal actions.
The execution domain system allows Linux to provide limited support for binaries compiled under
other UNIX-like operating systems. Since Linux 2.6.12, the ADDR_NO_RANDOMIZE flag is available
which disables ASLR in a running process.

To turn o! ASLR in userland at runtime, setarch tool can be used to set di!erent personality flags:

(res/2018/img/dd9.png)

Now all the necessary pieces are in place to run the self-modifying dd:
(res/2018/img/dd10.png)

System Calls
All of the above methods have one huge downside (except tmpfs) – they allow execution of
shellcode, but not an executable object (ELF file). Pure assembly shellcode has limited usage
and is not scalable if we need more sophisticated functionality.

Once again, kernel developers came to the rescue – starting from Linux 3.17 a new system call
was introduced called memfd_create() . It creates an anonymous file and returns a file descriptor
that refers to it. The file behaves like a regular file. However, it lives in RAM and is automatically
released when all references to it are dropped.

In other words, the Linux kernel provides a way to create a memory-only file which looks
and feels like a regular file and can be mmap()’ed/execve()’ed.

The following plan covers creating a memfd-based file in a virtual memory and, eventually,
uploading our tools of choice to the victim machine without storing them on a disk:

generate a shellcode which will create a memfd file in a memory


inject the shellcode into a dd process (see Self-modifying dd (res/2018/#Self-modifying_dd)
section)
'suspend' the dd process (also done by the shellcode)
prepare a tool of choice to be uploaded (statically linked uname is used as an example)
transfer base64-encoded tool into the victim machine via an in-band data link (over a shell
session) directly into memfd file
finally, run the tool

The first thing is to create a new shellcode (see Appendix B (res/2018/#Appendix_B)). The new
shellcode reopens closed stdin and stdout file descriptors, calls memfd_create() creating a memory-
only file (named AAAA ), and invokes the pause() syscall to 'suspend' the calling process (dd).
Suspending is necessary because we want to prevent dd process from exiting and, instead, make
its memfd file accessible to other processes (via procfs). The exit() syscall in the shellcode should
never be reached.

Then we shinjinize dd, suspend it and check if memfd file is exposed in the memory:
victim$echo-n-e
"\x48\x31\xc0\x48\x31\xff|x66\xbf|x0a|x00\xb8\x20\x00\x00\x00\x0f\x05\x48\x31\xc0\x48\xf
f\xc7\xb8\x20\x00\x00\x00\x0f\x05\x68\x41\x41\x41\x41\x48\x89\xe7\xbe\x00\x00\x00\x00\xb
8\x3f\x01\x00\x00\x0f\x05\xb8\x22\x00\x00\x00\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x3
I\xff\x0f\×05"|setarchx86_64-Rddof=/proc/self/membs=1seek=$((0x555555554000+
0x9c2b))conv=notrunc10<&011<&1&
(1]3071
victim$ls-al/proc/'pidofdd'/fd/
total 0
dr-x------2reenz0hreenz0h0Jul 9 21:40.
dr-xr-xr-x9reenzohreenz0h Jul 9 21:39
Lr-X- 1reenz0hreenz0h64 Jul 9 21:400->'pipe:[53169]'
lrwx- 1 reenz0hreenz0h64 Jul 9 21:40l->/dev/pts/1
lr-x- 1 reenz0hreenz0h64 Jul 9 21:4010->'pipe:[53169]'
.rWX- 1 reenz0hreenz0h64Jul 9 21:4011->/dev/pts/1
rwX- reenz0hreenz0h64Jul 9 21:402->/dev/pts/1
lrwx- 1 reenz0hreenz0h64Jul 9 21:403->'/memfd:AAAA(deleted)'

(res/2018/img/sc1.png)

The next step is to prepare our tool for uploading. Please note that attackers’ tools have to be
either statically linked or use the same dynamic libs as on a target machine.

(res/2018/img/sc2.png)

Now just ‘echo’ the Base64-encoded tool into memfd-file and run it:

(res/2018/img/sc3.png)
Note that the memfd file can be 'reused'; the same file descriptor can 'store' the next tool if
necessary (overwriting the previous one):

(res/2018/img/sc4.png)

What if a victim machine runs a kernel older than 3.17?


There is a C library function called shm_open(3). It creates a new POSIX shared object in memory. A
POSIX shared memory object is, in e!ect, a handle which can be used by unrelated processes to
mmap() the same region of shared memory.

Let’s look into Glibc source code. shm_open() calls open() on some shm_name:
(from glibc/sysdeps/posix/shm_open.c
(https://code.woboq.org/userspace/glibc/sysdeps/posix/shm_open.c.html))

(res/2018/img/sc5.png)
Which, in turn, is dynamically allocated with shm_dir:
(from glibc/sysdeps/posix/shm-directory.h
(https://code.woboq.org/userspace/glibc/sysdeps/posix/shm-directory.h.html))

(res/2018/img/sc6.png)

shm_dir is a concatenation of _PATH_DEV with "shm/":


(from glibc/sysdeps/posix/shm_open.c
(https://code.woboq.org/userspace/glibc/sysdeps/posix/shm_open.c.html))
(res/2018/img/sc7.png)

and _PATH_DEV is defined as /dev/.

So, it turns out that shm_open() just creates/opens a file on the tmpfs file system, but that was
already covered in the tmpfs (res/2018/#Tmpfs) section.

OPSEC Considerations
[ ↑ ] (res/2018/#md-page-menu)
Any o!ensive activity on the target machine requires thinking about side-e!ects. Even if we try not
to touch the disk with any code, our actions might still leave some 'residue'.

These include (but are not limited to):


1. Logs (ie. shell history). In this case adversary has to make sure logs are either removed or
overwritten (sometimes not possible due to lack of privileges).
2. Process list – occasionally another user viewing processes running on the victim machine might
spot weird process names (ie. /proc/< num >/fd/3). This can be circumvented by changing the
argv[0] string in the target process.
3. Swappiness – even if our artifacts live in virtual memory, in most cases they can be swapped
out to disk (analysis of swap space is a separate topic). It potentially can be dodged with:

mlock(), mlockall(), mmap() - requires root or at least CAP_IPC_LOCK capability


sysctl vm.swappiness or /proc/sys/vm/swappiness – requires root privileges
cgroups (memory.swappiness) – requires root or privilege to modify cgroup
The last one does not guarantee that under heavy load the memory manager will not swap the
process to disk anyway (ie. root cgroup allows swapping and needs memory).

Acknowledgements
[ ↑ ] (res/2018/#md-page-menu)
Hasherezade for unintended inspiration
mak for interesting discussions and content review
hardkor for content review

References
[ ↑ ] (res/2018/#md-page-menu)
1. In-Memory PE EXE Execution by Z0MBiE/29A
https://github.com/fdiskyou/Zines/blob/master/29a/29a-6.zip
(https://github.com/fdiskyou/Zines/blob/master/29a/29a-6.zip)
2. Remote Library Injection by skape & jt
http://www.hick.org/code/skape/papers/remote-library-injection.pdf
(http://www.hick.org/code/skape/papers/remote-library-injection.pdf)
3. Reflective DLL Injection by Stephen Fewer
https://www.dc414.org/wp-content/uploads/2011/01/242.pdf (https://www.dc414.org/wp-
content/uploads/2011/01/242.pdf)
4. Loading a DLL from memory by Joachim Bauch
https://www.joachim-bauch.de/tutorials/loading-a-dll-from-memory/ (https://www.joachim-
bauch.de/tutorials/loading-a-dll-from-memory/)
5. Reflective DLL Injection with PowerShell by clymb3r
https://clymb3r.wordpress.com/2013/04/06/reflective-dll-injection-with-powershell/
(https://clymb3r.wordpress.com/2013/04/06/reflective-dll-injection-with-powershell/)
6. The Design and Implementation of Userland Exec by the grugq
https://grugq.github.io/docs/ul_exec.txt (https://grugq.github.io/docs/ul_exec.txt)
7. Injected Evil by Z0MBiE/29A
http://z0mbie.daemonlab.org/infelf.html (http://z0mbie.daemonlab.org/infelf.html)
8. Advanced Antiforensics : SELF by Pluf & Ripe
http://phrack.org/issues/63/11.html (http://phrack.org/issues/63/11.html)
9. Run-time Thread Injection The Jugaad way by Aseem Jakhar
http://www.securitybyte.org/resources/2011/presentations/runtime-thread-injection-and-
execution-in-linux-processes.pdf
(http://www.securitybyte.org/resources/2011/presentations/runtime-thread-injection-and-
execution-in-linux-processes.pdf)
10. Implementation of SELF in python by mak
https://github.com/mak/pyself (https://github.com/mak/pyself)
11. Linux based inter-process code injection without ptrace(2) by Rory McNamara
https://blog.gdssecurity.com/labs/2017/9/5/linux-based-inter-process-code-injection-without-
ptrace2.html (https://blog.gdssecurity.com/labs/2017/9/5/linux-based-inter-process-code-
injection-without-ptrace2.html)

Appendix A
[ ↑ ] (res/2018/#md-page-menu)
Example 'Hello world' shellcode used in the experiments:

(res/2018/img/app1.png)

Appendix B
[ ↑ ] (res/2018/#md-page-menu)
Memfd-create() shellcode:
(res/2018/img/app2.png)

Copyright © 2024 SEKTOR7. All rights reserved.


Website generated with MDwiki (http://www.mdwiki.info) © Timo Dörr and contributors.

You might also like