D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Как баг (или фича) GitHub был использован для заражения любителей читов с помощью LUA зверя
Злоумышленники использовали фейк оригинального сайта читов, «отравление поисковой выдачи» и баг (фичу) Гитхаба, чтобы обмануть начинающих читеров для установки их LUA зверя. Наша (ресерчерей) заметка разделена на две части: первая опишет баг, который помог доставить пейлоад, вторая часть - попытка анализа Lua JIT малвары.
Спасибо:
Игровой чит, которым воспользовались в атаке, это оупенсорс aim-бот под названием AIMMY, расположенный в следующем репо Гитхаба: Babyhamsta/Aimmy и с веб-сайтом aimmy.dev.
Злоумышленники клонировали оригинальный репо сюда -
В поисковой выдаче вредоносный сайт был размещен выше настоящего сайта aim-бота (вероятно из-за «отравления поисковой выдачи»). Пользователи. которые ищут aim-бот будут вероятно попадать на вредоносный сайт и загружать малвару.
3. Баг Гитхаба (или фича).
Ключевой элемент успеха атаки - это хостинг пейлоада в настоящем репо. Таким образом злоумышленники дистанциируются от хостинга пейлоада и могут успешно скрыть ссылку загрузки в нормальном трафике. Использование данного трюка делает сложным для жертвы понять то, что на самом деле она нажимает на зловредную ссылку. Но как злоумышленникам удается разместить их пейлоад в чужом репо? Мы были в реальном затруднении до того, как @JustasMasiulis обнаружил баг Гитхаба … или фичу?
При открытии тикета проблемы (issue) в репо - любой файл, прикрепленный к тикету будет размещаться в том же репо Гитхаба, где был открыт тикет проблемы. Файлы сохраняются, если даже сам тикет проблемы не был сохранен. Это означает, что любой человек может загрузить любой файл в любой гит репо на Гитхабе и не оставить никаких следов, что этот файл существует за исключением прямой ссылки на него.
https://twitter.com/x/status/1764171634469122165
4. Клонирование вредоносного репо.
Злоумышленники использовали умный способ, чтобы скрыть факт того, что они клонировали оригинальный репо Гитхаба. ArsTechnica недавно публиковала разбор похожих зловредных атак клонирования, но так как в данном случае мы наблюдали всю атаку в режиме реального времени, мы можем добавить некоторые детали:
Зверь сам по себе довольно уникальный. Он состоит из файла Lua JIT file, собранного из LuaJIT исполняемого файла, использованного для интерпретации JIT файла, и batch скрипт для повышения прав и запуска JIT в интерпретаторе.
Сэмпл
Малвара доставляется в malshare ZIP-архиве
Используя LuaJIT Decompiler v2 у нас получилось декомпилировать JIT и восстановить код Lua, только чтобы найти, что он был тяжело обфусцирован.
Код: Скопировать в буфер обмена
Для обфускации использовался Lua обфускатор с открытым кодом под названием Prometheus. Этот бот обфускатора шифрует строки и использует виртуальную машину для защиты кода Lua.
Инструментарий окружения Lua
Вместо того чтобы пытаться взломать виртуальную машину, мы попробовали динамически проследить за кодом Lua. Так как зловредный код Lua передал аргумент интерпретатору LuaJIT, мы сначала попробовали воссоздать окружение Lua, подгружая код трассировки до запуска JIT, концепт основанный на идеях Ника из блога - Hooking LuaJIT.
Мы использовали следующий скрипт вместе с
Код: Скопировать в буфер обмена
Это имело ограниченный успех, потому что в виртуальной машине была функция защиты от несанкционированного доступа (которая по итогу была обойдена Jollyc) и у меня возникли сложности... главным образом по тому, что я не знаю ничего о Lua и работа над ошибками занимала много времени.
Мусор Lua - избавляемся от зашифрованных строк
Эта идея принадлежит Jolyc, Fishy-Sticks и 0AVX. Как Fishy-Sticks сказал об этом...
Хук lj_str_free
Функция
Мы добавим простой код и перекомпилируем:
Код: Скопировать в буфер обмена
Дополнительные хуки могут быть добавлены в
Код: Скопировать в буфер обмена
Поскольку многие строковые операторы приводят к выделению в память частичных строк, в дампе много шума, но мы смогли восстановить полные структуры, определения функций, а также строки, используемые о связи малвары с C2 и ее удержания в системе. Что еще можно сделать — объединить этот подход с более классической трассировкой API, чтобы обеспечить более целостное представление о вредоносном ПО.
Источник: https://research.openanalysis.net/github/lua/2024/03/03/lua-malware.html
- Обзор техники.
- Доставка пейлоада.
- Баг (фича) GitHub.
- Клонирование вредоносного репо.
- Анализ LUA зверя.
Злоумышленники использовали фейк оригинального сайта читов, «отравление поисковой выдачи» и баг (фичу) Гитхаба, чтобы обмануть начинающих читеров для установки их LUA зверя. Наша (ресерчерей) заметка разделена на две части: первая опишет баг, который помог доставить пейлоад, вторая часть - попытка анализа Lua JIT малвары.
Спасибо:
- @JustasMasiulis
- 0AVX
- Fish-Sticks
- Jollyc
- @xusheng6
- Themida за сэмплы
Игровой чит, которым воспользовались в атаке, это оупенсорс aim-бот под названием AIMMY, расположенный в следующем репо Гитхаба: Babyhamsta/Aimmy и с веб-сайтом aimmy.dev.
Злоумышленники клонировали оригинальный репо сюда -
https[:]//github[.]com/nehuenbohm/Aimmy
и сделали фейк настоящего сайта - https[:]//aimmy[.]app
с ссылками на клонированный репо Гитхаба. На фальшивом веб-сайте была изменена настоящая ссылка загрузки на ссылку для загрузки малвары, размещенную на https[:]//github[.]com/Babyhamsta/Aimmy/files/14475029/Aimmy.zip
. Обратите внимание, что ZIP архив по ссылке размещен в оригинальном репо Гитхаба.В поисковой выдаче вредоносный сайт был размещен выше настоящего сайта aim-бота (вероятно из-за «отравления поисковой выдачи»). Пользователи. которые ищут aim-бот будут вероятно попадать на вредоносный сайт и загружать малвару.
3. Баг Гитхаба (или фича).

Ключевой элемент успеха атаки - это хостинг пейлоада в настоящем репо. Таким образом злоумышленники дистанциируются от хостинга пейлоада и могут успешно скрыть ссылку загрузки в нормальном трафике. Использование данного трюка делает сложным для жертвы понять то, что на самом деле она нажимает на зловредную ссылку. Но как злоумышленникам удается разместить их пейлоад в чужом репо? Мы были в реальном затруднении до того, как @JustasMasiulis обнаружил баг Гитхаба … или фичу?
При открытии тикета проблемы (issue) в репо - любой файл, прикрепленный к тикету будет размещаться в том же репо Гитхаба, где был открыт тикет проблемы. Файлы сохраняются, если даже сам тикет проблемы не был сохранен. Это означает, что любой человек может загрузить любой файл в любой гит репо на Гитхабе и не оставить никаких следов, что этот файл существует за исключением прямой ссылки на него.
- Нажмите
New Issue
в выбранном репо GitHub; - Перетащите файл пейлоада в описание тикета;
- Сохраните сгенерированную ссылку из описания тикета;
- Закройте тикет без его сабмита или сохранения;
- Ссылка на загруженный пейлоад останется активной, даже если тикет проблемы никогда не был сохранен.
https://twitter.com/x/status/1764171634469122165
4. Клонирование вредоносного репо.
Злоумышленники использовали умный способ, чтобы скрыть факт того, что они клонировали оригинальный репо Гитхаба. ArsTechnica недавно публиковала разбор похожих зловредных атак клонирования, но так как в данном случае мы наблюдали всю атаку в режиме реального времени, мы можем добавить некоторые детали:
- Целевой репо полностью загружается на новый аккаунт, это не форк.
- Несмотря на то, что история коммитов гита теперь ведется в новом репо, там не будет сведений о том, что это форк оригинального репо.
- Злоумышленник делает тысячи пустых коммитов, скорее всего скриптами, чтобы представить будто бы они являются основным контрибутором в проект. В случае нашего aim-бота было сделано больше семи тысяч коммитов за 24 часа.
- Последний коммит был сделан в README проекта с изменением ссылок загрузки на ссылку загрузки пейлоада. Коммит был сделан от имени "проверенного пользователя" (verified user) Гитхаба, потому что пользователь проверен для этого репо.
Зверь сам по себе довольно уникальный. Он состоит из файла Lua JIT file, собранного из LuaJIT исполняемого файла, использованного для интерпретации JIT файла, и batch скрипт для повышения прав и запуска JIT в интерпретаторе.
Сэмпл
Малвара доставляется в malshare ZIP-архиве
- c912762952152c40646a61d7cc80a74f61ddd7aad292a1812f66e76b405f9660
Aimmy.bat
- Batch скрипт, используемый для запуска lua кода в интерпретаторе
- 1cf20b8449ea84c684822a5e8ab3672213072db8267061537d1ce4ec2c30c42a
AimmyLauncher.exe
- LuaJIT интерпретатор
- d6d3c8ea51a025b3abeb70c9c8a17ac46cf82e5a46a259e9aaa9d245212d9e17
README.txt
- fa3224ec83c69883519941c0e010697bcdc0518a3d9e2c081cd54f5e9458b253
data
- Компилированный вредоносный Lua JIT код, magic-байты
1B 4C 4A
- Компилированный вредоносный Lua JIT код, magic-байты
- ff976f6e965e3793e278fa9bf5e80b9b226a0b3932b9da764bffc8e41e6cdb60
lua51.dll
Используя LuaJIT Decompiler v2 у нас получилось декомпилировать JIT и восстановить код Lua, только чтобы найти, что он был тяжело обфусцирован.
Код: Скопировать в буфер обмена
Код:
local var_0_19 = {
var_0_17[var_0_18("\xA7Z$\\oӈ.", 8170536974433)],
var_0_17[var_0_18("\x83\xEB\xC1\xEC\xAER\x0E\xCB", 32340223605166)],
var_0_17[var_0_18("\x98E\r\f", 18847752807337)],
var_0_17[var_0_18("Z\x16g\xD2", 17704612011450)],
var_0_17[var_0_18("K۽^\xAE\xF8_\xFF", 2538651564100)],
var_0_17[var_0_18("\x1Bdo\xF6B\xF3\x92\xC0\x84^zg\x8B\xB1\x95\xC3", 17854945091482)],
var_0_17[var_0_18("\xFFYk\xF5\xAB37\x83", 21489022010759)],
var_0_17[var_0_18("\x8Eq\x85\x93yJ.\xD8", 17231694304248)],
var_0_17[var_0_18("\xDD,G\x17\xEBg#h", 1115184245546)],
var_0_17[var_0_18("DE\x10D", 12159446557750)],
var_0_17[var_0_18("(\x92\xE7\xB9X\xEE8C\x19Z\xBE\x8Fq\xBF", 24217461828912)],
var_0_17[var_0_18("\xF8cQsU\xAF\x9C\x9C", 12518619714798)],
var_0_17[var_0_18("o\x01\xC1\xC7\xE7$(\x84", 29604450893836)],
. . .
Для обфускации использовался Lua обфускатор с открытым кодом под названием Prometheus. Этот бот обфускатора шифрует строки и использует виртуальную машину для защиты кода Lua.
Инструментарий окружения Lua
Вместо того чтобы пытаться взломать виртуальную машину, мы попробовали динамически проследить за кодом Lua. Так как зловредный код Lua передал аргумент интерпретатору LuaJIT, мы сначала попробовали воссоздать окружение Lua, подгружая код трассировки до запуска JIT, концепт основанный на идеях Ника из блога - Hooking LuaJIT.
Мы использовали следующий скрипт вместе с
-e
аргументом для интерпретатора:AimmyLauncher.exe -e <script> data
Код: Скопировать в буфер обмена
Код:
jit.off()
FILE_PATH = "C:\\LuaJitHookLogs\\"
STARTING_TIME = os.clock()
GDUMPED = false
function table.show(t)
local function serialize(arr, level)
local str = ""
local indent = string.rep(" ", level*2)
for i, v in pairs(arr) do
if type(v) == "table" then
str = str .. indent .. i .. ":\n" .. serialize(v, level+1)
else
str = str .. indent .. i .. ": " .. tostring(v) .. "\n"
end
end
return str
end
return serialize(t, 0)
end
function dumpGlobals()
local fname = FILE_PATH .. "globals_" .. STARTING_TIME .. ".txt"
local globalsFile = io.open(fname, "w")
globalsFile:write(table.show(_G, "_G"))
globalsFile:flush()
globalsFile:close()
end
function trace(event, line)
local info = debug.getinfo(2)
if not info then return end
if not info.name then return end
if string.len(info.name) <= 1 then return end
if (not GDUMPED) then
dumpGlobals()
GDUMPED = true
end
local fname = FILE_PATH .. "trace_" .. STARTING_TIME .. ".txt"
local traceFile = io.open(fname, "a")
traceFile:write(info.name .. "()\n")
local a = 1
while true do
local name, value = debug.getlocal(2, a)
if not name then break end
if not value then break end
traceFile:write(tostring(name) .. ": " .. tostring(value) .. "\n")
a = a + 1
end
traceFile:flush()
traceFile:close()
end
debug.sethook(trace, "c")
Это имело ограниченный успех, потому что в виртуальной машине была функция защиты от несанкционированного доступа (которая по итогу была обойдена Jollyc) и у меня возникли сложности... главным образом по тому, что я не знаю ничего о Lua и работа над ошибками занимала много времени.
Мусор Lua - избавляемся от зашифрованных строк
Эта идея принадлежит Jolyc, Fishy-Sticks и 0AVX. Как Fishy-Sticks сказал об этом...
Lua - это управляемый язык, в котором есть сборщик мусора, я и AVX смотрим сейчас, где собираются строки, чтобы могли избавиться от информации о строках до того, как они будут собраны, чтобы придержать эти зашифрованные строкиLuaJIT немного отличается от нормального Lua, поэтому нам потребовалось некоторое время. чтобы во всем разобраться. Все, что нам требовалось, это клонировать LuaJIT repository, найти сборщик мусора для кода LuaJIT и добавить наш собственный хук, чтобы избавится от строк до того, как они будут освобождены. Мы могли затем компилировать нашу собственную версию LuaJIT и использовать ее для запуска вредоносного кода Lua, чтобы выбросить эти строки.
Нажмите, чтобы раскрыть...
Хук lj_str_free
Функция
lj_str_free function
в lj_str.c
отвечает за освобождение строковой памяти. Мы добавим простой код и перекомпилируем:
Код: Скопировать в буфер обмена
Код:
void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s)
{
const char* myStr = strdata(s);
if (myStr) {
printf("STRING: %s\n", myStr);
FILE* newfile = fopen("log.txt", "a");
fprintf(newfile, "STRING: %s\n", myStr);
fclose(newfile);
}
g->str.num--;
lj_mem_free(g, s, lj_str_size(s->len));
}
Дополнительные хуки могут быть добавлены в
lib_os.c
на уровне системных функций, чтобы избавиться от взаимодействия между Lua и хостом.Код: Скопировать в буфер обмена
Код:
LJLIB_CF(os_execute)
{
const char* cmd2 = luaL_optstring(L, 1, NULL);
if (cmd2) {
printf("OS_EXECUTE: %s\n", cmd2);
FILE* newfile = fopen("log.txt", "a");
fprintf(newfile, "OS_EXECUTE: %s\n", cmd2);
fclose(newfile);
}
Поскольку многие строковые операторы приводят к выделению в память частичных строк, в дампе много шума, но мы смогли восстановить полные структуры, определения функций, а также строки, используемые о связи малвары с C2 и ее удержания в системе. Что еще можно сделать — объединить этот подход с более классической трассировкой API, чтобы обеспечить более целостное представление о вредоносном ПО.
Источник: https://research.openanalysis.net/github/lua/2024/03/03/lua-malware.html