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 |
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!
Comments
BISP Program
ehsaas program 8171