翻譯作品一之Service Banner Fingerprinting in C

 翻譯作品一之Service Banner Fingerprinting in C

                               ---nightcat

 

一.前言:

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


二.正文:
                              服務banner的識別

  1.0   介紹

這篇文章是對modular's tcpscan 文章系列的補充,覆蓋瞭如何用 c 編程來進行服務banner的識別。同時也假設你已經理解了tcpscan系列文章 及 掌握了http和ftp的協議方面的知識。文章中給出的完整的原代碼都是單獨的,目的是想讀者能簡單的加這麼原代碼到自己的程序中去!所有的函數所要求的最小的庫函數包括:

  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <netdb.h>

 2.0 ssh/smtp 指紋識別

在開始編寫代碼的之前,先看看手工識別的banner指紋,讓我們用目前爲止最簡單的服務sshd來進行指紋識別。因爲要完成它,只要兩步就可以。連接到目標主機,服務banner就自動回返回所要的信息給我們。

這個過程可以可視化爲:

  *********************      ***********************      ******************
  * CONNECT TO DAEMON * ===> * DAEMON SHOWS BANNER * ===> * GET THE BANNER *
  *********************      ***********************      ******************

先用telnet連接到ssh daemon 就可以得到返回的banner:


  snow:~# telnet truncode.org 22
  Trying 207.44.194.142...
  Connected to truncode.org.
  Escape character is '^]'.
  SSH-1.99-OpenSSH_3.1p1
  ^]
  telnet> quit
  Connection closed.
  snow:~# 

你可以看到當你連接到目標主機的,這個ssh daemon 會自動的返回banner的版本:"SSH-1.99-OpenSSH_3.1p1" . 這個過程是同樣輕易的應用到smtp服務banner的判斷.


現在你可以連接到目標主機的25端口:

 
  snow:~# telnet euronet.nl 25
  Trying 194.134.0.10...
  Connected to euronet.nl.
  Escape character is '^]'.
  220 pop1.euronet.nl ESMTP Postfix
  ^]
  telnet> quit
  Connection closed.
  snow:~#

 

沒有什麼其他的方法來識別這兩種服務。因爲這兩個服務的識別都是一樣的,我只討論ssh daemon 識別函數的代碼。 你可以指定端口爲smtp.下面是一個識別ssh daomon 的代碼實例:

  char *grab_sshb(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure *//*遠程地址結構*/
    char *buf_s;                  /* buffer string pointer    *//*緩衝區字符串指針*/
    char *bnr_s;                  /* banner string pointer    *//*banner字符串指針*/
    int sockfd;                   /* socket file descriptor   *//*socket 文件描述符*/

    buf_s = (char *)malloc(250);
//建立套接字
    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);
//連接
    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }
//接受字節
    if(recv(sockfd, buf_s, 250, 0) <= 0) { close(sockfd); return NULL; }

    bnr_s = (char *)malloc(strlen(buf_s)-1);
    strncpy(bnr_s, buf_s, strlen(buf_s)-1);

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }


這段原代碼相當簡潔,我們可以正確的返回和先前可視化一樣的過程:連接到目標主機,接收目標主機返回的的banenr,複製查詢的的banner ,最後關閉連接。


  3.0 http/ftp 識別

這個技術稍微比ssh和smtp難點點,因爲我們事實上要和服務器進行執行一些信息交互.一個顯而易件的不同就是我們需要提供一些額外的字符來激發這項技術去工作.我們開始http服務識別,這個服務不能自動的呈現任何的東西在連接後。而需要送出一個請求:"HEAD / HTTP/1.0/n/n"命令到服務,"/n/n"的意思就是重複兩次回車鍵.我們可以再次得到可視化的過程:

 *********************      ****************      ***********************
  * CONNECT TO SERVER * ===> * REQUEST HEAD * ===> * SERVER SENDS BANNER *
  *********************      ****************      ***********************
                                                                ||
                      //
         ***************      ******************
        * FILTER DATA * <=== * GET THE BANNER *
       ***************      ******************

連接到telnet和接收banner.在代碼的最後進行數據過濾,注意以下的過程:

  snow:~# telnet www.euronet.nl 80
  Trying 194.134.0.158...
  Connected to www.euronet.nl.
  Escape character is '^]'.
  HEAD / HTTP/1.0

  HTTP/1.1 302 Found
  Date: Fri, 21 Mar 2003 21:06:55 GMT
  Server: Apache/1.3.26 (Unix)
  Location: http://home.euronet.nl/
  Connection: close
  Content-Type: text/html; charset=iso-8859-1

  Connection closed by foreign host.
  snow:~#


你可以看到當發出了HEAD / HTTP/1.0命令後會返回很多的數據信息。事實上我們感興趣的信息是"Server:"後面的部分。這是我們過濾代碼所要實現的。這個代碼將會執行以下的步驟:連接到目標主機的服務,送出命令,接收返回信息和過濾數據信息:

  char *ltnet_httpb(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure */
    char *buf_s;                  /* buffer string pointer    */
    char *bnr_s;                  /* banner string pointer    */
    char *str_p;                  /* pointer to Server: part  */
    int sockfd;                   /* socket file descriptor   */
    int itr_s0;                   /* string iterate integer   */
    int itr_s1;                   /* string iterate integer   */

    buf_s = (char *)malloc(1000);

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);

    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }

    if(send(sockfd, "HEAD / HTTP/1.0/n/n", 17, 0) <= 0) {
      close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }

    if((str_p = strstr(buf_s, "Server:")) == NULL) {
      close(sockfd); return NULL; }

    for(itr_s0 = 0; itr_s0 < strlen(str_p); itr_s0++) {
      if(str_p[itr_s0] == '/n') break;
    }

    bnr_s = (char *)malloc(itr_s0 - 8);

    for(itr_s1 = 0; itr_s1 < itr_s0; itr_s1++) {
      if(str_p[itr_s1 + 8] == '/n') break;
      bnr_s[itr_s1] = str_p[itr_s1 + 8];
    }

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }

請密切注意執行實際過濾的部分,首先這段代碼找到"Server:" 行,返回一個指向這行的指針:

  if((str_p = strstr(buf_s, "Server:")) == NULL) {
    close(sockfd); return NULL; }

現在指針"str_p" 指向"Server:" 行的緩衝區的開始地址。下一步我們想去做的是得到實際banner前面是沒有帶"Server:"的字符行,從保存到"bnr_s".我們開始這個過程要先分配內存給"bnr_s"。

bnr_s = (char *)malloc(itr_s0 - 8);
請好好看看這一行,我們分配給"Server:" 行的字節數,減去8。這8個字節就是"Server: "  正是我們想刪除掉的。接下來我們所做的是重複完全的字符串,複製它到"bnr_s"。現在"bnr_s"包含沒有"Server: "字符的字符串。

我們轉入ftp部分。我們討論兩種識別ftp服務banner的方法。第一個是很容易的,因爲它僅僅混合ssh/smtp和http識別的方法。連接到目標主機,取得banner ,隨後過濾它,這個可視化過程就好象:

  *********************      ***********************      ******************
  * CONNECT TO DAEMON * ===> * DAEMON SHOWS BANNER * ===> * GET THE BANNER *
  *********************      ***********************      ******************
                                                                   ||
                                     //
                                   ***************
                                        * FILTER DATA *
                                 ***************

如果我們用telent來效仿這個過程,它看起來就想以下:
  snow:~# telnet euronet.nl 21
  Trying 194.134.0.10...
  Connected to euronet.nl.
  Escape character is '^]'.
  220 gaia.euronet.nl FTP server (Version wu-2.4.2-academ (3) Fri Jun 23
  20:47:26 MET DST 2000) ready.
  ^]
  telnet> quit
  Connection closed.
  snow:~#

正如你看到的,它會直接的呈現banner(這不是所有情況下都會發生的)在"220" 後面,這個可能的僅僅是得到版本信息。正如http服務哪個所做的。下面的代碼事實上只是我們之前所做的混合:

  char *ltnet_ftpb(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure */
    char *buf_s;                  /* buffer string pointer    */
    char *bnr_s;                  /* banner string pointer    */
    char *str_p;                  /* pointer to Server: part  */
    int sockfd;                   /* socket file descriptor   */
    int itr_s0;                   /* string iterate integer   */
    int itr_s1;                   /* string iterate integer   */

    buf_s = (char *)malloc(250);

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);

    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }

    if(recv(sockfd, buf_s, 250, 0) <= 0) { close(sockfd); return NULL; }

    if((str_p = strstr(buf_s, "220")) == NULL) { close(sockfd); return NULL; }

    for(itr_s0 = 0; itr_s0 < strlen(str_p); itr_s0++) {
      if(str_p[itr_s0] == '/n') break;
    }

    bnr_s = (char *)malloc(itr_s0 - 8);

    for(itr_s1 = 0; itr_s1 < itr_s0; itr_s1++) {
      if(str_p[itr_s1 + 4] == '/n') break;
      bnr_s[itr_s1] = str_p[itr_s1 + 4];
    }

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }

雖然這個ftp識別的方法是十分的簡潔,但不總是有效。除次外,有另外一種識別ftp服務的方法,
這項技術要求我們用匿名用戶進去,這個方法要求服務能夠允許匿名登陸。如果我們能以匿名的身份成功進入,我們能想ftp服務發出"SYST"命令,ftp服務將會送回系統類型。描述這個方法的圖解可以可視化爲:
  *********************      *******************      ******************
  * CONNECT TO DAEMON * ===> * LOGIN TO SERVER * ===> * GET THE BANNER *
  *********************      *******************      ******************
                                                              ||
                                //
                                ***************
                                           * FILTER DATA *
                             ***************

如果我們用telnet來效仿,它看起來就象如下:

 snow:~# telnet ftp.cdrom.com 21
  Trying 208.217.74.248...
  Connected to cdrom.wip.digitalriver.com.
  Escape character is '^]'.
  220 drftp.digitalriver.com NcFTPd Server (licensed copy) ready.
  USER anonymous
  331 Guest login ok, send your complete e-mail address as password.
  PASS [email protected]
  230-You are user #28 of 300 simultaneous users allowed.
  230-
  230 Logged in anonymously.
  SYST
  215 UNIX Type: L

儘管如此,這段代碼沒有出示與先前我們所做的有多大的差異,唯一的不同就是我們送出多重命令到服務("USER", "PASS" and "SYST"),在這個過程之後,我們再次過濾數據信息,下面是實現的代碼:

char *ltnet_ftps(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure */
    char *buf_s;                  /* buffer string pointer    */
    char *bnr_s;                  /* banner string pointer    */
    char *str_p;                  /* pointer to Server: part  */
    int sockfd;                   /* socket file descriptor   */
    int itr_s0;                   /* string iterate integer   */
    int itr_s1;                   /* string iterate integer   */

    buf_s = (char *)malloc(1000);

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);

    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }

    if(send(sockfd, "USER anonymous/n", 15, 0) <= 0) {
      close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }
    if(send(sockfd, "PASS [email protected]/n", 26, 0) <= 0) {
      close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }
    if(send(sockfd, "SYST/n", 5, 0) <= 0) { close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }

    if((str_p = strstr(buf_s, "UNIX Type:")) == NULL) {
      close(sockfd); return NULL; }

    for(itr_s0 = 0; itr_s0 < strlen(str_p); itr_s0++) {
      if(str_p[itr_s0] == '/n') break;
    }

    bnr_s = (char *)malloc(itr_s0 - 8);

    for(itr_s1 = 0; itr_s1 < itr_s0; itr_s1++) {
      if(str_p[itr_s1 + 11] == '/n') break;
      bnr_s[itr_s1] = str_p[itr_s1 + 11];
    }

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }

  4.0 編輯更多的代碼

當然有很多的方法你可以識別的。但是我希望這篇文章可以幫助你明白如何去開始。如果是打算去執行這段代碼,你可能要整理一下,同時也要加入更多的錯誤檢查!

copyright(c) 2003 truncode.org

 

 

  三.附原文:

Service Banner Fingerprinting in C

 _                         _    
| |_ ___ _ _ ___ ___ ___ _| |___
|  _|  _| | |   |  _| . | . | -_|
|_| |_| |___|_|_|___|___|___|___|

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


0x01 INTRODUCTION

This paper is written as a supplement to modular's tcpscan series. This paper
covers how to write C programs that will perform Banner Fingerprinting. This
paper assumes that you have already understood the tcpscan series from modular,
and that you possess knowledge of the following protocols: HTTP and FTP. The
complete source code in this article consists of stand-alone functions with the
intention to allow the reader to implement them easily into his own programs.
All functions require the following minimal list of includes:


  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <netdb.h>


0x02 SSH/SMTP FINGERPRINTING

Before we start coding, we will have a look at the subject of manual banner
fingerprinting. Let uss start this one off with by far the easiest service to
fingerprint: SSHD. This is because we actually only have two things to
accomplish: connect to the SSH daemon at the target host and retrieve the
banner that will be automatically presented to us.

The process might be visually presented like so:


  *********************      ***********************      ******************
  * CONNECT TO DAEMON * ===> * DAEMON SHOWS BANNER * ===> * GET THE BANNER *
  *********************      ***********************      ******************


Start by connecting with telnet to a SSH daemon and retrieve the banner:


  snow:~# telnet truncode.org 22
  Trying 207.44.194.142...
  Connected to truncode.org.
  Escape character is '^]'.
  SSH-1.99-OpenSSH_3.1p1
  ^]
  telnet> quit
  Connection closed.
  snow:~# 


As you can see after connecting to the target host, the SSH daemon will
automatically present the version banner: "SSH-1.99-OpenSSH_3.1p1". This
process is relatively easy and can also be applied as well to the SMTP
service banner.

Now we connect to port 25 on the target host:


  snow:~# telnet euronet.nl 25
  Trying 194.134.0.10...
  Connected to euronet.nl.
  Escape character is '^]'.
  220 pop1.euronet.nl ESMTP Postfix
  ^]
  telnet> quit
  Connection closed.
  snow:~#


Well there is not much to fingerprinting these two services. Since the process
for fingerprinting these services is identical, I will only discuss the code
for the SSH daemon fingerprint function. You can specify the port for SMTP. The
following is an example of code that will fingerprint the SSH daemon:


  char *grab_sshb(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure */
    char *buf_s;                  /* buffer string pointer    */
    char *bnr_s;                  /* banner string pointer    */
    int sockfd;                   /* socket file descriptor   */

    buf_s = (char *)malloc(250);

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);

    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }

    if(recv(sockfd, buf_s, 250, 0) <= 0) { close(sockfd); return NULL; }

    bnr_s = (char *)malloc(strlen(buf_s)-1);
    strncpy(bnr_s, buf_s, strlen(buf_s)-1);

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }


This source code is rather straight forward. We do exactly the same as
presented in the former visual process: connect to the target host and receive
the banner it sends, then copy the banner in question and close the connection.


0x03 HTTP/FTP FINGERPRINTING

This technique is slightly more difficult than SSH and SMTP because we actually
need to perform some interaction with the server. An obvious difference is that
we need to incite some minor string magic for this technique to work. We begin
with HTTP server fingerprinting. The server does not automatically present any
banner on connect. A request to the server is needed by sending the:
"HEAD / HTTP/1.0/n/n" command, where "/n/n" represents <ENTER> twice. We state
this again visually:


  *********************      ****************      ***********************
  * CONNECT TO SERVER * ===> * REQUEST HEAD * ===> * SERVER SENDS BANNER *
  *********************      ****************      ***********************
                                                                ||
                      //
         ***************      ******************
        * FILTER DATA * <=== * GET THE BANNER *
       ***************      ******************          


Connect with telnet and retreive the banner. The data filtering will only be
done with the final code. Pay attention to the following process:


  snow:~# telnet www.euronet.nl 80
  Trying 194.134.0.158...
  Connected to www.euronet.nl.
  Escape character is '^]'.
  HEAD / HTTP/1.0

  HTTP/1.1 302 Found
  Date: Fri, 21 Mar 2003 21:06:55 GMT
  Server: Apache/1.3.26 (Unix)
  Location: http://home.euronet.nl/
  Connection: close
  Content-Type: text/html; charset=iso-8859-1

  Connection closed by foreign host.
  snow:~#


As you can see the server returns a lot of data after the HEAD / HTTP/1.0
command. Actually the only data we are interested in is the banner behind the
"Server:". This is what our filter code will achieve. The code will perform the
following: connect to the server at the target host, send the command, receive
the server HEAD and filter the data:


  char *ltnet_httpb(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure */
    char *buf_s;                  /* buffer string pointer    */
    char *bnr_s;                  /* banner string pointer    */
    char *str_p;                  /* pointer to Server: part  */
    int sockfd;                   /* socket file descriptor   */
    int itr_s0;                   /* string iterate integer   */
    int itr_s1;                   /* string iterate integer   */

    buf_s = (char *)malloc(1000);

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);

    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }

    if(send(sockfd, "HEAD / HTTP/1.0/n/n", 17, 0) <= 0) {
      close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }

    if((str_p = strstr(buf_s, "Server:")) == NULL) {
      close(sockfd); return NULL; }

    for(itr_s0 = 0; itr_s0 < strlen(str_p); itr_s0++) {
      if(str_p[itr_s0] == '/n') break;
    }

    bnr_s = (char *)malloc(itr_s0 - 8);

    for(itr_s1 = 0; itr_s1 < itr_s0; itr_s1++) {
      if(str_p[itr_s1 + 8] == '/n') break;
      bnr_s[itr_s1] = str_p[itr_s1 + 8];
    }

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }


Take a close look at the section that performs the actual filtering. First the
code looks at the "Server:" line and returns a pointer to it:


  if((str_p = strstr(buf_s, "Server:")) == NULL) {
    close(sockfd); return NULL; }


Now "str_p" points to where the "Server:" line starts in the buffer. The next
thing we want to do is retrieve the actual banner without the "Server:" line in
front of it and put it into "bnr_s". We start this process my allocating memory
to "bnr_s"


  bnr_s = (char *)malloc(itr_s0 - 8);


Please take a good look at this line. We allocate the amount of bytes on the
"Server:" line, minus 8. The 8 bytes represent: "Server: " which we want to
strip off. The next thing we do is iterate through the string and copy it to
"bnr_s". Now "bnr_s" contains the stripped "Server: " string.

We will move onto the FTP section now. We discuss two ways of fingerprinting
the FTP server. The first one is easy, because it's just a mix of the SSH/SMTP
and HTTP fingerprinting. Connect to to the target host, get the banner and then
filter it. This can be visually presented like so:


  *********************      ***********************      ******************
  * CONNECT TO DAEMON * ===> * DAEMON SHOWS BANNER * ===> * GET THE BANNER *
  *********************      ***********************      ******************
                                                                   ||
                                     //
                                   ***************
                                        * FILTER DATA *
                                 ***************


If we emulate this process with telnet, it will look like the following:


  snow:~# telnet euronet.nl 21
  Trying 194.134.0.10...
  Connected to euronet.nl.
  Escape character is '^]'.
  220 gaia.euronet.nl FTP server (Version wu-2.4.2-academ (3) Fri Jun 23
  20:47:26 MET DST 2000) ready.
  ^]
  telnet> quit
  Connection closed.
  snow:~#


As you can see it will directly present the banner (this is not what happens in
all cases) after the "220" and then it is possible to strip it down to the
version only, just like what was done with the HTTP server. The code is
actually just a mix of what we have done before:


  char *ltnet_ftpb(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure */
    char *buf_s;                  /* buffer string pointer    */
    char *bnr_s;                  /* banner string pointer    */
    char *str_p;                  /* pointer to Server: part  */
    int sockfd;                   /* socket file descriptor   */
    int itr_s0;                   /* string iterate integer   */
    int itr_s1;                   /* string iterate integer   */

    buf_s = (char *)malloc(250);

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);

    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }

    if(recv(sockfd, buf_s, 250, 0) <= 0) { close(sockfd); return NULL; }

    if((str_p = strstr(buf_s, "220")) == NULL) { close(sockfd); return NULL; }

    for(itr_s0 = 0; itr_s0 < strlen(str_p); itr_s0++) {
      if(str_p[itr_s0] == '/n') break;
    }

    bnr_s = (char *)malloc(itr_s0 - 8);

    for(itr_s1 = 0; itr_s1 < itr_s0; itr_s1++) {
      if(str_p[itr_s1 + 4] == '/n') break;
      bnr_s[itr_s1] = str_p[itr_s1 + 4];
    }

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }


Although this way of FTP fingerprinting is fairly straight forward, it may not
always be effective. But there is another way of fingerprinting the FTP server,
a technique that requires us to login with the anonymous account. This method
requires the server to have the anonymous login enabled. If we can succesfully
log in as an anonymous user we can give the "SYST" command and the FTP server
will send back the system type. The scheme that represents this method is
presented visually like so:


  *********************      *******************      ******************
  * CONNECT TO DAEMON * ===> * LOGIN TO SERVER * ===> * GET THE BANNER *
  *********************      *******************      ******************
                                                              ||
                                //
                                ***************
                                           * FILTER DATA *
                             ***************


If we emulate this with telnet, it will look like the following:


  snow:~# telnet ftp.cdrom.com 21
  Trying 208.217.74.248...
  Connected to cdrom.wip.digitalriver.com.
  Escape character is '^]'.
  220 drftp.digitalriver.com NcFTPd Server (licensed copy) ready.
  USER anonymous
  331 Guest login ok, send your complete e-mail address as password.
  PASS [email protected]
  230-You are user #28 of 300 simultaneous users allowed.
  230-
  230 Logged in anonymously.
  SYST
  215 UNIX Type: L8


Still this code does not show a lot of difference compared to what we have done
before . The only difference is that we send multiple commands to the server
("USER", "PASS" and "SYST"). After that process we just filter the data again.
This is the code to achieve that:


  char *ltnet_ftps(char *t_addr, int t_port) {
    struct sockaddr_in rmtaddr;   /* remote address structure */
    char *buf_s;                  /* buffer string pointer    */
    char *bnr_s;                  /* banner string pointer    */
    char *str_p;                  /* pointer to Server: part  */
    int sockfd;                   /* socket file descriptor   */
    int itr_s0;                   /* string iterate integer   */
    int itr_s1;                   /* string iterate integer   */

    buf_s = (char *)malloc(1000);

    sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    rmtaddr.sin_family = AF_INET;
    rmtaddr.sin_port = htons(t_port);
    rmtaddr.sin_addr.s_addr = inet_addr(t_addr);

    if(connect(sockfd, (struct sockaddr *)&rmtaddr,
      sizeof(struct sockaddr)) != 0) { close(sockfd); return NULL; }

    if(send(sockfd, "USER anonymous/n", 15, 0) <= 0) {
      close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }
    if(send(sockfd, "PASS [email protected]/n", 26, 0) <= 0) {
      close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }
    if(send(sockfd, "SYST/n", 5, 0) <= 0) { close(sockfd); return NULL; }
    if(recv(sockfd, buf_s, 1000, 0) <= 0) { close(sockfd); return NULL; }

    if((str_p = strstr(buf_s, "UNIX Type:")) == NULL) {
      close(sockfd); return NULL; }

    for(itr_s0 = 0; itr_s0 < strlen(str_p); itr_s0++) {
      if(str_p[itr_s0] == '/n') break;
    }

    bnr_s = (char *)malloc(itr_s0 - 8);

    for(itr_s1 = 0; itr_s1 < itr_s0; itr_s1++) {
      if(str_p[itr_s1 + 11] == '/n') break;
      bnr_s[itr_s1] = str_p[itr_s1 + 11];
    }

    close(sockfd);
    free(buf_s);
    return bnr_s;
  }


0x04 WEAVE MORE CODE

Of course there is a lot more you can fingerprint, but I hope this paper has
helped to show you how you might begin. If you are going to implement the code
in this paper, you might clean it up and add some error checking.

copyright(c) 2003 truncode.org

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章