The clues from "objdump -R" showed this executable to be
using expected calls for a daemonizing network server. Some interesting stuff
slightly outside the scope of a plain network daemon were the calls to
"getenv", and "rand".
Using the disassembler of our choice, we followed the flow of execution
through watching it
setting up signal handlers, daemonizing, reading stuff out of the environment
variable "KKEY", seeding the random number generator, binding to port 7756,
listening, and finally sitting in an "accept" call waiting for incoming
connections.
We identified the "client handler" function that got called after an "accept" returned,
noting it was the only place "read" and "write" are called from. Tracing
the output, and what it did with input, we constructed the following
chain of expected events for a connection lifetime:
- server sends 4 bytes of random seed
- client sends 2 bytes of the "length" of more bytes to follow
- client sends "length"-many bytes of payload
- server validates 4 bytes of payload with a function we named "random_permute" (which also took the random seed as an argument)
- server validates the next 0x17 bytes with a function we named "permuteB"
- server validates the next 5 bytes with a function we named "permuteC"
- server sends 2 byte "length"
- server sends "length"-many bytes, containing the value of the KKEY environment variable
Studying "permuteC", we found it just wanted to see the string "GIMME".
Studying "permuteB", we found it was testing that every 3 bytes in the string
of 0x17 bytes each had odd values. (Though, it seemed that leaving higher
bytes at 0 was a good idea.) So, 8 triplets of value "1".
Studying "random_permute" gave us a headache, so we just ripped it out of the
disassembly, made an assembler "random_permute.s" file out of it, and tacked
it on to our C network client:
/* Run as "./client300 kenshoto.allyourboxarebelongto.us 7756" */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
int sockfd;
extern random_permute( uint32_t current, uint32_t cycle );
unsigned char * gank(int count) {
static unsigned char buffer[1025];
int i, n;
bzero(buffer,1025);
n = read(sockfd,buffer,count);
if (n < count) {
if (errno == EINTR) return NULL;
}
printf("read %d/%d: ",n,count);
for (i=0;i<n;i++) {
printf("%02x ",buffer[i]);
}
printf("\n");
return buffer;
}
int shove(int len, unsigned char * data)
{
int i;
printf("write: %d: ",len);
for (i=0;i<len;i++) {
printf("%02x ",data[i]);
}
printf("\n");
return write(sockfd,data,len);
}
int main(int argc, char *argv[])
{
int portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
unsigned char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
exit(1);
}
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0) {
perror("connect");
exit(1);
}
unsigned char response[] =
"\xe0\x7e\x68\x5b" /* rand_permute */
"\x00\x00\x01" /* 8 triples of odds */
"\x00\x00\x01"
"\x00\x00\x01"
"\x00\x00\x01"
"\x00\x00\x01"
"\x00\x00\x01"
"\x00\x00\x01"
"\x00\x00\x01"
"GIMME"; /* 5 magic bytes */
unsigned char * buf = gank(4);
// start commenting here
printf("\\x%02x\\x%02x\\x%02x\\x%02x\n",buf[0],buf[1],buf[2],buf[3]);
uint32_t crc = random_permute( *((uint32_t*)buf), 0 );
printf("CRC: \\x%08x\n",crc);
*((uint32_t*)response)=crc;
printf("\\x%02x\\x%02x\\x%02x\\x%02x\n",
response[0],response[1],response[2],response[3]);
// end commenting here
uint16_t togo = 4+(8*3)+5;
shove(2,&togo);
shove(togo,response);
buf = gank(1024);
printf("%s\n",buf);
return 0;
}
After playing with the client, we actually discovered that the
"random_permute" function wasn't actually a CRC-like permute at all. It always
produced the same output, regardless of input: "\xe0\x7e\x68\x5b". (As a
result, you can comment out the call to random_permute above, as noted, and
the client will still run correctly.)