- PVSM.RU - https://www.pvsm.ru -

Анализ утёкших паролей Gmail, Yandex и Mail.ru

Совсем недавно в публичный доступ попали базы паролей популярных почтовых сервисов [1 [1],2 [2],3 [3]] и сегодня мы их проанализируем и ответим на ряд вопросов о качестве паролей и возможном источнике (или источниках). Так же мы обсудим метрики качества отдельных паролей и всей выборки.

Не менее интересными являются некоторые аномалии и закономерности баз паролей, возможно, они смогут пролить свет на то, что могло служить источником данных и насколько данная выборка является опасной с точки зрения обычного пользователя.

Формально, мы рассмотрим следующие вопросы: насколько надежными являются пароли в базе и могли ли они быть собраны словарной атакой? Есть ли признаки фишинговых атак? Могла ли «утечка» данных быть единственным источником данных? Могла ли данная база быть аккумулирована в течении длительного периода или данные исключительно «свежие»?

Структура статьи:

  1. Описание данных [4]
  2. Невалидные пароли и не-пароли [5]
  3. Распределение длины паролей [6]
  4. Распределение надёжности паролей [7]
  5. Словарная атака [8]
  6. Топ паролей [9]
  7. Выборка Gmail [10]
  8. Выборка Rambler [11]
  9. Анализ открытых источников [12]
  10. Заключение [13]

Описание данных


Данные из всех трех баз представляют собой набор пар адрес-пароль, разделенные двоеточием. Никаких других «мета-данных» недоступно. Однако данные достаточно зашумленные т.е. в них присутствуют строки не являющимися ни адресами почты, ни допустимыми паролями.
Анализ утёкших паролей Gmail, Yandex и Mail.ru

Если мы исследуем особенности данных, то сможем выдвинуть (или опровергнуть) гипотезу о том, в результате какого процесса пароли могли быть получены.

Невалидные пароли и не-пароли


Самые простой критерий невалидности пароля — несоответствие длины пароля требованиям почтовых сервисов.
Анализ утёкших паролей Gmail, Yandex и Mail.ru
Полученные данные говорят, что пароли из выборки не могли быть получены в результате «внутренней» утечки, так как несколько тысяч паролей не являются валидными паролями в принципе из-за ограничений на длину пароля в шесть символов (а для современных паролей gmail в восемь символов).

Рассмотрим эти аномально длинные (более 60) и короткие пароли (менее 6) в деталях.

Примеры

Длинные пароли представляют собой куски HTML-кода, один из репрезентативных примеров:
Анализ утёкших паролей Gmail, Yandex и Mail.ru
Подобные примеры указывают, что одним из источников паролей мог быть фишинг. Запись в базе явно не была проверена человеком и получена автоматически, на фишинг так же указывает тот факт, что в пароле присутствует html-разметка, что довольно нетипично для кражи пароля через заражение.

Краткая выборка слишком коротких паролей:
Анализ утёкших паролей Gmail, Yandex и Mail.ru

Еще один индикатор того, что одним из источников мог быть фишинг — отсутствие логина и пароля в записях. Особенно интересно выглядит апостроф без указания пароля. Возможно, потенциальная жертва догадалась о фишинговой форме и попыталась проверить наличие SQL инъекции.

Что можно однозначно утверждать по проверенным данным? Автоматической валидации базы не происходило. Наиболее вероятные гипотезы: фишинг и заражение вирусом.

Для того, чтобы оценить качество всей выборки, мы удалим из неё заведомо неверные пароли длины меньше 6 и больше 60 и рассмотрим всё распределение в целом по нескольким параметрам.

Распределение длины паролей


Как видно из графика ниже, большая часть паролей имеет длину в 8 или менее символов. Что может указывать на то, что существенный пласт паролей потенциально неустойчив к различному виду атак переборных атак.
Анализ утёкших паролей Gmail, Yandex и Mail.ru

Распределение надёжности паролей


Для того, чтобы проверить эту гипотезу, рассмотрим простую метрику надежности пароля основанную на
стандарте PCI [14].
Пусть за удовлетворение одного из следующих условий пароль получает условный балл:

  • пароль содержит не менее 7ми символов;
  • пароль содержит хотя бы одну строчную букву;
  • пароль содержит хотя бы одну прописную букву;
  • пароль содержит хотя бы одну цифру;
  • пароль содержит хотя бы один специальный символ.

Если пароль получает 4/5, то мы называем его надежным (очень надежным за 5/5), соответственно 3/5 назовем средним, а 2/5 слабым (0 или 1 балл назовем очень слабым). Код на языке R приведен ниже.

Функция надежности

library("Hmisc")
strength <- function(password){
  # must contain at least 7 characters
  score = 0
  if (nchar(password) >= 7){
    inc(score) <- 1
  }
  # at least one digit
  if(grepl("[[:digit:]]", password)){
    inc(score) <- 1
  }
  # at least one lowercase letter 
  if(grepl("[[:lower:]]", password)){
    inc(score) <- 1
  }
  # at least one uppercase letter 
  if(grepl("[[:upper:]]", password)){
    inc(score) <- 1
  }
  # at least one special symbol
  if(grepl("[#!?^@*+&%]", password)){
    inc(score) <- 1
  }
  # 0-1 very weak
  # 2 - weak
  # 3 - medium
  # 4 - strong
  # 5 - very strong
  return(score)
}

Тогда распределение надёжности имеет вид:
Анализ утёкших паролей Gmail, Yandex и Mail.ru
Как видно из графика большинство паролей попадают в категорию не-надежные. В качестве примера рассмотрим пароли нулевой надёжности, так как скорее всего это ещё один репрезентативный пример невалидных паролей.

Пароли нулевой надёжности

Анализ утёкших паролей Gmail, Yandex и Mail.ru
Как видно из примеров выше, данные пароли не являются валидными (и с точки зрения человека выглядят скорее ошибкой ввода, чем действительным паролем), так как почтовые сервисы не дают зарегистрировать ящик, если считают пароль слишком простым, например, повторением одного и того же символа шесть раз. А значит, что возможно ещё больший пласт паролей не является валидным согласно современным требованиям.

Возможно, что существенная часть базы собрана в течении длительного периода времени, когда требования к паролям были мягче? Иначе довольно сложно объяснить столь большую группу паролей, не соответствующих требованиям современных почтовых систем.

Словарная атака


В качестве дополнительного аргумента проведем следующий эксперимент: возьмём выборку релевантных словарей паролей из общего доступа, проведем атаку на доступные пароли по этим словарям и оценим какой процент паролей содержится в этой выборке словарей (автор буквально не уходил дальше первых трёх ссылок гугла по запросу [password dictionary]).
Анализ утёкших паролей Gmail, Yandex и Mail.ru
Из таблицы выше видно, что существенная доля паролей содержится в словарях, что так же указывает на то, что часть паролей могла быть получена в результате словарной атаки (или какой-то модификации перебора).

Топ паролей


Приведем подборку наиболее популярных паролей и заметим, что большая часть сейчас не является допустимыми паролями.
Анализ утёкших паролей Gmail, Yandex и Mail.ru

Выборка Gmail


Действия и данные, описанные и полученные в данной и следующей части, были произведены и переданы другом моего друга, пожелавшего остаться неизвестным.

Задача: проверить валидность (т.е. что пароль действительно подходит) паролей. Действие: по небольшой выборке из ~150-200 попробовать получить доступ к ящикам. Из всей выборки в принципе валидными являются ~2-3% (через несколько часов появления данных в открытом доступе), и фактически все являются деактивированными на момент проверки. Реально действующими являлись менее 1% ящиков и те заброшены владельцами по крайней мере в течении года.

Выборка Rambler


Несложно обнаружить в сети списки «действительно валидных» адресов, составленных широким кругом заинтересованных лиц (ака кулхацкеры).
Анализ утёкших паролей Gmail, Yandex и Mail.ru
Что интересно, среди них довольно большой процент адресов рамблера.

Rambler был предупрежден за несколько дней до публикации и был получен ответ, что необходимые меры безопасности будут приняты в ближайшее время.

<юмор>Анализ утёкших паролей Gmail, Yandex и Mail.ru</юмор>

Что интересно, процент валидных паролей существенно выше и до последнего времени rambler был вне медийного поля событий и не активизировал дополнительных систем безопасности.

Это позволило неизвестному антропологу утечки оценить последние моменты жизни почтовых ящиков. Несмотря на валидность паролей, все ящики являлись заброшенными в течении долгого времени (~1-1.5 года) и заканчивались одним из подобных писем:
Анализ утёкших паролей Gmail, Yandex и Mail.ru
Что является еще одним подтверждением гипотезы о фишинге и кумулятивной природе базы.

Анализ открытых источников


Вернемся к рассмотрению открытых источников. Активный поиск по паролям-логинам, привел нас к ряду раздач с геймерских форумов:
Анализ утёкших паролей Gmail, Yandex и Mail.ru
Оказывается, что часть списка уже в какой-то форме гуляла по сети.

Таким образом данные позволяют отвергнуть гипотезу о единственном источнике данных таком как «внутренняя утечка».

Основная часть используемого кода:

Анализ данных и визуализация

source("multiplot.R")
source("password_strength.R")
library("ggplot2")
print("loading yandex data")
yandex <- read.csv("yandex.txt", header = FALSE, sep = ":", quote = "", stringsAsFactors = FALSE)
print("loading mailru data")
mailru <- read.csv("mail.txt",   header = FALSE, sep = ":", quote = "", stringsAsFactors = FALSE)
print("loading gmail data")
gmail  <- read.csv("gmail.txt",  header = FALSE, sep = ":", quote = "", stringsAsFactors = FALSE)
##testing if data loaded correctly
print("testing, if loaded correctly")
print(head(yandex))
print(head(mailru))
print(head(gmail))
##changing names
names(yandex) <- c("email", "password")
names(mailru) <- c("email", "password")
names(gmail)  <- c("email", "password")
print("computing lengths of passwords and adding to the datasets")
yandex$pass_length   <- sapply(yandex$password, nchar)
mailru$pass_length   <- sapply(mailru$password, nchar)
gmail$pass_length    <- sapply(gmail$password,  nchar)
print("number of invalid passwords by length")
print(nrow(yandex[yandex$pass_length <  6,]))
print(nrow(yandex[yandex$pass_length >  60,]))
print(nrow(mailru[mailru$pass_length <  6,]))
print(nrow(mailru[mailru$pass_length >  60,]))
print(nrow(gmail[gmail$pass_length <  6,]))
print(nrow(gmail[gmail$pass_length >  60,]))
print("removing invalid passwords by length")
yandex <- subset(yandex, pass_length >= 6 & pass_length <= 60)
mailru <- subset(mailru, pass_length >= 6 & pass_length <= 60)
gmail  <- subset(gmail , pass_length >= 6 & pass_length <= 60)
#print("checking that they are removed")
print(nrow(yandex[yandex$pass_length <  6,]))
print(nrow(yandex[yandex$pass_length >  60,]))
print(nrow(mailru[mailru$pass_length <  6,]))
print(nrow(mailru[mailru$pass_length >  60,]))
print(nrow(gmail[gmail$pass_length <  6,]))
print(nrow(gmail[gmail$pass_length >  60,]))
print("visualizing distribution of password lenghts by provider")
gmailcolor  <- "deepskyblue"
yandexcolor <- "orangered1"
mailrucolor <- "limegreen"
 pgmail <- ggplot(data=gmail, aes(x=pass_length)) + scale_x_discrete(limits=seq(6, 20, 1), breaks=seq(6, 20, 1), drop=TRUE) + geom_histogram(colour="black", fill=gmailcolor, aes(y=..density..)) + coord_cartesian(xlim=c(5,21.5)) + xlab(expression("Длина пароля"))+ ylab(expression("Доля"))+ggtitle("Gmail")
 pyandex <- ggplot(data=yandex, aes(x=pass_length)) + scale_x_discrete(limits=seq(6, 21, 1), breaks=seq(6, 21, 1), drop=TRUE) + geom_histogram(colour="black", fill=yandexcolor, aes(y=..density..)) + coord_cartesian(xlim=c(5,21.5)) + xlab(expression("Длина пароля"))+ ylab(expression("Доля"))+ggtitle("Yandex")     
 pmailru <- ggplot(data=mailru, aes(x=pass_length)) + scale_x_discrete(limits=seq(6, 20, 1), breaks=seq(6, 20, 1), drop=TRUE) + geom_histogram(colour="black", fill=mailrucolor, aes(y=..density..)) + coord_cartesian(xlim=c(5,20.5)) + xlab(expression("Длина пароля"))+ ylab(expression("Доля"))+ggtitle("Mail.ru")     
 multiplot(pgmail, pyandex, pmailru, cols=3)

print("computing strength of the passwords")
yandex$strength <- sapply(yandex$password, strength)
mailru$strength <- sapply(mailru$password, strength)
gmail$strength  <- sapply(gmail$password,  strength)
 print(head(yandex))
 print(head(mailru))
 print(head(gmail))
scale <- scale_x_discrete(limits=c(1,2,3,4,5), breaks=c(1,2,3,4,5), drop=TRUE, labels=c("Оченьnслабый", "Слабый", "Средний", "Надежный", "Оченьnнадежный"))
pgmail <- ggplot(data=gmail  , aes(factor(strength))) + geom_bar(colour="black", fill=gmailcolor) + xlab(expression("Надежность"))+ coord + ylab(expression("Доля"))+ggtitle("Gmail") + scale
pyandex <- ggplot(data=yandex, aes(factor(strength))) + geom_bar(colour="black", fill=yandexcolor, binwidth=0.5) + xlab(expression("Надежность"))+ coord + ylab(expression("Доля"))+ggtitle("Yandex") + scale
pmailru <- ggplot(data=mailru, aes(factor(strength))) + geom_bar(colour="black", fill=mailrucolor, binwidth=0.5) + xlab(expression("Надежность"))+ coord + ylab(expression("Доля"))+ggtitle("Mail.ru") + scale    
multiplot(pgmail, pyandex, pmailru, cols=3)
 print("Zero strength passwords")
 print("GMAIL")
 print(head(gmail[gmail$strength == 0,]))
 print("YANDEX")
 print(head(yandex[yandex$strength == 0,]))
 print("MAILRU")
 print(head(mailru[mailru$strength == 0,]))

table_gmail  <- sort(table(gmail$password) , TRUE)
table_yandex <- sort(table(yandex$password), TRUE)
table_mailru <- sort(table(mailru$password), TRUE)

print("gmail most frequent")
print(head(table_gmail, 100))
print("yandex most frequent")
print(head(table_yandex,100))
print("mailru most frequent")
print(head(table_mailru,100))

only_pass_gmail  <- gmail[ ,2] 
write.csv(only_pass_gmail,  "only_pass_gmail",  row.names = FALSE)
only_pass_yandex <- yandex[,2] 
write.csv(only_pass_yandex, "only_pass_yandex", row.names = FALSE)
only_pass_mailru <- mailru[,2] 
write.csv(only_pass_mailru, "only_pass_mailru", row.names = FALSE)

Код эксперимента 'словарная атака'

#!/bin/bash
data=sample_mailru
dict=saved_dict_mailru
> $dict
j=0
while read p; do
  ((j++))
  echo -n $j
  if grep -q "^$p$" dictionary/*; then
    echo " in "
    echo $p >> $dict
  else
    echo " out " 
  fi
  if (("$j" > 10000)); then
    break
  fi
done <$data

Заключение


Таким образом наиболее вероятной выглядит гипотеза, что данная выборка — компиляция различных источников (фишинг, заражение, словарно-переборные атаки, собрание популярных подборок) в течении длительного периода времени. Достаточная часть данных в принципе не является валидными паролями по формальным синтаксическим критериям, что также подтвердила экспериментальная проверка.

С точки зрения пользователя данное событие не несет существенной опасности и скорее выглядит попыткой создания инфоповода.

Автор: varagian

Источник [15]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/analiz-danny-h/69546

Ссылки в тексте:

[1] 1: http://habrahabr.ru/post/235949/

[2] 2: http://habrahabr.ru/post/236077/

[3] 3: http://habrahabr.ru/post/236283/

[4] Описание данных: #data

[5] Невалидные пароли и не-пароли: #valid

[6] Распределение длины паролей: #length

[7] Распределение надёжности паролей: #stength

[8] Словарная атака: #dictionary

[9] Топ паролей: #top

[10] Выборка Gmail: #sampleGmail

[11] Выборка Rambler: #sampleRambler

[12] Анализ открытых источников: #sources

[13] Заключение: #conclusions

[14] стандарте PCI: https://limoanywhere.uservoice.com/knowledgebase/articles/170461-what-are-the-password-requirements-for-pci-complia

[15] Источник: http://habrahabr.ru/post/236759/