Can an Uno be made into a Trojan horse device?

Imagine a scenario: I supply you with a blank Arduino Uno, perhaps just running the blink sketch to placate you. I have oodles of resources, but excluding the incorporation of a fake ATmega328P chip.

  1. Can I pre-program it (bootloader?) so that it will run certain of my instructions and not all of yours when you come to load your own sketches? For example, but not restricted to, overloading calls to analogRead()?

  2. What steps would be necessary for you to validate the Uno I sent you is kosher from a security perspective?

  3. What steps would be necessary for me to defeat step 2?

  4. From feelings in your water and general experience here, what %age of this forum's readership would be sufficiently technically proficient to complete step 2?


This is a thought exercise in conspiracy theories and true random number generation :smiling_imp: I do not work for the CIA. Or...

cossoft: 1. Can I pre-program it (bootloader?) so that it will run certain of my instructions and not all of yours when you come to load your own sketches? For example, but not restricted to, overloading calls to analogRead()?

Possibly. Your 'bootloader' would likely be larger than the official bootloader and some people will notice that.

cossoft: 2. What steps would be necessary for you to validate the Uno I sent you is kosher from a security perspective?

Burn a fresh bootloader.

cossoft: 3. What steps would be necessary for me to defeat step 2?

You could burn fuses to protect your chip from programming... but that would cause the "Burn a fresh bootloader." to fail in an obvious way. Your chip would likely be discarded.

cossoft: 4. From feelings in your water and general experience here, what %age of this forum's readership would be sufficiently technically proficient to complete step 2?

We get a lot of beginners. Maybe 0.1% of the total population and 5% of the active population.

cossoft: This is a thought exercise in conspiracy theories and true random number generation

If you are talking about "randomSeed(analogRead(A0))" then there is no need. There are only 1024 possibilities, easily brute-forced. If you try to replace analogRead() with a constant, people will wonder why they always get the same "random" numbers.

analogRead() etc. are resolved while source code is compiled by a user. The bootloader is not the BIOS known from other systems.

cossoft: Imagine a scenario: I supply you with a blank Arduino Uno, perhaps just running the blink sketch to placate you. I have oodles of resources, but excluding the incorporation of a fake ATmega328P chip.

  1. Can I pre-program it (bootloader?) so that it will run certain of my instructions and not all of yours when you come to load your own sketches? For example, but not restricted to, overloading calls to analogRead()?

Doubtful.

The bootloader is the code run the moment the Arduino is booted, waits for activity on the Rx (a new sketch is uploaded), and upon timeout tells the MCU to execute the first instruction of the uploaded code.

Unless the sketch calls code that's in the bootloader (as in: runs instructions stored in those specific addresses in the flash memory) that code will never run again. Mind: I'm now talking about machine instructions, not the C++ code. Normally your sketch will never call the code in the bootloader's part of the flash memory, i.e. the first 2 kB or so of it. I expect that somewhere under the hood the compiler is instructed to use the address of the first byte following the bootloader as lowest byte to be used in the compiled code, as that this is also the point from where the code is actually stored on the Arduino's flash memory.

The only way I think you could modify the behaviour of a regular analogRead() call or other standard Arduino functions is by changing that in the IDE so that code is inserted at compile time.

If your pre-installed bootloader is larger than the space the compiler reserves for it, this may cause your bootloader to stop working after the first sketch upload, as part of the bootloader is overwritten. Or if your large bootloader is set to store the new upload at an address that's different from where the compiler expects the code to go, that will result in the code not working at all and showing unpredictable behaviour, probably crashing real soon.

In case I'd receive a device that I have my doubts about, it'd indeed be a simple case of plugging in an ISP programmer and programming that way. With or without bootloader. Guaranteed no more other code present; and if fuses were set to prevent that from happening I'd see error messages, reset those fuses, or everything failing just bin the part and get a new one. Arduinos are almost cheap enough to be disposable.

DrDiettrich: analogRead() etc. are resolved while source code is compiled by a user. The bootloader is not the BIOS known from other systems.

I appreciate that. My thinking was analogRead() instructions would be detected at sketch upload and then overloaded/re linked at that point. Would that be possible since you'd effectively be recompiling and that's a big job? Clearly GCC v.10 couldn't be stuffed into the bootloader space, but could a much smaller dedicated codebase be? Just enough to overload analogRead()? Say reduce it's resolution to say 5 bits?

The thought experiment doesn't have to require reasonableness, just possible(ness).

It is hard to imagine why anyone would bother to turn an Uno into a Trojan Horse, given the limited number of hobbyist targets, and that millions of IoT devices are much more powerful, ubiquitous, connected to the internet and are either hacked already, or just waiting to be hacked.

cossoft: Would that be possible since you'd effectively be recompiling and that's a big job?

You certainly wouldn't be compiling or linking since all of that is done before the binary is sent to the Arduino. You would have to recognize the machine code and patch the binary. Why patch analogRead() when you can patch randomSeed()? I doubt anyone would notice if you zeroed out all but 5 bits of the value being passed.

cossoft: I appreciate that. My thinking was analogRead() instructions would be detected at sketch upload and then overloaded/re linked at that point. Would that be possible since you'd effectively be recompiling and that's a big job? Clearly GCC v.10 couldn't be stuffed into the bootloader space, but could a much smaller dedicated codebase be? Just enough to overload analogRead()? Say reduce it's resolution to say 5 bits?

The thought experiment doesn't have to require reasonableness, just possible(ness).

I only understand the bootloader in principle, not in it's implementation details, but it seems like it would be pretty straightforward to detect some instruction sequence in the flash load and modify it.

With analogRead() as the example, the binary instructions that read the ADC sample conversion registers is predictable and reasonably unique. The bootloader could replace some part of that instruction sequence with a call to a pre-coded subroutine in the bootloader instruction space to, for instance, read and negate the value of the ADC sample register and return that to the User code.

As in the first reply, loading a known good bootloader would protect against this particular attack.

It would be easier to patch a much bigger controller to emulate an ATmega328, so that all instructions and hardware access can be modified as desired, like in a virtual machine. But this would also require a much higher clock rate, or the slowness of the emulation, power consumption etc. would be detected quite easily. Also the different behaviour of a sketch on another board (Mini, Micro...) with a controller of a different case and pinout would ring alarm bells.

johnwasser: Why patch analogRead() when you can patch randomSeed()?

The thought experiment regards a cryptographic strength entropy source. randomSeed() isn't used. The objective is to determine whether it is security and marketing appropriate to supply it with an Uno, or have the end user source one and program it themselves.

From the responses (thanks all), I'm minded to think that it's too risky to supply an Uno. Whilst difficult, it does seem that analogRead() could be subverted given sufficient resources. The forces against strong encryption are too powerful and the risks too great. And perception is everything in security marketing.

What effective strategy could be pursued, assuming that such a patch could be made? What would it attempt to do? There are a million ways of using and connecting the device, how would the patch know how to effect any kind of predictable outcome? For example, suppose you decide to monitor the serial port. Now what? Send the results where? How?

:)

It's just nonsense to restrict malware to a board (Uno) that is not normally used in real projects. Modifications of the downloaded code are detected in the following Verify stage, where all differences can be found and explored.

MrMark:
As in the first reply, loading a known good bootloader would protect against this particular attack.

Or of course not using a bootloader at all… I’ve build quite a few projects based on ATtiny chips now, so that is routine. The Nano even comes with convenient ICSP headers (the Uno has this as well I think? That’s how much I use that board!) where the USBAsp plugs in making uploading as easy as when using a bootloader.

johnwasser: Burn a fresh bootloader.

Is the bootloader region readable from the application?

If "yes" then the bootloader can be verified using a normal sketch.

DrDiettrich: It's just nonsense to restrict malware to a board (Uno) that is not normally used in real projects. Modifications of the downloaded code are detected in the following Verify stage, where all differences can be found and explored.

Do you mean not including any like these: https://uk.rs-online.com/web/p/shields-for-arduino/8850917/ ?

cossoft: Do you mean not including any like these: https://uk.rs-online.com/web/p/shields-for-arduino/8850917/ ?

It is indeed based on an Arduino, but not an Uno... Why would you even want to keep that bootloader in an industrial application where you're not likely to change software much if ever? And then not after lots and lots of testing on other boards... preferably without unnecessary frills like a bootloader...