

Go LD_PRELOAD backdoor experiment - mattbostock
https://github.com/mattbostock/go-ldpreload-backdoor

======
readams
So from what I can tell this is not in any way a security issue or anything
new. If you can LD_PRELOAD a library when running a system executable, then
you can backdoor it trivially since LD_PRELOAD is designed to allow you to
substitute existing symbols with your own symbols. The use of go here matters
not at all, and this is not an attack against system executables that use go.

Of course, you can't, as an ordinary user, start system executables with
elevated privileges and set LD_PRELOAD.

~~~
0x0
Agree that this sounds like a non-story. If you can execute code, you can
execute code! Wow.

Obviously LD_PRELOAD isn't parsed by suid binaries.

~~~
jerf
Relevant and fun:
[http://blogs.msdn.com/b/oldnewthing/archive/2007/08/07/42687...](http://blogs.msdn.com/b/oldnewthing/archive/2007/08/07/4268706.aspx)

(Also no accusation to the original author intended. It's just relevant and
fun to the parent message.)

------
forgottenpass
Often unknown dynamic linker trick, not a backdoor.

Although, if anyone reading this has never heard of LD_PRELOAD, take a look at
a better resource than this link because it can be a powerful debugging and
testing tool.

------
geofft
Ooh, I really like that "go backdoor()" works here and it can be injected into
a non-Go process.

Looks like you can somewhat generically write C-ABI shared libraries in C:
[https://blog.filippo.io/building-python-modules-with-
go-1-5/](https://blog.filippo.io/building-python-modules-with-go-1-5/)

This has been one of the reasons I've been writing/learning Rust instead of
Go, and I'm glad to see competition :)

------
m00dy
In this experiment, hooked user-space application needs to execute strrchr
function at least once. Otherwise, it won't work.

~~~
mattbostock
That's right; the ones I tested were 'ps' and 'top'.

I did try hooking __libc_start_main, but had difficulty using dl.Sym() with
it. I wasn't sure if I converted the function signature to Go incorrectly or
if it's a limitation of dl.Sym().

This was the error I was hitting:
[https://github.com/rainycape/dl/blob/484344929c1867aec9517b5...](https://github.com/rainycape/dl/blob/484344929c1867aec9517b52918c2e3232a552fe/trampoline.go#L116)

So I chose a method with a simpler function signature I found using ltrace,
just to get it working as an experiment.

~~~
geofft
If you can manage to register a constructor function, you can get your code
executing without the target code having to call anything. In GCC, adding
__attribute__((constructor)) before a no-argument function returning void
works. I don't have a specific suggesiton for how to make this work in Go,
although if you can add arbitrary C-compatible attributes and use the platform
linker, setting the constructor attribute should just work.

In C++, constructors on static global objects get run automatically (via this
mechanism), so if Go has something similar, that would work. (Stable) Rust
goes out of its way to make you not able to do this because it's an anti-
pattern, but there's a stupid trick if you can assume GNU:
[https://github.com/geofft/redhook/blob/master/src/lib.rs#L34...](https://github.com/geofft/redhook/blob/master/src/lib.rs#L34-L42)

Alternatively, you can just write an __attribute__((constructor)) function in
C that calls an exported Go function that in turn calls `go backdoor()`, and
link them all together.

~~~
mattbostock
Thanks for the tips, I'll give these a try. Much appreciated.

