Oracle TNS Enumeration

The practical way to understand how to a specific protocol works is to use it’s client tools and monitor its packets.

If you take a look to pure connection of SQL*plus client to a TNS listener from Wireshark, you’ll find the first connect packet as bellow

Wireshark
Figure 1. TNS Packet
  • TNS Packet Description
    1. Transparent Network Substrate Protocol
    2. Packet Length: 224
    3. Packet Checksum: 0x0000
    4. Packet Type: Connect (1) 0x01
    5. Reserved Byte: 00
    6. Header Checksum: 0x0000
    7. Connect
    8. Version: 315
    9. Version (Compatible): 300
    10. Service Options: 0x0c41
    11. Session Data Unit Size: 8192
    12. Maximum Transmission Data Unit Size: 65535
    13. NT Protocol Characteristics: 0x7f08
    14. Line Turnaround Value: 0
    15. Value of 1 in Hardware: 0100
    16. Length of Connect Data: 154
    17. Offset to Connect Data: 70
    18. Maximum Receivable Connect Data: 2048
    19. Connect Flags 0: 0x41
    20. Connect Flags 1: 0x41
    21. Trace Cross Facility Item 1: 0x00000000
    22. Trace Cross Facility Item 2: 0x00000000
    23. Trace Unique Connection ID: 0x0000000000000000
    24. Connect Data: (DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=XE)(CID=(PROGRAM=sqlplus@Archer)(HOST=Archer)(USER=KING)))(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.13)(PORT=1521)))
  • TNS Packet Hexdump
    1. 0000 08 00 27 3a fb 1d 3c 77 e6 68 66 e9 08 00 45 00 ..':..<w.hf...E.
    2. 0010 01 14 65 4f 40 00 40 06 53 28 c0 a8 00 0f c0 a8 ..eO@.@.S(......
    3. 0020 00 0d 81 32 05 f1 04 d7 76 08 c9 98 31 e3 80 18 ...2....v...1...
    4. 0030 00 e5 0f 40 00 00 01 01 08 0a 0d 8a 13 4a 05 44 ...@.........J.D
    5. 0040 03 b3 00 e0 00 00 01 00 00 00 01 3b 01 2c 0c 41 ...........;.,.A
    6. 0050 20 00 ff ff 7f 08 00 00 01 00 00 9a 00 46 00 00 ............F..
    7. 0060 08 00 41 41 00 00 00 00 00 00 00 00 00 00 00 00 ..AA............
    8. 0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 .............. .
    9. 0080 00 20 00 00 00 00 00 00 28 44 45 53 43 52 49 50 . ......(DESCRIP
    10. 0090 54 49 4f 4e 3d 28 43 4f 4e 4e 45 43 54 5f 44 41 TION=(CONNECT_DA
    11. 00a0 54 41 3d 28 53 45 52 56 49 43 45 5f 4e 41 4d 45 TA=(SERVICE_NAME
    12. 00b0 3d 58 45 29 28 43 49 44 3d 28 50 52 4f 47 52 41 =XE)(CID=(PROGRA
    13. 00c0 4d 3d 73 71 6c 70 6c 75 73 40 41 72 63 68 65 72 M=sqlplus@Archer
    14. 00d0 29 28 48 4f 53 54 3d 41 72 63 68 65 72 29 28 55 )(HOST=Archer)(U
    15. 00e0 53 45 52 3d 4b 49 4e 47 29 29 29 28 41 44 44 52 SER=KING)))(ADDR
    16. 00f0 45 53 53 3d 28 50 52 4f 54 4f 43 4f 4c 3d 54 43 ESS=(PROTOCOL=TC
    17. 0100 50 29 28 48 4f 53 54 3d 31 39 32 2e 31 36 38 2e P)(HOST=192.168.
    18. 0110 30 2e 31 33 29 28 50 4f 52 54 3d 31 35 32 31 29 0.13)(PORT=1521)
    19. 0120 29 29 ))

Now base on our understanding, let’s to build an equivalent request using ruby.

  • TNS packet builder
  1. def tns_packet(connect_data)
  2. #=> Transparent Network Substrate Protocol
  3. # Packet Length
  4. pkt = [58 + connect_data.length].pack('n')
  5. # Packet Checksum
  6. pkt << "\x00\x00"
  7. # Packet Type: Connect(1)
  8. pkt << "\x01"
  9. # Reserved Byte
  10. pkt << "\x00"
  11. # Header Checksum
  12. pkt << "\x00\x00"
  13. #=> Connect
  14. # Version
  15. pkt << "\x01\x36"
  16. # Version (Compatible)
  17. pkt << "\x01\x2C"
  18. # Service Options
  19. pkt << "\x00\x00"
  20. # Session Data Unit Size
  21. pkt << "\x08\x00"
  22. # Maximum Transmission Data Unit Size
  23. pkt << "\xFF\xFF"
  24. # NT Protocol Characteristics
  25. pkt << "\x7F\x08"
  26. # Line Turnaround Value
  27. pkt << "\x00\x00"
  28. # Value of 1 in Hardware
  29. pkt << "\x00\x01"
  30. # Length of Connect Data
  31. pkt << [connect_data.length].pack('n')
  32. # Offset to Connect Data
  33. pkt << "\x00\x3A"
  34. # Maximum Receivable Connect Data
  35. pkt << "\x00\x00\x00\x00"
  36. # Connect Flags 0
  37. pkt << "\x00"
  38. # Connect Flags 1
  39. pkt << "\x00"
  40. # Trace Cross Facility Item 1
  41. pkt << "\x00\x00\x00\x00"
  42. # Trace Cross Facility Item 2
  43. pkt << "\x00\x00\x00\x00"
  44. # Trace Unique Connection ID
  45. pkt << "\x00\x00\x34\xE6\x00\x00\x00\x01"
  46. # Connect Data
  47. pkt << "\x00\x00\x00\x00\x00\x00\x00\x00"
  48. pkt << connect_data
  49. return pkt
  50. end
  • SID Request

There is a data structure for interacting with the TNS which is similar to the following (DESCRIPTION=(CONNECT_DATA=(SID=#{sid})(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))(ADDRESS=(PROTOCOL=tcp)(HOST=#{host})(PORT=#{port})))

  1. def sid_request(sid,host, port)
  2. connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=#{sid})(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))(ADDRESS=(PROTOCOL=tcp)(HOST=#{host})(PORT=#{port})))"
  3. pkt = tns_packet(connect_data)
  4. end

Now we have everything to send our packet, let’s to build a simple tns brute force to enumerate the exist tns listeners. The default behavior for oracle 11g is to reply with nothing if listener exist, and reply with error if it doesn’t, the error similar to this g"[(DESCRIPTION=(TMP=)(VSNNUM=186647040)(ERR=12505)(ERROR_STACK=(ERROR=(CODE=12505)(EMFI=4)))).

Let’s to warp everything together by build a SID brute force script

SID Brute Force

tns_brute.rb

  1. #!/usr/bin/env ruby
  2. # -*- coding: binary -*-
  3. require 'socket'
  4. if ARGV.size < 1
  5. puts "Usage:\n#{__FILE__} <IP ADDRESS> [PORT]"
  6. exit 0
  7. else
  8. host = ARGV[0]
  9. port = ARGV[1] || 1521
  10. end
  11. sid = ARGV[2] || 'PLSExtProc'
  12. #
  13. # Build TNS Packet
  14. #
  15. def tns_packet(connect_data)
  16. #=> Transparent Network Substrate Protocol
  17. # Packet Length
  18. pkt = [58 + connect_data.length].pack('n')
  19. # Packet Checksum
  20. pkt << "\x00\x00"
  21. # Packet Type: Connect(1)
  22. pkt << "\x01"
  23. # Reserved Byte
  24. pkt << "\x00"
  25. # Header Checksum
  26. pkt << "\x00\x00"
  27. #=> Connect
  28. # Version
  29. pkt << "\x01\x36"
  30. # Version (Compatible)
  31. pkt << "\x01\x2C"
  32. # Service Options
  33. pkt << "\x00\x00"
  34. # Session Data Unit Size
  35. pkt << "\x08\x00"
  36. # Maximum Transmission Data Unit Size
  37. pkt << "\xFF\xFF"
  38. # NT Protocol Characteristics
  39. pkt << "\x7F\x08"
  40. # Line Turnaround Value
  41. pkt << "\x00\x00"
  42. # Value of 1 in Hardware
  43. pkt << "\x00\x01"
  44. # Length of Connect Data
  45. pkt << [connect_data.length].pack('n')
  46. # Offset to Connect Data
  47. pkt << "\x00\x3A"
  48. # Maximum Receivable Connect Data
  49. pkt << "\x00\x00\x00\x00"
  50. # Connect Flags 0
  51. pkt << "\x00"
  52. # Connect Flags 1
  53. pkt << "\x00"
  54. # Trace Cross Facility Item 1
  55. pkt << "\x00\x00\x00\x00"
  56. # Trace Cross Facility Item 2
  57. pkt << "\x00\x00\x00\x00"
  58. # Trace Unique Connection ID
  59. pkt << "\x00\x00\x34\xE6\x00\x00\x00\x01"
  60. # Connect Data
  61. pkt << "\x00\x00\x00\x00\x00\x00\x00\x00"
  62. pkt << connect_data
  63. return pkt
  64. end
  65. #
  66. # SID Request Data
  67. #
  68. def sid_request(sid,host, port)
  69. connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=#{sid})(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))(ADDRESS=(PROTOCOL=tcp)(HOST=#{host})(PORT=#{port})))"
  70. pkt = tns_packet(connect_data)
  71. end
  72. sids = [ 'N00TEXIST', 'PLSExtProc', 'ORACLE', 'ORA', 'ORA1', 'ORA2', 'XE', 'SOA', 'SOA1', 'SOA2', 'DBA1', 'DBA2' 'HR', 'HR1', 'HR2','SAP', 'TEST']
  73. sids.each do |sid|
  74. s = TCPSocket.new host, port.to_i
  75. s.send sid_request(sid, host, port), 0
  76. response = s.recv(1000)
  77. puts "[+] Found SID: " + sid if response.scan(/ERROR/).empty?
  78. # puts "[+] No SID: " + sid , response unless response.scan(/ERROR/).empty?
  79. s.close
  80. end

Run it

  1. ruby tns_brute.rb 192.168.0.13 1521
  2. [+] Found SID: PLSExtProc
  3. [+] Found SID: XE

Notes:

  • This script will work on Oracle 11g and before
  • Notice # -*- coding: binary -*- at the top of the script because we are working on pure binary data that may not mean anything to the language.