実行結果(telnet www.yahoo.cp.jp 80 -> GET /index.html HTTP/1.0)
[j05030@~]% telnet www.yahoo.co.jp 80 Trying 203.216.247.225... Connected to www.yahoo.co.jp. Escape character is '^]'. GET /index.html HTTP/1.0 HTTP/1.1 200 OK Date: Tue, 19 Dec 2006 13:35:07 GMT P3P: policyref="http://privacy.yahoo.co.jp/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV" Expires: -1 Pragma: no-cache Cache-Control: no-cache Connection: close Content-Type: text/html; charset=euc-jp <html> <head > <meta http-equiv="Content-Type" content="text/html; charset=euc-jp"> <!--京-- > <title >Yahoo! JAPAN</title> <meta name="description" content="日本最大級のポータルサイト。検索、オークション 、ニュース、メール、コミュニティ、ショッピング、など80以上のサービスを展開。あな たの生活をより豊かにする「ライフ・エンジン」を目指していきます。"> <style type="text/css" media="all"> ・・・以下省略・・・
変更点
実行結果(client.c)
[j05030@tcp:ip]% ./client Connected. Input Keyword = warning: this program uses gets(), which is unsafe. yama Keyword = [yama] / Data = [kawa] Input Keyword = 123 Keyword = [123] / Data = [456] Input Keyword = xyz Keyword = [xyz] / Data = [XYZ] Input Keyword = Disocnnected.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/time.h> #include <unistd.h> #define PORT 5524 /* Port Number */ #define SOCK_MAX 5 /* Max Socket */ #define UNUSED (-1) #define DATAFILE "/Users/j05030/jikken2/tcpip/data.txt" #define DELIMITS " \t\n\r" char datavalue[256]; char buff[256]; char *Modification(char *str, int length); int main() { int s[SOCK_MAX + 1]; int max = 0; int n = 0; int len; fd_set readfds; int clilen; struct sockaddr_in saddr; struct sockaddr_in caddr; char str[1024]; int i, j; int msglen; char *p; if ((s[0] = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } bzero((char *)&saddr, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons(PORT); if ((bind(s[0], (struct sockaddr *)&saddr, sizeof(saddr))) == -1) { perror("bind"); exit(1); } if ((listen(s[0], SOCK_MAX)) == -1) { perror("listen"); exit(1); } max = 1; while (1) { FD_ZERO(&readfds); printf("max: %d\n", max); for (i = 0; i < max; i++) { if (s[i] != UNUSED) { FD_SET(s[i], &readfds); } } if ((n = select(FD_SETSIZE, &readfds, NULL, NULL, NULL)) == -1) { perror("select"); exit(1); } printf("select returns: %d\n", n); for (i = 1; i < max; i++) { if (s[i] != UNUSED) { if (FD_ISSET(s[i], &readfds)) { printf("s[%d] ready for reading\n", i); if ((msglen = read(s[i], str, sizeof(str))) == -1) { /* Failed */ perror("read"); } else if (msglen != 0) { /* Successed */ printf("client[%d]: %s", i, str); /* Modified */ Modification(str,msglen); for (j = 1; j < max; j++) { if (s[j] != UNUSED) { write(s[j], str, strlen(str)); } } } else { printf("client[%d]: connection closed.\n", i); close(s[i]); s[i] = UNUSED; } } } } if (FD_ISSET(s[0], &readfds) != 0) { printf("Accept New one.\n"); len = sizeof(caddr); s[max] = accept(s[0], (struct sockaddr *)&caddr, &len); printf("%d = accept()\n", s[max]); if (s[max] == -1) { perror(NULL); exit(1); } if (max < SOCK_MAX) { printf("client accepted(%d).\n", max); max++; } else { printf("refuse connection.\n"); strcpy(str, "Server is too busy.\n"); write(s[max], str, strlen(str)); close(s[max]); } } } } /* Return */ char *Modification(char *str, int length) { int i; FILE *fp; int n; if((FILE *)NULL == (fp = fopen(DATAFILE,"r"))) return (char *)NULL; n=strlen(str); str[n-1]='\0'; for(;;) { char *p; char *pp; fgets(buff,256,fp); if(feof(fp)) break; if(ferror(fp)) break; if((char *)NULL == (p = strtok(buff,DELIMITS))) continue; if((char *)NULL == (pp = strtok((char *)NULL,DELIMITS))) continue; if(0 == strcmp(p,str)) { fclose(fp); strcpy(str,pp); return str; } } fclose(fp); sprintf(str,"\n"); return str; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define PORT 5524 #define HOST_NAME "nw0530.st.ie.u-ryukyu.ac.jp" main(int argc, char *argv[]) { struct sockaddr_in addr; struct hostent *hp; int fd; int len; char buf[1024]; int ret; /* Make Socket */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); exit(1); } bzero((char *)&addr, sizeof(addr)); if ((hp = gethostbyname(HOST_NAME)) == NULL) { perror("No such host"); exit(1); } bcopy(hp->h_addr, &addr.sin_addr, hp->h_length); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); /* Connect to Server */ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0){ perror("Server connected"); exit(1); } /* Data Read */ while (fgets(buf, 1024, stdin)) { write(fd, buf, 1024); ret = read(fd, buf, 1024); buf[ret] = '\0'; printf("%s\n",buf); } close(fd); exit(0); }
[j05030@~]% ./client_noinetd yama kawa 123 456 xyz XYZ
Source Code
/*** TCP Client ***/ #include#include #include #include #include #include #include #include #define PORT 80 #define SERVICE_NAME "http" #define PROTOCOL "tcp" void Disconnect(int SocketNumber) { close(SocketNumber); } void SendData(int SocketNumber,char *line) { if(strlen(line) != write(SocketNumber,line,strlen(line))) { printf("\7Send Failed.\n"); exit(1); } } int RecvData(int SocketNumber,char *line) { int i; for(i = 0 ;;) { if(1 != read(SocketNumber,line + i,1)){ continue; } else { i++; *(line + i) = (char)0; if(((char)('\n')) == *(line + i - 1)) return i; } } } int main() { int Sock; char Key[256]; char Data[256]; char buff[256]; char HOST_NAME[256]; char answ[3]; int SocketNumber; /* Socket descripter */ struct servent *ServiceEntry; /* service entry */ struct hostent *HostEntry; /* host entry */ struct sockaddr_in sin; /* Socket Entry */ /* Get Service Entry by Service-Name */ if((struct servent *)NULL == (ServiceEntry = getservbyname(SERVICE_NAME,PROTOCOL))) { printf("\7No service [%s].\n",SERVICE_NAME); exit(1); } /* Get Host Entry by Host-Name */ printf("Input server address = "); scanf("%s",HOST_NAME); printf("\n"); if((struct hostent *)NULL == (HostEntry = gethostbyname(HOST_NAME))) { printf("\7No Hosts [%s].\n",HOST_NAME); exit(1); } /* Get Socket */ if(0 > (SocketNumber = (socket(PF_INET,SOCK_STREAM,0)))) { printf("\7Cannot get Socket.\n"); exit(1); } bzero((char *)(&sin), sizeof(sin)); bcopy(HostEntry->h_addr,&sin.sin_addr,HostEntry->h_length); sin.sin_family = PF_INET; sin.sin_port = htons(PORT); if(0 > connect(SocketNumber,(struct sockaddr *)(&sin),sizeof(sin))) { printf("\7Cannot Connect.\n"); exit(1); } Sock = SocketNumber; /* Connect */ printf("Connected.\n"); char *p; printf("download index.html (yes?/no?)\n"); scanf("%s",answ); if ( strcmp(answ,"yes") == 0) { fflush(stdout); strcpy(Key,"/index.html"); if(0 == strlen(Key)){ Disconnect(Sock); printf("Disocnnected.\n"); exit(1); } sprintf(buff,"GET %s HTTP/1.0\r\n",Key); SendData(Sock,buff); sprintf(buff,"Host: %s:%d\r\n", HOST_NAME, PORT); SendData(Sock,buff); sprintf(buff,"\r\n"); SendData(Sock,buff); /* Send It ! */ while (1){ char buf[256]; int size; size = read(Sock, buf, 256); if ( size > 0 ){ write(1, buf, size); } else { break; } } } Disconnect(Sock); printf("Disocnnected.\n"); }
実行結果
[j05030@~]% ./gethtml Input server address = www.u-ryukyu.ac.jp Connected. download index.html (yes?/no?) yes HTTP/1.1 200 OK Date: Tue, 12 Dec 2006 06:33:01 GMT Server: Apache/2.2.2 (Fedora) Last-Modified: Tue, 12 Dec 2006 06:11:30 GMT ETag: "122b27d-23a6-2734a880" Accept-Ranges: bytes Content-Length: 9126 Connection: close Content-Type: text/html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> ####################################### <style type="text/css"> <!-- td { font-size: smaller; } --> </style> <!-- <script language="javascript" src="preview/rollover.js"></script> --> <style type="text/css"> </style> <link href="preview/ryudai2.css" rel="stylesheet" type="text/css"> </head>
ソースコード
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #define BUFFSIZE BUFSIZ int main(int argc, char *argv[]) { int port; int socket_fd; char str[BUFFSIZE]; struct sockaddr_in addr; if(argc >= 3) { printf("Usage: %s [IP addr \n", argv[0]); return 1; } if(argc == 1) { strcpy(str, "127.0.0.1"); } else { strcpy(str, argv[1]); } printf("\"%s\" PortScan Start!!\n", str); for(port=1; port<1024; port++) { if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); return 1; } addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(str); addr.sin_port = htons(port); if(connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { } else { printf("%4d\n", port); } close(socket_fd); } printf("There are using\n"); }
[j05030@~]% ./portscan "127.0.0.1" PortScan Start!! 80 427 548 631 There are using
問題の原因
操作していないバッファdmy[]に値が入ることがあるという問題の原因は, gets()関数に起因する。このgets()関数は読み込む文字列のサイズを 指定できないので,バッファのサイズを超えて読み込みを続けてしまうことがあり, このために、バッファのサイズを超える文字列を読み込み結果として,buf[]に 格納できなくなり,dmy[]に値が入ってしまうことになる。gccのコンパイラーでも warning: this program uses gets(), which is unsafe.と警告が出る。
問題の解決方法
この問題の解決方法は、既に考えられており、fgets()関数を用いることで 解決することができる。fgets()関数では保存先のメモリだけではなく、そのメモリの サイズも指定することができるので、gets()関数のようにバッファのサイズを超えて文字列 を読み込むことはなくなる。 以下にこのgets()をfgets()に書き換えたプログラムを示す。
実行結果(BUFLEN=20)
[j05030@~]% ./bufovf before buf(Len:0) = dmy(Len:0) = warning: this program uses gets(), which is unsafe. zxsdftyui90op;lkjhgfdesw3456789olkjhgfdsw34567890p@;lkjhgfdsdzxfcgvhbjnkjloi98765trewsdfghjkl;:/.;lkjhgtfrdsdfghjkl after buf(Len:115) = zxsdftyui90op;lkjhgfdesw3456789olkjhgfdsw34567890p@;lkjhgfdsdzxfcgvhbjnkjloi98765trewsdfghjkl;:/.;lkjhgtfrdsdfghjkl dmy(Len:0) = Segmentation fault
#include <stdio.h> #include <stdlib.h> #include <string.h> #define BUFLEN 20 int main(int argc, char **argv) { char *rtn; char dmy[BUFLEN]; char buf[BUFLEN]; memset(dmy, '\0', BUFLEN); memset(buf, '\0', BUFLEN); printf("before\n"); printf("buf(Len:%d) = %S\n", strlen(buf), buf); printf("dmy(Len:%d) = %s\n", strlen(dmy), dmy); if ((rtn = fgets(buf,sizeof(buf),stdin) == NULL) { exit(-1); } printf("after\n"); printf("buf(Len:%d) = %s\n", strlen(buf), buf); printf("dmy(Len:%d) = %s\n", strlen(dmy), dmy); }
[j05030@~]% ./bufovf before buf(Len:0) = dmy(Len:0) = 1^[23ertyhjkl;/:lmnbgfcdxzasdfgyhujiop@^-09754e3wqertyuiop@-p0o9i8uytrewsdfghjk:;lkiujyhtredrftgyhujikop@[ after buf(Len:19) = 13ertyhjkl;/:lmnb dmy(Len:0) =
セキュリティ上の問題
バッファオーバーフローを悪用した不正アクセスを行なうには高度な知識が必要である、 しかし、これが実行できるとバッファオーバーフローを引き起こしたプログラムが持っているアクセス権 の範囲で任意の動作を行なうことが可能となる。 一般にWebサーバなど、インターネット経由でサービスを提供するプログラムは管理者権限で 稼動しているため、こういったプログラムのバッファオーバーフローを使われてしまうと、 不正侵入者に管理者権限を奪われることもある。