Is Swift vulnerable to Trojan Source attacks?

Came across this article regarding Trojan Sources (https://trojansource.codes/trojan-source.pdf). Curious if the Swift compiler (and Xcode) are vulnerable to this sort of attack?

4 Likes

Some quick checks in an Xcode Playground suggest that it is.

1 Like

You’re correct. There are a number of mitigations suggested in UAX#31 to address the related topic of homoglyph attacks which we’ve discussed in the past here but never made it through the Swift Evolution process all the way. To my mind this is another manifestation of the same underlying issue, although the solutions required to address it may be a little different.

1 Like

Yes. However, the forums here do not render it in the same way that Xcode does. Copying and pasting this into Xcode (or a plain TextEdit document) should show the problem:

func test() {
    let accessLevel = "user"
    if accessLevel != "user<U+202E><U+2066>// check if admin<U+2069><U+2066>" {
        print("You're an admin")
    }
}

test()
3 Likes

string spoofing is easy done by more traditional methods. below contains two different methods: a zero width spacer between "u" and "s" in the second string (which you can notice if you put the cursor at the beginning of "user" and advance it one by one with the arrow key). also cyrillic "е" is used instead of latin "e" in the second string (homoglyph).

func test() {
    let accessLevel = "user"
    if accessLevel != "u​sеr" {
        print("You're an admin")
    }
}

test()

the following can also be used for attacks in swift, doesn't use any special characters:

func bar() {
    print("hello, world")
}

func foo() {
    return
    // ignore the rest
    bar()
}

foo()

good thing it gives a warning: Warning: Expression following 'return' is treated as an argument of the 'return'

1 Like

Another one not in this article: é (U+00E9) and (U+0065 and U+0301) are two different symbols in Swift. But any tool that pass text through unicode normalization will change them and they'll become the same. Textual search & replace will also generally treat them the same even if they're encoded differently in the file and seen as different symbols by the compiler.

2 Likes

Rust already fixed it.
https://github.com/rust-lang/rust/pull/90462

1 Like

If you're using SwiftLint and want to lint for the characters listed in the paper:

custom_rules:
  trojan_source:
    regex: "[\u202A\u202B\u202D\u202E\u2066\u2067\u2068\u202C\u2069]"
    severity: error
    message: "Source should not contain characters that may be used in reordering attacks. See https://trojansource.codes/trojan-source.pdf"

I also opened an issue to request this be added to the set of built-in rules: Rule Request: trojan_source · Issue #3751 · realm/SwiftLint · GitHub

Feedback welcome if anyone spots problems with this approach.

3 Likes