All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
@ 2018-11-06 14:51 Andrea Barisani
  2018-11-09  0:37 ` Fabio Estevam
  2018-11-13 20:57 ` Simon Goldschmidt
  0 siblings, 2 replies; 23+ messages in thread
From: Andrea Barisani @ 2018-11-06 14:51 UTC (permalink / raw)
  To: u-boot


Hello everyone, the following advisory has been posted last week to
oss-security, per discussion with Tom Rini, whom helped us in the
pre-notification phase, I also post it here for discussion.

Cheers

-------------------------------------------------------------------------------

Security advisory: U-Boot verified boot bypass
==============================================

The Universal Boot Loader - U-Boot [1] verified boot feature allows
cryptographic authentication of signed kernel images, before their execution.

This feature is essential in maintaining a full chain of trust on systems which
are secure booted by means of an hardware anchor.

Multiple techniques have been identified that allow to execute arbitrary code,
within a running U-Boot instance, by means of externally provided
unauthenticated data.

All such techniques spawn from the lack of memory allocation protection within
the U-Boot architecture, which results in several means of providing
excessively large images during the boot process.

Some implementers might find the following issues as an intrinsic
characteristic of the U-Boot memory model, and consequently a mere aspect of
correct U-Boot configuration and command restrictions.

However in our opinion the inability of U-Boot to protect itself when loading
binaries is an unexpected result of non trivial understanding, particularly
important to emphasize in trusted boot scenarios.

This advisory details two specific techniques that allow to exploit U-Boot lack
of memory allocation restrictions, with the most severe case also detailing a
workaround to mitigate the issue.

It must be emphasized that cases detailed in the next sections only represent
two possible occurrences of such architectural limitation, other U-Boot image
loading functions are extremely likely to suffer from the same validation
issues.

To a certain extent the identified issues are similar to one of the findings
reported as CVE-2018-1000205 [2], however they concern different functions
which in some cases are at a lower level, therefore earlier in the boot image
loading stage.

Again all such issues are a symptom of the same core architectural limitation,
being the lack of memory allocation constraints for received images.

It is highly recommended, for implementers of trusted boot schemes, to review
use of all U-Boot booting/loading commands, and not merely the two specific
ones involved in the findings below, to apply limitations (where
applicable/possible) to the size of loaded images in relation to the available
RAM.

It should also be emphasized that any trusted boot scheme must also rely on an
appropriate lockdown of all possibilities for interactive consoles, by boot
process interruption or failure, to ever be prompted.


U-Boot insufficient boundary checks in filesystem image load
------------------------------------------------------------

The U-Boot bootloader supports kernel loading from a variety of filesystem
formats, through the `load` command or its filesystem specific equivalents
(e.g. `ext2load`, `ext4load`, `fatload`, etc.)

These commands do not protect system memory from being overwritten when loading
files of a length that exceeds the boundaries of the relocated U-Boot memory
region, filled with the loaded file starting from the passed `addr` variable.

Therefore an excessively large boot image, saved on the filesystem, can be
crafted to overwrite all U-Boot static and runtime memory segments, and in
general all device addressable memory starting from the `addr` load address
argument.

The memory overwrite can directly lead to arbitrary code execution, fully
controlled by the contents of the loaded image.

When verified boot is implemented, the issue allows to bypass its intended
validation as the memory overwrite happens before any validation can take
place.

The following example illustrates the issue, triggered with a 129MB file on a
machine with 128MB or RAM:

```
U-Boot 2018.09-rc1 (Oct 10 2018 - 10:52:54 +0200)

DRAM:  128 MiB
Flash: 128 MiB
MMC:   MMC: 0

# print memory information
=> bdinfo
arch_number = 0x000008E0
boot_params = 0x60002000
DRAM bank   = 0x00000000
-> start    = 0x60000000
-> size     = 0x08000000
DRAM bank   = 0x00000001
-> start    = 0x80000000
-> size     = 0x00000004
eth0name    = smc911x-0
ethaddr     = 52:54:00:12:34:56
current eth = smc911x-0
ip_addr     = <NULL>
baudrate    = 38400 bps
TLB addr    = 0x67FF0000
relocaddr   = 0x67F96000
reloc off   = 0x07796000
irq_sp      = 0x67EF5EE0
sp start    = 0x67EF5ED0

# load large file
=> ext2load mmc 0 0x60000000 fitimage.itb

# In this specific example U-Boot falls in an infinite loop, results vary
# depending on the test case and filesystem/device driver used. A debugging
# session demonstrates memory being overwritten:
(gdb) p gd
$28 = (volatile gd_t *) 0x67ef5ef8
(gdb) p *gd
$27 = {bd = 0x7f7f7f7f, flags = 2139062143, baudrate = 2139062143, ... }
(gdb) x/300x 0x67ef5ef8
0x67ef5ef8:	0x7f7f7f7f	0x7f7f7f7f	0x7f7f7f7f	0x7f7f7f7f
```

It can be seen that memory address belonging to U-Boot data segments, in this
specific case the global data structure `gd`, is overwritten with payload
originating from `fitimage.itb` (filled with `0x7f7f7f7f`).

### Impact

Arbitrary code execution can be achieved within a U-Boot instance by means of
unauthenticated binary images, loaded through the `load` command or its
filesystem specific equivalents.

It should be emphasized that all load commands are likely to be affected by the
same underlying root cause of this vulnerability.

### Workaround

The optional `bytes` argument can be passed to all load commands to restrict
the maximum size of the retrieved data.

The issue can be therefore mitigated by passing a `bytes` argument with a value
consistent with the U-Boot memory regions mapping and size.


U-Boot insufficient boundary checks in network image boot
---------------------------------------------------------

The U-Boot bootloader supports kernel loading from a variety of network
sources, such as TFTP via the `tftpboot` command.

This command does not protect system memory from being overwritten when loading
files of a length that exceeds the boundaries of the relocated U-Boot memory
region, filled with the loaded file starting from the passed `loadAddr`
variable.

Therefore an excessively large boot image, served over TFTP, can be crafted to
overwrite all U-Boot static and runtime memory segments, and in general all
device addressable memory starting from the `loadAddr` load address argument.

The memory overwrite can directly lead to arbitrary code execution, fully
controlled by the contents of the loaded image.

When verified boot is implemented, the issue allows to bypass its intended
validation as the memory overwrite happens before any validation can take
place.

The issue can be exploited by several means:

  - An excessively large crafted boot image file is parsed by the
    `tftp_handler` function which lacks any size checks, allowing the memory
    overwrite.

  - A malicious server can manipulate TFTP packet sequence numbers to store
    downloaded file chunks at arbitrary memory locations, given that the
    sequence number is directly used by the `tftp_handler` function to calculate
    the destination address for downloaded file chunks.

    Additionally the `store_block` function, used to store downloaded file
    chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
    value of 0, triggers an unchecked integer underflow.

    This allows to potentially erase memory located before the `loadAddr` when
    a packet is sent with a null, following at least one valid packet.

The following example illustrates the issue, triggered with a 129MB file on a
machine with 128MB or RAM:

```
U-Boot 2018.09-rc1 (Oct 10 2018 - 10:52:54 +0200)

DRAM:  128 MiB
Flash: 128 MiB
MMC:   MMC: 0

# print memory information
=> bdinfo
arch_number = 0x000008E0
boot_params = 0x60002000
DRAM bank   = 0x00000000
-> start    = 0x60000000
-> size     = 0x08000000
DRAM bank   = 0x00000001
-> start    = 0x80000000
-> size     = 0x00000004
eth0name    = smc911x-0
ethaddr     = 52:54:00:12:34:56
current eth = smc911x-0
ip_addr     = <NULL>
baudrate    = 38400 bps
TLB addr    = 0x67FF0000
relocaddr   = 0x67F96000
reloc off   = 0x07796000
irq_sp      = 0x67EF5EE0
sp start    = 0x67EF5ED0

# configure environment
=> setenv loadaddr 0x60000000
=> dhcp
smc911x: MAC 52:54:00:12:34:56
smc911x: detected LAN9118 controller
smc911x: phy initialized
smc911x: MAC 52:54:00:12:34:56
BOOTP broadcast 1
DHCP client bound to address 10.0.0.20 (1022 ms)
Using smc911x-0 device
TFTP from server 10.0.0.1; our IP address is 10.0.0.20
Filename 'fitimage.bin'.
Load address: 0x60000000
Loading: #################################################################
...
         ####################################

R00=7f7f7f7f R01=67fedf6e R02=00000000 R03=7f7f7f7f
R04=7f7f7f7f R05=7f7f7f7f R06=7f7f7f7f R07=7f7f7f7f
R08=7f7f7f7f R09=7f7f7f7f R10=0000d677 R11=67fef670
R12=00000000 R13=67ef5cd0 R14=02427f7f R15=7f7f7f7e
PSR=400001f3 -Z-- T S svc32
```

It can be seen that the program counter (PC, r15) is set to an address
originating from `fitimage.itb` (filled with `0x7f7f7f7f`), as the result of
the U-Boot memory overwrite.

### Impact

Arbitrary code execution can be achieved within a U-Boot instance by means of
unauthenticated binary images, passed through TFTP and loaded through the
`tftpboot` command, or by a malicious TFTP server capable of sending arbitrary
response packets.

It should be emphasized that all network boot commands are likely to be
affected by the same underlying root cause of this vulnerability.

### Workaround

The `tftpboot` command lacks any optional argument to restrict the maximum size
of downloaded images, therefore the only workaround at this time is to avoid
using this command on environments that require trusted boot.


Affected version
----------------

All released U-Boot versions, at the time of this advisory release, are
believed to be vulnerable.

All tests have been performed against U-Boot version 2018.09-rc1.


Credit
------

Vulnerabilities discovered and reported by the Inverse Path team at F-Secure,
in collaboration with Quarkslab.


CVE
---

CVE-2018-18440: U-Boot insufficient boundary checks in filesystem image load
CVE-2018-18439: U-Boot insufficient boundary checks in network image boot


Timeline
--------

2018-10-05: network boot finding identified during internal security audit
            by Inverse Path team at F-Secure in collaboration with Quarkslab.

2018-10-10: filesystem load finding identified during internal security audit
            by Inverse Path team at F-Secure.

2018-10-12: vulnerability reported by Inverse Path team at F-Secure to U-Boot
            core maintainer and Google security, embargo set to 2018-11-02.

2018-10-16: Google closes ticket reporting that ChromeOS is not affected due
            to their specific environment customizations.

2018-10-17: CVE IDs requested to MITRE and assigned.

2018-11-02: advisory release.


References
----------

[1] https://www.denx.de/wiki/U-Boot
[2] https://lists.denx.de/pipermail/u-boot/2018-June/330487.html


Permalink
---------

https://github.com/inversepath/usbarmory/blob/master/software/secure_boot/Security_Advisory-Ref_IPVR2018-0001.txt

-- 
Andrea Barisani     Head of Hardware Security |     F-Secure
                                      Founder | Inverse Path

https://www.f-secure.com             https://inversepath.com
0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
       "Pluralitas non est ponenda sine necessitate"

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-06 14:51 [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities Andrea Barisani
@ 2018-11-09  0:37 ` Fabio Estevam
  2018-11-09  6:11   ` Simon Goldschmidt
  2018-11-13 20:57 ` Simon Goldschmidt
  1 sibling, 1 reply; 23+ messages in thread
From: Fabio Estevam @ 2018-11-09  0:37 UTC (permalink / raw)
  To: u-boot

Hi Andrea,

On Tue, Nov 6, 2018 at 12:57 PM Andrea Barisani
<andrea.barisani@f-secure.com> wrote:

> # load large file
> => ext2load mmc 0 0x60000000 fitimage.itb

Does this change work for you?
http://dark-code.bulix.org/u6gw3b-499924

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-09  0:37 ` Fabio Estevam
@ 2018-11-09  6:11   ` Simon Goldschmidt
  2018-11-09  9:46     ` Andrea Barisani
  0 siblings, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-09  6:11 UTC (permalink / raw)
  To: u-boot

On Fri, Nov 9, 2018 at 1:37 AM Fabio Estevam <festevam@gmail.com> wrote:
>
> Hi Andrea,
>
> On Tue, Nov 6, 2018 at 12:57 PM Andrea Barisani
> <andrea.barisani@f-secure.com> wrote:
>
> > # load large file
> > => ext2load mmc 0 0x60000000 fitimage.itb
>
> Does this change work for you?
> http://dark-code.bulix.org/u6gw3b-499924

My understanding was U-Boot text or stack could get overwritten which
leads to the loaded bytes being executed as code.
So you would have to check that the loaded range is within ram but not
within that reserved range of code or stack (or heap).

Getting this reserved range is what 'boot_start_lmb' does (in
bootm.c). Maybe this code can be refactored and reused in fs.c to get
a valid range for loading?

Additionally, your patch checks the loaded file's size without taking
the load address into account. So unless I read that wrong, your check
is only valid for 'addr == 0'.
Plus, the 'bytes' parameter should probably be a restriction to the
file's size when checking for a valid load range.

Simon

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-09  6:11   ` Simon Goldschmidt
@ 2018-11-09  9:46     ` Andrea Barisani
  2018-11-09 10:24       ` Simon Goldschmidt
  2018-11-11 14:22       ` Wolfgang Denk
  0 siblings, 2 replies; 23+ messages in thread
From: Andrea Barisani @ 2018-11-09  9:46 UTC (permalink / raw)
  To: u-boot

On Fri, Nov 09, 2018 at 07:11:36AM +0100, Simon Goldschmidt wrote:
> On Fri, Nov 9, 2018 at 1:37 AM Fabio Estevam <festevam@gmail.com> wrote:
> >
> > Hi Andrea,
> >
> > On Tue, Nov 6, 2018 at 12:57 PM Andrea Barisani
> > <andrea.barisani@f-secure.com> wrote:
> >
> > > # load large file
> > > => ext2load mmc 0 0x60000000 fitimage.itb
> >
> > Does this change work for you?
> > http://dark-code.bulix.org/u6gw3b-499924
> 
> My understanding was U-Boot text or stack could get overwritten which
> leads to the loaded bytes being executed as code.
> So you would have to check that the loaded range is within ram but not
> within that reserved range of code or stack (or heap).
> 

Exactly, merely checking RAM size is not sufficient. The specific memory
layout would need to be accounted for which means understanding where the
stack and heap are located, their direction of growth and to ensure that the
loaded payload can never overwrite them along with all other U-Boot data
segments.

This is not easy given that the stack and heap size I think can only be
guessed and not precisely limited, additionally board configurations have the
ability to set arbitrary stack, relocation and load addresses which
complicates things even further in understanding exactly how the memory
layout is set.

> Getting this reserved range is what 'boot_start_lmb' does (in
> bootm.c). Maybe this code can be refactored and reused in fs.c to get
> a valid range for loading?
> 
> Additionally, your patch checks the loaded file's size without taking
> the load address into account. So unless I read that wrong, your check
> is only valid for 'addr == 0'.
> Plus, the 'bytes' parameter should probably be a restriction to the
> file's size when checking for a valid load range.
> 
> Simon

-- 
Andrea Barisani     Head of Hardware Security |     F-Secure
                                      Founder | Inverse Path

https://www.f-secure.com             https://inversepath.com
0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
       "Pluralitas non est ponenda sine necessitate"

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-09  9:46     ` Andrea Barisani
@ 2018-11-09 10:24       ` Simon Goldschmidt
  2018-11-09 21:25         ` Simon Goldschmidt
  2018-11-11 14:22       ` Wolfgang Denk
  1 sibling, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-09 10:24 UTC (permalink / raw)
  To: u-boot

On Fri, Nov 9, 2018 at 10:46 AM Andrea Barisani
<andrea.barisani@f-secure.com> wrote:
>
> On Fri, Nov 09, 2018 at 07:11:36AM +0100, Simon Goldschmidt wrote:
> > On Fri, Nov 9, 2018 at 1:37 AM Fabio Estevam <festevam@gmail.com> wrote:
> > >
> > > Hi Andrea,
> > >
> > > On Tue, Nov 6, 2018 at 12:57 PM Andrea Barisani
> > > <andrea.barisani@f-secure.com> wrote:
> > >
> > > > # load large file
> > > > => ext2load mmc 0 0x60000000 fitimage.itb
> > >
> > > Does this change work for you?
> > > http://dark-code.bulix.org/u6gw3b-499924
> >
> > My understanding was U-Boot text or stack could get overwritten which
> > leads to the loaded bytes being executed as code.
> > So you would have to check that the loaded range is within ram but not
> > within that reserved range of code or stack (or heap).
> >
>
> Exactly, merely checking RAM size is not sufficient. The specific memory
> layout would need to be accounted for which means understanding where the
> stack and heap are located, their direction of growth and to ensure that the
> loaded payload can never overwrite them along with all other U-Boot data
> segments.
>
> This is not easy given that the stack and heap size I think can only be
> guessed and not precisely limited, additionally board configurations have the
> ability to set arbitrary stack, relocation and load addresses which
> complicates things even further in understanding exactly how the memory
> layout is set.

It's not easy, but in my opinion, it should already be solved by the
code in 'boot_start_lmb' mentioned in my last mail.
This function includes arch and board callbacks that should be able to
return a safe memory range.

The only thing that cannot be controlled here is stack size, that's
true. The ARM port tries to solve this by getting the current stack
pointer and subtracting "4K to be safe". As far as I know, there are
no methods in U-Boot currently to ensure this is safe, though. And
depending on the RAM size, we could just subtract more. Personally, I
wouldn't mind subtracting some MBytes on my board. Actually using such
a stack would definively be another bug that needs fixing.

But it seems a good start to use these functions to limit loading from fs, too.

Simon

>
> > Getting this reserved range is what 'boot_start_lmb' does (in
> > bootm.c). Maybe this code can be refactored and reused in fs.c to get
> > a valid range for loading?
> >
> > Additionally, your patch checks the loaded file's size without taking
> > the load address into account. So unless I read that wrong, your check
> > is only valid for 'addr == 0'.
> > Plus, the 'bytes' parameter should probably be a restriction to the
> > file's size when checking for a valid load range.
> >
> > Simon
>
> --
> Andrea Barisani     Head of Hardware Security |     F-Secure
>                                       Founder | Inverse Path
>
> https://www.f-secure.com             https://inversepath.com
> 0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
>        "Pluralitas non est ponenda sine necessitate"

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-09 10:24       ` Simon Goldschmidt
@ 2018-11-09 21:25         ` Simon Goldschmidt
  2018-11-09 22:14           ` Fabio Estevam
  0 siblings, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-09 21:25 UTC (permalink / raw)
  To: u-boot

On 09.11.2018 11:24, Simon Goldschmidt wrote:
> On Fri, Nov 9, 2018 at 10:46 AM Andrea Barisani
> <andrea.barisani@f-secure.com> wrote:
>> On Fri, Nov 09, 2018 at 07:11:36AM +0100, Simon Goldschmidt wrote:
>>> On Fri, Nov 9, 2018 at 1:37 AM Fabio Estevam <festevam@gmail.com> wrote:
>>>> Hi Andrea,
>>>>
>>>> On Tue, Nov 6, 2018 at 12:57 PM Andrea Barisani
>>>> <andrea.barisani@f-secure.com> wrote:
>>>>
>>>>> # load large file
>>>>> => ext2load mmc 0 0x60000000 fitimage.itb
>>>> Does this change work for you?
>>>> http://dark-code.bulix.org/u6gw3b-499924
>>> My understanding was U-Boot text or stack could get overwritten which
>>> leads to the loaded bytes being executed as code.
>>> So you would have to check that the loaded range is within ram but not
>>> within that reserved range of code or stack (or heap).
>>>
>> Exactly, merely checking RAM size is not sufficient. The specific memory
>> layout would need to be accounted for which means understanding where the
>> stack and heap are located, their direction of growth and to ensure that the
>> loaded payload can never overwrite them along with all other U-Boot data
>> segments.
>>
>> This is not easy given that the stack and heap size I think can only be
>> guessed and not precisely limited, additionally board configurations have the
>> ability to set arbitrary stack, relocation and load addresses which
>> complicates things even further in understanding exactly how the memory
>> layout is set.
> It's not easy, but in my opinion, it should already be solved by the
> code in 'boot_start_lmb' mentioned in my last mail.
> This function includes arch and board callbacks that should be able to
> return a safe memory range.

I have a patched version running here with the above mentioned changes 
that should fix this issue. A few cleanups are missing before sending a 
patch though (changes are in fs.c and lmb.c only).

Simon

>
> The only thing that cannot be controlled here is stack size, that's
> true. The ARM port tries to solve this by getting the current stack
> pointer and subtracting "4K to be safe". As far as I know, there are
> no methods in U-Boot currently to ensure this is safe, though. And
> depending on the RAM size, we could just subtract more. Personally, I
> wouldn't mind subtracting some MBytes on my board. Actually using such
> a stack would definively be another bug that needs fixing.
>
> But it seems a good start to use these functions to limit loading from fs, too.
>
> Simon
>
>>> Getting this reserved range is what 'boot_start_lmb' does (in
>>> bootm.c). Maybe this code can be refactored and reused in fs.c to get
>>> a valid range for loading?
>>>
>>> Additionally, your patch checks the loaded file's size without taking
>>> the load address into account. So unless I read that wrong, your check
>>> is only valid for 'addr == 0'.
>>> Plus, the 'bytes' parameter should probably be a restriction to the
>>> file's size when checking for a valid load range.
>>>
>>> Simon
>> --
>> Andrea Barisani     Head of Hardware Security |     F-Secure
>>                                        Founder | Inverse Path
>>
>> https://www.f-secure.com             https://inversepath.com
>> 0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
>>         "Pluralitas non est ponenda sine necessitate"

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-09 21:25         ` Simon Goldschmidt
@ 2018-11-09 22:14           ` Fabio Estevam
  0 siblings, 0 replies; 23+ messages in thread
From: Fabio Estevam @ 2018-11-09 22:14 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On Fri, Nov 9, 2018 at 7:25 PM Simon Goldschmidt
<simon.k.r.goldschmidt@gmail.com> wrote:

> I have a patched version running here with the above mentioned changes
> that should fix this issue. A few cleanups are missing before sending a
> patch though (changes are in fs.c and lmb.c only).

That's good news, thanks.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-09  9:46     ` Andrea Barisani
  2018-11-09 10:24       ` Simon Goldschmidt
@ 2018-11-11 14:22       ` Wolfgang Denk
  2018-11-11 23:21         ` Heinrich Schuchardt
  1 sibling, 1 reply; 23+ messages in thread
From: Wolfgang Denk @ 2018-11-11 14:22 UTC (permalink / raw)
  To: u-boot

Dear Andrea,

In message <20181109094615.GC9586@lambda.inversepath.com> you wrote:
>
> Exactly, merely checking RAM size is not sufficient. The specific memory
> layout would need to be accounted for which means understanding where the
> stack and heap are located, their direction of growth and to ensure that the
> loaded payload can never overwrite them along with all other U-Boot data
> segments.

This is pretty easy.  On all architectures I'm aware of the stack
has the lowest location in memory, and is growing downward.

> This is not easy given that the stack and heap size I think can only be
> guessed and not precisely limited, additionally board configurations have the
> ability to set arbitrary stack, relocation and load addresses which
> complicates things even further in understanding exactly how the memory
> layout is set.

I think this is not that complicated.  At least in standard U-Boot
(not speaking for SPL) it should be sufficient to check the current
stack pointer (which is easy to read) and take this a upper limit of
available/allowed memory. If we add some reasonable safety margin
(say, 1 MB or so) we should be really safe.

> > Additionally, your patch checks the loaded file's size without taking
> > the load address into account. So unless I read that wrong, your check
> > is only valid for 'addr == 0'.

The approach is also not appliccable to networ boot; with TFTP we
don't know the image size in advance.

Eventyally the boundary checking should be done where the image
content actually gets copied to memory.

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
I think it's a new feature. Don't tell anyone it was an accident. :-)
  -- Larry Wall on s/foo/bar/eieio in <10911@jpl-devvax.JPL.NASA.GOV>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-11 14:22       ` Wolfgang Denk
@ 2018-11-11 23:21         ` Heinrich Schuchardt
  2018-11-12  6:56           ` Simon Goldschmidt
  2018-11-12  8:00           ` Wolfgang Denk
  0 siblings, 2 replies; 23+ messages in thread
From: Heinrich Schuchardt @ 2018-11-11 23:21 UTC (permalink / raw)
  To: u-boot

On 11/11/18 3:22 PM, Wolfgang Denk wrote:
> Dear Andrea,
> 
> In message <20181109094615.GC9586@lambda.inversepath.com> you wrote:
>>
>> Exactly, merely checking RAM size is not sufficient. The specific memory
>> layout would need to be accounted for which means understanding where the
>> stack and heap are located, their direction of growth and to ensure that the
>> loaded payload can never overwrite them along with all other U-Boot data
>> segments.
> 
> This is pretty easy.  On all architectures I'm aware of the stack
> has the lowest location in memory, and is growing downward.
> 
>> This is not easy given that the stack and heap size I think can only be
>> guessed and not precisely limited, additionally board configurations have the
>> ability to set arbitrary stack, relocation and load addresses which
>> complicates things even further in understanding exactly how the memory
>> layout is set.
> 
> I think this is not that complicated.  At least in standard U-Boot
> (not speaking for SPL) it should be sufficient to check the current
> stack pointer (which is easy to read) and take this a upper limit of
> available/allowed memory. If we add some reasonable safety margin
> (say, 1 MB or so) we should be really safe.

Unfortunately this does not hold true. E.g. the Odroid-C2 has the secure
monitor in the middle of the RAM. You would not want to overwrite those
addresses.

For a board with a device tree all reserved memory areas should be
secured against overwriting.

Best regards

Heinrich

> 
>>> Additionally, your patch checks the loaded file's size without taking
>>> the load address into account. So unless I read that wrong, your check
>>> is only valid for 'addr == 0'.
> 
> The approach is also not appliccable to networ boot; with TFTP we
> don't know the image size in advance.
> 
> Eventyally the boundary checking should be done where the image
> content actually gets copied to memory.
> 
> Best regards,
> 
> Wolfgang Denk
> 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-11 23:21         ` Heinrich Schuchardt
@ 2018-11-12  6:56           ` Simon Goldschmidt
  2018-11-12 18:03             ` Heinrich Schuchardt
  2018-11-12  8:00           ` Wolfgang Denk
  1 sibling, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-12  6:56 UTC (permalink / raw)
  To: u-boot

On Mon, Nov 12, 2018 at 12:22 AM Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 11/11/18 3:22 PM, Wolfgang Denk wrote:
> > Dear Andrea,
> >
> > In message <20181109094615.GC9586@lambda.inversepath.com> you wrote:
> >>
> >> Exactly, merely checking RAM size is not sufficient. The specific memory
> >> layout would need to be accounted for which means understanding where the
> >> stack and heap are located, their direction of growth and to ensure that the
> >> loaded payload can never overwrite them along with all other U-Boot data
> >> segments.
> >
> > This is pretty easy.  On all architectures I'm aware of the stack
> > has the lowest location in memory, and is growing downward.
> >
> >> This is not easy given that the stack and heap size I think can only be
> >> guessed and not precisely limited, additionally board configurations have the
> >> ability to set arbitrary stack, relocation and load addresses which
> >> complicates things even further in understanding exactly how the memory
> >> layout is set.
> >
> > I think this is not that complicated.  At least in standard U-Boot
> > (not speaking for SPL) it should be sufficient to check the current
> > stack pointer (which is easy to read) and take this a upper limit of
> > available/allowed memory. If we add some reasonable safety margin
> > (say, 1 MB or so) we should be really safe.
>
> Unfortunately this does not hold true. E.g. the Odroid-C2 has the secure
> monitor in the middle of the RAM. You would not want to overwrite those
> addresses.
>
> For a board with a device tree all reserved memory areas should be
> secured against overwriting.

That's why I proposed to use the already existing memory reservation
scheme 'lmb' (used in loading boot images).

In your case, 'board_lmb_reserve' should make sure the secure monitor
does not get overwritten.
The 'arch_lmb_reserve' function for arm already ensures U-Boot text,
heap and stack don't get overwritten. It could be improved to reserve
+1M to the current stack pointer where it does reserve +4K now.

I am working on a patch for the 'load' issue (which could be reused
for the tftp issue). There are some problems with the existing lmb
code though, which delayed me a bit. However, given that this doesn't
make it into the 2018.11 release, anyway, I figured some more days to
get it cleaner won't hurt...

Simon

>
> Best regards
>
> Heinrich
>
> >
> >>> Additionally, your patch checks the loaded file's size without taking
> >>> the load address into account. So unless I read that wrong, your check
> >>> is only valid for 'addr == 0'.
> >
> > The approach is also not appliccable to networ boot; with TFTP we
> > don't know the image size in advance.
> >
> > Eventyally the boundary checking should be done where the image
> > content actually gets copied to memory.
> >
> > Best regards,
> >
> > Wolfgang Denk
> >
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-11 23:21         ` Heinrich Schuchardt
  2018-11-12  6:56           ` Simon Goldschmidt
@ 2018-11-12  8:00           ` Wolfgang Denk
  1 sibling, 0 replies; 23+ messages in thread
From: Wolfgang Denk @ 2018-11-12  8:00 UTC (permalink / raw)
  To: u-boot

Dear Heinrich,

In message <450f8b6e-b2c0-0a5f-14e0-50c58103aec5@gmx.de> you wrote:
>
> > I think this is not that complicated.  At least in standard U-Boot
> > (not speaking for SPL) it should be sufficient to check the current
> > stack pointer (which is easy to read) and take this a upper limit of
> > available/allowed memory. If we add some reasonable safety margin
> > (say, 1 MB or so) we should be really safe.
> 
> Unfortunately this does not hold true. E.g. the Odroid-C2 has the secure
> monitor in the middle of the RAM. You would not want to overwrite those
> addresses.

Urgh... Is there a (technical, say hardware) reason for such a
unlucky design?  Who would willingly fragment memory like that?

> For a board with a device tree all reserved memory areas should be
> secured against overwriting.

True.

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
The first 90% of a project takes 90% of the time, the last 10%  takes
the other 90% of the time.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-12  6:56           ` Simon Goldschmidt
@ 2018-11-12 18:03             ` Heinrich Schuchardt
  2018-11-12 18:58               ` Simon Goldschmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Heinrich Schuchardt @ 2018-11-12 18:03 UTC (permalink / raw)
  To: u-boot

On 11/12/18 7:56 AM, Simon Goldschmidt wrote:
> On Mon, Nov 12, 2018 at 12:22 AM Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>>
>> On 11/11/18 3:22 PM, Wolfgang Denk wrote:
>>> Dear Andrea,
>>>
>>> In message <20181109094615.GC9586@lambda.inversepath.com> you wrote:
>>>>
>>>> Exactly, merely checking RAM size is not sufficient. The specific memory
>>>> layout would need to be accounted for which means understanding where the
>>>> stack and heap are located, their direction of growth and to ensure that the
>>>> loaded payload can never overwrite them along with all other U-Boot data
>>>> segments.
>>>
>>> This is pretty easy.  On all architectures I'm aware of the stack
>>> has the lowest location in memory, and is growing downward.
>>>
>>>> This is not easy given that the stack and heap size I think can only be
>>>> guessed and not precisely limited, additionally board configurations have the
>>>> ability to set arbitrary stack, relocation and load addresses which
>>>> complicates things even further in understanding exactly how the memory
>>>> layout is set.
>>>
>>> I think this is not that complicated.  At least in standard U-Boot
>>> (not speaking for SPL) it should be sufficient to check the current
>>> stack pointer (which is easy to read) and take this a upper limit of
>>> available/allowed memory. If we add some reasonable safety margin
>>> (say, 1 MB or so) we should be really safe.
>>
>> Unfortunately this does not hold true. E.g. the Odroid-C2 has the secure
>> monitor in the middle of the RAM. You would not want to overwrite those
>> addresses.
>>
>> For a board with a device tree all reserved memory areas should be
>> secured against overwriting.
> 
> That's why I proposed to use the already existing memory reservation
> scheme 'lmb' (used in loading boot images).
> 
> In your case, 'board_lmb_reserve' should make sure the secure monitor
> does not get overwritten.
> The 'arch_lmb_reserve' function for arm already ensures U-Boot text,
> heap and stack don't get overwritten. It could be improved to reserve
> +1M to the current stack pointer where it does reserve +4K now.

If board_lmb_reserve() should be the solution I would prefer that not
each individual board calls board_lmb_reserve() but that some common
code is used to iterate over the memory reservations in the device tree.

Cheers

Heinrich

> 
> I am working on a patch for the 'load' issue (which could be reused
> for the tftp issue). There are some problems with the existing lmb
> code though, which delayed me a bit. However, given that this doesn't
> make it into the 2018.11 release, anyway, I figured some more days to
> get it cleaner won't hurt...
> 
> Simon
> 
>>
>> Best regards
>>
>> Heinrich
>>
>>>
>>>>> Additionally, your patch checks the loaded file's size without taking
>>>>> the load address into account. So unless I read that wrong, your check
>>>>> is only valid for 'addr == 0'.
>>>
>>> The approach is also not appliccable to networ boot; with TFTP we
>>> don't know the image size in advance.
>>>
>>> Eventyally the boundary checking should be done where the image
>>> content actually gets copied to memory.
>>>
>>> Best regards,
>>>
>>> Wolfgang Denk
>>>
>>
>> _______________________________________________
>> U-Boot mailing list
>> U-Boot at lists.denx.de
>> https://lists.denx.de/listinfo/u-boot
> 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-12 18:03             ` Heinrich Schuchardt
@ 2018-11-12 18:58               ` Simon Goldschmidt
  0 siblings, 0 replies; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-12 18:58 UTC (permalink / raw)
  To: u-boot

On 12.11.2018 19:03, Heinrich Schuchardt wrote:
> On 11/12/18 7:56 AM, Simon Goldschmidt wrote:
>> On Mon, Nov 12, 2018 at 12:22 AM Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>>> On 11/11/18 3:22 PM, Wolfgang Denk wrote:
>>>> Dear Andrea,
>>>>
>>>> In message <20181109094615.GC9586@lambda.inversepath.com> you wrote:
>>>>> Exactly, merely checking RAM size is not sufficient. The specific memory
>>>>> layout would need to be accounted for which means understanding where the
>>>>> stack and heap are located, their direction of growth and to ensure that the
>>>>> loaded payload can never overwrite them along with all other U-Boot data
>>>>> segments.
>>>> This is pretty easy.  On all architectures I'm aware of the stack
>>>> has the lowest location in memory, and is growing downward.
>>>>
>>>>> This is not easy given that the stack and heap size I think can only be
>>>>> guessed and not precisely limited, additionally board configurations have the
>>>>> ability to set arbitrary stack, relocation and load addresses which
>>>>> complicates things even further in understanding exactly how the memory
>>>>> layout is set.
>>>> I think this is not that complicated.  At least in standard U-Boot
>>>> (not speaking for SPL) it should be sufficient to check the current
>>>> stack pointer (which is easy to read) and take this a upper limit of
>>>> available/allowed memory. If we add some reasonable safety margin
>>>> (say, 1 MB or so) we should be really safe.
>>> Unfortunately this does not hold true. E.g. the Odroid-C2 has the secure
>>> monitor in the middle of the RAM. You would not want to overwrite those
>>> addresses.
>>>
>>> For a board with a device tree all reserved memory areas should be
>>> secured against overwriting.
>> That's why I proposed to use the already existing memory reservation
>> scheme 'lmb' (used in loading boot images).
>>
>> In your case, 'board_lmb_reserve' should make sure the secure monitor
>> does not get overwritten.
>> The 'arch_lmb_reserve' function for arm already ensures U-Boot text,
>> heap and stack don't get overwritten. It could be improved to reserve
>> +1M to the current stack pointer where it does reserve +4K now.
> If board_lmb_reserve() should be the solution I would prefer that not
> each individual board calls board_lmb_reserve() but that some common
> code is used to iterate over the memory reservations in the device tree.

I know that the efi loader has its own scheme of memory reservation. I 
just thought it cleaner to stay with lmb for fs_load and tftp as lmb is 
already used in image loading/booting.

But using the memory reservations from U-Boot device tree definitively 
makes sense, thanks for the hint. This should probably be done for the 
bootm code, too...

Simon

>
> Cheers
>
> Heinrich
>
>> I am working on a patch for the 'load' issue (which could be reused
>> for the tftp issue). There are some problems with the existing lmb
>> code though, which delayed me a bit. However, given that this doesn't
>> make it into the 2018.11 release, anyway, I figured some more days to
>> get it cleaner won't hurt...
>>
>> Simon
>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>>>>> Additionally, your patch checks the loaded file's size without taking
>>>>>> the load address into account. So unless I read that wrong, your check
>>>>>> is only valid for 'addr == 0'.
>>>> The approach is also not appliccable to networ boot; with TFTP we
>>>> don't know the image size in advance.
>>>>
>>>> Eventyally the boundary checking should be done where the image
>>>> content actually gets copied to memory.
>>>>
>>>> Best regards,
>>>>
>>>> Wolfgang Denk
>>>>
>>> _______________________________________________
>>> U-Boot mailing list
>>> U-Boot at lists.denx.de
>>> https://lists.denx.de/listinfo/u-boot

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-06 14:51 [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities Andrea Barisani
  2018-11-09  0:37 ` Fabio Estevam
@ 2018-11-13 20:57 ` Simon Goldschmidt
  2018-11-14 11:52   ` Andrea Barisani
  1 sibling, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-13 20:57 UTC (permalink / raw)
  To: u-boot

On 06.11.2018 15:51, Andrea Barisani wrote:
> [..]
> The issue can be exploited by several means:
>
>    - An excessively large crafted boot image file is parsed by the
>      `tftp_handler` function which lacks any size checks, allowing the memory
>      overwrite.
>
>    - A malicious server can manipulate TFTP packet sequence numbers to store
>      downloaded file chunks at arbitrary memory locations, given that the
>      sequence number is directly used by the `tftp_handler` function to calculate
>      the destination address for downloaded file chunks.
>
>      Additionally the `store_block` function, used to store downloaded file
>      chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
>      value of 0, triggers an unchecked integer underflow.
>
>      This allows to potentially erase memory located before the `loadAddr` when
>      a packet is sent with a null, following at least one valid packet.

Do you happen to have more details on this suggested integer underflow? 
I have tried to reproduce it, but I failed to get a memory write address 
before 'load_addr'. This is because the 'store_block' function does not 
directly use the underflowed integer as a block counter, but adds 
'tcp_block_wrap_offset' to this offset.

To me it seems like alternating between '0' and 'not 0' for the block 
counter could increase memory overwrites, but I fail to see how you can 
use this to store chunks at arbitrary memory locations. All you can do 
is subtract one block size from 'tftp_block_wrap_offset'...

Simon

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-13 20:57 ` Simon Goldschmidt
@ 2018-11-14 11:52   ` Andrea Barisani
  2018-11-14 12:03     ` Simon Goldschmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Andrea Barisani @ 2018-11-14 11:52 UTC (permalink / raw)
  To: u-boot

On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
> On 06.11.2018 15:51, Andrea Barisani wrote:
> > [..]
> > The issue can be exploited by several means:
> > 
> >    - An excessively large crafted boot image file is parsed by the
> >      `tftp_handler` function which lacks any size checks, allowing the memory
> >      overwrite.
> > 
> >    - A malicious server can manipulate TFTP packet sequence numbers to store
> >      downloaded file chunks at arbitrary memory locations, given that the
> >      sequence number is directly used by the `tftp_handler` function to calculate
> >      the destination address for downloaded file chunks.
> > 
> >      Additionally the `store_block` function, used to store downloaded file
> >      chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
> >      value of 0, triggers an unchecked integer underflow.
> > 
> >      This allows to potentially erase memory located before the `loadAddr` when
> >      a packet is sent with a null, following at least one valid packet.
> 
> Do you happen to have more details on this suggested integer underflow? I
> have tried to reproduce it, but I failed to get a memory write address
> before 'load_addr'. This is because the 'store_block' function does not
> directly use the underflowed integer as a block counter, but adds
> 'tcp_block_wrap_offset' to this offset.
> 
> To me it seems like alternating between '0' and 'not 0' for the block
> counter could increase memory overwrites, but I fail to see how you can use
> this to store chunks at arbitrary memory locations. All you can do is
> subtract one block size from 'tftp_block_wrap_offset'...
> 
> Simon
>

Hello Simon,

the integer underflow can happen if a malicious TFTP server, able to control
the TFTP packets sequence number, sends a crafted packet with sequence number
set to 0 during a flow.

This happens because, within the store_block() function, the 'block' argument
is declared as 'int' and when it is invoked inside tftp_handler() (case
TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
tftp_cur_block is the sequence number extracted from the tftp packet without
any previous check):

static inline void store_block(int block, uchar *src, unsigned len)
                               ^^^^^^^^^ can have negative values (e.g.  -1)
{
        ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
        ^^^^^
        here if block is -1 the result stored onto offset would be a very
        large unsigned number, due to type conversions
}

static void tftp_handler(...){

case TFTP_DATA:
        ...
                if (tftp_cur_block == tftp_prev_block) {
                        /* Same block again; ignore it. */
                        break;
                }

                tftp_prev_block = tftp_cur_block;
                timeout_count_max = tftp_timeout_count_max;
                net_set_timeout_handler(timeout_ms, tftp_timeout_handler);

                store_block(tftp_cur_block - 1, pkt + 2, len);
                            ^^^^^^^^^^^^^^^^^^
}

For these reasons the issue does not appear to be merely a "one block size"
substraction, but rather offset can reach very large values. Unless I am
missing something that I don't see of course...

You should probably prevent the underflow by placing a check against
tftp_cur_block before the store_block() invocation, but I defer to you for a
better implementation of the fix as you certainly know the overall logic much
better.

-- 
Andrea Barisani     Head of Hardware Security |     F-Secure
                                      Founder | Inverse Path

https://www.f-secure.com             https://inversepath.com
0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
       "Pluralitas non est ponenda sine necessitate"

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 11:52   ` Andrea Barisani
@ 2018-11-14 12:03     ` Simon Goldschmidt
  2018-11-14 14:45       ` Andrea Barisani
  0 siblings, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-14 12:03 UTC (permalink / raw)
  To: u-boot

On 14.11.2018 12:52, Andrea Barisani wrote:
> On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
>> On 06.11.2018 15:51, Andrea Barisani wrote:
>>> [..]
>>> The issue can be exploited by several means:
>>>
>>>     - An excessively large crafted boot image file is parsed by the
>>>       `tftp_handler` function which lacks any size checks, allowing the memory
>>>       overwrite.
>>>
>>>     - A malicious server can manipulate TFTP packet sequence numbers to store
>>>       downloaded file chunks at arbitrary memory locations, given that the
>>>       sequence number is directly used by the `tftp_handler` function to calculate
>>>       the destination address for downloaded file chunks.
>>>
>>>       Additionally the `store_block` function, used to store downloaded file
>>>       chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
>>>       value of 0, triggers an unchecked integer underflow.
>>>
>>>       This allows to potentially erase memory located before the `loadAddr` when
>>>       a packet is sent with a null, following at least one valid packet.
>> Do you happen to have more details on this suggested integer underflow? I
>> have tried to reproduce it, but I failed to get a memory write address
>> before 'load_addr'. This is because the 'store_block' function does not
>> directly use the underflowed integer as a block counter, but adds
>> 'tcp_block_wrap_offset' to this offset.
>>
>> To me it seems like alternating between '0' and 'not 0' for the block
>> counter could increase memory overwrites, but I fail to see how you can use
>> this to store chunks at arbitrary memory locations. All you can do is
>> subtract one block size from 'tftp_block_wrap_offset'...
>>
>> Simon
>>
> Hello Simon,
>
> the integer underflow can happen if a malicious TFTP server, able to control
> the TFTP packets sequence number, sends a crafted packet with sequence number
> set to 0 during a flow.
>
> This happens because, within the store_block() function, the 'block' argument
> is declared as 'int' and when it is invoked inside tftp_handler() (case
> TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
> tftp_cur_block is the sequence number extracted from the tftp packet without
> any previous check):
>
> static inline void store_block(int block, uchar *src, unsigned len)
>                                 ^^^^^^^^^ can have negative values (e.g.  -1)
> {
>          ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
>          ^^^^^
>          here if block is -1 the result stored onto offset would be a very
>          large unsigned number, due to type conversions

And this is exatclty my point. This might be bad coding style, but for 
me it works: 'block' is an 'int' and is '-1', so 'block * 
tftp_block_size' is '-512'. Now from the code flow in tftp_handler(), 
it's clear that if we come here with tftp_cur_block == 0 (so 'block' is 
-1), 'tftp_block_wrap_offset' is not 0 but some positive value 'x * 
tftp_block_size' (see function 'update_block_number').

So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still 
fail to see how this can be a very large positive number resulting in an 
effective negative offset or arbitrary write.

> }
>
> static void tftp_handler(...){
>
> case TFTP_DATA:
>          ...
>                  if (tftp_cur_block == tftp_prev_block) {
>                          /* Same block again; ignore it. */
>                          break;
>                  }
>
>                  tftp_prev_block = tftp_cur_block;
>                  timeout_count_max = tftp_timeout_count_max;
>                  net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
>
>                  store_block(tftp_cur_block - 1, pkt + 2, len);
>                              ^^^^^^^^^^^^^^^^^^
> }
>
> For these reasons the issue does not appear to be merely a "one block size"
> substraction, but rather offset can reach very large values. Unless I am
> missing something that I don't see of course...

So I take it this "bug" report is from reading the code only, not from 
actually testing it and seeing the arbitrary memory write? I wouldn't 
have expected this in a CVE report...

> You should probably prevent the underflow by placing a check against
> tftp_cur_block before the store_block() invocation, but I defer to you for a
> better implementation of the fix as you certainly know the overall logic much
> better.

Don't get me wrong: I'm just yet another user of U-Boot and I don't know 
the code better than you do. In fact, I looked at the tftp code for the 
first time yesterday after reading you report on the tftp issue in detail.


Simon

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 12:03     ` Simon Goldschmidt
@ 2018-11-14 14:45       ` Andrea Barisani
  2018-11-14 15:13         ` Simon Goldschmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Andrea Barisani @ 2018-11-14 14:45 UTC (permalink / raw)
  To: u-boot

On Wed, Nov 14, 2018 at 01:03:12PM +0100, Simon Goldschmidt wrote:
> On 14.11.2018 12:52, Andrea Barisani wrote:
> > On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
> > > On 06.11.2018 15:51, Andrea Barisani wrote:
> > > > [..]
> > > > The issue can be exploited by several means:
> > > > 
> > > >     - An excessively large crafted boot image file is parsed by the
> > > >       `tftp_handler` function which lacks any size checks, allowing the memory
> > > >       overwrite.
> > > > 
> > > >     - A malicious server can manipulate TFTP packet sequence numbers to store
> > > >       downloaded file chunks at arbitrary memory locations, given that the
> > > >       sequence number is directly used by the `tftp_handler` function to calculate
> > > >       the destination address for downloaded file chunks.
> > > > 
> > > >       Additionally the `store_block` function, used to store downloaded file
> > > >       chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
> > > >       value of 0, triggers an unchecked integer underflow.
> > > > 
> > > >       This allows to potentially erase memory located before the `loadAddr` when
> > > >       a packet is sent with a null, following at least one valid packet.
> > > Do you happen to have more details on this suggested integer underflow? I
> > > have tried to reproduce it, but I failed to get a memory write address
> > > before 'load_addr'. This is because the 'store_block' function does not
> > > directly use the underflowed integer as a block counter, but adds
> > > 'tcp_block_wrap_offset' to this offset.
> > > 
> > > To me it seems like alternating between '0' and 'not 0' for the block
> > > counter could increase memory overwrites, but I fail to see how you can use
> > > this to store chunks at arbitrary memory locations. All you can do is
> > > subtract one block size from 'tftp_block_wrap_offset'...
> > > 
> > > Simon
> > > 
> > Hello Simon,
> > 
> > the integer underflow can happen if a malicious TFTP server, able to control
> > the TFTP packets sequence number, sends a crafted packet with sequence number
> > set to 0 during a flow.
> > 
> > This happens because, within the store_block() function, the 'block' argument
> > is declared as 'int' and when it is invoked inside tftp_handler() (case
> > TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
> > tftp_cur_block is the sequence number extracted from the tftp packet without
> > any previous check):
> > 
> > static inline void store_block(int block, uchar *src, unsigned len)
> >                                 ^^^^^^^^^ can have negative values (e.g.  -1)
> > {
> >          ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
> >          ^^^^^
> >          here if block is -1 the result stored onto offset would be a very
> >          large unsigned number, due to type conversions
> 
> And this is exatclty my point. This might be bad coding style, but for me it
> works: 'block' is an 'int' and is '-1', so 'block * tftp_block_size' is
> '-512'. Now from the code flow in tftp_handler(), it's clear that if we come
> here with tftp_cur_block == 0 (so 'block' is -1), 'tftp_block_wrap_offset'
> is not 0 but some positive value 'x * tftp_block_size' (see function
> 'update_block_number').
>
> So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still fail
> to see how this can be a very large positive number resulting in an
> effective negative offset or arbitrary write.
> 

I understand your point, however what does happen when we enter the 'case
TFTP_DATA' and we are in the first block received, so we trigger
new_transfer() that sets the tftp_block_wrap_offset to 0 *and*
tftp_mcast_active is set?

I don't see any protection for this case for the underflow, am I wrong?

static void new_transfer(void)
{
        tftp_prev_block = 0;
        tftp_block_wrap = 0;
        tftp_block_wrap_offset = 0;
#ifdef CONFIG_CMD_TFTPPUT
        tftp_put_final_block_sent = 0;
#endif
}

...
case TFTP_DATA:

                if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
                    tftp_state == STATE_RECV_WRQ) {
                        /* first block received */
                        tftp_state = STATE_DATA;
                        tftp_remote_port = src;
                        new_transfer();
                        ^^^^^^^^^^^^^^^

#ifdef CONFIG_MCAST_TFTP
                        if (tftp_mcast_active) { /* start!=1 common if mcast */   <<<< HERE
                                tftp_prev_block = tftp_cur_block - 1;
                        } else
#endif
                        if (tftp_cur_block != 1) {      /* Assertion */
                                puts("\nTFTP error: ");
                                printf("First block is not block 1 (%ld)\n",
                                       tftp_cur_block);
                                puts("Starting again\n\n");
                                net_start_again();
                                break;
                        }
                }

                if (tftp_cur_block == tftp_prev_block) {
                        /* Same block again; ignore it. */
                        break;
                }

                tftp_prev_block = tftp_cur_block;
                timeout_count_max = tftp_timeout_count_max;
                net_set_timeout_handler(timeout_ms, tftp_timeout_handler);

                store_block(tftp_cur_block - 1, pkt + 2, len);
                            ^^^^^^^^^^^^^^^^^^
This should result in having -1 and thus -512 as result of the 'offset' math
that converted to ulong would result in a very large value.

> > }
> > 
> > static void tftp_handler(...){
> > 
> > case TFTP_DATA:
> >          ...
> >                  if (tftp_cur_block == tftp_prev_block) {
> >                          /* Same block again; ignore it. */
> >                          break;
> >                  }
> > 
> >                  tftp_prev_block = tftp_cur_block;
> >                  timeout_count_max = tftp_timeout_count_max;
> >                  net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
> > 
> >                  store_block(tftp_cur_block - 1, pkt + 2, len);
> >                              ^^^^^^^^^^^^^^^^^^
> > }
> > 
> > For these reasons the issue does not appear to be merely a "one block size"
> > substraction, but rather offset can reach very large values. Unless I am
> > missing something that I don't see of course...
> 
> So I take it this "bug" report is from reading the code only, not from
> actually testing it and seeing the arbitrary memory write? I wouldn't have
> expected this in a CVE report...
> 

As you see from our report the core issues have been fully tested and
reproduced.

It is true however that the additional remark on the `store_block' function
has only been evaluated by code analysis, in the context of the advisory it
seemed something worth notice in relation to the code structure but again, as
you say we didn't practically test that specific aspect, while everything
else was tested and reproduced.

The vulnerability report highlights two (in our opinion) critical
vulnerabilities, one of which described a secondary aspect only checked by
means of source code analysis.

The secondary aspect that we are discussing does not change the overall
impact of the TFTP bugs, which remains unchanged as arbitrary code execution
can anyway be achieved.

Thanks!

> > You should probably prevent the underflow by placing a check against
> > tftp_cur_block before the store_block() invocation, but I defer to you for a
> > better implementation of the fix as you certainly know the overall logic much
> > better.
> 
> Don't get me wrong: I'm just yet another user of U-Boot and I don't know the
> code better than you do. In fact, I looked at the tftp code for the first
> time yesterday after reading you report on the tftp issue in detail.
> 
> 
> Simon

-- 
Andrea Barisani     Head of Hardware Security |     F-Secure
                                      Founder | Inverse Path

https://www.f-secure.com             https://inversepath.com
0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
       "Pluralitas non est ponenda sine necessitate"

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 14:45       ` Andrea Barisani
@ 2018-11-14 15:13         ` Simon Goldschmidt
  2018-11-14 15:26           ` Andrea Barisani
  0 siblings, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-14 15:13 UTC (permalink / raw)
  To: u-boot

On 14.11.2018 15:45, Andrea Barisani wrote:
> On Wed, Nov 14, 2018 at 01:03:12PM +0100, Simon Goldschmidt wrote:
>> On 14.11.2018 12:52, Andrea Barisani wrote:
>>> On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
>>>> On 06.11.2018 15:51, Andrea Barisani wrote:
>>>>> [..]
>>>>> The issue can be exploited by several means:
>>>>>
>>>>>      - An excessively large crafted boot image file is parsed by the
>>>>>        `tftp_handler` function which lacks any size checks, allowing the memory
>>>>>        overwrite.
>>>>>
>>>>>      - A malicious server can manipulate TFTP packet sequence numbers to store
>>>>>        downloaded file chunks at arbitrary memory locations, given that the
>>>>>        sequence number is directly used by the `tftp_handler` function to calculate
>>>>>        the destination address for downloaded file chunks.
>>>>>
>>>>>        Additionally the `store_block` function, used to store downloaded file
>>>>>        chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
>>>>>        value of 0, triggers an unchecked integer underflow.
>>>>>
>>>>>        This allows to potentially erase memory located before the `loadAddr` when
>>>>>        a packet is sent with a null, following at least one valid packet.
>>>> Do you happen to have more details on this suggested integer underflow? I
>>>> have tried to reproduce it, but I failed to get a memory write address
>>>> before 'load_addr'. This is because the 'store_block' function does not
>>>> directly use the underflowed integer as a block counter, but adds
>>>> 'tcp_block_wrap_offset' to this offset.
>>>>
>>>> To me it seems like alternating between '0' and 'not 0' for the block
>>>> counter could increase memory overwrites, but I fail to see how you can use
>>>> this to store chunks at arbitrary memory locations. All you can do is
>>>> subtract one block size from 'tftp_block_wrap_offset'...
>>>>
>>>> Simon
>>>>
>>> Hello Simon,
>>>
>>> the integer underflow can happen if a malicious TFTP server, able to control
>>> the TFTP packets sequence number, sends a crafted packet with sequence number
>>> set to 0 during a flow.
>>>
>>> This happens because, within the store_block() function, the 'block' argument
>>> is declared as 'int' and when it is invoked inside tftp_handler() (case
>>> TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
>>> tftp_cur_block is the sequence number extracted from the tftp packet without
>>> any previous check):
>>>
>>> static inline void store_block(int block, uchar *src, unsigned len)
>>>                                  ^^^^^^^^^ can have negative values (e.g.  -1)
>>> {
>>>           ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
>>>           ^^^^^
>>>           here if block is -1 the result stored onto offset would be a very
>>>           large unsigned number, due to type conversions
>> And this is exatclty my point. This might be bad coding style, but for me it
>> works: 'block' is an 'int' and is '-1', so 'block * tftp_block_size' is
>> '-512'. Now from the code flow in tftp_handler(), it's clear that if we come
>> here with tftp_cur_block == 0 (so 'block' is -1), 'tftp_block_wrap_offset'
>> is not 0 but some positive value 'x * tftp_block_size' (see function
>> 'update_block_number').
>>
>> So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still fail
>> to see how this can be a very large positive number resulting in an
>> effective negative offset or arbitrary write.
>>
> I understand your point, however what does happen when we enter the 'case
> TFTP_DATA' and we are in the first block received, so we trigger
> new_transfer() that sets the tftp_block_wrap_offset to 0 *and*
> tftp_mcast_active is set?
>
> I don't see any protection for this case for the underflow, am I wrong?
>
> static void new_transfer(void)
> {
>          tftp_prev_block = 0;
>          tftp_block_wrap = 0;
>          tftp_block_wrap_offset = 0;
> #ifdef CONFIG_CMD_TFTPPUT
>          tftp_put_final_block_sent = 0;
> #endif
> }
>
> ...
> case TFTP_DATA:
>
>                  if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
>                      tftp_state == STATE_RECV_WRQ) {
>                          /* first block received */
>                          tftp_state = STATE_DATA;
>                          tftp_remote_port = src;
>                          new_transfer();
>                          ^^^^^^^^^^^^^^^

See some lines below...

>
> #ifdef CONFIG_MCAST_TFTP
>                          if (tftp_mcast_active) { /* start!=1 common if mcast */   <<<< HERE
>                                  tftp_prev_block = tftp_cur_block - 1;
>                          } else
> #endif
>                          if (tftp_cur_block != 1) {      /* Assertion */

If tftp_cur_block is 0 for the first block, we stop right away. No 
chance to reach store_block() at that time.

>                                  puts("\nTFTP error: ");
>                                  printf("First block is not block 1 (%ld)\n",
>                                         tftp_cur_block);
>                                  puts("Starting again\n\n");
>                                  net_start_again();
>                                  break;
>                          }
>                  }
>
>                  if (tftp_cur_block == tftp_prev_block) {
>                          /* Same block again; ignore it. */
>                          break;
>                  }
>
>                  tftp_prev_block = tftp_cur_block;
>                  timeout_count_max = tftp_timeout_count_max;
>                  net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
>
>                  store_block(tftp_cur_block - 1, pkt + 2, len);
>                              ^^^^^^^^^^^^^^^^^^
> This should result in having -1 and thus -512 as result of the 'offset' math
> that converted to ulong would result in a very large value.
>
>>> }
>>>
>>> static void tftp_handler(...){
>>>
>>> case TFTP_DATA:
>>>           ...
>>>                   if (tftp_cur_block == tftp_prev_block) {
>>>                           /* Same block again; ignore it. */
>>>                           break;
>>>                   }
>>>
>>>                   tftp_prev_block = tftp_cur_block;
>>>                   timeout_count_max = tftp_timeout_count_max;
>>>                   net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
>>>
>>>                   store_block(tftp_cur_block - 1, pkt + 2, len);
>>>                               ^^^^^^^^^^^^^^^^^^
>>> }
>>>
>>> For these reasons the issue does not appear to be merely a "one block size"
>>> substraction, but rather offset can reach very large values. Unless I am
>>> missing something that I don't see of course...
>> So I take it this "bug" report is from reading the code only, not from
>> actually testing it and seeing the arbitrary memory write? I wouldn't have
>> expected this in a CVE report...
>>
> As you see from our report the core issues have been fully tested and
> reproduced.

Yes. Thanks for that. I'm working on fixing them :-)

>
> It is true however that the additional remark on the `store_block' function
> has only been evaluated by code analysis, in the context of the advisory it
> seemed something worth notice in relation to the code structure but again, as
> you say we didn't practically test that specific aspect, while everything
> else was tested and reproduced.
>
> The vulnerability report highlights two (in our opinion) critical
> vulnerabilities, one of which described a secondary aspect only checked by
> means of source code analysis.

In my opinion as well these are critical, yes.

> The secondary aspect that we are discussing does not change the overall
> impact of the TFTP bugs, which remains unchanged as arbitrary code execution
> can anyway be achieved.

Of course. I'm working on fixing the actual bug and while debugging it 
tried to fix the other thing you mentioned. I could not reproduce it in 
a test setup (where I can freely send tftp packets). That's why I asked. 
The other bugs are of course not affected by this one not being valid.

Thanks for confirming this.

Simon

>
> Thanks!
>
>>> You should probably prevent the underflow by placing a check against
>>> tftp_cur_block before the store_block() invocation, but I defer to you for a
>>> better implementation of the fix as you certainly know the overall logic much
>>> better.
>> Don't get me wrong: I'm just yet another user of U-Boot and I don't know the
>> code better than you do. In fact, I looked at the tftp code for the first
>> time yesterday after reading you report on the tftp issue in detail.
>>
>>
>> Simon

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 15:13         ` Simon Goldschmidt
@ 2018-11-14 15:26           ` Andrea Barisani
  2018-11-14 15:35             ` Daniele Bianco
  0 siblings, 1 reply; 23+ messages in thread
From: Andrea Barisani @ 2018-11-14 15:26 UTC (permalink / raw)
  To: u-boot

On Wed, Nov 14, 2018 at 04:13:00PM +0100, Simon Goldschmidt wrote:
> On 14.11.2018 15:45, Andrea Barisani wrote:
> > On Wed, Nov 14, 2018 at 01:03:12PM +0100, Simon Goldschmidt wrote:
> > > On 14.11.2018 12:52, Andrea Barisani wrote:
> > > > On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
> > > > > On 06.11.2018 15:51, Andrea Barisani wrote:
> > > > > > [..]
> > > > > > The issue can be exploited by several means:
> > > > > > 
> > > > > >      - An excessively large crafted boot image file is parsed by the
> > > > > >        `tftp_handler` function which lacks any size checks, allowing the memory
> > > > > >        overwrite.
> > > > > > 
> > > > > >      - A malicious server can manipulate TFTP packet sequence numbers to store
> > > > > >        downloaded file chunks at arbitrary memory locations, given that the
> > > > > >        sequence number is directly used by the `tftp_handler` function to calculate
> > > > > >        the destination address for downloaded file chunks.
> > > > > > 
> > > > > >        Additionally the `store_block` function, used to store downloaded file
> > > > > >        chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
> > > > > >        value of 0, triggers an unchecked integer underflow.
> > > > > > 
> > > > > >        This allows to potentially erase memory located before the `loadAddr` when
> > > > > >        a packet is sent with a null, following at least one valid packet.
> > > > > Do you happen to have more details on this suggested integer underflow? I
> > > > > have tried to reproduce it, but I failed to get a memory write address
> > > > > before 'load_addr'. This is because the 'store_block' function does not
> > > > > directly use the underflowed integer as a block counter, but adds
> > > > > 'tcp_block_wrap_offset' to this offset.
> > > > > 
> > > > > To me it seems like alternating between '0' and 'not 0' for the block
> > > > > counter could increase memory overwrites, but I fail to see how you can use
> > > > > this to store chunks at arbitrary memory locations. All you can do is
> > > > > subtract one block size from 'tftp_block_wrap_offset'...
> > > > > 
> > > > > Simon
> > > > > 
> > > > Hello Simon,
> > > > 
> > > > the integer underflow can happen if a malicious TFTP server, able to control
> > > > the TFTP packets sequence number, sends a crafted packet with sequence number
> > > > set to 0 during a flow.
> > > > 
> > > > This happens because, within the store_block() function, the 'block' argument
> > > > is declared as 'int' and when it is invoked inside tftp_handler() (case
> > > > TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
> > > > tftp_cur_block is the sequence number extracted from the tftp packet without
> > > > any previous check):
> > > > 
> > > > static inline void store_block(int block, uchar *src, unsigned len)
> > > >                                  ^^^^^^^^^ can have negative values (e.g.  -1)
> > > > {
> > > >           ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
> > > >           ^^^^^
> > > >           here if block is -1 the result stored onto offset would be a very
> > > >           large unsigned number, due to type conversions
> > > And this is exatclty my point. This might be bad coding style, but for me it
> > > works: 'block' is an 'int' and is '-1', so 'block * tftp_block_size' is
> > > '-512'. Now from the code flow in tftp_handler(), it's clear that if we come
> > > here with tftp_cur_block == 0 (so 'block' is -1), 'tftp_block_wrap_offset'
> > > is not 0 but some positive value 'x * tftp_block_size' (see function
> > > 'update_block_number').
> > > 
> > > So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still fail
> > > to see how this can be a very large positive number resulting in an
> > > effective negative offset or arbitrary write.
> > > 
> > I understand your point, however what does happen when we enter the 'case
> > TFTP_DATA' and we are in the first block received, so we trigger
> > new_transfer() that sets the tftp_block_wrap_offset to 0 *and*
> > tftp_mcast_active is set?
> > 
> > I don't see any protection for this case for the underflow, am I wrong?
> > 
> > static void new_transfer(void)
> > {
> >          tftp_prev_block = 0;
> >          tftp_block_wrap = 0;
> >          tftp_block_wrap_offset = 0;
> > #ifdef CONFIG_CMD_TFTPPUT
> >          tftp_put_final_block_sent = 0;
> > #endif
> > }
> > 
> > ...
> > case TFTP_DATA:
> > 
> >                  if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
> >                      tftp_state == STATE_RECV_WRQ) {
> >                          /* first block received */
> >                          tftp_state = STATE_DATA;
> >                          tftp_remote_port = src;
> >                          new_transfer();
> >                          ^^^^^^^^^^^^^^^
> 
> See some lines below...
> 
> > 
> > #ifdef CONFIG_MCAST_TFTP
> >                          if (tftp_mcast_active) { /* start!=1 common if mcast */   <<<< HERE
> >                                  tftp_prev_block = tftp_cur_block - 1;
> >                          } else
> > #endif
> >                          if (tftp_cur_block != 1) {      /* Assertion */
> 
> If tftp_cur_block is 0 for the first block, we stop right away. No chance to
> reach store_block() at that time.
>

CC'ing my colleague Daniele whom can better reply further on this.
 
> >                                  puts("\nTFTP error: ");
> >                                  printf("First block is not block 1 (%ld)\n",
> >                                         tftp_cur_block);
> >                                  puts("Starting again\n\n");
> >                                  net_start_again();
> >                                  break;
> >                          }
> >                  }
> > 
> >                  if (tftp_cur_block == tftp_prev_block) {
> >                          /* Same block again; ignore it. */
> >                          break;
> >                  }
> > 
> >                  tftp_prev_block = tftp_cur_block;
> >                  timeout_count_max = tftp_timeout_count_max;
> >                  net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
> > 
> >                  store_block(tftp_cur_block - 1, pkt + 2, len);
> >                              ^^^^^^^^^^^^^^^^^^
> > This should result in having -1 and thus -512 as result of the 'offset' math
> > that converted to ulong would result in a very large value.
> > 
> > > > }
> > > > 
> > > > static void tftp_handler(...){
> > > > 
> > > > case TFTP_DATA:
> > > >           ...
> > > >                   if (tftp_cur_block == tftp_prev_block) {
> > > >                           /* Same block again; ignore it. */
> > > >                           break;
> > > >                   }
> > > > 
> > > >                   tftp_prev_block = tftp_cur_block;
> > > >                   timeout_count_max = tftp_timeout_count_max;
> > > >                   net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
> > > > 
> > > >                   store_block(tftp_cur_block - 1, pkt + 2, len);
> > > >                               ^^^^^^^^^^^^^^^^^^
> > > > }
> > > > 
> > > > For these reasons the issue does not appear to be merely a "one block size"
> > > > substraction, but rather offset can reach very large values. Unless I am
> > > > missing something that I don't see of course...
> > > So I take it this "bug" report is from reading the code only, not from
> > > actually testing it and seeing the arbitrary memory write? I wouldn't have
> > > expected this in a CVE report...
> > > 
> > As you see from our report the core issues have been fully tested and
> > reproduced.
> 
> Yes. Thanks for that. I'm working on fixing them :-)
> 

And that's much appreciated :)

> > 
> > It is true however that the additional remark on the `store_block' function
> > has only been evaluated by code analysis, in the context of the advisory it
> > seemed something worth notice in relation to the code structure but again, as
> > you say we didn't practically test that specific aspect, while everything
> > else was tested and reproduced.
> > 
> > The vulnerability report highlights two (in our opinion) critical
> > vulnerabilities, one of which described a secondary aspect only checked by
> > means of source code analysis.
> 
> In my opinion as well these are critical, yes.
> 
> > The secondary aspect that we are discussing does not change the overall
> > impact of the TFTP bugs, which remains unchanged as arbitrary code execution
> > can anyway be achieved.
> 
> Of course. I'm working on fixing the actual bug and while debugging it tried
> to fix the other thing you mentioned. I could not reproduce it in a test
> setup (where I can freely send tftp packets). That's why I asked. The other
> bugs are of course not affected by this one not being valid.
> 

Understood.

Cheers

> Thanks for confirming this.
> 
> Simon
> 
> > 
> > Thanks!
> > 
> > > > You should probably prevent the underflow by placing a check against
> > > > tftp_cur_block before the store_block() invocation, but I defer to you for a
> > > > better implementation of the fix as you certainly know the overall logic much
> > > > better.
> > > Don't get me wrong: I'm just yet another user of U-Boot and I don't know the
> > > code better than you do. In fact, I looked at the tftp code for the first
> > > time yesterday after reading you report on the tftp issue in detail.
> > > 
> > > 
> > > Simon
> 
> 

-- 
Andrea Barisani     Head of Hardware Security |     F-Secure
                                      Founder | Inverse Path

https://www.f-secure.com             https://inversepath.com
0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
       "Pluralitas non est ponenda sine necessitate"

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 15:26           ` Andrea Barisani
@ 2018-11-14 15:35             ` Daniele Bianco
  2018-11-14 15:51               ` Simon Goldschmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Daniele Bianco @ 2018-11-14 15:35 UTC (permalink / raw)
  To: u-boot

On Wed, Nov 14, 2018 at 04:26:17PM +0100, Andrea Barisani wrote:
> On Wed, Nov 14, 2018 at 04:13:00PM +0100, Simon Goldschmidt wrote:
> > On 14.11.2018 15:45, Andrea Barisani wrote:
> > > On Wed, Nov 14, 2018 at 01:03:12PM +0100, Simon Goldschmidt wrote:
> > > > On 14.11.2018 12:52, Andrea Barisani wrote:
> > > > > On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
> > > > > > On 06.11.2018 15:51, Andrea Barisani wrote:
> > > > > > > [..]
> > > > > > > The issue can be exploited by several means:
> > > > > > > 
> > > > > > >      - An excessively large crafted boot image file is parsed by the
> > > > > > >        `tftp_handler` function which lacks any size checks, allowing the memory
> > > > > > >        overwrite.
> > > > > > > 
> > > > > > >      - A malicious server can manipulate TFTP packet sequence numbers to store
> > > > > > >        downloaded file chunks at arbitrary memory locations, given that the
> > > > > > >        sequence number is directly used by the `tftp_handler` function to calculate
> > > > > > >        the destination address for downloaded file chunks.
> > > > > > > 
> > > > > > >        Additionally the `store_block` function, used to store downloaded file
> > > > > > >        chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
> > > > > > >        value of 0, triggers an unchecked integer underflow.
> > > > > > > 
> > > > > > >        This allows to potentially erase memory located before the `loadAddr` when
> > > > > > >        a packet is sent with a null, following at least one valid packet.
> > > > > > Do you happen to have more details on this suggested integer underflow? I
> > > > > > have tried to reproduce it, but I failed to get a memory write address
> > > > > > before 'load_addr'. This is because the 'store_block' function does not
> > > > > > directly use the underflowed integer as a block counter, but adds
> > > > > > 'tcp_block_wrap_offset' to this offset.
> > > > > > 
> > > > > > To me it seems like alternating between '0' and 'not 0' for the block
> > > > > > counter could increase memory overwrites, but I fail to see how you can use
> > > > > > this to store chunks at arbitrary memory locations. All you can do is
> > > > > > subtract one block size from 'tftp_block_wrap_offset'...
> > > > > > 
> > > > > > Simon
> > > > > > 
> > > > > Hello Simon,
> > > > > 
> > > > > the integer underflow can happen if a malicious TFTP server, able to control
> > > > > the TFTP packets sequence number, sends a crafted packet with sequence number
> > > > > set to 0 during a flow.
> > > > > 
> > > > > This happens because, within the store_block() function, the 'block' argument
> > > > > is declared as 'int' and when it is invoked inside tftp_handler() (case
> > > > > TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
> > > > > tftp_cur_block is the sequence number extracted from the tftp packet without
> > > > > any previous check):
> > > > > 
> > > > > static inline void store_block(int block, uchar *src, unsigned len)
> > > > >                                  ^^^^^^^^^ can have negative values (e.g.  -1)
> > > > > {
> > > > >           ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
> > > > >           ^^^^^
> > > > >           here if block is -1 the result stored onto offset would be a very
> > > > >           large unsigned number, due to type conversions
> > > > And this is exatclty my point. This might be bad coding style, but for me it
> > > > works: 'block' is an 'int' and is '-1', so 'block * tftp_block_size' is
> > > > '-512'. Now from the code flow in tftp_handler(), it's clear that if we come
> > > > here with tftp_cur_block == 0 (so 'block' is -1), 'tftp_block_wrap_offset'
> > > > is not 0 but some positive value 'x * tftp_block_size' (see function
> > > > 'update_block_number').
> > > > 
> > > > So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still fail
> > > > to see how this can be a very large positive number resulting in an
> > > > effective negative offset or arbitrary write.
> > > > 
> > > I understand your point, however what does happen when we enter the 'case
> > > TFTP_DATA' and we are in the first block received, so we trigger
> > > new_transfer() that sets the tftp_block_wrap_offset to 0 *and*
> > > tftp_mcast_active is set?
> > > 
> > > I don't see any protection for this case for the underflow, am I wrong?
> > > 
> > > static void new_transfer(void)
> > > {
> > >          tftp_prev_block = 0;
> > >          tftp_block_wrap = 0;
> > >          tftp_block_wrap_offset = 0;
> > > #ifdef CONFIG_CMD_TFTPPUT
> > >          tftp_put_final_block_sent = 0;
> > > #endif
> > > }
> > > 
> > > ...
> > > case TFTP_DATA:
> > > 
> > >                  if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
> > >                      tftp_state == STATE_RECV_WRQ) {
> > >                          /* first block received */
> > >                          tftp_state = STATE_DATA;
> > >                          tftp_remote_port = src;
> > >                          new_transfer();
> > >                          ^^^^^^^^^^^^^^^
> > 
> > See some lines below...
> > 
> > > 
> > > #ifdef CONFIG_MCAST_TFTP
> > >                          if (tftp_mcast_active) { /* start!=1 common if mcast */   <<<< HERE
> > >                                  tftp_prev_block = tftp_cur_block - 1;
> > >                          } else
> > > #endif
> > >                          if (tftp_cur_block != 1) {      /* Assertion */
> > 
> > If tftp_cur_block is 0 for the first block, we stop right away. No chance to
> > reach store_block() at that time.
> >
> 
> CC'ing my colleague Daniele whom can better reply further on this.

Hi Simon,
the 'if (tftp_cur_block != 1)' is not triggered if 'tftp_mcast_active'
is set (and the CONFIG_MCAST_TFTP is defined).

Please note the code indentation does not help in this case as it is
misleading, but this is because of the #ifdef.

Cheers,
Daniele

>  
> > >                                  puts("\nTFTP error: ");
> > >                                  printf("First block is not block 1 (%ld)\n",
> > >                                         tftp_cur_block);
> > >                                  puts("Starting again\n\n");
> > >                                  net_start_again();
> > >                                  break;
> > >                          }
> > >                  }
> > > 
> > >                  if (tftp_cur_block == tftp_prev_block) {
> > >                          /* Same block again; ignore it. */
> > >                          break;
> > >                  }
> > > 
> > >                  tftp_prev_block = tftp_cur_block;
> > >                  timeout_count_max = tftp_timeout_count_max;
> > >                  net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
> > > 
> > >                  store_block(tftp_cur_block - 1, pkt + 2, len);
> > >                              ^^^^^^^^^^^^^^^^^^
> > > This should result in having -1 and thus -512 as result of the 'offset' math
> > > that converted to ulong would result in a very large value.
> > > 
> > > > > }
> > > > > 
> > > > > static void tftp_handler(...){
> > > > > 
> > > > > case TFTP_DATA:
> > > > >           ...
> > > > >                   if (tftp_cur_block == tftp_prev_block) {
> > > > >                           /* Same block again; ignore it. */
> > > > >                           break;
> > > > >                   }
> > > > > 
> > > > >                   tftp_prev_block = tftp_cur_block;
> > > > >                   timeout_count_max = tftp_timeout_count_max;
> > > > >                   net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
> > > > > 
> > > > >                   store_block(tftp_cur_block - 1, pkt + 2, len);
> > > > >                               ^^^^^^^^^^^^^^^^^^
> > > > > }
> > > > > 
> > > > > For these reasons the issue does not appear to be merely a "one block size"
> > > > > substraction, but rather offset can reach very large values. Unless I am
> > > > > missing something that I don't see of course...
> > > > So I take it this "bug" report is from reading the code only, not from
> > > > actually testing it and seeing the arbitrary memory write? I wouldn't have
> > > > expected this in a CVE report...
> > > > 
> > > As you see from our report the core issues have been fully tested and
> > > reproduced.
> > 
> > Yes. Thanks for that. I'm working on fixing them :-)
> > 
> 
> And that's much appreciated :)
> 
> > > 
> > > It is true however that the additional remark on the `store_block' function
> > > has only been evaluated by code analysis, in the context of the advisory it
> > > seemed something worth notice in relation to the code structure but again, as
> > > you say we didn't practically test that specific aspect, while everything
> > > else was tested and reproduced.
> > > 
> > > The vulnerability report highlights two (in our opinion) critical
> > > vulnerabilities, one of which described a secondary aspect only checked by
> > > means of source code analysis.
> > 
> > In my opinion as well these are critical, yes.
> > 
> > > The secondary aspect that we are discussing does not change the overall
> > > impact of the TFTP bugs, which remains unchanged as arbitrary code execution
> > > can anyway be achieved.
> > 
> > Of course. I'm working on fixing the actual bug and while debugging it tried
> > to fix the other thing you mentioned. I could not reproduce it in a test
> > setup (where I can freely send tftp packets). That's why I asked. The other
> > bugs are of course not affected by this one not being valid.
> > 
> 
> Understood.
> 
> Cheers
> 
> > Thanks for confirming this.
> > 
> > Simon
> > 
> > > 
> > > Thanks!
> > > 
> > > > > You should probably prevent the underflow by placing a check against
> > > > > tftp_cur_block before the store_block() invocation, but I defer to you for a
> > > > > better implementation of the fix as you certainly know the overall logic much
> > > > > better.
> > > > Don't get me wrong: I'm just yet another user of U-Boot and I don't know the
> > > > code better than you do. In fact, I looked at the tftp code for the first
> > > > time yesterday after reading you report on the tftp issue in detail.
> > > > 
> > > > 
> > > > Simon
> > 
> > 
> 
> -- 
> Andrea Barisani     Head of Hardware Security |     F-Secure
>                                       Founder | Inverse Path
> 
> https://www.f-secure.com             https://inversepath.com
> 0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
>        "Pluralitas non est ponenda sine necessitate"
--
  Daniele Bianco
  Hardware Security | F-Secure

  <daniele.bianco@f-secure.com> | https://www.f-secure.com
  GPG Key fingerprint = 88A7 43F4 F28F 1B9D 6F2D  4AC5 AE75 822E 9544 A497

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 15:35             ` Daniele Bianco
@ 2018-11-14 15:51               ` Simon Goldschmidt
  2018-11-14 19:07                 ` Simon Goldschmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-14 15:51 UTC (permalink / raw)
  To: u-boot

On 14.11.2018 16:35, Daniele Bianco wrote:
> On Wed, Nov 14, 2018 at 04:26:17PM +0100, Andrea Barisani wrote:
>> On Wed, Nov 14, 2018 at 04:13:00PM +0100, Simon Goldschmidt wrote:
>>> On 14.11.2018 15:45, Andrea Barisani wrote:
>>>> On Wed, Nov 14, 2018 at 01:03:12PM +0100, Simon Goldschmidt wrote:
>>>>> On 14.11.2018 12:52, Andrea Barisani wrote:
>>>>>> On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
>>>>>>> On 06.11.2018 15:51, Andrea Barisani wrote:
>>>>>>>> [..]
>>>>>>>> The issue can be exploited by several means:
>>>>>>>>
>>>>>>>>       - An excessively large crafted boot image file is parsed by the
>>>>>>>>         `tftp_handler` function which lacks any size checks, allowing the memory
>>>>>>>>         overwrite.
>>>>>>>>
>>>>>>>>       - A malicious server can manipulate TFTP packet sequence numbers to store
>>>>>>>>         downloaded file chunks at arbitrary memory locations, given that the
>>>>>>>>         sequence number is directly used by the `tftp_handler` function to calculate
>>>>>>>>         the destination address for downloaded file chunks.
>>>>>>>>
>>>>>>>>         Additionally the `store_block` function, used to store downloaded file
>>>>>>>>         chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
>>>>>>>>         value of 0, triggers an unchecked integer underflow.
>>>>>>>>
>>>>>>>>         This allows to potentially erase memory located before the `loadAddr` when
>>>>>>>>         a packet is sent with a null, following at least one valid packet.
>>>>>>> Do you happen to have more details on this suggested integer underflow? I
>>>>>>> have tried to reproduce it, but I failed to get a memory write address
>>>>>>> before 'load_addr'. This is because the 'store_block' function does not
>>>>>>> directly use the underflowed integer as a block counter, but adds
>>>>>>> 'tcp_block_wrap_offset' to this offset.
>>>>>>>
>>>>>>> To me it seems like alternating between '0' and 'not 0' for the block
>>>>>>> counter could increase memory overwrites, but I fail to see how you can use
>>>>>>> this to store chunks at arbitrary memory locations. All you can do is
>>>>>>> subtract one block size from 'tftp_block_wrap_offset'...
>>>>>>>
>>>>>>> Simon
>>>>>>>
>>>>>> Hello Simon,
>>>>>>
>>>>>> the integer underflow can happen if a malicious TFTP server, able to control
>>>>>> the TFTP packets sequence number, sends a crafted packet with sequence number
>>>>>> set to 0 during a flow.
>>>>>>
>>>>>> This happens because, within the store_block() function, the 'block' argument
>>>>>> is declared as 'int' and when it is invoked inside tftp_handler() (case
>>>>>> TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
>>>>>> tftp_cur_block is the sequence number extracted from the tftp packet without
>>>>>> any previous check):
>>>>>>
>>>>>> static inline void store_block(int block, uchar *src, unsigned len)
>>>>>>                                   ^^^^^^^^^ can have negative values (e.g.  -1)
>>>>>> {
>>>>>>            ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
>>>>>>            ^^^^^
>>>>>>            here if block is -1 the result stored onto offset would be a very
>>>>>>            large unsigned number, due to type conversions
>>>>> And this is exatclty my point. This might be bad coding style, but for me it
>>>>> works: 'block' is an 'int' and is '-1', so 'block * tftp_block_size' is
>>>>> '-512'. Now from the code flow in tftp_handler(), it's clear that if we come
>>>>> here with tftp_cur_block == 0 (so 'block' is -1), 'tftp_block_wrap_offset'
>>>>> is not 0 but some positive value 'x * tftp_block_size' (see function
>>>>> 'update_block_number').
>>>>>
>>>>> So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still fail
>>>>> to see how this can be a very large positive number resulting in an
>>>>> effective negative offset or arbitrary write.
>>>>>
>>>> I understand your point, however what does happen when we enter the 'case
>>>> TFTP_DATA' and we are in the first block received, so we trigger
>>>> new_transfer() that sets the tftp_block_wrap_offset to 0 *and*
>>>> tftp_mcast_active is set?
>>>>
>>>> I don't see any protection for this case for the underflow, am I wrong?
>>>>
>>>> static void new_transfer(void)
>>>> {
>>>>           tftp_prev_block = 0;
>>>>           tftp_block_wrap = 0;
>>>>           tftp_block_wrap_offset = 0;
>>>> #ifdef CONFIG_CMD_TFTPPUT
>>>>           tftp_put_final_block_sent = 0;
>>>> #endif
>>>> }
>>>>
>>>> ...
>>>> case TFTP_DATA:
>>>>
>>>>                   if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
>>>>                       tftp_state == STATE_RECV_WRQ) {
>>>>                           /* first block received */
>>>>                           tftp_state = STATE_DATA;
>>>>                           tftp_remote_port = src;
>>>>                           new_transfer();
>>>>                           ^^^^^^^^^^^^^^^
>>> See some lines below...
>>>
>>>> #ifdef CONFIG_MCAST_TFTP
>>>>                           if (tftp_mcast_active) { /* start!=1 common if mcast */   <<<< HERE
>>>>                                   tftp_prev_block = tftp_cur_block - 1;
>>>>                           } else
>>>> #endif
>>>>                           if (tftp_cur_block != 1) {      /* Assertion */
>>> If tftp_cur_block is 0 for the first block, we stop right away. No chance to
>>> reach store_block() at that time.
>>>
>> CC'ing my colleague Daniele whom can better reply further on this.
> Hi Simon,
> the 'if (tftp_cur_block != 1)' is not triggered if 'tftp_mcast_active'
> is set (and the CONFIG_MCAST_TFTP is defined).
>
> Please note the code indentation does not help in this case as it is
> misleading, but this is because of the #ifdef.

Ah, now I do see it, thanks for the hint! Indeed, the indentation of 
that else totally hid it from my eyes that the next block wasn't 
executed always!

Luckily, searching through the whole mainline codebase shows no users of 
this option (CONFIG_MCAST_TFTP), so I guess this is not a real world 
problem, currently :-)

Thanks for your explanation and your fast response!

Cheers,
Simon

>
> Cheers,
> Daniele
>
>>   
>>>>                                   puts("\nTFTP error: ");
>>>>                                   printf("First block is not block 1 (%ld)\n",
>>>>                                          tftp_cur_block);
>>>>                                   puts("Starting again\n\n");
>>>>                                   net_start_again();
>>>>                                   break;
>>>>                           }
>>>>                   }
>>>>
>>>>                   if (tftp_cur_block == tftp_prev_block) {
>>>>                           /* Same block again; ignore it. */
>>>>                           break;
>>>>                   }
>>>>
>>>>                   tftp_prev_block = tftp_cur_block;
>>>>                   timeout_count_max = tftp_timeout_count_max;
>>>>                   net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
>>>>
>>>>                   store_block(tftp_cur_block - 1, pkt + 2, len);
>>>>                               ^^^^^^^^^^^^^^^^^^
>>>> This should result in having -1 and thus -512 as result of the 'offset' math
>>>> that converted to ulong would result in a very large value.
>>>>
>>>>>> }
>>>>>>
>>>>>> static void tftp_handler(...){
>>>>>>
>>>>>> case TFTP_DATA:
>>>>>>            ...
>>>>>>                    if (tftp_cur_block == tftp_prev_block) {
>>>>>>                            /* Same block again; ignore it. */
>>>>>>                            break;
>>>>>>                    }
>>>>>>
>>>>>>                    tftp_prev_block = tftp_cur_block;
>>>>>>                    timeout_count_max = tftp_timeout_count_max;
>>>>>>                    net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
>>>>>>
>>>>>>                    store_block(tftp_cur_block - 1, pkt + 2, len);
>>>>>>                                ^^^^^^^^^^^^^^^^^^
>>>>>> }
>>>>>>
>>>>>> For these reasons the issue does not appear to be merely a "one block size"
>>>>>> substraction, but rather offset can reach very large values. Unless I am
>>>>>> missing something that I don't see of course...
>>>>> So I take it this "bug" report is from reading the code only, not from
>>>>> actually testing it and seeing the arbitrary memory write? I wouldn't have
>>>>> expected this in a CVE report...
>>>>>
>>>> As you see from our report the core issues have been fully tested and
>>>> reproduced.
>>> Yes. Thanks for that. I'm working on fixing them :-)
>>>
>> And that's much appreciated :)
>>
>>>> It is true however that the additional remark on the `store_block' function
>>>> has only been evaluated by code analysis, in the context of the advisory it
>>>> seemed something worth notice in relation to the code structure but again, as
>>>> you say we didn't practically test that specific aspect, while everything
>>>> else was tested and reproduced.
>>>>
>>>> The vulnerability report highlights two (in our opinion) critical
>>>> vulnerabilities, one of which described a secondary aspect only checked by
>>>> means of source code analysis.
>>> In my opinion as well these are critical, yes.
>>>
>>>> The secondary aspect that we are discussing does not change the overall
>>>> impact of the TFTP bugs, which remains unchanged as arbitrary code execution
>>>> can anyway be achieved.
>>> Of course. I'm working on fixing the actual bug and while debugging it tried
>>> to fix the other thing you mentioned. I could not reproduce it in a test
>>> setup (where I can freely send tftp packets). That's why I asked. The other
>>> bugs are of course not affected by this one not being valid.
>>>
>> Understood.
>>
>> Cheers
>>
>>> Thanks for confirming this.
>>>
>>> Simon
>>>
>>>> Thanks!
>>>>
>>>>>> You should probably prevent the underflow by placing a check against
>>>>>> tftp_cur_block before the store_block() invocation, but I defer to you for a
>>>>>> better implementation of the fix as you certainly know the overall logic much
>>>>>> better.
>>>>> Don't get me wrong: I'm just yet another user of U-Boot and I don't know the
>>>>> code better than you do. In fact, I looked at the tftp code for the first
>>>>> time yesterday after reading you report on the tftp issue in detail.
>>>>>
>>>>>
>>>>> Simon
>>>
>> -- 
>> Andrea Barisani     Head of Hardware Security |     F-Secure
>>                                        Founder | Inverse Path
>>
>> https://www.f-secure.com             https://inversepath.com
>> 0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
>>         "Pluralitas non est ponenda sine necessitate"
> --
>    Daniele Bianco
>    Hardware Security | F-Secure
>
>    <daniele.bianco@f-secure.com> | https://www.f-secure.com
>    GPG Key fingerprint = 88A7 43F4 F28F 1B9D 6F2D  4AC5 AE75 822E 9544 A497

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 15:51               ` Simon Goldschmidt
@ 2018-11-14 19:07                 ` Simon Goldschmidt
  2018-11-14 23:36                   ` Joe Hershberger
  0 siblings, 1 reply; 23+ messages in thread
From: Simon Goldschmidt @ 2018-11-14 19:07 UTC (permalink / raw)
  To: u-boot

On 14.11.2018 16:51, Simon Goldschmidt wrote:
> On 14.11.2018 16:35, Daniele Bianco wrote:
>> On Wed, Nov 14, 2018 at 04:26:17PM +0100, Andrea Barisani wrote:
>>> On Wed, Nov 14, 2018 at 04:13:00PM +0100, Simon Goldschmidt wrote:
>>>> On 14.11.2018 15:45, Andrea Barisani wrote:
>>>>> On Wed, Nov 14, 2018 at 01:03:12PM +0100, Simon Goldschmidt wrote:
>>>>>> On 14.11.2018 12:52, Andrea Barisani wrote:
>>>>>>> On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
>>>>>>>> On 06.11.2018 15:51, Andrea Barisani wrote:
>>>>>>>>> [..]
>>>>>>>>> The issue can be exploited by several means:
>>>>>>>>>
>>>>>>>>>        - An excessively large crafted boot image file is parsed by the
>>>>>>>>>          `tftp_handler` function which lacks any size checks, allowing the memory
>>>>>>>>>          overwrite.
>>>>>>>>>
>>>>>>>>>        - A malicious server can manipulate TFTP packet sequence numbers to store
>>>>>>>>>          downloaded file chunks at arbitrary memory locations, given that the
>>>>>>>>>          sequence number is directly used by the `tftp_handler` function to calculate
>>>>>>>>>          the destination address for downloaded file chunks.
>>>>>>>>>
>>>>>>>>>          Additionally the `store_block` function, used to store downloaded file
>>>>>>>>>          chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
>>>>>>>>>          value of 0, triggers an unchecked integer underflow.
>>>>>>>>>
>>>>>>>>>          This allows to potentially erase memory located before the `loadAddr` when
>>>>>>>>>          a packet is sent with a null, following at least one valid packet.
>>>>>>>> Do you happen to have more details on this suggested integer underflow? I
>>>>>>>> have tried to reproduce it, but I failed to get a memory write address
>>>>>>>> before 'load_addr'. This is because the 'store_block' function does not
>>>>>>>> directly use the underflowed integer as a block counter, but adds
>>>>>>>> 'tcp_block_wrap_offset' to this offset.
>>>>>>>>
>>>>>>>> To me it seems like alternating between '0' and 'not 0' for the block
>>>>>>>> counter could increase memory overwrites, but I fail to see how you can use
>>>>>>>> this to store chunks at arbitrary memory locations. All you can do is
>>>>>>>> subtract one block size from 'tftp_block_wrap_offset'...
>>>>>>>>
>>>>>>>> Simon
>>>>>>>>
>>>>>>> Hello Simon,
>>>>>>>
>>>>>>> the integer underflow can happen if a malicious TFTP server, able to control
>>>>>>> the TFTP packets sequence number, sends a crafted packet with sequence number
>>>>>>> set to 0 during a flow.
>>>>>>>
>>>>>>> This happens because, within the store_block() function, the 'block' argument
>>>>>>> is declared as 'int' and when it is invoked inside tftp_handler() (case
>>>>>>> TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
>>>>>>> tftp_cur_block is the sequence number extracted from the tftp packet without
>>>>>>> any previous check):
>>>>>>>
>>>>>>> static inline void store_block(int block, uchar *src, unsigned len)
>>>>>>>                                    ^^^^^^^^^ can have negative values (e.g.  -1)
>>>>>>> {
>>>>>>>             ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
>>>>>>>             ^^^^^
>>>>>>>             here if block is -1 the result stored onto offset would be a very
>>>>>>>             large unsigned number, due to type conversions
>>>>>> And this is exatclty my point. This might be bad coding style, but for me it
>>>>>> works: 'block' is an 'int' and is '-1', so 'block * tftp_block_size' is
>>>>>> '-512'. Now from the code flow in tftp_handler(), it's clear that if we come
>>>>>> here with tftp_cur_block == 0 (so 'block' is -1), 'tftp_block_wrap_offset'
>>>>>> is not 0 but some positive value 'x * tftp_block_size' (see function
>>>>>> 'update_block_number').
>>>>>>
>>>>>> So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still fail
>>>>>> to see how this can be a very large positive number resulting in an
>>>>>> effective negative offset or arbitrary write.
>>>>>>
>>>>> I understand your point, however what does happen when we enter the 'case
>>>>> TFTP_DATA' and we are in the first block received, so we trigger
>>>>> new_transfer() that sets the tftp_block_wrap_offset to 0 *and*
>>>>> tftp_mcast_active is set?
>>>>>
>>>>> I don't see any protection for this case for the underflow, am I wrong?
>>>>>
>>>>> static void new_transfer(void)
>>>>> {
>>>>>            tftp_prev_block = 0;
>>>>>            tftp_block_wrap = 0;
>>>>>            tftp_block_wrap_offset = 0;
>>>>> #ifdef CONFIG_CMD_TFTPPUT
>>>>>            tftp_put_final_block_sent = 0;
>>>>> #endif
>>>>> }
>>>>>
>>>>> ...
>>>>> case TFTP_DATA:
>>>>>
>>>>>                    if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
>>>>>                        tftp_state == STATE_RECV_WRQ) {
>>>>>                            /* first block received */
>>>>>                            tftp_state = STATE_DATA;
>>>>>                            tftp_remote_port = src;
>>>>>                            new_transfer();
>>>>>                            ^^^^^^^^^^^^^^^
>>>> See some lines below...
>>>>
>>>>> #ifdef CONFIG_MCAST_TFTP
>>>>>                            if (tftp_mcast_active) { /* start!=1 common if mcast */   <<<< HERE
>>>>>                                    tftp_prev_block = tftp_cur_block - 1;
>>>>>                            } else
>>>>> #endif
>>>>>                            if (tftp_cur_block != 1) {      /* Assertion */
>>>> If tftp_cur_block is 0 for the first block, we stop right away. No chance to
>>>> reach store_block() at that time.
>>>>
>>> CC'ing my colleague Daniele whom can better reply further on this.
>> Hi Simon,
>> the 'if (tftp_cur_block != 1)' is not triggered if 'tftp_mcast_active'
>> is set (and the CONFIG_MCAST_TFTP is defined).
>>
>> Please note the code indentation does not help in this case as it is
>> misleading, but this is because of the #ifdef.
> Ah, now I do see it, thanks for the hint! Indeed, the indentation of
> that else totally hid it from my eyes that the next block wasn't
> executed always!
>
> Luckily, searching through the whole mainline codebase shows no users of
> this option (CONFIG_MCAST_TFTP), so I guess this is not a real world
> problem, currently :-)

+ Joe

Getting better still: multicast tftp (CONFIG_MCAST_TFTP) does not 
compile and it's broken since changing from IPaddr_t (an u32) to struct 
in_addr four and a half years ago. So we're lucky that this definitively 
is not a real world problem!

Joe, should we remove CONFIG_MCAST_TFTP or fix it? Given that it hasn't 
been used more than 4 years?

Simon

>
> Thanks for your explanation and your fast response!
>
> Cheers,
> Simon
>
>> Cheers,
>> Daniele
>>
>>>    
>>>>>                                    puts("\nTFTP error: ");
>>>>>                                    printf("First block is not block 1 (%ld)\n",
>>>>>                                           tftp_cur_block);
>>>>>                                    puts("Starting again\n\n");
>>>>>                                    net_start_again();
>>>>>                                    break;
>>>>>                            }
>>>>>                    }
>>>>>
>>>>>                    if (tftp_cur_block == tftp_prev_block) {
>>>>>                            /* Same block again; ignore it. */
>>>>>                            break;
>>>>>                    }
>>>>>
>>>>>                    tftp_prev_block = tftp_cur_block;
>>>>>                    timeout_count_max = tftp_timeout_count_max;
>>>>>                    net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
>>>>>
>>>>>                    store_block(tftp_cur_block - 1, pkt + 2, len);
>>>>>                                ^^^^^^^^^^^^^^^^^^
>>>>> This should result in having -1 and thus -512 as result of the 'offset' math
>>>>> that converted to ulong would result in a very large value.
>>>>>
>>>>>>> }
>>>>>>>
>>>>>>> static void tftp_handler(...){
>>>>>>>
>>>>>>> case TFTP_DATA:
>>>>>>>             ...
>>>>>>>                     if (tftp_cur_block == tftp_prev_block) {
>>>>>>>                             /* Same block again; ignore it. */
>>>>>>>                             break;
>>>>>>>                     }
>>>>>>>
>>>>>>>                     tftp_prev_block = tftp_cur_block;
>>>>>>>                     timeout_count_max = tftp_timeout_count_max;
>>>>>>>                     net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
>>>>>>>
>>>>>>>                     store_block(tftp_cur_block - 1, pkt + 2, len);
>>>>>>>                                 ^^^^^^^^^^^^^^^^^^
>>>>>>> }
>>>>>>>
>>>>>>> For these reasons the issue does not appear to be merely a "one block size"
>>>>>>> substraction, but rather offset can reach very large values. Unless I am
>>>>>>> missing something that I don't see of course...
>>>>>> So I take it this "bug" report is from reading the code only, not from
>>>>>> actually testing it and seeing the arbitrary memory write? I wouldn't have
>>>>>> expected this in a CVE report...
>>>>>>
>>>>> As you see from our report the core issues have been fully tested and
>>>>> reproduced.
>>>> Yes. Thanks for that. I'm working on fixing them :-)
>>>>
>>> And that's much appreciated :)
>>>
>>>>> It is true however that the additional remark on the `store_block' function
>>>>> has only been evaluated by code analysis, in the context of the advisory it
>>>>> seemed something worth notice in relation to the code structure but again, as
>>>>> you say we didn't practically test that specific aspect, while everything
>>>>> else was tested and reproduced.
>>>>>
>>>>> The vulnerability report highlights two (in our opinion) critical
>>>>> vulnerabilities, one of which described a secondary aspect only checked by
>>>>> means of source code analysis.
>>>> In my opinion as well these are critical, yes.
>>>>
>>>>> The secondary aspect that we are discussing does not change the overall
>>>>> impact of the TFTP bugs, which remains unchanged as arbitrary code execution
>>>>> can anyway be achieved.
>>>> Of course. I'm working on fixing the actual bug and while debugging it tried
>>>> to fix the other thing you mentioned. I could not reproduce it in a test
>>>> setup (where I can freely send tftp packets). That's why I asked. The other
>>>> bugs are of course not affected by this one not being valid.
>>>>
>>> Understood.
>>>
>>> Cheers
>>>
>>>> Thanks for confirming this.
>>>>
>>>> Simon
>>>>
>>>>> Thanks!
>>>>>
>>>>>>> You should probably prevent the underflow by placing a check against
>>>>>>> tftp_cur_block before the store_block() invocation, but I defer to you for a
>>>>>>> better implementation of the fix as you certainly know the overall logic much
>>>>>>> better.
>>>>>> Don't get me wrong: I'm just yet another user of U-Boot and I don't know the
>>>>>> code better than you do. In fact, I looked at the tftp code for the first
>>>>>> time yesterday after reading you report on the tftp issue in detail.
>>>>>>
>>>>>>
>>>>>> Simon
>>> -- 
>>> Andrea Barisani     Head of Hardware Security |     F-Secure
>>>                                         Founder | Inverse Path
>>>
>>> https://www.f-secure.com             https://inversepath.com
>>> 0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
>>>          "Pluralitas non est ponenda sine necessitate"
>> --
>>     Daniele Bianco
>>     Hardware Security | F-Secure
>>
>>     <daniele.bianco@f-secure.com> | https://www.f-secure.com
>>     GPG Key fingerprint = 88A7 43F4 F28F 1B9D 6F2D  4AC5 AE75 822E 9544 A497
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities
  2018-11-14 19:07                 ` Simon Goldschmidt
@ 2018-11-14 23:36                   ` Joe Hershberger
  0 siblings, 0 replies; 23+ messages in thread
From: Joe Hershberger @ 2018-11-14 23:36 UTC (permalink / raw)
  To: u-boot

Hi Simon,
On Wed, Nov 14, 2018 at 1:07 PM Simon Goldschmidt
<simon.k.r.goldschmidt@gmail.com> wrote:
>
> On 14.11.2018 16:51, Simon Goldschmidt wrote:
> > On 14.11.2018 16:35, Daniele Bianco wrote:
> >> On Wed, Nov 14, 2018 at 04:26:17PM +0100, Andrea Barisani wrote:
> >>> On Wed, Nov 14, 2018 at 04:13:00PM +0100, Simon Goldschmidt wrote:
> >>>> On 14.11.2018 15:45, Andrea Barisani wrote:
> >>>>> On Wed, Nov 14, 2018 at 01:03:12PM +0100, Simon Goldschmidt wrote:
> >>>>>> On 14.11.2018 12:52, Andrea Barisani wrote:
> >>>>>>> On Tue, Nov 13, 2018 at 09:57:23PM +0100, Simon Goldschmidt wrote:
> >>>>>>>> On 06.11.2018 15:51, Andrea Barisani wrote:
> >>>>>>>>> [..]
> >>>>>>>>> The issue can be exploited by several means:
> >>>>>>>>>
> >>>>>>>>>        - An excessively large crafted boot image file is parsed by the
> >>>>>>>>>          `tftp_handler` function which lacks any size checks, allowing the memory
> >>>>>>>>>          overwrite.
> >>>>>>>>>
> >>>>>>>>>        - A malicious server can manipulate TFTP packet sequence numbers to store
> >>>>>>>>>          downloaded file chunks at arbitrary memory locations, given that the
> >>>>>>>>>          sequence number is directly used by the `tftp_handler` function to calculate
> >>>>>>>>>          the destination address for downloaded file chunks.
> >>>>>>>>>
> >>>>>>>>>          Additionally the `store_block` function, used to store downloaded file
> >>>>>>>>>          chunks in memory, when invoked by `tftp_handler` with a `tftp_cur_block`
> >>>>>>>>>          value of 0, triggers an unchecked integer underflow.
> >>>>>>>>>
> >>>>>>>>>          This allows to potentially erase memory located before the `loadAddr` when
> >>>>>>>>>          a packet is sent with a null, following at least one valid packet.
> >>>>>>>> Do you happen to have more details on this suggested integer underflow? I
> >>>>>>>> have tried to reproduce it, but I failed to get a memory write address
> >>>>>>>> before 'load_addr'. This is because the 'store_block' function does not
> >>>>>>>> directly use the underflowed integer as a block counter, but adds
> >>>>>>>> 'tcp_block_wrap_offset' to this offset.
> >>>>>>>>
> >>>>>>>> To me it seems like alternating between '0' and 'not 0' for the block
> >>>>>>>> counter could increase memory overwrites, but I fail to see how you can use
> >>>>>>>> this to store chunks at arbitrary memory locations. All you can do is
> >>>>>>>> subtract one block size from 'tftp_block_wrap_offset'...
> >>>>>>>>
> >>>>>>>> Simon
> >>>>>>>>
> >>>>>>> Hello Simon,
> >>>>>>>
> >>>>>>> the integer underflow can happen if a malicious TFTP server, able to control
> >>>>>>> the TFTP packets sequence number, sends a crafted packet with sequence number
> >>>>>>> set to 0 during a flow.
> >>>>>>>
> >>>>>>> This happens because, within the store_block() function, the 'block' argument
> >>>>>>> is declared as 'int' and when it is invoked inside tftp_handler() (case
> >>>>>>> TFTP_DATA) this value is passed by doing 'tftp_cur_block - 1' (where
> >>>>>>> tftp_cur_block is the sequence number extracted from the tftp packet without
> >>>>>>> any previous check):
> >>>>>>>
> >>>>>>> static inline void store_block(int block, uchar *src, unsigned len)
> >>>>>>>                                    ^^^^^^^^^ can have negative values (e.g.  -1)
> >>>>>>> {
> >>>>>>>             ulong offset = block * tftp_block_size + tftp_block_wrap_offset;
> >>>>>>>             ^^^^^
> >>>>>>>             here if block is -1 the result stored onto offset would be a very
> >>>>>>>             large unsigned number, due to type conversions
> >>>>>> And this is exatclty my point. This might be bad coding style, but for me it
> >>>>>> works: 'block' is an 'int' and is '-1', so 'block * tftp_block_size' is
> >>>>>> '-512'. Now from the code flow in tftp_handler(), it's clear that if we come
> >>>>>> here with tftp_cur_block == 0 (so 'block' is -1), 'tftp_block_wrap_offset'
> >>>>>> is not 0 but some positive value 'x * tftp_block_size' (see function
> >>>>>> 'update_block_number').
> >>>>>>
> >>>>>> So the resulting 'offset' is '-512 + (x * 512)' where 'x > 0'. I still fail
> >>>>>> to see how this can be a very large positive number resulting in an
> >>>>>> effective negative offset or arbitrary write.
> >>>>>>
> >>>>> I understand your point, however what does happen when we enter the 'case
> >>>>> TFTP_DATA' and we are in the first block received, so we trigger
> >>>>> new_transfer() that sets the tftp_block_wrap_offset to 0 *and*
> >>>>> tftp_mcast_active is set?
> >>>>>
> >>>>> I don't see any protection for this case for the underflow, am I wrong?
> >>>>>
> >>>>> static void new_transfer(void)
> >>>>> {
> >>>>>            tftp_prev_block = 0;
> >>>>>            tftp_block_wrap = 0;
> >>>>>            tftp_block_wrap_offset = 0;
> >>>>> #ifdef CONFIG_CMD_TFTPPUT
> >>>>>            tftp_put_final_block_sent = 0;
> >>>>> #endif
> >>>>> }
> >>>>>
> >>>>> ...
> >>>>> case TFTP_DATA:
> >>>>>
> >>>>>                    if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
> >>>>>                        tftp_state == STATE_RECV_WRQ) {
> >>>>>                            /* first block received */
> >>>>>                            tftp_state = STATE_DATA;
> >>>>>                            tftp_remote_port = src;
> >>>>>                            new_transfer();
> >>>>>                            ^^^^^^^^^^^^^^^
> >>>> See some lines below...
> >>>>
> >>>>> #ifdef CONFIG_MCAST_TFTP
> >>>>>                            if (tftp_mcast_active) { /* start!=1 common if mcast */   <<<< HERE
> >>>>>                                    tftp_prev_block = tftp_cur_block - 1;
> >>>>>                            } else
> >>>>> #endif
> >>>>>                            if (tftp_cur_block != 1) {      /* Assertion */
> >>>> If tftp_cur_block is 0 for the first block, we stop right away. No chance to
> >>>> reach store_block() at that time.
> >>>>
> >>> CC'ing my colleague Daniele whom can better reply further on this.
> >> Hi Simon,
> >> the 'if (tftp_cur_block != 1)' is not triggered if 'tftp_mcast_active'
> >> is set (and the CONFIG_MCAST_TFTP is defined).
> >>
> >> Please note the code indentation does not help in this case as it is
> >> misleading, but this is because of the #ifdef.
> > Ah, now I do see it, thanks for the hint! Indeed, the indentation of
> > that else totally hid it from my eyes that the next block wasn't
> > executed always!
> >
> > Luckily, searching through the whole mainline codebase shows no users of
> > this option (CONFIG_MCAST_TFTP), so I guess this is not a real world
> > problem, currently :-)
>
> + Joe
>
> Getting better still: multicast tftp (CONFIG_MCAST_TFTP) does not
> compile and it's broken since changing from IPaddr_t (an u32) to struct
> in_addr four and a half years ago. So we're lucky that this definitively
> is not a real world problem!
>
> Joe, should we remove CONFIG_MCAST_TFTP or fix it? Given that it hasn't
> been used more than 4 years?

Seems reasonable to remove MCAST_TFTP.

Cheers,
-Joe

> Simon
>
> >
> > Thanks for your explanation and your fast response!
> >
> > Cheers,
> > Simon
> >
> >> Cheers,
> >> Daniele
> >>
> >>>
> >>>>>                                    puts("\nTFTP error: ");
> >>>>>                                    printf("First block is not block 1 (%ld)\n",
> >>>>>                                           tftp_cur_block);
> >>>>>                                    puts("Starting again\n\n");
> >>>>>                                    net_start_again();
> >>>>>                                    break;
> >>>>>                            }
> >>>>>                    }
> >>>>>
> >>>>>                    if (tftp_cur_block == tftp_prev_block) {
> >>>>>                            /* Same block again; ignore it. */
> >>>>>                            break;
> >>>>>                    }
> >>>>>
> >>>>>                    tftp_prev_block = tftp_cur_block;
> >>>>>                    timeout_count_max = tftp_timeout_count_max;
> >>>>>                    net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
> >>>>>
> >>>>>                    store_block(tftp_cur_block - 1, pkt + 2, len);
> >>>>>                                ^^^^^^^^^^^^^^^^^^
> >>>>> This should result in having -1 and thus -512 as result of the 'offset' math
> >>>>> that converted to ulong would result in a very large value.
> >>>>>
> >>>>>>> }
> >>>>>>>
> >>>>>>> static void tftp_handler(...){
> >>>>>>>
> >>>>>>> case TFTP_DATA:
> >>>>>>>             ...
> >>>>>>>                     if (tftp_cur_block == tftp_prev_block) {
> >>>>>>>                             /* Same block again; ignore it. */
> >>>>>>>                             break;
> >>>>>>>                     }
> >>>>>>>
> >>>>>>>                     tftp_prev_block = tftp_cur_block;
> >>>>>>>                     timeout_count_max = tftp_timeout_count_max;
> >>>>>>>                     net_set_timeout_handler(timeout_ms, tftp_timeout_handler);
> >>>>>>>
> >>>>>>>                     store_block(tftp_cur_block - 1, pkt + 2, len);
> >>>>>>>                                 ^^^^^^^^^^^^^^^^^^
> >>>>>>> }
> >>>>>>>
> >>>>>>> For these reasons the issue does not appear to be merely a "one block size"
> >>>>>>> substraction, but rather offset can reach very large values. Unless I am
> >>>>>>> missing something that I don't see of course...
> >>>>>> So I take it this "bug" report is from reading the code only, not from
> >>>>>> actually testing it and seeing the arbitrary memory write? I wouldn't have
> >>>>>> expected this in a CVE report...
> >>>>>>
> >>>>> As you see from our report the core issues have been fully tested and
> >>>>> reproduced.
> >>>> Yes. Thanks for that. I'm working on fixing them :-)
> >>>>
> >>> And that's much appreciated :)
> >>>
> >>>>> It is true however that the additional remark on the `store_block' function
> >>>>> has only been evaluated by code analysis, in the context of the advisory it
> >>>>> seemed something worth notice in relation to the code structure but again, as
> >>>>> you say we didn't practically test that specific aspect, while everything
> >>>>> else was tested and reproduced.
> >>>>>
> >>>>> The vulnerability report highlights two (in our opinion) critical
> >>>>> vulnerabilities, one of which described a secondary aspect only checked by
> >>>>> means of source code analysis.
> >>>> In my opinion as well these are critical, yes.
> >>>>
> >>>>> The secondary aspect that we are discussing does not change the overall
> >>>>> impact of the TFTP bugs, which remains unchanged as arbitrary code execution
> >>>>> can anyway be achieved.
> >>>> Of course. I'm working on fixing the actual bug and while debugging it tried
> >>>> to fix the other thing you mentioned. I could not reproduce it in a test
> >>>> setup (where I can freely send tftp packets). That's why I asked. The other
> >>>> bugs are of course not affected by this one not being valid.
> >>>>
> >>> Understood.
> >>>
> >>> Cheers
> >>>
> >>>> Thanks for confirming this.
> >>>>
> >>>> Simon
> >>>>
> >>>>> Thanks!
> >>>>>
> >>>>>>> You should probably prevent the underflow by placing a check against
> >>>>>>> tftp_cur_block before the store_block() invocation, but I defer to you for a
> >>>>>>> better implementation of the fix as you certainly know the overall logic much
> >>>>>>> better.
> >>>>>> Don't get me wrong: I'm just yet another user of U-Boot and I don't know the
> >>>>>> code better than you do. In fact, I looked at the tftp code for the first
> >>>>>> time yesterday after reading you report on the tftp issue in detail.
> >>>>>>
> >>>>>>
> >>>>>> Simon
> >>> --
> >>> Andrea Barisani     Head of Hardware Security |     F-Secure
> >>>                                         Founder | Inverse Path
> >>>
> >>> https://www.f-secure.com             https://inversepath.com
> >>> 0x864C9B9E 0A76 074A 02CD E989 CE7F AC3F DA47 578E 864C 9B9E
> >>>          "Pluralitas non est ponenda sine necessitate"
> >> --
> >>     Daniele Bianco
> >>     Hardware Security | F-Secure
> >>
> >>     <daniele.bianco@f-secure.com> | https://www.f-secure.com
> >>     GPG Key fingerprint = 88A7 43F4 F28F 1B9D 6F2D  4AC5 AE75 822E 9544 A497
> >
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2018-11-14 23:36 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-06 14:51 [U-Boot] CVE-2018-18439, CVE-2018-18440 - U-Boot verified boot bypass vulnerabilities Andrea Barisani
2018-11-09  0:37 ` Fabio Estevam
2018-11-09  6:11   ` Simon Goldschmidt
2018-11-09  9:46     ` Andrea Barisani
2018-11-09 10:24       ` Simon Goldschmidt
2018-11-09 21:25         ` Simon Goldschmidt
2018-11-09 22:14           ` Fabio Estevam
2018-11-11 14:22       ` Wolfgang Denk
2018-11-11 23:21         ` Heinrich Schuchardt
2018-11-12  6:56           ` Simon Goldschmidt
2018-11-12 18:03             ` Heinrich Schuchardt
2018-11-12 18:58               ` Simon Goldschmidt
2018-11-12  8:00           ` Wolfgang Denk
2018-11-13 20:57 ` Simon Goldschmidt
2018-11-14 11:52   ` Andrea Barisani
2018-11-14 12:03     ` Simon Goldschmidt
2018-11-14 14:45       ` Andrea Barisani
2018-11-14 15:13         ` Simon Goldschmidt
2018-11-14 15:26           ` Andrea Barisani
2018-11-14 15:35             ` Daniele Bianco
2018-11-14 15:51               ` Simon Goldschmidt
2018-11-14 19:07                 ` Simon Goldschmidt
2018-11-14 23:36                   ` Joe Hershberger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.