Team Arrogance Whitepaper

roothack.org

by Team Arrogance

Our box was a slackware 7.1 system. In general, slackware starts
out pretty secure, but there are a lot of services running by
default (mostly through inetd) that we didn't need or want.

We started out by going in and killing all the default services.
The installed SSH (ssh communications non-commercial 3.1.0) was
deemed to be secure, so we did not switch to OpenSSH. All non
essential setuids were removed (the only ones that were kept were
passwd and su). Our initial 3 services were ssh, chargen and smtp.
Chargen was run through inetd under our socketcall patch (explained
below) and we used a maximum-chrooted postfix (again under socketcall)
for smtp. When the number of services was increased to 8, we added
proftpd, rsync, finger, httpd and wumpusd.

httpd and wumpusd were home-written daemons. httpd, which was not RFC
compliant (did not wait for a request) just spat out the redhat 7.1
default install page. wumpusd was hunt the wumpus bound to port 23.
fingerd was a perl finger daemon called 'plfingerd-0.3' found online.

All services other than ssh were run under a LKM we wrote called the
socketcall patch. This patch (code at the end) allowed a non-priveledged
user to bind to any port but did not allow any set*id operations at all.
This patch made it so if there were any 0days for rsync, postfix, or other
publically available daemons that they could not get root. The only point
of possible failure was sshd.

One thing that was supposed to happen (but did not because of arp-spoof
attacks) was the opening of a shell. For this, we created a derivative
of the socketcall patch that just prevented all set*id operations without
granting access to the socket system call. Opening shells never happened,
but we were ready for it.

Our best attacker had modem troubles through the entire games, so we
didn't do too much in terms of attack. We were sniffing constantly but
due to the NAT setup (no gateway accounts), everybody was using ssh2.
We also tried (for about an hour) and arp-spoof attack on one of the
machines, but after witnessing a couple connections without any passwords
being logged, we decided that nobody would fall for such an obvious
attack. Aside from that, everybody was good enough to upgrade to the
latest versions of software and/or patch vulnerabilities, so we had
no opportunites to script kiddie them.

As far as the attacks against our own box, most of the time people were
using the same arp spoof attack that we tried. Unfortunatly, this attack
was continued for several days, preventing access to our machine. Also,
a user on IRC attempted to learn our strategy. We're still unsure whether
"she" was part of a team or serious.

We considered adding environment variable caveats to passwd & su, as
has been done by other teams in the past, but decided that our kernel
patches were sufficient.

--------------------- socketcall.c ---------------------
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/config.h>
#include <linux/dirent.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <sys/reboot.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <unistd.h>

#define SOCKET_UID 10000

extern void *sys_call_table[];


/******************************************/
int (*rsocketcall)(int, unsigned long *);
/******************************************/

/******************************************/
int (*rsetuid)(uid_t);
int (*rsetreuid)(uid_t, uid_t);
int (*rsetresuid)(uid_t, uid_t, uid_t);
int (*rsetfsuid)(uid_t);
int (*rsetgid)(gid_t);
int (*rsetregid)(gid_t, gid_t);
int (*rsetresgid)(gid_t, gid_t, gid_t);
int (*rsetfsgid)(gid_t);
/******************************************/

int hsocketcall(int call, unsigned long *args){
unsigned int euid;
int ret;

if(current->uid==SOCKET_UID){
euid=current->euid;
current->euid=0;
}
ret=(*rsocketcall)(call, args);
if(current->uid==SOCKET_UID) current->euid=euid;
return ret;
}


int hsetuid(uid_t uid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetuid)(uid);
}

int hsetreuid(uid_t uid, uid_t euid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetreuid)(uid, euid);
}

int hsetresuid(uid_t uid, uid_t euid, uid_t suid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetresuid)(uid, euid, suid);
}

int hsetfsuid(uid_t fsuid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetfsuid)(fsuid);
}


int hsetgid(gid_t gid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetgid)(gid);
}

int hsetregid(gid_t gid, gid_t egid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetregid)(gid, egid);
}

int hsetresgid(gid_t gid, gid_t egid, gid_t sgid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetresgid)(gid, egid, sgid);
}

int hsetfsgid(gid_t fsgid){
if(current->uid==SOCKET_UID) return -1;
return (*rsetfsgid)(fsgid);
}


int init_module(void){
rsocketcall=sys_call_table[SYS_socketcall];
sys_call_table[SYS_socketcall]=(void *)hsocketcall;

rsetuid=sys_call_table[SYS_setuid];
sys_call_table[SYS_setuid]=(void *)hsetuid;
rsetreuid=sys_call_table[SYS_setreuid];
sys_call_table[SYS_setreuid]=(void *)hsetreuid;
rsetresuid=sys_call_table[SYS_setresuid];
sys_call_table[SYS_setresuid]=(void *)hsetresuid;
rsetfsuid=sys_call_table[SYS_setfsuid];
sys_call_table[SYS_setfsuid]=(void *)hsetfsuid;
rsetgid=sys_call_table[SYS_setgid];
sys_call_table[SYS_setgid]=(void *)hsetgid;
rsetregid=sys_call_table[SYS_setregid];
sys_call_table[SYS_setregid]=(void *)hsetregid;
rsetresgid=sys_call_table[SYS_setresgid];
sys_call_table[SYS_setresgid]=(void *)hsetresgid;
rsetfsgid=sys_call_table[SYS_setfsgid];
sys_call_table[SYS_setfsgid]=(void *)hsetfsgid;

return 0;
}


void cleanup_module(void){
sys_call_table[SYS_socketcall]=rsocketcall;

sys_call_table[SYS_setuid]=rsetuid;
sys_call_table[SYS_setreuid]=rsetreuid;
sys_call_table[SYS_setresuid]=rsetresuid;
sys_call_table[SYS_setfsuid]=rsetfsuid;
sys_call_table[SYS_setgid]=rsetgid;
sys_call_table[SYS_setregid]=rsetregid;
sys_call_table[SYS_setresgid]=rsetresgid;
sys_call_table[SYS_setfsgid]=rsetfsgid;
}
----------------------- nosuid.c -----------------------
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/config.h>
#include <linux/dirent.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <sys/reboot.h>
#include <sys/syscall.h>
#include <sys/utsname.h>

#define NOSUID_UID 10001

extern void *sys_call_table[];

/**********************************************/
int (*rsetuid)(uid_t);
int (*rsetreuid)(uid_t, uid_t);
int (*rsetresuid)(uid_t, uid_t, uid_t);
int (*rsetfsuid)(uid_t);
int (*rsetgid)(gid_t);
int (*rsetregid)(gid_t, gid_t);
int (*rsetresgid)(gid_t, gid_t, gid_t);
int (*rsetfsgid)(gid_t);
/**********************************************/

int hsetuid(uid_t uid) {
if(current->uid == NOSUID_UID) return -1;
return rsetuid(uid);
}

int hsetreuid(uid_t uid, uid_t euid) {
if(current->uid == NOSUID_UID) return -1;
return rsetreuid(uid, euid);
}

int hsetresuid(uid_t uid, uid_t euid, uid_t suid) {
if(current->uid == NOSUID_UID) return -1;
return rsetresuid(uid, euid, suid);
}

int hsetfsuid(uid_t fsuid) {
if(current->uid == NOSUID_UID) return -1;
return rsetfsuid(fsuid);
}

int hsetgid(gid_t gid) {
if(current->uid == NOSUID_UID) return -1;
return rsetgid(gid);
}

int hsetregid(gid_t gid, gid_t egid) {
if(current->uid == NOSUID_UID) return -1;
return rsetregid(gid, egid);
}

int hsetresgid(gid_t gid, gid_t egid, gid_t sgid) {
if(current->uid == NOSUID_UID) return -1;
return rsetresgid(gid, egid, sgid);
}

int hsetfsgid(gid_t fsgid) {
if(current->uid == NOSUID_UID) return -1;
return rsetfsgid(fsgid);
}

int init_module(void) {
rsetuid=sys_call_table[SYS_setuid];
sys_call_table[SYS_setuid]=(void *)hsetuid;
rsetreuid=sys_call_table[SYS_setreuid];
sys_call_table[SYS_setreuid]=(void *)hsetreuid;
rsetresuid=sys_call_table[SYS_setresuid];
sys_call_table[SYS_setresuid]=(void *)hsetresuid;
rsetfsuid=sys_call_table[SYS_setfsuid];
sys_call_table[SYS_setfsuid]=(void *)hsetfsuid;
rsetgid=sys_call_table[SYS_setgid];
sys_call_table[SYS_setgid]=(void *)hsetgid;
rsetregid=sys_call_table[SYS_setregid];
sys_call_table[SYS_setregid]=(void *)hsetregid;
rsetresgid=sys_call_table[SYS_setresgid];
sys_call_table[SYS_setresgid]=(void *)hsetresgid;
rsetfsgid=sys_call_table[SYS_setfsgid];
sys_call_table[SYS_setfsgid]=(void *)hsetfsgid;

return 0;
}

void cleanup_module(void) {
sys_call_table[SYS_setuid]=rsetuid;
sys_call_table[SYS_setreuid]=rsetreuid;
sys_call_table[SYS_setresuid]=rsetresuid;
sys_call_table[SYS_setfsuid]=rsetfsuid;
sys_call_table[SYS_setgid]=rsetgid;
sys_call_table[SYS_setregid]=rsetregid;
sys_call_table[SYS_setresgid]=rsetresgid;
sys_call_table[SYS_setfsgid]=rsetfsgid;
}
----------------------- wumpus.c -----------------------
#include <netinet/in.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>

extern char **environ;

int main(int argc, char **argv) {
int cont, create_socket, new_socket;
struct sockaddr_in address;
socklen_t addrlen;
const int port = 23;
pid_t childpid, child2;
int status;
int num_children = 0;

signal(SIGCHLD, SIG_IGN);

create_socket = socket(AF_INET, SOCK_STREAM, 0);
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
bind(create_socket, (struct sockaddr *) &address, sizeof(address));
listen(create_socket, 3);
addrlen = sizeof(struct sockaddr_in);
while(1) {
new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen);
if(num_children < 5) {
childpid = fork();
num_children++;
if(childpid == 0) {
child2 = fork();
if(child2 == 0) {
dup2(new_socket, 0);
dup2(new_socket, 1);
dup2(new_socket, 2);
execl("/home/jupiter/wump", "wump", NULL);
} else {
waitpid(child2, &status, 0);
num_children--;
shutdown(new_socket, 2);
}
}
} else {
send(new_socket, "Too many connections. Try again later\n", strlen("Too many connections. Try again later\n"), 0);
shutdown(new_socket, 2);
}
}
close(create_socket);
}
----------------------- httpd.c ------------------------
nclude <sys/types.h>
#include <sys/socket.h>
#include <iostream.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "webpage.h" /* char text[] contains web page content */

int main() {
unsigned int create_socket, new_socket, addrlen;
struct sockaddr_in address;
const int port = 80;

create_socket = socket(AF_INET, SOCK_STREAM, 0);
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if(bind(create_socket, (struct sockaddr*) &address, sizeof(address)) == -1) {
perror("bind");
return 0;
}
listen(create_socket, 3);
addrlen = sizeof(struct sockaddr_in);
while(1) {
new_socket = accept(create_socket, (struct sockaddr *) &address, &addrlen);
send(new_socket, text, strlen(text), 0);
shutdown(new_socket, 2);
}
}
--------------------------------------------------------