In murse v0.3.0, signing support was introduced. There is a bug in the
code for the verify subcommand that causes it to ignore the supplied
key, allowing an attacker to create directories and delete files.
For Open Fortress players, this does not affect you as of this post;
signing is not enabled on the servers (so you already have to trust the
mirror you use.) However, once/if signing is available, then
verification will be vulnerable to the issues.
To mitigate the issue, simply upgrade to v0.3.1. Downloads for v0.3.0
will be removed shortly.
This bug is caused by a simple typo. In murse, there is a Client object
that handles downloading and verification of revisions.
type Client struct {
key *ecdsa.PublicKey
repo string
h *http.Client
}
When the client goes to download the revision it will check if
Client.key is nil. If it isn't, then it uses the key to verify the revision.
// ...
if c.key != nil {
// ...
ok := verifyContents(c.key, object, sig)
if !ok {
return nil, errors.New("failed cryptographic check")
}
// ...
}
// ...
Creating an object is done via the NewClient() function, which will set
the Client.key value from the second argument.
func NewClient(repo string, key *ecdsa.PublicKey) *Client {
return &Client{
repo: repo,
h: &http.Client{},
key: key,
}
}
At the beginning of the verification function, we declare a variable
with a pointer to ecdsa.PublicKey. If the --verify-sigs flag was passed
we populate this with a key.
var key *ecdsa.PublicKey
if checkSigs {
b, err := io.ReadAll(os.Stdin)
if err != nil {
errPrintln(err)
return 1
}
key, err = readPublicKey(b)
if err != nil {
errPrintln(err)
return 1
}
// ...
}
The problem is that there was a typo, where the key argument was set to nil.
client := NewClient(url, nil) // Should be (url, key)
This sets Client.key to nil, which means revisions are downloaded
without checking their signatures. After this, the verification checks
the filesystem in the order of deletes, directories, and finally files.
Any paths marked as a new directory or deleted are overwritten. However,
the signature for objects are checked manually.
if checkSigs {
sig, err := client.GetObjectSig(change.Object)
if err != nil {
return errf(err)
}
err = treadSave(key, object, change.MD5, sig, path, change.Object)
if err != nil {
return errf(err)
}
// ...
}
Since key is nil, and treadSave key expects it to be non-nil, the
program will stop.