Hacker News new | past | comments | ask | show | jobs | submit login
Unprivileged process injection techniques in Linux (joev.dev)
161 points by joe_v 8 months ago | hide | past | favorite | 47 comments



Hi, I'm the author of the `dlinject` tool referenced in the article. Sadly I haven't been maintaining it and it doesn't work on modern distros anymore - not for any fundamental reasons, it just needs some compatibility tweaks. However, it's been forked as `asminject`[1], with bug fixes and other bells and whistles. I consider it to be the latest evolution of that particular approach, and I should probably update the dlinject readme to point at it.

Thank you for the writeup!

[1] https://github.com/BishopFox/asminject


The history actually goes back quite a bit further.

Exactly 20 years ago I wrote and released userland exec().

https://seclists.org/bugtraq/2004/Jan/2

Good to see that the technique is still viable after two decades.

On a related note, this sort of issue (difficulty researching the origins of techniques, and hacking history in general) is a problem that will only get worse. As a community we haven’t created an institutional memory beyond “the oldest hacker you know.”


> we haven’t created an institutional memory beyond “the oldest hacker you know.”

Which I'd wager is due to over-reliance on search engines. The net is stuffed to the brim with useless bullshit designed to steal eyeballs, so finding anything somebody published two decades ago is now impossible. Internet Archive is useful if you already know what website used to exist, not so useful if you don't.

Whatever happened to that website that was a combination of blog + archive of exploit POCs? Wasn't it called PacketStorm? I just tried to find it with two search engines and came up empty. That would've been an ideal place to track down old techniques and news.


> Good to see that the technique is still viable after two decades.

It absolutely blew my mind to learn that Debian is still shipping with Yama mitigations disabled by default (last time I checked, which was about a year ago). I think they're one of the only mainstream distros to be doing this, although I haven't done a comprehensive survey.


The Debian patch for this setting is: https://salsa.debian.org/kernel-team/linux/-/blob/master/deb...

The decision made there is from 2013 (see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=712740), so it might be worth revisiting.

Could you file a bug report to get the discussion started?


I think this is so users can choose what level of restriction they want using kernel.yama.ptrace_scope with sysctl, 0 being the default and 2 being the most restrictive.


You can configure it on most distros, it doesn't excuse having an insecure default.


Hah! I was just referencing this paper the other day when mucking with the linker for an unrelated reason. I probably should have chosen a better name for my article - I am trying to cover the cases of "you have Linux command execution, how do you run native code?" as opposed to your approach which as I understand is more: "you are running native code, how can you load a separate ELF in-process?"

Agreed about institutional memory; zines/blogs are very important; but at the end of the day I usually end up just asking in some corner of IRC.


> "you have Linux command execution, how do you run native code?"

I was going to ask you what the precise situation is in which you'd apply the ideas from the blog post as I don't know what exactly is meant by "process injection". I think the article would benefit from providing a little bit more background for us non-hackers / non-pentesters. Still, very interesting article – thank you!

PS: The article says

> you need a writable location on disk; this is not always true in e.g. read-only chroots, filesystems, containers, etc

Couldn't you create a temporary file in-memory (e.g. in /dev/shm or in some tmpfs), make it executable (+x) and then execute it?


Apologies it's a little scattered. Roughly it's about dealing with situations where you can execute a command but now want to run a native executable, and how much noise such a thing will make in the presence of monitoring.

> Couldn't you create a temporary file in-memory (e.g. in /dev/shm or in some tmpfs), make it executable (+x) and then execute it?

It all depends on how your environment is set up: whether a tmpfs or shm device is mounted and writable by your user is up to the admin. For example, on many embedded devices you often want to avoid writes to prevent any sort of filesystem wear, or because you have a write-once media like a ROM; so the whole fs will be mounted readonly. With chroots it's best practice to provide a minimal environment - unless tempfiles are needed there will usually not be a /tmp. Try `docker run --read-only -ti ubuntu bash` as another example:

``` root@9302f159e0e0:/tmp# touch a touch: cannot touch 'a': Read-only file system ```


My apologies for the delay, Joe, I was on vacation. Now that I'm home, I gave this a try but at least on my machine writing to /dev/shm/ works as I remembered, even with --read-only:

    $ docker run --read-only -ti ubuntu bash
    root@3dfdab770505:/# echo "bar" > /dev/shm/foo
    root@3dfdab770505:/# cat /dev/shm/foo
    bar
So, again, couldn't you just write your binary to /dev/shm and execute it?


It is fairly common to have noexec on /dev/shm; filesystem configurations are always up to the admin so they could feasibly set anything.


Thanks for reminding me of noexec; I'm no Linux security expert by any means, so I was merely trying to figure out what's possible and what's not.

It looks like mounting /dev/shm with noexec is not that common, though, is it? See e.g.

https://unix.stackexchange.com/questions/670362/mounting-dev...

More generally, it regularly blows my mind how hard it is to harden a Linux installation, and how many pitfalls and caveats there are.


Ah, so, in 2005 I wrote about that when I implemented rexec() — remote exec() — which takes a binary and then copies it over an arbitrary text only link (like ssh) and executes it completely in memory without touching disk.

http://phrack.org/issues/62/8.html

The idea was that if you have access to a box via a shell and you want to run your own binary without leaving evidence behind, you’d use rexec() to do that.


That is a really useful implementation and a good way to use gdb to "live off the land". It is interesting how ops changes like moving to containers/VMs affect pentesting techniques; over the last decade I find myself relying less and less on live-off-the-land in a lot of engagements. When you can use them though they have a lot of advantages.

Also never realized that others have implemented (and I guess patented?) syscall proxying, I have heard that idea discussed before for offensive tooling and wondered how well it works in practice.


Syscall proxying was very old even when I wrote that article. The problem with syscall proxying is that it is slow. Take any process and imagine adding network latency to every single syscall. On a local network is incredibly slow, but over any sort of real distance it is just impossible.

That’s why I pushed everything to the target system. Run it local as much as possible.

Back then there were no containers or VMs to use. These days I think you should be bringing your environment with you. Unless there are serious reasons not to.


That makes sense, something like `grep $USER /etc/passwd` would shuffle the whole passwd file over the wire, etc; for a lot of post-exploitation stuff I could see it causing more trouble than it's worth.


Oh, it’s far far worse than that. Just the core operation would be:

open() — network round trip fstat() — network round trip brk() — network round trip read() — network round trip Shuffle data over network read() — network round trip Shuffle data over network

Etc etc

For a “grep root /etc/passwd” there are 88 syscalls on a Debian 11. If we assume a very generous 50ms latency for every syscall, that means we’re waiting 4.4s for the result.

The use case for syscall proxying is limited to when you don’t want to upload an exploit onto a target machine, but you need to run the exploit on that machine. So it could be an LPE or something.

It is way too slow for post exploitation work.


Userland exec was a very interesting read when I came across it some years ago; thanks for publishing it!

The technique still mostly works, but on recent glibc+Linux, you also have to unregister the rseq area before cleaning out the address space (which requires computing the address first, which is a little cumbersome). Otherwise, if the rseq area is registered but unmapped, the kernel will forcefully stop the program.

(That said, nowadays memfd_create + fexecve is likely a more robust alternative in many cases.)


Yup, probably the more robust approach.


Ha. I did the exact same thing on Windows to inject DLLs into Chrome once.

EDIT: Geez. I sure wish I knew what's so objectionable about what I said.


I didn't vote, but to answer your question, you couldn't possibly have done the "exact same thing" as a linux-specific userland exec technique and have it work as a DLL injection technique on Windows. I'm sure it could've been conceptually similar, but not identical, and maybe those differences would be interesting to discuss.


"Exact same thing" as in re-implemented a basic OS feature in user mode to bypass an intercepted feature. Obviously it can't be literally exactly the same thing, because that wouldn't be implementing something, but rather using existing source code.


I didn't vote either way until you edited your post to add the meta comment. I don't like posters discussing moderation of their posts (I find that discussion about themselves to be vain and distracting from the topic), and I feel gross and off topic myself talking about it even now.

The chrome thing sounds cool.


I didn't comment about the voting itself, I asked what was wrong with my comment. There's a difference between "oh, I'm getting down-voted, this sucks" and "what's wrong with what I said?" The former is whining, while the latter invites discussion. If telling someone else what you think about their comment counts as "meta" discussion and is therefore boring then that kind of kills the purpose of a discussion forum.


The strange taboos of ingroups


To be fair, the guidelines[0] request of us:

> Please don't comment about the voting on comments. It never does any good, and it makes boring reading.

Which, yeah.

[0] – https://news.ycombinator.com/newsguidelines.html


Maybe one day HN will realise that giving the ability to downvote is pretty pointless and needlessly open to abuse.

That negative ability just promotes negativity itself and a bloody pia when I am reading a greyed out message because I am too stubborn to go into the settings and change defaults - so I just steam on thinking HN are using a relic of an ideology.


It's an ingroup reinforcement method.

If you're part of a cult, and you're not allowed to eject someone from the cult, you can at least ignore them or walk away from them when they talk to you, if you don't agree with what they say/do. It's a way for the members of the cult to reinforce the ingroup's behaviors without having overt power over the group. For other people watching this behavior, it reinforces the need to align themselves with the larger group, to avoid the same fate.

The first rule of fight club is you don't talk about fight club.


You're just describing the behavior of every society ever.

I'm not sure where some folks got the idea that they could say whatever they wanted in whatever community they wanted and not expect any reactions, but that's clearly an inhuman expectation. I'm guessing that attitude is a byproduct of internet commenting.

I guess you could call society an "ingroup" or a "cult" like you're doing. It technically fits the definition. It also gives you a convenient victim complex to wave about whenever you feel like people aren't agreeing with you enough. But let's be honest: not every social pariah is a Galileo.


Not really. Society is a broad concept, which contains many different concepts, two of which are ingroup and outgroup. It's a complex topic, but suffice to say that within the concept of ingroup, there are some common behaviors specific to certain kinds of groups, but not all of them. Much like with EQ, if you learn about the tendencies of humans in social groups, you can learn to manage your own behavior to resist falling into common traps. But those who don't learn about them are often subject to them, with unfortunate effects.

As to your assertion ("idea that they could say whatever they wanted in whatever community they wanted and not expect any reactions"), I don't think I or anyone else said that or suggested it. Sounds like a sweeping generalization designed to reinforce an opinion you want to believe. But it also seems like you drank the kool-aid a while back, so I'm pretty sure I can't influence your opinion.


You're just repeating what I said with different words. Yes, throughout history, people who bucked the rules and practices of society might have been called "out group members", but that's just a label. The substance is that they're eschewing society, and obviously should expect society to react and shun them. Confront them, maybe. Some societies used banishment.

Either way, the label is being self-applied here to indulge a victim complex in someone who doesn't like that society is reacting to their decision to go against society. Consider that total jerks and crazy folks use exactly the same logic.

Not that anyone here is a jerk, or crazy, just that the "I'm an independent thinker and everyone else is drinking the kool aid" argument is tired and oblivious and self-indulgent and applies equally when used by jerks and nuts. The dude on the street corner shouting racial slurs at the top of their lungs and swatting at invisible spiders thinks he's "just part of the out group", too.


>I'm not sure where some folks got the idea that they could say whatever they wanted in whatever community they wanted and not expect any reactions

Usually, historically, if you said something someone didn't like you could expect the person to either confront you directly, or to silently change their opinion of you and possibly change their behavior, perhaps to your detriment. Even before the Internet existed, there was something else someone might have done, which is to send you an anonymous note reading "fuck you". No argument, no way to reply, nothing but a cheap gratuitous offense that serves no purpose other than upset the person who receives it. I argue that downvoting a comment you don't like is equivalent to that.


> Even before the Internet existed, there was something else someone might have done, which is to send you an anonymous note reading "fuck you". No argument, no way to reply, nothing but a cheap gratuitous offense that serves no purpose other than upset the person who receives it. I argue that downvoting a comment you don't like is equivalent to that.

A better equivalent would be arguing from an anonymous throwaway account (*cough*), where any replies go into the void, rather than to an established community member.

A comment either adds value in people's opinion, or it detracts, or neither. The society that is HN has a voting system to communicate that info, because that is what we prefer.

If people find the non-meta substance of a post interesting enough to reply, they absolutely will do so. But we have the guidelines recommending against moderation meta-discussion because you and the moderation of your comment aren't the topic, and you shouldn't try to make it the topic, because we find that to be more boring than the actual topic. Maybe you find yourself more interesting, or you think conversation is boring unless it includes moderation meta-discussion. In most cases, I don't think others agree (hence the guidelines and the voting).

You can call our society an "in-group", you can call someone who ignores what society wants an "out group member", but in the end, I don't know why it surprises people that society responds with feedback (though perhaps not in the exact feedback format one may personally prefer).


>A better equivalent would be arguing from an anonymous throwaway account (cough), where any replies go into the void, rather than to an established community member.

I don't agree with this. How can you say that your reply "goes into the void", just because the name of the account you're responding to is "throwaway892238" rather than "fluoridation"? In either case there's a person on the other end, how does the screen name they go by change anything? I could equally say that unless you tell me your real name, or unless you let me put my hand down your pants, or any other equally arbitrary standard, I don't consider you a real person. Neither the screen name, nor their real name, nor what's in their pants, is what the actual person is.


> How can you say that your reply "goes into the void", just because the name of the account you're responding to is "throwaway892238" rather than "fluoridation"?

I have no idea which of our community members any given throwaway is, I'll never know, their comments are just anonymous notes left with no way to respond to an actual member of our community with an actual history and an actual reputation (vs a throwaway they may never check again). That's why a throwaway comment is like an anonymous "fuck you" note. Or, put another way:

> Throwaway accounts are ok for sensitive information, but please don't create accounts routinely. HN is a community—users should have an identity that others can relate to.[0]

My point is further supported by the rest of my post that you responded to, which is the substance of our discussion. Continuing to argue over an analogy instead of the substance of the matter would be silly.

[0]: https://news.ycombinator.com/newsguidelines.html


Happy opseccy new year Grug :)


Merry OPSEC, and a happy OPSEC Year!


It's a bit wild, but you can use memfd_create to do things like load libraries or binaries, on a filesystem that has no read/write access and noexec enabled.

I have been meaning to do a blog post about this, since it doesn't seem to be common knowledge.

Originally, I thought of it as a response to a Reddit question: "How can I load a shared library from a .jar directly into memory?"

https://old.reddit.com/r/java/comments/15lcwil/load_shared_l...


> on a filesystem that has...

memfd_create's whole selling point is that it isn't backed by a filesystem; it isn't "on" one in the first place, so there is nowhere for it to inherit such restrictions from. The consequences of that can be surprising though, I agree, and are worth exploring and writing about.


Why does Bash have permission to ptrace or read other process's memory. This should already be locked down, but I suspect it's not because for some reason a lot of systems do not use LSMs or don't care about security in general.


The same bash instance is the parent of the process being attacked so it meets the requirements to ptrace at yama/ptrace_scope == 1.


In the case of stelf-loader, the bash instance is attacking itself. It's not especially unexpected for a process to be able to modify its own memory.


>It's not especially unexpected for a process to be able to modify its own memory.

It is unexpected for Bash to do that so it shouldn't be given access to ptrace.


stelf-loader does not use ptrace


Writing to /proc/pid/mem requires access to ptrace. I never said it would use ptrace directly.


It's gated by the same access control logic that governs ptrace yes, but it does not use ptrace directly nor indirectly. The first step of that logic is:

> If the calling thread and the target thread are in the same thread group, access is always allowed.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: