| + |
---
|
| + |
title: "Disclosure of Replay Attack Vulnerability in Signed References"
|
| + |
redirect_from: /2026/03/23/vulnerability-disclosure
|
| + |
image: radicle-1.png
|
| + |
---
|
| + |
|
| + |
As announced in the release notes for [Radicle 1.7.0]({% post_url 2026-03-18-radicle-1.7.0 %}),
|
| + |
that version contains a fix for a security vulnerability.
|
| + |
[Radicle 1.7.1]({% post_url 2026-03-20-radicle-1.7.1 %}), and
|
| + |
[Radicle 1.8.0]({% post_url 2026-03-30-radicle-1.8.0 %}),
|
| + |
which were released since, also contain this fix.
|
| + |
We did not disclose the vulnerability at the time of releasing Radicle 1.7.0 in order to give users time to upgrade,
|
| + |
before we disclose the security vulnerability in detail, which is the purpose of this post.
|
| + |
|
| + |
First off, we would like to offer our heartfelt thanks to our community member Felix Bargfeldt, online also known as [Defelo]. 🙌
|
| + |
He made us aware of the vulnerability and explained the problem in detail.
|
| + |
We stayed in touch with Felix during the process starting from the notification up to the release of the mitigation,
|
| + |
and Felix stayed responsive with valuable feedback throughout the process.
|
| + |
|
| + |
In this, we see confirmation of how valuable our community is and how it shapes Radicle, even when it comes to complex issues at the core of the system.
|
| + |
It is evidence for the power of free software, not just as code bases but as community efforts.
|
| + |
|
| + |
## Timeline
|
| + |
|
| + |
| Date and Time | Event |
|
| + |
|-------------------|-------|
|
| + |
| 2026-02-14T23:43Z | Felix notifies us about the security vulnerability.
|
| + |
| 2026-02-14T23:51Z | We reply to Felix, acknowledging the issue.
|
| + |
| 2026-02-17T00:20Z | We have implemented an exploit in the form of an E2E test, and inform Felix.
|
| + |
| 2026-03-05T18:47Z | We inform Felix about our plan to implement a mitigation.
|
| + |
| 2026-03-05T23:13Z | Felix provides us with valuable feedback on our mitigation.
|
| + |
| 2026-03-18T16:11Z | We release Radicle 1.7.0.
|
| + |
| 2026-03-20T08:27Z | We release Radicle 1.7.1.
|
| + |
| 2026-03-30T12:00Z | We release Radicle 1.8.0.
|
| + |
|
| + |
## Introduction to Signed References
|
| + |
|
| + |
In order to understand the vulnerability, some knowledge about Signed References is required.
|
| + |
However, this knowledge is not required for regular use of Radicle.
|
| + |
Signed References surface via `rad inspect --sigrefs` and `rad sync status`,
|
| + |
but are otherwise internal, and not a concept that users will have to think about much when they use Radicle.
|
| + |
We thus briefly explain the relevant parts of the design.
|
| + |
|
| + |
Whenever a user pushes changes to a repository via `git push`,
|
| + |
and whenever a user operates on [Collaborative Objects] (e.g., repository identity, issues, and patches),
|
| + |
associated with a repository, they "change their references".
|
| + |
This means that the target object identifiers (OIDs) of some Git references in their own namespace changes.
|
| + |
|
| + |
For example, consider this invocation of `git push`:
|
| + |
|
| + |
```shell
|
| + |
$ git push
|
| + |
To rad://z4…ji/z6Mk…BU8Vi
|
| + |
87fa120...145e1e6 cool-feature -> cool-feature
|
| + |
```
|
| + |
|
| + |
Here, the user `z6Mk…BU8Vi` is working on repository `z4…ji` and
|
| + |
intends to advance the branch `cool-feature` from
|
| + |
the target OID with unique prefix `87fa120` to
|
| + |
the target OID with unique prefix `145e1e6`.
|
| + |
When operating on a Collaborative Object, the name of the updated reference is of the form `refs/cobs/<typename>/<id>`, but the principle is the same.
|
| + |
A reference is updated.
|
| + |
(It is possible to update multiple references "at once", but this is not relevant with regards to the vulnerability.)
|
| + |
|
| + |
This update of a reference is recorded in what we call the Signed References of the user.
|
| + |
In the corresponding bare repository in Radicle storage, i.e. in our example `$RAD_HOME/storage/z4…ji`,
|
| + |
the reference `refs/namespaces/z6Mk…BU8Vi/refs/rad/sigrefs` targets a commit which encodes the current state of the user's references.
|
| + |
Think of this commit as a snapshot of all Git references and all Collaborative Objects at some point in time.
|
| + |
This commit is internal to Radicle.
|
| + |
Its commit message and contents are *not* user-controlled, but computed by Radicle.
|
| + |
|
| + |
The contents of this commit are as follows:
|
| + |
|
| + |
```
|
| + |
$ git -C $RAD_HOME/storage/z4…ji ls-tree refs/namespaces/z6Mk…BU8Vi/refs/rad/sigrefs
|
| + |
100644 blob d5…bc refs
|
| + |
100644 blob 6c…0a signature
|
| + |
````
|
| + |
|
| + |
`refs` is a text file.
|
| + |
Its contents are in the same format as the output of [`git show-ref`],
|
| + |
which lists, line by line in lexicographical order, all references with their respective target OID:
|
| + |
|
| + |
```
|
| + |
e4…f0 refs/cobs/xyz.radicle.id/c9…d9
|
| + |
e1…6a refs/cobs/xyz.radicle.patch/f2…f4
|
| + |
87…20 refs/heads/cool-feature
|
| + |
83…40 refs/heads/main
|
| + |
b1…3a refs/tags/releases/0.9.3
|
| + |
```
|
| + |
|
| + |
`signature` is a binary file which contains an Ed25519 signature over `refs`,
|
| + |
performed with the user's signing key.
|
| + |
|
| + |
As the user intends to change the target for `refs/heads/cool-feature`,
|
| + |
a new commit of the above shape is computed, and the reference `refs/namespaces/z6Mk…BU8Vi/refs/rad/sigrefs`
|
| + |
is advanced to point at it.
|
| + |
The purpose of the new commit is to record the new state of the references of the user,
|
| + |
after applying the user-intended changes to the user-controlled references.
|
| + |
|
| + |
The (shortened) diff for `refs` implied by the above example looks like this:
|
| + |
|
| + |
```diff
|
| + |
-87…20 refs/heads/cool-feature
|
| + |
+14…e6 refs/heads/cool-feature
|
| + |
```
|
| + |
|
| + |
The history of these commits thus makes up all changes the user made to their references,
|
| + |
starting from their earliest change (which might be repository initialization, if they did initialize the repository) to their latest, like advancing `cool-feature`.
|
| + |
|
| + |
During the process of fetching this history, only fast-forward changes to this history are accepted.
|
| + |
|
| + |
## Replay Vulnerability
|
| + |
|
| + |
Versions of Radicle prior to 1.7.0 were vulnerable to replay attacks.
|
| + |
It was possible to pick any previous `refs` file, together with the corresponding `signature` file,
|
| + |
out of the victim's history, and forge a new commit as a child of the latest, legitimate commit made by the victim
|
| + |
without the victim knowing or agreeing.
|
| + |
The forged commit could be shared with other nodes among the network, and such changes, unintended by the victim,
|
| + |
would be indistinguishable from intended changes.
|
| + |
|
| + |
This was possible since the signature was only made over `refs`,
|
| + |
which does not include any kind of [nonce] or further replay protection.
|
| + |
|
| + |
One possible attack would be to replay the earliest `refs` in a victim's history, whenever they advance their `refs/rad/sigrefs`.
|
| + |
The effect would very likely be that their repository appears empty and stale, as most repositories start out with just an initial commit.
|
| + |
|
| + |
Another possible attack would be to replay `refs` at a state where the repository contains a security vulnerability,
|
| + |
to prevent dissemination of a mitigation.
|
| + |
|
| + |
These attacks can be performed at scale.
|
| + |
|
| + |
## Mitigation
|
| + |
|
| + |
A widely known measure to prevent replay attacks is to use cryptographic [nonce]s.
|
| + |
|
| + |
There are two immediate questions to answer:
|
| + |
- Where to "put" the nonce?
|
| + |
- How to generate or compute the nonce?
|
| + |
|
| + |
### Where?
|
| + |
|
| + |
Within the team, we discussed three proposals mitigate.
|
| + |
|
| + |
##### Nonce in New Blob
|
| + |
|
| + |
One proposal we considered was to add another blob to the aforementioned shape of commits.
|
| + |
This blob would contain the object ID of the `refs` blob, and the nonce.
|
| + |
It would of course also be signed using the signing key of the respective user that controls the namespace.
|
| + |
Such a design would have introduced more complexity into the verification process.
|
| + |
|
| + |
##### Signed Pushes
|
| + |
|
| + |
We did consider to immediately implement a new component to succeed Signed References based on [signed pushes].
|
| + |
Such a change would have meant introducing a new component,
|
| + |
at the core of Radicle, just like Signed References;
|
| + |
which is a complex task.
|
| + |
We quickly realized that this is likely the better alternative to Signed References in the long term (more on that below),
|
| + |
but that it would be very risky to implement under time pressure,
|
| + |
as we wanted to secure the network as quickly as possible.
|
| + |
|
| + |
##### Nonce in References
|
| + |
|
| + |
Another proposal we considered was to add a new internal reference,
|
| + |
right into the list of references into the `refs` blob.
|
| + |
This "internal" reference would not be user-controlled, but its target would always be the value of the nonce.
|
| + |
The drawback of this design is that it conflates two concerns,
|
| + |
or rather, two kinds of data:
|
| + |
User-controlled references and internal references.
|
| + |
Both would appear next to each other in `refs` with no way to distinguish the two cases,
|
| + |
other than the name of the reference, which in the case of user-controlled references is, also, user-controlled.
|
| + |
This design is less clean in a sense.
|
| + |
|
| + |
Such a decision was made in the past already.
|
| + |
In commit `989edacd564fa658358f5ccfd08c243c5ebd8cda`,
|
| + |
which was released as part of Radicle 1.1.0,
|
| + |
the reference `refs/rad/root` was introduced.
|
| + |
At that time, the reason was to prevent so called "graft attacks",
|
| + |
which we will not go into detail here.
|
| + |
|
| + |
#### Decision
|
| + |
|
| + |
With new ideas to improve Signed References on a more pervasive way,
|
| + |
and the pre-existing `refs/rad/root` mixup, we decided to implement
|
| + |
the third design proposal, that is, to add an internal reference.
|
| + |
|
| + |
### What?
|
| + |
|
| + |
In our case, to simplify, including a nonce in `refs` ("signing over a nonce") would mean to sign over data that is never re-used.
|
| + |
Using a cryptographically secure source of randomness is often regarded as a sufficient condition for obtaining nonces,
|
| + |
and this way of obtaining nonces is very common.
|
| + |
The chance of reading the same random data twice in that case is vanishingly small.
|
| + |
However, it is not a necessary condition for a nonce to be random in that sense.
|
| + |
The important property is that the nonce is never re-used.
|
| + |
Also, in many other session-based protocols, it is also important that the nonce is not guessable.
|
| + |
In our case, no sessions are involved.
|
| + |
|
| + |
We realized that realistically our design space for a nonce in the `refs` blob
|
| + |
is very constrained.
|
| + |
|
| + |
#### Backwards Compatibility
|
| + |
|
| + |
Mitigation of such a vulnerability "in a void",
|
| + |
is straight forward, when disregarding
|
| + |
backwards compatibility.
|
| + |
Implementing a backward incompatible change would have meant that we would
|
| + |
have to implement migration tools, and require action by all users.
|
| + |
It was thus pretty clear to us from the beginning that we would aim
|
| + |
for a mitigation that is backwards compatible.
|
| + |
|
| + |
The crucial design constraint we ran into was that in earlier and widely deployed versions of Radicle fetching fails.
|
| + |
The failure was due to a check for dangling references, i.e., references that target OIDs which do not resolve to an object, during fetch.
|
| + |
This ruled out the possibility to abuse the target OID of the newly introduced reference to a random number.
|
| + |
|
| + |
However, we are lucky!
|
| + |
With the exception of the root commit, there always is one object that we can use as a target, and whose value is a hash over all previous history.
|
| + |
It is the OID of the previous commit in history, in Git also called the "parent".
|
| + |
Note that commits can have any positive integer number of parents.
|
| + |
If that number is zero, we call the commit a root commit.
|
| + |
If the number is greater than one, we call the commit a merge commit.
|
| + |
|
| + |
The fact that for the root commit we cannot point at the parent is not problematic, since there also is no prior history that might be replayed.
|
| + |
|
| + |
Thus, in commit `d3bc868e84c334f113806df1737f52cc57c5453d`,
|
| + |
we introduced the new internal reference `refs/rad/sigrefs-parent`.
|
| + |
It may only be present in `refs` if there is some prior history, i.e.,
|
| + |
if the commit is not the root commit.
|
| + |
If it is present, then its value must match the `parent` header of the commit that contains the `ref` blob.
|
| + |
|
| + |
#### Upsides of this Design
|
| + |
|
| + |
When considering the complexity of the verification mechanism,
|
| + |
using the commit ID of the parent has advantages compared to the use of a random number.
|
| + |
When using a random number, to be sure that it was not used before,
|
| + |
and thus to detect an attack, all random numbers used previously
|
| + |
must be compared.
|
| + |
This translates to a traversal of all prior history in the namespace,
|
| + |
which is expensive.
|
| + |
The cost to do so grows linearly in the length of the history.
|
| + |
On the other hand, the hash of the parent commit is known locally,
|
| + |
with constant overhead, no matter how large the history grows.
|
| + |
|
| + |
#### Downsides of this Design
|
| + |
|
| + |
Earlier versions of Radicle do not know about this new internal reference,
|
| + |
and will thus treat it just like a user-controlled reference.
|
| + |
This might cause confusion, because the user never explicitly creates or modifies the reference.
|
| + |
|
| + |
## Scanning the Network
|
| + |
|
| + |
To assess the impact of this vulnerability, and improve our understanding how widespread attacks might be on the network, we developed a dedicated scanning tool (see [`rad:z3zzcqqBGP1NdvvMbSDt2jYYp9jSB`]).
|
| + |
The scanner operates on Radicle storage of a node.
|
| + |
It traverses the history of Signed References (`refs/rad/sigrefs`), across all repositories and their namespaces in storage.
|
| + |
|
| + |
Specifically, it looks for evidence of replay attacks by detecting sets of commits that all refer to the same state. We want to select, out of one history pointed at by `refs/rad/sigrefs`, all commits that refer to the same `refs` blob.
|
| + |
We call a collection of commits that all refer to the same `refs` blob a *cluster*.
|
| + |
Naturally such clusters can be identified by the combination of Repository Identifier and Node Identifier,
|
| + |
which narrows down the history it was found in, and the blob ID of the `refs` blob that all commits, that are members of the cluster.
|
| + |
Note that empty clusters are not meaningful in our context,
|
| + |
and that clusters of size 1 are of no particular interest to us.
|
| + |
We are interested in clusters with a size of at least two,
|
| + |
which is the smallest kind of cluster that could be an attack.
|
| + |
|
| + |
### False Positives
|
| + |
|
| + |
Also note that this analysis is prone to false positives.
|
| + |
Seeing a cluster is necessary for an attack, but not sufficient.
|
| + |
It is possible for users to legitimately arrive at the same state twice.
|
| + |
For example, assume that `HEAD` is not behind `rad/main` and that there are no other changes to the namespace in parallel, then this simple sequence of pushes would lead to at least one cluster of size at least two:
|
| + |
|
| + |
```
|
| + |
git push HEAD:main
|
| + |
git push HEAD^1:main
|
| + |
git push HEAD:main
|
| + |
```
|
| + |
|
| + |
Further, note that the scanner does not perform any cryptographic verification of the
|
| + |
signature in the `signature` blob.
|
| + |
This makes the scanner much faster, and may only increase the number of false positives.
|
| + |
As Radicle storage is under control of Radicle tooling, which of course does verify
|
| + |
signatures, we chose to trust that the data in the store was not otherwise tampered with.
|
| + |
|
| + |
### Scope of Our Scans
|
| + |
|
| + |
We scanned our two nodes iris.radicle.xyz and rosa.radicle.xyz at 2026-03-30T08:30Z.
|
| + |
These two nodes are very well connected to the rest of the network,
|
| + |
and they have a permissive seeding policy.
|
| + |
Thus, they provide a good view of all public repositories on the main network.
|
| + |
|
| + |
### Results
|
| + |
|
| + |
| Measure | iris | rosa |
|
| + |
|----------------------------------------|------:|------:|
|
| + |
| Repositories scanned | 7 090 | 7 073 |
|
| + |
| Repositories containing duplication | 199 | 199 |
|
| + |
| Namespaces scanned | 9 212 | 9 174 |
|
| + |
| Namespaces containing duplication | 228 | 228 |
|
| + |
| Duplication clusters | 477 | 476 |
|
| + |
| Maximum cluster size | 24 | 24 |
|
| + |
| Median cluster size | 2 | 2 |
|
| + |
| Average cluster size (nearest integer) | 2 | 2 |
|
| + |
|
| + |
A histogram of cluster sizes on iris.radicle.xyz follows.
|
| + |
The data for rosa.radicle.xyz differs, as expected, only marginally (see table above).
|
| + |
|
| + |
| Size | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 21 | 24 |
|
| + |
|-------|----:|---:|--:|--:|--:|--:|---:|---:|---:|
|
| + |
| Count | 421 | 36 | 6 | 7 | 3 | 1 | 1 | 1 | 1 |
|
| + |
|
| + |
The larger a cluster, the less likely it is that such a cluster is the
|
| + |
result of legitimate activity.
|
| + |
|
| + |
### Inspecting Duplication Clusters in Your Namespace
|
| + |
|
| + |
<details>
|
| + |
<summary>Show all clusters of size greater than two.</summary>
|
| + |
Data is shown in YAML to save some vertical space.
|
| + |
The structure of keys is RID → NID → Object ID of <code>refs</code>.
|
| + |
The values are the object IDs of commits in the cluster.
|
| + |
|
| + |
For example, the first two clusters in the listing below are in repository
|
| + |
<code>z2…4C</code> and namespace <code>z6Mk…5Tkm</code>. The duplicated <code>refs</code> are
|
| + |
identified by <code>10…d9</code> for the first cluster of size 3, and
|
| + |
by <code>8d…d8</code> for the second cluster of size 3.
|
| + |
<pre>
|
| + |
data:
|
| + |
z22qZYU1Rozc61i8kT7RCt3Wmwa4C:
|
| + |
z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm:
|
| + |
10fc2cfb880bb7e2a96df378cd4869747b35ced9:
|
| + |
- 020d2110c8ec5ce0ccfc9cdce88f639fe094ee0e
|
| + |
- 3996116f2628af1033d48ec20ebd656b0c2a4922
|
| + |
- b68d4e6795ba13a5380e476766f82c6c61cef17e
|
| + |
8d901f488e1a68bab5383619da8126c34af7aad8:
|
| + |
- e2973b023e5b93daf24cbd45fc4dce640dae26c7
|
| + |
- 8fb4cd4f89b1d632a937c4479dbb9398f05345f1
|
| + |
- ff0ec7352ed28b922e286dc991c928724f649187
|
| + |
z233GtFMEuSTU35DQ7Ya8GuCD7TVr:
|
| + |
z6MkuvmuRwL2XVB3zDFZQpiY2fxnr6rp4Q1L61uZ1RDTk9Nf:
|
| + |
5a068717317eba995abd2e5fe423dfbfae60c8f7:
|
| + |
- fa112a588c8bdf34ff255fc43272b63505e45d29
|
| + |
- 46f81c05eb5e0454e21487d44782d8c28201f0d5
|
| + |
- 63a8a6947b30b48133be5efefd65b3fde9122780
|
| + |
- 5af4f25a846a47d41ee1cd8968617bde531970c9
|
| + |
- c1e2510fb16a537a3173dacc18caab4e0a2e958a
|
| + |
- 9d3f0a3647e7d022b0eb06b1aee734d70e5e4448
|
| + |
- b809917f5ae21c3dbe4858d6d6e0302488ede004
|
| + |
- 7e7c85e1bbc0341ab9d24c369043d31a626285a0
|
| + |
- 9885c53ce0a5212aa0c44eac591fabfc9b3e6d1d
|
| + |
bacac318f72171920749ce6d8ae6679a0557cf0b:
|
| + |
- 675e4186f0324c15ec7f0e41f0f5508b2e4a9407
|
| + |
- ca70fb259845a75e720971ac40fce70502732b8a
|
| + |
- 2c633473b1789024ae3e4074cb238850ddddcc55
|
| + |
- cf823b6ae75528650a5f3385446ed8d9d9cc83f7
|
| + |
- 0286b3d098d55f39b598a80e9151aea1b6555dbe
|
| + |
c45eede36f0f5ddf54c95cdc392e3d43d1a2184a:
|
| + |
- 599ab3267c6dc371c5a3d623df1c6a6f4ae1208d
|
| + |
- 59504a6ab0d1e409fd59746ac12ca6cde2450d1d
|
| + |
- 33c4e6fec3cefdda7ab53ce546d0767ab033b813
|
| + |
- 8311f777e544168bcb402906897a115334bc2210
|
| + |
- d55539ba6a11f6bcf1881b411555fb277e837cd2
|
| + |
- 9e92764db881fd77f5bf09a1f4c4184573ab00e1
|
| + |
- 08e6c1ff9c3e46b3a949df9f9b567f9246aceb7a
|
| + |
- 0d885934bf59df743cde21fd110320f1ced0faeb
|
| + |
- 94af66ae97bd7b31b1192957467b4b23a9f8e131
|
| + |
- e419d91c3c0deedb7615875cef629f20d69d83fa
|
| + |
- c96144f14d32a62b43631f0d5b1a0a8b4ec81c25
|
| + |
- 8fa0c1b0c4c46f053efbf683102f5d669fc8c587
|
| + |
- 9774ac4729cfbd487fdc3b6dc3e91fb09d2c3d9a
|
| + |
- 3e188979ed34d546d6a247a9537ed540d3f2852f
|
| + |
- aacb4bd22068d64c5834720911d6c1468ccbd1c2
|
| + |
- 893f75739b782a1190fc35d1d20be08217f5132f
|
| + |
- 477ee900e592af7625921beede080e1cdd7d0c7c
|
| + |
- 589ddd8b56a8a9540a8316f624d1e44f3c84ab0c
|
| + |
- 68e1e31c4be1f8a9ca6ad6b6c36387893079f17c
|
| + |
- 395deda862561ccf15dad3553ea851e27184fc21
|
| + |
- a8ef1db64223235074ed0e633e4336c9db433b47
|
| + |
- 95e9e3ad4fd68d23d994d120f5e422eb93e5af50
|
| + |
- fd65fb4c2e66dd3b672419d82dfb1d9bfb10a054
|
| + |
- dff86f8e623b5964bd4fa87459b46fd7c3d73ce0
|
| + |
fc2f3d44f50e4f0558a4911812d8863fab8473bc:
|
| + |
- f8a692f98629ccd366859b65ce4c1ab3dd99b39b
|
| + |
- e2decf4023e5d0f8f6a1d36ba5a0f20f69f54d46
|
| + |
- 8be1619100b5b08c22e582664fb7bf2590dceb62
|
| + |
- 745b0c26a85bb599970f43a05f0e805e270c754f
|
| + |
z292aD8q3r8xqhJvLe2zv1b24gu7a:
|
| + |
z6MkvVZk1A3KBApWJXv2Ju4H14ErDfRGxh8zxdXSZ4vACDg5:
|
| + |
54d0afcb118591ba1f45e822787ee149289da62f:
|
| + |
- fc98f114b5e9f47356dede7a11f37deba2153a85
|
| + |
- a536df85542a4e0a6b115d604db32c26d502f7d9
|
| + |
- b15e19bcd897cbcc2d12e35671ae6cc4a6f1ae19
|
| + |
z2DJwn8E33K8RVGPFzJwCmuB8rCGP:
|
| + |
z6Mkondm5dgAxsQnV3iuvZBCGMAQgfAg4zSdJWoNVJr7i7wp:
|
| + |
57d28bd65223769563a12eb7bf81176f70a83461:
|
| + |
- 743aa52dd80abd549a557809cba1a9809bcd4a30
|
| + |
- 6bd29cecfa07f32f0e339e2a4bb4b7329362a03a
|
| + |
- 08c6e6cc77a5af5e44c09c133f8eb0f44428af7e
|
| + |
z2G17GyYnKvPja34T4fWjiT6Bmrm2:
|
| + |
z6MksoRnvY29wWrgpGaeg5ztVAy1D3xVNjC1n5mFwuWPtwDz:
|
| + |
56fb7f200d7e03def9b32ca538d022c96e1fc00c:
|
| + |
- a021902e967d3270c366632b0cf797f847b4aa51
|
| + |
- c40d455ed716f47bd39ed55511bd534344a96a71
|
| + |
- fcac32e023de0e8702be6a3eb98dfb9ecf1bbb06
|
| + |
- 7d576445429711b6e04d6cde77a0c9d644f6c18e
|
| + |
z2M2jmXxycq7i2Yqx6PjidxUgdGDe:
|
| + |
z6Mkondm5dgAxsQnV3iuvZBCGMAQgfAg4zSdJWoNVJr7i7wp:
|
| + |
afb3c989b0a77c2fa39fc337adb336633e12a3fc:
|
| + |
- eff038cb188c5f5eef8c91c3e915bddd9ee765fa
|
| + |
- fdc35f8432464210ffd214f8bb783e9582b69792
|
| + |
- afce8df50881ced72cf91678c994d35122df9c0f
|
| + |
z2SWNmqFhpqKCwBCFLjosCnjny5b1:
|
| + |
z6Mknjohi1pApYKAt8XeZzjwiWSqhG8bJS77ZT8pcyTrj9ir:
|
| + |
e4792a846a12e85cacca51153081320b357101a5:
|
| + |
- 6089f604ef6f385c2fbebcef452d3ff6796c82cd
|
| + |
- 9060e7da5f0c85bf308dc69f9edfaba0435f1aad
|
| + |
- 167bf94bb8d0bf4368d2bfc0381c4425191047c9
|
| + |
- cd24cb2f721e7d35d729dcaf8d6d4bb200a89d30
|
| + |
- 4d2a29ee2186ce0bda2110cf4dee4a3840dc7b05
|
| + |
z2SXkaceJw3YmS89T1xGysnFSjWsw:
|
| + |
z6MkvVZk1A3KBApWJXv2Ju4H14ErDfRGxh8zxdXSZ4vACDg5:
|
| + |
17962e342eb08d3380f08bf9e0c084749d8e6423:
|
| + |
- b980d0dc074e61b69c73f09ff3e110a3e272a7d9
|
| + |
- fcc743e8908c61def5e388cdd78afcb6d793511b
|
| + |
- 820852767b33581f49fbe93474ff0c0e03821702
|
| + |
9e5f6d3ce756b7531063c34f285f625c2e45e5d7:
|
| + |
- 2dfae40117a6ddc69fbe68f772d9974971b5ec1e
|
| + |
- 675c57c5dec49cb2cbb8b23382e0f796a89207f6
|
| + |
- a7b733f9187ee893a361aedcdc5c284a2690e9f9
|
| + |
b468e088741015fbe10ebbbab3b46ed002f347f1:
|
| + |
- acaf9014354b97d3414b5386c3595bc6a489ec93
|
| + |
- 5c88f502495edd901ad4e6e79adebd8a8fea4e36
|
| + |
- 56b34e6a79a45e2b239f91631ba9bd97a344273c
|
| + |
z2UPb8WFofVxXXK1A7918M3gxe1s2:
|
| + |
z6MktH4iY2Mvq5ZkmoM7ayB4GM3U4p6HXKgB98nafiWkCam7:
|
| + |
71ec6ed930d93e4eb17aa8b97fb6cb683941ce00:
|
| + |
- 51bc3f33cb677b6714f846f09cbf80ce31d4ba3b
|
| + |
- 1496341c0c335ef4da53d409cc22658c5795c050
|
| + |
- f86f63915f942c8fbf4d858d7cd17305ed450fc2
|
| + |
z2a7Te5b28CX5YyPQ7ihrdG2EEUsC:
|
| + |
z6MkiuWYPxbojmhUiGGWrzSuJK3HDpbbtWo3QMm3e9VP2gJP:
|
| + |
aa0319d9e647a13ee9d8dd4339d0e23faba7701e:
|
| + |
- d6c032e15173e7c803343989f107126d187acd65
|
| + |
- e8e506ae8025f7b335b99b8cd4f88359b16ad786
|
| + |
- 8c953561309289057af43d36866bc1b211f07b5e
|
| + |
- cf20913f07da4da0ad99e3439ee5b450c1c7d410
|
| + |
- c4a60d727d45851bb8ab998b0efb4742745a8934
|
| + |
- 6f9e7185a80c9ea2cccafc31bd0bebdf9c8d3ca9
|
| + |
z6MksH6Yr4pkJqPYnY4N5e5a5bCdyCW88grKRkkK6KeMGwLN:
|
| + |
6c83cd06fab7dd8b0c59ab0b1d268460870384e5:
|
| + |
- 5748faf8d6f4f4cbf31a82e92c21198f8eb1cfcd
|
| + |
- efb603a0570c596d81cf226930e1c33016948310
|
| + |
- 2844cae8e87fbda1334fcf818b8e2b63fc3d1a3e
|
| + |
z2tDzYbAXxTQEKTGFVwiJPajkbeDU:
|
| + |
z6MkiksiCWT4LDhMQK75t6y3m2gj5iQPE1nhJfJuX1wd5Uef:
|
| + |
4224a04869cf6abf60562c58ad793256a9e2fb5e:
|
| + |
- c59eedd1f496d1837629d25f94d5b110c149c1ea
|
| + |
- 1be90b217604b93ca89d2492b4ac0b19cc9d6efb
|
| + |
- a93136ea49b575065959cb0f9c416d85218f9748
|
| + |
z31RADNa8uNMWFep88bhenzxTR1Ei:
|
| + |
z6MkpC1ahHS1r9KR12LbYnoponj8swuaAEiNpvVvB9hcXhvh:
|
| + |
156bae94c8df83e75a00a3e07e94120c7f873a7e:
|
| + |
- 4bad6e268ab12e6c5e6c42972d7e3d5555d7559d
|
| + |
- 7339bfd5ef35cb2cbd34999658adbdb9388f79b1
|
| + |
- 6f3fb0a302a6f27a7dc4fe1a21a29f6292edc811
|
| + |
9e5254c88d36fc6dc42145c897ec242d359b6ace:
|
| + |
- 35fabc1232f1682ac1de036b3ae9badfac8066ed
|
| + |
- f5fe3e0b6c7c6a0a88c11c95ce462165a89791aa
|
| + |
- 4ca5aa550a6a10e7125e3478bc6102df6a1b5586
|
| + |
z3bBVej6SJXuahv5hCXhiLKeB9N9k:
|
| + |
z6MkvVZk1A3KBApWJXv2Ju4H14ErDfRGxh8zxdXSZ4vACDg5:
|
| + |
ac1013b2f350d0b8b64b287d7e0d03ceb819161c:
|
| + |
- 66060f455ca37923d5f80407484de8f2d7999325
|
| + |
- fdac3aac66474d0d71adbd4d2a2e22c00cadcd46
|
| + |
- a2f8a5c0229b79c1e9bc4c18e7806239fd19451c
|
| + |
z3cyotNHuasWowQ2h4yF9c3tFFdvc:
|
| + |
z6Mkgom1bTxdh9fMFxFNXFMw3SbXnma6NARdsfcFuunurCap:
|
| + |
39fd416cd098ca04e92808fbda34ba6ed5ed1003:
|
| + |
- 6cb96494730c64e0936c4e6c81813b5fef4b041b
|
| + |
- 5bf7ee385158e6591a6ff5ea8281140c1ea768d3
|
| + |
- 322fd2ead09b974c1c179114414412b52dcd8563
|
| + |
z6MkvZwzK64f3GuDcAs6dEcje89ddfHkBjS1v9Dkh7aCGq3C:
|
| + |
9bf2cf1021e0ca153212db5c530349e59729ad50:
|
| + |
- e81e336b1f89f78e8362c9c33fc659aec6ef8e38
|
| + |
- b4c8b0cceb0fd00215b9cd4eb58c1284c4d05eeb
|
| + |
- 91d69ad99564695610b111a2186781e1bdd93d71
|
| + |
z3gqcJUoA1n9HaHKufZs5FCSGazv5:
|
| + |
z6MkgEMYod7Hxfy9qCvDv5hYHkZ4ciWmLFgfvm3Wn1b2w2FV:
|
| + |
27b23b3e05c6d6a4ae1d9db3e53e046989dabd0d:
|
| + |
- 406c96681faca1a8def7ab01184f6bcc16dd55d6
|
| + |
- b748c1d7cda0486fd02565bfeb556f65da8826cd
|
| + |
- 3708d86b4d14b34b9dd1cc2b5b3785f4bdc2f852
|
| + |
9167c0a5c209bf3a31adf25d59acc411e469fe70:
|
| + |
- e11752a3a42617b40c427292add2235db54308c9
|
| + |
- 420a0134ff94e9037c10bfb324cc403f01d119a1
|
| + |
- 69145ba62b572ff49b7c8836d8efce348c23e2f2
|
| + |
- f0a59d5fd4ec17d3a12b88732cb52651233b72d6
|
| + |
- 6f5c92b73e1756098fbe99a5e2b98fdcd1888f1e
|
| + |
- 4910e88b9caddb43382fea2a75ea1dd0a674f7c6
|
| + |
z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM:
|
| + |
1f3685f577c2319748cc43f2b97559e6e6f27b30:
|
| + |
- f0ad9600f3bfa3c193f52507480ec161be0b0cd0
|
| + |
- 2fc43986f24c1a8ca092186e9d179f02d4a5f288
|
| + |
- 979e00e332a9dff186e28bf6f2ed8cc62047b558
|
| + |
fb8387acc1c8cb9fe33d13ecaf01f3a5eff6e07e:
|
| + |
- 40361a592d391b8b56e23173605ac490eb227d94
|
| + |
- ab52bc081d8720216c67c2643b70779e31467199
|
| + |
- 729ef07fef8fc79fb2d6dcfc71d374b20095471b
|
| + |
z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz:
|
| + |
4996cff3821f7cc5b4850a56efcc887ff6983866:
|
| + |
- e0502b2c843d0997e43e63548388ce6884d73dd0
|
| + |
- 178142f005781af571830ba573019e3e57e28f62
|
| + |
- 7176c630b9f2cc22c8859ccfdcfd6d66da438af7
|
| + |
- ac7dcb92b998f9716872421f2d45faf285784940
|
| + |
ebf3aedc50f350e8670a1178a70c5af1217eb664:
|
| + |
- dee5da561a0c76ec07938f9b88047b3f3bb210c7
|
| + |
- d3f58fe73caeededacb892c82cc649545b1008e0
|
| + |
- 6d28c01d2afaf9fd0919592b2e90ea94e48e9905
|
| + |
- 34c04e812208f53ea43fabba3a70720b949f6c72
|
| + |
- a8301092c4983b672a66f0a263aa6c95b813caaa
|
| + |
z3i8ZmSrLFsuGFkvCsMBCSskqDipZ:
|
| + |
z6Mkum88mKcX2sARx8R18RZUmAdakPFbXtdHzn8JNVEDqkxj:
|
| + |
10ab8994984779b7746bcbfd84d837aba87d9d47:
|
| + |
- d8f8dcb0f40af5fae6ec61fe8223c0b0a02fba7f
|
| + |
- e6b58eb83fb64e662b0cb6acae2e207271fc9e4b
|
| + |
- 4f40235f10e11a7569b69fae658466918bc74300
|
| + |
- d83c5dba70548f42e53ccdbb4bb0133d1c3f16bc
|
| + |
z3kozTn4Kn5eJtgJQj1aCFUpqxW5Y:
|
| + |
z6MkvVZk1A3KBApWJXv2Ju4H14ErDfRGxh8zxdXSZ4vACDg5:
|
| + |
a1f828485c94c89636d5c9e22a541dcc908a203a:
|
| + |
- 0fecd475bd4df5cd2003f6e492d2505c7071e53f
|
| + |
- dc737994daff3d70f92a80aff220b1090a5ff041
|
| + |
- 090a873a48ad5815ce40078c10f443b87d8cdcf2
|
| + |
z3nSmSzzTjgtbapU2P33JDdZ9Tca:
|
| + |
z6MkuvmuRwL2XVB3zDFZQpiY2fxnr6rp4Q1L61uZ1RDTk9Nf:
|
| + |
17207437aa5606e660b95066a0e9ac7b42a1eee3:
|
| + |
- 9ad94184c9fc8056103a8d31dd4f3c1c1df3362a
|
| + |
- fa5ce57a31843137769a85aa9425fdd872e5e29b
|
| + |
- 507b9b36086db7e6a55a61bc67a7fe6a9312db9d
|
| + |
- 72e2c085aad4b34f7f5bbcb0cd89c32a9d34243f
|
| + |
- b4457712ae6f94886f1d3040aa93a1fa6e9661c9
|
| + |
- 8a4a76c132672b8d91cb5d1a9f521a1d2d36d7cf
|
| + |
- 9333a611f28e897afaf0290badf8db1d4c06fa8f
|
| + |
- 89990057bd7b6bf9f9b779c78efb995458569b14
|
| + |
- b5bb2f7011fb9d20a8f9e92f41070a838b48ba25
|
| + |
- 6b23ea763d319333257caabd2875255660ed5ab8
|
| + |
472d33e0d3bef33f5c5fb4914826b62d65fc7bea:
|
| + |
- f8b44c41c15e91041dd54bd92f8a1797d55347eb
|
| + |
- 021a78dc7265aab50b531829179dcf71d0d0b468
|
| + |
- 07bb5c12feacb35989efebf1e3d9b0f5310e65da
|
| + |
- 9fcc377078cca9bca17105b7ddc6297c4fe7a983
|
| + |
- 7b794d3d35e563258bf5d9adc0778fda06bc8197
|
| + |
66721d2144d0958526fa3325a5dc36b2a62dc488:
|
| + |
- a48104cdc5d6c932ee7bec90000f19c4a6ca49e4
|
| + |
- c2755f5706a240f5b95e1c9458b736799dd5eeac
|
| + |
- 8b0319df5b51b56c1d4325f328d3803b766370b7
|
| + |
- 0d882594a00d654b20528a8acdc185798becf623
|
| + |
- c019f8d3be7650cc13cf21f21c0282864c257e55
|
| + |
99f9a48b0ba538e7f27317be95bb4cdc51c49daa:
|
| + |
- 0db528badd5050b34051c3979e5d830f01cc3641
|
| + |
- bc3bbfb732ac8a29fe14d7b0e709987c8a16e254
|
| + |
- 66553217cf89ac330e44753637c3e4fc5143d39b
|
| + |
- cc5fe0d799d35b9b8435cd759dfedaef2d37224b
|
| + |
- b13205272467ba26553379cdaea893b81efa217b
|
| + |
c6ca4c1bdbaaaa2072a6cbe8a8c0dd35e17d054e:
|
| + |
- bf927bab4c19d1dd9ff7fa54cfe4c856c5ca59f6
|
| + |
- 2a47e681d9f2ea5ddd41880854ff48b907718752
|
| + |
- b3e532bf7173d40623d50b18003694331ee80aa1
|
| + |
- 2de991b41ad48faa98e1040e31370363a6640581
|
| + |
- 2c402f3f304f438781b15c3737a1560a0f5adc7a
|
| + |
- 194b61a9f6b189f608881985fd40d5e8558c2267
|
| + |
- 7557388731ea0423262ac4b1f6a2244e011ff475
|
| + |
- 6582b20dfa5f27155aab2dc9a6bc06d13f40774e
|
| + |
- 6a6bbc67eaeb0051acf84da47eeea4472961f152
|
| + |
- e3e134332b6c34eec0cc30be2cda372c6a92e3f4
|
| + |
- 5d0d86ee7096389a417d0bfd027a17b6a528b9eb
|
| + |
- 33cc59256f07423667100a85257859e777506175
|
| + |
- e993a8beea0af22b10123967bc7132b1711ce7ac
|
| + |
- 3e83e00b335a111b8dc1e014aed4e3cfb7fa368e
|
| + |
- 83958c8a9f43d284e0ef241e70176012a34f1959
|
| + |
- d78e17ef99837cecc7dc1c6207d0f00217d4d241
|
| + |
- 1c5f2a29b0b1b68cfe1094c2c3bd81dec907a3ee
|
| + |
- cdd4571c8ca88fcd3aaaf70cc2ad7ec865a0c55a
|
| + |
- 6a2c1ed4bdb097c1174fdec1a498b74b24bf69f5
|
| + |
- d1ce807a52045776ea73af97a2f9d4b7e2cbd6e6
|
| + |
- 294f05fc3fcc0269bf1e61cf6ed7b0997fcfe743
|
| + |
z3qsiV1cLueoZ7mbEAHCTA58yPP7e:
|
| + |
z6MkfSMUiSTtktgCbXDgjkAUcBadNzKgHrNnE6BPRC6GnNk4:
|
| + |
02cb4108bea242fa2dc698c66fd2d7ecf3882609:
|
| + |
- 13ca3c8d3808d4c2dc654e764927a80b66398c53
|
| + |
- ab8d6a02fe136652e1a52f04640ca92c9d091895
|
| + |
- 5173ae463e6614117eba2c473c53560c39f962d4
|
| + |
- 17267c59133cb2c6b629f51852f40cf25c6e036c
|
| + |
6452b1f1a37939d0e8d8b7d91c72f6244d790627:
|
| + |
- a4c02a390638da0a0aa0aded889caab47faa6eb2
|
| + |
- 5b8151b4e73e8492b88612e3c19e9a4aff37b671
|
| + |
- 6dd4a929c83431a7129c1c7d40015f2d574d1dbf
|
| + |
z3rwhJ9rQ82H6GXg7ZCt3UNpStbaW:
|
| + |
z6Mkg4WMCHiK1wGCXLJidM4fWQuuCtb7jVmuLqCxSEwJLtvp:
|
| + |
ef70e392e8afb2327318c897900a90c8d920f5d8:
|
| + |
- 8939508dc9cfe8f31fc06e194130a679aae9b2ce
|
| + |
- ad545fbbc5ac30ddcd39959bf8dbe07e7e7e9883
|
| + |
- f4ff9f55a20689cd9a11dd13c811dfbfe46c4823
|
| + |
z3xjvkTXHZpBuLUEZjEqmmcsk8HPe:
|
| + |
z6MksoRnvY29wWrgpGaeg5ztVAy1D3xVNjC1n5mFwuWPtwDz:
|
| + |
fff559359e9b22c3b309dabae53a47d0a6559d0e:
|
| + |
- 546bc4a6364ac2c573367707480a3c68f817f939
|
| + |
- 76ac736a3eff6d45812127cc7ce7a8eca77262f8
|
| + |
- 0b01f44d1c574ee54583519d9fed104687fdfbc0
|
| + |
z4P7htL76j7ag3F76gTaT1zLuZRe7:
|
| + |
z6MkvVZk1A3KBApWJXv2Ju4H14ErDfRGxh8zxdXSZ4vACDg5:
|
| + |
cfe6ba4988ea2810dc597ba9f7e7f267ff8fc476:
|
| + |
- 2ee7eb06e05afd9e59f9173de38106db591a91c1
|
| + |
- 9634b76df4de925fcc3adef4399731b6c52f75e5
|
| + |
- 371b13211bbbf7316a890678ae845a9810c77e11
|
| + |
- e770f7d340ab06314ca0af4e0f0fd03a3cf11bd5
|
| + |
- a7e51437bb06757807b0a59dcefd3f6230e88d5a
|
| + |
- 5aaf3dc86d8be531d271c73d9eb9474beebea8ed
|
| + |
dc10dcaa1b25378d08fc855febe81ea79c823ea0:
|
| + |
- 7ef26dd108bdfbb95b9e2e4a84e629c46d68274a
|
| + |
- 7977fa4c1d2f78ac6a4554f0fe16738fee7f2340
|
| + |
- 54de6e53f6303cdddc0e974f7b69b6057ac9714c
|
| + |
- 7c09f2fa5e3d1e311f868f9d711df3b1f34f3c85
|
| + |
- 8f3d5a12f84c1041ea2882fbecfae588c89b88b6
|
| + |
z4SmCptZ81EcBS5pbvFNbppFkY1Cw:
|
| + |
z6MkpZ5eyZFjPDiF3KqtS6yJmqq1gkN3KRMHx64ein5qTtb9:
|
| + |
459ec8af8ac81d68a961d67d7ad30e49a6627c56:
|
| + |
- 104f16f083d510415a280d61e8e34f457190b35d
|
| + |
- 45f94ce863f46333e9bb83d03daa3cac6691953a
|
| + |
- 09e0f91ace979849cf0a10e9431ab2dbac26df80
|
| + |
z4V1sjrXqjvFdnCUbxPFqd5p4DtH5:
|
| + |
z6MkeyYzy2PmF8RdXzDXeNzbUPmqxjPG9mgDSwZGcr5Dccbv:
|
| + |
c896b7e48ec5eafa181d705454034405ee0a5d17:
|
| + |
- d9e34d2957cfad92d6027e080f59bd9f070e81a6
|
| + |
- 76b44d734b28d2827d997a1680cf71d474299203
|
| + |
- c362cd037f247eaf393c646af0a52670a74b22c4
|
| + |
z4V2SLbvf46Ttgkxkuyvboc5yYbGy:
|
| + |
z6MksYHVGBqiS1dpnhToGMP6gMjHPnHrbH5NEqa54W32CAjk:
|
| + |
9c62d61f65d9d8655f8bfb3877b197f1e794e691:
|
| + |
- 2e0a912e0702da0591c1eb229a87e1addb326c61
|
| + |
- d5a0dbdf6db706729ce48c03571892e4ec6eaeb3
|
| + |
- e69e8ba85fbfd0b8dc7b21b7203a7247702c6a81
|
| + |
z5EmYRhaFMaaccNYmeufWat8x1BA:
|
| + |
z6MkgvaRdahQZfnf7ccMHByeHtTYrMrM3MuSGYawF6ZL6DNj:
|
| + |
3b74250fd2343e03f479495ccdb9e054326efca9:
|
| + |
- 57857ec2e1e1e782b554ddd21158076ea0744986
|
| + |
- 47449832294923399fdc44d875c73b43fd13c0d8
|
| + |
- 2548f44064824ec39f566a20e292c5caddb6f398
|
| + |
z8sG73WVo2TAKjoqh3vBvhfRqPsw:
|
| + |
z6MkvVZk1A3KBApWJXv2Ju4H14ErDfRGxh8zxdXSZ4vACDg5:
|
| + |
52c8f1d5c7eee9f2e40f376219f8018166a7e298:
|
| + |
- c6357c77874ff8e3a61ee91e247b2c5b19256223
|
| + |
- 79f89bf1d911fd13cb0635e5d910506417f656e4
|
| + |
- 20f5a8643df6d891a970e1ad60217d6bceb8cfe8
|
| + |
- 9404609cefe6f61a9c723d3da80b014ae7e4f4d5
|
| + |
a5cd8e052ee1207e9d7767e285f7b9d72ca8640b:
|
| + |
- b8a1b10684646f5b5c0a452faca73e45037c5cd8
|
| + |
- 62bd2e9431c2fba38525f75b3f92f25b8740ee23
|
| + |
- ec48e456ee6ae7bdb1c883650872e64d11a46926
|
| + |
zcmrr8AZD7dcRwAKnXBroJKyT2En:
|
| + |
z6MkpC1ahHS1r9KR12LbYnoponj8swuaAEiNpvVvB9hcXhvh:
|
| + |
641b572966b093f16d7bdf9236fc5f1328f6b221:
|
| + |
- af6a171c5b48ad9e1dcf00ac4de777d6b1f53f3a
|
| + |
- bacc1a70110fbcb293861f86f85a1c9aaca927a8
|
| + |
- 6fc23526bf9ad9f40f51b778dcda804f1e6eec44
|
| + |
zjTKEBr1jwtpu7CkJtBd6jTnCXSE:
|
| + |
z6MkpC1ahHS1r9KR12LbYnoponj8swuaAEiNpvVvB9hcXhvh:
|
| + |
0774b3680463fb175c1bbc18d5d6ada7f70e7bde:
|
| + |
- 40a9bc8ba4a26be70fa547ee41c05e3faab26cca
|
| + |
- 6015e13edb37132dc9b2064d8c35e4b737efe786
|
| + |
- 2cee6f9645d259fc317ae351c6d8c344b89f17de
|
| + |
2c2bc8da268c4f4f4390ffe74acc9108165f0ae5:
|
| + |
- 35db02c7d4577cbb36211e355e089ea7fbafa745
|
| + |
- 8f50aa62cb1bb88706623beaa85e331bc49f8db9
|
| + |
- 51f567a1cede0e5adb3cf016a232a105ed08a4f9
|
| + |
8d897c693c1818d2b3e20a3c9bc4850b3aa399b2:
|
| + |
- 7c52d18dbf0482342b24ca408695f8ff30b218e5
|
| + |
- 861e64627a58e708dbdba038be27035388710c9e
|
| + |
- 364ef81a3563d12d5147a3d63ebe8e15d87bd090
|
| + |
f20ae096292aa467e3f22765bdfdb6e6dde874f6:
|
| + |
- 4ac9381c78a06668314b76be1f41bb487ebc70f4
|
| + |
- 2d65a1b459d8c15f8dd48907da0e925d81859140
|
| + |
- 6b51bc85a5121985d5b5bce498580cd871d748b4
|
| + |
fd1ae02017faa8be202f2756d50ef96a1b35cfde:
|
| + |
- bca64f8cb5fe5bee3b0a80b307d5b35a01874bb8
|
| + |
- e5dd135f1d7f37aed5f15a4eb52983ce043e83c0
|
| + |
- a22eac9724b66573680d84f8d39bf406e17518f6
|
| + |
zoBPQV6X2FH296n9gQxJr6suvSSi:
|
| + |
z6MkewCavrcnLurWU4TzMUYT4kX9FSknYvbZEWbh1CnGdzaP:
|
| + |
f84508c2e4ea65a8779bd03981b54680322c0d73:
|
| + |
- ee4d6709d57c0846c9a2f39d4084d2a3e236b299
|
| + |
- 435c10668b909d59bc7e182ed4bb8af9ceccc8c4
|
| + |
- 7128946bde23df75ca4fae07c0815c27aed150c0
|
| + |
zu5U7GRpyZRXeHfz6K5vdPq5CVVM:
|
| + |
z6MkvVZk1A3KBApWJXv2Ju4H14ErDfRGxh8zxdXSZ4vACDg5:
|
| + |
ced794c0d15bb629df1eb3d8f3637a083dd754ef:
|
| + |
- 1932fb5ef1f27eea461ed042ef1fe173114e7fc1
|
| + |
- 7153ece0e045fce12d715ccd328f4abe990f3a9a
|
| + |
- 4b56ab21a4b8705e7a6ba4d2862f4a0b67903fd4
|
| + |
</pre>
|
| + |
</details>
|
| + |
|
| + |
Alternatively, consult one of the files `{iris,rosa}-aggregated.json` in
|
| + |
[`rad:z3zzcqqBGP1NdvvMbSDt2jYYp9jSB`] or run the scanner on your own node.
|
| + |
|
| + |
If you need help interpreting the data, please reach out to us [via team@radicle.xyz](mailto:team@radicle.xyz)
|
| + |
or [via Zulip](https://radicle.zulipchat.com/#narrow/channel/369873-Support).
|
| + |
|
| + |
## Vulnerable Versions
|
| + |
|
| + |
All versions of Radicle prior to `d3bc868e84c334f113806df1737f52cc57c5453d`,
|
| + |
which is included in release 1.7.0, are vulnerable.
|
| + |
|
| + |
## Fixed Version
|
| + |
|
| + |
### In Radicle 1.7.0
|
| + |
|
| + |
In Radicle 1.7.0, a major refactor of the signed references logic was written.
|
| + |
The base of the refactor was taking the previous implementation and performing a rewrite that allowed us to focus on the core logic.
|
| + |
This allowed us to quickly iterate on the above ideas,
|
| + |
and then implement the mitigation mechanism of including the `refs/rad/sigrefs-parent` in the `refs` blob.
|
| + |
|
| + |
As mentioned earlier, the inclusion of the `refs/rad/root` in the `refs` blob was previously introduced to prevent graft attacks.
|
| + |
There was a note to make its inclusion a hard error once enough time had passed.
|
| + |
We took the opportunity of this release to make this change as well.
|
| + |
|
| + |
If Radicle 1.7.0 would have rejected all histories that do not end in a commit with `refs/rad/sigrefs-parent` in their `refs`,
|
| + |
this would have also amounted to backwards incompatibility.
|
| + |
It would be impossible to fetch updates from nodes that have not yet updated to Radicle 1.7.0 via the network.
|
| + |
That would have caused lots of disruption.
|
| + |
|
| + |
#### Replay Detection
|
| + |
|
| + |
We cannot travel back in time and retroactively add `refs/rad/sigrefs-parent` to
|
| + |
the `refs` blob in the histories of all namespaces.
|
| + |
What is possible, however, is to detect replay attacks in old histories.
|
| + |
|
| + |
It is possible to traverse the history and look for duplicate `refs` blobs
|
| + |
(or `signature` blobs, as the signature is deterministic from the signing key of the user and the `refs` blob).
|
| + |
If the `refs` of the head of `refs/rad/sigrefs` is found to be duplicated in the history,
|
| + |
we can decide to skip this head, and try its parent.
|
| + |
This process can be repeated iteratively, and it is guaranteed to terminate,
|
| + |
since the `refs` blob in the root commit cannot possibly be referred to by any
|
| + |
earlier commit in the history; simply because there is no earlier commit.
|
| + |
|
| + |
There is one slight oddity. As alluded to earlier the user may legitimately
|
| + |
wish to restore an earlier state.
|
| + |
The detection mechanisms would skip such an update, and effectively drop the update.
|
| + |
|
| + |
### In Radicle 1.7.1
|
| + |
|
| + |
Once Radicle 1.7.0 was released, our community was encouraged to update.
|
| + |
Generally, we would introduce release candidates before releasing a new version.
|
| + |
Unfortunately, due to the vulnerability, we needed to release without the usual ceremony.
|
| + |
|
| + |
When our users updated to 1.7.0, they quickly noted that there were some issues.
|
| + |
The first was unrelated to the vulnerability, and revolved around parsing of IPv6 addresses.
|
| + |
However, the second was due to the aforementioned hard error of the reference `refs/rad/root`
|
| + |
not present in the `refs` blob.
|
| + |
|
| + |
To provide some context, the `refs/rad/root` mechanism was introduced to prevent a graft attack.
|
| + |
Essentially, it ties the commits in `refs/rad/sigrefs` to the repository they are introduced in.
|
| + |
This reference is calculated by Radicle, and there was a code path that introduced a check for its existence,
|
| + |
and if it did not exist then it would be created.
|
| + |
This change was introduced in `989edacd564fa658358f5ccfd08c243c5ebd8cda`.
|
| + |
Then, 10 days later, another change was introduced.
|
| + |
From that change (commit `09f796234d76f4a25807371bb709c18678ac7bc9`) onwards the identity root would *always* be computed
|
| + |
(by traversing the history of the identity COB to its root).
|
| + |
Since this method would now always result in returning an OID,
|
| + |
our previous condition for a missing `refs/rad/root` would never return true.
|
| + |
Any repositories initialized via `rad init` after 1.1.0 were not affected,
|
| + |
but those initialized earlier, e.g., on 1.0.0, would be missing `refs/rad/root`.
|
| + |
|
| + |
All that said, there were a significant number of repositories that did not include the `refs/rad/root` reference.
|
| + |
Furthermore, this meant the hard error became a backwards incompatible change.
|
| + |
We relaxed this condition so that nodes could make progress in Radicle 1.7.1.
|
| + |
|
| + |
### In Radicle 1.8.0
|
| + |
|
| + |
With 1.7.1 released, we realised that we had one more piece of the puzzle to implement.
|
| + |
We needed to make sure that downgrade attacks are not possible,
|
| + |
and to allow users to opt-in for more strict verification.
|
| + |
|
| + |
In order to achieve this, we introduced a new concept, which we call "feature level".
|
| + |
It describes, on an ordinal scale, which features are detected on
|
| + |
the history of Signed References.
|
| + |
For now, all known features are security features, so currently
|
| + |
feature levels translate directly to security level.
|
| + |
Also, these feature levels are strictly monotonic, i.e., higher
|
| + |
ones include all features of the lower ones.
|
| + |
|
| + |
The three feature levels, as of Radicle 1.8.0, are:
|
| + |
|
| + |
none < root < parent
|
| + |
|
| + |
- `none`: This is the original behaviour with a `refs` and `signature` blob,
|
| + |
where the `refs` contain neither `refs/rad/root` nor `refs/rad/sigrefs-parent`.
|
| + |
- `root`: Requires the reference `refs/rad/root`. Added in
|
| + |
Radicle 1.1.0 (see above).
|
| + |
- `parent`: Requires the same features as `root`, and additionally the
|
| + |
reference `refs/rad/sigrefs-parent`. Added in Radicle 1.7.0.
|
| + |
|
| + |
The feature level for namespaces is now printed by `rad inspect --sigrefs`.
|
| + |
This allows users to understand which feature level their collaborators are on.
|
| + |
|
| + |
The reading and verification of signed references now detects if the feature level is `parent`.
|
| + |
If so, verification can return quickly.
|
| + |
Otherwise, in the case that it is `root` or `none`, the history is traversed.
|
| + |
The traversal is expensive, as it performs a number of reads from the Git repository that is
|
| + |
roughly linear in the length of the history.
|
| + |
As more and more users upgrade to 1.7.0 and above these traversals will have to be
|
| + |
performed less and less, restoring loading performance of Signed References.
|
| + |
The results of the traversal is used for further feature detection, and also for detection of duplicate `refs`.
|
| + |
|
| + |
After completion of the traversal, the features can be inspected detect downgrade attacks.
|
| + |
For example, this would be the case if the head commit could be at a `root` feature level, and its parent be at the `parent` feature level.
|
| + |
In some cases, this could be a legitimate downgrade from a user upgrading to 1.7.0 and back to 1.6.1, due to the aforementioned compatibility issues.
|
| + |
The solution to this is to always allow the user to create a new commit with the latest feature level.
|
| + |
|
| + |
If no downgrade was detected, the verification process then takes the first non-replayed commit as the head,
|
| + |
and loads references from it.
|
| + |
|
| + |
With the feature levels in place, we introduced a configuration option for the fetch protocol.
|
| + |
In the Radicle configuration file, `$RAD_HOME/config.json`, a new option was introduced.
|
| + |
The option's key is `node.fetch.signedReferences.featureLevel.minimum` and its value is one of the feature level values, as string: `"none"`, `"root"`, `"parent"`; defaulting to `"none"`.
|
| + |
The value is used during a fetch of a repository from the network.
|
| + |
The fetch will reject any namespaces that have a `rad/sigrefs` head commit that is below the given minimum.
|
| + |
That is to say, if set to `none`, then namespaces will be fetched, but still verified with above rules.
|
| + |
If set to `root`, then the fetched namespaces are protected from graft attacks.
|
| + |
Finally, if set to `parent`, then the fetched namespaces are protected from graft attacks, and replay attacks.
|
| + |
|
| + |
You may notice that setting this configuration value allows to trade-off security vs. backwards compatibility.
|
| + |
As more nodes upgrade to 1.8.0, users should update the minimum to `parent`, as soon as possible.
|
| + |
Once that is done, there is one last escape hatch for fetching and cloning.
|
| + |
The `rad sync --fetch` and `rad clone` commands now include a `--signed-refs-feature-level` option.
|
| + |
Its expected value is, once again, one of the feature levels,
|
| + |
and its behaviour is the same as the above configuration option.
|
| + |
|
| + |
Roughly speaking, by increasing the feature level to `root`, one can expect
|
| + |
backwards compatibility to 1.1.0, and by setting `parent`, one can expect
|
| + |
backwards compatibility to 1.7.0.
|
| + |
|
| + |
## Recommended Actions
|
| + |
|
| + |
1. Update to Radicle 1.8.0 as soon as possible.
|
| + |
2. Wait until other users, especially those that you collaborate with, have updated to Radicle 1.8.0.
|
| + |
Use `rad inspect --sigrefs` per repository to assess.
|
| + |
3. Edit your configuration with `node.fetch.signedReferences.featureLevel.minimum = "parent"` for improved security.
|
| + |
|
| + |
### Seed Node Operators
|
| + |
|
| + |
Because, to the best of our knowledge, there are no replay attacks happening on the network,
|
| + |
we think that no immediate action other than updating to Radicle 1.8.0 is necessary.
|
| + |
|
| + |
However, please familiarize yourself with the newly introduced concept of a "feature level" (see above).
|
| + |
Consider to set `node.fetch.signedReferences.featureLevel.minimum = "parent"` after a reasonable period of time to allow others to update.
|
| + |
The Radicle team will do this for seed.radicle.xyz within days, and for iris.radicle.xyz and rosa.radicle.xyz
|
| + |
within the next weeks.
|
| + |
If however, we notice an uptick in suspicious behavior on the network, we will considerably accelerate these timelines.
|
| + |
|
| + |
## The Future
|
| + |
|
| + |
### Monitoring the Network
|
| + |
|
| + |
We will use the scanner mentioned above to continuously detect potential attacks,
|
| + |
and extend it to also understand how many nodes have updated to Radicle 1.8.0.
|
| + |
|
| + |
### Automated Cross-Version Testing
|
| + |
|
| + |
One take away from the process of implementing and testing mitigations,
|
| + |
and their impact on compatibility, was that we will continue to improve our
|
| + |
automated cross-version testing capabilities.
|
| + |
This will help reveal issues before we release.
|
| + |
|
| + |
### Signed References
|
| + |
|
| + |
Signed References are a design that was conceived some time ago before Radicle reached 1.0.0.
|
| + |
We think that a similar solution, based on [signed pushes] and storing [push certificates] would be more sustainable.
|
| + |
The Linux kernel project, for example, provides transparency logs based on this format,
|
| + |
and it is not specific to Radicle, but defined and maintained by the Git project.
|
| + |
|
| + |
Our goal is to have future versions of Radicle to support both Signed References as well as push certificates,
|
| + |
to allow for a large time-window of cross-compatibility.
|
| + |
Of course, removing support for Signed References means a breaking change down the line.
|
| + |
|
| + |
---
|
| + |
|
| + |
Acknowledgements: We would like to thank maninak and rudolfs for their helpful comments on drafts of this disclosure.
|
| + |
|
| + |
[Defelo]: https://defelo.de/
|
| + |
[nonce]: https://csrc.nist.gov/glossary/term/nonce
|
| + |
[`git show-ref`]: https://git-scm.com/docs/git-show-ref
|
| + |
[Collaborative Objects]: https://radicle.xyz/guides/protocol#collaborative-objects
|
| + |
[signed pushes]: https://people.kernel.org/monsieuricon/signed-git-pushes
|
| + |
[push certificates]: https://git-scm.com/docs/pack-protocol#_push_certificate
|
| + |
[`rad:z3zzcqqBGP1NdvvMbSDt2jYYp9jSB`]: https://app.radicle.xyz/nodes/seed.radicle.xyz/rad:z3zzcqqBGP1NdvvMbSDt2jYYp9jSB
|
| |
\ No newline at end of file
|