Opened 15 years ago

Last modified 15 years ago

#142 new defect

analyze consequences of recent TLS plaintext-injection vulnerability

Reported by: Brian Warner Owned by:
Priority: major Milestone: undecided
Component: unknown Version: 0.4.1
Keywords: security Cc:

Description

A week ago, a new problem in TLS surfaced, in which an attacker can inject arbitrary plaintext into the supposedly-authenticated connection just after a "renegotiation" takes place. There was also some suggestion that the attacker could force a renegotiation to occur whenever they liked. I don't yet know if this means that the attacker can inject data into just the beginning of the conversation, or into other places too.

Foolscap is somewhat less vulnerable to this than systems (like HTTP using client-side certificates) that depend upon authorizing clients. Foolscap generally doesn't care *who* you are, just whether or not you know the swissnum.

However, a plaintext-injecting attacker could still cause mischief, sometimes with consequences that violate the expected security properties. If the injection can occur after a getReferenceByName, then the attacker will be able to send arbitrary messages to objects which the real client has already accessed and retains a RemoteReference to (because the swissnum-to-CLID mapping is cached for each connection). They'd have to guess which object you're using, and what methods/arguments it responds to, and couldn't see the results.

The attacker could also inject a message which updates the "vocab table" (a mapping from small integer to string, used to compress frequently-used strings like "getReferenceByName"). This would probably only be usable to prevent methods from being called (a DoS attack), but the attacker might also be able to e.g. transform calls to remote_foo() into calls to remote_bar().

We need to:

  • learn more about the TLS vulnerability: arbitrary injection, or only at the beginning?
  • identify where renegotiation might take place
  • explain how foolscap-using applications might be vulnerable

(note that if every single message included the swissnum, and we didn't use CLIDs at all, then the injection-vulnerability window would be much smaller: they'd have to jam something exactly after the swissnum and before the normal message name/args).

Change History (2)

comment:1 Changed 15 years ago by Brian Warner

I've done some more research, and brainstormed with Zooko, and I think there's a plausible but hard-to-exploit vulnerability. First, the background:

  • the attack (described fairly well here) allows the attacker to participate in an arbitrary conversation with the server, then connect the client and step out of the loop. The server-side application cannot tell that the two conversations were with different parties. The client sees nothing of the attacker's conversation.
  • this allows the attacker to put the server in an arbitrary state before handing the connection off to the client
  • Foolscap connections (inside the TLS wrapper) start with an exchange of HELLO blocks, then one side sends a DECISION block, then both sides switch to banana, then both send a SET-VOCAB token. Later, when a client wants to invoke a method, it will send a get-clid-for-swissnum call message, then an invoke-method-on-clid call message.
  • the HELLO and DECISION blocks are rfc822-style, terminated by a double newline
  • the call messages look like OPEN STRING("call") reqid clid STRING(methodname) OPEN STRING("arguments") numposargs posarg[0] posarg[1].. kwargname0 kwarg[0] kwargname1 kwarg[1] CLOSE CLOSE
    • where CLOSE is a 0x89 byte
    • and STRING("foo") is a little-endian base-128 length number, a 0x82 byte, and then the actual bytes of the string

The best attack I can think of so far requires the following conditions:

  • an attacker who can sit in the middle of the network, playing MitM
  • a client "Alice" who performs a highly predictable message send, such as a script which always calls the same method on the same object, using constant-length arguments. The length of the message tokens is the important thing to predict, not the contents. (the attacker may be able to glean these lengths by observing normal client requests, just by watching ciphertext go by, and/or by talking to the client directly)
    • for example, presume that Alice invokes getReference(secretfurl) and rref.callRemote("withdraw", "100 dollars")
  • the server "Bob" must offer a pastebin-like service (PUT and GET of arbitrary data to some stored location), and the attacker must have access to this service. The GET message must take some non-trivial amount of time to process. The last argument to the PUT message must be a string. The attacker will use one pastebin slot to fake the server's responses, and a second to gather the client's secrets, which he can retrieve later.

If the attack succeeds, the attacker gets to learn the swissnum and/or argument values of the client's message send.

The attack proceeds as follows:

  • the attacker accepts the client's connection, and performs the plaintext HTTP-like which-Tub-do-you-want part of the protocol. It responds with "101 Switching Protocols" and "Upgrade: TLS" as usual, records the client's response (the TLS "Client Hello" message), then stalls the client for a while.
  • then the attacker connects to the server, using a tubid on the same side of the server's tubid as the client has (so the same side makes the DECISION block). It negotiates normally (as itself, no impersonation needed).
  • the attacker obtains access to two pastebin slots
  • the attacker fills the first slot with a specially-crafted message, which looks like COLON + NEWLINE + HELLO-BLOCK + NEWLINE + NEWLINE + (maybe)DECISION + CLID-RESPONSE + FAKE-RESPONSE + ADD-VOCAB-PREFIX, where CLID-RESPONSE, FAKE-RESPONSE, and ADD-VOCAB-PREFIX are banana sequences described below
  • the attacker sends a GET message for that first slot, knowing that the response won't be sent for a few seconds
  • attacker sends the first half of a PUT message for the second pastebin slot, everything up to the length and "STRING" token for the last argument. The length must be carefully pre-calculated. The attacker does not send the actual string data, nor the terminating double CLOSE token.
  • now, the attacker replays the client's TLS negotiation message, inside its encrypted session, triggering the renegotiation. The attacker forwards all subsequent packets, but at this point they are locked out of the encrypted session.

From the point of view of the client "Alice":

  • she connects, performs the plaintext HTTP-like negotiation, sends her TLS Client Hello, sees a pause, then sees the server's TLS Server Hello, and negotiates TLS normally
  • she sends her foolscap HELLO block, and starts reading data until she sees a double-newline, then parses it as an rfc822-style header, interpreting it as the server's own HELLO block. What she actually gets is the ANSWER sequence for the attacker's pastebin GET request which completed just after her TLS negotiation, which looks like {{{OPEN STRING("answer") reqid STRING(contents) CLOSE}}}. The first part (up to the first colon+newline in the contents) will be interpreted as a weirdly-named header line, with an empty value, as long as 'reqid' doesn't happen to be 0x0a. The first part of the contents will be treated as the server's HELLO block, which can be a literal copy of the HELLO block received by the attacker from the same server. The "my-tub-id" value will match the TLS session and be accepted by the client.
  • The tail of the pastebin contents will be a banana token sequence which matches the CLOSE token of the GET response. This could be a simple ADD-VOCAB token sequence, which is effectively ignored. This "banana injection" technique prevents Alice from dropping the connection because of a mismatched CLOSE token, and can hide the fact that anything sneaky is taking place.
  • Now Alice believes she has received a HELLO block, and one side or the other makes the DECISION. If Alice has the higher tubid (and thus sends the DECISION block), the attacker must accomodate this in the length he pre-calculates. If the server has the higher tubid, the attacker must include a copy of the server's DECISION block in the pastebin contents.
  • Now Alice prepares to invoke her method, by exchanging her swissnum for a CLID (connection-local ID). She sends OPEN STRING("call") reqid 0 STRING("getReferenceByName") OPEN STRING("arguments") 1 STRING(swissnum) CLOSE CLOSE. All of this goes into the attacker's second pastebin.
  • The next thing Alice sees is the CLID-RESPONSE portion of the attacker's first pastebin, which should be a normal ANSWER sequence telling Alice that the server has assigned a CLID for her swissnum. The CLID can be arbitrary: the real server will never see it appear (as a CLID).
  • Next, Alice will send her (call) sequence to invoke her "withdraw" method. This too goes into the attacker's second pastebin. Her (call) sequence ends with CLOSE CLOSE. The attacker must calculate the length of his capture to include every byte up to but not including Alice's final CLOSE CLOSE. The final double-CLOSE token sequence is interpreted by the server as finishing the pastebin PUT operation.
  • Alice now waits for a response to her "withdraw" message, which is supplied by the attacker's FAKE-RESPONSE sequence. Assuming the attacker can guess the request-id (which is a simple counter and will probably be "2"), Alice will treat the response as if it came from the real method invocation.
  • Alice now sees the ADD-VOCAB-PREFIX, which is a banana sequence that initiates an add-vocab-token message, but leaves off the final CLOSE token. This absorbs the real CLOSE token returned by the original pastebin GET invocation.
  • Finally, Alice sees the response sequence for the pastebin PUT operation that she was unintentionally partcipating in. This will be ignored (or quietly logged), as it will be for a requestid that she is not waiting upon.
  • Alice might invoke other methods later.

From the point of view of the server:

  • somebody with the attacker's tubid connects and negotiates a session
  • they fill one pastebin
  • they fill a second pastebin
  • they might invoke other methods later

The end state is that Alice thinks she has invoked her "withdraw" method successfully, but she has not. The attacker can read his second pastebin later and learn the swissnum that alice used for the withdrawal and the arguments she passed.

The attack depends upon being able to exactly predict the length of the messages that Alice will send to the server, so that the CLOSE CLOSE tokens line up correctly. If they don't, the server will not accept the pastebin PUT method and the secrets will be discarded. But nothing that Alice sends is intentionally of random length, and randomly-generated swissnums are always the same size. The attack is easier if we only care about the swissnum and not the method arguments (if we care about argument values, we need them to be of constant length). It is also easier if we don't need to hide the evidence from Alice (making sure she doesn't witness any errors).

This attack relies upon a pastebin-like GET service to amplify the attacker's chosen-prefix (towards-the-server) ability into the ability to send arbitrary data towards the client as well, plus a PUT service to let them see what the victim is sending. An easier GET-only variant would be to use this ability to trick Alice into using the wrong CLID, perhaps in conjunction with a "deposit" method. The contents of the GET pastebin could include a fake response to the "getReferenceByName" method that claimed CLID=5, where in fact CLID 5 had previously been allocated by the attacker to point at his own account instead of Alice's intended beneficiary. This form of attack would require less predictability of her method's arguments, since it wouldn't need a CLOSE CLOSE token sequence to line up anywhere. However, it would still need a dummy method of some sort to absorb her HELLO (and maybe DECISION) block, since these are otherwise likely to cause a banana-level error and drop the connection. An ADD-VOCAB sequence would serve as a good absorb-and-discard tool.

The hypothetical pastebin service must take a non-zero time to respond, because the attacker must have enough time to splice in the client's real connection before the server transmits the pastebin's contents. The new client-to-server connection must be negotiated before the client observes the HELLO block, because that is the point at which the client will ask the TLS transport for the peer certificate in use, and that cert must match the expected tubid for negotiation to proceed. Foolscap's use of eventual-send in method dispatch probably makes this easier to accomplish.

The attack can only be carried out against users of the same Tub as the pastebin service.

A Tahoe storage server could probably be used as a pastebin service. However, Tahoe's end-to-end nature means clients don't send many high-value secrets to a storage server (really just the write-enabler, and lease renew/expire secrets), making this a fairly uninteresting place to attack. I don't believe that foolscap "Gifts" (third-party references) could be used to extend this attack to point at an alternate server: nothing the client sees will cause it to send any secrets that it didn't intend to reveal to the target Tub.

There might be a way to use Foolscap error messages as a pastebin GET service, for example the "method 'foo' not defined in RIBlah" error message, using a carefully constructed long method name. The token sequence generated by this would be more complex and require more careful balancing, and any occurrences of a double-newline in the message would break the attack. On the other hand, using a third-party reference in the arguments of a benign message call (which then fails to resolve with a carefully-constructed failure argument) would allow the attacker to control both the timing and the content of the message that gets sent to the client.

comment:2 Changed 15 years ago by Brian Warner

http://www.theregister.co.uk/2009/11/14/ssl_renegotiation_bug_exploited describes a Twitter exploit that uses this same technique, publishing secret information (in this case, a user's twitter credentials) through a pastebin-like service (in this case, a twitter status update).

Note: See TracTickets for help on using tickets.