Monday, February 13, 2012

Setting up Joomla! as a test website

Joomla! has quite a lot of sample data that is useful for HTTP stress-testing. It provides a variety of resources that can be parsed and retrieved by the http_bot of botloader. Setup is easy, but I thought I'd put it on record so people can follow the installation quickly.

  1. On Ubuntu and other versions of Linux you'll need to install Mysql, php5 and apache2. To get the php to work you'll need the apache php module (libapache2-mod-php5). For the mysql you'll need to install both the server and client. When you install mysql server it asks for a user name and password. Use "root" and give any password, but remember it, because you'll need it later.
  2. Now download Joomla!. Unzip the files and rename the directory to "joomla". Now copy the joomla directory to /var/www or wherever your web-root is located:
    cp -r joomla /var/www
    Now make sure that the installer can modify the joomla directory. cd into /var/www and type:
    sudo chown -R www-data joomla
    At least on Ubuntu 'www-data' is the name of the user who is running apache. Or you can use chmod -R +w joomla if you prefer, but that's a lot less secure, though it doesn't matter on a local testbed.
  3. Now edit index.html, which you'll find in /var/www, and add a line somewhere in the body of the HTML:
    <p>Why not visit our wonderful <a href="/joomla/">Joomla! site</a>?</p>
    This provides a link into the main sample data which http_bot will follow when attacking the site. Otherwise it will only find index.html, and keep downloading that - pretty ineffective. So this step is important
  4. Now run the Joomla! installer. It's located at http://localhost/joomla/. Click through the pages, making sure that it detects Mysql.
    • If it says that the installation directory is unwritable, try the chmod -R +w joomla command from /var/www.
    • If it says that mysql is undetectable you need to install something - check that you have the mysql plugin for Apache.
    • When it asks for the mysql username and password give the ones you specified above in step 1.
    • When it asks if you want to install sample data, say YES.
    • For the rest, just follow the suggested options
  5. Now test the installation. Navigate to http://localhost, click on the link you created earlier and make sure that the website is all working. If it says "downloading" when you access a php page, you must have failed to install php5 correctly.

Sunday, February 12, 2012

Measure CPU usage on Windows with PDH

On Windows to measure the CPU usage you need to use the Performance Data Helper (PDH). Microsoft distribute a dll (pdh.dll). Although not ideal you can link with this. Pdh.lib would be better but I have no idea where to get it. I'm going to describe how to measure total CPU usage using pdh.dll, and three headers, windows.h, pdh.h and pdhmsg.h. The latter two come with the Microsoft SDK. The tools I used were the MinGW tool set in NetBeans.

You have to do five things:

  1. Create a query using PdhOpenQuery.
  2. Add a counter to it via PdhAddCounter
  3. Call PdhCollectQueryData twice on the query, sleeping in between
  4. Call PdhGetFormattedCounterValue on the counter
  5. Close the query

Now you should have a formatted data value you can display using printf or whatever. Here's a minimal program that does it (proper error-handling is left as an exercise for the reader):

PDH_HQUERY query;
PDH_STATUS status = PdhOpenQuery(NULL,(DWORD_PTR)NULL,&query);
if (status==ERROR_SUCCESS )
{
  HCOUNTER hCounter;
  status = PdhAddCounter( query, 
    "\Processor(_Total)\% Processor Time", 0, &hCounter );
    if (status==ERROR_SUCCESS)
    {
      status = PdhCollectQueryData(query);
      if (status==ERROR_SUCCESS)
      {
        Sleep(1000);
        status = PdhCollectQueryData(query);
        DWORD ret;
        PDH_FMT_COUNTERVALUE value;
        status = PdhGetFormattedCounterValue(hCounter, 
          PDH_FMT_DOUBLE|PDH_FMT_NOCAP100,&ret,&value);
        if (status==ERROR_SUCCESS)
        {
          printf("CPU Total usage: %2.2f\n",value.doubleValue);
        }
        else
          printf("error\n");
      }
      else
        printf("error\n");
    }
    else
      printf("error\n");
    PdhCloseQuery( query );
  }
  else
    printf("error\n");
}

Tuesday, October 25, 2011

snmp_bot

snmp_bot is a library for botloader. Instead of attacking a target snmp_bot gathers data from it. It works best when the data-gathering and the attacking are carried out on separate networks:

Configuring snmp_bot

To configure snmp_bot you need to specify an option-string in the conf file which you feed to botloader. Because data-gathering bots behave differently from attack bots the name of snmp_bot is prefixed with '#':

#snmp_bot 1 debug=1 time=120 oids=inOctets:.1.3.6.1.4.1.8072.2.6.1.1.0,droppedOctets:.1.3.6.1.4.1.8072.2.6.1.2.0,outOctets:.1.3.6.1.4.1.8072.2.6.1.3.0,cpuLoad1Min:.1.3.6.1.4.1.8072.2.6.1.4.0,userCPU%:.1.3.6.1.4.1.8072.2.6.1.5.0,systemCPU%:.1.3.6.1.4.1.8072.2.6.1.6.0,realMemoryFree%:.1.3.6.1.4.1.8072.2.6.1.7.0,totalMemoryFree%:.1.3.6.1.4.1.8072.2.6.1.8.0 dest_ip=192.168.200.68

This must all be on one line, so it's a bit difficult to edit. The snmp_oids are specified via the oids option. This consists of a comma-separated list of oids, with an optional column-heading separated by a colon. So the oid value "droppedOctets:.1.3.6.1.4.1.8072.2.6.1.2.0" means "monitor the oid .1.3.6.1.4.1.8072.2.6.1.2.0" on the target and write the values to a file with the heading "droppedOctets". The interval for sampling the oid value is set to 5 seconds, but you can change this when invoking botloader on the commandline:

sudo botloader -c snmp_bot.conf -i en0 -t 10

This means that the sampling interval should be 10 seconds instead of 5.

Other values

Botloader itself takes charge of gathering data from any data bots it is asked to monitor. But it also gathers sent and received counts from the attack bots and adds that to the log. So you will find columns "sent" and "rcvd" also in the output.

The default output file is called "data.dat". If you rename this as "something.txt" you can load it into Excel or OpenOffice spreadsheet as CSV format, using tabs as delimters. Then you can see the table of values properly:

Sunday, August 7, 2011

Broken pipe on write

This one puzzled me for a bit. I opened a socket, bound it to a local interface alias address, called connect to a remote machine. Everything was fine. No errors, errno = 0. Then I called write on the socket and it says: "broken pipe". It seems that the cause was that I forgot to set the family for the call to connect:

struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons( atoi(serv) );
if ( !inet_pton(AF_INET, host, &servaddr.sin_addr) )
{
 printf("inet_pton failed\n");
 rc = -1;
}
else if ( connect(fd,(const struct sockaddr *)&servaddr, 
    sizeof(servaddr)) == 0 )
{
    printf("Connected\n");
    // return connected socket
    rc = fd;
}

So I thought I'd file this one for reference, in case I get it again. It's amazing how a simple typo can get you into so much trouble.

Sunday, November 7, 2010

Getting/Setting the netmask in Linux/BSD

Recently I have been wrestling with a method for generating multiple aliases for one interface, as in the last post. Part of this was finding suitable ip-addresses to act as aliases. My first idea was to choose free ip-addresses on the local subnet, depending on how big it was, then pinging all possible addresses to find out which ones were already taken. This turned out much more complex than I had thought, unreliable and slow.

Then I had the idea of just expanding the interface's netmask up a level, ignoring all the addresses of the existing subnet without testing them, and generating alias ips that couldn't possibly conflict with any already on the network. To do that I needed to get and set the current netmask programmatically. I didn't find much on the setting (you won't find it in Steven's book, for example). So I wrote my own two routines get_netmask and set_netmask and put them into an example program that even works on BSD:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <string.h>

/**
 * @param skfd an open socket
 * @param intf the interface name, e.g. eth0
 * @return the netmask as a string
 */
char *get_netmask( int skfd, char *intf )
{
    struct ifreq ifr;
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy( ifr.ifr_name, intf, IFNAMSIZ-1 );
    if ( ioctl(skfd,SIOCGIFNETMASK,&ifr) == -1 )
    {
        printf("could not read interface %s\n",intf);
        exit( 0 );
    }
    /* display result */
    return inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
}
/**
 * @param skfd an open socket
 * @param intf the interface name, e.g. eth0
 * @param newmask the new mask a a string e.g. "255.255.0.0"
 */
void set_netmask( int skfd, char *intf, char *newmask )
{
    struct ifreq ifr;
    unsigned int dst;
    struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
    memset(&ifr, 0, sizeof(ifr));
    sin->sin_family = AF_INET;
    if ( !inet_pton(AF_INET, newmask, &sin->sin_addr) )
    {
        printf("failed to convert netmask\n");
        exit( 0 );
    }
    strncpy( ifr.ifr_name, intf, IFNAMSIZ-1 );
    if ( ioctl(skfd,SIOCSIFNETMASK,&ifr) == -1 )
    {
        printf("could not read interface %s\n",intf);
        exit( 0 );
    }
}
/**
 * main entry routine
 * accepts 2 arguments: the interface name and the new netmask
 * e.g. ./intf eth0 255.255.0.0
 */
int main( int argc, char **argv )
{
    char *str;
    int skfd = socket( AF_INET, SOCK_DGRAM, 0);
    if ( skfd == -1 )
    {
        printf("Couldn't open socket\n");
        exit(0);
    }
    printf("netmask was: %s\n", get_netmask(skfd,argv[1]) );
    set_netmask( skfd, argv[1], argv[2] );
    printf("netmask is now: %s\n", get_netmask(skfd,argv[1]) );
    close( skfd );
    return 0;
}

Friday, October 8, 2010

Programmatic interface alias creation in Linux

In *nix you can create an alias for an interface. So for my ethernet card residing at eth0, whose address is normally 192.168.100.4, I can also create a large number of aliases, for example, 192.168.100.45 for interface eth0:45. I have to give it a unique name not more than 16 bytes long, or it won't be able to distinguish it from my normal interface. Now why would I want to do that? Because, in my case I want to define hundreds or thousands of aliased addresses on my machine, so I can bind on to each of them and send ip-packets from those aliased addresses to a server. That way, I can simulate a DDoS attack of 200 or 5000 machines with just one machine. That's cool. But how do you do it?

You're supposed to use ifconfig, a commandline tool, but since I needed speed I wanted to be able to do it programmatically. How? On BSD you can call ioctl with SIOCAIFADDR to set the ip-address of an interface. But on Linux you have to use SIOCSIFADDR instead. Just give it a unique name and call ioctl with that argument and you're done. Since I didn't see any sample code for this when I Googled, I thought I'd save other people's time by putting this up. It copies the default netmask and broadcast address from the default interface, so you don't have to set these. You'll need to be supervisor to execute it.

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>

int skfd;
int set_ip_using(const char *name, int c, unsigned long ip)
{
    struct ifreq ifr;
    struct sockaddr_in sin;

    strncpy(ifr.ifr_name, name, IFNAMSIZ);
    memset(&sin, 0, sizeof(struct sockaddr));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = ip;
    memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
    if (ioctl(skfd, c, &ifr) < 0)
 {
  printf("failed to set ip-address\n");
  exit(0);
    }
 return 0;
}
int main ( int argc, char **argv )
{
 char *ip_address=NULL;
 char *interface;
 if ( argc==3 )
 {
  struct sockaddr_in sin;
  unsigned long ip;
  ip_address=argv[2];
  interface = argv[1];
  skfd = socket( AF_INET, SOCK_DGRAM, 0);
  if ( skfd == -1 )
  {
   printf("Couldn't open socket\n");
   exit(0);
  }
  inet_pton( AF_INET, ip_address, &sin );
  memcpy(&ip, &sin.sin_addr.s_addr, sizeof(unsigned long));  
  set_ip_using( interface,SIOCSIFADDR,ip );
  close( skfd );
 }
 else
  printf("usage: setip <interface-name> <ip-address>\n");
}

Tuesday, September 28, 2010

Two-way dynamic linking in C

Building a dynamic library (or shared object lib) on Linux is easy. All you do is compile your code with the -fPIC or -fpic flags then use the -shared flag for the link-step:

gcc -fPIC foo.c -shared -o foo.so

Then you can load the dynamic library in your main program by using dl_open and dl_sym to get any global symbol from the library.

But what if you also want your shared object library to link with symbols in the main program? This might be necessary, for example, if the dynamic module wanted to obtain a key from the main program. It would have to call a routine in main just as main is calling routines in the library. In this case you must instruct the main program also to export its symbols during linking:

gcc main.c -ldl -rdynamic -o main

Then, main, when it loads the shared object lib foo, will enable both foo to call routines in main, and main to call routines in foo.

Here's main.c. You can use the last command above to compile it

#include <dlfcn.h>
#include <stdio.h>
int bar()
{
 printf("dynamic lib called bar\n");
 return 0;
}
int main( int argc, char **argv )
{
 void *handle;
 handle = dlopen( "./foo.so", RTLD_GLOBAL|RTLD_LAZY );
 if ( handle != NULL )
 {
  int (*r)(void);
  int res;
  *(void **)(&r) = dlsym(handle, "foo");
  res = (*r)();
  printf("foo returned %d\n",res );
 }
 else
  printf("failed to open foo.so\n");
}

And foo.c. This compiles with the first command above:

#include <stdio.h>
extern int bar();
int foo()
{
 printf("called foo inside lib\n");
 bar();
 return 0;
}

Then run it by calling main:

./main
called foo inside lib
dynamic lib called bar
foo returned 0