CVE-2018-3759 (published 2018-06-13T15:29Z)

private_address_check ruby gem before 0.5.0 is vulnerable to a time-of-check time-of-use (TOCTOU) race condition due to the address the socket uses not being checked. DNS entries with a TTL of 0 can trigger this case where the initial resolution is a public address but the subsequent resolution is a private address.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 3.7, LOW (CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: CWE-362
cwe_name: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')
description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
extended_description: 
            This can have security implications when the expected synchronization is in security-critical code, such as recording whether a user is authenticated or modifying important state information that should not be influenced by an outsider.
            A race condition occurs within concurrent environments, and is effectively a property of a code sequence. Depending on the context, a code sequence may be in the form of a function call, a small number of instructions, a series of program invocations, etc.
            A race condition violates these properties, which are closely related:
               
                  Exclusivity - the code sequence is given exclusive access to the shared resource, i.e., no other code sequence can modify properties of the shared resource before the original sequence has completed execution.
                  Atomicity - the code sequence is behaviorally atomic, i.e., no other thread or process can concurrently execute the same sequence of instructions (or a subset) against the same resource.
               
            A race condition exists when an "interfering code sequence" can still access the shared resource, violating exclusivity. Programmers may assume that certain code sequences execute too quickly to be affected by an interfering code sequence; when they are not, this violates atomicity. For example, the single "x++" statement may appear atomic at the code layer, but it is actually non-atomic at the instruction layer, since it involves a read (the original value of x), followed by a computation (x+1), followed by a write (save the result to x).
            The interfering code sequence could be "trusted" or "untrusted." A trusted interfering code sequence occurs within the product; it cannot be modified by the attacker, and it can only be invoked indirectly. An untrusted interfering code sequence can be authored directly by the attacker, and typically it is external to the vulnerable product.
         
         

Fix information:
repo_url: https://github.com/jtdowney/private_address_check
commit_details: repo_url: https://github.com/jtdowney/private_address_check
author: John Downey
author_date: 2018-05-03 20:05:19-05:00
msg: Fix TOCTOU bug

This fixes the time of check to time of use (TOCTOU) bug reported by Sajjad
Hashemian.
repository: repo_url: https://github.com/jtdowney/private_address_check
repo_name: jtdowney/private_address_check
description: Ruby gem to help prevent Server Side Request Forgery
repo_language: Ruby
owner: jtdowney
file_changes: filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -61,8 +61,18 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
 
 Bug reports and pull requests are welcome on GitHub at https://github.com/jtdowney/private_address_check. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
 
+## Security
+
+If you've found a security issue in `private_address_check`, please reach out to @jtdowney via email to report.
+
+### Time of check to time of use
+
+A library like `private_address_check` is going to be easily susceptible to attacks like [time of check to time of use](https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use). DNS entries with a TTL of 0 can trigger this case where the initial resolution is a public address by the subsequent resolution is a private address. There are some possible defenses and workarounds:
+
+- Use the TCPSocket extension in this library which checks the address the socket uses. This is most useful if your system is built on native Ruby like Net::HTTP.
+- Use a feature like the `resolve` capability in curl and [curb](https://www.rubydoc.info/github/taf2/curb/Curl/Easy#resolve=-instance_method) to force the resolution to a pre-checked IP address.
+- Implement your own caching DNS resolver with something like dnsmasq or unbound. These tools let you set a minimum cache time that can override the TTL of 0.
 
 ## License
 
 The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
-

programming_language: Markdown
filename: tcpsocket_ext.rb
change_type: ModificationType.MODIFY
diff: @@ -14,11 +14,10 @@ end
 TCPSocket.class_eval do
   alias initialize_without_private_address_check initialize
 
-  def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
-    if Thread.current[:private_address_check] && PrivateAddressCheck.resolves_to_private_address?(remote_host)
+  def initialize(*args)
+    initialize_without_private_address_check(*args)
+    if Thread.current[:private_address_check] && PrivateAddressCheck.resolves_to_private_address?(remote_address.ip_address)
       raise PrivateAddressCheck::PrivateConnectionAttemptedError
     end
-
-    initialize_without_private_address_check(remote_host, remote_port, local_host, local_port)
   end
 end

programming_language: Ruby
filename: tcpsocket_ext_test.rb
change_type: ModificationType.MODIFY
diff: @@ -3,11 +3,15 @@ require 'private_address_check/tcpsocket_ext'
 
 class TCPSocketExtTest < Minitest::Test
   def test_private_address
+    server = TCPServer.new(63453)
+    thread = Thread.start { server.accept }
     assert_raises PrivateAddressCheck::PrivateConnectionAttemptedError do
       PrivateAddressCheck.only_public_connections do
-        TCPSocket.new("localhost", 80)
+        TCPSocket.new("localhost", 63453)
       end
     end
+  ensure
+    thread.exit if thread
   end
 
   def test_public_address

programming_language: Ruby