Guest User

Securely Open GUI With PHP v0.1

a guest
Jul 19th, 2020
284
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 28.09 KB | None | 0 0
  1. #!/usr/bin/sh
  2. searchPathForPhp() {
  3. ( IFS=:
  4. for p in $PATH; do
  5. phppath="$(command -v "$p"/php[0-9]*)" 2>/dev/null
  6. if test "0$?" -eq 0; then echo "$phppath" | tail -n1; return 0; fi
  7. done
  8. )
  9. return 1;
  10. }
  11. zenitylogin() {
  12. prompter="$1"; shift 1
  13. if test -z "$username" -eq 0; then
  14. msg='Please enter username and password so that PHP can be installed so things can continue to work smoothly'
  15. logininfo="$("$prompter" --forms --text="$msg" --add-entry='Username' --add-password='Password' --separator='
  16. ')"
  17. # If the user clicked "cancel" or a Zenity error, then don't even try:
  18. if test "0$?" -ne 0; then exit 1; fi
  19. username="$(printf '%s' "$logininfo" | head -n1)"
  20. password="$(printf '%s' "$logininfo" | tail -n1)"
  21. fi
  22. printf "%s\n" "$password" | sudo -S -u "$username" -- "$@"
  23. }
  24. run-su-cmd() {
  25. sucmd="$1"; program="$2"; shift 2
  26. "$sucmd" -c "'""$program""'"' "$@"' - "$USER" -- "$0" "$@"
  27. }
  28. phppath="$(command -v php)" 2>/dev/null
  29. if test "0$?" -ne 0; then
  30. # calling searchPathForPhp will set the status code to 1 if not available
  31. phppath="$(searchPathForPhp)"
  32. fi
  33. if test "0$?" -ne 0; then
  34. # Prompt the user about installing packages and confirm
  35. confmsg="$(pwd) needs to install PHP in order to work. Are you OKAY with this script installing PHP automatically?"
  36. if test -x "$(command -v xmessage)" 2>/dev/null; then
  37. test "$(xmessage -buttons Yes,No -default No -print "$confmsg")" = "No"
  38. else
  39. # Prompt the user about installing packages
  40. test "$(osascript -e 'display dialog "'"$confmsg"'" buttons {"Yes", "No"} default button "No"')" = "No"
  41. fi
  42. if test "0$?" -ne 0; then exit 1; fi # Exit if we don't have consent
  43. e=""
  44. if test "$(id -u)" -ne 0; then
  45. case "$-" in
  46. "*i*")
  47. if test -x "$(command -v sudo)" 2>/dev/null; then
  48. # Most Linux distros
  49. e=sudo
  50. else
  51. # BSD
  52. e="run-su-cmd su"
  53. fi
  54. ;;
  55. *)
  56. if test -x "$(command -v pkexec)" 2>/dev/null; then
  57. e="pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY"
  58. elif test -x "$(command -v gksudo)" 2>/dev/null; then
  59. e=gksudo
  60. elif test -x "$(command -v gksu)" 2>/dev/null; then
  61. e="run-su-cmd gksu"
  62. elif test -x "$(command -v zenity)" 2>/dev/null; then
  63. e="zenitylogin zenity"
  64. elif test -x "$(command -v yad)" 2>/dev/null; then
  65. e="zenitylogin yad"
  66. fi
  67. ;;
  68. esac
  69. fi
  70. if test -x "$(command -v apt)" 2>/dev/null; then $e apt install -y php-cgi && $e apt install -y php72 || $e apt install -y php
  71. elif test -x "$(command -v apk)" 2>/dev/null; then $e apk add --no-cache php72 || $e apk add --no-cache php
  72. elif test -x "$(command -v apt-get)" 2>/dev/null; then $e apt-get install -y php-cgi && $e apt-get install -y php72 || $e apt-get install -y php
  73. elif test -x "$(command -v yum)" 2>/dev/null; then $e yum install php72-cli || yum install php-cli
  74. elif test -x "$(command -v pacman)" 2>/dev/null; then $e pacman -S php72 || $e pacman -S php
  75. elif test -x "$(command -v brew)" 2>/dev/null; then $e brew install php72 || $e brew install php
  76. elif test -x "$(command -v dnf)" 2>/dev/null; then $e dnf install php72-cli || $e dnf install php-cli
  77. elif test -x "$(command -v zypper)" 2>/dev/null; then $e zypper install php-cli || $e zypper install php
  78. elif test -x "$(command -v pkg)" 2>/dev/null; then $e pkg install php72
  79. elif test -x "$(command -v emerge)" 2>/dev/null; then $e emerge --ask dev-lang/php:7.2
  80. elif test -x "$(command -v pkgman)" 2>/dev/null; then $e pkgman install cmd:install
  81. fi
  82. phppath="$(sh -c 'command -v php7.2')" 2>/dev/null
  83. if test "0$?" -ne 0; then
  84. phppath="$(sh -c 'command -v php')" 2>/dev/null
  85. if test "0$?" -ne 0; then
  86. # failed to install PHP
  87. echo "FAILED TO INSTALL PACKAGE: Package manager not found, you have no internet connection, or another error occured. You must manually install PHP (>=5.4)">&2
  88. exit 1
  89. fi
  90. fi
  91. fi
  92.  
  93. if test -x "$(command -v grep)" 2>/dev/null; then
  94. grep -A1073741823 '<''?php' "$0" | php -- "$@"
  95. else
  96. echo '/PHP_SCRIPT_''STARTS_AFTER_HERE/+1,$p' | ed -s "$0" | php -- "$@"
  97. fi
  98. exit 0
  99. PHP_SCRIPT_STARTS_AFTER_HERE
  100. <?php
  101.  
  102. // The most likely break I see potentially happening in a far future PHP release is deprecating and removing
  103. // case-insensitive booleans. However, we do not know yet whether they will become uppercase/lowercase
  104. if (!defined('false')) define('false', !1);
  105. if (!defined('true')) define('true', !0);
  106. if (function_exists('set_time_limit')) set_time_limit( 0 );
  107.  
  108. $isTheOsWindows = strcasecmp(PHP_SHLIB_SUFFIX, 'dll') === 0;
  109. $isEffectivelyRoot = @is_writable($isTheOsWindows ? "/usr/bin/env" : @getenv("COMSPEC")) ?: false;
  110.  
  111. // Random ports between these two values will be attempted until one is found
  112. define("TCP_SERVER_MAX_LISTEN_PORT", 65535);
  113. define("TCP_SERVER_MIN_LISTEN_PORT", 1025);
  114.  
  115. $preferedListeningIPArray = array_merge(
  116. ["localhost", "127.0.0.1", "[::1]"],
  117. @gethostbynamel("localhost") ?: []
  118. );
  119.  
  120. $localHostIpv6s = @dns_get_record("localhost", DNS_AAAA);
  121. if ($localHostIpv6s) foreach ($localHostIpv6s as $record)
  122. if (filter_var($record["ipv6"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false)
  123. array_push( $preferedListeningIPArray, '[' . strtolower($record["ipv6"]) . ']' );
  124. unset( $localHostIpv6s );
  125.  
  126. $preferedListeningIPArray = array_unique( $preferedListeningIPArray );
  127.  
  128. function getPathToProcessByPID($pid) {
  129. global $isTheOsWindows;
  130. if ($isTheOsWindows) {
  131. $procInfo = @shell_exec('wmic process where processID=' . $pid . ' get ExecutablePath /format:value');
  132. $procInfo = trim($procInfo);
  133. $eqIndex = strpos($procInfo, '=');
  134. return $eqIndex === false ? $procInfo : substr($procInfo, $eqIndex+1);
  135. } elseif (file_exists('/proc/' . $pid . '/exe')) {
  136. return readlink('/proc/' . $pid . '/exe'); // ultra fast
  137. } else {
  138. @exec('lsof -Fn -p ' . $pid . ' 2>/dev/null' . $pid, $linesList);
  139.  
  140. $isInTheZone = false;
  141. foreach($linesList as $line) {
  142. if ($line[0] === 'f') $isInTheZone = $line === 'ftxt';
  143. if ($line[0] === 'n' && $isInTheZone) return str_replace(
  144. ["\\\\", "\\n"],
  145. ["\\", "\n"],
  146. substr($line, 1)
  147. );
  148. }
  149. }
  150.  
  151. return false;
  152. }
  153. $bothBracketsArray = ['[', ']'];
  154. function doIpsAndPortsEqual($a, $b) {
  155. global $bothBracketsArray;
  156. $lastPosA = strrpos($a, ":"); // port #
  157. $lastPosB = strrpos($b, ":"); // port #
  158. if (substr($a, $lastPosA+1) !== substr($b, $lastPosB+1)) return false;
  159. $ipA = substr($a, 0, $lastPosA);
  160. $ipB = substr($a, 0, $lastPosB);
  161. if (strpos($ipA, ':') !== false) { // Ipv6
  162. return inet_pton(str_replace($bothBracketsArray, '', $ipA)) === inet_pton(str_replace($bothBracketsArray, '', $ipB));
  163. } else { // Ipv4
  164. return $ipA === $ipB;
  165. }
  166. }
  167. function getAllPIDSConnectingToTheSocket($possibleIpsAndPorts) {
  168. global $isTheOsWindows, $supportsNetStat;
  169. // Basically cross-platform execution and parsing of netstat with lsof as backup
  170. $out = array();
  171. if ($isTheOsWindows) {
  172. if (!$supportsNetStat) return false;
  173. @exec(escapeshellarg($supportsNetStat) . " -n -o", $outputLines, $statInt);
  174. if ($statInt !== 0) return false;
  175. if (!empty($outputLines)) foreach ($outputLines as $line) {
  176. if (strcasecmp(substr($line, 0, 4), " tcp") !== 0) continue;
  177. $arr = preg_split('/\s+/', ' ', strtolower(trim($line)));
  178. foreach ($possibleIpsAndPorts as $ip)
  179. if (doIpsAndPortsEqual($arr[2], $ip)) {
  180. array_push($out, $arr[4]);
  181. break;
  182. }
  183. }
  184. } elseif ($supportsNetStat) {
  185. @exec(escapeshellarg($supportsNetStat) . " -W -p -n --tcp || netstat -W -p -n 2>/dev/null", $outputLines, $statInt);
  186. if ($statInt !== 0) return false;
  187. if (!empty($outputLines)) foreach ($outputLines as $line) {
  188. if (substr($line, 0, 3) !== "tcp") continue;
  189. $arr = preg_split('/\s+/', ' ', $line);
  190. foreach ($possibleIpsAndPorts as $ip)
  191. if (doIpsAndPortsEqual($arr[4], $ip)) {
  192. array_push($out, $arr[4]);
  193. break;
  194. }
  195. }
  196. } else { // much slower version but works on MacOS:
  197. @exec("lsof -iTCP -n -P -F", $outputLines, $statInt);
  198. if ($statInt !== 0) return false;
  199. $pid = false;
  200. if (!empty($outputLines)) foreach ($outputLines as $line) {
  201. if ($line[0] === 'p') { $pid = @intval(substr($line, 1)) ?: false; continue; }
  202. if ($line[0] !== 'n') { continue; }
  203. $destAddress = substr($line, strpos($line, '->')+2);
  204. foreach ($possibleIpsAndPorts as $ip)
  205. if (doIpsAndPortsEqual($destAddress, $ip)) {
  206. if ($pid !== false) { array_push($out, $pid); $pid = false; }
  207. break;
  208. }
  209. }
  210. }
  211. return $out;
  212. }
  213. function isSocketConnectionSafe() {
  214. global $possibleIpsAndPorts;
  215. // whether items in the $browserFolders are the only things connected to the socket
  216. $possiblePIDs = getAllPIDSConnectingToTheSocket( $possibleIpsAndPorts );
  217. $myOwnPID = getmypid(); // a core PHP function
  218. if (!empty($possiblePIDs)) foreach ($possiblePIDs as $pid) {
  219. if ($pid == $myOwnPID) continue; // deliberate double equals
  220. $proc = strtolower( getPathToProcessByPID($pid) );
  221. if ($proc === false) continue; // there's nothing we can do
  222. $isSafe = false;
  223. foreach ($possibleIpsAndPorts as $path) {
  224. $len = strlen( $path );
  225. if ($proc[$len] === DIRECTORY_SEPARATOR && substr($proc, $len) === $path) {
  226. $isSafe = true;
  227. }
  228. }
  229. if (!$isSafe) return false;
  230. }
  231. return true;
  232. }
  233.  
  234. function is_browser_safe($enforcePerms, $fullPath, &$resolvedPath) {
  235. global $isTheOsWindows, $isEffectivelyRoot;
  236. // checks to make sure its executable and only editable by the root
  237. if (!is_executable($fullPath)) return false;
  238. $resolved = @realpath( $fullPath ) ?: $fullPath;
  239. if ($enforcePerms) {
  240. if (!$isEffectivelyRoot && is_writable($resolved)) return false;
  241. if (!$isTheOsWindows) {
  242. if ((@fileowner($resolved) ?: 0) !== 0) return false; // ensure owner is root
  243. $perms = @fileperms( $resolved ) ?: 0;
  244. if ((@filegroup($resolved) ?: 0) !== 0 && ($perms & 16)) return false;
  245. if ($perms & 2) return false; // if the world can write to it
  246. }
  247. }
  248. $resolvedPath = $resolved;
  249. return true;
  250. }
  251.  
  252. $foundBrowser = NULL; // MacOS does not support su; Haiku doesn't support getent, only id:
  253. $fullBrowserPath = NULL;
  254. $supportsNohup = $supportsNetStat = $supportsCScript = $supportsSudo = $supportsId = $supportsLogger = false;
  255. if ($isTheOsWindows) {
  256. // Search the registry for the user's internet browsers
  257. // TODO: How to avoid the temporary file? That could be a security liability because a fast-polling program
  258. // might be able to slip in and modify it right before PHP reads it
  259. $programFiles = strtolower( @getenv('ProgramFiles') );
  260. // Vivaldi and Amigo browsers install locally and are too insecure to be used safely
  261. $browserFolders = ["$programFiles\\google", "$programFiles\\mozilla firefox", "$programFiles\\bravesoftware\brave-browser",
  262. "$programFiles\\tencent\\qqintl\\bin", "$programFiles\\yandex\\yandexbrowser", "$programFiles\\opera", "$programFiles\\baidu",
  263. "$programFiles\\ucbrowser", "$programFiles\\maxthon", "$programFiles\\internet explorer", "$programFiles\\safari"];
  264. if (!empty($sysWindowsDir = @getenv("SystemRoot")) array_push($browserFolders, "$sysWindowsDir\\SystemApps"); // Microsoft Edge
  265. $tmpWindowsTegFile = tempnam(sys_get_temp_dir(), bin2hex(openssl_random_pseudo_bytes(6)));
  266. //shell_exec('reg export "HKLM\\SOFTWARE\\Clients\\StartMenuInternet" ' . escapeshellarg($tmpWindowsTegFile) . ' /y');
  267. //$regLines = @file($tmpWindowsTegFile) ?: [];
  268. if (!empty($programFiles = strtolower( @getenv('ProgramFiles(x86)') ))) {
  269. //shell_exec('reg export "HKLM\\SOFTWARE\\Wow6432Node\\Clients\\StartMenuInternet" ' . escapeshellarg($tmpWindowsTegFile) . ' /y');
  270. //$regLines = array_merge($regLines, @file($tmpWindowsTegFile) ?: []);
  271. array_push($browserFolders, "$programFiles\\google", "$programFiles\\mozilla firefox", "$programFiles\\yandex\\yandexbrowser",
  272. "$programFiles\\bravesoftware\brave-browser", "$programFiles\\opera", "$programFiles\\maxthon", "$programFiles\\ucbrowser",
  273. "$programFiles\\tencent\\qqintl\\bin", "$programFiles\\internet explorer", "$programFiles\\baidu", "$programFiles\\safari");
  274. }
  275.  
  276. $foundBrowser = "explorer.exe"; // On windows, we use explorer.exe only
  277. $sysWindowsDir = $sysWindowsDir ?: "C:\\Windows";
  278. $fullBrowserPath = is_executable("$sysWindowsDir\\explorer.exe") ? "$sysWindowsDir\\explorer.exe"
  279. : "$sysWindowsDir\\System32\\explorer.exe";
  280.  
  281. $pathDirs = explode(PATH_SEPARATOR, @getenv('PATH') ?: "$sysWindowsDir\system32;$sysWindowsDir" );
  282. foreach($pathDirs as $path) {
  283. $files = @scandir( $path );
  284. if (!empty($files)) foreach ($files as $filename) {
  285. switch (basename(strtolower($filename), ".exe")) {
  286. case "netstat": $supportsNetStat = $path . DIRECTORY_SEPARATOR . $filename; continue;
  287. case "cscript": $supportsCScript = $path . DIRECTORY_SEPARATOR . $filename; continue;
  288. }
  289. }
  290. }
  291.  
  292.  
  293. /*$usePathToTheCommand = false;
  294. foreach ($regLines as $line) {
  295. $trimmedLine = trim($line);
  296. if (empty($trimmedLine)) { $usePathToTheCommand = false; continue; }
  297. if (substr($trimmedLine,-9) !== "\\command]" || !$usePathToTheCommand) { continue; }
  298. $usePathToTheCommand = true;
  299. $eqIndex = strpos($trimmedLine, "=");
  300. if ($eqIndex === false || $trimmedLine[$eqIndex+1] !== '"') continue;
  301. $fullCommandLine = @json_decode( substr($trimmedLine, $eqIndex+1) );
  302. if (empty($fullCommandLine)) return;
  303. if ($fullCommandLine[0] === '"') $fullCommandLine = substr($fullCommandLine, 1, strpos($fullCommandLine, '"', 1)-1);
  304. else $fullCommandLine = substr($fullCommandLine, 0, strpos($fullCommandLine, ' ', 1)-1);
  305. array_push( $browserFolders, dirname($fullCommandLine) );
  306. }*/
  307.  
  308. unlink($tmpWindowsTegFile); unset($tmpWindowsTegFile); unset($regLines); unset($sysWindowsDir); unset($programFiles);
  309. } else $browserFolders = ["/opt/google/chrome", "/usr/lib/firefox"];
  310.  
  311. function readEtcEnv() {
  312. $etcEnv = @file("/etc/environment");
  313. if (!empty($etcEnv)) foreach ($etcEnv as $line) {
  314. if (substr($line, 0, 5) === 'PATH=') return $line[5] === '"' ? @json_decode( substr($line, 5) ) : substr($line, 5);
  315. }
  316. }
  317. function findBrowserProgram($enforcePerms) {
  318. global $foundBrowser, $supportsNohup, $supportsSudo, $supportsCScript, $supportsId,
  319. $supportsNetStat, $browserFolders, $isTheOsWindows, $fullBrowserPath;
  320. if ($fullBrowserPath !== NULL) return $fullBrowserPath;
  321. //$suffix = $isTheOsWindows ? '.exe' : '';
  322. $preferedPrograms = ["x-www-browser","git$suffix","xdg-open","sensible-browser","open","gnome-open"];
  323. $specificBrowsers = [
  324. "firefox", "iceweasel", "google-chrome", "chrome", "chromium", "chromium-browser", "opera", "brave", "kfmclient",
  325. "konqueror", "seamonkey", "iceape", "w3m", "elinks", "links", "lynx", "dillo", "python", "python3", "python2"];
  326. $pathDirs=explode(PATH_SEPARATOR,@getenv('PATH')?:readEtcEnv()?:"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
  327.  
  328. $resolvedPath = "";
  329. $tmpResolvedPath = '';
  330. $found = "";
  331.  
  332. $bestSpecificBrowser = "";
  333. $priority = 99;
  334.  
  335. foreach($pathDirs as $path) {
  336. if (!in_array($path, $browserFolders) && ($isEffectivelyRoot ? /*$isTheOsWindows ||*/ @fileowner($path) === 0
  337. && @filegroup($path) === 0 && (@fileperms($path) & 2) === 0 : !is_writable($path)))
  338. array_push($browserFolders, $path);
  339.  
  340. $contents = @opendir($path, SCANDIR_SORT_NONE);
  341. if ($contents === false) continue;
  342.  
  343. while (($filename = readdir($contents)) !== false) {
  344. $lowered = strtolower($filename);
  345.  
  346. switch (/*$isTheOsWindows ? basename($lowered, ".exe") :*/ $filename) {
  347. case "nohup": $supportsNohup = $path . DIRECTORY_SEPARATOR . $filename; continue;
  348. case "sudo": $supportsSudo = $path . DIRECTORY_SEPARATOR . $filename; continue;
  349. case "id": $supportsId = $path . DIRECTORY_SEPARATOR . $filename; continue;
  350. case "netstat": $supportsNetStat = $path . DIRECTORY_SEPARATOR . $filename; continue;
  351. case "logger": $supportsLogger = $path . DIRECTORY_SEPARATOR . $filename; continue;
  352. }
  353.  
  354. if ($priority !== -1 && in_array($lowered, $preferedPrograms)
  355. && is_browser_safe($enforcePerms, $path . DIRECTORY_SEPARATOR . $filename, $resolvedPath)) {
  356. if (!in_array(dirname(strtolower($resolvedPath)), $browserFolders))
  357. array_push($browserFolders, dirname(strtolower($resolvedPath)));
  358. $found = $filename;
  359. $priority = -1;
  360. break;
  361. }
  362. $specificBrowserIndex = array_search($lowered, $specificBrowsers, true);
  363. if ($specificBrowserIndex !== false && 0 <= $specificBrowserIndex
  364. && is_browser_safe($enforcePerms, $path . DIRECTORY_SEPARATOR . $filename, $tmpResolvedPath)) {
  365. if (!in_array(dirname(strtolower($browserDir)), $browserFolders))
  366. array_push($browserFolders, dirname(strtolower($browserDir)));
  367. if ($specificBrowserIndex < $priority && $priority !== -1) {
  368. $resolvedPath = $tmpResolvedPath;
  369. $found = $filename;
  370. $priority = $specificBrowserIndex;
  371. }
  372. }
  373. }
  374. @closedir( $contents );
  375. }
  376.  
  377. if ($enforcePerms && empty($found)) return findBrowserProgram( false );
  378.  
  379. $fullBrowserPath = $resolvedPath;
  380. $foundBrowser = $found;
  381.  
  382. return $fullBrowserPath;
  383. }
  384.  
  385. function openURLInBrowser($weburl) {
  386. $browserProgram = findBrowserProgram( true );
  387. if (empty($browserProgram)) throw Exception("Cannot open a browser to the specified URL");
  388.  
  389. $cmdLine = escapeshellarg($browserProgram) . ' ';
  390. switch (basename(strtolower($browserProgram), ".exe")) { // elegant concise PHP
  391. // a lot came from https://github.com/git/git/blob/master/git-web--browse.sh
  392. case "firefox": case "iceweasel": case "seamonkey": case "iceape":
  393. $ffversion = shell_exec($browserProgram . ' -version');
  394. $ffversion = intval( end( explode(" ", $ffversion) ), 10 );
  395. if ($ffversion <= 2) $cmdLine = $cmdLine . '-new-tab ';
  396. break;
  397. case "git":
  398. $cmdLine = $cmdLine . 'web--browse ';
  399. break;
  400. case "python": case "python2": case "python3":
  401. $cmdLine = $cmdLine . '-mwebbrowser ';
  402. break;
  403. case "google-chrome": case "chrome": case "chromium": case "chromium-browser":
  404. break; // nothing to do here
  405. case "w3m": case "elinks": case "links": case "lynx": case"cygstart":
  406. break; // nothing to do here
  407. case "konqueror": case "kfmclient":
  408. $cmdLine = $cmdLine . 'newTab ';
  409. break;
  410. case "explorer.exe": case "xdg-open": case "sensible-browser":
  411. case "open": case "gnome-open": case "x-www-browser":
  412. break; // nothing to do here
  413. }
  414.  
  415. $cmdLine = $cmdLine . escapeshellarg($weburl); // append the URL we want
  416.  
  417. // Now, open the browser by using proc_open + writing to STDIN to hide the UUID we pass
  418. executeCmdHidden( $cmdLine );
  419. }
  420. function readProcessStreamToCompletion($proc, $pipes) {
  421. stream_get_contents($pipes[1]); // read to completion
  422. fclose($pipes[0]);
  423. fclose($pipes[1]);
  424. proc_close($process);
  425. }
  426. function nopeRoot($str) {
  427. // empty catches the cases of undefined from error supression and
  428. return empty($str) || $str === "root" ? false : $str;
  429. }
  430. function show_fatality($msg) {
  431. global $supportsLogger;
  432. if ($supportsLogger) @shell_exec('logger -p daemon.alert -s ' . escapeshellarg($msg));
  433. if (@is_writable('/dev/kmsg')) @file_put_contents('/dev/kmsg', '<3>' . $msg, FILE_APPEND);
  434. // xmessage may be ugly but it is available on every device with a desktop in the Linux universe
  435. @shell_exec('xmessage -center '.escapeshellarg(wordwrap('ERROR in '.dirname(__FILE__).': '.$msg,80,"\n",true)));
  436. exit( 1 );
  437. }
  438. function executeCmdHidden($cmdLine) {
  439. global $isTheOsWindows, $supportsNohup, $supportsSudo, $supportsCScript, $supportsId,
  440. $isEffectivelyRoot;
  441. // This function pipes into the shell to hide the UUID passed to the web browser
  442. $realProcCMD = "";
  443. $commandToPipeIn = "";
  444.  
  445. if (!$isTheOsWindows) {
  446. $realProcCMD = "sh";
  447. $cmdLine = ($supportsNohup ? 'nohup ' : '') . $cmdLine . ' >/dev/null 2>&1 &';
  448. //if ($displayEnv = @getenv("DISPLAY")) $cmdLine = escapeshellarg("DISPLAY=".$displayEnv)." ".$cmdLine;
  449. if ($isEffectivelyRoot) {
  450. // if we are root, then we need to tone down who to run as
  451. $runAsUser = nopeRoot(@getenv("SUDO_USER")) ?: nopeRoot(@shell_exec("logname")) ?: nopeRoot(@getenv("USER"));
  452. if ($runAsUser === false && !empty($xauthvar = @getenv("XAUTHORITY"))) {
  453. $userId = intval(substr($xauthvar, strcspn($xauthvar, '0123456789'))) ?: 0;
  454. if ($userId !== 0 && $supportsId) {
  455. $runAsUser = @shell_exec("id -nu " . $userId) ?: false;
  456. } elseif ($userId !== 0) {
  457. $runAsUser = @shell_exec("getent passwd " . $userId) ?: false;
  458. if ($runAsUser !== false) $runAsUser = substr($runAsUser, strpos($runAsUser, ":")-1);
  459. }
  460. } elseif ($runAsUser === false) { // MacOS doesn't do X11
  461. $whoText = explode("\n", @shell_exec("who") ?: "");
  462. foreach ($whoText as $line) {
  463. $lineTrimmed = trim( $line );
  464. if (stripos($lineTrimmed, " console ") !== false)
  465. $runAsUser = nopeRoot(substr($lineTrimmed, 0, strpos($lineTrimmed, " ")-1));
  466. }
  467. }
  468. if ($runAsUser === false) { // last-ditch effort to try to find a suitable directory
  469. $openedHome = @opendir("/home");
  470. if (!empty($openedHome)) while (($dir = readdir($openedHome)) !== false)
  471. if (is_readable("/home/$dir")) {
  472. $runAsUser = $dir;
  473. break;
  474. }
  475. closedir( $openedHome );
  476. }
  477. if ($runAsUser === false) show_fatality('this is running as root and cannot find user to log into as to ' .
  478. 'start the web browser so you can have a GUI. Please open issue with details on GitHub. Many thanks.');
  479. if ($supportsSudo) {
  480. $commandToPipeIn = 'sudu -u ' . escapeshellarg($runAsUser) . ' -- ' . $cmdLine . "\n";
  481. } else {
  482. $commandToPipeIn = 'su -c '.escapeshellarg($cmdLine)." ".escapeshellarg($runAsUser)."\n";
  483. }
  484. } else {
  485. $commandToPipeIn = $cmdLine . "\n";
  486. }
  487. if (!$supportsNohup) {
  488. $realProcCMD = substr($commandToPipeIn, 0, -1);
  489. $commandToPipeIn = '';
  490. }
  491. } elseif ($supportsCScript && $isTheOsWindows) {
  492. $replacedCharacters = explode("", '\\"\'|^&'); // let's be conservative as IDK VBScript
  493. $replacementChars = [];
  494. foreach ($replacedCharacters as $char) {
  495. // Source: https://www.learnqtp.com/how-can-you-display-quot-in-qtp/
  496. array_push($replacementChars, '" & Chr(' . ord($char) . ') & "');
  497. }
  498. $escapedCmd = str_replace(
  499. $replacedCharacters,
  500. $replacementChars,
  501. $cmdLine
  502. );
  503. $realProcCMD = "CScript.exe /nologo execStdIn.vbs 2>&1";
  504. $commandToPipeIn = 'Call WScript.CreateObject("WScript.Shell").Run("' . $escapedCmd . '", 9, false)' . "\r\n";
  505. } else {
  506. // when all else fails, we must wait for the process to exit and waste memory
  507. $realProcCMD = $cmdLine;
  508. }
  509.  
  510. $proc = proc_open($realProcCMD, [["pipe","r"], ["pipe","w"]], $pipes);
  511.  
  512. if (!empty($commandToPipeIn)) fwrite($pipes[0], $commandToPipeIn, strlen($commandToPipeIn));
  513.  
  514. register_shutdown_function('readProcessStreamToCompletion', $proc, $pipes); // because readProcessStreamToCompletion may block
  515. }
  516. function findAvailableSocketIpAndPort() {
  517. global $preferedListeningIPArray;
  518.  
  519. // stream_socket_server is (suprise, suprise!) part of the core library and available even if all extensions are disabled
  520. $testedListenerPorts = [];
  521. $thresholdLength = round((TCP_SERVER_MAX_LISTEN_PORT - TCP_SERVER_MIN_LISTEN_PORT + 2) * 0.875); // threshold is 1 in 8 chance
  522. while (count($testedListenerPorts) < $thresholdLength) {
  523. $port = random_int(TCP_SERVER_MIN_LISTEN_PORT, TCP_SERVER_MAX_LISTEN_PORT);
  524. if (in_array($port, $testedListenerPorts)) continue;
  525. foreach($preferedListeningIPArray as $ip) {
  526. // try to open a socket on the port at the ip address. If succeed, we are good to go.
  527. $socket = stream_socket_server("tcp://" . $ip . ':' . $port, $errno, $errstr);
  528. if ($socket !== false) {
  529. return [$socket, $ip . ':' . $port];
  530. }
  531. }
  532. array_push($testedListenerPorts, $port);
  533. }
  534. // linear search through remaining ports to avoid
  535. for ($port = TCP_SERVER_MIN_LISTEN_PORT; $port <= TCP_SERVER_MAX_LISTEN_PORT; $port++) {
  536. if (in_array($port, $testedListenerPorts)) continue;
  537. foreach($preferedListeningIPArray as $ip) {
  538. // try to open a socket on the port at the ip address. If succeed, we are good to go.
  539. $socket = stream_socket_server("tcp://" . $ip . ':' . $port, $errno, $errstr);
  540. if ($socket !== false) {
  541. return [$socket, $ip . ':' . $port];
  542. }
  543. }
  544. }
  545. }
  546. if (!function_exists('random_int')) {
  547. if (!function_exists('mcrypt_create_iv')) {
  548. // This is still decently secure because you need 512 entries from Mersenne Twister in order to guess next one,
  549. // and >99% of the time the first attempt will succeed.
  550. mt_srand( @microtime() ?: mt_rand() );
  551. function random_int($min, $max) {
  552. return return mt_rand($min, $max);
  553. }
  554. } else {
  555. function random_int($min, $max) {
  556. return $min + (hexdec(bin2hex(
  557. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
  558. )) % ($max - $min + 1));
  559. }
  560. }
  561. }
  562. if (!function_exists("openssl_random_pseudo_bytes")) {
  563. function openssl_random_pseudo_bytes($n) {
  564. $str = '';
  565. for ($i = 0; ($i+3) <= $len; $i=$i+3) {
  566. $int = random_int(0, 16777216);
  567. $str = $str . chr($int) . chr($int >> 8) . chr($int >> 16);
  568. }
  569. for ( ; $i < $len; $i++)
  570. $str = $str . chr(random_int(0, 256));
  571. return $str;
  572. }
  573. }
  574. if (!function_exists('password_hash')) {
  575. $password_hash_salt = base64_encode(openssl_random_pseudo_bytes(18));
  576. $password_hash_salt = str_replace(['+','/','='], '', $password_hash_salt);
  577. $password_hash_param = '$' . implode('$', [
  578. phpversion() < "5.3.7" ? "2a" : "2y", // I only use phpversion when I have to
  579. phpversion() < "5.3.7" ? "09" : "06", // add the cost in two digits
  580. substr($password_hash_salt, 0, 22) // add the salt
  581. ]);
  582.  
  583. function password_hash($input, $_algo) {
  584. global $password_hash_param;
  585. if (defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1) {
  586. return crypt( base64_encode($input), $password_hash_param );
  587. } else {
  588. return hash("sha256", $input, false);
  589. }
  590. }
  591. }
  592.  
  593. $secretKey = str_replace(['+','/','='], ['-','_',''], base64_encode(openssl_random_pseudo_bytes(384)));
  594. $usedHashAlgo = defined('PASSWORD_BCRYPT') ? PASSWORD_BCRYPT : PASSWORD_DEFAULT;
  595. $publicHash = password_hash($secretKey, $usedHashAlgo, ['cost'=>6]);
  596.  
  597. // Here, we start the actual GUI:
  598. list($serverSocket, $serverIpAndPort) = findAvailableSocketIpAndPort();
  599. fclose( $serverSocket );
  600.  
  601. executeCmdHidden(
  602. 'php ' .
  603. (empty($password_hash_salt) ? '' : '-d custom_public_gui_param=' . $password_hash_param . ' ') .
  604. '-d custom_public_gui_hash=' . $publicHash . ' ' .
  605. '-S ' . escapeshellarg($serverIpAndPort) . ' ' .
  606. '-t ' . escapeshellarg(getcwd() . DIRECTORY_SEPARATOR . 'gui-root') . ' ' .
  607. '> ' . ($isTheOsWindows ? 'NUL' : '/dev/null')
  608. );
  609.  
  610.  
  611.  
  612. list($startSocket, $startIpAndPort) = findAvailableSocketIpAndPort();
  613. // HTTP is no less secure than HTTPS at localhost:
  614. openURLInBrowser('http://' . $startIpAndPort);
  615.  
  616. do {
  617. // accept the web browser's request and feed in the real key
  618. $socketresource = stream_socket_accept($startSocket);
  619. if (!isSocketConnectionSafe()) {
  620. fclose( $socketresource );
  621. continue; // keep attempting the connection until we know it is safe
  622. }
  623. stream_get_contents( $socketresource );
  624. } while(false);
  625.  
  626. // wait for the server to finish opening before opening the web browser
  627. while (file_get_contents('http://' . $serverIpAndPort . '/ping-wait-for-server-to-become-open.txt') === false) @time_nanosleep(0, 8e6);
  628.  
  629. $contentText = (
  630. "<!doctype html>\r\n" .
  631. '<html lang="en"><head><meta http-equiv="refresh" content="0;url=http://' . $serverIpAndPort . '/?uuid=' . $secretKey . '"/>' .
  632. '<title>Redirecting...</title></head><body><script type="text/javascript">location="' .
  633. 'http://' . $serverIpAndPort . "/?uuid=" . $secretKey . '"</script></body></html>'
  634. );
  635.  
  636. $stringToWrite = (
  637. "HTTP/1.1 302 OK\r\n" .
  638. "Content-Encoding: identity\r\n" .
  639. "Age: 0\r\n" .
  640. "Content-Type: text/html; charset=UTF-8\r\n" .
  641. "Date: " . gmdate('D, d M Y H:i:s T') . "\r\n" .
  642. "Content-Length: " . strlen($contentText) . "\r\n" .
  643. "Location: http://" . $serverIpAndPort . "/?uuid=" . $secretKey . "\r\n" .
  644. "Connection: close\r\n" .
  645. "\r\n" . // second line ending ends header fields
  646. $contentText // Finally the actualtext content
  647. );
  648. fflush($socketresource);
  649. fclose($socketresource);
  650. fclose($startSocket);
Add Comment
Please, Sign In to add comment