Reverse Engineering Reptile Kernel module to Extract Authentication code

 This time I will show how to (partially) reverse engineer a linux kernel module. The linux kernel module is larger than usual kernel module because it is actually a Reptile-based rootkit dropped by some hacker. 

Part 1. Get basic module information

First we gather basic module info by using modinfo :

[root@lb lkrg-0.9.1]# modinfo falc0n

filename:       /lib/modules/3.10.0-1127.10.1.el7.x86_64/kernel/sdc/falc0n.ko

intree:         Y

license:        GPL

retpoline:      Y

rhelversion:    7.8

srcversion:     81F508029A53F7490CCDB44

depends:        

vermagic:       3.10.0-1127.10.1.el7.x86_64 SMP mod_unload modversions 


We could also refer to the kernel module file not yet installed in the OS :

[root@lb lkrg-0.9.1]# file falc0n.ko

falc0n.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=1bde6abf4732eb620983032a47fbcf07689ece11, not stripped

[root@lb lkrg-0.9.1]# modinfo falc0n.ko

filename:       /root/lkrg-0.9.1/falc0n.ko

intree:         Y

license:        GPL

retpoline:      Y

rhelversion:    7.8

srcversion:     81F508029A53F7490CCDB44

depends:        

vermagic:       3.10.0-1127.10.1.el7.x86_64 SMP mod_unload modversions 

[root@lb lkrg-0.9.1]#

[root@lb lkrg-0.9.1]# stat falc0n.ko

  File: 'falc0n.ko'

  Size: 801936    Blocks: 1568       IO Block: 4096   regular file

Device: fd00h/64768d Inode: 318269      Links: 1

Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)

Context: unconfined_u:object_r:admin_home_t:s0

Access: 2021-05-29 09:22:04.773399890 +0700

Modify: 2021-05-26 11:14:33.154362601 +0700

Change: 2021-05-26 11:14:33.154362601 +0700

 Birth: -


Part 2. List symbols inside the module


Then we examine the symbols inside the kernel module (be mindful that the kernel module is an ELF file). 


[root@lb lkrg-0.9.1]# gdb falc0n.ko 

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7

Copyright (C) 2013 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-redhat-linux-gnu".

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>...

Reading symbols from /root/lkrg-0.9.1/falc0n.ko...done.

(gdb) info variables

All defined variables:


File /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:

static const char __UNIQUE_ID_intree28[9];

static const char __UNIQUE_ID_license27[12];

static char parasite_blob[631368];


File ./arch/x86/include/asm/vvar.h:

static const volatile unsigned long * const vvaraddr_jiffies;

static const int * const vvaraddr_vgetcpu_mode;

static const struct vsyscall_gtod_data * const vvaraddr_vsyscall_gtod_data;


File /tmp/.ICE-unix/rc1/parasite_loader/p_load.mod.c:

struct module __this_module;

static const char __UNIQUE_ID_retpoline11[12];

static const char __UNIQUE_ID_rhelversion10[16];

static const char __UNIQUE_ID_srcversion9[35];

static const char __UNIQUE_ID_vermagic8[65];

static const struct modversion_info ____versions[4];

static const char __module_depends[9];


File ./arch/x86/include/asm/vvar.h:

static const volatile unsigned long * const vvaraddr_jiffies;

static const int * const vvaraddr_vgetcpu_mode;

static const struct vsyscall_gtod_data * const vvaraddr_vsyscall_gtod_data;

(gdb) info functions

All defined functions:


File /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:

int init_module(void);

static int ksym_lookup_cb(unsigned long *, const char *, void *, unsigned long);

(gdb) quit

dd

For a large kernel module, the symbols are quite sparse. A lot of space are being used for the parasite_blob (631368 bytes, that is a bit more than 78% of the size of the kernel module file). 

The name of the file (kmatryoshka.c) refers to Matryoshka doll, a russian doll, that contains another doll inside. This is the picture of matryoshka taken from wikipedia https://en.wikipedia.org/wiki/Matryoshka_doll :


Well, in order to examine the doll inside we need to take it out first. Reading the matryoshka.c in the Reptile github (https://github.com/f0rb1dd3n/Reptile/blob/master/kernel/kmatryoshka/kmatryoshka.c) we could skip some part of reverse engineering; the init_module function decrypts the parasite_blob using some key. We want to have the decrypted binary, so we somehow need to run the init_module.

(gdb) p init_module()

You can't do that without a process to debug.

(gdb) run

Starting program: /root/lkrg-0.9.1/falc0n.ko 

/bin/bash: /root/lkrg-0.9.1/falc0n.ko: cannot execute binary file

/bin/bash: /root/lkrg-0.9.1/falc0n.ko: Success

During startup program exited with code 126.

(gdb) break init_module

Breakpoint 1 at 0x52: file /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c, line 57.

(gdb) run

Starting program: /root/lkrg-0.9.1/falc0n.ko 

/bin/bash: /root/lkrg-0.9.1/falc0n.ko: cannot execute binary file

/bin/bash: /root/lkrg-0.9.1/falc0n.ko: Success

During startup program exited with code 126.

So far, we are unable to run the init_module function directly. Do we need to do some kernel debugging and single-step into kernel module loading process? No, I found a simpler way: we just need to get the init_module function to execute in gdb.


Part 3. Creating Hello World Program to Run init_module


#include <stdio.h>

extern void init_module();

int main()

{

   printf("Hello world!\n");

   init_module();

   return 0;


}

This is a basic hello world program written in c, that somehow runs init_module. Even we don't have any source code from original rootkit this compiles just fine :

[root@lb lkrg-0.9.1]# cc -c my.c

[root@lb lkrg-0.9.1]# ls -l my.*

-rw-r--r--. 1 root root  122 May 29 10:17 my.c

-rw-r--r--. 1 root root 1568 May 29 10:18 my.o


Now lets try to link both my.o and falc0n.ko :

[root@lb lkrg-0.9.1]# gcc -o debug_doll my.o falc0n.ko

falc0n.ko: In function `ksym_lookup_name':

/tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:52: undefined reference to `kallsyms_on_each_symbol'

/tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:52: undefined reference to `kallsyms_on_each_symbol'

falc0n.ko: In function `current_thread_info':

/usr/src/kernels/3.10.0-1127.10.1.el7.x86_64/./arch/x86/include/asm/thread_info.h:218: undefined reference to `kernel_stack'

/usr/src/kernels/3.10.0-1127.10.1.el7.x86_64/./arch/x86/include/asm/thread_info.h:218: undefined reference to `kernel_stack'

falc0n.ko: In function `init_module':

/tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:76: undefined reference to `__x86_indirect_thunk_rax'

collect2: error: ld returned 1 exit status


Some errors occured because I am being too bold :D. The error cames out during link process. Well, just google around to disable the error, because we don't really care whether the init_module completes, we just want to run the decryption part :


[root@lb lkrg-0.9.1]# gcc -Wl,--unresolved-symbols=ignore-all -o debug_doll my.o falc0n.ko 

[root@lb lkrg-0.9.1]# ls -l debug_doll 

-rwxr-xr-x. 1 root root 721608 May 29 10:22 debug_doll


Voila, we got an executable!


Part 4. Running the Hello World

Lets debug it :


[root@lb lkrg-0.9.1]# gdb ./debug_doll

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7

Copyright (C) 2013 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-redhat-linux-gnu".

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>...

Reading symbols from /root/lkrg-0.9.1/debug_doll...done.

(gdb) info functions

All defined functions:


File /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:

int init_module(void);

static int ksym_lookup_cb(unsigned long *, const char *, void *, unsigned long);


Non-debugging symbols:

0x0000000000400438  _init

0x0000000000400470  puts@plt

0x0000000000400480  __libc_start_main@plt

0x0000000000400490  __gmon_start__@plt

0x00000000004004a0  _start

0x00000000004004d0  deregister_tm_clones

0x0000000000400500  register_tm_clones

0x0000000000400540  __do_global_dtors_aux

0x0000000000400560  frame_dummy

0x000000000040058d  main

0x0000000000400740  __libc_csu_init

0x00000000004007b0  __libc_csu_fini

0x00000000004007b4  _fini

(gdb) info variables

All defined variables:


File /tmp/.ICE-unix/rc1/parasite_loader/p_load.mod.c:

struct module __this_module;

static const char __UNIQUE_ID_retpoline11[12];

static const char __UNIQUE_ID_rhelversion10[16];

static const char __UNIQUE_ID_srcversion9[35];

static const char __UNIQUE_ID_vermagic8[65];

static const struct modversion_info ____versions[4];

static const char __module_depends[9];


File ./arch/x86/include/asm/vvar.h:

static const volatile unsigned long * const vvaraddr_jiffies;

static const int * const vvaraddr_vgetcpu_mode;

static const struct vsyscall_gtod_data * const vvaraddr_vsyscall_gtod_data;


File /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:

static const char __UNIQUE_ID_intree28[9];

static const char __UNIQUE_ID_license27[12];

static char parasite_blob[631368];


File ./arch/x86/include/asm/vvar.h:

static const volatile unsigned long * const vvaraddr_jiffies;

static const int * const vvaraddr_vgetcpu_mode;

static const struct vsyscall_gtod_data * const vvaraddr_vsyscall_gtod_data;


Non-debugging symbols:

0x00000000004007c0  _IO_stdin_used

0x00000000004007c8  __dso_handle

0x0000000000400ab8  __FRAME_END__

0x0000000000600e10  __frame_dummy_init_array_entry

0x0000000000600e10  __init_array_start

0x0000000000600e18  __do_global_dtors_aux_fini_array_entry

0x0000000000600e18  __init_array_end

0x0000000000600e20  __JCR_END__

0x0000000000600e20  __JCR_LIST__

0x0000000000600e28  _DYNAMIC

0x0000000000601000  _GLOBAL_OFFSET_TABLE_

0x0000000000601030  __data_start

0x0000000000601030  data_start

0x000000000069b288  __TMC_END__

0x000000000069b4c8  __bss_start

0x000000000069b4c8  _edata

0x000000000069b4c8  completed.6355

0x000000000069b4d0  _end


The resulting monster is a combination of our hello world code and the kernel module :D

(gdb) break init_module

Breakpoint 1 at 0x4005da: file /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c, line 57.

(gdb) run

Starting program: /root/lkrg-0.9.1/./debug_doll 

Hello world!


Breakpoint 1, init_module () at /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:57

57 /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c: No such file or directory.

Missing separate debuginfos, use: debuginfo-install glibc-2.17-324.el7_9.x86_64

(gdb) bt

#0  init_module () at /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:57

#1  0x00000000004005a5 in main ()


Wonderful, we could single step happily inside here :

(gdb) nexti

0x00000000004005dc 57 in /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c

(gdb) nexti

do_encode (ptr=0x601040 <parasite_blob>, len=631368, key=3368175696) at /tmp/.ICE-unix/rc1/parasite_loader/encrypt/encrypt.h:15

15 /tmp/.ICE-unix/rc1/parasite_loader/encrypt/encrypt.h: No such file or directory.

(gdb) nexti

0x00000000004005e8 15 in /tmp/.ICE-unix/rc1/parasite_loader/encrypt/encrypt.h

(gdb) nexti

15 in /tmp/.ICE-unix/rc1/parasite_loader/encrypt/encrypt.h

(gdb) disas

Dump of assembler code for function init_module:

   0x00000000004005da <+0>: xor    %esi,%esi

   0x00000000004005dc <+2>: mov    $0x9a248,%r9d

   0x00000000004005e2 <+8>: mov    $0xd,%r8d

   0x00000000004005e8 <+14>: mov    %r9d,%eax

=> 0x00000000004005eb <+17>: xor    %edx,%edx

   0x00000000004005ed <+19>: sub    %esi,%eax

   0x00000000004005ef <+21>: mov    %eax,%edi

   0x00000000004005f1 <+23>: div    %r8d

   0x00000000004005f4 <+26>: xor    $0xc8c24850,%edi

   0x00000000004005fa <+32>: mov    %dl,%cl

   0x00000000004005fc <+34>: rol    %cl,%edi

   0x00000000004005fe <+36>: xor    %edi,0x601040(%rsi)

   0x0000000000400604 <+42>: add    $0x4,%rsi

   0x0000000000400608 <+46>: cmp    $0x9a244,%rsi

   0x000000000040060f <+53>: jne    0x4005e8 <init_module+14>

   0x0000000000400611 <+55>: push   %rbp

   0x0000000000400612 <+56>: mov    $0x4005ac,%rdi

   0x0000000000400619 <+63>: mov    %rsp,%rbp

   0x000000000040061c <+66>: push   %r12

   0x000000000040061e <+68>: lea    -0x20(%rbp),%rsi

   0x0000000000400622 <+72>: push   %rbx

   0x0000000000400623 <+73>: sub    $0x10,%rsp

   0x0000000000400627 <+77>: movq   $0x0,-0x18(%rbp)

   0x000000000040062f <+85>: sub    $0x20,%rsp

   0x0000000000400633 <+89>: lea    0xf(%rsp),%rax

   0x0000000000400638 <+94>: and    $0xfffffffffffffff0,%rax

   0x000000000040063c <+98>: movl   $0x5f737973,(%rax)


Actually, reading this code I found this a bit different from the loop in the Reptile source code, the comparison-and-loop code is a bit different :

   0x0000000000400608 <+46>: cmp    $0x9a244,%rsi

   0x000000000040060f <+53>: jne    0x4005e8 <init_module+14>


This compares a value (9a244h, which is 631364 in decimal) with rsi (source index register), and loop backs if its not equal; and the source code is :

	while (len > sizeof(key)) {
		*(unsigned int *)ptr ^= custom_rol32(key ^ len, (len % 13));
		len -= sizeof(key), ptr += sizeof(key);
	}

This might mean the compiler optimization is too powerful for me to understand or the source code being used is different. Never mind that, just run it until the XOR (decryption) parts are complete :

(gdb) break *0x0000000000400611

Breakpoint 2 at 0x400611: file /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c, line 57.

(gdb) continue

Continuing.


Breakpoint 2, init_module () at /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c:57

57 /tmp/.ICE-unix/rc1/parasite_loader/kmatryoshka.c: No such file or directory.


If you are not familiar with gdb, you might just want to continue without creating breakpoint and wait for the program to crash :D.

Now to write the decrypted parasite_blob into a file, first we need to determine the starting memory address and also the ending memory address:

(gdb) x parasite_blob

0x601040 <parasite_blob>: 0x464c457f

(gdb) x parasite_blob+631368

0x69b288: 0x00000000


To dump just use the dump memory command:

(gdb) dump binary memory parasite0.bin parasite_blob parasite_blob+631368

(gdb) quit

A debugging session is active.


Inferior 1 [process 7238] will be killed.


Quit anyway? (y or n) y

[root@lb lkrg-0.9.1]# ls -l parasite0.bin

-rw-r--r--. 1 root root 631368 May 29 10:36 parasite0.bin


Lets see inside it:

[root@lb lkrg-0.9.1]# gdb parasite0.bin

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7

Copyright (C) 2013 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-redhat-linux-gnu".

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>...

Reading symbols from /root/lkrg-0.9.1/parasite0.bin...done.

(gdb) info functions

All defined functions:


File /tmp/.ICE-unix/rc1//rep_mod.c:

void _add(char *, int, int);

void _sub(char *, int, int);

void _xor(char *, int, int);

int exec(char **);

int f_check(void *, ssize_t);

struct task_struct *find_task(pid_t);

int flag_tasks(pid_t, int);

void hide(void);

void hide(void);

int hide_content(void *, ssize_t);

int is_invisible(pid_t);


File /tmp/.ICE-unix/rc1//khook/engine.c:

void khook_cleanup(void);

int khook_init(void);


File /tmp/.ICE-unix/rc1//rep_mod.c:

unsigned int magic_packet_hook(const struct nf_hook_ops *, struct sk_buff *, const struct net_device *, const struct net_device *, 

    int (*)(struct sk_buff *));

char **parse(char *);

int shell_exec_queue(char *, char *, char *, char *);

void shell_execer(struct work_struct *);

void show(void);

static void __khook___d_lookup(void);

static void __khook_audit_alloc(void);


File /tmp/.ICE-unix/rc1//khook/engine.c:

static int __khook_cleanup_hooks(void *);


File /tmp/.ICE-unix/rc1//rep_mod.c:

static void __khook_compat_filldir(void);

static void __khook_compat_filldir64(void);

static void __khook_compat_fillonedir(void);

static void __khook_copy_creds(void);

static void __khook_exit_creds(void);

static void __khook_filldir(void);

static void __khook_filldir64(void);

static void __khook_fillonedir(void);

static void __khook_find_task_by_vpid(void);

static void __khook_inet_ioctl(void);


File /tmp/.ICE-unix/rc1//khook/engine.c:

static void __khook_init(khook_t *);

static int __khook_init_hooks(void *);


File /tmp/.ICE-unix/rc1//rep_mod.c:

static void __khook_load_elf_binary(void);

static void __khook_next_tgid(void);

static void __khook_tcp4_seq_show(void);


We just extracted the actual kernel module !


Part 5. Finding the AUTH key


In the reptile rootkit, there is a 64-bit key that must be used when sending commands to the kernel module. In this post I will just focus on the khook_inet_ioctl function to dissasemble, which looks like this :



Lets check the disassembled binary :

[root@lb lkrg-0.9.1]# gdb parasite0.bin

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7

Copyright (C) 2013 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-redhat-linux-gnu".

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>...

Reading symbols from /root/lkrg-0.9.1/parasite0.bin...done.

(gdb) disas khook_inet_ioctl

Dump of assembler code for function khook_inet_ioctl:

   0x0000000000001210 <+0>: push   %rbp

   0x0000000000001211 <+1>: mov    %rsp,%rbp

   0x0000000000001214 <+4>: push   %r13

   0x0000000000001216 <+6>: mov    %rdi,%r13

   0x0000000000001219 <+9>: push   %r12

   0x000000000000121b <+11>: mov    %rdx,%r12

   0x000000000000121e <+14>: push   %rbx

   0x000000000000121f <+15>: mov    %esi,%ebx

   0x0000000000001221 <+17>: sub    $0x28,%rsp

   0x0000000000001225 <+21>: lock incl 0x0(%rip)        # 0x122c <khook_inet_ioctl+28>

   0x000000000000122c <+28>: cmp    $0xe892ec29,%esi

   0x0000000000001232 <+34>: sete   %al

   0x0000000000001235 <+37>: jne    0x1260 <khook_inet_ioctl+80>

   0x0000000000001237 <+39>: mov    $0xfd651acb,%edx

   0x000000000000123c <+44>: cmp    %rdx,%r12

   0x000000000000123f <+47>: jne    0x1260 <khook_inet_ioctl+80>

   0x0000000000001241 <+49>: mov    0x0(%rip),%esi        # 0x1247 <khook_inet_ioctl+55>

   0x0000000000001247 <+55>: test   %esi,%esi

   0x0000000000001249 <+57>: je     0x1298 <khook_inet_ioctl+136>

   0x000000000000124b <+59>: movl   $0x0,0x0(%rip)        # 0x1255 <khook_inet_ioctl+69>

   0x0000000000001255 <+69>: xor    %eax,%eax

   0x0000000000001257 <+71>: jmp    0x1282 <khook_inet_ioctl+114>

   0x0000000000001259 <+73>: nopl   0x0(%rax)

   0x0000000000001260 <+80>: mov    0x0(%rip),%ecx        # 0x1266 <khook_inet_ioctl+86>

   0x0000000000001266 <+86>: test   %ecx,%ecx

   0x0000000000001268 <+88>: je     0x126e <khook_inet_ioctl+94>

   0x000000000000126a <+90>: test   %al,%al

   0x000000000000126c <+92>: jne    0x12b0 <khook_inet_ioctl+160>

   0x000000000000126e <+94>: mov    %r12,%rdx

   0x0000000000001271 <+97>: mov    %ebx,%esi

   0x0000000000001273 <+99>: mov    %r13,%rdi

   0x0000000000001276 <+102>: mov    0x0(%rip),%rax        # 0x127d <khook_inet_ioctl+109>

   0x000000000000127d <+109>: callq  0x1282 <khook_inet_ioctl+114>

   0x0000000000001282 <+114>: lock decl 0x0(%rip)        # 0x1289 <khook_inet_ioctl+121>

   0x0000000000001289 <+121>: add    $0x28,%rsp

   0x000000000000128d <+125>: pop    %rbx

   0x000000000000128e <+126>: pop    %r12

   0x0000000000001290 <+128>: pop    %r13

   0x0000000000001292 <+130>: pop    %rbp

   0x0000000000001293 <+131>: retq   

   0x0000000000001294 <+132>: nopl   0x0(%rax)

   0x0000000000001298 <+136>: movl   $0x1,0x0(%rip)        # 0x12a2 <khook_inet_ioctl+146>

   0x00000000000012a2 <+146>: xor    %eax,%eax

   0x00000000000012a4 <+148>: jmp    0x1282 <khook_inet_ioctl+114>

   0x00000000000012a6 <+150>: nopw   %cs:0x0(%rax,%rax,1)

   0x00000000000012b0 <+160>: lea    0x8(%rsp),%rdi

   0x00000000000012b5 <+165>: xor    %edx,%edx

   0x00000000000012b7 <+167>: mov    $0x10,%esi

   0x00000000000012bc <+172>: callq  0x12c1 <khook_inet_ioctl+177>

   0x00000000000012c1 <+177>: lea    0x8(%rsp),%rdi

   0x00000000000012c6 <+182>: mov    $0x10,%edx

---Type <return> to continue, or q <return> to quit---


If we read the source code, AUTH is being compared to cmd, and HTUA is being compared to args. I am not quite familiar with the 64-bit world (I learned assembly from 16-bit DOS world :D ) so I need to read about the x86_64 calling convention before proceeding further. By some googling we find this pdf (https://aaronbloomfield.github.io/pdr/book/x86-64bit-ccc-chapter.pdf) which has this important paragraph:

To pass parameters to the subroutine, we put up to six of them into registers (in order: rdi, rsi, rdx, rcx, r8, r9). If there are more than six parameters to the subroutine, then push the rest onto the stack in reverse order (i.e. last parameter first) – since the stack grows down, the first of the extra parameters (really the seventh parameter) parameter will be stored at the lowest address (this inversion of parameters was historically used to allow functions to be passed a variable number of parameters).

So, rdi => first parameter (sock in the source code), rsi => second parameter (cmd), rdx => third parameter (arg). For 32-bit registers, we have edi which is the 32-bit lower half of rdi, esi which is 32-bit lower half of rsi, and edx which is 32-bit lower half of rdx :

Taken from https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture
Taken from https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture

Comparing back to the khook_inet_ioctl which has this code :


static int khook_inet_ioctl(struct socket *sock, unsigned int cmd,
			    unsigned long arg)
{
	int ret = 0;
	unsigned int pid;
	struct control args;
	struct sockaddr_in addr;

	if (cmd == AUTH && arg == HTUA) {

We could also say: the second parameter (cmd) is also stored in esi, and the third parameter (arg) is also stored in edx. 

Based on the analysis above then we concluded that this code :

   0x000000000000122c <+28>: cmp    $0xe892ec29,%esi

   0x0000000000001232 <+34>: sete   %al

   0x0000000000001235 <+37>: jne    0x1260 <khook_inet_ioctl+80>

means comparison of cmd to AUTH, and this code :

   0x000000000000121b <+11>: mov    %rdx,%r12

...

   0x0000000000001237 <+39>: mov    $0xfd651acb,%edx

   0x000000000000123c <+44>: cmp    %rdx,%r12

   0x000000000000123f <+47>: jne    0x1260 <khook_inet_ioctl+80>

means comparison of arg to HTUA.


And... we just extracted the AUTH and HTUA code ! AUTH is 0xe892ec29 and HTUA is 0xfd651acb.


Part 6. Using the AUTH key


The AUTH key is being used in the reptile cmd utility. So I just cloned the userland directory from Reptile github (https://github.com/f0rb1dd3n/Reptile/tree/1e17bc82ea8e4f9b4eaf15619ed6bcd283ad0e17/userland)


Then build cmd using the AUTH and HTUA code :

[root@lb userland]# cc -o my_cmd -DAUTH=0xe892ec29 -DHTUA=0xfd651acb cmd.c

[root@lb userland]# ls -l my_cmd

-rwxr-xr-x. 1 root root 13184 May 29 11:03 my_cmd

Try it for a test drive:

[root@lb userland]# ./my_cmd show

Success!

[root@lb userland]# touch falc0n.txt

[root@lb userland]# ls -l

total 16

-rw-rw-r--. 1 root root 1416 Jun 27  2020 Makefile

drwxrwxr-x. 2 root root   56 Jun 27  2020 client

-rw-rw-r--. 1 root root 3624 Jun 27  2020 cmd.c

drwxrwxr-x. 2 root root   33 Jun 27  2020 crypto

drwxrwxr-x. 2 root root   98 Jun 27  2020 include

-rw-rw-r--. 1 root root 8129 Jun 27  2020 shell.c

drwxrwxr-x. 2 root root   19 Jun 27  2020 transport

[root@lb userland]# ./my_cmd hide

Success!

[root@lb userland]# ls -l

total 48

-rw-rw-r--. 1 root root  1416 Jun 27  2020 Makefile

drwxrwxr-x. 2 root root    56 Jun 27  2020 client

-rwxr-xr-x. 1 root root 13184 May 26 19:34 cmd

-rw-rw-r--. 1 root root  3624 Jun 27  2020 cmd.c

drwxrwxr-x. 2 root root    33 Jun 27  2020 crypto

-rw-r--r--. 1 root root     0 May 29 11:03 falc0n.txt

drwxrwxr-x. 2 root root    98 Jun 27  2020 include

-rwxr-xr-x. 1 root root 13184 May 29 11:03 my_cmd

drwxr-xr-x. 2 root root     6 May 26 19:33 output

-rw-rw-r--. 1 root root  8129 Jun 27  2020 shell.c

drwxrwxr-x. 2 root root    19 Jun 27  2020 transport


It works!


Conclusion


This post shows how could we decrypt and reverse engineer reptile authentication key using standard linux tools such as gdb and gcc. 

Comments

Ely Girl said…
Ehsaas Program provides financial assistance in cash to people who have lost their jobs or have low incomes. The federal government funded and managed .

BISP Program
ehsaas program 8171

Popular posts from this blog

Long running process in Linux using PHP

SAP System Copy Lessons Learned