We will provide live commit logs here soon. Until then feel free to view the mwcollect.org repository with Trac.
Playing with the honeytrap module, I saw some strange behaviour from hosts attacking nepenthes. They connected, exploitet to gain shell, connected the shell, and run a ftp command. Nepenthes connected the viris ftp server, and asked for the file, providing the port where to send the file via the PORT command (active ftp). In some cases, the virus would send the file to a very different port, the honeytrap module kicked it, and we got the file send to a shell emulation.
So, we checked if there was a bug when sending the PORT command in nepenthes, and found none, having a look on some sdbot forks ftpd code, we got this:
char buf[100];
char a[4];
char b[4];
char c[4];
char d[4];
char p1[50];
char p2[50];
char tmpip[15];
int po,po2;
if ( strcmp(tmpbuf,"PORT") == 0 )
{
sscanf(buf,"%*s %[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]",a,b,c,d,p1,p2);
po = atoi(p1);
po2 = atoi(p2);
memset(p1,0,sizeof(p1));
sprintf(p1,"%x%x\n",po,po2);
h = strtoul(p1, NULL, 16);
sprintf(tmpip,"%s.%s.%s.%s",a,b,c,d);
send(i,"200 PORT command successful.\n",29 , 0);
}
The PORT command is parsed using sscanf, and splitup in a b c d p1 and p2. Each comma seperated value in this listing is a bytes value, its range is from 0-255. a.b.c.d represents the remotes ip, and p1 and p2 are parts of the port.
Obviously ‘recalculating’ the port failed in some cases, but why? Using the code to create a PORT command from nepenthes and the code from the virus to parse the command, we ended up that 61440 of 65536 ports would work with the virus code, which meant in 4080/65536 of all cases, or ~6.2% the ftp download would fail as the virus would ‘calculate’ the wrong port.
Now, to understand why the virus fails, one has to understand how the portnumber is encoded into the PORT command. The port command has 2 bytes, and therefore needs 2 comma seperated values in the PORT command, when creating the PORT command, you get the integer value of the first byte of the port, print it, and then print the integer value of the second byte of the port.
int main(int argc, char **argv)
{
if ( argc < 2)
return 0;
int port = atoi(argv[1]);
printf("PORT 127,0,0,1,%i,%i\n",(port >> 8) & 0xff, port & 0xff );
}
as an example gives
./make_port 4711 PORT 127,0,0,1,18,103
so the port is encoded into 18 and 103, to regain the value of the port, one could calculate
ntohs((18 << 8) | 103)
but the viri authors decided to do better, and did
sprintf(p1,"%x%x\n",po,po2); h = strtoul(p1, NULL, 16);
where p1 would be “1267\n” using our values, and h would be 4711, the right port.
Now, to see it failing, lets see what happens when using port 1280.
./make_port 1280 PORT 127,0,0,1,5,0 our port 1280 their p1 string 50 their port 80
As you might be able to see, their p1 string is too short, it should be “0500” which is 1280 when using 16 as base. The port calculation would fail on every port where the hex value of the lower byte starts with “0”, using
sprintf(p1,"%02x%02x\n",po,po2);
instead, or using the previous posted shifting would prevent this.
To workaround this problem, we decided to use only ports for ftp known to be working with buggy virii.