Integrated Terminal
Attacking Web Applications with Ffuf
Directory Fuzzing
Now that we understand the concept of Web Fuzzing and know our wordlist, we should be ready to start using ffuf
to find website directories.
Ffuf
Ffuf
is pre-installed on your PwnBox instance. If you want to use it on your own machine, you can either use "apt install ffuf -y
" or download it and use it from its GitHub Repo. As a new user of this tool, we will start by issuing the ffuf -h
command to see how the tools can be used:
Evseytube@htb[/htb]$ ffuf -h
HTTP OPTIONS:
-H Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
-X HTTP method to use (default: GET)
-b Cookie data `"NAME1=VALUE1; NAME2=VALUE2"` for copy as curl functionality.
-d POST data
-recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
-recursion-depth Maximum recursion depth. (default: 0)
-u Target URL
...SNIP...
MATCHER OPTIONS:
-mc Match HTTP status codes, or "all" for everything. (default: 200,204,301,302,307,401,403)
-ms Match HTTP response size
...SNIP...
FILTER OPTIONS:
-fc Filter HTTP status codes from response. Comma separated list of codes and ranges
-fs Filter HTTP response size. Comma separated list of sizes and ranges
...SNIP...
INPUT OPTIONS:
...SNIP...
-w Wordlist file path and (optional) keyword separated by colon. eg. '/path/to/wordlist:KEYWORD'
OUTPUT OPTIONS:
-o Write output to file
...SNIP...
EXAMPLE USAGE:
Fuzz file paths from wordlist.txt, match all responses but filter out those with content-size 42.
Colored, verbose output.
ffuf -w wordlist.txt -u https://example.org/FUZZ -mc all -fs 42 -c -v
...SNIP...
As we can see, the help
output is quite large, so we only kept the options that may become relevant for us in this module.
Directory Fuzzing
As we can see from the example above, the main two options are -w
for wordlists and -u
for the URL. We can assign a keyword to a wordlist to refer to it where
we want to fuzz. For example, we can pick our wordlist and assign the
keyword FUZZ
to it by adding :FUZZ
after it:
Evseytube@htb[/htb]$ ffuf -w /opt/useful/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ
Next, as we want to be fuzzing for web directories, we can place the FUZZ
keyword where the directory would be within our URL, with:
Evseytube@htb[/htb]$ ffuf -w <SNIP> -u http://SERVER_IP:PORT/FUZZ
Now, let's start our target in the question below and run our final command on it:
Evseytube@htb[/htb]$ ffuf -w /opt/useful/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://SERVER_IP:PORT/FUZZ
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.1.0-git
________________________________________________
:: Method : GET
:: URL : http://SERVER_IP:PORT/FUZZ
:: Wordlist : FUZZ: /opt/useful/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
________________________________________________
<SNIP>
blog [Status: 301, Size: 326, Words: 20, Lines: 10]
:: Progress: [87651/87651] :: Job [1/1] :: 9739 req/sec :: Duration: [0:00:09] :: Errors: 0 ::
We see that ffuf
tested for almost 90k URLs in less than 10 seconds. This speed may vary depending on your internet speed and ping if you used ffuf
on your machine, but it should still be extremely fast.
We can even make it go faster if we are in a hurry by increasing the number of threads to 200, for example, with -t 200
, but this is not recommended, especially when used on a remote site, as it may disrupt it, and cause a Denial of Service
,
or bring down your internet connection in severe cases. We do get a
couple of hits, and we can visit one of them to verify that it exists:
We get an empty page, indicating that the directory does not have a
dedicated page, but also shows that we do have access to it, as we do
not get an HTTP code 404 Not Found
or 403 Access Denied
.
In the next section, we will look for pages under this directory to see
whether it is really empty or has hidden files and pages.
/ 1 spawns left
start...
Questions
Answer the question(s) below
to complete this Section and earn cubes!
Target: 94.237.59.206:48519
Life Left:
3096 minutes
Table of Contents
Introduction
Basic Fuzzing
Directory Fuzzing
Page Fuzzing
Recursive Fuzzing
Domain Fuzzing
DNS Records
Sub-domain Fuzzing
Vhost Fuzzing
Filtering Results
Parameter Fuzzing
Parameter Fuzzing - GET
Parameter Fuzzing - POST
Value Fuzzing
Skills Assessment
My Workstation
OFFLINE
/ 1 spawns left
setTimeout(() => {
initReferralScheme();
}, 1000);
$('table').addClass('table table-striped text-left').wrap('
');
$('.training-module a').attr('target', '_blank').attr('rel', 'noopener nofollow');
$('.favouriteBtn').click(function() {
let id = $(this).data('module-id');
axios.get('/api/modules/favourite/' + id).then(response => {
let data = response.data;
if (data.fav === 1) {
$(this).removeClass('far fa').addClass('fa');
toastr['success'](data.message, 'Success');
} else {
$(this).removeClass('far fa').addClass('far');
toastr['success'](data.message, 'Success');
}
});
});
function encode_utf8(string) {
return unescape(encodeURIComponent(string));
}
$('.btnAnswer').click(function() {
let question_id = $(this).data('question-id');
$(this).attr('disabled', 1);
$(this).find('.submit-button-text').addClass('d-none');
$(this).find('.submit-button-loader').removeClass('d-none');
let utf8EncodedString = encode_utf8($('#answer' + question_id).val());
axios.post('/api/check/answer', {
answer: btoa(utf8EncodedString),
question_id: question_id
}).then(response => {
let data = response.data;
if (data.success === 0) {
$(this).removeAttr('disabled');
if (data.can_ask_for_help && data.is_discord_linked) {
$(`.d-discord-${question_id}`).removeClass('d-none');
$('#requestHelpModal').modal('show').data('question_id', question_id);
} else if (data.can_ask_for_help && !data.is_discord_linked) {
$('#linkDiscordModal').modal('show');
}
toastr['error'](data.message, 'Error');
} else {
$(this).attr('disabled', 1);
$('#answer' + question_id).attr('disabled', 1);
$('#answer' + question_id).addClass('text-success');
$('#hintBtn' + question_id).attr('disabled', 1);
$(`.d-discord-${question_id}`).addClass('d-none');
toastr['success'](data.message, 'Success');
completeCheck();
}
$(this).find('.submit-button-text').removeClass('d-none');
$(this).find('.submit-button-loader').addClass('d-none');
});
});
$('.btnAnswerExercise').click(function() {
let exercise_id = $(this).data('exercise-id');
$(this).attr('disabled', 1);
$(this).find('.submit-button-text').addClass('d-none');
$(this).find('.submit-button-loader').removeClass('d-none');
let utf8EncodedString = encode_utf8($(`#exerciseAnswer${exercise_id}`).val());
axios.post(`/api/check/exercise/${exercise_id}/answer`, {
answer: btoa(utf8EncodedString)
}).then(response => {
let data = response.data;
if (data.success === 0) {
$(this).removeAttr('disabled');
toastr['error'](data.message, 'Error');
} else {
$(this).attr('disabled', 1);
$(`#exerciseAnswer${exercise_id}`).attr('disabled', 1);
$(`#exerciseAnswer${exercise_id}`).addClass('text-success');
toastr['success'](data.message, 'Success');
}
$(this).find('.submit-button-text').removeClass('d-none');
$(this).find('.submit-button-loader').addClass('d-none');
});
});
$('.btnAnswerExerciseMultiple').click(function() {
$(this).attr('disabled', 1);
$(this).find('.submit-button-text').addClass('d-none');
$(this).find('.submit-button-loader').removeClass('d-none');
let exercise_id = $(this).data('exercise-id');
$(this).attr('disabled', 1);
let utf8EncodedString = $(`#exerciseAnswer${exercise_id}`).find('input:checked').val();
axios.post(`/api/check/exercise/${exercise_id}/answer`, {
answer: btoa(utf8EncodedString)
}).then(response => {
let data = response.data;
if (data.success === 0) {
$(this).removeAttr('disabled');
toastr['error'](data.message, 'Error');
$(`#exerciseAnswer${exercise_id} input[type=radio]:checked`).parent().parent().find(`.exercise-answer-justification`).removeClass('d-none');
$(`#exerciseAnswer${exercise_id} input[type=radio]:checked`).attr('disabled', 1);
$(`#exerciseAnswer${exercise_id} input[type=radio]:checked`).parent().find('label').addClass('text-danger red');
} else {
$(this).attr('disabled', 1);
$(`#exerciseAnswer${exercise_id}`).attr('disabled', 1);
$(`#exerciseAnswer${exercise_id} input[type=radio]:checked`).parent().find('label').addClass('text-success green');
$(`#exerciseAnswer${exercise_id} input[type=radio]:checked`).parent().find('label').removeClass('red');
$(`#exerciseAnswer${exercise_id} input[type=radio]`).attr('disabled', 1);
$(`#exerciseAnswer${exercise_id} .exercise-answer-justification`).removeClass('d-none');
toastr['success'](data.message, 'Success');
}
$(this).find('.submit-button-text').removeClass('d-none');
$(this).find('.submit-button-loader').addClass('d-none');
});
});
$('.reveal-modal').click(e => {
let id = $(e.target).data('exercise-id');
$(`#revealExerciseAnswer${id}`).modal('hide');
});
$(document).ready(function() {
setTimeout(()=>{
// we add a delay berofre calling this function
// in order the axios headers to have been set
completeCheck();
},500)
$('pre').filter(function() {
return this.className.match(/\blanguage-/);
}).each(function() {
let heading = $(this).prevAll().closest('h4').last().text();
if (this.className === ' language-shell-session') {
wrapTerminal(this, heading);
} else if (this.className === ' language-powershell-session') {
wrapPSTerminal(this, heading);
} else if (this.className === ' language-cmd-session') {
wrapCMDTerminal(this, heading);
} else if (this.className.startsWith(' language-')) {
var language = this.className.split('-').pop();
wrapCodeBlock(this, language);
}
});
wrapBrowser($('.website-screenshot'));
$('span:contains(\'[!bash!]\')').html('Evseytube@htb[/htb]');
let _vpnSelector = $('.vpnSelector');
let _selectedServer = _vpnSelector.val();
let _vpnLoader = $('.vpn-loader');
_vpnSelector.change(function() {
let vpnId = $('.vpnSelector option:selected').val();
if (!vpnId) return;
_vpnLoader.removeClass('d-none');
_vpnLoader.addClass('d-flex');
_vpnSelector.addClass('d-none');
axios.post(`/api/v2/vpn-servers/${vpnId}/switch`, {}).then(response => {
_vpnLoader.removeClass('d-flex');
_vpnLoader.addClass('d-none');
_vpnSelector.removeClass('d-none');
toastr['success']('Vpn server switched successfully', 'Success');
$('.startInstanceBtn').each(function() {
$(this).removeClass('disabled');
});
}).catch(err => {
_vpnLoader.removeClass('d-flex');
_vpnLoader.addClass('d-none');
_vpnSelector.removeClass('d-none');
toastr['error'](err.response.data.message, 'Error');
_vpnSelector.val(_selectedServer);
});
});
});
$('.download-vpn-settings').click(() => {
let type = 'regular';
let protocol = $('input[name="vpn-protocol"]:checked').val();
axios.get(`/api/v2/vpn-servers/key/download?type=${type}&protocol=${protocol}`).then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `academy-${type}.ovpn`);
document.body.appendChild(link);
link.click();
}).catch(err => {
toastr['error'](err.response.data.message, 'Error');
});
});
$('#completeBtn').click(() => {
$('#completeBtn').addClass('disabled');
$('#complete-button-text').addClass('d-none');
$('#complete-button-loader').removeClass('d-none');
});
function completeCheck() {
axios.get('/api/check/complete/485').then(response => {
let data = response.data;
if (data.success === 1) {
if (data.completed === true) {
$('#completeBtn').show();
}
}
});
}
function wrapTerminal(element, heading) {
$(element).css('line-height', '0px');
$(element).wrap('
')
.parent().wrap('
')
.prepend(`
${heading}
`);
}
function wrapPSTerminal(element, heading) {
$(element).css('background', '#012456').children().css('color', 'white');
$(element).css('line-height', '0px');
$(element).wrap('
')
.parent().wrap('
')
.prepend(`
${heading}
`);
}
function wrapCMDTerminal(element, heading) {
$(element).css('background', 'black').children().css('color', 'white');
$(element).css('line-height', '0px');
$(element).wrap('
')
.parent().wrap('
')
.prepend(`
${heading}
`);
}
function wrapCodeBlock(element, lang) {
$(element).wrap('
')
.parent().wrap('
')
.prepend('
');
}
function wrapBrowserOld(element) {
$(element).css('border', '0px').css('border-radius', '0');
let url = element.data('url');
$(element).wrap('
')
.parent().wrap('
')
.prepend(`
`);
}
function wrapBrowser(element) {
$(element).css('border', '0px').css('border-radius', '0');
$(element).each(function(index, e) {
let url = $(e).data('url');
$(e).wrap('
')
.parent().wrap('
')
.prepend(`
`);
});
}
function requestHelp() {
let question_id = $('#requestHelpModal').data('question_id');
axios.post(`/api/question/${question_id}/help`)
.then(({ data }) => {
$('#requestHelpModal').modal('hide');
if (data.success == 0) {
toastr['error'](data.message, 'Error');
return;
}
toastr['success']('Request sent!', 'Success');
});
}
function openRequestModal(question_id) {
$('#requestHelpModal').modal('show').data('question_id', question_id);
}
function openWalkthroughModal(index, sectionTitle) {
let _walkthrougModal = $('#walkthroughModal');
const _walkthroughContent = $('#walkthroughModal').find('.training-module');
const _walkthroughLoader = $('.walkthrough-loader');
_walkthrougModal.modal('show');
_walkthrougModal.on('shown.bs.modal', function(event) {
const indexNumber = new Number(index);
_walkthroughLoader.removeClass('d-none');
_walkthroughLoader.addClass('d-block');
let question = `Question ${indexNumber + 1}`;
let element = $("h1").filter(function() {
// Filter all the h1 headers, to find the exact section title
return $(this).text().trim().toLowerCase() === sectionTitle.toLowerCase();
}).nextAll("h2").filter(function() {
// then filter all the h2 headers, to find the exact question as well
return $(this).text().trim().toLowerCase() === question.toLowerCase();
}).first();
if (element.length) {
setTimeout(() => {
_walkthroughLoader.addClass('d-none');
_walkthroughLoader.removeClass('d-block');
_walkthroughContent.removeClass('d-none');
_walkthrougModal.animate({
scrollTop: element.offset().top - _walkthrougModal.offset().top + _walkthrougModal.scrollTop()
}, 500);
}, 1500);
} else {
_walkthroughLoader.addClass('d-none');
_walkthroughLoader.removeClass('d-block');
_walkthroughContent.removeClass('d-none');
}
});
_walkthrougModal.on('hidden.bs.modal', function(event) {
// Make sure that content gets hidden again
// in case the user re-opens the modal multiple times
_walkthroughContent.addClass('d-none');
});
}