Analysis of the US-based Cybersecurity & Infrastructure Security Agency (CISA) Known Exploited Vulnerabilities (KEV) catalogue.
Some sources use the KEV as a source of absolute truth regarding software security. There are significant issues with the KEV, however, which are well described in this medium post.
Therefore no conclusions are possible solely with the KEV data and take any results shown here with a large grain of salt.
Although the data is presented as well structured, it has a lot of free-text in the columns which needs to be parsed to get the required information.
The aim here is to primarily focus on iOS and Android, which also raises some issues. Due to its open source nature Android is not owned by a single organisation, although Google is the primary supporter of the core system. Thus, the ‘shortDescription’ column needs to be searched for Android relevance.
For iOS, most vulnerabilities are resolved by Apple across all platforms at the same time where relevant so again searches need to be made of the ‘shortDescription’ column. The problem with that is that ‘iOS’ can also match to ‘FortiOS’, Cicso’s ‘IOS’ and ‘BIOS’ strings.
A big distinction regarding severity of vulnerabilities is whether they are exploitable remotely or otherwise. The KEV download doesn’t mark these up, however, and a keyword search for ‘remote’ seems to be sufficient.
# add column to identify which vulnerabilities are remotely executable or not
dat = dat %>% mutate(remote = ifelse(str_count(shortDescription,"remote") > 0, TRUE, FALSE))
# reorder factor to make colours more sensible
dat$remote <- factor(dat$remote, levels = c(TRUE, FALSE))
# summarise counts by vendor
summ.df = dat %>% group_by(vendorProject, remote) %>%
summarise(n = n()) %>%
filter(n > 5)
ggplot(summ.df, aes(x = fct_reorder(vendorProject, n), y = n, fill = remote)) +
geom_col() +
scale_fill_brewer(palette = 'Paired') +
labs(
title = 'Total Exploited Vulnerabilities Since 2021',
subtitle = 'Spilt by whether remotely exploitable or not',
x = '',
y = 'Count',
fill = 'Remotely exploitble',
caption = 'Source: CISA (n < 5 omitted)'
) +
coord_flip() +
theme_classic()
Apple
# For all Apple entries find any which mention 'iOS'.
apple.df = dat %>% filter(vendorProject == 'Apple') %>%
filter(str_detect(shortDescription, "iOS")) %>%
group_by(format(dueDate, "%Y")) %>%
summarise(n = n())
colnames(apple.df) <- c('Year', 'Count')
knitr::kable(apple.df, format = "html", table.attr = "style='width:30%;'") %>%
kableExtra::kable_styling(position = "left")
| Year | Count |
|---|---|
| 2021 | 14 |
| 2022 | 26 |
| 2023 | 21 |
| 2024 | 7 |
| 2025 | 7 |
Android
# For all Android entries find any which mention 'iOS'.
android.df = dat %>% filter(vendorProject == 'Android' | str_detect(shortDescription, "Android")) %>%
group_by(format(dueDate, "%Y")) %>%
summarise(n = n())
colnames(android.df) <- c('Year', 'Count')
knitr::kable(android.df,
col.names = c('Year', 'Count'),
format = "html",
table.attr = "style='width:30%;'") %>%
kableExtra::kable_styling(position = "left")
| Year | Count |
|---|---|
| 2021 | 1 |
| 2022 | 7 |
| 2023 | 5 |
| 2024 | 6 |
| 2025 | 1 |
apple.df = data.frame(OS = 'iOS', apple.df)
android.df = data.frame(OS = 'Android', android.df)
all.df = rbind(apple.df, android.df)
pl = ggplot(all.df, aes(x = Year, y = Count, group = OS, colour = OS)) +
geom_line(linewidth = 1.3) +
geom_point(colour = 'black', size = 1.8) +
geom_point(colour = 'white', size = 1.4) +
labs(title = "iOS vs Android Vulnerabilities") +
theme_classic()
ggplotly(pl)